summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/input.h372
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h15
-rw-r--r--src/common/settings_input.h70
-rw-r--r--src/core/CMakeLists.txt21
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp20
-rw-r--r--src/core/core.cpp14
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/frontend/applets/controller.cpp45
-rw-r--r--src/core/frontend/applets/controller.h8
-rw-r--r--src/core/frontend/emu_window.cpp98
-rw-r--r--src/core/frontend/emu_window.h30
-rw-r--r--src/core/frontend/framebuffer_layout.cpp7
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/frontend/input.h217
-rw-r--r--src/core/hid/emulated_console.cpp229
-rw-r--r--src/core/hid/emulated_console.h188
-rw-r--r--src/core/hid/emulated_controller.cpp1081
-rw-r--r--src/core/hid/emulated_controller.h392
-rw-r--r--src/core/hid/emulated_devices.cpp451
-rw-r--r--src/core/hid/emulated_devices.h209
-rw-r--r--src/core/hid/hid_core.cpp168
-rw-r--r--src/core/hid/hid_core.h73
-rw-r--r--src/core/hid/hid_types.h631
-rw-r--r--src/core/hid/input_converter.cpp383
-rw-r--r--src/core/hid/input_converter.h95
-rw-r--r--src/core/hid/input_interpreter.cpp (renamed from src/core/frontend/input_interpreter.cpp)25
-rw-r--r--src/core/hid/input_interpreter.h (renamed from src/core/frontend/input_interpreter.h)54
-rw-r--r--src/core/hid/motion_input.cpp (renamed from src/input_common/motion_input.cpp)57
-rw-r--r--src/core/hid/motion_input.h (renamed from src/input_common/motion_input.h)27
-rw-r--r--src/core/hle/kernel/k_page_table.cpp4
-rw-r--r--src/core/hle/kernel/k_page_table.h2
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp22
-rw-r--r--src/core/hle/kernel/svc.cpp60
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp19
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h6
-rw-r--r--src/core/hle/service/am/applets/applets.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h44
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h21
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp72
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h82
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp257
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h109
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp59
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h59
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp64
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h63
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1621
-rw-r--r--src/core/hle/service/hid/controllers/npad.h649
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp139
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h86
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp32
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h61
-rw-r--r--src/core/hle/service/hid/hid.cpp408
-rw-r--r--src/core/hle/service/hid/hid.h33
-rw-r--r--src/core/hle/service/hid/ring_lifo.h54
-rw-r--r--src/core/hle/service/ldr/ldr.cpp8
-rw-r--r--src/core/hle/service/ns/ns.cpp23
-rw-r--r--src/core/hle/service/ns/ns.h7
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp69
-rw-r--r--src/core/hle/service/ns/pdm_qry.h33
-rw-r--r--src/core/hle/service/pm/pm.cpp47
-rw-r--r--src/input_common/CMakeLists.txt60
-rwxr-xr-xsrc/input_common/analog_from_button.cpp238
-rw-r--r--src/input_common/drivers/gc_adapter.cpp (renamed from src/input_common/gcadapter/gc_adapter.cpp)489
-rw-r--r--src/input_common/drivers/gc_adapter.h135
-rw-r--r--src/input_common/drivers/keyboard.cpp112
-rw-r--r--src/input_common/drivers/keyboard.h56
-rw-r--r--src/input_common/drivers/mouse.cpp185
-rw-r--r--src/input_common/drivers/mouse.h81
-rw-r--r--src/input_common/drivers/sdl_driver.cpp924
-rw-r--r--src/input_common/drivers/sdl_driver.h (renamed from src/input_common/sdl/sdl_impl.h)59
-rw-r--r--src/input_common/drivers/tas_input.cpp315
-rw-r--r--src/input_common/drivers/tas_input.h (renamed from src/input_common/tas/tas_input.h)151
-rw-r--r--src/input_common/drivers/touch_screen.cpp53
-rw-r--r--src/input_common/drivers/touch_screen.h44
-rw-r--r--src/input_common/drivers/udp_client.cpp591
-rw-r--r--src/input_common/drivers/udp_client.h (renamed from src/input_common/udp/client.h)128
-rw-r--r--src/input_common/gcadapter/gc_adapter.h168
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp356
-rw-r--r--src/input_common/gcadapter/gc_poller.h78
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp304
-rw-r--r--[-rwxr-xr-x]src/input_common/helpers/stick_from_buttons.h (renamed from src/input_common/analog_from_button.h)7
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp81
-rw-r--r--src/input_common/helpers/touch_from_buttons.h (renamed from src/input_common/touch_from_button.h)7
-rw-r--r--src/input_common/helpers/udp_protocol.cpp (renamed from src/input_common/udp/protocol.cpp)2
-rw-r--r--src/input_common/helpers/udp_protocol.h (renamed from src/input_common/udp/protocol.h)75
-rw-r--r--src/input_common/input_engine.cpp364
-rw-r--r--src/input_common/input_engine.h232
-rw-r--r--src/input_common/input_mapping.cpp207
-rw-r--r--src/input_common/input_mapping.h83
-rw-r--r--src/input_common/input_poller.cpp971
-rw-r--r--src/input_common/input_poller.h217
-rw-r--r--src/input_common/keyboard.cpp121
-rw-r--r--src/input_common/keyboard.h47
-rw-r--r--src/input_common/main.cpp468
-rw-r--r--src/input_common/main.h152
-rw-r--r--src/input_common/motion_from_button.cpp34
-rw-r--r--src/input_common/motion_from_button.h25
-rw-r--r--src/input_common/mouse/mouse_input.cpp223
-rw-r--r--src/input_common/mouse/mouse_input.h116
-rw-r--r--src/input_common/mouse/mouse_poller.cpp299
-rw-r--r--src/input_common/mouse/mouse_poller.h109
-rw-r--r--src/input_common/sdl/sdl.cpp19
-rw-r--r--src/input_common/sdl/sdl.h51
-rw-r--r--src/input_common/sdl/sdl_impl.cpp1658
-rw-r--r--src/input_common/tas/tas_input.cpp455
-rw-r--r--src/input_common/tas/tas_poller.cpp101
-rw-r--r--src/input_common/tas/tas_poller.h43
-rw-r--r--src/input_common/touch_from_button.cpp53
-rw-r--r--src/input_common/udp/client.cpp526
-rw-r--r--src/input_common/udp/udp.cpp110
-rw-r--r--src/input_common/udp/udp.h57
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp43
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp38
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp6
-rw-r--r--src/video_core/video_core.cpp6
-rw-r--r--src/video_core/video_core.h2
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/applets/qt_controller.cpp153
-rw-r--r--src/yuzu/applets/qt_controller.h18
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp112
-rw-r--r--src/yuzu/applets/qt_software_keyboard.h12
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp70
-rw-r--r--src/yuzu/applets/qt_web_browser.h12
-rw-r--r--src/yuzu/bootmanager.cpp370
-rw-r--r--src/yuzu/bootmanager.h22
-rw-r--r--src/yuzu/configuration/config.cpp222
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp9
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h7
-rw-r--r--src/yuzu/configuration/configure_general.cpp20
-rw-r--r--src/yuzu/configuration/configure_general.ui99
-rw-r--r--src/yuzu/configuration/configure_input.cpp66
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui217
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp1183
-rw-r--r--src/yuzu/configuration/configure_input_player.h57
-rw-r--r--src/yuzu/configuration/configure_input_player.ui50
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp715
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h173
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp12
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui70
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp276
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h78
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui335
-rw-r--r--src/yuzu/configuration/configure_tas.cpp2
-rw-r--r--src/yuzu/configuration/configure_tas.ui9
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp27
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.h5
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp74
-rw-r--r--src/yuzu/configuration/configure_vibration.h3
-rw-r--r--src/yuzu/debugger/controller.cpp75
-rw-r--r--src/yuzu/debugger/controller.h38
-rw-r--r--src/yuzu/main.cpp531
-rw-r--r--src/yuzu/main.h11
-rw-r--r--src/yuzu/main.ui99
-rw-r--r--src/yuzu/uisettings.cpp4
-rw-r--r--src/yuzu/util/overlay_dialog.cpp27
-rw-r--r--src/yuzu/util/overlay_dialog.h10
-rw-r--r--src/yuzu_cmd/config.cpp178
-rw-r--r--src/yuzu_cmd/default_ini.h17
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp72
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h11
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp1
175 files changed, 14766 insertions, 11656 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c044c1ea..61510cc0f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -167,7 +167,7 @@ macro(yuzu_find_packages)
167 set(REQUIRED_LIBS 167 set(REQUIRED_LIBS
168 # Cmake Pkg Prefix Version Conan Pkg 168 # Cmake Pkg Prefix Version Conan Pkg
169 "Catch2 2.13.7 catch2/2.13.7" 169 "Catch2 2.13.7 catch2/2.13.7"
170 "fmt 8.0 fmt/8.0.0" 170 "fmt 8.0.1 fmt/8.0.1"
171 "lz4 1.8 lz4/1.9.2" 171 "lz4 1.8 lz4/1.9.2"
172 "nlohmann_json 3.8 nlohmann_json/3.8.0" 172 "nlohmann_json 3.8 nlohmann_json/3.8.0"
173 "ZLIB 1.2 zlib/1.2.11" 173 "ZLIB 1.2 zlib/1.2.11"
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 23d43a394..919da4a53 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -73,6 +73,7 @@ add_library(common STATIC
73 hex_util.h 73 hex_util.h
74 host_memory.cpp 74 host_memory.cpp
75 host_memory.h 75 host_memory.h
76 input.h
76 intrusive_red_black_tree.h 77 intrusive_red_black_tree.h
77 literals.h 78 literals.h
78 logging/backend.cpp 79 logging/backend.cpp
diff --git a/src/common/input.h b/src/common/input.h
new file mode 100644
index 000000000..eaee0bdea
--- /dev/null
+++ b/src/common/input.h
@@ -0,0 +1,372 @@
1// Copyright 2017 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 <functional>
8#include <memory>
9#include <string>
10#include <unordered_map>
11#include <utility>
12#include "common/logging/log.h"
13#include "common/param_package.h"
14#include "common/uuid.h"
15
16namespace Common::Input {
17
18// Type of data that is expected to recieve or send
19enum class InputType {
20 None,
21 Battery,
22 Button,
23 Stick,
24 Analog,
25 Trigger,
26 Motion,
27 Touch,
28 Color,
29 Vibration,
30 Nfc,
31 Ir,
32};
33
34// Internal battery charge level
35enum class BatteryLevel : u32 {
36 None,
37 Empty,
38 Critical,
39 Low,
40 Medium,
41 Full,
42 Charging,
43};
44
45enum class PollingMode {
46 // Constant polling of buttons, analogs and motion data
47 Active,
48 // Only update on button change, digital analogs
49 Pasive,
50 // Enable near field communication polling
51 NFC,
52 // Enable infrared camera polling
53 IR,
54};
55
56// Vibration reply from the controller
57enum class VibrationError {
58 None,
59 NotSupported,
60 Disabled,
61 Unknown,
62};
63
64// Polling mode reply from the controller
65enum class PollingError {
66 None,
67 NotSupported,
68 Unknown,
69};
70
71// Hint for amplification curve to be used
72enum class VibrationAmplificationType {
73 Linear,
74 Exponential,
75};
76
77// Analog properties for calibration
78struct AnalogProperties {
79 // Anything below this value will be detected as zero
80 float deadzone{};
81 // Anyting above this values will be detected as one
82 float range{1.0f};
83 // Minimum value to be detected as active
84 float threshold{0.5f};
85 // Drift correction applied to the raw data
86 float offset{};
87 // Invert direction of the sensor data
88 bool inverted{};
89};
90
91// Single analog sensor data
92struct AnalogStatus {
93 float value{};
94 float raw_value{};
95 AnalogProperties properties{};
96};
97
98// Button data
99struct ButtonStatus {
100 Common::UUID uuid{};
101 bool value{};
102 bool inverted{};
103 bool toggle{};
104 bool locked{};
105};
106
107// Internal battery data
108using BatteryStatus = BatteryLevel;
109
110// Analog and digital joystick data
111struct StickStatus {
112 Common::UUID uuid{};
113 AnalogStatus x{};
114 AnalogStatus y{};
115 bool left{};
116 bool right{};
117 bool up{};
118 bool down{};
119};
120
121// Analog and digital trigger data
122struct TriggerStatus {
123 Common::UUID uuid{};
124 AnalogStatus analog{};
125 ButtonStatus pressed{};
126};
127
128// 3D vector representing motion input
129struct MotionSensor {
130 AnalogStatus x{};
131 AnalogStatus y{};
132 AnalogStatus z{};
133};
134
135// Motion data used to calculate controller orientation
136struct MotionStatus {
137 // Gyroscope vector measurement in radians/s.
138 MotionSensor gyro{};
139 // Acceleration vector measurement in G force
140 MotionSensor accel{};
141 // Time since last measurement in microseconds
142 u64 delta_timestamp{};
143 // Request to update after reading the value
144 bool force_update{};
145};
146
147// Data of a single point on a touch screen
148struct TouchStatus {
149 ButtonStatus pressed{};
150 AnalogStatus x{};
151 AnalogStatus y{};
152 int id{};
153};
154
155// Physical controller color in RGB format
156struct BodyColorStatus {
157 u32 body{};
158 u32 buttons{};
159};
160
161// HD rumble data
162struct VibrationStatus {
163 f32 low_amplitude{};
164 f32 low_frequency{};
165 f32 high_amplitude{};
166 f32 high_frequency{};
167 VibrationAmplificationType type;
168};
169
170// Physical controller LED pattern
171struct LedStatus {
172 bool led_1{};
173 bool led_2{};
174 bool led_3{};
175 bool led_4{};
176};
177
178// List of buttons to be passed to Qt that can be translated
179enum class ButtonNames {
180 Undefined,
181 Invalid,
182 // This will display the engine name instead of the button name
183 Engine,
184 // This will display the button by value instead of the button name
185 Value,
186 ButtonLeft,
187 ButtonRight,
188 ButtonDown,
189 ButtonUp,
190 TriggerZ,
191 TriggerR,
192 TriggerL,
193 ButtonA,
194 ButtonB,
195 ButtonX,
196 ButtonY,
197 ButtonStart,
198
199 // DS4 button names
200 L1,
201 L2,
202 L3,
203 R1,
204 R2,
205 R3,
206 Circle,
207 Cross,
208 Square,
209 Triangle,
210 Share,
211 Options,
212};
213
214// Callback data consisting of an input type and the equivalent data status
215struct CallbackStatus {
216 InputType type{InputType::None};
217 ButtonStatus button_status{};
218 StickStatus stick_status{};
219 AnalogStatus analog_status{};
220 TriggerStatus trigger_status{};
221 MotionStatus motion_status{};
222 TouchStatus touch_status{};
223 BodyColorStatus color_status{};
224 BatteryStatus battery_status{};
225 VibrationStatus vibration_status{};
226};
227
228// Triggered once every input change
229struct InputCallback {
230 std::function<void(CallbackStatus)> on_change;
231};
232
233/// An abstract class template for an input device (a button, an analog input, etc.).
234class InputDevice {
235public:
236 virtual ~InputDevice() = default;
237
238 // Request input device to update if necessary
239 virtual void SoftUpdate() {
240 return;
241 }
242
243 // Force input device to update data regardless of the current state
244 virtual void ForceUpdate() {
245 return;
246 }
247
248 // Sets the function to be triggered when input changes
249 void SetCallback(InputCallback callback_) {
250 callback = std::move(callback_);
251 }
252
253 // Triggers the function set in the callback
254 void TriggerOnChange(CallbackStatus status) {
255 if (callback.on_change) {
256 callback.on_change(status);
257 }
258 }
259
260private:
261 InputCallback callback;
262};
263
264/// An abstract class template for an output device (rumble, LED pattern, polling mode).
265class OutputDevice {
266public:
267 virtual ~OutputDevice() = default;
268
269 virtual void SetLED([[maybe_unused]] LedStatus led_status) {
270 return;
271 }
272
273 virtual VibrationError SetVibration([[maybe_unused]] VibrationStatus vibration_status) {
274 return VibrationError::NotSupported;
275 }
276
277 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
278 return PollingError::NotSupported;
279 }
280};
281
282/// An abstract class template for a factory that can create input devices.
283template <typename InputDeviceType>
284class Factory {
285public:
286 virtual ~Factory() = default;
287 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
288};
289
290namespace Impl {
291
292template <typename InputDeviceType>
293using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
294
295template <typename InputDeviceType>
296struct FactoryList {
297 static FactoryListType<InputDeviceType> list;
298};
299
300template <typename InputDeviceType>
301FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
302
303} // namespace Impl
304
305/**
306 * Registers an input device factory.
307 * @tparam InputDeviceType the type of input devices the factory can create
308 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
309 * a device
310 * @param factory the factory object to register
311 */
312template <typename InputDeviceType>
313void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
314 auto pair = std::make_pair(name, std::move(factory));
315 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
316 LOG_ERROR(Input, "Factory '{}' already registered", name);
317 }
318}
319
320/**
321 * Unregisters an input device factory.
322 * @tparam InputDeviceType the type of input devices the factory can create
323 * @param name the name of the factory to unregister
324 */
325template <typename InputDeviceType>
326void UnregisterFactory(const std::string& name) {
327 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
328 LOG_ERROR(Input, "Factory '{}' not registered", name);
329 }
330}
331
332/**
333 * Create an input device from given paramters.
334 * @tparam InputDeviceType the type of input devices to create
335 * @param params a serialized ParamPackage string that contains all parameters for creating the
336 * device
337 */
338template <typename InputDeviceType>
339std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& params) {
340 const Common::ParamPackage package(params);
341 const std::string engine = package.Get("engine", "null");
342 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
343 const auto pair = factory_list.find(engine);
344 if (pair == factory_list.end()) {
345 if (engine != "null") {
346 LOG_ERROR(Input, "Unknown engine name: {}", engine);
347 }
348 return std::make_unique<InputDeviceType>();
349 }
350 return pair->second->Create(package);
351}
352
353/**
354 * Create an input device from given paramters.
355 * @tparam InputDeviceType the type of input devices to create
356 * @param A ParamPackage that contains all parameters for creating the device
357 */
358template <typename InputDeviceType>
359std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) {
360 const std::string engine = package.Get("engine", "null");
361 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
362 const auto pair = factory_list.find(engine);
363 if (pair == factory_list.end()) {
364 if (engine != "null") {
365 LOG_ERROR(Input, "Unknown engine name: {}", engine);
366 }
367 return std::make_unique<InputDeviceType>();
368 }
369 return pair->second->Create(package);
370}
371
372} // namespace Common::Input
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 3bcaa072f..6964a8273 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -183,6 +183,7 @@ void RestoreGlobalState(bool is_powered_on) {
183 values.max_anisotropy.SetGlobal(true); 183 values.max_anisotropy.SetGlobal(true);
184 values.use_speed_limit.SetGlobal(true); 184 values.use_speed_limit.SetGlobal(true);
185 values.speed_limit.SetGlobal(true); 185 values.speed_limit.SetGlobal(true);
186 values.fps_cap.SetGlobal(true);
186 values.use_disk_shader_cache.SetGlobal(true); 187 values.use_disk_shader_cache.SetGlobal(true);
187 values.gpu_accuracy.SetGlobal(true); 188 values.gpu_accuracy.SetGlobal(true);
188 values.use_asynchronous_gpu_emulation.SetGlobal(true); 189 values.use_asynchronous_gpu_emulation.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 42f8b4a7d..313f1fa7f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -6,7 +6,6 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array> 8#include <array>
9#include <atomic>
10#include <map> 9#include <map>
11#include <optional> 10#include <optional>
12#include <string> 11#include <string>
@@ -525,7 +524,7 @@ struct Values {
525 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 524 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
526 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 525 Setting<bool> accelerate_astc{true, "accelerate_astc"};
527 Setting<bool> use_vsync{true, "use_vsync"}; 526 Setting<bool> use_vsync{true, "use_vsync"};
528 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 527 RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
529 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 528 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
530 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, 529 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
531 ShaderBackend::SPIRV, "shader_backend"}; 530 ShaderBackend::SPIRV, "shader_backend"};
@@ -560,25 +559,19 @@ struct Values {
560 Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 559 Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
561 560
562 Setting<bool> motion_enabled{true, "motion_enabled"}; 561 Setting<bool> motion_enabled{true, "motion_enabled"};
563 BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
564 "motion_device"};
565 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; 562 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
563 BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"};
566 564
567 BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; 565 BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
568 BasicSetting<bool> tas_enable{false, "tas_enable"}; 566 BasicSetting<bool> tas_enable{false, "tas_enable"};
569 BasicSetting<bool> tas_loop{false, "tas_loop"}; 567 BasicSetting<bool> tas_loop{false, "tas_loop"};
570 BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
571 568
572 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 569 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
573 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 570 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
574 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 571 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
575 std::string mouse_device;
576 MouseButtonsRaw mouse_buttons;
577 572
578 BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 573 BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
579 BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; 574 BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"};
580 KeyboardKeysRaw keyboard_keys;
581 KeyboardModsRaw keyboard_mods;
582 575
583 BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; 576 BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
584 ButtonsRaw debug_pad_buttons; 577 ButtonsRaw debug_pad_buttons;
@@ -586,14 +579,11 @@ struct Values {
586 579
587 TouchscreenInput touchscreen; 580 TouchscreenInput touchscreen;
588 581
589 BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"};
590 BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", 582 BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850",
591 "touch_device"}; 583 "touch_device"};
592 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; 584 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
593 std::vector<TouchFromButtonMap> touch_from_button_maps; 585 std::vector<TouchFromButtonMap> touch_from_button_maps;
594 586
595 std::atomic_bool is_device_reload_pending{true};
596
597 // Data Storage 587 // Data Storage
598 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 588 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
599 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 589 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
@@ -614,6 +604,7 @@ struct Values {
614 BasicSetting<bool> extended_logging{false, "extended_logging"}; 604 BasicSetting<bool> extended_logging{false, "extended_logging"};
615 BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; 605 BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
616 BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; 606 BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
607 BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"};
617 608
618 // Miscellaneous 609 // Miscellaneous
619 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 610 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 609600582..4ff37e186 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -62,11 +62,22 @@ enum Values : int {
62 62
63constexpr int STICK_HID_BEGIN = LStick; 63constexpr int STICK_HID_BEGIN = LStick;
64constexpr int STICK_HID_END = NumAnalogs; 64constexpr int STICK_HID_END = NumAnalogs;
65constexpr int NUM_STICKS_HID = NumAnalogs;
66 65
67extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
68} // namespace NativeAnalog 67} // namespace NativeAnalog
69 68
69namespace NativeTrigger {
70enum Values : int {
71 LTrigger,
72 RTrigger,
73
74 NumTriggers,
75};
76
77constexpr int TRIGGER_HID_BEGIN = LTrigger;
78constexpr int TRIGGER_HID_END = NumTriggers;
79} // namespace NativeTrigger
80
70namespace NativeVibration { 81namespace NativeVibration {
71enum Values : int { 82enum Values : int {
72 LeftVibrationDevice, 83 LeftVibrationDevice,
@@ -115,10 +126,20 @@ constexpr int NUM_MOUSE_HID = NumMouseButtons;
115extern const std::array<const char*, NumMouseButtons> mapping; 126extern const std::array<const char*, NumMouseButtons> mapping;
116} // namespace NativeMouseButton 127} // namespace NativeMouseButton
117 128
129namespace NativeMouseWheel {
130enum Values {
131 X,
132 Y,
133
134 NumMouseWheels,
135};
136
137extern const std::array<const char*, NumMouseWheels> mapping;
138} // namespace NativeMouseWheel
139
118namespace NativeKeyboard { 140namespace NativeKeyboard {
119enum Keys { 141enum Keys {
120 None, 142 None,
121 Error,
122 143
123 A = 4, 144 A = 4,
124 B, 145 B,
@@ -156,22 +177,22 @@ enum Keys {
156 N8, 177 N8,
157 N9, 178 N9,
158 N0, 179 N0,
159 Enter, 180 Return,
160 Escape, 181 Escape,
161 Backspace, 182 Backspace,
162 Tab, 183 Tab,
163 Space, 184 Space,
164 Minus, 185 Minus,
165 Equal, 186 Plus,
166 LeftBrace, 187 OpenBracket,
167 RightBrace, 188 CloseBracket,
168 Backslash, 189 Pipe,
169 Tilde, 190 Tilde,
170 Semicolon, 191 Semicolon,
171 Apostrophe, 192 Quote,
172 Grave, 193 Backquote,
173 Comma, 194 Comma,
174 Dot, 195 Period,
175 Slash, 196 Slash,
176 CapsLockKey, 197 CapsLockKey,
177 198
@@ -188,7 +209,7 @@ enum Keys {
188 F11, 209 F11,
189 F12, 210 F12,
190 211
191 SystemRequest, 212 PrintScreen,
192 ScrollLockKey, 213 ScrollLockKey,
193 Pause, 214 Pause,
194 Insert, 215 Insert,
@@ -257,8 +278,18 @@ enum Keys {
257 ScrollLockActive, 278 ScrollLockActive,
258 KPComma, 279 KPComma,
259 280
260 KPLeftParenthesis, 281 Ro = 0x87,
261 KPRightParenthesis, 282 KatakanaHiragana,
283 Yen,
284 Henkan,
285 Muhenkan,
286 NumPadCommaPc98,
287
288 HangulEnglish = 0x90,
289 Hanja,
290 KatakanaKey,
291 HiraganaKey,
292 ZenkakuHankaku,
262 293
263 LeftControlKey = 0xE0, 294 LeftControlKey = 0xE0,
264 LeftShiftKey, 295 LeftShiftKey,
@@ -307,6 +338,8 @@ enum Modifiers {
307 CapsLock, 338 CapsLock,
308 ScrollLock, 339 ScrollLock,
309 NumLock, 340 NumLock,
341 Katakana,
342 Hiragana,
310 343
311 NumKeyboardMods, 344 NumKeyboardMods,
312}; 345};
@@ -324,11 +357,6 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
324using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
325using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
326using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; 359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
327using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
328
329using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
330using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
331using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
332 360
333constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
334constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
@@ -342,6 +370,11 @@ enum class ControllerType {
342 RightJoycon, 370 RightJoycon,
343 Handheld, 371 Handheld,
344 GameCube, 372 GameCube,
373 Pokeball,
374 NES,
375 SNES,
376 N64,
377 SegaGenesis,
345}; 378};
346 379
347struct PlayerInput { 380struct PlayerInput {
@@ -349,7 +382,6 @@ struct PlayerInput {
349 ControllerType controller_type; 382 ControllerType controller_type;
350 ButtonsRaw buttons; 383 ButtonsRaw buttons;
351 AnalogsRaw analogs; 384 AnalogsRaw analogs;
352 VibrationsRaw vibrations;
353 MotionsRaw motions; 385 MotionsRaw motions;
354 386
355 bool vibration_enabled; 387 bool vibration_enabled;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9f0fbba2d..eee8e2ccd 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -132,11 +132,23 @@ add_library(core STATIC
132 frontend/emu_window.h 132 frontend/emu_window.h
133 frontend/framebuffer_layout.cpp 133 frontend/framebuffer_layout.cpp
134 frontend/framebuffer_layout.h 134 frontend/framebuffer_layout.h
135 frontend/input_interpreter.cpp
136 frontend/input_interpreter.h
137 frontend/input.h
138 hardware_interrupt_manager.cpp 135 hardware_interrupt_manager.cpp
139 hardware_interrupt_manager.h 136 hardware_interrupt_manager.h
137 hid/emulated_console.cpp
138 hid/emulated_console.h
139 hid/emulated_controller.cpp
140 hid/emulated_controller.h
141 hid/emulated_devices.cpp
142 hid/emulated_devices.h
143 hid/hid_core.cpp
144 hid/hid_core.h
145 hid/hid_types.h
146 hid/input_converter.cpp
147 hid/input_converter.h
148 hid/input_interpreter.cpp
149 hid/input_interpreter.h
150 hid/motion_input.cpp
151 hid/motion_input.h
140 hle/api_version.h 152 hle/api_version.h
141 hle/ipc.h 153 hle/ipc.h
142 hle/ipc_helpers.h 154 hle/ipc_helpers.h
@@ -402,6 +414,7 @@ add_library(core STATIC
402 hle/service/hid/hid.h 414 hle/service/hid/hid.h
403 hle/service/hid/irs.cpp 415 hle/service/hid/irs.cpp
404 hle/service/hid/irs.h 416 hle/service/hid/irs.h
417 hle/service/hid/ring_lifo.h
405 hle/service/hid/xcd.cpp 418 hle/service/hid/xcd.cpp
406 hle/service/hid/xcd.h 419 hle/service/hid/xcd.h
407 hle/service/hid/errors.h 420 hle/service/hid/errors.h
@@ -466,6 +479,8 @@ add_library(core STATIC
466 hle/service/ns/language.h 479 hle/service/ns/language.h
467 hle/service/ns/ns.cpp 480 hle/service/ns/ns.cpp
468 hle/service/ns/ns.h 481 hle/service/ns/ns.h
482 hle/service/ns/pdm_qry.cpp
483 hle/service/ns/pdm_qry.h
469 hle/service/ns/pl_u.cpp 484 hle/service/ns/pl_u.cpp
470 hle/service/ns/pl_u.h 485 hle/service/ns/pl_u.h
471 hle/service/nvdrv/devices/nvdevice.h 486 hle/service/nvdrv/devices/nvdevice.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 4e73cc03a..56836bd05 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -86,6 +86,26 @@ public:
86 num_instructions, MemoryReadCode(pc)); 86 num_instructions, MemoryReadCode(pc));
87 } 87 }
88 88
89 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
90 VAddr value) override {
91 switch (op) {
92 case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
93 static constexpr u64 ICACHE_LINE_SIZE = 64;
94
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break;
98 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache();
101 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break;
106 }
107 }
108
89 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
90 switch (exception) { 110 switch (exception) {
91 case Dynarmic::A64::Exception::WaitForInterrupt: 111 case Dynarmic::A64::Exception::WaitForInterrupt:
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 07448fd29..473ab9f81 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,6 +27,7 @@
27#include "core/file_sys/vfs_concat.h" 27#include "core/file_sys/vfs_concat.h"
28#include "core/file_sys/vfs_real.h" 28#include "core/file_sys/vfs_real.h"
29#include "core/hardware_interrupt_manager.h" 29#include "core/hardware_interrupt_manager.h"
30#include "core/hid/hid_core.h"
30#include "core/hle/kernel/k_process.h" 31#include "core/hle/kernel/k_process.h"
31#include "core/hle/kernel/k_scheduler.h" 32#include "core/hle/kernel/k_scheduler.h"
32#include "core/hle/kernel/kernel.h" 33#include "core/hle/kernel/kernel.h"
@@ -126,7 +127,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
126 127
127struct System::Impl { 128struct System::Impl {
128 explicit Impl(System& system) 129 explicit Impl(System& system)
129 : kernel{system}, fs_controller{system}, memory{system}, 130 : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
130 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} 131 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
131 132
132 SystemResultStatus Run() { 133 SystemResultStatus Run() {
@@ -391,6 +392,7 @@ struct System::Impl {
391 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 392 std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
392 std::unique_ptr<Core::DeviceMemory> device_memory; 393 std::unique_ptr<Core::DeviceMemory> device_memory;
393 Core::Memory::Memory memory; 394 Core::Memory::Memory memory;
395 Core::HID::HIDCore hid_core;
394 CpuManager cpu_manager; 396 CpuManager cpu_manager;
395 std::atomic_bool is_powered_on{}; 397 std::atomic_bool is_powered_on{};
396 bool exit_lock = false; 398 bool exit_lock = false;
@@ -615,6 +617,14 @@ const Kernel::KernelCore& System::Kernel() const {
615 return impl->kernel; 617 return impl->kernel;
616} 618}
617 619
620HID::HIDCore& System::HIDCore() {
621 return impl->hid_core;
622}
623
624const HID::HIDCore& System::HIDCore() const {
625 return impl->hid_core;
626}
627
618Timing::CoreTiming& System::CoreTiming() { 628Timing::CoreTiming& System::CoreTiming() {
619 return impl->core_timing; 629 return impl->core_timing;
620} 630}
@@ -825,8 +835,6 @@ void System::ApplySettings() {
825 if (IsPoweredOn()) { 835 if (IsPoweredOn()) {
826 Renderer().RefreshBaseSettings(); 836 Renderer().RefreshBaseSettings();
827 } 837 }
828
829 Service::HID::ReloadInputDevices();
830} 838}
831 839
832} // namespace Core 840} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 01bc0a2c7..645e5c241 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -89,6 +89,10 @@ namespace Core::Hardware {
89class InterruptManager; 89class InterruptManager;
90} 90}
91 91
92namespace Core::HID {
93class HIDCore;
94}
95
92namespace Core { 96namespace Core {
93 97
94class ARM_Interface; 98class ARM_Interface;
@@ -285,6 +289,12 @@ public:
285 /// Provides a constant reference to the kernel instance. 289 /// Provides a constant reference to the kernel instance.
286 [[nodiscard]] const Kernel::KernelCore& Kernel() const; 290 [[nodiscard]] const Kernel::KernelCore& Kernel() const;
287 291
292 /// Gets a mutable reference to the HID interface.
293 [[nodiscard]] HID::HIDCore& HIDCore();
294
295 /// Gets an immutable reference to the HID interface.
296 [[nodiscard]] const HID::HIDCore& HIDCore() const;
297
288 /// Provides a reference to the internal PerfStats instance. 298 /// Provides a reference to the internal PerfStats instance.
289 [[nodiscard]] Core::PerfStats& GetPerfStats(); 299 [[nodiscard]] Core::PerfStats& GetPerfStats();
290 300
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 03bbedf8b..6dbd38ffa 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -5,16 +5,15 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/frontend/applets/controller.h" 7#include "core/frontend/applets/controller.h"
8#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hid/emulated_controller.h"
9#include "core/hle/service/hid/hid.h" 9#include "core/hid/hid_core.h"
10#include "core/hle/service/sm/sm.h" 10#include "core/hid/hid_types.h"
11 11
12namespace Core::Frontend { 12namespace Core::Frontend {
13 13
14ControllerApplet::~ControllerApplet() = default; 14ControllerApplet::~ControllerApplet() = default;
15 15
16DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) 16DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {}
17 : service_manager{service_manager_} {}
18 17
19DefaultControllerApplet::~DefaultControllerApplet() = default; 18DefaultControllerApplet::~DefaultControllerApplet() = default;
20 19
@@ -22,24 +21,20 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
22 const ControllerParameters& parameters) const { 21 const ControllerParameters& parameters) const {
23 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); 22 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
24 23
25 auto& npad =
26 service_manager.GetService<Service::HID::Hid>("hid")
27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29
30 auto& players = Settings::values.players.GetValue();
31
32 const std::size_t min_supported_players = 24 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players; 25 parameters.enable_single_mode ? 1 : parameters.min_players;
34 26
35 // Disconnect Handheld first. 27 // Disconnect Handheld first.
36 npad.DisconnectNpadAtIndex(8); 28 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
29 handheld->Disconnect();
37 30
38 // Deduce the best configuration based on the input parameters. 31 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) { 32 for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
33 auto* controller = hid_core.GetEmulatedControllerByIndex(index);
34
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected. 35 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers. 36 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNpadAtIndex(index); 37 controller->Disconnect();
43 38
44 // Only connect the minimum number of required players. 39 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) { 40 if (index >= min_supported_players) {
@@ -49,27 +44,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
49 // Connect controllers based on the following priority list from highest to lowest priority: 44 // Connect controllers based on the following priority list from highest to lowest priority:
50 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld 45 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
51 if (parameters.allow_pro_controller) { 46 if (parameters.allow_pro_controller) {
52 npad.AddNewControllerAt( 47 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
53 npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index); 48 controller->Connect();
54 } else if (parameters.allow_dual_joycons) { 49 } else if (parameters.allow_dual_joycons) {
55 npad.AddNewControllerAt( 50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
56 npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index); 51 controller->Connect();
57 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { 52 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
58 // Assign left joycons to even player indices and right joycons to odd player indices. 53 // Assign left joycons to even player indices and right joycons to odd player indices.
59 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and 54 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
60 // a right Joycon for Player 2 in 2 Player Assist mode. 55 // a right Joycon for Player 2 in 2 Player Assist mode.
61 if (index % 2 == 0) { 56 if (index % 2 == 0) {
62 npad.AddNewControllerAt( 57 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
63 npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index); 58 controller->Connect();
64 } else { 59 } else {
65 npad.AddNewControllerAt( 60 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); 61 controller->Connect();
67 } 62 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 63 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode.GetValue()) { 64 !Settings::values.use_docked_mode.GetValue()) {
70 // We should *never* reach here under any normal circumstances. 65 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), 66 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
72 index); 67 controller->Connect();
73 } else { 68 } else {
74 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); 69 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
75 } 70 }
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index b0626a0f9..014bc8901 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -8,8 +8,8 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace Service::SM { 11namespace Core::HID {
12class ServiceManager; 12class HIDCore;
13} 13}
14 14
15namespace Core::Frontend { 15namespace Core::Frontend {
@@ -44,14 +44,14 @@ public:
44 44
45class DefaultControllerApplet final : public ControllerApplet { 45class DefaultControllerApplet final : public ControllerApplet {
46public: 46public:
47 explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); 47 explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
48 ~DefaultControllerApplet() override; 48 ~DefaultControllerApplet() override;
49 49
50 void ReconfigureControllers(std::function<void()> callback, 50 void ReconfigureControllers(std::function<void()> callback,
51 const ControllerParameters& parameters) const override; 51 const ControllerParameters& parameters) const override;
52 52
53private: 53private:
54 Service::SM::ServiceManager& service_manager; 54 HID::HIDCore& hid_core;
55}; 55};
56 56
57} // namespace Core::Frontend 57} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index e1f7e5886..57c6ffc43 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -3,87 +3,23 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex> 5#include <mutex>
6#include "common/settings.h"
7#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
8#include "core/frontend/input.h"
9 7
10namespace Core::Frontend { 8namespace Core::Frontend {
11 9
12GraphicsContext::~GraphicsContext() = default; 10GraphicsContext::~GraphicsContext() = default;
13 11
14class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
15 public std::enable_shared_from_this<TouchState> {
16public:
17 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
18 return std::make_unique<Device>(shared_from_this());
19 }
20
21 std::mutex mutex;
22
23 Input::TouchStatus status;
24
25private:
26 class Device : public Input::TouchDevice {
27 public:
28 explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {}
29 Input::TouchStatus GetStatus() const override {
30 if (auto state = touch_state.lock()) {
31 std::lock_guard guard{state->mutex};
32 return state->status;
33 }
34 return {};
35 }
36
37 private:
38 std::weak_ptr<TouchState> touch_state;
39 };
40};
41
42EmuWindow::EmuWindow() { 12EmuWindow::EmuWindow() {
43 // TODO: Find a better place to set this. 13 // TODO: Find a better place to set this.
44 config.min_client_area_size = 14 config.min_client_area_size =
45 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height); 15 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
46 active_config = config; 16 active_config = config;
47 touch_state = std::make_shared<TouchState>();
48 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
49}
50
51EmuWindow::~EmuWindow() {
52 Input::UnregisterFactory<Input::TouchDevice>("emu_window");
53}
54
55/**
56 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
57 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
58 * @param framebuffer_x Framebuffer x-coordinate to check
59 * @param framebuffer_y Framebuffer y-coordinate to check
60 * @return True if the coordinates are within the touchpad, otherwise false
61 */
62static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x,
63 u32 framebuffer_y) {
64 return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
65 framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
66}
67
68std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
69 new_x = std::max(new_x, framebuffer_layout.screen.left);
70 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
71
72 new_y = std::max(new_y, framebuffer_layout.screen.top);
73 new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
74
75 return std::make_pair(new_x, new_y);
76} 17}
77 18
78void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) { 19EmuWindow::~EmuWindow() {}
79 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
80 return;
81 }
82 if (id >= touch_state->status.size()) {
83 return;
84 }
85 20
86 std::lock_guard guard{touch_state->mutex}; 21std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
22 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
87 const float x = 23 const float x =
88 static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / 24 static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
89 static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); 25 static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
@@ -91,31 +27,17 @@ void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
91 static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / 27 static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
92 static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); 28 static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
93 29
94 touch_state->status[id] = std::make_tuple(x, y, true); 30 return std::make_pair(x, y);
95}
96
97void EmuWindow::TouchReleased(size_t id) {
98 if (id >= touch_state->status.size()) {
99 return;
100 }
101 std::lock_guard guard{touch_state->mutex};
102 touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
103} 31}
104 32
105void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) { 33std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
106 if (id >= touch_state->status.size()) { 34 new_x = std::max(new_x, framebuffer_layout.screen.left);
107 return; 35 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
108 }
109
110 if (!std::get<2>(touch_state->status[id])) {
111 return;
112 }
113 36
114 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { 37 new_y = std::max(new_y, framebuffer_layout.screen.top);
115 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); 38 new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
116 }
117 39
118 TouchPressed(framebuffer_x, framebuffer_y, id); 40 return std::make_pair(new_x, new_y);
119} 41}
120 42
121void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) { 43void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 8a86a1d27..e413a520a 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -113,28 +113,6 @@ public:
113 virtual bool IsShown() const = 0; 113 virtual bool IsShown() const = 0;
114 114
115 /** 115 /**
116 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
117 * @param framebuffer_x Framebuffer x-coordinate that was pressed
118 * @param framebuffer_y Framebuffer y-coordinate that was pressed
119 * @param id Touch event ID
120 */
121 void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id);
122
123 /**
124 * Signal that a touch released event has occurred (e.g. mouse click released)
125 * @param id Touch event ID
126 */
127 void TouchReleased(size_t id);
128
129 /**
130 * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
131 * @param framebuffer_x Framebuffer x-coordinate
132 * @param framebuffer_y Framebuffer y-coordinate
133 * @param id Touch event ID
134 */
135 void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id);
136
137 /**
138 * Returns currently active configuration. 116 * Returns currently active configuration.
139 * @note Accesses to the returned object need not be consistent because it may be modified in 117 * @note Accesses to the returned object need not be consistent because it may be modified in
140 * another thread 118 * another thread
@@ -212,6 +190,11 @@ protected:
212 client_area_height = size.second; 190 client_area_height = size.second;
213 } 191 }
214 192
193 /**
194 * Converts a screen postion into the equivalent touchscreen position.
195 */
196 std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
197
215 WindowSystemInfo window_info; 198 WindowSystemInfo window_info;
216 199
217private: 200private:
@@ -237,9 +220,6 @@ private:
237 WindowConfig config; ///< Internal configuration (changes pending for being applied in 220 WindowConfig config; ///< Internal configuration (changes pending for being applied in
238 /// ProcessConfigurationChanges) 221 /// ProcessConfigurationChanges)
239 WindowConfig active_config; ///< Internal active configuration 222 WindowConfig active_config; ///< Internal active configuration
240
241 class TouchState;
242 std::shared_ptr<TouchState> touch_state;
243}; 223};
244 224
245} // namespace Core::Frontend 225} // namespace Core::Frontend
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 4b58b672a..26a5b12aa 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -25,7 +25,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
25 ASSERT(height > 0); 25 ASSERT(height > 0);
26 // The drawing code needs at least somewhat valid values for both screens 26 // The drawing code needs at least somewhat valid values for both screens
27 // so just calculate them both even if the other isn't showing. 27 // so just calculate them both even if the other isn't showing.
28 FramebufferLayout res{width, height, false, {}}; 28 FramebufferLayout res{
29 .width = width,
30 .height = height,
31 .screen = {},
32 .is_srgb = false,
33 };
29 34
30 const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); 35 const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
31 const float emulation_aspect_ratio = EmulationAspectRatio( 36 const float emulation_aspect_ratio = EmulationAspectRatio(
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 2e36c0163..8e341e4e2 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -35,17 +35,8 @@ enum class AspectRatio {
35struct FramebufferLayout { 35struct FramebufferLayout {
36 u32 width{ScreenUndocked::Width}; 36 u32 width{ScreenUndocked::Width};
37 u32 height{ScreenUndocked::Height}; 37 u32 height{ScreenUndocked::Height};
38 bool is_srgb{};
39
40 Common::Rectangle<u32> screen; 38 Common::Rectangle<u32> screen;
41 39 bool is_srgb{};
42 /**
43 * Returns the ration of pixel size of the screen, compared to the native size of the undocked
44 * Switch screen.
45 */
46 float GetScalingRatio() const {
47 return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
48 }
49}; 40};
50 41
51/** 42/**
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
deleted file mode 100644
index f1747c5b2..000000000
--- a/src/core/frontend/input.h
+++ /dev/null
@@ -1,217 +0,0 @@
1// Copyright 2017 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 <functional>
8#include <memory>
9#include <string>
10#include <tuple>
11#include <unordered_map>
12#include <utility>
13#include "common/logging/log.h"
14#include "common/param_package.h"
15#include "common/quaternion.h"
16#include "common/vector_math.h"
17
18namespace Input {
19
20enum class AnalogDirection : u8 {
21 RIGHT,
22 LEFT,
23 UP,
24 DOWN,
25};
26struct AnalogProperties {
27 float deadzone;
28 float range;
29 float threshold;
30};
31template <typename StatusType>
32struct InputCallback {
33 std::function<void(StatusType)> on_change;
34};
35
36/// An abstract class template for an input device (a button, an analog input, etc.).
37template <typename StatusType>
38class InputDevice {
39public:
40 virtual ~InputDevice() = default;
41 virtual StatusType GetStatus() const {
42 return {};
43 }
44 virtual StatusType GetRawStatus() const {
45 return GetStatus();
46 }
47 virtual AnalogProperties GetAnalogProperties() const {
48 return {};
49 }
50 virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
51 return {};
52 }
53 virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
54 [[maybe_unused]] f32 amp_high,
55 [[maybe_unused]] f32 freq_high) const {
56 return {};
57 }
58 void SetCallback(InputCallback<StatusType> callback_) {
59 callback = std::move(callback_);
60 }
61 void TriggerOnChange() {
62 if (callback.on_change) {
63 callback.on_change(GetStatus());
64 }
65 }
66
67private:
68 InputCallback<StatusType> callback;
69};
70
71/// An abstract class template for a factory that can create input devices.
72template <typename InputDeviceType>
73class Factory {
74public:
75 virtual ~Factory() = default;
76 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
77};
78
79namespace Impl {
80
81template <typename InputDeviceType>
82using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
83
84template <typename InputDeviceType>
85struct FactoryList {
86 static FactoryListType<InputDeviceType> list;
87};
88
89template <typename InputDeviceType>
90FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
91
92} // namespace Impl
93
94/**
95 * Registers an input device factory.
96 * @tparam InputDeviceType the type of input devices the factory can create
97 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
98 * a device
99 * @param factory the factory object to register
100 */
101template <typename InputDeviceType>
102void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
103 auto pair = std::make_pair(name, std::move(factory));
104 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
105 LOG_ERROR(Input, "Factory '{}' already registered", name);
106 }
107}
108
109/**
110 * Unregisters an input device factory.
111 * @tparam InputDeviceType the type of input devices the factory can create
112 * @param name the name of the factory to unregister
113 */
114template <typename InputDeviceType>
115void UnregisterFactory(const std::string& name) {
116 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
117 LOG_ERROR(Input, "Factory '{}' not registered", name);
118 }
119}
120
121/**
122 * Create an input device from given paramters.
123 * @tparam InputDeviceType the type of input devices to create
124 * @param params a serialized ParamPackage string contains all parameters for creating the device
125 */
126template <typename InputDeviceType>
127std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
128 const Common::ParamPackage package(params);
129 const std::string engine = package.Get("engine", "null");
130 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
131 const auto pair = factory_list.find(engine);
132 if (pair == factory_list.end()) {
133 if (engine != "null") {
134 LOG_ERROR(Input, "Unknown engine name: {}", engine);
135 }
136 return std::make_unique<InputDeviceType>();
137 }
138 return pair->second->Create(package);
139}
140
141/**
142 * A button device is an input device that returns bool as status.
143 * true for pressed; false for released.
144 */
145using ButtonDevice = InputDevice<bool>;
146
147/**
148 * An analog device is an input device that returns a tuple of x and y coordinates as status. The
149 * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
150 * direction
151 */
152using AnalogDevice = InputDevice<std::tuple<float, float>>;
153
154/**
155 * A vibration device is an input device that returns an unsigned byte as status.
156 * It represents whether the vibration device supports vibration or not.
157 * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
158 */
159using VibrationDevice = InputDevice<u8>;
160
161/**
162 * A motion status is an object that returns a tuple of accelerometer state vector,
163 * gyroscope state vector, rotation state vector, orientation state matrix and quaterion state
164 * vector.
165 *
166 * For both 3D vectors:
167 * x+ is the same direction as RIGHT on D-pad.
168 * y+ is normal to the touch screen, pointing outward.
169 * z+ is the same direction as UP on D-pad.
170 *
171 * For accelerometer state vector
172 * Units: g (gravitational acceleration)
173 *
174 * For gyroscope state vector:
175 * Orientation is determined by right-hand rule.
176 * Units: deg/sec
177 *
178 * For rotation state vector
179 * Units: rotations
180 *
181 * For orientation state matrix
182 * x vector
183 * y vector
184 * z vector
185 *
186 * For quaternion state vector
187 * xyz vector
188 * w float
189 */
190using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
191 std::array<Common::Vec3f, 3>, Common::Quaternion<f32>>;
192
193/**
194 * A motion device is an input device that returns a motion status object
195 */
196using MotionDevice = InputDevice<MotionStatus>;
197
198/**
199 * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
200 * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
201 * pressed.
202 */
203using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
204
205/**
206 * A touch device is an input device that returns a touch status object
207 */
208using TouchDevice = InputDevice<TouchStatus>;
209
210/**
211 * A mouse device is an input device that returns a tuple of two floats and four ints.
212 * The first two floats are X and Y device coordinates of the mouse (from 0-1).
213 * The s32s are the mouse wheel.
214 */
215using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
216
217} // namespace Input
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
new file mode 100644
index 000000000..80db8e9c6
--- /dev/null
+++ b/src/core/hid/emulated_console.cpp
@@ -0,0 +1,229 @@
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/settings.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/input_converter.h"
8
9namespace Core::HID {
10EmulatedConsole::EmulatedConsole() = default;
11
12EmulatedConsole::~EmulatedConsole() = default;
13
14void EmulatedConsole::ReloadFromSettings() {
15 // Using first motion device from player 1. No need to assign any unique config at the moment
16 const auto& player = Settings::values.players.GetValue()[0];
17 motion_params = Common::ParamPackage(player.motions[0]);
18
19 ReloadInput();
20}
21
22void EmulatedConsole::SetTouchParams() {
23 // TODO(german77): Support any number of fingers
24 std::size_t index = 0;
25
26 // Hardcode mouse, touchscreen and cemuhook parameters
27 if (!Settings::values.mouse_enabled) {
28 // We can't use mouse as touch if native mouse is enabled
29 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
30 }
31 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
32 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
33 touch_params[index++] =
34 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
35 touch_params[index++] =
36 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
37
38 const auto button_index =
39 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
40 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
41
42 // Map the rest of the fingers from touch from button configuration
43 for (const auto& config_entry : touch_buttons) {
44 if (index >= touch_params.size()) {
45 continue;
46 }
47 Common::ParamPackage params{config_entry};
48 Common::ParamPackage touch_button_params;
49 const int x = params.Get("x", 0);
50 const int y = params.Get("y", 0);
51 params.Erase("x");
52 params.Erase("y");
53 touch_button_params.Set("engine", "touch_from_button");
54 touch_button_params.Set("button", params.Serialize());
55 touch_button_params.Set("x", x);
56 touch_button_params.Set("y", y);
57 touch_button_params.Set("touch_id", static_cast<int>(index));
58 touch_params[index] = touch_button_params;
59 index++;
60 }
61}
62
63void EmulatedConsole::ReloadInput() {
64 // If you load any device here add the equivalent to the UnloadInput() function
65 SetTouchParams();
66
67 motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
68 if (motion_devices) {
69 Common::Input::InputCallback motion_callback{
70 [this](Common::Input::CallbackStatus callback) { SetMotion(callback); }};
71 motion_devices->SetCallback(motion_callback);
72 }
73
74 // Unique index for identifying touch device source
75 std::size_t index = 0;
76 for (auto& touch_device : touch_devices) {
77 touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
78 if (!touch_device) {
79 continue;
80 }
81 Common::Input::InputCallback touch_callback{
82 [this, index](Common::Input::CallbackStatus callback) { SetTouch(callback, index); }};
83 touch_device->SetCallback(touch_callback);
84 index++;
85 }
86}
87
88void EmulatedConsole::UnloadInput() {
89 motion_devices.reset();
90 for (auto& touch : touch_devices) {
91 touch.reset();
92 }
93}
94
95void EmulatedConsole::EnableConfiguration() {
96 is_configuring = true;
97 SaveCurrentConfig();
98}
99
100void EmulatedConsole::DisableConfiguration() {
101 is_configuring = false;
102}
103
104bool EmulatedConsole::IsConfiguring() const {
105 return is_configuring;
106}
107
108void EmulatedConsole::SaveCurrentConfig() {
109 if (!is_configuring) {
110 return;
111 }
112}
113
114void EmulatedConsole::RestoreConfig() {
115 if (!is_configuring) {
116 return;
117 }
118 ReloadFromSettings();
119}
120
121Common::ParamPackage EmulatedConsole::GetMotionParam() const {
122 return motion_params;
123}
124
125void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
126 motion_params = param;
127 ReloadInput();
128}
129
130void EmulatedConsole::SetMotion(Common::Input::CallbackStatus callback) {
131 std::lock_guard lock{mutex};
132 auto& raw_status = console.motion_values.raw_status;
133 auto& emulated = console.motion_values.emulated;
134
135 raw_status = TransformToMotion(callback);
136 emulated.SetAcceleration(Common::Vec3f{
137 raw_status.accel.x.value,
138 raw_status.accel.y.value,
139 raw_status.accel.z.value,
140 });
141 emulated.SetGyroscope(Common::Vec3f{
142 raw_status.gyro.x.value,
143 raw_status.gyro.y.value,
144 raw_status.gyro.z.value,
145 });
146 emulated.UpdateRotation(raw_status.delta_timestamp);
147 emulated.UpdateOrientation(raw_status.delta_timestamp);
148
149 if (is_configuring) {
150 TriggerOnChange(ConsoleTriggerType::Motion);
151 return;
152 }
153
154 auto& motion = console.motion_state;
155 motion.accel = emulated.GetAcceleration();
156 motion.gyro = emulated.GetGyroscope();
157 motion.rotation = emulated.GetGyroscope();
158 motion.orientation = emulated.GetOrientation();
159 motion.quaternion = emulated.GetQuaternion();
160 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
161
162 TriggerOnChange(ConsoleTriggerType::Motion);
163}
164
165void EmulatedConsole::SetTouch(Common::Input::CallbackStatus callback,
166 [[maybe_unused]] std::size_t index) {
167 if (index >= console.touch_values.size()) {
168 return;
169 }
170 std::lock_guard lock{mutex};
171
172 console.touch_values[index] = TransformToTouch(callback);
173
174 if (is_configuring) {
175 TriggerOnChange(ConsoleTriggerType::Touch);
176 return;
177 }
178
179 // TODO(german77): Remap touch id in sequential order
180 console.touch_state[index] = {
181 .position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
182 .id = static_cast<u32>(console.touch_values[index].id),
183 .pressed = console.touch_values[index].pressed.value,
184 };
185
186 TriggerOnChange(ConsoleTriggerType::Touch);
187}
188
189ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
190 return console.motion_values;
191}
192
193TouchValues EmulatedConsole::GetTouchValues() const {
194 return console.touch_values;
195}
196
197ConsoleMotion EmulatedConsole::GetMotion() const {
198 return console.motion_state;
199}
200
201TouchFingerState EmulatedConsole::GetTouch() const {
202 return console.touch_state;
203}
204
205void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
206 for (const auto& poller_pair : callback_list) {
207 const ConsoleUpdateCallback& poller = poller_pair.second;
208 if (poller.on_change) {
209 poller.on_change(type);
210 }
211 }
212}
213
214int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
215 std::lock_guard lock{mutex};
216 callback_list.insert_or_assign(last_callback_key, update_callback);
217 return last_callback_key++;
218}
219
220void EmulatedConsole::DeleteCallback(int key) {
221 std::lock_guard lock{mutex};
222 const auto& iterator = callback_list.find(key);
223 if (iterator == callback_list.end()) {
224 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
225 return;
226 }
227 callback_list.erase(iterator);
228}
229} // 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..25c183eee
--- /dev/null
+++ b/src/core/hid/emulated_console.h
@@ -0,0 +1,188 @@
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 <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/vector_math.h"
19#include "core/hid/hid_types.h"
20#include "core/hid/motion_input.h"
21
22namespace Core::HID {
23
24struct ConsoleMotionInfo {
25 Common::Input::MotionStatus raw_status{};
26 MotionInput emulated{};
27};
28
29using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
30using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
31
32using ConsoleMotionParams = Common::ParamPackage;
33using TouchParams = std::array<Common::ParamPackage, 16>;
34
35using ConsoleMotionValues = ConsoleMotionInfo;
36using TouchValues = std::array<Common::Input::TouchStatus, 16>;
37
38struct TouchFinger {
39 u64 last_touch{};
40 Common::Point<float> position{};
41 u32 id{};
42 TouchAttribute attribute{};
43 bool pressed{};
44};
45
46// Contains all motion related data that is used on the services
47struct ConsoleMotion {
48 Common::Vec3f accel{};
49 Common::Vec3f gyro{};
50 Common::Vec3f rotation{};
51 std::array<Common::Vec3f, 3> orientation{};
52 Common::Quaternion<f32> quaternion{};
53 bool is_at_rest{};
54};
55
56using TouchFingerState = std::array<TouchFinger, 16>;
57
58struct ConsoleStatus {
59 // Data from input_common
60 ConsoleMotionValues motion_values{};
61 TouchValues touch_values{};
62
63 // Data for HID services
64 ConsoleMotion motion_state{};
65 TouchFingerState touch_state{};
66};
67
68enum class ConsoleTriggerType {
69 Motion,
70 Touch,
71 All,
72};
73
74struct ConsoleUpdateCallback {
75 std::function<void(ConsoleTriggerType)> on_change;
76};
77
78class EmulatedConsole {
79public:
80 /**
81 * Contains all input data related to the console like motion and touch input
82 */
83 EmulatedConsole();
84 ~EmulatedConsole();
85
86 YUZU_NON_COPYABLE(EmulatedConsole);
87 YUZU_NON_MOVEABLE(EmulatedConsole);
88
89 /// Removes all callbacks created from input devices
90 void UnloadInput();
91
92 /// Sets the emulated console into configuring mode. Locking all HID service events from being
93 /// moddified
94 void EnableConfiguration();
95
96 /// Returns the emulated console to the normal behaivour
97 void DisableConfiguration();
98
99 /// Returns true if the emulated console is on configuring mode
100 bool IsConfiguring() const;
101
102 /// Reload all input devices
103 void ReloadInput();
104
105 /// Overrides current mapped devices with the stored configuration and reloads all input devices
106 void ReloadFromSettings();
107
108 /// Saves the current mapped configuration
109 void SaveCurrentConfig();
110
111 /// Reverts any mapped changes made that weren't saved
112 void RestoreConfig();
113
114 // Returns the current mapped motion device
115 Common::ParamPackage GetMotionParam() const;
116
117 /**
118 * Updates the current mapped motion device
119 * @param ParamPackage with controller data to be mapped
120 */
121 void SetMotionParam(Common::ParamPackage param);
122
123 /// Returns the latest status of motion input from the console with parameters
124 ConsoleMotionValues GetMotionValues() const;
125
126 /// Returns the latest status of touch input from the console with parameters
127 TouchValues GetTouchValues() const;
128
129 /// Returns the latest status of motion input from the console
130 ConsoleMotion GetMotion() const;
131
132 /// Returns the latest status of touch input from the console
133 TouchFingerState GetTouch() const;
134
135 /**
136 * Adds a callback to the list of events
137 * @param ConsoleUpdateCallback that will be triggered
138 * @return an unique key corresponding to the callback index in the list
139 */
140 int SetCallback(ConsoleUpdateCallback update_callback);
141
142 /**
143 * Removes a callback from the list stopping any future events to this object
144 * @param Key corresponding to the callback index in the list
145 */
146 void DeleteCallback(int key);
147
148private:
149 /// Creates and stores the touch params
150 void SetTouchParams();
151
152 /**
153 * Updates the motion status of the console
154 * @param A CallbackStatus containing gyro and accelerometer data
155 */
156 void SetMotion(Common::Input::CallbackStatus callback);
157
158 /**
159 * Updates the touch status of the console
160 * @param callback: A CallbackStatus containing the touch position
161 * @param index: Finger ID to be updated
162 */
163 void SetTouch(Common::Input::CallbackStatus callback, std::size_t index);
164
165 /**
166 * Triggers a callback that something has changed on the console status
167 * @param Input type of the event to trigger
168 */
169 void TriggerOnChange(ConsoleTriggerType type);
170
171 bool is_configuring{false};
172 f32 motion_sensitivity{0.01f};
173
174 ConsoleMotionParams motion_params;
175 TouchParams touch_params;
176
177 ConsoleMotionDevices motion_devices;
178 TouchDevices touch_devices;
179
180 mutable std::mutex mutex;
181 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
182 int last_callback_key = 0;
183
184 // Stores the current status of all console input
185 ConsoleStatus console;
186};
187
188} // 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..466ff5542
--- /dev/null
+++ b/src/core/hid/emulated_controller.cpp
@@ -0,0 +1,1081 @@
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 "core/hid/emulated_controller.h"
6#include "core/hid/input_converter.h"
7
8namespace Core::HID {
9constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
10constexpr s32 HID_TRIGGER_MAX = 0x7fff;
11
12EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
13
14EmulatedController::~EmulatedController() = default;
15
16NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
17 switch (type) {
18 case Settings::ControllerType::ProController:
19 return NpadStyleIndex::ProController;
20 case Settings::ControllerType::DualJoyconDetached:
21 return NpadStyleIndex::JoyconDual;
22 case Settings::ControllerType::LeftJoycon:
23 return NpadStyleIndex::JoyconLeft;
24 case Settings::ControllerType::RightJoycon:
25 return NpadStyleIndex::JoyconRight;
26 case Settings::ControllerType::Handheld:
27 return NpadStyleIndex::Handheld;
28 case Settings::ControllerType::GameCube:
29 return NpadStyleIndex::GameCube;
30 case Settings::ControllerType::Pokeball:
31 return NpadStyleIndex::Pokeball;
32 case Settings::ControllerType::NES:
33 return NpadStyleIndex::NES;
34 case Settings::ControllerType::SNES:
35 return NpadStyleIndex::SNES;
36 case Settings::ControllerType::N64:
37 return NpadStyleIndex::N64;
38 case Settings::ControllerType::SegaGenesis:
39 return NpadStyleIndex::SegaGenesis;
40 default:
41 return NpadStyleIndex::ProController;
42 }
43}
44
45Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
46 switch (type) {
47 case NpadStyleIndex::ProController:
48 return Settings::ControllerType::ProController;
49 case NpadStyleIndex::JoyconDual:
50 return Settings::ControllerType::DualJoyconDetached;
51 case NpadStyleIndex::JoyconLeft:
52 return Settings::ControllerType::LeftJoycon;
53 case NpadStyleIndex::JoyconRight:
54 return Settings::ControllerType::RightJoycon;
55 case NpadStyleIndex::Handheld:
56 return Settings::ControllerType::Handheld;
57 case NpadStyleIndex::GameCube:
58 return Settings::ControllerType::GameCube;
59 case NpadStyleIndex::Pokeball:
60 return Settings::ControllerType::Pokeball;
61 case NpadStyleIndex::NES:
62 return Settings::ControllerType::NES;
63 case NpadStyleIndex::SNES:
64 return Settings::ControllerType::SNES;
65 case NpadStyleIndex::N64:
66 return Settings::ControllerType::N64;
67 case NpadStyleIndex::SegaGenesis:
68 return Settings::ControllerType::SegaGenesis;
69 default:
70 return Settings::ControllerType::ProController;
71 }
72}
73
74void EmulatedController::ReloadFromSettings() {
75 const auto player_index = NpadIdTypeToIndex(npad_id_type);
76 const auto& player = Settings::values.players.GetValue()[player_index];
77
78 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
79 button_params[index] = Common::ParamPackage(player.buttons[index]);
80 }
81 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
82 stick_params[index] = Common::ParamPackage(player.analogs[index]);
83 }
84 for (std::size_t index = 0; index < player.motions.size(); ++index) {
85 motion_params[index] = Common::ParamPackage(player.motions[index]);
86 }
87
88 controller.colors_state.left = {
89 .body = player.body_color_left,
90 .button = player.button_color_left,
91 };
92
93 controller.colors_state.right = {
94 .body = player.body_color_right,
95 .button = player.button_color_right,
96 };
97
98 controller.colors_state.fullkey = controller.colors_state.left;
99
100 // Other or debug controller should always be a pro controller
101 if (npad_id_type != NpadIdType::Other) {
102 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
103 } else {
104 SetNpadStyleIndex(NpadStyleIndex::ProController);
105 }
106
107 if (player.connected) {
108 Connect();
109 } else {
110 Disconnect();
111 }
112
113 ReloadInput();
114}
115
116void EmulatedController::LoadDevices() {
117 // TODO(german77): Use more buttons to detect the correct device
118 const auto left_joycon = button_params[Settings::NativeButton::DRight];
119 const auto right_joycon = button_params[Settings::NativeButton::A];
120
121 // Triggers for GC controllers
122 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
123 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
124
125 battery_params[LeftIndex] = left_joycon;
126 battery_params[RightIndex] = right_joycon;
127 battery_params[LeftIndex].Set("battery", true);
128 battery_params[RightIndex].Set("battery", true);
129
130 output_params[LeftIndex] = left_joycon;
131 output_params[RightIndex] = right_joycon;
132 output_params[LeftIndex].Set("output", true);
133 output_params[RightIndex].Set("output", true);
134
135 LoadTASParams();
136
137 std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
138 button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
139 button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
140 std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
141 stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
142 stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
143 std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
144 motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
145 motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
146 std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
147 Common::Input::CreateDevice<Common::Input::InputDevice>);
148 std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
149 Common::Input::CreateDevice<Common::Input::InputDevice>);
150 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
151 Common::Input::CreateDevice<Common::Input::OutputDevice>);
152
153 // Initialize TAS devices
154 std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
155 Common::Input::CreateDevice<Common::Input::InputDevice>);
156 std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
157 Common::Input::CreateDevice<Common::Input::InputDevice>);
158}
159
160void EmulatedController::LoadTASParams() {
161 const auto player_index = NpadIdTypeToIndex(npad_id_type);
162 Common::ParamPackage common_params{};
163 common_params.Set("engine", "tas");
164 common_params.Set("port", static_cast<int>(player_index));
165 for (auto& param : tas_button_params) {
166 param = common_params;
167 }
168 for (auto& param : tas_stick_params) {
169 param = common_params;
170 }
171
172 // TODO(german77): Replace this with an input profile or something better
173 tas_button_params[Settings::NativeButton::A].Set("button", 0);
174 tas_button_params[Settings::NativeButton::B].Set("button", 1);
175 tas_button_params[Settings::NativeButton::X].Set("button", 2);
176 tas_button_params[Settings::NativeButton::Y].Set("button", 3);
177 tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
178 tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
179 tas_button_params[Settings::NativeButton::L].Set("button", 6);
180 tas_button_params[Settings::NativeButton::R].Set("button", 7);
181 tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
182 tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
183 tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
184 tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
185 tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
186 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
187 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
188 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
189 tas_button_params[Settings::NativeButton::SL].Set("button", 16);
190 tas_button_params[Settings::NativeButton::SR].Set("button", 17);
191 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
192 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
193
194 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
195 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
196 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
197 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
198}
199
200void EmulatedController::ReloadInput() {
201 // If you load any device here add the equivalent to the UnloadInput() function
202 LoadDevices();
203 for (std::size_t index = 0; index < button_devices.size(); ++index) {
204 if (!button_devices[index]) {
205 continue;
206 }
207 const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
208 Common::Input::InputCallback button_callback{
209 [this, index, uuid](Common::Input::CallbackStatus callback) {
210 SetButton(callback, index, uuid);
211 }};
212 button_devices[index]->SetCallback(button_callback);
213 button_devices[index]->ForceUpdate();
214 }
215
216 for (std::size_t index = 0; index < stick_devices.size(); ++index) {
217 if (!stick_devices[index]) {
218 continue;
219 }
220 const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
221 Common::Input::InputCallback stick_callback{
222 [this, index, uuid](Common::Input::CallbackStatus callback) {
223 SetStick(callback, index, uuid);
224 }};
225 stick_devices[index]->SetCallback(stick_callback);
226 stick_devices[index]->ForceUpdate();
227 }
228
229 for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
230 if (!trigger_devices[index]) {
231 continue;
232 }
233 const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
234 Common::Input::InputCallback trigger_callback{
235 [this, index, uuid](Common::Input::CallbackStatus callback) {
236 SetTrigger(callback, index, uuid);
237 }};
238 trigger_devices[index]->SetCallback(trigger_callback);
239 trigger_devices[index]->ForceUpdate();
240 }
241
242 for (std::size_t index = 0; index < battery_devices.size(); ++index) {
243 if (!battery_devices[index]) {
244 continue;
245 }
246 Common::Input::InputCallback battery_callback{
247 [this, index](Common::Input::CallbackStatus callback) { SetBattery(callback, index); }};
248 battery_devices[index]->SetCallback(battery_callback);
249 battery_devices[index]->ForceUpdate();
250 }
251
252 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
253 if (!motion_devices[index]) {
254 continue;
255 }
256 Common::Input::InputCallback motion_callback{
257 [this, index](Common::Input::CallbackStatus callback) { SetMotion(callback, index); }};
258 motion_devices[index]->SetCallback(motion_callback);
259 motion_devices[index]->ForceUpdate();
260 }
261
262 // Use a common UUID for TAS
263 const auto tas_uuid = Common::UUID{0x0, 0x7A5};
264
265 // Register TAS devices. No need to force update
266 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
267 if (!tas_button_devices[index]) {
268 continue;
269 }
270 Common::Input::InputCallback button_callback{
271 [this, index, tas_uuid](Common::Input::CallbackStatus callback) {
272 SetButton(callback, index, tas_uuid);
273 }};
274 tas_button_devices[index]->SetCallback(button_callback);
275 }
276
277 for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
278 if (!tas_stick_devices[index]) {
279 continue;
280 }
281 Common::Input::InputCallback stick_callback{
282 [this, index, tas_uuid](Common::Input::CallbackStatus callback) {
283 SetStick(callback, index, tas_uuid);
284 }};
285 tas_stick_devices[index]->SetCallback(stick_callback);
286 }
287}
288
289void EmulatedController::UnloadInput() {
290 for (auto& button : button_devices) {
291 button.reset();
292 }
293 for (auto& stick : stick_devices) {
294 stick.reset();
295 }
296 for (auto& motion : motion_devices) {
297 motion.reset();
298 }
299 for (auto& trigger : trigger_devices) {
300 trigger.reset();
301 }
302 for (auto& battery : battery_devices) {
303 battery.reset();
304 }
305 for (auto& output : output_devices) {
306 output.reset();
307 }
308 for (auto& button : tas_button_devices) {
309 button.reset();
310 }
311 for (auto& stick : tas_stick_devices) {
312 stick.reset();
313 }
314}
315
316void EmulatedController::EnableConfiguration() {
317 is_configuring = true;
318 tmp_is_connected = is_connected;
319 tmp_npad_type = npad_type;
320}
321
322void EmulatedController::DisableConfiguration() {
323 is_configuring = false;
324
325 // Apply temporary npad type to the real controller
326 if (tmp_npad_type != npad_type) {
327 if (is_connected) {
328 Disconnect();
329 }
330 SetNpadStyleIndex(tmp_npad_type);
331 }
332
333 // Apply temporary connected status to the real controller
334 if (tmp_is_connected != is_connected) {
335 if (tmp_is_connected) {
336 Connect();
337 return;
338 }
339 Disconnect();
340 }
341}
342
343bool EmulatedController::IsConfiguring() const {
344 return is_configuring;
345}
346
347void EmulatedController::SaveCurrentConfig() {
348 const auto player_index = NpadIdTypeToIndex(npad_id_type);
349 auto& player = Settings::values.players.GetValue()[player_index];
350 player.connected = is_connected;
351 player.controller_type = MapNPadToSettingsType(npad_type);
352 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
353 player.buttons[index] = button_params[index].Serialize();
354 }
355 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
356 player.analogs[index] = stick_params[index].Serialize();
357 }
358 for (std::size_t index = 0; index < player.motions.size(); ++index) {
359 player.motions[index] = motion_params[index].Serialize();
360 }
361}
362
363void EmulatedController::RestoreConfig() {
364 if (!is_configuring) {
365 return;
366 }
367 ReloadFromSettings();
368}
369
370std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
371 EmulatedDeviceIndex device_index) const {
372 std::vector<Common::ParamPackage> devices;
373 for (const auto& param : button_params) {
374 if (!param.Has("engine")) {
375 continue;
376 }
377 const auto devices_it = std::find_if(
378 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
379 return param.Get("engine", "") == param_.Get("engine", "") &&
380 param.Get("guid", "") == param_.Get("guid", "") &&
381 param.Get("port", 0) == param_.Get("port", 0);
382 });
383 if (devices_it != devices.end()) {
384 continue;
385 }
386 Common::ParamPackage device{};
387 device.Set("engine", param.Get("engine", ""));
388 device.Set("guid", param.Get("guid", ""));
389 device.Set("port", param.Get("port", 0));
390 devices.push_back(device);
391 }
392
393 for (const auto& param : stick_params) {
394 if (!param.Has("engine")) {
395 continue;
396 }
397 if (param.Get("engine", "") == "analog_from_button") {
398 continue;
399 }
400 const auto devices_it = std::find_if(
401 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
402 return param.Get("engine", "") == param_.Get("engine", "") &&
403 param.Get("guid", "") == param_.Get("guid", "") &&
404 param.Get("port", 0) == param_.Get("port", 0);
405 });
406 if (devices_it != devices.end()) {
407 continue;
408 }
409 Common::ParamPackage device{};
410 device.Set("engine", param.Get("engine", ""));
411 device.Set("guid", param.Get("guid", ""));
412 device.Set("port", param.Get("port", 0));
413 devices.push_back(device);
414 }
415 return devices;
416}
417
418Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
419 if (index >= button_params.size()) {
420 return {};
421 }
422 return button_params[index];
423}
424
425Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
426 if (index >= stick_params.size()) {
427 return {};
428 }
429 return stick_params[index];
430}
431
432Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
433 if (index >= motion_params.size()) {
434 return {};
435 }
436 return motion_params[index];
437}
438
439void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
440 if (index >= button_params.size()) {
441 return;
442 }
443 button_params[index] = param;
444 ReloadInput();
445}
446
447void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
448 if (index >= stick_params.size()) {
449 return;
450 }
451 stick_params[index] = param;
452 ReloadInput();
453}
454
455void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
456 if (index >= motion_params.size()) {
457 return;
458 }
459 motion_params[index] = param;
460 ReloadInput();
461}
462
463void EmulatedController::SetButton(Common::Input::CallbackStatus callback, std::size_t index,
464 Common::UUID uuid) {
465 if (index >= controller.button_values.size()) {
466 return;
467 }
468 {
469 std::lock_guard lock{mutex};
470 bool value_changed = false;
471 const auto new_status = TransformToButton(callback);
472 auto& current_status = controller.button_values[index];
473
474 // Only read button values that have the same uuid or are pressed once
475 if (current_status.uuid != uuid) {
476 if (!new_status.value) {
477 return;
478 }
479 }
480
481 current_status.toggle = new_status.toggle;
482 current_status.uuid = uuid;
483
484 // Update button status with current
485 if (!current_status.toggle) {
486 current_status.locked = false;
487 if (current_status.value != new_status.value) {
488 current_status.value = new_status.value;
489 value_changed = true;
490 }
491 } else {
492 // Toggle button and lock status
493 if (new_status.value && !current_status.locked) {
494 current_status.locked = true;
495 current_status.value = !current_status.value;
496 value_changed = true;
497 }
498
499 // Unlock button ready for next press
500 if (!new_status.value && current_status.locked) {
501 current_status.locked = false;
502 }
503 }
504
505 if (!value_changed) {
506 return;
507 }
508
509 if (is_configuring) {
510 controller.npad_button_state.raw = NpadButton::None;
511 controller.debug_pad_button_state.raw = 0;
512 TriggerOnChange(ControllerTriggerType::Button, false);
513 return;
514 }
515
516 switch (index) {
517 case Settings::NativeButton::A:
518 controller.npad_button_state.a.Assign(current_status.value);
519 controller.debug_pad_button_state.a.Assign(current_status.value);
520 break;
521 case Settings::NativeButton::B:
522 controller.npad_button_state.b.Assign(current_status.value);
523 controller.debug_pad_button_state.b.Assign(current_status.value);
524 break;
525 case Settings::NativeButton::X:
526 controller.npad_button_state.x.Assign(current_status.value);
527 controller.debug_pad_button_state.x.Assign(current_status.value);
528 break;
529 case Settings::NativeButton::Y:
530 controller.npad_button_state.y.Assign(current_status.value);
531 controller.debug_pad_button_state.y.Assign(current_status.value);
532 break;
533 case Settings::NativeButton::LStick:
534 controller.npad_button_state.stick_l.Assign(current_status.value);
535 break;
536 case Settings::NativeButton::RStick:
537 controller.npad_button_state.stick_r.Assign(current_status.value);
538 break;
539 case Settings::NativeButton::L:
540 controller.npad_button_state.l.Assign(current_status.value);
541 controller.debug_pad_button_state.l.Assign(current_status.value);
542 break;
543 case Settings::NativeButton::R:
544 controller.npad_button_state.r.Assign(current_status.value);
545 controller.debug_pad_button_state.r.Assign(current_status.value);
546 break;
547 case Settings::NativeButton::ZL:
548 controller.npad_button_state.zl.Assign(current_status.value);
549 controller.debug_pad_button_state.zl.Assign(current_status.value);
550 break;
551 case Settings::NativeButton::ZR:
552 controller.npad_button_state.zr.Assign(current_status.value);
553 controller.debug_pad_button_state.zr.Assign(current_status.value);
554 break;
555 case Settings::NativeButton::Plus:
556 controller.npad_button_state.plus.Assign(current_status.value);
557 controller.debug_pad_button_state.plus.Assign(current_status.value);
558 break;
559 case Settings::NativeButton::Minus:
560 controller.npad_button_state.minus.Assign(current_status.value);
561 controller.debug_pad_button_state.minus.Assign(current_status.value);
562 break;
563 case Settings::NativeButton::DLeft:
564 controller.npad_button_state.left.Assign(current_status.value);
565 controller.debug_pad_button_state.d_left.Assign(current_status.value);
566 break;
567 case Settings::NativeButton::DUp:
568 controller.npad_button_state.up.Assign(current_status.value);
569 controller.debug_pad_button_state.d_up.Assign(current_status.value);
570 break;
571 case Settings::NativeButton::DRight:
572 controller.npad_button_state.right.Assign(current_status.value);
573 controller.debug_pad_button_state.d_right.Assign(current_status.value);
574 break;
575 case Settings::NativeButton::DDown:
576 controller.npad_button_state.down.Assign(current_status.value);
577 controller.debug_pad_button_state.d_down.Assign(current_status.value);
578 break;
579 case Settings::NativeButton::SL:
580 controller.npad_button_state.left_sl.Assign(current_status.value);
581 controller.npad_button_state.right_sl.Assign(current_status.value);
582 break;
583 case Settings::NativeButton::SR:
584 controller.npad_button_state.left_sr.Assign(current_status.value);
585 controller.npad_button_state.right_sr.Assign(current_status.value);
586 break;
587 case Settings::NativeButton::Home:
588 case Settings::NativeButton::Screenshot:
589 break;
590 }
591 }
592 if (!is_connected) {
593 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
594 Connect();
595 }
596 if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
597 Connect();
598 }
599 }
600 TriggerOnChange(ControllerTriggerType::Button, true);
601}
602
603void EmulatedController::SetStick(Common::Input::CallbackStatus callback, std::size_t index,
604 Common::UUID uuid) {
605 if (index >= controller.stick_values.size()) {
606 return;
607 }
608 std::lock_guard lock{mutex};
609 const auto stick_value = TransformToStick(callback);
610
611 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
612 if (controller.stick_values[index].uuid != uuid) {
613 if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) {
614 return;
615 }
616 }
617
618 controller.stick_values[index] = stick_value;
619 controller.stick_values[index].uuid = uuid;
620
621 if (is_configuring) {
622 controller.analog_stick_state.left = {};
623 controller.analog_stick_state.right = {};
624 TriggerOnChange(ControllerTriggerType::Stick, false);
625 return;
626 }
627
628 const AnalogStickState stick{
629 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
630 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
631 };
632
633 switch (index) {
634 case Settings::NativeAnalog::LStick:
635 controller.analog_stick_state.left = stick;
636 controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
637 controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
638 controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
639 controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
640 break;
641 case Settings::NativeAnalog::RStick:
642 controller.analog_stick_state.right = stick;
643 controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
644 controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
645 controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
646 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
647 break;
648 }
649
650 TriggerOnChange(ControllerTriggerType::Stick, true);
651}
652
653void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std::size_t index,
654 Common::UUID uuid) {
655 if (index >= controller.trigger_values.size()) {
656 return;
657 }
658 std::lock_guard lock{mutex};
659 const auto trigger_value = TransformToTrigger(callback);
660
661 // Only read trigger values that have the same uuid or are pressed once
662 if (controller.stick_values[index].uuid != uuid) {
663 if (!trigger_value.pressed.value) {
664 return;
665 }
666 }
667
668 controller.trigger_values[index] = trigger_value;
669 controller.trigger_values[index].uuid = uuid;
670
671 if (is_configuring) {
672 controller.gc_trigger_state.left = 0;
673 controller.gc_trigger_state.right = 0;
674 TriggerOnChange(ControllerTriggerType::Trigger, false);
675 return;
676 }
677
678 const auto trigger = controller.trigger_values[index];
679
680 switch (index) {
681 case Settings::NativeTrigger::LTrigger:
682 controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
683 controller.npad_button_state.zl.Assign(trigger.pressed.value);
684 break;
685 case Settings::NativeTrigger::RTrigger:
686 controller.gc_trigger_state.right =
687 static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
688 controller.npad_button_state.zr.Assign(trigger.pressed.value);
689 break;
690 }
691
692 TriggerOnChange(ControllerTriggerType::Trigger, true);
693}
694
695void EmulatedController::SetMotion(Common::Input::CallbackStatus callback, std::size_t index) {
696 if (index >= controller.motion_values.size()) {
697 return;
698 }
699 std::lock_guard lock{mutex};
700 auto& raw_status = controller.motion_values[index].raw_status;
701 auto& emulated = controller.motion_values[index].emulated;
702
703 raw_status = TransformToMotion(callback);
704 emulated.SetAcceleration(Common::Vec3f{
705 raw_status.accel.x.value,
706 raw_status.accel.y.value,
707 raw_status.accel.z.value,
708 });
709 emulated.SetGyroscope(Common::Vec3f{
710 raw_status.gyro.x.value,
711 raw_status.gyro.y.value,
712 raw_status.gyro.z.value,
713 });
714 emulated.UpdateRotation(raw_status.delta_timestamp);
715 emulated.UpdateOrientation(raw_status.delta_timestamp);
716 force_update_motion = raw_status.force_update;
717
718 if (is_configuring) {
719 TriggerOnChange(ControllerTriggerType::Motion, false);
720 return;
721 }
722
723 auto& motion = controller.motion_state[index];
724 motion.accel = emulated.GetAcceleration();
725 motion.gyro = emulated.GetGyroscope();
726 motion.rotation = emulated.GetRotations();
727 motion.orientation = emulated.GetOrientation();
728 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
729
730 TriggerOnChange(ControllerTriggerType::Motion, true);
731}
732
733void EmulatedController::SetBattery(Common::Input::CallbackStatus callback, std::size_t index) {
734 if (index >= controller.battery_values.size()) {
735 return;
736 }
737 std::lock_guard lock{mutex};
738 controller.battery_values[index] = TransformToBattery(callback);
739
740 if (is_configuring) {
741 TriggerOnChange(ControllerTriggerType::Battery, false);
742 return;
743 }
744
745 bool is_charging = false;
746 bool is_powered = false;
747 NpadBatteryLevel battery_level = 0;
748 switch (controller.battery_values[index]) {
749 case Common::Input::BatteryLevel::Charging:
750 is_charging = true;
751 is_powered = true;
752 battery_level = 6;
753 break;
754 case Common::Input::BatteryLevel::Medium:
755 battery_level = 6;
756 break;
757 case Common::Input::BatteryLevel::Low:
758 battery_level = 4;
759 break;
760 case Common::Input::BatteryLevel::Critical:
761 battery_level = 2;
762 break;
763 case Common::Input::BatteryLevel::Empty:
764 battery_level = 0;
765 break;
766 case Common::Input::BatteryLevel::None:
767 case Common::Input::BatteryLevel::Full:
768 default:
769 is_powered = true;
770 battery_level = 8;
771 break;
772 }
773
774 switch (index) {
775 case LeftIndex:
776 controller.battery_state.left = {
777 .is_powered = is_powered,
778 .is_charging = is_charging,
779 .battery_level = battery_level,
780 };
781 break;
782 case RightIndex:
783 controller.battery_state.right = {
784 .is_powered = is_powered,
785 .is_charging = is_charging,
786 .battery_level = battery_level,
787 };
788 break;
789 case DualIndex:
790 controller.battery_state.dual = {
791 .is_powered = is_powered,
792 .is_charging = is_charging,
793 .battery_level = battery_level,
794 };
795 break;
796 }
797 TriggerOnChange(ControllerTriggerType::Battery, true);
798}
799
800bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
801 if (device_index >= output_devices.size()) {
802 return false;
803 }
804 if (!output_devices[device_index]) {
805 return false;
806 }
807 const auto player_index = NpadIdTypeToIndex(npad_id_type);
808 const auto& player = Settings::values.players.GetValue()[player_index];
809 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
810
811 if (!player.vibration_enabled) {
812 return false;
813 }
814
815 // Exponential amplification is too strong at low amplitudes. Switch to a linear
816 // amplification if strength is set below 0.7f
817 const Common::Input::VibrationAmplificationType type =
818 strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential
819 : Common::Input::VibrationAmplificationType::Linear;
820
821 const Common::Input::VibrationStatus status = {
822 .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f),
823 .low_frequency = vibration.low_frequency,
824 .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f),
825 .high_frequency = vibration.high_frequency,
826 .type = type,
827 };
828 return output_devices[device_index]->SetVibration(status) ==
829 Common::Input::VibrationError::None;
830}
831
832bool EmulatedController::TestVibration(std::size_t device_index) {
833 if (device_index >= output_devices.size()) {
834 return false;
835 }
836 if (!output_devices[device_index]) {
837 return false;
838 }
839
840 // Send a slight vibration to test for rumble support
841 constexpr Common::Input::VibrationStatus status = {
842 .low_amplitude = 0.001f,
843 .low_frequency = 160.0f,
844 .high_amplitude = 0.001f,
845 .high_frequency = 320.0f,
846 .type = Common::Input::VibrationAmplificationType::Linear,
847 };
848 return output_devices[device_index]->SetVibration(status) ==
849 Common::Input::VibrationError::None;
850}
851
852void EmulatedController::SetLedPattern() {
853 for (auto& device : output_devices) {
854 if (!device) {
855 continue;
856 }
857
858 const LedPattern pattern = GetLedPattern();
859 const Common::Input::LedStatus status = {
860 .led_1 = pattern.position1 != 0,
861 .led_2 = pattern.position2 != 0,
862 .led_3 = pattern.position3 != 0,
863 .led_4 = pattern.position4 != 0,
864 };
865 device->SetLED(status);
866 }
867}
868
869void EmulatedController::Connect() {
870 {
871 std::lock_guard lock{mutex};
872 if (is_configuring) {
873 tmp_is_connected = true;
874 TriggerOnChange(ControllerTriggerType::Connected, false);
875 return;
876 }
877
878 if (is_connected) {
879 return;
880 }
881 is_connected = true;
882 }
883 TriggerOnChange(ControllerTriggerType::Connected, true);
884}
885
886void EmulatedController::Disconnect() {
887 {
888 std::lock_guard lock{mutex};
889 if (is_configuring) {
890 tmp_is_connected = false;
891 TriggerOnChange(ControllerTriggerType::Disconnected, false);
892 return;
893 }
894
895 if (!is_connected) {
896 return;
897 }
898 is_connected = false;
899 }
900 TriggerOnChange(ControllerTriggerType::Disconnected, true);
901}
902
903bool EmulatedController::IsConnected(bool get_temporary_value) const {
904 if (get_temporary_value && is_configuring) {
905 return tmp_is_connected;
906 }
907 return is_connected;
908}
909
910bool EmulatedController::IsVibrationEnabled() const {
911 const auto player_index = NpadIdTypeToIndex(npad_id_type);
912 const auto& player = Settings::values.players.GetValue()[player_index];
913 return player.vibration_enabled;
914}
915
916NpadIdType EmulatedController::GetNpadIdType() const {
917 return npad_id_type;
918}
919
920NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
921 if (get_temporary_value && is_configuring) {
922 return tmp_npad_type;
923 }
924 return npad_type;
925}
926
927void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
928 {
929 std::lock_guard lock{mutex};
930
931 if (is_configuring) {
932 if (tmp_npad_type == npad_type_) {
933 return;
934 }
935 tmp_npad_type = npad_type_;
936 TriggerOnChange(ControllerTriggerType::Type, false);
937 return;
938 }
939
940 if (npad_type == npad_type_) {
941 return;
942 }
943 if (is_connected) {
944 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
945 NpadIdTypeToIndex(npad_id_type));
946 }
947 npad_type = npad_type_;
948 }
949 TriggerOnChange(ControllerTriggerType::Type, true);
950}
951
952LedPattern EmulatedController::GetLedPattern() const {
953 switch (npad_id_type) {
954 case NpadIdType::Player1:
955 return LedPattern{1, 0, 0, 0};
956 case NpadIdType::Player2:
957 return LedPattern{1, 1, 0, 0};
958 case NpadIdType::Player3:
959 return LedPattern{1, 1, 1, 0};
960 case NpadIdType::Player4:
961 return LedPattern{1, 1, 1, 1};
962 case NpadIdType::Player5:
963 return LedPattern{1, 0, 0, 1};
964 case NpadIdType::Player6:
965 return LedPattern{1, 0, 1, 0};
966 case NpadIdType::Player7:
967 return LedPattern{1, 0, 1, 1};
968 case NpadIdType::Player8:
969 return LedPattern{0, 1, 1, 0};
970 default:
971 return LedPattern{0, 0, 0, 0};
972 }
973}
974
975ButtonValues EmulatedController::GetButtonsValues() const {
976 return controller.button_values;
977}
978
979SticksValues EmulatedController::GetSticksValues() const {
980 return controller.stick_values;
981}
982
983TriggerValues EmulatedController::GetTriggersValues() const {
984 return controller.trigger_values;
985}
986
987ControllerMotionValues EmulatedController::GetMotionValues() const {
988 return controller.motion_values;
989}
990
991ColorValues EmulatedController::GetColorsValues() const {
992 return controller.color_values;
993}
994
995BatteryValues EmulatedController::GetBatteryValues() const {
996 return controller.battery_values;
997}
998
999NpadButtonState EmulatedController::GetNpadButtons() const {
1000 if (is_configuring) {
1001 return {};
1002 }
1003 return controller.npad_button_state;
1004}
1005
1006DebugPadButton EmulatedController::GetDebugPadButtons() const {
1007 if (is_configuring) {
1008 return {};
1009 }
1010 return controller.debug_pad_button_state;
1011}
1012
1013AnalogSticks EmulatedController::GetSticks() const {
1014 if (is_configuring) {
1015 return {};
1016 }
1017 // Some drivers like stick from buttons need constant refreshing
1018 for (auto& device : stick_devices) {
1019 if (!device) {
1020 continue;
1021 }
1022 device->SoftUpdate();
1023 }
1024 return controller.analog_stick_state;
1025}
1026
1027NpadGcTriggerState EmulatedController::GetTriggers() const {
1028 if (is_configuring) {
1029 return {};
1030 }
1031 return controller.gc_trigger_state;
1032}
1033
1034MotionState EmulatedController::GetMotions() const {
1035 if (force_update_motion) {
1036 for (auto& device : motion_devices) {
1037 if (!device) {
1038 continue;
1039 }
1040 device->ForceUpdate();
1041 }
1042 }
1043 return controller.motion_state;
1044}
1045
1046ControllerColors EmulatedController::GetColors() const {
1047 return controller.colors_state;
1048}
1049
1050BatteryLevelState EmulatedController::GetBattery() const {
1051 return controller.battery_state;
1052}
1053
1054void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1055 for (const auto& poller_pair : callback_list) {
1056 const ControllerUpdateCallback& poller = poller_pair.second;
1057 if (!is_npad_service_update && poller.is_npad_service) {
1058 continue;
1059 }
1060 if (poller.on_change) {
1061 poller.on_change(type);
1062 }
1063 }
1064}
1065
1066int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1067 std::lock_guard lock{mutex};
1068 callback_list.insert_or_assign(last_callback_key, update_callback);
1069 return last_callback_key++;
1070}
1071
1072void EmulatedController::DeleteCallback(int key) {
1073 std::lock_guard lock{mutex};
1074 const auto& iterator = callback_list.find(key);
1075 if (iterator == callback_list.end()) {
1076 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
1077 return;
1078 }
1079 callback_list.erase(iterator);
1080}
1081} // 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..2c5d51bc8
--- /dev/null
+++ b/src/core/hid/emulated_controller.h
@@ -0,0 +1,392 @@
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 <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/settings.h"
19#include "common/vector_math.h"
20#include "core/hid/hid_types.h"
21#include "core/hid/motion_input.h"
22
23namespace Core::HID {
24const std::size_t max_emulated_controllers = 2;
25struct ControllerMotionInfo {
26 Common::Input::MotionStatus raw_status{};
27 MotionInput emulated{};
28};
29
30using ButtonDevices =
31 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
32using StickDevices =
33 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
34using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using BatteryDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using OutputDevices =
41 std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
42
43using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
44using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
45using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
46using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
47using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
48using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
49
50using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
51using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
52using TriggerValues =
53 std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
54using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
55using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
56using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
57using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
58
59struct AnalogSticks {
60 AnalogStickState left{};
61 AnalogStickState right{};
62};
63
64struct ControllerColors {
65 NpadControllerColor fullkey{};
66 NpadControllerColor left{};
67 NpadControllerColor right{};
68};
69
70struct BatteryLevelState {
71 NpadPowerInfo dual{};
72 NpadPowerInfo left{};
73 NpadPowerInfo right{};
74};
75
76struct ControllerMotion {
77 Common::Vec3f accel{};
78 Common::Vec3f gyro{};
79 Common::Vec3f rotation{};
80 std::array<Common::Vec3f, 3> orientation{};
81 bool is_at_rest{};
82};
83
84enum EmulatedDeviceIndex : u8 {
85 LeftIndex,
86 RightIndex,
87 DualIndex,
88 AllDevices,
89};
90
91using MotionState = std::array<ControllerMotion, 2>;
92
93struct ControllerStatus {
94 // Data from input_common
95 ButtonValues button_values{};
96 SticksValues stick_values{};
97 ControllerMotionValues motion_values{};
98 TriggerValues trigger_values{};
99 ColorValues color_values{};
100 BatteryValues battery_values{};
101 VibrationValues vibration_values{};
102
103 // Data for HID serices
104 NpadButtonState npad_button_state{};
105 DebugPadButton debug_pad_button_state{};
106 AnalogSticks analog_stick_state{};
107 MotionState motion_state{};
108 NpadGcTriggerState gc_trigger_state{};
109 ControllerColors colors_state{};
110 BatteryLevelState battery_state{};
111};
112
113enum class ControllerTriggerType {
114 Button,
115 Stick,
116 Trigger,
117 Motion,
118 Color,
119 Battery,
120 Vibration,
121 Connected,
122 Disconnected,
123 Type,
124 All,
125};
126
127struct ControllerUpdateCallback {
128 std::function<void(ControllerTriggerType)> on_change;
129 bool is_npad_service;
130};
131
132class EmulatedController {
133public:
134 /**
135 * Contains all input data related to this controller. Like buttons, joysticks, motion.
136 * @param Npad id type for this specific controller
137 */
138 explicit EmulatedController(NpadIdType npad_id_type_);
139 ~EmulatedController();
140
141 YUZU_NON_COPYABLE(EmulatedController);
142 YUZU_NON_MOVEABLE(EmulatedController);
143
144 /// Converts the controller type from settings to npad type
145 static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
146
147 /// Converts npad type to the equivalent of controller type from settings
148 static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
149
150 /// Gets the NpadIdType for this controller
151 NpadIdType GetNpadIdType() const;
152
153 /// Sets the NpadStyleIndex for this controller
154 void SetNpadStyleIndex(NpadStyleIndex npad_type_);
155
156 /**
157 * Gets the NpadStyleIndex for this controller
158 * @param If true tmp_npad_type will be returned
159 * @return NpadStyleIndex set on the controller
160 */
161 NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
162
163 /// Sets the connected status to true
164 void Connect();
165
166 /// Sets the connected status to false
167 void Disconnect();
168
169 /**
170 * Is the emulated connected
171 * @param If true tmp_is_connected will be returned
172 * @return true if the controller has the connected status
173 */
174 bool IsConnected(bool get_temporary_value = false) const;
175
176 /// Returns true if vibration is enabled
177 bool IsVibrationEnabled() const;
178
179 /// Removes all callbacks created from input devices
180 void UnloadInput();
181
182 /// Sets the emulated console into configuring mode. Locking all HID service events from being
183 /// moddified
184 void EnableConfiguration();
185
186 /// Returns the emulated console to the normal behaivour
187 void DisableConfiguration();
188
189 /// Returns true if the emulated device is on configuring mode
190 bool IsConfiguring() const;
191
192 /// Reload all input devices
193 void ReloadInput();
194
195 /// Overrides current mapped devices with the stored configuration and reloads all input devices
196 void ReloadFromSettings();
197
198 /// Saves the current mapped configuration
199 void SaveCurrentConfig();
200
201 /// Reverts any mapped changes made that weren't saved
202 void RestoreConfig();
203
204 /// Returns a vector of mapped devices from the mapped button and stick parameters
205 std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const;
206
207 // Returns the current mapped button device
208 Common::ParamPackage GetButtonParam(std::size_t index) const;
209
210 // Returns the current mapped stick device
211 Common::ParamPackage GetStickParam(std::size_t index) const;
212
213 // Returns the current mapped motion device
214 Common::ParamPackage GetMotionParam(std::size_t index) const;
215
216 /**
217 * Updates the current mapped button device
218 * @param ParamPackage with controller data to be mapped
219 */
220 void SetButtonParam(std::size_t index, Common::ParamPackage param);
221
222 /**
223 * Updates the current mapped stick device
224 * @param ParamPackage with controller data to be mapped
225 */
226 void SetStickParam(std::size_t index, Common::ParamPackage param);
227
228 /**
229 * Updates the current mapped motion device
230 * @param ParamPackage with controller data to be mapped
231 */
232 void SetMotionParam(std::size_t index, Common::ParamPackage param);
233
234 /// Returns the latest button status from the controller with parameters
235 ButtonValues GetButtonsValues() const;
236
237 /// Returns the latest analog stick status from the controller with parameters
238 SticksValues GetSticksValues() const;
239
240 /// Returns the latest trigger status from the controller with parameters
241 TriggerValues GetTriggersValues() const;
242
243 /// Returns the latest motion status from the controller with parameters
244 ControllerMotionValues GetMotionValues() const;
245
246 /// Returns the latest color status from the controller with parameters
247 ColorValues GetColorsValues() const;
248
249 /// Returns the latest battery status from the controller with parameters
250 BatteryValues GetBatteryValues() const;
251
252 /// Returns the latest status of button input for the npad service
253 NpadButtonState GetNpadButtons() const;
254
255 /// Returns the latest status of button input for the debug pad service
256 DebugPadButton GetDebugPadButtons() const;
257
258 /// Returns the latest status of stick input from the mouse
259 AnalogSticks GetSticks() const;
260
261 /// Returns the latest status of trigger input from the mouse
262 NpadGcTriggerState GetTriggers() const;
263
264 /// Returns the latest status of motion input from the mouse
265 MotionState GetMotions() const;
266
267 /// Returns the latest color value from the controller
268 ControllerColors GetColors() const;
269
270 /// Returns the latest battery status from the controller
271 BatteryLevelState GetBattery() const;
272
273 /*
274 * Sends a specific vibration to the output device
275 * @return returns true if vibration had no errors
276 */
277 bool SetVibration(std::size_t device_index, VibrationValue vibration);
278
279 /*
280 * Sends a small vibration to the output device
281 * @return returns true if SetVibration was successfull
282 */
283 bool TestVibration(std::size_t device_index);
284
285 /// Returns the led pattern corresponding to this emulated controller
286 LedPattern GetLedPattern() const;
287
288 /// Asks the output device to change the player led pattern
289 void SetLedPattern();
290
291 /**
292 * Adds a callback to the list of events
293 * @param ConsoleUpdateCallback that will be triggered
294 * @return an unique key corresponding to the callback index in the list
295 */
296 int SetCallback(ControllerUpdateCallback update_callback);
297
298 /**
299 * Removes a callback from the list stopping any future events to this object
300 * @param Key corresponding to the callback index in the list
301 */
302 void DeleteCallback(int key);
303
304private:
305 /// creates input devices from params
306 void LoadDevices();
307
308 /// Set the params for TAS devices
309 void LoadTASParams();
310
311 /**
312 * Updates the button status of the controller
313 * @param callback: A CallbackStatus containing the button status
314 * @param index: Button ID of the to be updated
315 */
316 void SetButton(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
317
318 /**
319 * Updates the analog stick status of the controller
320 * @param callback: A CallbackStatus containing the analog stick status
321 * @param index: stick ID of the to be updated
322 */
323 void SetStick(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
324
325 /**
326 * Updates the trigger status of the controller
327 * @param callback: A CallbackStatus containing the trigger status
328 * @param index: trigger ID of the to be updated
329 */
330 void SetTrigger(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
331
332 /**
333 * Updates the motion status of the controller
334 * @param callback: A CallbackStatus containing gyro and accelerometer data
335 * @param index: motion ID of the to be updated
336 */
337 void SetMotion(Common::Input::CallbackStatus callback, std::size_t index);
338
339 /**
340 * Updates the battery status of the controller
341 * @param callback: A CallbackStatus containing the battery status
342 * @param index: Button ID of the to be updated
343 */
344 void SetBattery(Common::Input::CallbackStatus callback, std::size_t index);
345
346 /**
347 * Triggers a callback that something has changed on the controller status
348 * @param type: Input type of the event to trigger
349 * @param is_service_update: indicates if this event should be sended to only services
350 */
351 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
352
353 NpadIdType npad_id_type;
354 NpadStyleIndex npad_type{NpadStyleIndex::None};
355 bool is_connected{false};
356 bool is_configuring{false};
357 f32 motion_sensitivity{0.01f};
358 bool force_update_motion{false};
359
360 // Temporary values to avoid doing changes while the controller is on configuration mode
361 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
362 bool tmp_is_connected{false};
363
364 ButtonParams button_params;
365 StickParams stick_params;
366 ControllerMotionParams motion_params;
367 TriggerParams trigger_params;
368 BatteryParams battery_params;
369 OutputParams output_params;
370
371 ButtonDevices button_devices;
372 StickDevices stick_devices;
373 ControllerMotionDevices motion_devices;
374 TriggerDevices trigger_devices;
375 BatteryDevices battery_devices;
376 OutputDevices output_devices;
377
378 // TAS related variables
379 ButtonParams tas_button_params;
380 StickParams tas_stick_params;
381 ButtonDevices tas_button_devices;
382 StickDevices tas_stick_devices;
383
384 mutable std::mutex mutex;
385 std::unordered_map<int, ControllerUpdateCallback> callback_list;
386 int last_callback_key = 0;
387
388 // Stores the current status of all controller input
389 ControllerStatus controller;
390};
391
392} // 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..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
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
new file mode 100644
index 000000000..05a945d08
--- /dev/null
+++ b/src/core/hid/emulated_devices.h
@@ -0,0 +1,209 @@
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 <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/settings.h"
17#include "core/hid/hid_types.h"
18
19namespace Core::HID {
20using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
21 Settings::NativeKeyboard::NumKeyboardKeys>;
22using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
23 Settings::NativeKeyboard::NumKeyboardMods>;
24using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
25 Settings::NativeMouseButton::NumMouseButtons>;
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29
30using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
32
33using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
35using KeyboardModifierValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
37using MouseButtonValues =
38 std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
39using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus;
42
43struct MousePosition {
44 f32 x;
45 f32 y;
46};
47
48struct DeviceStatus {
49 // Data from input_common
50 KeyboardValues keyboard_values{};
51 KeyboardModifierValues keyboard_moddifier_values{};
52 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{};
55
56 // Data for HID serices
57 KeyboardKey keyboard_state{};
58 KeyboardModifier keyboard_moddifier_state{};
59 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{};
62};
63
64enum class DeviceTriggerType {
65 Keyboard,
66 KeyboardModdifier,
67 Mouse,
68};
69
70struct InterfaceUpdateCallback {
71 std::function<void(DeviceTriggerType)> on_change;
72};
73
74class EmulatedDevices {
75public:
76 /**
77 * Contains all input data related to external devices that aren't necesarily a controller
78 * like keyboard and mouse
79 */
80 EmulatedDevices();
81 ~EmulatedDevices();
82
83 YUZU_NON_COPYABLE(EmulatedDevices);
84 YUZU_NON_MOVEABLE(EmulatedDevices);
85
86 /// Removes all callbacks created from input devices
87 void UnloadInput();
88
89 /// Sets the emulated console into configuring mode. Locking all HID service events from being
90 /// moddified
91 void EnableConfiguration();
92
93 /// Returns the emulated console to the normal behaivour
94 void DisableConfiguration();
95
96 /// Returns true if the emulated device is on configuring mode
97 bool IsConfiguring() const;
98
99 /// Reload all input devices
100 void ReloadInput();
101
102 /// Overrides current mapped devices with the stored configuration and reloads all input devices
103 void ReloadFromSettings();
104
105 /// Saves the current mapped configuration
106 void SaveCurrentConfig();
107
108 /// Reverts any mapped changes made that weren't saved
109 void RestoreConfig();
110
111 /// Returns the latest status of button input from the keyboard with parameters
112 KeyboardValues GetKeyboardValues() const;
113
114 /// Returns the latest status of button input from the keyboard modifiers with parameters
115 KeyboardModifierValues GetKeyboardModdifierValues() const;
116
117 /// Returns the latest status of button input from the mouse with parameters
118 MouseButtonValues GetMouseButtonsValues() const;
119
120 /// Returns the latest status of button input from the keyboard
121 KeyboardKey GetKeyboard() const;
122
123 /// Returns the latest status of button input from the keyboard modifiers
124 KeyboardModifier GetKeyboardModifier() const;
125
126 /// Returns the latest status of button input from the mouse
127 MouseButton GetMouseButtons() const;
128
129 /// Returns the latest mouse coordinates
130 MousePosition GetMousePosition() const;
131
132 /// Returns the latest mouse wheel change
133 AnalogStickState GetMouseWheel() const;
134
135 /**
136 * Adds a callback to the list of events
137 * @param InterfaceUpdateCallback that will be triggered
138 * @return an unique key corresponding to the callback index in the list
139 */
140 int SetCallback(InterfaceUpdateCallback update_callback);
141
142 /**
143 * Removes a callback from the list stopping any future events to this object
144 * @param Key corresponding to the callback index in the list
145 */
146 void DeleteCallback(int key);
147
148private:
149 /// Helps assigning a value to keyboard_state
150 void UpdateKey(std::size_t key_index, bool status);
151
152 /**
153 * Updates the touch status of the keyboard device
154 * @param callback: A CallbackStatus containing the key status
155 * @param index: key ID to be updated
156 */
157 void SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index);
158
159 /**
160 * Updates the keyboard status of the keyboard device
161 * @param callback: A CallbackStatus containing the modifier key status
162 * @param index: modifier key ID to be updated
163 */
164 void SetKeyboardModifier(Common::Input::CallbackStatus callback, std::size_t index);
165
166 /**
167 * Updates the mouse button status of the mouse device
168 * @param callback: A CallbackStatus containing the button status
169 * @param index: Button ID to be updated
170 */
171 void SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index);
172
173 /**
174 * Updates the mouse wheel status of the mouse device
175 * @param callback: A CallbackStatus containing the wheel status
176 * @param index: wheel ID to be updated
177 */
178 void SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index);
179
180 /**
181 * Updates the mouse position status of the mouse device
182 * @param callback: A CallbackStatus containing the position status
183 * @param index: stick ID to be updated
184 */
185 void SetMouseStick(Common::Input::CallbackStatus callback);
186
187 /**
188 * Triggers a callback that something has changed on the device status
189 * @param Input type of the event to trigger
190 */
191 void TriggerOnChange(DeviceTriggerType type);
192
193 bool is_configuring{false};
194
195 KeyboardDevices keyboard_devices;
196 KeyboardModifierDevices keyboard_modifier_devices;
197 MouseButtonDevices mouse_button_devices;
198 MouseAnalogDevices mouse_analog_devices;
199 MouseStickDevice mouse_stick_device;
200
201 mutable std::mutex mutex;
202 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
203 int last_callback_key = 0;
204
205 // Stores the current status of all external device input
206 DeviceStatus device_status;
207};
208
209} // 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..741a69c3c
--- /dev/null
+++ b/src/core/hid/hid_core.cpp
@@ -0,0 +1,168 @@
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/emulated_console.h"
7#include "core/hid/emulated_controller.h"
8#include "core/hid/emulated_devices.h"
9#include "core/hid/hid_core.h"
10
11namespace Core::HID {
12
13HIDCore::HIDCore()
14 : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
15 player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
16 player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
17 player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
18 player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
19 player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
20 player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
21 player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
22 other{std::make_unique<EmulatedController>(NpadIdType::Other)},
23 handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
24 console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
25
26HIDCore::~HIDCore() = default;
27
28EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
29 switch (npad_id_type) {
30 case NpadIdType::Player1:
31 return player_1.get();
32 case NpadIdType::Player2:
33 return player_2.get();
34 case NpadIdType::Player3:
35 return player_3.get();
36 case NpadIdType::Player4:
37 return player_4.get();
38 case NpadIdType::Player5:
39 return player_5.get();
40 case NpadIdType::Player6:
41 return player_6.get();
42 case NpadIdType::Player7:
43 return player_7.get();
44 case NpadIdType::Player8:
45 return player_8.get();
46 case NpadIdType::Other:
47 return other.get();
48 case NpadIdType::Handheld:
49 return handheld.get();
50 case NpadIdType::Invalid:
51 default:
52 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
53 return nullptr;
54 }
55}
56
57const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
58 switch (npad_id_type) {
59 case NpadIdType::Player1:
60 return player_1.get();
61 case NpadIdType::Player2:
62 return player_2.get();
63 case NpadIdType::Player3:
64 return player_3.get();
65 case NpadIdType::Player4:
66 return player_4.get();
67 case NpadIdType::Player5:
68 return player_5.get();
69 case NpadIdType::Player6:
70 return player_6.get();
71 case NpadIdType::Player7:
72 return player_7.get();
73 case NpadIdType::Player8:
74 return player_8.get();
75 case NpadIdType::Other:
76 return other.get();
77 case NpadIdType::Handheld:
78 return handheld.get();
79 case NpadIdType::Invalid:
80 default:
81 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
82 return nullptr;
83 }
84}
85EmulatedConsole* HIDCore::GetEmulatedConsole() {
86 return console.get();
87}
88
89const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
90 return console.get();
91}
92
93EmulatedDevices* HIDCore::GetEmulatedDevices() {
94 return devices.get();
95}
96
97const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
98 return devices.get();
99}
100
101EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
102 return GetEmulatedController(IndexToNpadIdType(index));
103}
104
105const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
106 return GetEmulatedController(IndexToNpadIdType(index));
107}
108
109void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
110 supported_style_tag.raw = style_tag.raw;
111}
112
113NpadStyleTag HIDCore::GetSupportedStyleTag() const {
114 return supported_style_tag;
115}
116
117s8 HIDCore::GetPlayerCount() const {
118 s8 active_players = 0;
119 for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
120 const auto* const controller = GetEmulatedControllerByIndex(player_index);
121 if (controller->IsConnected()) {
122 active_players++;
123 }
124 }
125 return active_players;
126}
127
128NpadIdType HIDCore::GetFirstNpadId() const {
129 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
130 const auto* const controller = GetEmulatedControllerByIndex(player_index);
131 if (controller->IsConnected()) {
132 return controller->GetNpadIdType();
133 }
134 }
135 return NpadIdType::Player1;
136}
137
138void HIDCore::ReloadInputDevices() {
139 player_1->ReloadFromSettings();
140 player_2->ReloadFromSettings();
141 player_3->ReloadFromSettings();
142 player_4->ReloadFromSettings();
143 player_5->ReloadFromSettings();
144 player_6->ReloadFromSettings();
145 player_7->ReloadFromSettings();
146 player_8->ReloadFromSettings();
147 other->ReloadFromSettings();
148 handheld->ReloadFromSettings();
149 console->ReloadFromSettings();
150 devices->ReloadFromSettings();
151}
152
153void HIDCore::UnloadInputDevices() {
154 player_1->UnloadInput();
155 player_2->UnloadInput();
156 player_3->UnloadInput();
157 player_4->UnloadInput();
158 player_5->UnloadInput();
159 player_6->UnloadInput();
160 player_7->UnloadInput();
161 player_8->UnloadInput();
162 other->UnloadInput();
163 handheld->UnloadInput();
164 console->UnloadInput();
165 devices->UnloadInput();
166}
167
168} // 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..609f40f3b
--- /dev/null
+++ b/src/core/hid/hid_core.h
@@ -0,0 +1,73 @@
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/hid_types.h"
10
11namespace Core::HID {
12class EmulatedConsole;
13class EmulatedController;
14class EmulatedDevices;
15} // namespace Core::HID
16
17namespace Core::HID {
18
19class HIDCore {
20public:
21 explicit HIDCore();
22 ~HIDCore();
23
24 YUZU_NON_COPYABLE(HIDCore);
25 YUZU_NON_MOVEABLE(HIDCore);
26
27 EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
28 const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
29
30 EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
31 const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
32
33 EmulatedConsole* GetEmulatedConsole();
34 const EmulatedConsole* GetEmulatedConsole() const;
35
36 EmulatedDevices* GetEmulatedDevices();
37 const EmulatedDevices* GetEmulatedDevices() const;
38
39 void SetSupportedStyleTag(NpadStyleTag style_tag);
40 NpadStyleTag GetSupportedStyleTag() const;
41
42 /// Counts the connected players from P1-P8
43 s8 GetPlayerCount() const;
44
45 /// Returns the first connected npad id
46 NpadIdType GetFirstNpadId() const;
47
48 /// Reloads all input devices from settings
49 void ReloadInputDevices();
50
51 /// Removes all callbacks from input common
52 void UnloadInputDevices();
53
54 /// Number of emulated controllers
55 static constexpr std::size_t available_controllers{10};
56
57private:
58 std::unique_ptr<EmulatedController> player_1;
59 std::unique_ptr<EmulatedController> player_2;
60 std::unique_ptr<EmulatedController> player_3;
61 std::unique_ptr<EmulatedController> player_4;
62 std::unique_ptr<EmulatedController> player_5;
63 std::unique_ptr<EmulatedController> player_6;
64 std::unique_ptr<EmulatedController> player_7;
65 std::unique_ptr<EmulatedController> player_8;
66 std::unique_ptr<EmulatedController> other;
67 std::unique_ptr<EmulatedController> handheld;
68 std::unique_ptr<EmulatedConsole> console;
69 std::unique_ptr<EmulatedDevices> devices;
70 NpadStyleTag supported_style_tag;
71};
72
73} // namespace Core::HID
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
new file mode 100644
index 000000000..acf54e233
--- /dev/null
+++ b/src/core/hid/hid_types.h
@@ -0,0 +1,631 @@
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 "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/point.h"
11#include "common/uuid.h"
12
13namespace Core::HID {
14
15enum class DeviceIndex : u8 {
16 Left = 0,
17 Right = 1,
18 None = 2,
19 MaxDeviceIndex = 3,
20};
21
22// This is nn::hid::NpadButton
23enum class NpadButton : u64 {
24 None = 0,
25 A = 1U << 0,
26 B = 1U << 1,
27 X = 1U << 2,
28 Y = 1U << 3,
29 StickL = 1U << 4,
30 StickR = 1U << 5,
31 L = 1U << 6,
32 R = 1U << 7,
33 ZL = 1U << 8,
34 ZR = 1U << 9,
35 Plus = 1U << 10,
36 Minus = 1U << 11,
37
38 Left = 1U << 12,
39 Up = 1U << 13,
40 Right = 1U << 14,
41 Down = 1U << 15,
42
43 StickLLeft = 1U << 16,
44 StickLUp = 1U << 17,
45 StickLRight = 1U << 18,
46 StickLDown = 1U << 19,
47
48 StickRLeft = 1U << 20,
49 StickRUp = 1U << 21,
50 StickRRight = 1U << 22,
51 StickRDown = 1U << 23,
52
53 LeftSL = 1U << 24,
54 LeftSR = 1U << 25,
55
56 RightSL = 1U << 26,
57 RightSR = 1U << 27,
58
59 Palma = 1U << 28,
60 Verification = 1U << 29,
61 HandheldLeftB = 1U << 30,
62 LagonCLeft = 1U << 31,
63 LagonCUp = 1ULL << 32,
64 LagonCRight = 1ULL << 33,
65 LagonCDown = 1ULL << 34,
66};
67DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
68
69enum class KeyboardKeyIndex : u32 {
70 A = 4,
71 B = 5,
72 C = 6,
73 D = 7,
74 E = 8,
75 F = 9,
76 G = 10,
77 H = 11,
78 I = 12,
79 J = 13,
80 K = 14,
81 L = 15,
82 M = 16,
83 N = 17,
84 O = 18,
85 P = 19,
86 Q = 20,
87 R = 21,
88 S = 22,
89 T = 23,
90 U = 24,
91 V = 25,
92 W = 26,
93 X = 27,
94 Y = 28,
95 Z = 29,
96 D1 = 30,
97 D2 = 31,
98 D3 = 32,
99 D4 = 33,
100 D5 = 34,
101 D6 = 35,
102 D7 = 36,
103 D8 = 37,
104 D9 = 38,
105 D0 = 39,
106 Return = 40,
107 Escape = 41,
108 Backspace = 42,
109 Tab = 43,
110 Space = 44,
111 Minus = 45,
112 Plus = 46,
113 OpenBracket = 47,
114 CloseBracket = 48,
115 Pipe = 49,
116 Tilde = 50,
117 Semicolon = 51,
118 Quote = 52,
119 Backquote = 53,
120 Comma = 54,
121 Period = 55,
122 Slash = 56,
123 CapsLock = 57,
124 F1 = 58,
125 F2 = 59,
126 F3 = 60,
127 F4 = 61,
128 F5 = 62,
129 F6 = 63,
130 F7 = 64,
131 F8 = 65,
132 F9 = 66,
133 F10 = 67,
134 F11 = 68,
135 F12 = 69,
136 PrintScreen = 70,
137 ScrollLock = 71,
138 Pause = 72,
139 Insert = 73,
140 Home = 74,
141 PageUp = 75,
142 Delete = 76,
143 End = 77,
144 PageDown = 78,
145 RightArrow = 79,
146 LeftArrow = 80,
147 DownArrow = 81,
148 UpArrow = 82,
149 NumLock = 83,
150 NumPadDivide = 84,
151 NumPadMultiply = 85,
152 NumPadSubtract = 86,
153 NumPadAdd = 87,
154 NumPadEnter = 88,
155 NumPad1 = 89,
156 NumPad2 = 90,
157 NumPad3 = 91,
158 NumPad4 = 92,
159 NumPad5 = 93,
160 NumPad6 = 94,
161 NumPad7 = 95,
162 NumPad8 = 96,
163 NumPad9 = 97,
164 NumPad0 = 98,
165 NumPadDot = 99,
166 Backslash = 100,
167 Application = 101,
168 Power = 102,
169 NumPadEquals = 103,
170 F13 = 104,
171 F14 = 105,
172 F15 = 106,
173 F16 = 107,
174 F17 = 108,
175 F18 = 109,
176 F19 = 110,
177 F20 = 111,
178 F21 = 112,
179 F22 = 113,
180 F23 = 114,
181 F24 = 115,
182 NumPadComma = 133,
183 Ro = 135,
184 KatakanaHiragana = 136,
185 Yen = 137,
186 Henkan = 138,
187 Muhenkan = 139,
188 NumPadCommaPc98 = 140,
189 HangulEnglish = 144,
190 Hanja = 145,
191 Katakana = 146,
192 Hiragana = 147,
193 ZenkakuHankaku = 148,
194 LeftControl = 224,
195 LeftShift = 225,
196 LeftAlt = 226,
197 LeftGui = 227,
198 RightControl = 228,
199 RightShift = 229,
200 RightAlt = 230,
201 RightGui = 231,
202};
203
204// This is nn::hid::NpadIdType
205enum class NpadIdType : u32 {
206 Player1 = 0x0,
207 Player2 = 0x1,
208 Player3 = 0x2,
209 Player4 = 0x3,
210 Player5 = 0x4,
211 Player6 = 0x5,
212 Player7 = 0x6,
213 Player8 = 0x7,
214 Other = 0x10,
215 Handheld = 0x20,
216
217 Invalid = 0xFFFFFFFF,
218};
219
220// This is nn::hid::NpadStyleIndex
221enum class NpadStyleIndex : u8 {
222 None = 0,
223 ProController = 3,
224 Handheld = 4,
225 HandheldNES = 4,
226 JoyconDual = 5,
227 JoyconLeft = 6,
228 JoyconRight = 7,
229 GameCube = 8,
230 Pokeball = 9,
231 NES = 10,
232 SNES = 12,
233 N64 = 13,
234 SegaGenesis = 14,
235 SystemExt = 32,
236 System = 33,
237 MaxNpadType = 34,
238};
239
240// This is nn::hid::NpadStyleSet
241enum class NpadStyleSet : u32 {
242 None = 0,
243 Fullkey = 1U << 0,
244 Handheld = 1U << 1,
245 JoyDual = 1U << 2,
246 JoyLeft = 1U << 3,
247 JoyRight = 1U << 4,
248 Gc = 1U << 5,
249 Palma = 1U << 6,
250 Lark = 1U << 7,
251 HandheldLark = 1U << 8,
252 Lucia = 1U << 9,
253 Lagoon = 1U << 10,
254 Lager = 1U << 11,
255 SystemExt = 1U << 29,
256 System = 1U << 30,
257};
258static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
259
260// This is nn::hid::VibrationDevicePosition
261enum class VibrationDevicePosition : u32 {
262 None = 0,
263 Left = 1,
264 Right = 2,
265};
266
267// This is nn::hid::VibrationDeviceType
268enum class VibrationDeviceType : u32 {
269 Unknown = 0,
270 LinearResonantActuator = 1,
271 GcErm = 2,
272};
273
274// This is nn::hid::VibrationGcErmCommand
275enum class VibrationGcErmCommand : u64 {
276 Stop = 0,
277 Start = 1,
278 StopHard = 2,
279};
280
281// This is nn::hid::NpadStyleTag
282struct NpadStyleTag {
283 union {
284 NpadStyleSet raw{};
285
286 BitField<0, 1, u32> fullkey;
287 BitField<1, 1, u32> handheld;
288 BitField<2, 1, u32> joycon_dual;
289 BitField<3, 1, u32> joycon_left;
290 BitField<4, 1, u32> joycon_right;
291 BitField<5, 1, u32> gamecube;
292 BitField<6, 1, u32> palma;
293 BitField<7, 1, u32> lark;
294 BitField<8, 1, u32> handheld_lark;
295 BitField<9, 1, u32> lucia;
296 BitField<10, 1, u32> lagoon;
297 BitField<11, 1, u32> lager;
298 BitField<29, 1, u32> system_ext;
299 BitField<30, 1, u32> system;
300 };
301};
302static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
303
304// This is nn::hid::TouchAttribute
305struct TouchAttribute {
306 union {
307 u32 raw{};
308 BitField<0, 1, u32> start_touch;
309 BitField<1, 1, u32> end_touch;
310 };
311};
312static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
313
314// This is nn::hid::TouchState
315struct TouchState {
316 u64 delta_time;
317 TouchAttribute attribute;
318 u32 finger;
319 Common::Point<u32> position;
320 u32 diameter_x;
321 u32 diameter_y;
322 u32 rotation_angle;
323};
324static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
325
326// This is nn::hid::NpadControllerColor
327struct NpadControllerColor {
328 u32 body;
329 u32 button;
330};
331static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
332
333// This is nn::hid::AnalogStickState
334struct AnalogStickState {
335 s32 x;
336 s32 y;
337};
338static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
339
340// This is nn::hid::server::NpadGcTriggerState
341struct NpadGcTriggerState {
342 s64 sampling_number{};
343 s32 left{};
344 s32 right{};
345};
346static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
347
348// This is nn::hid::system::NpadBatteryLevel
349using NpadBatteryLevel = u32;
350static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
351
352// This is nn::hid::system::NpadPowerInfo
353struct NpadPowerInfo {
354 bool is_powered;
355 bool is_charging;
356 INSERT_PADDING_BYTES(0x6);
357 NpadBatteryLevel battery_level;
358};
359static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
360
361struct LedPattern {
362 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
363 position1.Assign(light1);
364 position2.Assign(light2);
365 position3.Assign(light3);
366 position4.Assign(light4);
367 }
368 union {
369 u64 raw{};
370 BitField<0, 1, u64> position1;
371 BitField<1, 1, u64> position2;
372 BitField<2, 1, u64> position3;
373 BitField<3, 1, u64> position4;
374 };
375};
376
377struct NpadButtonState {
378 union {
379 NpadButton raw{};
380
381 // Buttons
382 BitField<0, 1, u64> a;
383 BitField<1, 1, u64> b;
384 BitField<2, 1, u64> x;
385 BitField<3, 1, u64> y;
386 BitField<4, 1, u64> stick_l;
387 BitField<5, 1, u64> stick_r;
388 BitField<6, 1, u64> l;
389 BitField<7, 1, u64> r;
390 BitField<8, 1, u64> zl;
391 BitField<9, 1, u64> zr;
392 BitField<10, 1, u64> plus;
393 BitField<11, 1, u64> minus;
394
395 // D-Pad
396 BitField<12, 1, u64> left;
397 BitField<13, 1, u64> up;
398 BitField<14, 1, u64> right;
399 BitField<15, 1, u64> down;
400
401 // Left JoyStick
402 BitField<16, 1, u64> stick_l_left;
403 BitField<17, 1, u64> stick_l_up;
404 BitField<18, 1, u64> stick_l_right;
405 BitField<19, 1, u64> stick_l_down;
406
407 // Right JoyStick
408 BitField<20, 1, u64> stick_r_left;
409 BitField<21, 1, u64> stick_r_up;
410 BitField<22, 1, u64> stick_r_right;
411 BitField<23, 1, u64> stick_r_down;
412
413 BitField<24, 1, u64> left_sl;
414 BitField<25, 1, u64> left_sr;
415
416 BitField<26, 1, u64> right_sl;
417 BitField<27, 1, u64> right_sr;
418
419 BitField<28, 1, u64> palma;
420 BitField<29, 1, u64> verification;
421 BitField<30, 1, u64> handheld_left_b;
422 BitField<31, 1, u64> lagon_c_left;
423 BitField<32, 1, u64> lagon_c_up;
424 BitField<33, 1, u64> lagon_c_right;
425 BitField<34, 1, u64> lagon_c_down;
426 };
427};
428static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");
429
430// This is nn::hid::DebugPadButton
431struct DebugPadButton {
432 union {
433 u32 raw{};
434 BitField<0, 1, u32> a;
435 BitField<1, 1, u32> b;
436 BitField<2, 1, u32> x;
437 BitField<3, 1, u32> y;
438 BitField<4, 1, u32> l;
439 BitField<5, 1, u32> r;
440 BitField<6, 1, u32> zl;
441 BitField<7, 1, u32> zr;
442 BitField<8, 1, u32> plus;
443 BitField<9, 1, u32> minus;
444 BitField<10, 1, u32> d_left;
445 BitField<11, 1, u32> d_up;
446 BitField<12, 1, u32> d_right;
447 BitField<13, 1, u32> d_down;
448 };
449};
450static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
451
452// This is nn::hid::ConsoleSixAxisSensorHandle
453struct ConsoleSixAxisSensorHandle {
454 u8 unknown_1;
455 u8 unknown_2;
456 INSERT_PADDING_BYTES_NOINIT(2);
457};
458static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
459 "ConsoleSixAxisSensorHandle is an invalid size");
460
461// This is nn::hid::SixAxisSensorHandle
462struct SixAxisSensorHandle {
463 NpadStyleIndex npad_type;
464 u8 npad_id;
465 DeviceIndex device_index;
466 INSERT_PADDING_BYTES_NOINIT(1);
467};
468static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
469
470struct SixAxisSensorFusionParameters {
471 f32 parameter1;
472 f32 parameter2;
473};
474static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
475 "SixAxisSensorFusionParameters is an invalid size");
476
477// This is nn::hid::VibrationDeviceHandle
478struct VibrationDeviceHandle {
479 NpadStyleIndex npad_type;
480 u8 npad_id;
481 DeviceIndex device_index;
482 INSERT_PADDING_BYTES_NOINIT(1);
483};
484static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
485
486// This is nn::hid::VibrationValue
487struct VibrationValue {
488 f32 low_amplitude;
489 f32 low_frequency;
490 f32 high_amplitude;
491 f32 high_frequency;
492};
493static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
494
495// This is nn::hid::VibrationDeviceInfo
496struct VibrationDeviceInfo {
497 VibrationDeviceType type{};
498 VibrationDevicePosition position{};
499};
500static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
501
502// This is nn::hid::KeyboardModifier
503struct KeyboardModifier {
504 union {
505 u32 raw{};
506 BitField<0, 1, u32> control;
507 BitField<1, 1, u32> shift;
508 BitField<2, 1, u32> left_alt;
509 BitField<3, 1, u32> right_alt;
510 BitField<4, 1, u32> gui;
511 BitField<8, 1, u32> caps_lock;
512 BitField<9, 1, u32> scroll_lock;
513 BitField<10, 1, u32> num_lock;
514 BitField<11, 1, u32> katakana;
515 BitField<12, 1, u32> hiragana;
516 };
517};
518
519static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");
520
521// This is nn::hid::KeyboardAttribute
522struct KeyboardAttribute {
523 union {
524 u32 raw{};
525 BitField<0, 1, u32> is_connected;
526 };
527};
528static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");
529
530// This is nn::hid::KeyboardKey
531struct KeyboardKey {
532 // This should be a 256 bit flag
533 std::array<u8, 32> key;
534};
535static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
536
537// This is nn::hid::MouseButton
538struct MouseButton {
539 union {
540 u32_le raw{};
541 BitField<0, 1, u32> left;
542 BitField<1, 1, u32> right;
543 BitField<2, 1, u32> middle;
544 BitField<3, 1, u32> forward;
545 BitField<4, 1, u32> back;
546 };
547};
548static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");
549
550// This is nn::hid::MouseAttribute
551struct MouseAttribute {
552 union {
553 u32 raw{};
554 BitField<0, 1, u32> transferable;
555 BitField<1, 1, u32> is_connected;
556 };
557};
558static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");
559
560// This is nn::hid::detail::MouseState
561struct MouseState {
562 s64 sampling_number;
563 s32 x;
564 s32 y;
565 s32 delta_x;
566 s32 delta_y;
567 // Axis Order in HW is switched for the wheel
568 s32 delta_wheel_y;
569 s32 delta_wheel_x;
570 MouseButton button;
571 MouseAttribute attribute;
572};
573static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
574
575/// Converts a NpadIdType to an array index.
576constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
577 switch (npad_id_type) {
578 case NpadIdType::Player1:
579 return 0;
580 case NpadIdType::Player2:
581 return 1;
582 case NpadIdType::Player3:
583 return 2;
584 case NpadIdType::Player4:
585 return 3;
586 case NpadIdType::Player5:
587 return 4;
588 case NpadIdType::Player6:
589 return 5;
590 case NpadIdType::Player7:
591 return 6;
592 case NpadIdType::Player8:
593 return 7;
594 case NpadIdType::Handheld:
595 return 8;
596 case NpadIdType::Other:
597 return 9;
598 default:
599 return 0;
600 }
601}
602
603/// Converts an array index to a NpadIdType
604constexpr NpadIdType IndexToNpadIdType(size_t index) {
605 switch (index) {
606 case 0:
607 return NpadIdType::Player1;
608 case 1:
609 return NpadIdType::Player2;
610 case 2:
611 return NpadIdType::Player3;
612 case 3:
613 return NpadIdType::Player4;
614 case 4:
615 return NpadIdType::Player5;
616 case 5:
617 return NpadIdType::Player6;
618 case 6:
619 return NpadIdType::Player7;
620 case 7:
621 return NpadIdType::Player8;
622 case 8:
623 return NpadIdType::Handheld;
624 case 9:
625 return NpadIdType::Other;
626 default:
627 return NpadIdType::Invalid;
628 }
629}
630
631} // namespace Core::HID
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
new file mode 100644
index 000000000..f5acff6e0
--- /dev/null
+++ b/src/core/hid/input_converter.cpp
@@ -0,0 +1,383 @@
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 <random>
6
7#include "common/input.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11
12Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
13 Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
14 switch (callback.type) {
15 case Common::Input::InputType::Analog:
16 case Common::Input::InputType::Trigger: {
17 const auto value = TransformToTrigger(callback).analog.value;
18 battery = Common::Input::BatteryLevel::Empty;
19 if (value > 0.2f) {
20 battery = Common::Input::BatteryLevel::Critical;
21 }
22 if (value > 0.4f) {
23 battery = Common::Input::BatteryLevel::Low;
24 }
25 if (value > 0.6f) {
26 battery = Common::Input::BatteryLevel::Medium;
27 }
28 if (value > 0.8f) {
29 battery = Common::Input::BatteryLevel::Full;
30 }
31 if (value >= 1.0f) {
32 battery = Common::Input::BatteryLevel::Charging;
33 }
34 break;
35 }
36 case Common::Input::InputType::Button:
37 battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
38 : Common::Input::BatteryLevel::Critical;
39 break;
40 case Common::Input::InputType::Battery:
41 battery = callback.battery_status;
42 break;
43 default:
44 LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
45 break;
46 }
47
48 return battery;
49}
50
51Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
52 Common::Input::ButtonStatus status{};
53 switch (callback.type) {
54 case Common::Input::InputType::Analog:
55 case Common::Input::InputType::Trigger:
56 status.value = TransformToTrigger(callback).pressed.value;
57 break;
58 case Common::Input::InputType::Button:
59 status = callback.button_status;
60 break;
61 default:
62 LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
63 break;
64 }
65
66 if (status.inverted) {
67 status.value = !status.value;
68 }
69
70 return status;
71}
72
73Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
74 Common::Input::MotionStatus status{};
75 switch (callback.type) {
76 case Common::Input::InputType::Button: {
77 Common::Input::AnalogProperties properties{
78 .deadzone = 0.0f,
79 .range = 1.0f,
80 .offset = 0.0f,
81 };
82 status.delta_timestamp = 5000;
83 status.force_update = true;
84 status.accel.x = {
85 .value = 0.0f,
86 .raw_value = 0.0f,
87 .properties = properties,
88 };
89 status.accel.y = {
90 .value = 0.0f,
91 .raw_value = 0.0f,
92 .properties = properties,
93 };
94 status.accel.z = {
95 .value = 0.0f,
96 .raw_value = -1.0f,
97 .properties = properties,
98 };
99 status.gyro.x = {
100 .value = 0.0f,
101 .raw_value = 0.0f,
102 .properties = properties,
103 };
104 status.gyro.y = {
105 .value = 0.0f,
106 .raw_value = 0.0f,
107 .properties = properties,
108 };
109 status.gyro.z = {
110 .value = 0.0f,
111 .raw_value = 0.0f,
112 .properties = properties,
113 };
114 if (TransformToButton(callback).value) {
115 std::random_device device;
116 std::mt19937 gen(device());
117 std::uniform_int_distribution<s16> distribution(-1000, 1000);
118 status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
119 status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
120 status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
121 status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
122 status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
123 status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
124 }
125 break;
126 }
127 case Common::Input::InputType::Motion:
128 status = callback.motion_status;
129 break;
130 default:
131 LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
132 break;
133 }
134 SanitizeAnalog(status.accel.x, false);
135 SanitizeAnalog(status.accel.y, false);
136 SanitizeAnalog(status.accel.z, false);
137 SanitizeAnalog(status.gyro.x, false);
138 SanitizeAnalog(status.gyro.y, false);
139 SanitizeAnalog(status.gyro.z, false);
140
141 return status;
142}
143
144Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
145 Common::Input::StickStatus status{};
146
147 switch (callback.type) {
148 case Common::Input::InputType::Stick:
149 status = callback.stick_status;
150 break;
151 default:
152 LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
153 break;
154 }
155
156 SanitizeStick(status.x, status.y, true);
157 const auto& properties_x = status.x.properties;
158 const auto& properties_y = status.y.properties;
159 const float x = status.x.value;
160 const float y = status.y.value;
161
162 // Set directional buttons
163 status.right = x > properties_x.threshold;
164 status.left = x < -properties_x.threshold;
165 status.up = y > properties_y.threshold;
166 status.down = y < -properties_y.threshold;
167
168 return status;
169}
170
171Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
172 Common::Input::TouchStatus status{};
173
174 switch (callback.type) {
175 case Common::Input::InputType::Touch:
176 status = callback.touch_status;
177 break;
178 case Common::Input::InputType::Stick:
179 status.x = callback.stick_status.x;
180 status.y = callback.stick_status.y;
181 break;
182 default:
183 LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
184 break;
185 }
186
187 SanitizeAnalog(status.x, true);
188 SanitizeAnalog(status.y, true);
189 float& x = status.x.value;
190 float& y = status.y.value;
191
192 // Adjust if value is inverted
193 x = status.x.properties.inverted ? 1.0f + x : x;
194 y = status.y.properties.inverted ? 1.0f + y : y;
195
196 // clamp value
197 x = std::clamp(x, 0.0f, 1.0f);
198 y = std::clamp(y, 0.0f, 1.0f);
199
200 if (status.pressed.inverted) {
201 status.pressed.value = !status.pressed.value;
202 }
203
204 return status;
205}
206
207Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
208 Common::Input::TriggerStatus status{};
209 float& raw_value = status.analog.raw_value;
210 bool calculate_button_value = true;
211
212 switch (callback.type) {
213 case Common::Input::InputType::Analog:
214 status.analog.properties = callback.analog_status.properties;
215 raw_value = callback.analog_status.raw_value;
216 break;
217 case Common::Input::InputType::Button:
218 status.analog.properties.range = 1.0f;
219 status.analog.properties.inverted = callback.button_status.inverted;
220 raw_value = callback.button_status.value ? 1.0f : 0.0f;
221 break;
222 case Common::Input::InputType::Trigger:
223 status = callback.trigger_status;
224 calculate_button_value = false;
225 break;
226 default:
227 LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
228 break;
229 }
230
231 SanitizeAnalog(status.analog, true);
232 const auto& properties = status.analog.properties;
233 float& value = status.analog.value;
234
235 // Set button status
236 if (calculate_button_value) {
237 status.pressed.value = value > properties.threshold;
238 }
239
240 // Adjust if value is inverted
241 value = properties.inverted ? 1.0f + value : value;
242
243 // clamp value
244 value = std::clamp(value, 0.0f, 1.0f);
245
246 return status;
247}
248
249Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
250 Common::Input::AnalogStatus status{};
251
252 switch (callback.type) {
253 case Common::Input::InputType::Analog:
254 status.properties = callback.analog_status.properties;
255 status.raw_value = callback.analog_status.raw_value;
256 break;
257 default:
258 LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
259 break;
260 }
261
262 SanitizeAnalog(status, false);
263
264 // Adjust if value is inverted
265 status.value = status.properties.inverted ? -status.value : status.value;
266
267 return status;
268}
269
270void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
271 const auto& properties = analog.properties;
272 float& raw_value = analog.raw_value;
273 float& value = analog.value;
274
275 if (!std::isnormal(raw_value)) {
276 raw_value = 0;
277 }
278
279 // Apply center offset
280 raw_value -= properties.offset;
281
282 // Set initial values to be formated
283 value = raw_value;
284
285 // Calculate vector size
286 const float r = std::abs(value);
287
288 // Return zero if value is smaller than the deadzone
289 if (r <= properties.deadzone || properties.deadzone == 1.0f) {
290 analog.value = 0;
291 return;
292 }
293
294 // Adjust range of value
295 const float deadzone_factor =
296 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
297 value = value * deadzone_factor / properties.range;
298
299 // Invert direction if needed
300 if (properties.inverted) {
301 value = -value;
302 }
303
304 // Clamp value
305 if (clamp_value) {
306 value = std::clamp(value, -1.0f, 1.0f);
307 }
308}
309
310void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
311 bool clamp_value) {
312 const auto& properties_x = analog_x.properties;
313 const auto& properties_y = analog_y.properties;
314 float& raw_x = analog_x.raw_value;
315 float& raw_y = analog_y.raw_value;
316 float& x = analog_x.value;
317 float& y = analog_y.value;
318
319 if (!std::isnormal(raw_x)) {
320 raw_x = 0;
321 }
322 if (!std::isnormal(raw_y)) {
323 raw_y = 0;
324 }
325
326 // Apply center offset
327 raw_x += properties_x.offset;
328 raw_y += properties_y.offset;
329
330 // Apply X scale correction from offset
331 if (std::abs(properties_x.offset) < 0.5f) {
332 if (raw_x > 0) {
333 raw_x /= 1 + properties_x.offset;
334 } else {
335 raw_x /= 1 - properties_x.offset;
336 }
337 }
338
339 // Apply Y scale correction from offset
340 if (std::abs(properties_y.offset) < 0.5f) {
341 if (raw_y > 0) {
342 raw_y /= 1 + properties_y.offset;
343 } else {
344 raw_y /= 1 - properties_y.offset;
345 }
346 }
347
348 // Invert direction if needed
349 raw_x = properties_x.inverted ? -raw_x : raw_x;
350 raw_y = properties_y.inverted ? -raw_y : raw_y;
351
352 // Set initial values to be formated
353 x = raw_x;
354 y = raw_y;
355
356 // Calculate vector size
357 float r = x * x + y * y;
358 r = std::sqrt(r);
359
360 // TODO(German77): Use deadzone and range of both axis
361
362 // Return zero if values are smaller than the deadzone
363 if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
364 x = 0;
365 y = 0;
366 return;
367 }
368
369 // Adjust range of joystick
370 const float deadzone_factor =
371 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
372 x = x * deadzone_factor / properties_x.range;
373 y = y * deadzone_factor / properties_x.range;
374 r = r * deadzone_factor / properties_x.range;
375
376 // Normalize joystick
377 if (clamp_value && r > 1.0f) {
378 x /= r;
379 y /= r;
380 }
381}
382
383} // namespace Core::HID
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
new file mode 100644
index 000000000..1492489d7
--- /dev/null
+++ b/src/core/hid/input_converter.h
@@ -0,0 +1,95 @@
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
7namespace Common::Input {
8struct CallbackStatus;
9enum class BatteryLevel : u32;
10using BatteryStatus = BatteryLevel;
11struct AnalogStatus;
12struct ButtonStatus;
13struct MotionStatus;
14struct StickStatus;
15struct TouchStatus;
16struct TriggerStatus;
17}; // namespace Common::Input
18
19namespace Core::HID {
20
21/**
22 * Converts raw input data into a valid battery status.
23 *
24 * @param Supported callbacks: Analog, Battery, Trigger.
25 * @return A valid BatteryStatus object.
26 */
27Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
28
29/**
30 * Converts raw input data into a valid button status. Applies invert properties to the output.
31 *
32 * @param Supported callbacks: Analog, Button, Trigger.
33 * @return A valid TouchStatus object.
34 */
35Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
36
37/**
38 * Converts raw input data into a valid motion status.
39 *
40 * @param Supported callbacks: Motion.
41 * @return A valid TouchStatus object.
42 */
43Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
44
45/**
46 * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
47 * properties to the output.
48 *
49 * @param Supported callbacks: Stick.
50 * @return A valid StickStatus object.
51 */
52Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
53
54/**
55 * Converts raw input data into a valid touch status.
56 *
57 * @param Supported callbacks: Touch.
58 * @return A valid TouchStatus object.
59 */
60Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
61
62/**
63 * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
64 * invert properties to the output. Button status uses the threshold property if necessary.
65 *
66 * @param Supported callbacks: Analog, Button, Trigger.
67 * @return A valid TriggerStatus object.
68 */
69Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
70
71/**
72 * Converts raw input data into a valid analog status. Applies offset, deadzone, range and
73 * invert properties to the output.
74 *
75 * @param Supported callbacks: Analog.
76 * @return A valid AnalogStatus object.
77 */
78Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
79
80/**
81 * Converts raw analog data into a valid analog value
82 * @param An analog object containing raw data and properties, bool that determines if the value
83 * needs to be clamped between -1.0f and 1.0f.
84 */
85void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
86
87/**
88 * Converts raw stick data into a valid stick value
89 * @param Two analog objects containing raw data and properties, bool that determines if the value
90 * needs to be clamped into the unit circle.
91 */
92void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
93 bool clamp_value);
94
95} // namespace Core::HID
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 9f6a90e8f..870422d82 100644
--- a/src/core/frontend/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -3,7 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/frontend/input_interpreter.h" 6#include "core/hid/hid_types.h"
7#include "core/hid/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid.h" 9#include "core/hle/service/hid/hid.h"
9#include "core/hle/service/sm/sm.h" 10#include "core/hle/service/sm/sm.h"
@@ -19,7 +20,7 @@ InputInterpreter::InputInterpreter(Core::System& system)
19InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
20 21
21void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
22 const u32 button_state = npad.GetAndResetPressState(); 23 const u64 button_state = npad.GetAndResetPressState();
23 24
24 previous_index = current_index; 25 previous_index = current_index;
25 current_index = (current_index + 1) % button_states.size(); 26 current_index = (current_index + 1) % button_states.size();
@@ -31,32 +32,30 @@ void InputInterpreter::ResetButtonStates() {
31 previous_index = 0; 32 previous_index = 0;
32 current_index = 0; 33 current_index = 0;
33 34
34 button_states[0] = 0xFFFFFFFF; 35 button_states[0] = 0xFFFFFFFFFFFFFFFF;
35 36
36 for (std::size_t i = 1; i < button_states.size(); ++i) { 37 for (std::size_t i = 1; i < button_states.size(); ++i) {
37 button_states[i] = 0; 38 button_states[i] = 0;
38 } 39 }
39} 40}
40 41
41bool InputInterpreter::IsButtonPressed(HIDButton button) const { 42bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
42 return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 43 return (button_states[current_index] & static_cast<u64>(button)) != 0;
43} 44}
44 45
45bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { 46bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
46 const bool current_press = 47 const bool current_press = (button_states[current_index] & static_cast<u64>(button)) != 0;
47 (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 48 const bool previous_press = (button_states[previous_index] & static_cast<u64>(button)) != 0;
48 const bool previous_press =
49 (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
50 49
51 return current_press && !previous_press; 50 return current_press && !previous_press;
52} 51}
53 52
54bool InputInterpreter::IsButtonHeld(HIDButton button) const { 53bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
55 u32 held_buttons{button_states[0]}; 54 u64 held_buttons{button_states[0]};
56 55
57 for (std::size_t i = 1; i < button_states.size(); ++i) { 56 for (std::size_t i = 1; i < button_states.size(); ++i) {
58 held_buttons &= button_states[i]; 57 held_buttons &= button_states[i];
59 } 58 }
60 59
61 return (held_buttons & (1U << static_cast<u8>(button))) != 0; 60 return (held_buttons & static_cast<u64>(button)) != 0;
62} 61}
diff --git a/src/core/frontend/input_interpreter.h b/src/core/hid/input_interpreter.h
index 9495e3daf..1c2e02142 100644
--- a/src/core/frontend/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -12,46 +12,14 @@ namespace Core {
12class System; 12class System;
13} 13}
14 14
15namespace Core::HID {
16enum class NpadButton : u64;
17}
18
15namespace Service::HID { 19namespace Service::HID {
16class Controller_NPad; 20class Controller_NPad;
17} 21}
18 22
19enum class HIDButton : u8 {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStickLeft,
39 LStickUp,
40 LStickRight,
41 LStickDown,
42
43 RStickLeft,
44 RStickUp,
45 RStickRight,
46 RStickDown,
47
48 LeftSL,
49 LeftSR,
50
51 RightSL,
52 RightSR,
53};
54
55/** 23/**
56 * The InputInterpreter class interfaces with HID to retrieve button press states. 24 * The InputInterpreter class interfaces with HID to retrieve button press states.
57 * Input is intended to be polled every 50ms so that a button is considered to be 25 * Input is intended to be polled every 50ms so that a button is considered to be
@@ -76,7 +44,7 @@ public:
76 * 44 *
77 * @returns True when the button is pressed. 45 * @returns True when the button is pressed.
78 */ 46 */
79 [[nodiscard]] bool IsButtonPressed(HIDButton button) const; 47 [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
80 48
81 /** 49 /**
82 * Checks whether any of the buttons in the parameter list is pressed. 50 * Checks whether any of the buttons in the parameter list is pressed.
@@ -85,7 +53,7 @@ public:
85 * 53 *
86 * @returns True when at least one of the buttons is pressed. 54 * @returns True when at least one of the buttons is pressed.
87 */ 55 */
88 template <HIDButton... T> 56 template <Core::HID::NpadButton... T>
89 [[nodiscard]] bool IsAnyButtonPressed() { 57 [[nodiscard]] bool IsAnyButtonPressed() {
90 return (IsButtonPressed(T) || ...); 58 return (IsButtonPressed(T) || ...);
91 } 59 }
@@ -98,7 +66,7 @@ public:
98 * 66 *
99 * @returns True when the button is pressed once. 67 * @returns True when the button is pressed once.
100 */ 68 */
101 [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const; 69 [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
102 70
103 /** 71 /**
104 * Checks whether any of the buttons in the parameter list is pressed once. 72 * Checks whether any of the buttons in the parameter list is pressed once.
@@ -107,7 +75,7 @@ public:
107 * 75 *
108 * @returns True when at least one of the buttons is pressed once. 76 * @returns True when at least one of the buttons is pressed once.
109 */ 77 */
110 template <HIDButton... T> 78 template <Core::HID::NpadButton... T>
111 [[nodiscard]] bool IsAnyButtonPressedOnce() const { 79 [[nodiscard]] bool IsAnyButtonPressedOnce() const {
112 return (IsButtonPressedOnce(T) || ...); 80 return (IsButtonPressedOnce(T) || ...);
113 } 81 }
@@ -119,7 +87,7 @@ public:
119 * 87 *
120 * @returns True when the button is held down. 88 * @returns True when the button is held down.
121 */ 89 */
122 [[nodiscard]] bool IsButtonHeld(HIDButton button) const; 90 [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
123 91
124 /** 92 /**
125 * Checks whether any of the buttons in the parameter list is held down. 93 * Checks whether any of the buttons in the parameter list is held down.
@@ -128,7 +96,7 @@ public:
128 * 96 *
129 * @returns True when at least one of the buttons is held down. 97 * @returns True when at least one of the buttons is held down.
130 */ 98 */
131 template <HIDButton... T> 99 template <Core::HID::NpadButton... T>
132 [[nodiscard]] bool IsAnyButtonHeld() const { 100 [[nodiscard]] bool IsAnyButtonHeld() const {
133 return (IsButtonHeld(T) || ...); 101 return (IsButtonHeld(T) || ...);
134 } 102 }
@@ -137,7 +105,7 @@ private:
137 Service::HID::Controller_NPad& npad; 105 Service::HID::Controller_NPad& npad;
138 106
139 /// Stores 9 consecutive button states polled from HID. 107 /// Stores 9 consecutive button states polled from HID.
140 std::array<u32, 9> button_states{}; 108 std::array<u64, 9> button_states{};
141 109
142 std::size_t previous_index{}; 110 std::size_t previous_index{};
143 std::size_t current_index{}; 111 std::size_t current_index{};
diff --git a/src/input_common/motion_input.cpp b/src/core/hid/motion_input.cpp
index 1c9d561c0..c25fea966 100644
--- a/src/input_common/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -2,13 +2,21 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included 3// Refer to the license.txt file included
4 4
5#include <random>
6#include "common/math_util.h" 5#include "common/math_util.h"
7#include "input_common/motion_input.h" 6#include "core/hid/motion_input.h"
8 7
9namespace InputCommon { 8namespace Core::HID {
10 9
11MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {} 10MotionInput::MotionInput() {
11 // Initialize PID constants with default values
12 SetPID(0.3f, 0.005f, 0.0f);
13}
14
15void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
16 kp = new_kp;
17 ki = new_ki;
18 kd = new_kd;
19}
12 20
13void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { 21void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
14 accel = acceleration; 22 accel = acceleration;
@@ -65,6 +73,8 @@ void MotionInput::UpdateRotation(u64 elapsed_time) {
65 rotations += gyro * sample_period; 73 rotations += gyro * sample_period;
66} 74}
67 75
76// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
77// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
68void MotionInput::UpdateOrientation(u64 elapsed_time) { 78void MotionInput::UpdateOrientation(u64 elapsed_time) {
69 if (!IsCalibrated(0.1f)) { 79 if (!IsCalibrated(0.1f)) {
70 ResetOrientation(); 80 ResetOrientation();
@@ -190,43 +200,6 @@ Common::Vec3f MotionInput::GetRotations() const {
190 return rotations; 200 return rotations;
191} 201}
192 202
193Input::MotionStatus MotionInput::GetMotion() const {
194 const Common::Vec3f gyroscope = GetGyroscope();
195 const Common::Vec3f accelerometer = GetAcceleration();
196 const Common::Vec3f rotation = GetRotations();
197 const std::array<Common::Vec3f, 3> orientation = GetOrientation();
198 const Common::Quaternion<f32> quaternion = GetQuaternion();
199 return {accelerometer, gyroscope, rotation, orientation, quaternion};
200}
201
202Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_magnitude) const {
203 std::random_device device;
204 std::mt19937 gen(device());
205 std::uniform_int_distribution<s16> distribution(-1000, 1000);
206 const Common::Vec3f gyroscope{
207 static_cast<f32>(distribution(gen)) * 0.001f,
208 static_cast<f32>(distribution(gen)) * 0.001f,
209 static_cast<f32>(distribution(gen)) * 0.001f,
210 };
211 const Common::Vec3f accelerometer{
212 static_cast<f32>(distribution(gen)) * 0.001f,
213 static_cast<f32>(distribution(gen)) * 0.001f,
214 static_cast<f32>(distribution(gen)) * 0.001f,
215 };
216 constexpr Common::Vec3f rotation;
217 constexpr std::array orientation{
218 Common::Vec3f{1.0f, 0.0f, 0.0f},
219 Common::Vec3f{0.0f, 1.0f, 0.0f},
220 Common::Vec3f{0.0f, 0.0f, 1.0f},
221 };
222 constexpr Common::Quaternion<f32> quaternion{
223 {0.0f, 0.0f, 0.0f},
224 1.0f,
225 };
226 return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation,
227 quaternion};
228}
229
230void MotionInput::ResetOrientation() { 203void MotionInput::ResetOrientation() {
231 if (!reset_enabled || only_accelerometer) { 204 if (!reset_enabled || only_accelerometer) {
232 return; 205 return;
@@ -304,4 +277,4 @@ void MotionInput::SetOrientationFromAccelerometer() {
304 quat = quat.Normalized(); 277 quat = quat.Normalized();
305 } 278 }
306} 279}
307} // namespace InputCommon 280} // namespace Core::HID
diff --git a/src/input_common/motion_input.h b/src/core/hid/motion_input.h
index efe74cf19..5b5b420bb 100644
--- a/src/input_common/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -7,13 +7,12 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/quaternion.h" 8#include "common/quaternion.h"
9#include "common/vector_math.h" 9#include "common/vector_math.h"
10#include "core/frontend/input.h"
11 10
12namespace InputCommon { 11namespace Core::HID {
13 12
14class MotionInput { 13class MotionInput {
15public: 14public:
16 explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); 15 explicit MotionInput();
17 16
18 MotionInput(const MotionInput&) = default; 17 MotionInput(const MotionInput&) = default;
19 MotionInput& operator=(const MotionInput&) = default; 18 MotionInput& operator=(const MotionInput&) = default;
@@ -21,6 +20,7 @@ public:
21 MotionInput(MotionInput&&) = default; 20 MotionInput(MotionInput&&) = default;
22 MotionInput& operator=(MotionInput&&) = default; 21 MotionInput& operator=(MotionInput&&) = default;
23 22
23 void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
24 void SetAcceleration(const Common::Vec3f& acceleration); 24 void SetAcceleration(const Common::Vec3f& acceleration);
25 void SetGyroscope(const Common::Vec3f& gyroscope); 25 void SetGyroscope(const Common::Vec3f& gyroscope);
26 void SetQuaternion(const Common::Quaternion<f32>& quaternion); 26 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
@@ -38,9 +38,6 @@ public:
38 [[nodiscard]] Common::Vec3f GetGyroscope() const; 38 [[nodiscard]] Common::Vec3f GetGyroscope() const;
39 [[nodiscard]] Common::Vec3f GetRotations() const; 39 [[nodiscard]] Common::Vec3f GetRotations() const;
40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; 40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
41 [[nodiscard]] Input::MotionStatus GetMotion() const;
42 [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude,
43 int gyro_magnitude) const;
44 41
45 [[nodiscard]] bool IsMoving(f32 sensitivity) const; 42 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
46 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; 43 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
@@ -59,16 +56,32 @@ private:
59 Common::Vec3f integral_error; 56 Common::Vec3f integral_error;
60 Common::Vec3f derivative_error; 57 Common::Vec3f derivative_error;
61 58
59 // Quaternion containing the device orientation
62 Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f}; 60 Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
61
62 // Number of full rotations in each axis
63 Common::Vec3f rotations; 63 Common::Vec3f rotations;
64
65 // Acceleration vector measurement in G force
64 Common::Vec3f accel; 66 Common::Vec3f accel;
67
68 // Gyroscope vector measurement in radians/s.
65 Common::Vec3f gyro; 69 Common::Vec3f gyro;
70
71 // Vector to be substracted from gyro measurements
66 Common::Vec3f gyro_drift; 72 Common::Vec3f gyro_drift;
67 73
74 // Minimum gyro amplitude to detect if the device is moving
68 f32 gyro_threshold = 0.0f; 75 f32 gyro_threshold = 0.0f;
76
77 // Number of invalid sequential data
69 u32 reset_counter = 0; 78 u32 reset_counter = 0;
79
80 // If the provided data is invalid the device will be autocalibrated
70 bool reset_enabled = true; 81 bool reset_enabled = true;
82
83 // Use accelerometer values to calculate position
71 bool only_accelerometer = true; 84 bool only_accelerometer = true;
72}; 85};
73 86
74} // namespace InputCommon 87} // namespace Core::HID
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 526b87241..9bda5c5b2 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -685,8 +685,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
685 return ResultSuccess; 685 return ResultSuccess;
686} 686}
687 687
688ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size, 688ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
689 KMemoryPermission perm) { 689 KMemoryPermission perm) {
690 690
691 std::lock_guard lock{page_table_lock}; 691 std::lock_guard lock{page_table_lock};
692 692
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 770c4841c..b7ec38f06 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -41,7 +41,7 @@ public:
41 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, 41 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
42 KMemoryPermission perm); 42 KMemoryPermission perm);
43 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); 43 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
44 ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm); 44 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
45 KMemoryInfo QueryInfo(VAddr addr); 45 KMemoryInfo QueryInfo(VAddr addr);
46 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); 46 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
47 ResultCode ResetTransferMemory(VAddr addr, std::size_t size); 47 ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 76fd8c285..1aad061e1 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -528,7 +528,7 @@ void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
528 std::lock_guard lock{HLE::g_hle_lock}; 528 std::lock_guard lock{HLE::g_hle_lock};
529 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 529 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
530 KMemoryPermission permission) { 530 KMemoryPermission permission) {
531 page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); 531 page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
532 }; 532 };
533 533
534 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), 534 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e42a6d36f..45e86a677 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -300,15 +300,16 @@ struct KernelCore::Impl {
300 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 300 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
301 KThread* GetHostDummyThread() { 301 KThread* GetHostDummyThread() {
302 auto make_thread = [this]() { 302 auto make_thread = [this]() {
303 std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel()); 303 std::lock_guard lk(dummy_thread_lock);
304 auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
304 KAutoObject::Create(thread.get()); 305 KAutoObject::Create(thread.get());
305 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); 306 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
306 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 307 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
307 return thread; 308 return thread.get();
308 }; 309 };
309 310
310 thread_local auto thread = make_thread(); 311 thread_local KThread* saved_thread = make_thread();
311 return thread.get(); 312 return saved_thread;
312 } 313 }
313 314
314 /// Registers a CPU core thread by allocating a host thread ID for it 315 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -695,6 +696,12 @@ struct KernelCore::Impl {
695 return port; 696 return port;
696 } 697 }
697 698
699 std::mutex server_ports_lock;
700 std::mutex server_sessions_lock;
701 std::mutex registered_objects_lock;
702 std::mutex registered_in_use_objects_lock;
703 std::mutex dummy_thread_lock;
704
698 std::atomic<u32> next_object_id{0}; 705 std::atomic<u32> next_object_id{0};
699 std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; 706 std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
700 std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; 707 std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
@@ -725,10 +732,6 @@ struct KernelCore::Impl {
725 std::unordered_set<KServerSession*> server_sessions; 732 std::unordered_set<KServerSession*> server_sessions;
726 std::unordered_set<KAutoObject*> registered_objects; 733 std::unordered_set<KAutoObject*> registered_objects;
727 std::unordered_set<KAutoObject*> registered_in_use_objects; 734 std::unordered_set<KAutoObject*> registered_in_use_objects;
728 std::mutex server_ports_lock;
729 std::mutex server_sessions_lock;
730 std::mutex registered_objects_lock;
731 std::mutex registered_in_use_objects_lock;
732 735
733 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 736 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
734 std::vector<Kernel::PhysicalCore> cores; 737 std::vector<Kernel::PhysicalCore> cores;
@@ -753,6 +756,9 @@ struct KernelCore::Impl {
753 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; 756 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
754 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 757 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
755 758
759 // Specifically tracked to be automatically destroyed with kernel
760 std::vector<std::unique_ptr<KThread>> dummy_threads;
761
756 bool is_multicore{}; 762 bool is_multicore{};
757 bool is_phantom_mode_for_singlecore{}; 763 bool is_phantom_mode_for_singlecore{};
758 u32 single_core_thread_id{}; 764 u32 single_core_thread_id{};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f9d99bc51..f0cd8471e 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1169,6 +1169,8 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) {
1169 return GetCurrentProcessorNumber(system); 1169 return GetCurrentProcessorNumber(system);
1170} 1170}
1171 1171
1172namespace {
1173
1172constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { 1174constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1173 switch (perm) { 1175 switch (perm) {
1174 case Svc::MemoryPermission::Read: 1176 case Svc::MemoryPermission::Read:
@@ -1179,10 +1181,24 @@ constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1179 } 1181 }
1180} 1182}
1181 1183
1182constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { 1184[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
1183 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; 1185 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
1184} 1186}
1185 1187
1188constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
1189 switch (perm) {
1190 case Svc::MemoryPermission::None:
1191 case Svc::MemoryPermission::Read:
1192 case Svc::MemoryPermission::ReadWrite:
1193 case Svc::MemoryPermission::ReadExecute:
1194 return true;
1195 default:
1196 return false;
1197 }
1198}
1199
1200} // Anonymous namespace
1201
1186static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, 1202static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
1187 u64 size, Svc::MemoryPermission map_perm) { 1203 u64 size, Svc::MemoryPermission map_perm) {
1188 LOG_TRACE(Kernel_SVC, 1204 LOG_TRACE(Kernel_SVC,
@@ -1262,6 +1278,34 @@ static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle,
1262 return UnmapSharedMemory(system, shmem_handle, address, size); 1278 return UnmapSharedMemory(system, shmem_handle, address, size);
1263} 1279}
1264 1280
1281static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
1282 VAddr address, u64 size, Svc::MemoryPermission perm) {
1283 LOG_TRACE(Kernel_SVC,
1284 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1285 process_handle, address, size, perm);
1286
1287 // Validate the address/size.
1288 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1289 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1290 R_UNLESS(size > 0, ResultInvalidSize);
1291 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1292
1293 // Validate the memory permission.
1294 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1295
1296 // Get the process from its handle.
1297 KScopedAutoObject process =
1298 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
1299 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
1300
1301 // Validate that the address is in range.
1302 auto& page_table = process->PageTable();
1303 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
1304
1305 // Set the memory permission.
1306 return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
1307}
1308
1265static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1309static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1266 VAddr page_info_address, Handle process_handle, 1310 VAddr page_info_address, Handle process_handle,
1267 VAddr address) { 1311 VAddr address) {
@@ -1459,10 +1503,14 @@ static void ExitProcess32(Core::System& system) {
1459 ExitProcess(system); 1503 ExitProcess(system);
1460} 1504}
1461 1505
1462static constexpr bool IsValidVirtualCoreId(int32_t core_id) { 1506namespace {
1507
1508constexpr bool IsValidVirtualCoreId(int32_t core_id) {
1463 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); 1509 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
1464} 1510}
1465 1511
1512} // Anonymous namespace
1513
1466/// Creates a new thread 1514/// Creates a new thread
1467static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, 1515static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1468 VAddr stack_bottom, u32 priority, s32 core_id) { 1516 VAddr stack_bottom, u32 priority, s32 core_id) {
@@ -1846,7 +1894,9 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) {
1846 return ResetSignal(system, handle); 1894 return ResetSignal(system, handle);
1847} 1895}
1848 1896
1849static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { 1897namespace {
1898
1899constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
1850 switch (perm) { 1900 switch (perm) {
1851 case MemoryPermission::None: 1901 case MemoryPermission::None:
1852 case MemoryPermission::Read: 1902 case MemoryPermission::Read:
@@ -1857,6 +1907,8 @@ static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
1857 } 1907 }
1858} 1908}
1859 1909
1910} // Anonymous namespace
1911
1860/// Creates a TransferMemory object 1912/// Creates a TransferMemory object
1861static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, 1913static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
1862 MemoryPermission map_perm) { 1914 MemoryPermission map_perm) {
@@ -2588,7 +2640,7 @@ static const FunctionDef SVC_Table_64[] = {
2588 {0x70, nullptr, "CreatePort"}, 2640 {0x70, nullptr, "CreatePort"},
2589 {0x71, nullptr, "ManageNamedPort"}, 2641 {0x71, nullptr, "ManageNamedPort"},
2590 {0x72, nullptr, "ConnectToPort"}, 2642 {0x72, nullptr, "ConnectToPort"},
2591 {0x73, nullptr, "SetProcessMemoryPermission"}, 2643 {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
2592 {0x74, nullptr, "MapProcessMemory"}, 2644 {0x74, nullptr, "MapProcessMemory"},
2593 {0x75, nullptr, "UnmapProcessMemory"}, 2645 {0x75, nullptr, "UnmapProcessMemory"},
2594 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, 2646 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 2721679c1..d073f2210 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -10,6 +10,9 @@
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/frontend/applets/controller.h" 12#include "core/frontend/applets/controller.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
15#include "core/hid/hid_types.h"
13#include "core/hle/result.h" 16#include "core/hle/result.h"
14#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/applet_controller.h" 18#include "core/hle/service/am/applets/applet_controller.h"
@@ -25,7 +28,7 @@ namespace Service::AM::Applets {
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters( 28static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, 29 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { 30 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NpadStyleSet npad_style_set; 31 Core::HID::NpadStyleTag npad_style_set;
29 npad_style_set.raw = private_arg.style_set; 32 npad_style_set.raw = private_arg.style_set;
30 33
31 return { 34 return {
@@ -243,19 +246,11 @@ void Controller::Execute() {
243void Controller::ConfigurationComplete() { 246void Controller::ConfigurationComplete() {
244 ControllerSupportResultInfo result_info{}; 247 ControllerSupportResultInfo result_info{};
245 248
246 const auto& players = Settings::values.players.GetValue();
247
248 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. 249 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
249 // Otherwise, only count connected players from P1-P8. 250 // Otherwise, only count connected players from P1-P8.
250 result_info.player_count = 251 result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
251 is_single_mode 252
252 ? 1 253 result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
253 : static_cast<s8>(std::count_if(players.begin(), players.end() - 2,
254 [](const auto& player) { return player.connected; }));
255
256 result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance(
257 players.begin(), std::find_if(players.begin(), players.end(),
258 [](const auto& player) { return player.connected; })));
259 254
260 result_info.result = 0; 255 result_info.result = 0;
261 256
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index 0a34c4fc0..1a832505e 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -16,6 +16,10 @@ namespace Core {
16class System; 16class System;
17} 17}
18 18
19namespace Core::HID {
20enum class NpadStyleSet : u32;
21}
22
19namespace Service::AM::Applets { 23namespace Service::AM::Applets {
20 24
21using IdentificationColor = std::array<u8, 4>; 25using IdentificationColor = std::array<u8, 4>;
@@ -52,7 +56,7 @@ struct ControllerSupportArgPrivate {
52 bool flag_1{}; 56 bool flag_1{};
53 ControllerSupportMode mode{}; 57 ControllerSupportMode mode{};
54 ControllerSupportCaller caller{}; 58 ControllerSupportCaller caller{};
55 u32 style_set{}; 59 Core::HID::NpadStyleSet style_set{};
56 u32 joy_hold_type{}; 60 u32 joy_hold_type{};
57}; 61};
58static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, 62static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7320b1c0f..134ac1ee2 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() {
231void AppletManager::SetDefaultAppletsIfMissing() { 231void AppletManager::SetDefaultAppletsIfMissing() {
232 if (frontend.controller == nullptr) { 232 if (frontend.controller == nullptr) {
233 frontend.controller = 233 frontend.controller =
234 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); 234 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
235 } 235 }
236 236
237 if (frontend.error == nullptr) { 237 if (frontend.error == nullptr) {
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index bda6e2557..f0f3105dc 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -4,13 +4,18 @@
4 4
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/core_timing.h" 6#include "core/core_timing.h"
7#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/controllers/console_sixaxis.h" 9#include "core/hle/service/hid/controllers/console_sixaxis.h"
8 10
9namespace Service::HID { 11namespace Service::HID {
10constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
11 13
12Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_) 14Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_)
13 : ControllerBase{system_} {} 15 : ControllerBase{hid_core_} {
16 console = hid_core.GetEmulatedConsole();
17}
18
14Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; 19Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
15 20
16void Controller_ConsoleSixAxis::OnInit() {} 21void Controller_ConsoleSixAxis::OnInit() {}
@@ -19,44 +24,31 @@ void Controller_ConsoleSixAxis::OnRelease() {}
19 24
20void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 25void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 26 std::size_t size) {
22 seven_six_axis.header.timestamp = core_timing.GetCPUTicks();
23 seven_six_axis.header.total_entry_count = 17;
24
25 if (!IsControllerActivated() || !is_transfer_memory_set) { 27 if (!IsControllerActivated() || !is_transfer_memory_set) {
26 seven_six_axis.header.entry_count = 0; 28 seven_sixaxis_lifo.buffer_count = 0;
27 seven_six_axis.header.last_entry_index = 0; 29 seven_sixaxis_lifo.buffer_tail = 0;
28 return; 30 return;
29 } 31 }
30 seven_six_axis.header.entry_count = 16;
31
32 const auto& last_entry =
33 seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
34 seven_six_axis.header.last_entry_index = (seven_six_axis.header.last_entry_index + 1) % 17;
35 auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
36 32
37 cur_entry.sampling_number = last_entry.sampling_number + 1; 33 const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
38 cur_entry.sampling_number2 = cur_entry.sampling_number; 34 next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
39 35
40 // Try to read sixaxis sensor states 36 // Try to read sixaxis sensor states
41 MotionDevice motion_device{}; 37 const auto motion_status = console->GetMotion();
42 const auto& device = motions[0]; 38
43 if (device) { 39 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
44 std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation,
45 motion_device.orientation, motion_device.quaternion) = device->GetStatus();
46 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f;
47 }
48 40
49 cur_entry.accel = motion_device.accel; 41 next_seven_sixaxis_state.accel = motion_status.accel;
50 // Zero gyro values as they just mess up with the camera 42 // Zero gyro values as they just mess up with the camera
51 // Note: Probably a correct sensivity setting must be set 43 // Note: Probably a correct sensivity setting must be set
52 cur_entry.gyro = {}; 44 next_seven_sixaxis_state.gyro = {};
53 cur_entry.quaternion = { 45 next_seven_sixaxis_state.quaternion = {
54 { 46 {
55 motion_device.quaternion.xyz.y, 47 motion_status.quaternion.xyz.y,
56 motion_device.quaternion.xyz.x, 48 motion_status.quaternion.xyz.x,
57 -motion_device.quaternion.w, 49 -motion_status.quaternion.w,
58 }, 50 },
59 -motion_device.quaternion.xyz.z, 51 -motion_status.quaternion.xyz.z,
60 }; 52 };
61 53
62 console_six_axis.sampling_number++; 54 console_six_axis.sampling_number++;
@@ -67,14 +59,8 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
67 // Update console six axis shared memory 59 // Update console six axis shared memory
68 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); 60 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
69 // Update seven six axis transfer memory 61 // Update seven six axis transfer memory
70 std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis)); 62 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
71} 63 std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
72
73void Controller_ConsoleSixAxis::OnLoadInputDevices() {
74 const auto player = Settings::values.players.GetValue()[0];
75 std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
76 player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(),
77 Input::CreateDevice<Input::MotionDevice>);
78} 64}
79 65
80void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) { 66void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
@@ -83,8 +69,7 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
83} 69}
84 70
85void Controller_ConsoleSixAxis::ResetTimestamp() { 71void Controller_ConsoleSixAxis::ResetTimestamp() {
86 auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index]; 72 seven_sixaxis_lifo.buffer_count = 0;
87 cur_entry.sampling_number = 0; 73 seven_sixaxis_lifo.buffer_tail = 0;
88 cur_entry.sampling_number2 = 0;
89} 74}
90} // namespace Service::HID 75} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index fd8a427af..279241858 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -5,16 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/quaternion.h" 10#include "common/quaternion.h"
11#include "core/frontend/input.h" 11#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedConsole;
17} // namespace Core::HID
13 18
14namespace Service::HID { 19namespace Service::HID {
15class Controller_ConsoleSixAxis final : public ControllerBase { 20class Controller_ConsoleSixAxis final : public ControllerBase {
16public: 21public:
17 explicit Controller_ConsoleSixAxis(Core::System& system_); 22 explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
18 ~Controller_ConsoleSixAxis() override; 23 ~Controller_ConsoleSixAxis() override;
19 24
20 // Called when the controller is initialized 25 // Called when the controller is initialized
@@ -26,9 +31,6 @@ public:
26 // When the controller is requesting an update for the shared memory 31 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 32 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 33
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32 // Called on InitializeSevenSixAxisSensor 34 // Called on InitializeSevenSixAxisSensor
33 void SetTransferMemoryPointer(u8* t_mem); 35 void SetTransferMemoryPointer(u8* t_mem);
34 36
@@ -38,43 +40,31 @@ public:
38private: 40private:
39 struct SevenSixAxisState { 41 struct SevenSixAxisState {
40 INSERT_PADDING_WORDS(4); // unused 42 INSERT_PADDING_WORDS(4); // unused
41 s64_le sampling_number{}; 43 s64 sampling_number{};
42 s64_le sampling_number2{};
43 u64 unknown{}; 44 u64 unknown{};
44 Common::Vec3f accel{}; 45 Common::Vec3f accel{};
45 Common::Vec3f gyro{}; 46 Common::Vec3f gyro{};
46 Common::Quaternion<f32> quaternion{}; 47 Common::Quaternion<f32> quaternion{};
47 }; 48 };
48 static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size"); 49 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
49
50 struct SevenSixAxisMemory {
51 CommonHeader header{};
52 std::array<SevenSixAxisState, 0x21> sevensixaxis_states{};
53 };
54 static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size");
55 50
51 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
56 struct ConsoleSharedMemory { 52 struct ConsoleSharedMemory {
57 u64_le sampling_number{}; 53 u64 sampling_number{};
58 bool is_seven_six_axis_sensor_at_rest{}; 54 bool is_seven_six_axis_sensor_at_rest{};
55 INSERT_PADDING_BYTES(4); // padding
59 f32 verticalization_error{}; 56 f32 verticalization_error{};
60 Common::Vec3f gyro_bias{}; 57 Common::Vec3f gyro_bias{};
61 }; 58 };
62 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); 59 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
63 60
64 struct MotionDevice { 61 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
65 Common::Vec3f accel; 62 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
66 Common::Vec3f gyro;
67 Common::Vec3f rotation;
68 std::array<Common::Vec3f, 3> orientation;
69 Common::Quaternion<f32> quaternion;
70 };
71 63
72 using MotionArray = 64 Core::HID::EmulatedConsole* console;
73 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>;
74 MotionArray motions;
75 u8* transfer_memory = nullptr; 65 u8* transfer_memory = nullptr;
76 bool is_transfer_memory_set = false; 66 bool is_transfer_memory_set = false;
77 ConsoleSharedMemory console_six_axis{}; 67 ConsoleSharedMemory console_six_axis{};
78 SevenSixAxisMemory seven_six_axis{}; 68 SevenSixAxisState next_seven_sixaxis_state{};
79}; 69};
80} // namespace Service::HID 70} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 9d1e6db6a..788ae9ae7 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -6,12 +6,12 @@
6 6
7namespace Service::HID { 7namespace Service::HID {
8 8
9ControllerBase::ControllerBase(Core::System& system_) : system(system_) {} 9ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
10ControllerBase::~ControllerBase() = default; 10ControllerBase::~ControllerBase() = default;
11 11
12void ControllerBase::ActivateController() { 12void ControllerBase::ActivateController() {
13 if (is_activated) { 13 if (is_activated) {
14 OnRelease(); 14 return;
15 } 15 }
16 is_activated = true; 16 is_activated = true;
17 OnInit(); 17 OnInit();
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 1556fb08e..7450eb20a 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -11,14 +11,14 @@ namespace Core::Timing {
11class CoreTiming; 11class CoreTiming;
12} 12}
13 13
14namespace Core { 14namespace Core::HID {
15class System; 15class HIDCore;
16} 16}
17 17
18namespace Service::HID { 18namespace Service::HID {
19class ControllerBase { 19class ControllerBase {
20public: 20public:
21 explicit ControllerBase(Core::System& system_); 21 explicit ControllerBase(Core::HID::HIDCore& hid_core_);
22 virtual ~ControllerBase(); 22 virtual ~ControllerBase();
23 23
24 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -35,26 +35,17 @@ public:
35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
36 std::size_t size) {} 36 std::size_t size) {}
37 37
38 // Called when input devices should be loaded
39 virtual void OnLoadInputDevices() = 0;
40
41 void ActivateController(); 38 void ActivateController();
42 39
43 void DeactivateController(); 40 void DeactivateController();
44 41
45 bool IsControllerActivated() const; 42 bool IsControllerActivated() const;
46 43
44 static const std::size_t hid_entry_count = 17;
45
47protected: 46protected:
48 bool is_activated{false}; 47 bool is_activated{false};
49 48
50 struct CommonHeader { 49 Core::HID::HIDCore& hid_core;
51 s64_le timestamp;
52 s64_le total_entry_count;
53 s64_le last_entry_index;
54 s64_le entry_count;
55 };
56 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
57
58 Core::System& system;
59}; 50};
60} // namespace Service::HID 51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index d439b8fb0..6a6fb9cab 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,15 +6,19 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/debug_pad.h" 12#include "core/hle/service/hid/controllers/debug_pad.h"
10 13
11namespace Service::HID { 14namespace Service::HID {
15constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
12 16
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 17Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_)
14[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 18 : ControllerBase{hid_core_} {
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 19 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
20}
16 21
17Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {}
18Controller_DebugPad::~Controller_DebugPad() = default; 22Controller_DebugPad::~Controller_DebugPad() = default;
19 23
20void Controller_DebugPad::OnInit() {} 24void Controller_DebugPad::OnInit() {}
@@ -23,63 +27,29 @@ void Controller_DebugPad::OnRelease() {}
23 27
24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 28void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
25 std::size_t size) { 29 std::size_t size) {
26 shared_memory.header.timestamp = core_timing.GetCPUTicks();
27 shared_memory.header.total_entry_count = 17;
28
29 if (!IsControllerActivated()) { 30 if (!IsControllerActivated()) {
30 shared_memory.header.entry_count = 0; 31 debug_pad_lifo.buffer_count = 0;
31 shared_memory.header.last_entry_index = 0; 32 debug_pad_lifo.buffer_tail = 0;
33 std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
32 return; 34 return;
33 } 35 }
34 shared_memory.header.entry_count = 16;
35 36
36 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; 37 const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
37 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 38 next_state.sampling_number = last_entry.sampling_number + 1;
38 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
39
40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 39
43 if (Settings::values.debug_pad_enabled) { 40 if (Settings::values.debug_pad_enabled) {
44 cur_entry.attribute.connected.Assign(1); 41 next_state.attribute.connected.Assign(1);
45 auto& pad = cur_entry.pad_state;
46 42
47 using namespace Settings::NativeButton; 43 const auto& button_state = controller->GetDebugPadButtons();
48 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); 44 const auto& stick_state = controller->GetSticks();
49 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
50 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
51 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
52 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
53 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
54 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
55 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
56 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
57 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
60 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
61 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
62 45
63 const auto [stick_l_x_f, stick_l_y_f] = 46 next_state.pad_state = button_state;
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 47 next_state.l_stick = stick_state.left;
65 const auto [stick_r_x_f, stick_r_y_f] = 48 next_state.r_stick = stick_state.right;
66 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
67 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
68 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
69 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
70 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
71 } 49 }
72 50
73 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 51 debug_pad_lifo.WriteNextEntry(next_state);
52 std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
74} 53}
75 54
76void Controller_DebugPad::OnLoadInputDevices() {
77 std::transform(Settings::values.debug_pad_buttons.begin(),
78 Settings::values.debug_pad_buttons.begin() +
79 Settings::NativeButton::NUM_BUTTONS_HID,
80 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
81 std::transform(Settings::values.debug_pad_analogs.begin(),
82 Settings::values.debug_pad_analogs.end(), analogs.begin(),
83 Input::CreateDevice<Input::AnalogDevice>);
84}
85} // namespace Service::HID 55} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 1b1645184..afe374fc2 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -8,15 +8,20 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "common/swap.h" 11#include "common/swap.h"
13#include "core/frontend/input.h"
14#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedController;
17struct DebugPadButton;
18struct AnalogStickState;
19} // namespace Core::HID
15 20
16namespace Service::HID { 21namespace Service::HID {
17class Controller_DebugPad final : public ControllerBase { 22class Controller_DebugPad final : public ControllerBase {
18public: 23public:
19 explicit Controller_DebugPad(Core::System& system_); 24 explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_);
20 ~Controller_DebugPad() override; 25 ~Controller_DebugPad() override;
21 26
22 // Called when the controller is initialized 27 // Called when the controller is initialized
@@ -28,66 +33,31 @@ public:
28 // When the controller is requesting an update for the shared memory 33 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 34 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 35
31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override;
33
34private: 36private:
35 struct AnalogStick { 37 // This is nn::hid::DebugPadAttribute
36 s32_le x; 38 struct DebugPadAttribute {
37 s32_le y;
38 };
39 static_assert(sizeof(AnalogStick) == 0x8);
40
41 struct PadState {
42 union {
43 u32_le raw{};
44 BitField<0, 1, u32> a;
45 BitField<1, 1, u32> b;
46 BitField<2, 1, u32> x;
47 BitField<3, 1, u32> y;
48 BitField<4, 1, u32> l;
49 BitField<5, 1, u32> r;
50 BitField<6, 1, u32> zl;
51 BitField<7, 1, u32> zr;
52 BitField<8, 1, u32> plus;
53 BitField<9, 1, u32> minus;
54 BitField<10, 1, u32> d_left;
55 BitField<11, 1, u32> d_up;
56 BitField<12, 1, u32> d_right;
57 BitField<13, 1, u32> d_down;
58 };
59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
61
62 struct Attributes {
63 union { 39 union {
64 u32_le raw{}; 40 u32 raw{};
65 BitField<0, 1, u32> connected; 41 BitField<0, 1, u32> connected;
66 }; 42 };
67 }; 43 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 44 static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
69 45
70 struct PadStates { 46 // This is nn::hid::DebugPadState
71 s64_le sampling_number; 47 struct DebugPadState {
72 s64_le sampling_number2; 48 s64 sampling_number;
73 Attributes attribute; 49 DebugPadAttribute attribute;
74 PadState pad_state; 50 Core::HID::DebugPadButton pad_state;
75 AnalogStick r_stick; 51 Core::HID::AnalogStickState r_stick;
76 AnalogStick l_stick; 52 Core::HID::AnalogStickState l_stick;
77 }; 53 };
78 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state"); 54 static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
79 55
80 struct SharedMemory { 56 // This is nn::hid::detail::DebugPadLifo
81 CommonHeader header; 57 Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
82 std::array<PadStates, 17> pad_states; 58 static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
83 INSERT_PADDING_BYTES(0x138); 59 DebugPadState next_state{};
84 };
85 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
86 SharedMemory shared_memory{};
87 60
88 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 61 Core::HID::EmulatedController* controller;
89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs;
92}; 62};
93} // namespace Service::HID 63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 764abb5b6..fe895c4f6 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -7,6 +7,7 @@
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/gesture.h" 11#include "core/hle/service/hid/controllers/gesture.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
@@ -23,16 +24,14 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 24 return static_cast<f32>(num * num);
24} 25}
25 26
26Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {} 27Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
28 console = hid_core.GetEmulatedConsole();
29}
27Controller_Gesture::~Controller_Gesture() = default; 30Controller_Gesture::~Controller_Gesture() = default;
28 31
29void Controller_Gesture::OnInit() { 32void Controller_Gesture::OnInit() {
30 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 33 gesture_lifo.buffer_count = 0;
31 mouse_finger_id[id] = MAX_POINTS; 34 gesture_lifo.buffer_tail = 0;
32 keyboard_finger_id[id] = MAX_POINTS;
33 udp_finger_id[id] = MAX_POINTS;
34 }
35 shared_memory.header.entry_count = 0;
36 force_update = true; 35 force_update = true;
37} 36}
38 37
@@ -40,50 +39,38 @@ void Controller_Gesture::OnRelease() {}
40 39
41void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 40void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
42 std::size_t size) { 41 std::size_t size) {
43 shared_memory.header.timestamp = core_timing.GetCPUTicks();
44 shared_memory.header.total_entry_count = 17;
45
46 if (!IsControllerActivated()) { 42 if (!IsControllerActivated()) {
47 shared_memory.header.entry_count = 0; 43 gesture_lifo.buffer_count = 0;
48 shared_memory.header.last_entry_index = 0; 44 gesture_lifo.buffer_tail = 0;
45 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
49 return; 46 return;
50 } 47 }
51 48
52 ReadTouchInput(); 49 ReadTouchInput();
53 50
54 GestureProperties gesture = GetGestureProperties(); 51 GestureProperties gesture = GetGestureProperties();
55 f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) / 52 f32 time_difference =
56 (1000 * 1000 * 1000); 53 static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
57 54
58 // Only update if necesary 55 // Only update if necesary
59 if (!ShouldUpdateGesture(gesture, time_difference)) { 56 if (!ShouldUpdateGesture(gesture, time_difference)) {
60 return; 57 return;
61 } 58 }
62 59
63 last_update_timestamp = shared_memory.header.timestamp; 60 last_update_timestamp = gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(data, size, gesture, time_difference); 61 UpdateGestureSharedMemory(data, size, gesture, time_difference);
65} 62}
66 63
67void Controller_Gesture::ReadTouchInput() { 64void Controller_Gesture::ReadTouchInput() {
68 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 65 const auto touch_status = console->GetTouch();
69 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 66 for (std::size_t id = 0; id < fingers.size(); ++id) {
70 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 67 fingers[id] = touch_status[id];
71 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
72 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
73 }
74
75 if (Settings::values.use_touch_from_button) {
76 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
77 for (std::size_t id = 0; id < mouse_status.size(); ++id) {
78 keyboard_finger_id[id] =
79 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
80 }
81 } 68 }
82} 69}
83 70
84bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 71bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
85 f32 time_difference) { 72 f32 time_difference) {
86 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 73 const auto& last_entry = GetLastGestureEntry();
87 if (force_update) { 74 if (force_update) {
88 force_update = false; 75 force_update = false;
89 return true; 76 return true;
@@ -97,7 +84,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
97 } 84 }
98 85
99 // Update on press and hold event after 0.5 seconds 86 // Update on press and hold event after 0.5 seconds
100 if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 && 87 if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
101 time_difference > press_delay) { 88 time_difference > press_delay) {
102 return enable_press_and_tap; 89 return enable_press_and_tap;
103 } 90 }
@@ -108,27 +95,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
108void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 95void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
109 GestureProperties& gesture, 96 GestureProperties& gesture,
110 f32 time_difference) { 97 f32 time_difference) {
111 TouchType type = TouchType::Idle; 98 GestureType type = GestureType::Idle;
112 Attribute attributes{}; 99 GestureAttribute attributes{};
113 100
114 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 101 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
115 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
116 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
117 102
118 if (shared_memory.header.entry_count < 16) { 103 // Reset next state to default
119 shared_memory.header.entry_count++; 104 next_state.sampling_number = last_entry.sampling_number + 1;
120 } 105 next_state.delta = {};
121 106 next_state.vel_x = 0;
122 cur_entry.sampling_number = last_entry.sampling_number + 1; 107 next_state.vel_y = 0;
123 cur_entry.sampling_number2 = cur_entry.sampling_number; 108 next_state.direction = GestureDirection::None;
124 109 next_state.rotation_angle = 0;
125 // Reset values to default 110 next_state.scale = 0;
126 cur_entry.delta = {};
127 cur_entry.vel_x = 0;
128 cur_entry.vel_y = 0;
129 cur_entry.direction = Direction::None;
130 cur_entry.rotation_angle = 0;
131 cur_entry.scale = 0;
132 111
133 if (gesture.active_points > 0) { 112 if (gesture.active_points > 0) {
134 if (last_gesture.active_points == 0) { 113 if (last_gesture.active_points == 0) {
@@ -141,46 +120,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
141 } 120 }
142 121
143 // Apply attributes 122 // Apply attributes
144 cur_entry.detection_count = gesture.detection_count; 123 next_state.detection_count = gesture.detection_count;
145 cur_entry.type = type; 124 next_state.type = type;
146 cur_entry.attributes = attributes; 125 next_state.attributes = attributes;
147 cur_entry.pos = gesture.mid_point; 126 next_state.pos = gesture.mid_point;
148 cur_entry.point_count = static_cast<s32>(gesture.active_points); 127 next_state.point_count = static_cast<s32>(gesture.active_points);
149 cur_entry.points = gesture.points; 128 next_state.points = gesture.points;
150 last_gesture = gesture; 129 last_gesture = gesture;
151 130
152 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 131 gesture_lifo.WriteNextEntry(next_state);
132 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
153} 133}
154 134
155void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, 135void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
156 Attribute& attributes) { 136 GestureAttribute& attributes) {
157 const auto& last_entry = GetLastGestureEntry(); 137 const auto& last_entry = GetLastGestureEntry();
158 138
159 gesture.detection_count++; 139 gesture.detection_count++;
160 type = TouchType::Touch; 140 type = GestureType::Touch;
161 141
162 // New touch after cancel is not considered new 142 // New touch after cancel is not considered new
163 if (last_entry.type != TouchType::Cancel) { 143 if (last_entry.type != GestureType::Cancel) {
164 attributes.is_new_touch.Assign(1); 144 attributes.is_new_touch.Assign(1);
165 enable_press_and_tap = true; 145 enable_press_and_tap = true;
166 } 146 }
167} 147}
168 148
169void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, 149void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
170 f32 time_difference) { 150 f32 time_difference) {
171 const auto& last_entry = GetLastGestureEntry(); 151 const auto& last_entry = GetLastGestureEntry();
172 152
173 // Promote to pan type if touch moved 153 // Promote to pan type if touch moved
174 for (size_t id = 0; id < MAX_POINTS; id++) { 154 for (size_t id = 0; id < MAX_POINTS; id++) {
175 if (gesture.points[id] != last_gesture.points[id]) { 155 if (gesture.points[id] != last_gesture.points[id]) {
176 type = TouchType::Pan; 156 type = GestureType::Pan;
177 break; 157 break;
178 } 158 }
179 } 159 }
180 160
181 // Number of fingers changed cancel the last event and clear data 161 // Number of fingers changed cancel the last event and clear data
182 if (gesture.active_points != last_gesture.active_points) { 162 if (gesture.active_points != last_gesture.active_points) {
183 type = TouchType::Cancel; 163 type = GestureType::Cancel;
184 enable_press_and_tap = false; 164 enable_press_and_tap = false;
185 gesture.active_points = 0; 165 gesture.active_points = 0;
186 gesture.mid_point = {}; 166 gesture.mid_point = {};
@@ -189,41 +169,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
189 } 169 }
190 170
191 // Calculate extra parameters of panning 171 // Calculate extra parameters of panning
192 if (type == TouchType::Pan) { 172 if (type == GestureType::Pan) {
193 UpdatePanEvent(gesture, last_gesture, type, time_difference); 173 UpdatePanEvent(gesture, last_gesture, type, time_difference);
194 return; 174 return;
195 } 175 }
196 176
197 // Promote to press type 177 // Promote to press type
198 if (last_entry.type == TouchType::Touch) { 178 if (last_entry.type == GestureType::Touch) {
199 type = TouchType::Press; 179 type = GestureType::Press;
200 } 180 }
201} 181}
202 182
203void Controller_Gesture::EndGesture(GestureProperties& gesture, 183void Controller_Gesture::EndGesture(GestureProperties& gesture,
204 GestureProperties& last_gesture_props, TouchType& type, 184 GestureProperties& last_gesture_props, GestureType& type,
205 Attribute& attributes, f32 time_difference) { 185 GestureAttribute& attributes, f32 time_difference) {
206 const auto& last_entry = GetLastGestureEntry(); 186 const auto& last_entry = GetLastGestureEntry();
207 187
208 if (last_gesture_props.active_points != 0) { 188 if (last_gesture_props.active_points != 0) {
209 switch (last_entry.type) { 189 switch (last_entry.type) {
210 case TouchType::Touch: 190 case GestureType::Touch:
211 if (enable_press_and_tap) { 191 if (enable_press_and_tap) {
212 SetTapEvent(gesture, last_gesture_props, type, attributes); 192 SetTapEvent(gesture, last_gesture_props, type, attributes);
213 return; 193 return;
214 } 194 }
215 type = TouchType::Cancel; 195 type = GestureType::Cancel;
216 force_update = true; 196 force_update = true;
217 break; 197 break;
218 case TouchType::Press: 198 case GestureType::Press:
219 case TouchType::Tap: 199 case GestureType::Tap:
220 case TouchType::Swipe: 200 case GestureType::Swipe:
221 case TouchType::Pinch: 201 case GestureType::Pinch:
222 case TouchType::Rotate: 202 case GestureType::Rotate:
223 type = TouchType::Complete; 203 type = GestureType::Complete;
224 force_update = true; 204 force_update = true;
225 break; 205 break;
226 case TouchType::Pan: 206 case GestureType::Pan:
227 EndPanEvent(gesture, last_gesture_props, type, time_difference); 207 EndPanEvent(gesture, last_gesture_props, type, time_difference);
228 break; 208 break;
229 default: 209 default:
@@ -231,15 +211,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
231 } 211 }
232 return; 212 return;
233 } 213 }
234 if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) { 214 if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
235 gesture.detection_count++; 215 gesture.detection_count++;
236 } 216 }
237} 217}
238 218
239void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 219void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
240 GestureProperties& last_gesture_props, TouchType& type, 220 GestureProperties& last_gesture_props, GestureType& type,
241 Attribute& attributes) { 221 GestureAttribute& attributes) {
242 type = TouchType::Tap; 222 type = GestureType::Tap;
243 gesture = last_gesture_props; 223 gesture = last_gesture_props;
244 force_update = true; 224 force_update = true;
245 f32 tap_time_difference = 225 f32 tap_time_difference =
@@ -251,44 +231,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
251} 231}
252 232
253void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 233void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
254 GestureProperties& last_gesture_props, TouchType& type, 234 GestureProperties& last_gesture_props, GestureType& type,
255 f32 time_difference) { 235 f32 time_difference) {
256 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
257 const auto& last_entry = GetLastGestureEntry(); 236 const auto& last_entry = GetLastGestureEntry();
258 237
259 cur_entry.delta = gesture.mid_point - last_entry.pos; 238 next_state.delta = gesture.mid_point - last_entry.pos;
260 cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference; 239 next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
261 cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference; 240 next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
262 last_pan_time_difference = time_difference; 241 last_pan_time_difference = time_difference;
263 242
264 // Promote to pinch type 243 // Promote to pinch type
265 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > 244 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
266 pinch_threshold) { 245 pinch_threshold) {
267 type = TouchType::Pinch; 246 type = GestureType::Pinch;
268 cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance; 247 next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
269 } 248 }
270 249
271 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / 250 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
272 (1 + (gesture.angle * last_gesture_props.angle))); 251 (1 + (gesture.angle * last_gesture_props.angle)));
273 // Promote to rotate type 252 // Promote to rotate type
274 if (std::abs(angle_between_two_lines) > angle_threshold) { 253 if (std::abs(angle_between_two_lines) > angle_threshold) {
275 type = TouchType::Rotate; 254 type = GestureType::Rotate;
276 cur_entry.scale = 0; 255 next_state.scale = 0;
277 cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; 256 next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
278 } 257 }
279} 258}
280 259
281void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 260void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
282 GestureProperties& last_gesture_props, TouchType& type, 261 GestureProperties& last_gesture_props, GestureType& type,
283 f32 time_difference) { 262 f32 time_difference) {
284 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
285 const auto& last_entry = GetLastGestureEntry(); 263 const auto& last_entry = GetLastGestureEntry();
286 cur_entry.vel_x = 264 next_state.vel_x =
287 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 265 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
288 cur_entry.vel_y = 266 next_state.vel_y =
289 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); 267 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
290 const f32 curr_vel = 268 const f32 curr_vel =
291 std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); 269 std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
292 270
293 // Set swipe event with parameters 271 // Set swipe event with parameters
294 if (curr_vel > swipe_threshold) { 272 if (curr_vel > swipe_threshold) {
@@ -297,105 +275,50 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
297 } 275 }
298 276
299 // End panning without swipe 277 // End panning without swipe
300 type = TouchType::Complete; 278 type = GestureType::Complete;
301 cur_entry.vel_x = 0; 279 next_state.vel_x = 0;
302 cur_entry.vel_y = 0; 280 next_state.vel_y = 0;
303 force_update = true; 281 force_update = true;
304} 282}
305 283
306void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 284void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
307 GestureProperties& last_gesture_props, TouchType& type) { 285 GestureProperties& last_gesture_props, GestureType& type) {
308 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
309 const auto& last_entry = GetLastGestureEntry(); 286 const auto& last_entry = GetLastGestureEntry();
310 287
311 type = TouchType::Swipe; 288 type = GestureType::Swipe;
312 gesture = last_gesture_props; 289 gesture = last_gesture_props;
313 force_update = true; 290 force_update = true;
314 cur_entry.delta = last_entry.delta; 291 next_state.delta = last_entry.delta;
315 292
316 if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) { 293 if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
317 if (cur_entry.delta.x > 0) { 294 if (next_state.delta.x > 0) {
318 cur_entry.direction = Direction::Right; 295 next_state.direction = GestureDirection::Right;
319 return; 296 return;
320 } 297 }
321 cur_entry.direction = Direction::Left; 298 next_state.direction = GestureDirection::Left;
322 return; 299 return;
323 } 300 }
324 if (cur_entry.delta.y > 0) { 301 if (next_state.delta.y > 0) {
325 cur_entry.direction = Direction::Down; 302 next_state.direction = GestureDirection::Down;
326 return; 303 return;
327 } 304 }
328 cur_entry.direction = Direction::Up; 305 next_state.direction = GestureDirection::Up;
329}
330
331void Controller_Gesture::OnLoadInputDevices() {
332 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
333 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
334 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
335}
336
337std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
338 // Dont assign any touch input to a point if disabled
339 if (!Settings::values.touchscreen.enabled) {
340 return std::nullopt;
341 }
342 std::size_t first_free_id = 0;
343 while (first_free_id < MAX_POINTS) {
344 if (!fingers[first_free_id].pressed) {
345 return first_free_id;
346 } else {
347 first_free_id++;
348 }
349 }
350 return std::nullopt;
351}
352
353Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
354 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
355} 306}
356 307
357const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { 308const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
358 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; 309 return gesture_lifo.ReadCurrentEntry().state;
359}
360
361std::size_t Controller_Gesture::UpdateTouchInputEvent(
362 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
363 const auto& [x, y, pressed] = touch_input;
364 if (finger_id > MAX_POINTS) {
365 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
366 return MAX_POINTS;
367 }
368 if (pressed) {
369 if (finger_id == MAX_POINTS) {
370 const auto first_free_id = GetUnusedFingerID();
371 if (!first_free_id) {
372 // Invalid finger id do nothing
373 return MAX_POINTS;
374 }
375 finger_id = first_free_id.value();
376 fingers[finger_id].pressed = true;
377 }
378 fingers[finger_id].pos = {x, y};
379 return finger_id;
380 }
381
382 if (finger_id != MAX_POINTS) {
383 fingers[finger_id].pressed = false;
384 }
385
386 return MAX_POINTS;
387} 310}
388 311
389Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 312Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
390 GestureProperties gesture; 313 GestureProperties gesture;
391 std::array<Finger, MAX_POINTS> active_fingers; 314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
392 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
393 [](const auto& finger) { return finger.pressed; }); 316 [](const auto& finger) { return finger.pressed; });
394 gesture.active_points = 317 gesture.active_points =
395 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 318 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
396 319
397 for (size_t id = 0; id < gesture.active_points; ++id) { 320 for (size_t id = 0; id < gesture.active_points; ++id) {
398 const auto& [active_x, active_y] = active_fingers[id].pos; 321 const auto& [active_x, active_y] = active_fingers[id].position;
399 gesture.points[id] = { 322 gesture.points[id] = {
400 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), 323 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
401 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), 324 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 7e7ae6625..0936a3fa3 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -8,13 +8,14 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "core/frontend/input.h" 11#include "core/hid/emulated_console.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
13 14
14namespace Service::HID { 15namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 16class Controller_Gesture final : public ControllerBase {
16public: 17public:
17 explicit Controller_Gesture(Core::System& system_); 18 explicit Controller_Gesture(Core::HID::HIDCore& hid_core_);
18 ~Controller_Gesture() override; 19 ~Controller_Gesture() override;
19 20
20 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -26,14 +27,12 @@ public:
26 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 29
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32private: 30private:
33 static constexpr size_t MAX_FINGERS = 16; 31 static constexpr size_t MAX_FINGERS = 16;
34 static constexpr size_t MAX_POINTS = 4; 32 static constexpr size_t MAX_POINTS = 4;
35 33
36 enum class TouchType : u32 { 34 // This is nn::hid::GestureType
35 enum class GestureType : u32 {
37 Idle, // Nothing touching the screen 36 Idle, // Nothing touching the screen
38 Complete, // Set at the end of a touch event 37 Complete, // Set at the end of a touch event
39 Cancel, // Set when the number of fingers change 38 Cancel, // Set when the number of fingers change
@@ -46,7 +45,8 @@ private:
46 Rotate, // All points rotating from the midpoint 45 Rotate, // All points rotating from the midpoint
47 }; 46 };
48 47
49 enum class Direction : u32 { 48 // This is nn::hid::GestureDirection
49 enum class GestureDirection : u32 {
50 None, 50 None,
51 Left, 51 Left,
52 Up, 52 Up,
@@ -54,51 +54,41 @@ private:
54 Down, 54 Down,
55 }; 55 };
56 56
57 struct Attribute { 57 // This is nn::hid::GestureAttribute
58 struct GestureAttribute {
58 union { 59 union {
59 u32_le raw{}; 60 u32 raw{};
60 61
61 BitField<4, 1, u32> is_new_touch; 62 BitField<4, 1, u32> is_new_touch;
62 BitField<8, 1, u32> is_double_tap; 63 BitField<8, 1, u32> is_double_tap;
63 }; 64 };
64 }; 65 };
65 static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); 66 static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
66 67
68 // This is nn::hid::GestureState
67 struct GestureState { 69 struct GestureState {
68 s64_le sampling_number; 70 s64 sampling_number;
69 s64_le sampling_number2; 71 s64 detection_count;
70 s64_le detection_count; 72 GestureType type;
71 TouchType type; 73 GestureDirection direction;
72 Direction direction; 74 Common::Point<s32> pos;
73 Common::Point<s32_le> pos; 75 Common::Point<s32> delta;
74 Common::Point<s32_le> delta;
75 f32 vel_x; 76 f32 vel_x;
76 f32 vel_y; 77 f32 vel_y;
77 Attribute attributes; 78 GestureAttribute attributes;
78 f32 scale; 79 f32 scale;
79 f32 rotation_angle; 80 f32 rotation_angle;
80 s32_le point_count; 81 s32 point_count;
81 std::array<Common::Point<s32_le>, 4> points; 82 std::array<Common::Point<s32>, 4> points;
82 };
83 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
84
85 struct SharedMemory {
86 CommonHeader header;
87 std::array<GestureState, 17> gesture_states;
88 };
89 static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
90
91 struct Finger {
92 Common::Point<f32> pos{};
93 bool pressed{};
94 }; 83 };
84 static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
95 85
96 struct GestureProperties { 86 struct GestureProperties {
97 std::array<Common::Point<s32_le>, MAX_POINTS> points{}; 87 std::array<Common::Point<s32>, MAX_POINTS> points{};
98 std::size_t active_points{}; 88 std::size_t active_points{};
99 Common::Point<s32_le> mid_point{}; 89 Common::Point<s32> mid_point{};
100 s64_le detection_count{}; 90 s64 detection_count{};
101 u64_le delta_time{}; 91 u64 delta_time{};
102 f32 average_distance{}; 92 f32 average_distance{};
103 f32 angle{}; 93 f32 angle{};
104 }; 94 };
@@ -114,61 +104,48 @@ private:
114 f32 time_difference); 104 f32 time_difference);
115 105
116 // Initializes new gesture 106 // Initializes new gesture
117 void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes); 107 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
118 108
119 // Updates existing gesture state 109 // Updates existing gesture state
120 void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference); 110 void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
121 111
122 // Terminates exiting gesture 112 // Terminates exiting gesture
123 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, 113 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
124 TouchType& type, Attribute& attributes, f32 time_difference); 114 GestureType& type, GestureAttribute& attributes, f32 time_difference);
125 115
126 // Set current event to a tap event 116 // Set current event to a tap event
127 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 117 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
128 TouchType& type, Attribute& attributes); 118 GestureType& type, GestureAttribute& attributes);
129 119
130 // Calculates and set the extra parameters related to a pan event 120 // Calculates and set the extra parameters related to a pan event
131 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 121 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
132 TouchType& type, f32 time_difference); 122 GestureType& type, f32 time_difference);
133 123
134 // Terminates the pan event 124 // Terminates the pan event
135 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 125 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
136 TouchType& type, f32 time_difference); 126 GestureType& type, f32 time_difference);
137 127
138 // Set current event to a swipe event 128 // Set current event to a swipe event
139 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 129 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
140 TouchType& type); 130 GestureType& type);
141
142 // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
143 [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
144 131
145 // Retrieves the last gesture entry, as indicated by shared memory indices. 132 // Retrieves the last gesture entry, as indicated by shared memory indices.
146 [[nodiscard]] GestureState& GetLastGestureEntry();
147 [[nodiscard]] const GestureState& GetLastGestureEntry() const; 133 [[nodiscard]] const GestureState& GetLastGestureEntry() const;
148 134
149 /**
150 * If the touch is new it tries to assign a new finger id, if there is no fingers available no
151 * changes will be made. Updates the coordinates if the finger id it's already set. If the touch
152 * ends delays the output by one frame to set the end_touch flag before finally freeing the
153 * finger id
154 */
155 size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
156 size_t finger_id);
157
158 // Returns the average distance, angle and middle point of the active fingers 135 // Returns the average distance, angle and middle point of the active fingers
159 GestureProperties GetGestureProperties(); 136 GestureProperties GetGestureProperties();
160 137
161 SharedMemory shared_memory{}; 138 // This is nn::hid::detail::GestureLifo
162 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 139 Lifo<GestureState, hid_entry_count> gesture_lifo{};
163 std::unique_ptr<Input::TouchDevice> touch_udp_device; 140 static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
164 std::unique_ptr<Input::TouchDevice> touch_btn_device; 141 GestureState next_state{};
165 std::array<size_t, MAX_FINGERS> mouse_finger_id{}; 142
166 std::array<size_t, MAX_FINGERS> keyboard_finger_id{}; 143 Core::HID::EmulatedConsole* console;
167 std::array<size_t, MAX_FINGERS> udp_finger_id{}; 144
168 std::array<Finger, MAX_POINTS> fingers{}; 145 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
169 GestureProperties last_gesture{}; 146 GestureProperties last_gesture{};
170 s64_le last_update_timestamp{}; 147 s64 last_update_timestamp{};
171 s64_le last_tap_timestamp{}; 148 s64 last_tap_timestamp{};
172 f32 last_pan_time_difference{}; 149 f32 last_pan_time_difference{};
173 bool force_update{false}; 150 bool force_update{false};
174 bool enable_press_and_tap{false}; 151 bool enable_press_and_tap{false};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index c6c620008..9588a6910 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,13 +6,18 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_devices.h"
10#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/keyboard.h" 11#include "core/hle/service/hid/controllers/keyboard.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
14 15
15Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {} 16Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_)
17 : ControllerBase{hid_core_} {
18 emulated_devices = hid_core.GetEmulatedDevices();
19}
20
16Controller_Keyboard::~Controller_Keyboard() = default; 21Controller_Keyboard::~Controller_Keyboard() = default;
17 22
18void Controller_Keyboard::OnInit() {} 23void Controller_Keyboard::OnInit() {}
@@ -21,51 +26,27 @@ void Controller_Keyboard::OnRelease() {}
21 26
22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 27void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
23 std::size_t size) { 28 std::size_t size) {
24 shared_memory.header.timestamp = core_timing.GetCPUTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0; 30 keyboard_lifo.buffer_count = 0;
29 shared_memory.header.last_entry_index = 0; 31 keyboard_lifo.buffer_tail = 0;
32 std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
30 return; 33 return;
31 } 34 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
35 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
36 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
37 35
38 cur_entry.sampling_number = last_entry.sampling_number + 1; 36 const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
39 cur_entry.sampling_number2 = cur_entry.sampling_number; 37 next_state.sampling_number = last_entry.sampling_number + 1;
40 38
41 cur_entry.key.fill(0);
42 if (Settings::values.keyboard_enabled) { 39 if (Settings::values.keyboard_enabled) {
43 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { 40 const auto& keyboard_state = emulated_devices->GetKeyboard();
44 auto& entry = cur_entry.key[i / KEYS_PER_BYTE]; 41 const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
45 entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
46 }
47 42
48 using namespace Settings::NativeKeyboard; 43 next_state.key = keyboard_state;
49 44 next_state.modifier = keyboard_modifier_state;
50 // TODO: Assign the correct key to all modifiers 45 next_state.attribute.is_connected.Assign(1);
51 cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
52 cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
53 cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
54 cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
55 cur_entry.modifier.gui.Assign(0);
56 cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
57 cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
58 cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
59 cur_entry.modifier.katakana.Assign(0);
60 cur_entry.modifier.hiragana.Assign(0);
61 } 46 }
62 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
63}
64 47
65void Controller_Keyboard::OnLoadInputDevices() { 48 keyboard_lifo.WriteNextEntry(next_state);
66 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), 49 std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
67 keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
68 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
69 keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
70} 50}
51
71} // namespace Service::HID 52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 172a80e9c..cf62d3896 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,15 +8,20 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "common/swap.h" 11#include "common/swap.h"
13#include "core/frontend/input.h"
14#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedDevices;
17struct KeyboardModifier;
18struct KeyboardKey;
19} // namespace Core::HID
15 20
16namespace Service::HID { 21namespace Service::HID {
17class Controller_Keyboard final : public ControllerBase { 22class Controller_Keyboard final : public ControllerBase {
18public: 23public:
19 explicit Controller_Keyboard(Core::System& system_); 24 explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_);
20 ~Controller_Keyboard() override; 25 ~Controller_Keyboard() override;
21 26
22 // Called when the controller is initialized 27 // Called when the controller is initialized
@@ -28,47 +33,21 @@ public:
28 // When the controller is requesting an update for the shared memory 33 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 34 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 35
31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override;
33
34private: 36private:
35 struct Modifiers { 37 // This is nn::hid::detail::KeyboardState
36 union {
37 u32_le raw{};
38 BitField<0, 1, u32> control;
39 BitField<1, 1, u32> shift;
40 BitField<2, 1, u32> left_alt;
41 BitField<3, 1, u32> right_alt;
42 BitField<4, 1, u32> gui;
43 BitField<8, 1, u32> caps_lock;
44 BitField<9, 1, u32> scroll_lock;
45 BitField<10, 1, u32> num_lock;
46 BitField<11, 1, u32> katakana;
47 BitField<12, 1, u32> hiragana;
48 };
49 };
50 static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
51
52 struct KeyboardState { 38 struct KeyboardState {
53 s64_le sampling_number; 39 s64 sampling_number;
54 s64_le sampling_number2; 40 Core::HID::KeyboardModifier modifier;
55 41 Core::HID::KeyboardAttribute attribute;
56 Modifiers modifier; 42 Core::HID::KeyboardKey key;
57 std::array<u8, 32> key;
58 }; 43 };
59 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size"); 44 static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
60 45
61 struct SharedMemory { 46 // This is nn::hid::detail::KeyboardLifo
62 CommonHeader header; 47 Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
63 std::array<KeyboardState, 17> pad_states; 48 static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
64 INSERT_PADDING_BYTES(0x28); 49 KeyboardState next_state{};
65 };
66 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
67 SharedMemory shared_memory{};
68 50
69 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys> 51 Core::HID::EmulatedDevices* emulated_devices;
70 keyboard_keys;
71 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
72 keyboard_mods;
73}; 52};
74} // namespace Service::HID 53} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 544a71948..ba79888ae 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -6,12 +6,17 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "core/hid/emulated_devices.h"
10#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/mouse.h" 11#include "core/hle/service/hid/controllers/mouse.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 15
14Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {} 16Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
17 emulated_devices = hid_core.GetEmulatedDevices();
18}
19
15Controller_Mouse::~Controller_Mouse() = default; 20Controller_Mouse::~Controller_Mouse() = default;
16 21
17void Controller_Mouse::OnInit() {} 22void Controller_Mouse::OnInit() {}
@@ -19,50 +24,35 @@ void Controller_Mouse::OnRelease() {}
19 24
20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 25void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 26 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
26 shared_memory.header.entry_count = 0; 28 mouse_lifo.buffer_count = 0;
27 shared_memory.header.last_entry_index = 0; 29 mouse_lifo.buffer_tail = 0;
30 std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
28 return; 31 return;
29 } 32 }
30 shared_memory.header.entry_count = 16;
31 33
32 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; 34 const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
33 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 35 next_state.sampling_number = last_entry.sampling_number + 1;
34 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
35 36
36 cur_entry.sampling_number = last_entry.sampling_number + 1; 37 next_state.attribute.raw = 0;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38
39 cur_entry.attribute.raw = 0;
40 if (Settings::values.mouse_enabled) { 38 if (Settings::values.mouse_enabled) {
41 const auto [px, py, sx, sy] = mouse_device->GetStatus(); 39 const auto& mouse_button_state = emulated_devices->GetMouseButtons();
42 const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width); 40 const auto& mouse_position_state = emulated_devices->GetMousePosition();
43 const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height); 41 const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
44 cur_entry.x = x; 42 next_state.attribute.is_connected.Assign(1);
45 cur_entry.y = y; 43 next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
46 cur_entry.delta_x = x - last_entry.x; 44 next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
47 cur_entry.delta_y = y - last_entry.y; 45 next_state.delta_x = next_state.x - last_entry.x;
48 cur_entry.mouse_wheel_x = sx; 46 next_state.delta_y = next_state.y - last_entry.y;
49 cur_entry.mouse_wheel_y = sy; 47 next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
50 cur_entry.attribute.is_connected.Assign(1); 48 next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
51 49
52 using namespace Settings::NativeMouseButton; 50 last_mouse_wheel_state = mouse_wheel_state;
53 cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus()); 51 next_state.button = mouse_button_state;
54 cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
55 cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
56 cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
57 cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
58 } 52 }
59 53
60 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 54 mouse_lifo.WriteNextEntry(next_state);
55 std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
61} 56}
62 57
63void Controller_Mouse::OnLoadInputDevices() {
64 mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
65 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
66 mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
67}
68} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 3d391a798..7559fc78d 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,15 +7,20 @@
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/settings.h"
11#include "common/swap.h" 10#include "common/swap.h"
12#include "core/frontend/input.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h"
13
14namespace Core::HID {
15class EmulatedDevices;
16struct MouseState;
17struct AnalogStickState;
18} // namespace Core::HID
14 19
15namespace Service::HID { 20namespace Service::HID {
16class Controller_Mouse final : public ControllerBase { 21class Controller_Mouse final : public ControllerBase {
17public: 22public:
18 explicit Controller_Mouse(Core::System& system_); 23 explicit Controller_Mouse(Core::HID::HIDCore& hid_core_);
19 ~Controller_Mouse() override; 24 ~Controller_Mouse() override;
20 25
21 // Called when the controller is initialized 26 // Called when the controller is initialized
@@ -27,53 +32,13 @@ public:
27 // When the controller is requesting an update for the shared memory 32 // When the controller is requesting an update for the shared memory
28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 33 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
29 34
30 // Called when input devices should be loaded
31 void OnLoadInputDevices() override;
32
33private: 35private:
34 struct Buttons { 36 // This is nn::hid::detail::MouseLifo
35 union { 37 Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
36 u32_le raw{}; 38 static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
37 BitField<0, 1, u32> left; 39 Core::HID::MouseState next_state{};
38 BitField<1, 1, u32> right;
39 BitField<2, 1, u32> middle;
40 BitField<3, 1, u32> forward;
41 BitField<4, 1, u32> back;
42 };
43 };
44 static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
45
46 struct Attributes {
47 union {
48 u32_le raw{};
49 BitField<0, 1, u32> transferable;
50 BitField<1, 1, u32> is_connected;
51 };
52 };
53 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
54
55 struct MouseState {
56 s64_le sampling_number;
57 s64_le sampling_number2;
58 s32_le x;
59 s32_le y;
60 s32_le delta_x;
61 s32_le delta_y;
62 s32_le mouse_wheel_x;
63 s32_le mouse_wheel_y;
64 Buttons button;
65 Attributes attribute;
66 };
67 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
68
69 struct SharedMemory {
70 CommonHeader header;
71 std::array<MouseState, 17> mouse_states;
72 };
73 SharedMemory shared_memory{};
74 40
75 std::unique_ptr<Input::MouseDevice> mouse_device; 41 Core::HID::AnalogStickState last_mouse_wheel_state;
76 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> 42 Core::HID::EmulatedDevices* emulated_devices;
77 mouse_button_devices;
78}; 43};
79} // namespace Service::HID 44} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 196876810..04b3a68c3 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -10,9 +10,9 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/core.h"
14#include "core/core_timing.h" 13#include "core/core_timing.h"
15#include "core/frontend/input.h" 14#include "core/hid/emulated_controller.h"
15#include "core/hid/hid_core.h"
16#include "core/hle/kernel/k_event.h" 16#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_readable_event.h" 17#include "core/hle/kernel/k_readable_event.h"
18#include "core/hle/kernel/k_writable_event.h" 18#include "core/hle/kernel/k_writable_event.h"
@@ -20,120 +20,26 @@
20#include "core/hle/service/kernel_helpers.h" 20#include "core/hle/service/kernel_helpers.h"
21 21
22namespace Service::HID { 22namespace Service::HID {
23constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr s32 HID_TRIGGER_MAX = 0x7fff;
25[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
26constexpr std::size_t NPAD_OFFSET = 0x9A00; 23constexpr std::size_t NPAD_OFFSET = 0x9A00;
27constexpr u32 BATTERY_FULL = 2; 24constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
28constexpr u32 MAX_NPAD_ID = 7; 25 Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
29constexpr std::size_t HANDHELD_INDEX = 8; 26 Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
30constexpr std::array<u32, 10> npad_id_list{ 27 Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other,
31 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 28 Core::HID::NpadIdType::Handheld,
32}; 29};
33 30
34enum class JoystickId : std::size_t { 31bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
35 Joystick_Left,
36 Joystick_Right,
37};
38
39Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
40 Settings::ControllerType type) {
41 switch (type) {
42 case Settings::ControllerType::ProController:
43 return NPadControllerType::ProController;
44 case Settings::ControllerType::DualJoyconDetached:
45 return NPadControllerType::JoyDual;
46 case Settings::ControllerType::LeftJoycon:
47 return NPadControllerType::JoyLeft;
48 case Settings::ControllerType::RightJoycon:
49 return NPadControllerType::JoyRight;
50 case Settings::ControllerType::Handheld:
51 return NPadControllerType::Handheld;
52 case Settings::ControllerType::GameCube:
53 return NPadControllerType::GameCube;
54 default:
55 UNREACHABLE();
56 return NPadControllerType::ProController;
57 }
58}
59
60Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
61 Controller_NPad::NPadControllerType type) {
62 switch (type) {
63 case NPadControllerType::ProController:
64 return Settings::ControllerType::ProController;
65 case NPadControllerType::JoyDual:
66 return Settings::ControllerType::DualJoyconDetached;
67 case NPadControllerType::JoyLeft:
68 return Settings::ControllerType::LeftJoycon;
69 case NPadControllerType::JoyRight:
70 return Settings::ControllerType::RightJoycon;
71 case NPadControllerType::Handheld:
72 return Settings::ControllerType::Handheld;
73 case NPadControllerType::GameCube:
74 return Settings::ControllerType::GameCube;
75 default:
76 UNREACHABLE();
77 return Settings::ControllerType::ProController;
78 }
79}
80
81std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
82 switch (npad_id) { 32 switch (npad_id) {
83 case 0: 33 case Core::HID::NpadIdType::Player1:
84 case 1: 34 case Core::HID::NpadIdType::Player2:
85 case 2: 35 case Core::HID::NpadIdType::Player3:
86 case 3: 36 case Core::HID::NpadIdType::Player4:
87 case 4: 37 case Core::HID::NpadIdType::Player5:
88 case 5: 38 case Core::HID::NpadIdType::Player6:
89 case 6: 39 case Core::HID::NpadIdType::Player7:
90 case 7: 40 case Core::HID::NpadIdType::Player8:
91 return npad_id; 41 case Core::HID::NpadIdType::Other:
92 case HANDHELD_INDEX: 42 case Core::HID::NpadIdType::Handheld:
93 case NPAD_HANDHELD:
94 return HANDHELD_INDEX;
95 case 9:
96 case NPAD_UNKNOWN:
97 return 9;
98 default:
99 UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
100 return 0;
101 }
102}
103
104u32 Controller_NPad::IndexToNPad(std::size_t index) {
105 switch (index) {
106 case 0:
107 case 1:
108 case 2:
109 case 3:
110 case 4:
111 case 5:
112 case 6:
113 case 7:
114 return static_cast<u32>(index);
115 case HANDHELD_INDEX:
116 return NPAD_HANDHELD;
117 case 9:
118 return NPAD_UNKNOWN;
119 default:
120 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
121 return 0;
122 }
123}
124
125bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
126 switch (npad_id) {
127 case 0:
128 case 1:
129 case 2:
130 case 3:
131 case 4:
132 case 5:
133 case 6:
134 case 7:
135 case NPAD_UNKNOWN:
136 case NPAD_HANDHELD:
137 return true; 43 return true;
138 default: 44 default:
139 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); 45 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
@@ -141,131 +47,215 @@ bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
141 } 47 }
142} 48}
143 49
144bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { 50bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
145 return IsNpadIdValid(device_handle.npad_id) && 51 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
146 device_handle.npad_type < NpadType::MaxNpadType && 52 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
147 device_handle.device_index < DeviceIndex::MaxDeviceIndex; 53 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
54}
55
56bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
57 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
58 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
59 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
148} 60}
149 61
150Controller_NPad::Controller_NPad(Core::System& system_, 62Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
151 KernelHelpers::ServiceContext& service_context_) 63 KernelHelpers::ServiceContext& service_context_)
152 : ControllerBase{system_}, service_context{service_context_} { 64 : ControllerBase{hid_core_}, service_context{service_context_} {
153 latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); 65 for (std::size_t i = 0; i < controller_data.size(); ++i) {
66 auto& controller = controller_data[i];
67 controller.device = hid_core.GetEmulatedControllerByIndex(i);
68 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
69 DEFAULT_VIBRATION_VALUE;
70 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
71 DEFAULT_VIBRATION_VALUE;
72 Core::HID::ControllerUpdateCallback engine_callback{
73 .on_change = [this,
74 i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
75 .is_npad_service = true,
76 };
77 controller.callback_key = controller.device->SetCallback(engine_callback);
78 }
154} 79}
155 80
156Controller_NPad::~Controller_NPad() { 81Controller_NPad::~Controller_NPad() {
82 for (std::size_t i = 0; i < controller_data.size(); ++i) {
83 auto& controller = controller_data[i];
84 controller.device->DeleteCallback(controller.callback_key);
85 }
157 OnRelease(); 86 OnRelease();
158} 87}
159 88
160void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { 89void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
161 const auto controller_type = connected_controllers[controller_idx].type; 90 std::size_t controller_idx) {
162 auto& controller = shared_memory_entries[controller_idx]; 91 if (type == Core::HID::ControllerTriggerType::All) {
163 if (controller_type == NPadControllerType::None) { 92 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
164 styleset_changed_events[controller_idx]->GetWritableEvent().Signal(); 93 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
165 return; 94 return;
166 } 95 }
167 controller.style_set.raw = 0; // Zero out 96 if (controller_idx >= controller_data.size()) {
168 controller.device_type.raw = 0; 97 return;
169 controller.system_properties.raw = 0; 98 }
99
100 auto& controller = controller_data[controller_idx];
101 const auto is_connected = controller.device->IsConnected();
102 const auto npad_type = controller.device->GetNpadStyleIndex();
103 const auto npad_id = controller.device->GetNpadIdType();
104 switch (type) {
105 case Core::HID::ControllerTriggerType::Connected:
106 case Core::HID::ControllerTriggerType::Disconnected:
107 if (is_connected == controller.is_connected) {
108 return;
109 }
110 UpdateControllerAt(npad_type, npad_id, is_connected);
111 break;
112 case Core::HID::ControllerTriggerType::Battery: {
113 if (!controller.is_connected) {
114 return;
115 }
116 auto& shared_memory = controller.shared_memory_entry;
117 const auto& battery_level = controller.device->GetBattery();
118 shared_memory.battery_level_dual = battery_level.dual.battery_level;
119 shared_memory.battery_level_left = battery_level.left.battery_level;
120 shared_memory.battery_level_right = battery_level.right.battery_level;
121 break;
122 }
123 default:
124 break;
125 }
126}
127
128void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
129 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
130 auto& controller = GetControllerFromNpadIdType(npad_id);
131 const auto controller_type = controller.device->GetNpadStyleIndex();
132 auto& shared_memory = controller.shared_memory_entry;
133 if (controller_type == Core::HID::NpadStyleIndex::None) {
134 controller.styleset_changed_event->GetWritableEvent().Signal();
135 return;
136 }
137 shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None;
138 shared_memory.device_type.raw = 0;
139 shared_memory.system_properties.raw = 0;
170 switch (controller_type) { 140 switch (controller_type) {
171 case NPadControllerType::None: 141 case Core::HID::NpadStyleIndex::None:
172 UNREACHABLE(); 142 UNREACHABLE();
173 break; 143 break;
174 case NPadControllerType::ProController: 144 case Core::HID::NpadStyleIndex::ProController:
175 controller.style_set.fullkey.Assign(1); 145 shared_memory.style_tag.fullkey.Assign(1);
176 controller.device_type.fullkey.Assign(1); 146 shared_memory.device_type.fullkey.Assign(1);
177 controller.system_properties.is_vertical.Assign(1); 147 shared_memory.system_properties.is_vertical.Assign(1);
178 controller.system_properties.use_plus.Assign(1); 148 shared_memory.system_properties.use_plus.Assign(1);
179 controller.system_properties.use_minus.Assign(1); 149 shared_memory.system_properties.use_minus.Assign(1);
180 controller.assignment_mode = NpadAssignments::Single; 150 shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
181 controller.footer_type = AppletFooterUiType::SwitchProController; 151 shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
152 break;
153 case Core::HID::NpadStyleIndex::Handheld:
154 shared_memory.style_tag.handheld.Assign(1);
155 shared_memory.device_type.handheld_left.Assign(1);
156 shared_memory.device_type.handheld_right.Assign(1);
157 shared_memory.system_properties.is_vertical.Assign(1);
158 shared_memory.system_properties.use_plus.Assign(1);
159 shared_memory.system_properties.use_minus.Assign(1);
160 shared_memory.system_properties.use_directional_buttons.Assign(1);
161 shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
162 shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
163 break;
164 case Core::HID::NpadStyleIndex::JoyconDual:
165 shared_memory.style_tag.joycon_dual.Assign(1);
166 shared_memory.device_type.joycon_left.Assign(1);
167 shared_memory.device_type.joycon_right.Assign(1);
168 shared_memory.system_properties.is_vertical.Assign(1);
169 shared_memory.system_properties.use_plus.Assign(1);
170 shared_memory.system_properties.use_minus.Assign(1);
171 shared_memory.system_properties.use_directional_buttons.Assign(1);
172 shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
173 shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
182 break; 174 break;
183 case NPadControllerType::Handheld: 175 case Core::HID::NpadStyleIndex::JoyconLeft:
184 controller.style_set.handheld.Assign(1); 176 shared_memory.style_tag.joycon_left.Assign(1);
185 controller.device_type.handheld_left.Assign(1); 177 shared_memory.device_type.joycon_left.Assign(1);
186 controller.device_type.handheld_right.Assign(1); 178 shared_memory.system_properties.is_horizontal.Assign(1);
187 controller.system_properties.is_vertical.Assign(1); 179 shared_memory.system_properties.use_minus.Assign(1);
188 controller.system_properties.use_plus.Assign(1); 180 shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
189 controller.system_properties.use_minus.Assign(1); 181 shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
190 controller.assignment_mode = NpadAssignments::Dual;
191 controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
192 break; 182 break;
193 case NPadControllerType::JoyDual: 183 case Core::HID::NpadStyleIndex::JoyconRight:
194 controller.style_set.joycon_dual.Assign(1); 184 shared_memory.style_tag.joycon_right.Assign(1);
195 controller.device_type.joycon_left.Assign(1); 185 shared_memory.device_type.joycon_right.Assign(1);
196 controller.device_type.joycon_right.Assign(1); 186 shared_memory.system_properties.is_horizontal.Assign(1);
197 controller.system_properties.is_vertical.Assign(1); 187 shared_memory.system_properties.use_plus.Assign(1);
198 controller.system_properties.use_plus.Assign(1); 188 shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
199 controller.system_properties.use_minus.Assign(1); 189 shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
200 controller.assignment_mode = NpadAssignments::Dual;
201 controller.footer_type = AppletFooterUiType::JoyDual;
202 break; 190 break;
203 case NPadControllerType::JoyLeft: 191 case Core::HID::NpadStyleIndex::GameCube:
204 controller.style_set.joycon_left.Assign(1); 192 shared_memory.style_tag.gamecube.Assign(1);
205 controller.device_type.joycon_left.Assign(1); 193 shared_memory.device_type.fullkey.Assign(1);
206 controller.system_properties.is_horizontal.Assign(1); 194 shared_memory.system_properties.is_vertical.Assign(1);
207 controller.system_properties.use_minus.Assign(1); 195 shared_memory.system_properties.use_plus.Assign(1);
208 controller.assignment_mode = NpadAssignments::Single;
209 controller.footer_type = AppletFooterUiType::JoyLeftHorizontal;
210 break; 196 break;
211 case NPadControllerType::JoyRight: 197 case Core::HID::NpadStyleIndex::Pokeball:
212 controller.style_set.joycon_right.Assign(1); 198 shared_memory.style_tag.palma.Assign(1);
213 controller.device_type.joycon_right.Assign(1); 199 shared_memory.device_type.palma.Assign(1);
214 controller.system_properties.is_horizontal.Assign(1); 200 shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
215 controller.system_properties.use_plus.Assign(1);
216 controller.assignment_mode = NpadAssignments::Single;
217 controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
218 break; 201 break;
219 case NPadControllerType::GameCube: 202 case Core::HID::NpadStyleIndex::NES:
220 controller.style_set.gamecube.Assign(1); 203 shared_memory.style_tag.lark.Assign(1);
221 // The GC Controller behaves like a wired Pro Controller 204 shared_memory.device_type.fullkey.Assign(1);
222 controller.device_type.fullkey.Assign(1);
223 controller.system_properties.is_vertical.Assign(1);
224 controller.system_properties.use_plus.Assign(1);
225 break; 205 break;
226 case NPadControllerType::Pokeball: 206 case Core::HID::NpadStyleIndex::SNES:
227 controller.style_set.palma.Assign(1); 207 shared_memory.style_tag.lucia.Assign(1);
228 controller.device_type.palma.Assign(1); 208 shared_memory.device_type.fullkey.Assign(1);
229 controller.assignment_mode = NpadAssignments::Single; 209 shared_memory.applet_footer.type = AppletFooterUiType::Lucia;
210 break;
211 case Core::HID::NpadStyleIndex::N64:
212 shared_memory.style_tag.lagoon.Assign(1);
213 shared_memory.device_type.fullkey.Assign(1);
214 shared_memory.applet_footer.type = AppletFooterUiType::Lagon;
215 break;
216 case Core::HID::NpadStyleIndex::SegaGenesis:
217 shared_memory.style_tag.lager.Assign(1);
218 shared_memory.device_type.fullkey.Assign(1);
219 break;
220 default:
230 break; 221 break;
231 } 222 }
232 223
233 controller.fullkey_color.attribute = ColorAttributes::Ok; 224 const auto& body_colors = controller.device->GetColors();
234 controller.fullkey_color.fullkey.body = 0;
235 controller.fullkey_color.fullkey.button = 0;
236 225
237 controller.joycon_color.attribute = ColorAttributes::Ok; 226 shared_memory.fullkey_color.attribute = ColorAttribute::Ok;
238 controller.joycon_color.left.body = 227 shared_memory.fullkey_color.fullkey = body_colors.fullkey;
239 Settings::values.players.GetValue()[controller_idx].body_color_left;
240 controller.joycon_color.left.button =
241 Settings::values.players.GetValue()[controller_idx].button_color_left;
242 controller.joycon_color.right.body =
243 Settings::values.players.GetValue()[controller_idx].body_color_right;
244 controller.joycon_color.right.button =
245 Settings::values.players.GetValue()[controller_idx].button_color_right;
246 228
247 // TODO: Investigate when we should report all batery types 229 shared_memory.joycon_color.attribute = ColorAttribute::Ok;
248 controller.battery_level_dual = BATTERY_FULL; 230 shared_memory.joycon_color.left = body_colors.left;
249 controller.battery_level_left = BATTERY_FULL; 231 shared_memory.joycon_color.right = body_colors.right;
250 controller.battery_level_right = BATTERY_FULL;
251 232
252 SignalStyleSetChangedEvent(IndexToNPad(controller_idx)); 233 // TODO: Investigate when we should report all batery types
234 const auto& battery_level = controller.device->GetBattery();
235 shared_memory.battery_level_dual = battery_level.dual.battery_level;
236 shared_memory.battery_level_left = battery_level.left.battery_level;
237 shared_memory.battery_level_right = battery_level.right.battery_level;
238
239 controller.is_connected = true;
240 controller.device->Connect();
241 SignalStyleSetChangedEvent(npad_id);
242 WriteEmptyEntry(controller.shared_memory_entry);
253} 243}
254 244
255void Controller_NPad::OnInit() { 245void Controller_NPad::OnInit() {
256 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
257 styleset_changed_events[i] =
258 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
259 }
260
261 if (!IsControllerActivated()) { 246 if (!IsControllerActivated()) {
262 return; 247 return;
263 } 248 }
264 249
265 OnLoadInputDevices(); 250 for (std::size_t i = 0; i < controller_data.size(); ++i) {
251 auto& controller = controller_data[i];
252 controller.styleset_changed_event =
253 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
254 }
266 255
267 if (style.raw == 0) { 256 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
268 // We want to support all controllers 257 // We want to support all controllers
258 Core::HID::NpadStyleTag style{};
269 style.handheld.Assign(1); 259 style.handheld.Assign(1);
270 style.joycon_left.Assign(1); 260 style.joycon_left.Assign(1);
271 style.joycon_right.Assign(1); 261 style.joycon_right.Assign(1);
@@ -273,173 +263,124 @@ void Controller_NPad::OnInit() {
273 style.fullkey.Assign(1); 263 style.fullkey.Assign(1);
274 style.gamecube.Assign(1); 264 style.gamecube.Assign(1);
275 style.palma.Assign(1); 265 style.palma.Assign(1);
266 style.lark.Assign(1);
267 style.lucia.Assign(1);
268 style.lagoon.Assign(1);
269 style.lager.Assign(1);
270 hid_core.SetSupportedStyleTag(style);
276 } 271 }
277 272
278 std::transform(Settings::values.players.GetValue().begin(), 273 supported_npad_id_types.resize(npad_id_list.size());
279 Settings::values.players.GetValue().end(), connected_controllers.begin(), 274 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
280 [](const Settings::PlayerInput& player) { 275 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
281 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), 276
282 player.connected}; 277 // Prefill controller buffers
283 }); 278 for (auto& controller : controller_data) {
284 279 auto& npad = controller.shared_memory_entry;
285 // Connect the Player 1 or Handheld controller if none are connected. 280 npad.fullkey_color = {
286 if (std::none_of(connected_controllers.begin(), connected_controllers.end(), 281 .attribute = ColorAttribute::NoController,
287 [](const ControllerHolder& controller) { return controller.is_connected; })) { 282 .fullkey = {},
288 const auto controller = 283 };
289 MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type); 284 npad.joycon_color = {
290 if (controller == NPadControllerType::Handheld) { 285 .attribute = ColorAttribute::NoController,
291 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; 286 .left = {},
292 connected_controllers[HANDHELD_INDEX] = {controller, true}; 287 .right = {},
293 } else { 288 };
294 Settings::values.players.GetValue()[0].connected = true; 289 // HW seems to initialize the first 19 entries
295 connected_controllers[0] = {controller, true}; 290 for (std::size_t i = 0; i < 19; ++i) {
291 WriteEmptyEntry(npad);
296 } 292 }
297 } 293 }
298 294
299 // Account for handheld 295 // Connect controllers
300 if (connected_controllers[HANDHELD_INDEX].is_connected) { 296 for (auto& controller : controller_data) {
301 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; 297 const auto& device = controller.device;
298 if (device->IsConnected()) {
299 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
300 }
302 } 301 }
302}
303 303
304 supported_npad_id_types.resize(npad_id_list.size()); 304void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
305 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 305 NPadGenericState dummy_pad_state{};
306 npad_id_list.size() * sizeof(u32)); 306 NpadGcTriggerState dummy_gc_state{};
307 dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
308 npad.fullkey_lifo.WriteNextEntry(dummy_pad_state);
309 dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1;
310 npad.handheld_lifo.WriteNextEntry(dummy_pad_state);
311 dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
312 npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state);
313 dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
314 npad.joy_left_lifo.WriteNextEntry(dummy_pad_state);
315 dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
316 npad.joy_right_lifo.WriteNextEntry(dummy_pad_state);
317 dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1;
318 npad.palma_lifo.WriteNextEntry(dummy_pad_state);
319 dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
320 npad.system_ext_lifo.WriteNextEntry(dummy_pad_state);
321 dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
322 npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
323}
307 324
308 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 325void Controller_NPad::OnRelease() {
309 const auto& controller = connected_controllers[i]; 326 for (std::size_t i = 0; i < controller_data.size(); ++i) {
310 if (controller.is_connected) { 327 auto& controller = controller_data[i];
311 AddNewControllerAt(controller.type, i); 328 service_context.CloseEvent(controller.styleset_changed_event);
329 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
330 VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
312 } 331 }
313 } 332 }
314} 333}
315 334
316void Controller_NPad::OnLoadInputDevices() { 335void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
317 const auto& players = Settings::values.players.GetValue();
318
319 std::lock_guard lock{mutex}; 336 std::lock_guard lock{mutex};
320 for (std::size_t i = 0; i < players.size(); ++i) { 337 auto& controller = GetControllerFromNpadIdType(npad_id);
321 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 338 const auto controller_type = controller.device->GetNpadStyleIndex();
322 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 339 if (!controller.device->IsConnected()) {
323 buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>); 340 return;
324 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
325 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
326 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
327 std::transform(players[i].vibrations.begin() +
328 Settings::NativeVibration::VIBRATION_HID_BEGIN,
329 players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
330 vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
331 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
332 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
333 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
334 for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
335 InitializeVibrationDeviceAtIndex(i, device_idx);
336 }
337 } 341 }
338}
339 342
340void Controller_NPad::OnRelease() { 343 auto& pad_entry = controller.npad_pad_state;
341 for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { 344 auto& trigger_entry = controller.npad_trigger_state;
342 for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { 345 const auto button_state = controller.device->GetNpadButtons();
343 VibrateControllerAtIndex(npad_idx, device_idx, {}); 346 const auto stick_state = controller.device->GetSticks();
344 } 347
348 using btn = Core::HID::NpadButton;
349 pad_entry.npad_buttons.raw = btn::None;
350 if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) {
351 constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R |
352 btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp |
353 btn::StickRRight | btn::StickRDown;
354 pad_entry.npad_buttons.raw = button_state.raw & right_button_mask;
355 pad_entry.r_stick = stick_state.right;
345 } 356 }
346 357
347 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { 358 if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) {
348 service_context.CloseEvent(styleset_changed_events[i]); 359 constexpr btn left_button_mask =
360 btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL |
361 btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown;
362 pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask;
363 pad_entry.l_stick = stick_state.left;
349 } 364 }
350}
351 365
352void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 366 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
353 std::lock_guard lock{mutex}; 367 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
368 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
369 }
354 370
355 const auto controller_idx = NPadIdToIndex(npad_id); 371 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
356 const auto controller_type = connected_controllers[controller_idx].type; 372 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
357 if (!connected_controllers[controller_idx].is_connected) { 373 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
358 return;
359 } 374 }
360 auto& pad_state = npad_pad_states[controller_idx].pad_states; 375
361 auto& lstick_entry = npad_pad_states[controller_idx].l_stick; 376 if (controller_type == Core::HID::NpadStyleIndex::GameCube) {
362 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 377 const auto& trigger_state = controller.device->GetTriggers();
363 auto& trigger_entry = npad_trigger_states[controller_idx]; 378 trigger_entry.l_analog = trigger_state.left;
364 const auto& button_state = buttons[controller_idx]; 379 trigger_entry.r_analog = trigger_state.right;
365 const auto& analog_state = sticks[controller_idx]; 380 pad_entry.npad_buttons.zl.Assign(false);
366 const auto [stick_l_x_f, stick_l_y_f] = 381 pad_entry.npad_buttons.zr.Assign(button_state.r);
367 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 382 pad_entry.npad_buttons.l.Assign(button_state.zl);
368 const auto [stick_r_x_f, stick_r_y_f] = 383 pad_entry.npad_buttons.r.Assign(button_state.zr);
369 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
370
371 using namespace Settings::NativeButton;
372 if (controller_type != NPadControllerType::JoyLeft) {
373 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
374 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
375 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
376 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
377 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
378 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
379 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
380 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
381
382 pad_state.r_stick_right.Assign(
383 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
384 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
385 pad_state.r_stick_left.Assign(
386 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
387 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
388 pad_state.r_stick_up.Assign(
389 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
390 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
391 pad_state.r_stick_down.Assign(
392 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
393 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
394 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
395 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
396 }
397
398 if (controller_type != NPadControllerType::JoyRight) {
399 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
400 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
401 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
402 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
403 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
404 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
405 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
406 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
407
408 pad_state.l_stick_right.Assign(
409 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
410 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
411 pad_state.l_stick_left.Assign(
412 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
413 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
414 pad_state.l_stick_up.Assign(
415 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
416 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
417 pad_state.l_stick_down.Assign(
418 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
419 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
420 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
421 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
422 }
423
424 if (controller_type == NPadControllerType::JoyLeft) {
425 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
426 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
427 }
428
429 if (controller_type == NPadControllerType::JoyRight) {
430 pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
431 pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
432 }
433
434 if (controller_type == NPadControllerType::GameCube) {
435 trigger_entry.l_analog = static_cast<s32>(
436 button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
437 trigger_entry.r_analog = static_cast<s32>(
438 button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
439 pad_state.zl.Assign(false);
440 pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
441 pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
442 pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
443 } 384 }
444} 385}
445 386
@@ -448,173 +389,132 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
448 if (!IsControllerActivated()) { 389 if (!IsControllerActivated()) {
449 return; 390 return;
450 } 391 }
451 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
452 auto& npad = shared_memory_entries[i];
453 const std::array<NPadGeneric*, 7> controller_npads{
454 &npad.fullkey_states, &npad.handheld_states, &npad.joy_dual_states,
455 &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
456 &npad.system_ext_states};
457
458 // There is the posibility to have more controllers with analog triggers
459 const std::array<TriggerGeneric*, 1> controller_triggers{
460 &npad.gc_trigger_states,
461 };
462
463 for (auto* main_controller : controller_npads) {
464 main_controller->common.entry_count = 16;
465 main_controller->common.total_entry_count = 17;
466
467 const auto& last_entry =
468 main_controller->npad[main_controller->common.last_entry_index];
469
470 main_controller->common.timestamp = core_timing.GetCPUTicks();
471 main_controller->common.last_entry_index =
472 (main_controller->common.last_entry_index + 1) % 17;
473
474 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
475
476 cur_entry.timestamp = last_entry.timestamp + 1;
477 cur_entry.timestamp2 = cur_entry.timestamp;
478 }
479
480 for (auto* analog_trigger : controller_triggers) {
481 analog_trigger->entry_count = 16;
482 analog_trigger->total_entry_count = 17;
483 392
484 const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; 393 for (std::size_t i = 0; i < controller_data.size(); ++i) {
394 auto& controller = controller_data[i];
395 auto& npad = controller.shared_memory_entry;
485 396
486 analog_trigger->timestamp = core_timing.GetCPUTicks(); 397 const auto& controller_type = controller.device->GetNpadStyleIndex();
487 analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
488 398
489 auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; 399 if (controller_type == Core::HID::NpadStyleIndex::None ||
490 400 !controller.device->IsConnected()) {
491 cur_entry.timestamp = last_entry.timestamp + 1; 401 // Refresh shared memory
492 cur_entry.timestamp2 = cur_entry.timestamp; 402 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
493 } 403 &controller.shared_memory_entry, sizeof(NpadInternalState));
494
495 const auto& controller_type = connected_controllers[i].type;
496
497 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
498 continue; 404 continue;
499 } 405 }
500 const u32 npad_index = static_cast<u32>(i);
501
502 RequestPadStateUpdate(npad_index);
503 auto& pad_state = npad_pad_states[npad_index];
504 auto& trigger_state = npad_trigger_states[npad_index];
505
506 auto& main_controller =
507 npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
508 auto& handheld_entry =
509 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
510 auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index];
511 auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index];
512 auto& right_entry =
513 npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index];
514 auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
515 auto& libnx_entry =
516 npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
517 auto& trigger_entry =
518 npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
519
520 libnx_entry.connection_status.raw = 0;
521 libnx_entry.connection_status.is_connected.Assign(1);
522 406
407 RequestPadStateUpdate(controller.device->GetNpadIdType());
408 auto& pad_state = controller.npad_pad_state;
409 auto& libnx_state = controller.npad_libnx_state;
410 auto& trigger_state = controller.npad_trigger_state;
411
412 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
413 // any controllers.
414 libnx_state.connection_status.raw = 0;
415 libnx_state.connection_status.is_connected.Assign(1);
523 switch (controller_type) { 416 switch (controller_type) {
524 case NPadControllerType::None: 417 case Core::HID::NpadStyleIndex::None:
525 UNREACHABLE(); 418 UNREACHABLE();
526 break; 419 break;
527 case NPadControllerType::ProController: 420 case Core::HID::NpadStyleIndex::ProController:
528 main_controller.connection_status.raw = 0; 421 case Core::HID::NpadStyleIndex::NES:
529 main_controller.connection_status.is_connected.Assign(1); 422 case Core::HID::NpadStyleIndex::SNES:
530 main_controller.connection_status.is_wired.Assign(1); 423 case Core::HID::NpadStyleIndex::N64:
531 main_controller.pad.pad_states.raw = pad_state.pad_states.raw; 424 case Core::HID::NpadStyleIndex::SegaGenesis:
532 main_controller.pad.l_stick = pad_state.l_stick; 425 pad_state.connection_status.raw = 0;
533 main_controller.pad.r_stick = pad_state.r_stick; 426 pad_state.connection_status.is_connected.Assign(1);
534 427 pad_state.connection_status.is_wired.Assign(1);
535 libnx_entry.connection_status.is_wired.Assign(1); 428
429 libnx_state.connection_status.is_wired.Assign(1);
430 pad_state.sampling_number =
431 npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
432 npad.fullkey_lifo.WriteNextEntry(pad_state);
433 break;
434 case Core::HID::NpadStyleIndex::Handheld:
435 pad_state.connection_status.raw = 0;
436 pad_state.connection_status.is_connected.Assign(1);
437 pad_state.connection_status.is_wired.Assign(1);
438 pad_state.connection_status.is_left_connected.Assign(1);
439 pad_state.connection_status.is_right_connected.Assign(1);
440 pad_state.connection_status.is_left_wired.Assign(1);
441 pad_state.connection_status.is_right_wired.Assign(1);
442
443 libnx_state.connection_status.is_wired.Assign(1);
444 libnx_state.connection_status.is_left_connected.Assign(1);
445 libnx_state.connection_status.is_right_connected.Assign(1);
446 libnx_state.connection_status.is_left_wired.Assign(1);
447 libnx_state.connection_status.is_right_wired.Assign(1);
448 pad_state.sampling_number =
449 npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
450 npad.handheld_lifo.WriteNextEntry(pad_state);
536 break; 451 break;
537 case NPadControllerType::Handheld: 452 case Core::HID::NpadStyleIndex::JoyconDual:
538 handheld_entry.connection_status.raw = 0; 453 pad_state.connection_status.raw = 0;
539 handheld_entry.connection_status.is_connected.Assign(1); 454 pad_state.connection_status.is_connected.Assign(1);
540 handheld_entry.connection_status.is_wired.Assign(1); 455 pad_state.connection_status.is_left_connected.Assign(1);
541 handheld_entry.connection_status.is_left_connected.Assign(1); 456 pad_state.connection_status.is_right_connected.Assign(1);
542 handheld_entry.connection_status.is_right_connected.Assign(1); 457
543 handheld_entry.connection_status.is_left_wired.Assign(1); 458 libnx_state.connection_status.is_left_connected.Assign(1);
544 handheld_entry.connection_status.is_right_wired.Assign(1); 459 libnx_state.connection_status.is_right_connected.Assign(1);
545 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 460 pad_state.sampling_number =
546 handheld_entry.pad.l_stick = pad_state.l_stick; 461 npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
547 handheld_entry.pad.r_stick = pad_state.r_stick; 462 npad.joy_dual_lifo.WriteNextEntry(pad_state);
548
549 libnx_entry.connection_status.is_wired.Assign(1);
550 libnx_entry.connection_status.is_left_connected.Assign(1);
551 libnx_entry.connection_status.is_right_connected.Assign(1);
552 libnx_entry.connection_status.is_left_wired.Assign(1);
553 libnx_entry.connection_status.is_right_wired.Assign(1);
554 break; 463 break;
555 case NPadControllerType::JoyDual: 464 case Core::HID::NpadStyleIndex::JoyconLeft:
556 dual_entry.connection_status.raw = 0; 465 pad_state.connection_status.raw = 0;
557 dual_entry.connection_status.is_connected.Assign(1); 466 pad_state.connection_status.is_connected.Assign(1);
558 dual_entry.connection_status.is_left_connected.Assign(1); 467 pad_state.connection_status.is_left_connected.Assign(1);
559 dual_entry.connection_status.is_right_connected.Assign(1); 468
560 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 469 libnx_state.connection_status.is_left_connected.Assign(1);
561 dual_entry.pad.l_stick = pad_state.l_stick; 470 pad_state.sampling_number =
562 dual_entry.pad.r_stick = pad_state.r_stick; 471 npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
563 472 npad.joy_left_lifo.WriteNextEntry(pad_state);
564 libnx_entry.connection_status.is_left_connected.Assign(1);
565 libnx_entry.connection_status.is_right_connected.Assign(1);
566 break; 473 break;
567 case NPadControllerType::JoyLeft: 474 case Core::HID::NpadStyleIndex::JoyconRight:
568 left_entry.connection_status.raw = 0; 475 pad_state.connection_status.raw = 0;
569 left_entry.connection_status.is_connected.Assign(1); 476 pad_state.connection_status.is_connected.Assign(1);
570 left_entry.connection_status.is_left_connected.Assign(1); 477 pad_state.connection_status.is_right_connected.Assign(1);
571 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 478
572 left_entry.pad.l_stick = pad_state.l_stick; 479 libnx_state.connection_status.is_right_connected.Assign(1);
573 left_entry.pad.r_stick = pad_state.r_stick; 480 pad_state.sampling_number =
574 481 npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
575 libnx_entry.connection_status.is_left_connected.Assign(1); 482 npad.joy_right_lifo.WriteNextEntry(pad_state);
576 break; 483 break;
577 case NPadControllerType::JoyRight: 484 case Core::HID::NpadStyleIndex::GameCube:
578 right_entry.connection_status.raw = 0; 485 pad_state.connection_status.raw = 0;
579 right_entry.connection_status.is_connected.Assign(1); 486 pad_state.connection_status.is_connected.Assign(1);
580 right_entry.connection_status.is_right_connected.Assign(1); 487 pad_state.connection_status.is_wired.Assign(1);
581 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 488
582 right_entry.pad.l_stick = pad_state.l_stick; 489 libnx_state.connection_status.is_wired.Assign(1);
583 right_entry.pad.r_stick = pad_state.r_stick; 490 pad_state.sampling_number =
584 491 npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
585 libnx_entry.connection_status.is_right_connected.Assign(1); 492 trigger_state.sampling_number =
493 npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
494 npad.fullkey_lifo.WriteNextEntry(pad_state);
495 npad.gc_trigger_lifo.WriteNextEntry(trigger_state);
586 break; 496 break;
587 case NPadControllerType::GameCube: 497 case Core::HID::NpadStyleIndex::Pokeball:
588 main_controller.connection_status.raw = 0; 498 pad_state.connection_status.raw = 0;
589 main_controller.connection_status.is_connected.Assign(1); 499 pad_state.connection_status.is_connected.Assign(1);
590 main_controller.connection_status.is_wired.Assign(1); 500 pad_state.sampling_number =
591 main_controller.pad.pad_states.raw = pad_state.pad_states.raw; 501 npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
592 main_controller.pad.l_stick = pad_state.l_stick; 502 npad.palma_lifo.WriteNextEntry(pad_state);
593 main_controller.pad.r_stick = pad_state.r_stick;
594 trigger_entry.l_analog = trigger_state.l_analog;
595 trigger_entry.r_analog = trigger_state.r_analog;
596
597 libnx_entry.connection_status.is_wired.Assign(1);
598 break; 503 break;
599 case NPadControllerType::Pokeball: 504 default:
600 pokeball_entry.connection_status.raw = 0;
601 pokeball_entry.connection_status.is_connected.Assign(1);
602 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
603 pokeball_entry.pad.l_stick = pad_state.l_stick;
604 pokeball_entry.pad.r_stick = pad_state.r_stick;
605 break; 505 break;
606 } 506 }
607 507
608 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 508 libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
609 // any controllers. 509 libnx_state.l_stick = pad_state.l_stick;
610 libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw; 510 libnx_state.r_stick = pad_state.r_stick;
611 libnx_entry.pad.l_stick = pad_state.l_stick; 511 npad.system_ext_lifo.WriteNextEntry(pad_state);
612 libnx_entry.pad.r_stick = pad_state.r_stick;
613 512
614 press_state |= static_cast<u32>(pad_state.pad_states.raw); 513 press_state |= static_cast<u32>(pad_state.npad_buttons.raw);
514
515 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
516 &controller.shared_memory_entry, sizeof(NpadInternalState));
615 } 517 }
616 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
617 shared_memory_entries.size() * sizeof(NPadEntry));
618} 518}
619 519
620void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 520void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -622,145 +522,138 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
622 if (!IsControllerActivated()) { 522 if (!IsControllerActivated()) {
623 return; 523 return;
624 } 524 }
625 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
626 auto& npad = shared_memory_entries[i];
627
628 const auto& controller_type = connected_controllers[i].type;
629
630 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
631 continue;
632 }
633 525
634 const std::array<SixAxisGeneric*, 6> controller_sixaxes{ 526 for (std::size_t i = 0; i < controller_data.size(); ++i) {
635 &npad.sixaxis_fullkey, &npad.sixaxis_handheld, &npad.sixaxis_dual_left, 527 auto& controller = controller_data[i];
636 &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
637 };
638 528
639 for (auto* sixaxis_sensor : controller_sixaxes) { 529 const auto& controller_type = controller.device->GetNpadStyleIndex();
640 sixaxis_sensor->common.entry_count = 16;
641 sixaxis_sensor->common.total_entry_count = 17;
642 530
643 const auto& last_entry = 531 if (controller_type == Core::HID::NpadStyleIndex::None ||
644 sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; 532 !controller.device->IsConnected()) {
645 533 continue;
646 sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
647 sixaxis_sensor->common.last_entry_index =
648 (sixaxis_sensor->common.last_entry_index + 1) % 17;
649
650 auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
651
652 cur_entry.timestamp = last_entry.timestamp + 1;
653 cur_entry.timestamp2 = cur_entry.timestamp;
654 } 534 }
655 535
656 // Try to read sixaxis sensor states 536 auto& npad = controller.shared_memory_entry;
657 std::array<MotionDevice, 2> motion_devices; 537 const auto& motion_state = controller.device->GetMotions();
658 538 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
659 if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) { 539 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
660 sixaxis_at_rest = true; 540 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
661 for (std::size_t e = 0; e < motion_devices.size(); ++e) { 541 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
662 const auto& device = motions[i][e]; 542 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
663 if (device) { 543 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
664 std::tie(motion_devices[e].accel, motion_devices[e].gyro, 544
665 motion_devices[e].rotation, motion_devices[e].orientation, 545 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
666 motion_devices[e].quaternion) = device->GetStatus(); 546 controller.sixaxis_at_rest = true;
667 sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; 547 for (std::size_t e = 0; e < motion_state.size(); ++e) {
668 } 548 controller.sixaxis_at_rest =
549 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
669 } 550 }
670 } 551 }
671 552
672 auto& full_sixaxis_entry =
673 npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index];
674 auto& handheld_sixaxis_entry =
675 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
676 auto& dual_left_sixaxis_entry =
677 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
678 auto& dual_right_sixaxis_entry =
679 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
680 auto& left_sixaxis_entry =
681 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
682 auto& right_sixaxis_entry =
683 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
684
685 switch (controller_type) { 553 switch (controller_type) {
686 case NPadControllerType::None: 554 case Core::HID::NpadStyleIndex::None:
687 UNREACHABLE(); 555 UNREACHABLE();
688 break; 556 break;
689 case NPadControllerType::ProController: 557 case Core::HID::NpadStyleIndex::ProController:
690 full_sixaxis_entry.attribute.raw = 0; 558 sixaxis_fullkey_state.attribute.raw = 0;
691 if (sixaxis_sensors_enabled && motions[i][0]) { 559 if (controller.sixaxis_sensor_enabled) {
692 full_sixaxis_entry.attribute.is_connected.Assign(1); 560 sixaxis_fullkey_state.attribute.is_connected.Assign(1);
693 full_sixaxis_entry.accel = motion_devices[0].accel; 561 sixaxis_fullkey_state.accel = motion_state[0].accel;
694 full_sixaxis_entry.gyro = motion_devices[0].gyro; 562 sixaxis_fullkey_state.gyro = motion_state[0].gyro;
695 full_sixaxis_entry.rotation = motion_devices[0].rotation; 563 sixaxis_fullkey_state.rotation = motion_state[0].rotation;
696 full_sixaxis_entry.orientation = motion_devices[0].orientation; 564 sixaxis_fullkey_state.orientation = motion_state[0].orientation;
697 } 565 }
698 break; 566 break;
699 case NPadControllerType::Handheld: 567 case Core::HID::NpadStyleIndex::Handheld:
700 handheld_sixaxis_entry.attribute.raw = 0; 568 sixaxis_handheld_state.attribute.raw = 0;
701 if (sixaxis_sensors_enabled && motions[i][0]) { 569 if (controller.sixaxis_sensor_enabled) {
702 handheld_sixaxis_entry.attribute.is_connected.Assign(1); 570 sixaxis_handheld_state.attribute.is_connected.Assign(1);
703 handheld_sixaxis_entry.accel = motion_devices[0].accel; 571 sixaxis_handheld_state.accel = motion_state[0].accel;
704 handheld_sixaxis_entry.gyro = motion_devices[0].gyro; 572 sixaxis_handheld_state.gyro = motion_state[0].gyro;
705 handheld_sixaxis_entry.rotation = motion_devices[0].rotation; 573 sixaxis_handheld_state.rotation = motion_state[0].rotation;
706 handheld_sixaxis_entry.orientation = motion_devices[0].orientation; 574 sixaxis_handheld_state.orientation = motion_state[0].orientation;
707 } 575 }
708 break; 576 break;
709 case NPadControllerType::JoyDual: 577 case Core::HID::NpadStyleIndex::JoyconDual:
710 dual_left_sixaxis_entry.attribute.raw = 0; 578 sixaxis_dual_left_state.attribute.raw = 0;
711 dual_right_sixaxis_entry.attribute.raw = 0; 579 sixaxis_dual_right_state.attribute.raw = 0;
712 if (sixaxis_sensors_enabled && motions[i][0]) { 580 if (controller.sixaxis_sensor_enabled) {
713 // Set motion for the left joycon 581 // Set motion for the left joycon
714 dual_left_sixaxis_entry.attribute.is_connected.Assign(1); 582 sixaxis_dual_left_state.attribute.is_connected.Assign(1);
715 dual_left_sixaxis_entry.accel = motion_devices[0].accel; 583 sixaxis_dual_left_state.accel = motion_state[0].accel;
716 dual_left_sixaxis_entry.gyro = motion_devices[0].gyro; 584 sixaxis_dual_left_state.gyro = motion_state[0].gyro;
717 dual_left_sixaxis_entry.rotation = motion_devices[0].rotation; 585 sixaxis_dual_left_state.rotation = motion_state[0].rotation;
718 dual_left_sixaxis_entry.orientation = motion_devices[0].orientation; 586 sixaxis_dual_left_state.orientation = motion_state[0].orientation;
719 } 587 }
720 if (sixaxis_sensors_enabled && motions[i][1]) { 588 if (controller.sixaxis_sensor_enabled) {
721 // Set motion for the right joycon 589 // Set motion for the right joycon
722 dual_right_sixaxis_entry.attribute.is_connected.Assign(1); 590 sixaxis_dual_right_state.attribute.is_connected.Assign(1);
723 dual_right_sixaxis_entry.accel = motion_devices[1].accel; 591 sixaxis_dual_right_state.accel = motion_state[1].accel;
724 dual_right_sixaxis_entry.gyro = motion_devices[1].gyro; 592 sixaxis_dual_right_state.gyro = motion_state[1].gyro;
725 dual_right_sixaxis_entry.rotation = motion_devices[1].rotation; 593 sixaxis_dual_right_state.rotation = motion_state[1].rotation;
726 dual_right_sixaxis_entry.orientation = motion_devices[1].orientation; 594 sixaxis_dual_right_state.orientation = motion_state[1].orientation;
727 } 595 }
728 break; 596 break;
729 case NPadControllerType::JoyLeft: 597 case Core::HID::NpadStyleIndex::JoyconLeft:
730 left_sixaxis_entry.attribute.raw = 0; 598 sixaxis_left_lifo_state.attribute.raw = 0;
731 if (sixaxis_sensors_enabled && motions[i][0]) { 599 if (controller.sixaxis_sensor_enabled) {
732 left_sixaxis_entry.attribute.is_connected.Assign(1); 600 sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
733 left_sixaxis_entry.accel = motion_devices[0].accel; 601 sixaxis_left_lifo_state.accel = motion_state[0].accel;
734 left_sixaxis_entry.gyro = motion_devices[0].gyro; 602 sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
735 left_sixaxis_entry.rotation = motion_devices[0].rotation; 603 sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
736 left_sixaxis_entry.orientation = motion_devices[0].orientation; 604 sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
737 } 605 }
738 break; 606 break;
739 case NPadControllerType::JoyRight: 607 case Core::HID::NpadStyleIndex::JoyconRight:
740 right_sixaxis_entry.attribute.raw = 0; 608 sixaxis_right_lifo_state.attribute.raw = 0;
741 if (sixaxis_sensors_enabled && motions[i][1]) { 609 if (controller.sixaxis_sensor_enabled) {
742 right_sixaxis_entry.attribute.is_connected.Assign(1); 610 sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
743 right_sixaxis_entry.accel = motion_devices[1].accel; 611 sixaxis_right_lifo_state.accel = motion_state[1].accel;
744 right_sixaxis_entry.gyro = motion_devices[1].gyro; 612 sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
745 right_sixaxis_entry.rotation = motion_devices[1].rotation; 613 sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
746 right_sixaxis_entry.orientation = motion_devices[1].orientation; 614 sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
747 } 615 }
748 break; 616 break;
749 case NPadControllerType::GameCube: 617 default:
750 case NPadControllerType::Pokeball:
751 break; 618 break;
752 } 619 }
620
621 sixaxis_fullkey_state.sampling_number =
622 npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
623 sixaxis_handheld_state.sampling_number =
624 npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
625 sixaxis_dual_left_state.sampling_number =
626 npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
627 sixaxis_dual_right_state.sampling_number =
628 npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
629 sixaxis_left_lifo_state.sampling_number =
630 npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
631 sixaxis_right_lifo_state.sampling_number =
632 npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
633
634 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
635 // This buffer only is updated on handheld on HW
636 npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
637 } else {
638 // Hanheld doesn't update this buffer on HW
639 npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
640 }
641
642 npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
643 npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
644 npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
645 npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
646 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
647 &controller.shared_memory_entry, sizeof(NpadInternalState));
753 } 648 }
754 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
755 shared_memory_entries.size() * sizeof(NPadEntry));
756} 649}
757 650
758void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) { 651void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
759 style.raw = style_set.raw; 652 hid_core.SetSupportedStyleTag(style_set);
760} 653}
761 654
762Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { 655Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
763 return style; 656 return hid_core.GetSupportedStyleTag();
764} 657}
765 658
766void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { 659void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
@@ -779,11 +672,11 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
779 return supported_npad_id_types.size(); 672 return supported_npad_id_types.size();
780} 673}
781 674
782void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 675void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
783 hold_type = joy_hold_type; 676 hold_type = joy_hold_type;
784} 677}
785 678
786Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { 679Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
787 return hold_type; 680 return hold_type;
788} 681}
789 682
@@ -803,29 +696,35 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
803 return communication_mode; 696 return communication_mode;
804} 697}
805 698
806void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) { 699void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
807 const std::size_t npad_index = NPadIdToIndex(npad_id); 700 NpadJoyAssignmentMode assignment_mode) {
808 ASSERT(npad_index < shared_memory_entries.size()); 701 if (!IsNpadIdValid(npad_id)) {
809 if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) { 702 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
810 shared_memory_entries[npad_index].assignment_mode = assignment_mode; 703 return;
704 }
705
706 auto& controller = GetControllerFromNpadIdType(npad_id);
707 if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
708 controller.shared_memory_entry.assignment_mode = assignment_mode;
811 } 709 }
812} 710}
813 711
814bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, 712bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
815 const VibrationValue& vibration_value) { 713 std::size_t device_index,
816 if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { 714 const Core::HID::VibrationValue& vibration_value) {
715 auto& controller = GetControllerFromNpadIdType(npad_id);
716 if (!controller.device->IsConnected()) {
817 return false; 717 return false;
818 } 718 }
819 719
820 const auto& player = Settings::values.players.GetValue()[npad_index]; 720 if (!controller.device->IsVibrationEnabled()) {
821 721 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
822 if (!player.vibration_enabled) { 722 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
823 if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
824 latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
825 // Send an empty vibration to stop any vibrations. 723 // Send an empty vibration to stop any vibrations.
826 vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); 724 Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
725 controller.device->SetVibration(device_index, vibration);
827 // Then reset the vibration value to its default value. 726 // Then reset the vibration value to its default value.
828 latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE; 727 controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
829 } 728 }
830 729
831 return false; 730 return false;
@@ -839,27 +738,25 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
839 const auto now = steady_clock::now(); 738 const auto now = steady_clock::now();
840 739
841 // Filter out non-zero vibrations that are within 10ms of each other. 740 // Filter out non-zero vibrations that are within 10ms of each other.
842 if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && 741 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
843 duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) < 742 duration_cast<milliseconds>(
743 now - controller.vibration[device_index].last_vibration_timepoint) <
844 milliseconds(10)) { 744 milliseconds(10)) {
845 return false; 745 return false;
846 } 746 }
847 747
848 last_vibration_timepoints[npad_index][device_index] = now; 748 controller.vibration[device_index].last_vibration_timepoint = now;
849 } 749 }
850 750
851 auto& vibration = vibrations[npad_index][device_index]; 751 Core::HID::VibrationValue vibration{
852 const auto player_vibration_strength = static_cast<f32>(player.vibration_strength); 752 vibration_value.low_amplitude, vibration_value.low_frequency,
853 const auto amp_low = 753 vibration_value.high_amplitude, vibration_value.high_frequency};
854 std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f); 754 return controller.device->SetVibration(device_index, vibration);
855 const auto amp_high =
856 std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
857 return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
858 vibration_value.freq_high);
859} 755}
860 756
861void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, 757void Controller_NPad::VibrateController(
862 const VibrationValue& vibration_value) { 758 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
759 const Core::HID::VibrationValue& vibration_value) {
863 if (!IsDeviceHandleValid(vibration_device_handle)) { 760 if (!IsDeviceHandleValid(vibration_device_handle)) {
864 return; 761 return;
865 } 762 }
@@ -868,42 +765,45 @@ void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_han
868 return; 765 return;
869 } 766 }
870 767
871 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 768 auto& controller = GetControllerFromHandle(vibration_device_handle);
872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 769 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
873 770
874 if (!vibration_devices_mounted[npad_index][device_index] || 771 if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
875 !connected_controllers[npad_index].is_connected) {
876 return; 772 return;
877 } 773 }
878 774
879 if (vibration_device_handle.device_index == DeviceIndex::None) { 775 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
880 UNREACHABLE_MSG("DeviceIndex should never be None!"); 776 UNREACHABLE_MSG("DeviceIndex should never be None!");
881 return; 777 return;
882 } 778 }
883 779
884 // Some games try to send mismatched parameters in the device handle, block these. 780 // Some games try to send mismatched parameters in the device handle, block these.
885 if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && 781 if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
886 (vibration_device_handle.npad_type == NpadType::JoyconRight || 782 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
887 vibration_device_handle.device_index == DeviceIndex::Right)) || 783 vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
888 (connected_controllers[npad_index].type == NPadControllerType::JoyRight && 784 (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
889 (vibration_device_handle.npad_type == NpadType::JoyconLeft || 785 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
890 vibration_device_handle.device_index == DeviceIndex::Left))) { 786 vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
891 return; 787 return;
892 } 788 }
893 789
894 // Filter out vibrations with equivalent values to reduce unnecessary state changes. 790 // Filter out vibrations with equivalent values to reduce unnecessary state changes.
895 if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && 791 if (vibration_value.low_amplitude ==
896 vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { 792 controller.vibration[device_index].latest_vibration_value.low_amplitude &&
793 vibration_value.high_amplitude ==
794 controller.vibration[device_index].latest_vibration_value.high_amplitude) {
897 return; 795 return;
898 } 796 }
899 797
900 if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { 798 if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index,
901 latest_vibration_values[npad_index][device_index] = vibration_value; 799 vibration_value)) {
800 controller.vibration[device_index].latest_vibration_value = vibration_value;
902 } 801 }
903} 802}
904 803
905void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 804void Controller_NPad::VibrateControllers(
906 const std::vector<VibrationValue>& vibration_values) { 805 const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
806 const std::vector<Core::HID::VibrationValue>& vibration_values) {
907 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 807 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
908 return; 808 return;
909 } 809 }
@@ -918,167 +818,231 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
918 } 818 }
919} 819}
920 820
921Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( 821Core::HID::VibrationValue Controller_NPad::GetLastVibration(
922 const DeviceHandle& vibration_device_handle) const { 822 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
923 if (!IsDeviceHandleValid(vibration_device_handle)) { 823 if (!IsDeviceHandleValid(vibration_device_handle)) {
924 return {}; 824 return {};
925 } 825 }
926 826
927 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 827 const auto& controller = GetControllerFromHandle(vibration_device_handle);
928 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 828 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
929 return latest_vibration_values[npad_index][device_index]; 829 return controller.vibration[device_index].latest_vibration_value;
930} 830}
931 831
932void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) { 832void Controller_NPad::InitializeVibrationDevice(
833 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
933 if (!IsDeviceHandleValid(vibration_device_handle)) { 834 if (!IsDeviceHandleValid(vibration_device_handle)) {
934 return; 835 return;
935 } 836 }
936 837
937 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 838 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
938 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 839 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
939 InitializeVibrationDeviceAtIndex(npad_index, device_index); 840 InitializeVibrationDeviceAtIndex(npad_index, device_index);
940} 841}
941 842
942void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, 843void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
943 std::size_t device_index) { 844 std::size_t device_index) {
845 auto& controller = GetControllerFromNpadIdType(npad_id);
944 if (!Settings::values.vibration_enabled.GetValue()) { 846 if (!Settings::values.vibration_enabled.GetValue()) {
945 vibration_devices_mounted[npad_index][device_index] = false; 847 controller.vibration[device_index].device_mounted = false;
946 return; 848 return;
947 } 849 }
948 850
949 if (vibrations[npad_index][device_index]) { 851 controller.vibration[device_index].device_mounted =
950 vibration_devices_mounted[npad_index][device_index] = 852 controller.device->TestVibration(device_index);
951 vibrations[npad_index][device_index]->GetStatus() == 1;
952 } else {
953 vibration_devices_mounted[npad_index][device_index] = false;
954 }
955} 853}
956 854
957void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 855void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
958 permit_vibration_session_enabled = permit_vibration_session; 856 permit_vibration_session_enabled = permit_vibration_session;
959} 857}
960 858
961bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { 859bool Controller_NPad::IsVibrationDeviceMounted(
860 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
962 if (!IsDeviceHandleValid(vibration_device_handle)) { 861 if (!IsDeviceHandleValid(vibration_device_handle)) {
963 return false; 862 return false;
964 } 863 }
965 864
966 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 865 const auto& controller = GetControllerFromHandle(vibration_device_handle);
967 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 866 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
968 return vibration_devices_mounted[npad_index][device_index]; 867 return controller.vibration[device_index].device_mounted;
969} 868}
970 869
971Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) { 870Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
972 return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent(); 871 if (!IsNpadIdValid(npad_id)) {
872 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
873 // Fallback to player 1
874 const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
875 return controller.styleset_changed_event->GetReadableEvent();
876 }
877
878 const auto& controller = GetControllerFromNpadIdType(npad_id);
879 return controller.styleset_changed_event->GetReadableEvent();
973} 880}
974 881
975void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const { 882void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
976 styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal(); 883 const auto& controller = GetControllerFromNpadIdType(npad_id);
884 controller.styleset_changed_event->GetWritableEvent().Signal();
977} 885}
978 886
979void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { 887void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
980 UpdateControllerAt(controller, npad_index, true); 888 Core::HID::NpadIdType npad_id) {
889 UpdateControllerAt(controller, npad_id, true);
981} 890}
982 891
983void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, 892void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
984 bool connected) { 893 Core::HID::NpadIdType npad_id, bool connected) {
894 auto& controller = GetControllerFromNpadIdType(npad_id);
985 if (!connected) { 895 if (!connected) {
986 DisconnectNpadAtIndex(npad_index); 896 DisconnectNpad(npad_id);
987 return; 897 return;
988 } 898 }
989 899
990 if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) { 900 controller.device->SetNpadStyleIndex(type);
991 Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type = 901 InitNewlyAddedController(npad_id);
992 MapNPadToSettingsType(controller);
993 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
994 connected_controllers[HANDHELD_INDEX] = {controller, true};
995 InitNewlyAddedController(HANDHELD_INDEX);
996 return;
997 }
998
999 Settings::values.players.GetValue()[npad_index].controller_type =
1000 MapNPadToSettingsType(controller);
1001 Settings::values.players.GetValue()[npad_index].connected = true;
1002 connected_controllers[npad_index] = {controller, true};
1003 InitNewlyAddedController(npad_index);
1004} 902}
1005 903
1006void Controller_NPad::DisconnectNpad(u32 npad_id) { 904void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1007 DisconnectNpadAtIndex(NPadIdToIndex(npad_id)); 905 if (!IsNpadIdValid(npad_id)) {
1008} 906 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
907 return;
908 }
1009 909
1010void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { 910 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
1011 for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { 911 auto& controller = GetControllerFromNpadIdType(npad_id);
912 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
1012 // Send an empty vibration to stop any vibrations. 913 // Send an empty vibration to stop any vibrations.
1013 VibrateControllerAtIndex(npad_index, device_idx, {}); 914 VibrateControllerAtIndex(npad_id, device_idx, {});
1014 vibration_devices_mounted[npad_index][device_idx] = false; 915 controller.vibration[device_idx].device_mounted = false;
1015 } 916 }
1016 917
1017 Settings::values.players.GetValue()[npad_index].connected = false; 918 auto& shared_memory_entry = controller.shared_memory_entry;
1018 connected_controllers[npad_index].is_connected = false; 919 shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
920 shared_memory_entry.device_type.raw = 0;
921 shared_memory_entry.system_properties.raw = 0;
922 shared_memory_entry.button_properties.raw = 0;
923 shared_memory_entry.battery_level_dual = 0;
924 shared_memory_entry.battery_level_left = 0;
925 shared_memory_entry.battery_level_right = 0;
926 shared_memory_entry.fullkey_color = {
927 .attribute = ColorAttribute::NoController,
928 .fullkey = {},
929 };
930 shared_memory_entry.joycon_color = {
931 .attribute = ColorAttribute::NoController,
932 .left = {},
933 .right = {},
934 };
935 shared_memory_entry.assignment_mode = NpadJoyAssignmentMode::Dual;
936 shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
1019 937
1020 auto& controller = shared_memory_entries[npad_index]; 938 controller.is_connected = false;
1021 controller.style_set.raw = 0; // Zero out 939 controller.device->Disconnect();
1022 controller.device_type.raw = 0; 940 SignalStyleSetChangedEvent(npad_id);
1023 controller.system_properties.raw = 0; 941 WriteEmptyEntry(controller.shared_memory_entry);
1024 controller.button_properties.raw = 0; 942}
1025 controller.battery_level_dual = 0;
1026 controller.battery_level_left = 0;
1027 controller.battery_level_right = 0;
1028 controller.fullkey_color = {};
1029 controller.joycon_color = {};
1030 controller.assignment_mode = NpadAssignments::Dual;
1031 controller.footer_type = AppletFooterUiType::None;
1032 943
1033 SignalStyleSetChangedEvent(IndexToNPad(npad_index)); 944void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
945 GyroscopeZeroDriftMode drift_mode) {
946 if (!IsDeviceHandleValid(sixaxis_handle)) {
947 LOG_ERROR(Service_HID, "Invalid handle");
948 return;
949 }
950 auto& controller = GetControllerFromHandle(sixaxis_handle);
951 controller.gyroscope_zero_drift_mode = drift_mode;
1034} 952}
1035 953
1036void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 954Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
1037 gyroscope_zero_drift_mode = drift_mode; 955 Core::HID::SixAxisSensorHandle sixaxis_handle) const {
956 if (!IsDeviceHandleValid(sixaxis_handle)) {
957 LOG_ERROR(Service_HID, "Invalid handle");
958 // Return the default value
959 return GyroscopeZeroDriftMode::Standard;
960 }
961 const auto& controller = GetControllerFromHandle(sixaxis_handle);
962 return controller.gyroscope_zero_drift_mode;
1038} 963}
1039 964
1040Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { 965bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
1041 return gyroscope_zero_drift_mode; 966 if (!IsDeviceHandleValid(sixaxis_handle)) {
967 LOG_ERROR(Service_HID, "Invalid handle");
968 // Return the default value
969 return true;
970 }
971 const auto& controller = GetControllerFromHandle(sixaxis_handle);
972 return controller.sixaxis_at_rest;
1042} 973}
1043 974
1044bool Controller_NPad::IsSixAxisSensorAtRest() const { 975void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1045 return sixaxis_at_rest; 976 bool sixaxis_status) {
977 if (!IsDeviceHandleValid(sixaxis_handle)) {
978 LOG_ERROR(Service_HID, "Invalid handle");
979 return;
980 }
981 auto& controller = GetControllerFromHandle(sixaxis_handle);
982 controller.sixaxis_sensor_enabled = sixaxis_status;
1046} 983}
1047 984
1048void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { 985void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1049 sixaxis_sensors_enabled = six_axis_status; 986 bool sixaxis_fusion_status) {
987 if (!IsDeviceHandleValid(sixaxis_handle)) {
988 LOG_ERROR(Service_HID, "Invalid handle");
989 return;
990 }
991 auto& controller = GetControllerFromHandle(sixaxis_handle);
992 controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
1050} 993}
1051 994
1052void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) { 995void Controller_NPad::SetSixAxisFusionParameters(
1053 sixaxis_fusion_parameter1 = parameter1; 996 Core::HID::SixAxisSensorHandle sixaxis_handle,
1054 sixaxis_fusion_parameter2 = parameter2; 997 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
998 if (!IsDeviceHandleValid(sixaxis_handle)) {
999 LOG_ERROR(Service_HID, "Invalid handle");
1000 return;
1001 }
1002 auto& controller = GetControllerFromHandle(sixaxis_handle);
1003 controller.sixaxis_fusion = sixaxis_fusion_parameters;
1055} 1004}
1056 1005
1057std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() { 1006Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
1058 return { 1007 Core::HID::SixAxisSensorHandle sixaxis_handle) {
1059 sixaxis_fusion_parameter1, 1008 if (!IsDeviceHandleValid(sixaxis_handle)) {
1060 sixaxis_fusion_parameter2, 1009 LOG_ERROR(Service_HID, "Invalid handle");
1061 }; 1010 // Since these parameters are unknow just return zeros
1011 return {};
1012 }
1013 auto& controller = GetControllerFromHandle(sixaxis_handle);
1014 return controller.sixaxis_fusion;
1062} 1015}
1063 1016
1064void Controller_NPad::ResetSixAxisFusionParameters() { 1017void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
1065 sixaxis_fusion_parameter1 = 0.0f; 1018 if (!IsDeviceHandleValid(sixaxis_handle)) {
1066 sixaxis_fusion_parameter2 = 0.0f; 1019 LOG_ERROR(Service_HID, "Invalid handle");
1020 return;
1021 }
1022 auto& controller = GetControllerFromHandle(sixaxis_handle);
1023 // Since these parameters are unknow just fill with zeros
1024 controller.sixaxis_fusion = {};
1067} 1025}
1068 1026
1069void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { 1027void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1070 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 1028 Core::HID::NpadIdType npad_id_2) {
1071 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 1029 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1030 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1031 npad_id_2);
1032 return;
1033 }
1034 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
1035 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
1072 1036
1073 // If the controllers at both npad indices form a pair of left and right joycons, merge them. 1037 // If the controllers at both npad indices form a pair of left and right joycons, merge them.
1074 // Otherwise, do nothing. 1038 // Otherwise, do nothing.
1075 if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft && 1039 if ((controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
1076 connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) || 1040 controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) ||
1077 (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && 1041 (controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
1078 connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { 1042 controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) {
1079 // Disconnect the joycon at the second id and connect the dual joycon at the first index. 1043 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1080 DisconnectNpad(npad_id_2); 1044 DisconnectNpad(npad_id_2);
1081 AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); 1045 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1082 } 1046 }
1083} 1047}
1084 1048
@@ -1092,61 +1056,61 @@ void Controller_NPad::StopLRAssignmentMode() {
1092 is_in_lr_assignment_mode = false; 1056 is_in_lr_assignment_mode = false;
1093} 1057}
1094 1058
1095bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { 1059bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1096 if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN || 1060 Core::HID::NpadIdType npad_id_2) {
1097 npad_id_2 == NPAD_UNKNOWN) { 1061 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1062 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1063 npad_id_2);
1064 return false;
1065 }
1066 if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
1067 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
1068 npad_id_2 == Core::HID::NpadIdType::Other) {
1098 return true; 1069 return true;
1099 } 1070 }
1100 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 1071 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
1101 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 1072 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
1073 const auto type_index_1 = controller_1->GetNpadStyleIndex();
1074 const auto type_index_2 = controller_2->GetNpadStyleIndex();
1102 1075
1103 if (!IsControllerSupported(connected_controllers[npad_index_1].type) || 1076 if (!IsControllerSupported(type_index_1) || !IsControllerSupported(type_index_2)) {
1104 !IsControllerSupported(connected_controllers[npad_index_2].type)) {
1105 return false; 1077 return false;
1106 } 1078 }
1107 1079
1108 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 1080 AddNewControllerAt(type_index_2, npad_id_1);
1109 1081 AddNewControllerAt(type_index_1, npad_id_2);
1110 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
1111 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
1112 1082
1113 return true; 1083 return true;
1114} 1084}
1115 1085
1116Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 1086Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
1117 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { 1087 if (!IsNpadIdValid(npad_id)) {
1118 // These are controllers without led patterns 1088 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1119 return LedPattern{0, 0, 0, 0}; 1089 return Core::HID::LedPattern{0, 0, 0, 0};
1120 }
1121 switch (npad_id) {
1122 case 0:
1123 return LedPattern{1, 0, 0, 0};
1124 case 1:
1125 return LedPattern{1, 1, 0, 0};
1126 case 2:
1127 return LedPattern{1, 1, 1, 0};
1128 case 3:
1129 return LedPattern{1, 1, 1, 1};
1130 case 4:
1131 return LedPattern{1, 0, 0, 1};
1132 case 5:
1133 return LedPattern{1, 0, 1, 0};
1134 case 6:
1135 return LedPattern{1, 0, 1, 1};
1136 case 7:
1137 return LedPattern{0, 1, 1, 0};
1138 default:
1139 return LedPattern{0, 0, 0, 0};
1140 } 1090 }
1091 const auto& controller = GetControllerFromNpadIdType(npad_id).device;
1092 return controller->GetLedPattern();
1141} 1093}
1142 1094
1143bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const { 1095bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
1144 return unintended_home_button_input_protection[NPadIdToIndex(npad_id)]; 1096 Core::HID::NpadIdType npad_id) const {
1097 if (!IsNpadIdValid(npad_id)) {
1098 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1099 // Return the default value
1100 return false;
1101 }
1102 const auto& controller = GetControllerFromNpadIdType(npad_id);
1103 return controller.unintended_home_button_input_protection;
1145} 1104}
1146 1105
1147void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 1106void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1148 u32 npad_id) { 1107 Core::HID::NpadIdType npad_id) {
1149 unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; 1108 if (!IsNpadIdValid(npad_id)) {
1109 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1110 return;
1111 }
1112 auto& controller = GetControllerFromNpadIdType(npad_id);
1113 controller.unintended_home_button_input_protection = is_protection_enabled;
1150} 1114}
1151 1115
1152void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1116void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1154,32 +1118,34 @@ void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1154} 1118}
1155 1119
1156void Controller_NPad::ClearAllConnectedControllers() { 1120void Controller_NPad::ClearAllConnectedControllers() {
1157 for (auto& controller : connected_controllers) { 1121 for (auto& controller : controller_data) {
1158 if (controller.is_connected && controller.type != NPadControllerType::None) { 1122 if (controller.device->IsConnected() &&
1159 controller.type = NPadControllerType::None; 1123 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
1160 controller.is_connected = false; 1124 controller.device->Disconnect();
1125 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1161 } 1126 }
1162 } 1127 }
1163} 1128}
1164 1129
1165void Controller_NPad::DisconnectAllConnectedControllers() { 1130void Controller_NPad::DisconnectAllConnectedControllers() {
1166 for (auto& controller : connected_controllers) { 1131 for (auto& controller : controller_data) {
1167 controller.is_connected = false; 1132 controller.device->Disconnect();
1168 } 1133 }
1169} 1134}
1170 1135
1171void Controller_NPad::ConnectAllDisconnectedControllers() { 1136void Controller_NPad::ConnectAllDisconnectedControllers() {
1172 for (auto& controller : connected_controllers) { 1137 for (auto& controller : controller_data) {
1173 if (controller.type != NPadControllerType::None && !controller.is_connected) { 1138 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1174 controller.is_connected = true; 1139 !controller.device->IsConnected()) {
1140 controller.device->Connect();
1175 } 1141 }
1176 } 1142 }
1177} 1143}
1178 1144
1179void Controller_NPad::ClearAllControllers() { 1145void Controller_NPad::ClearAllControllers() {
1180 for (auto& controller : connected_controllers) { 1146 for (auto& controller : controller_data) {
1181 controller.type = NPadControllerType::None; 1147 controller.device->Disconnect();
1182 controller.is_connected = false; 1148 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1183 } 1149 }
1184} 1150}
1185 1151
@@ -1187,16 +1153,16 @@ u32 Controller_NPad::GetAndResetPressState() {
1187 return press_state.exchange(0); 1153 return press_state.exchange(0);
1188} 1154}
1189 1155
1190bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 1156bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1191 if (controller == NPadControllerType::Handheld) { 1157 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1192 const bool support_handheld = 1158 const bool support_handheld =
1193 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1159 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1194 NPAD_HANDHELD) != supported_npad_id_types.end(); 1160 Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
1195 // Handheld is not even a supported type, lets stop here 1161 // Handheld is not even a supported type, lets stop here
1196 if (!support_handheld) { 1162 if (!support_handheld) {
1197 return false; 1163 return false;
1198 } 1164 }
1199 // Handheld should not be supported in docked mode 1165 // Handheld shouldn't be supported in docked mode
1200 if (Settings::values.use_docked_mode.GetValue()) { 1166 if (Settings::values.use_docked_mode.GetValue()) {
1201 return false; 1167 return false;
1202 } 1168 }
@@ -1205,20 +1171,31 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1205 } 1171 }
1206 1172
1207 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1173 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1208 [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { 1174 [](Core::HID::NpadIdType npad_id) {
1175 return npad_id <= Core::HID::NpadIdType::Player8;
1176 })) {
1177 Core::HID::NpadStyleTag style = GetSupportedStyleSet();
1209 switch (controller) { 1178 switch (controller) {
1210 case NPadControllerType::ProController: 1179 case Core::HID::NpadStyleIndex::ProController:
1211 return style.fullkey; 1180 return style.fullkey;
1212 case NPadControllerType::JoyDual: 1181 case Core::HID::NpadStyleIndex::JoyconDual:
1213 return style.joycon_dual; 1182 return style.joycon_dual;
1214 case NPadControllerType::JoyLeft: 1183 case Core::HID::NpadStyleIndex::JoyconLeft:
1215 return style.joycon_left; 1184 return style.joycon_left;
1216 case NPadControllerType::JoyRight: 1185 case Core::HID::NpadStyleIndex::JoyconRight:
1217 return style.joycon_right; 1186 return style.joycon_right;
1218 case NPadControllerType::GameCube: 1187 case Core::HID::NpadStyleIndex::GameCube:
1219 return style.gamecube; 1188 return style.gamecube;
1220 case NPadControllerType::Pokeball: 1189 case Core::HID::NpadStyleIndex::Pokeball:
1221 return style.palma; 1190 return style.palma;
1191 case Core::HID::NpadStyleIndex::NES:
1192 return style.lark;
1193 case Core::HID::NpadStyleIndex::SNES:
1194 return style.lucia;
1195 case Core::HID::NpadStyleIndex::N64:
1196 return style.lagoon;
1197 case Core::HID::NpadStyleIndex::SegaGenesis:
1198 return style.lager;
1222 default: 1199 default:
1223 return false; 1200 return false;
1224 } 1201 }
@@ -1227,4 +1204,48 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1227 return false; 1204 return false;
1228} 1205}
1229 1206
1207Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1208 const Core::HID::SixAxisSensorHandle& device_handle) {
1209 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1210 return GetControllerFromNpadIdType(npad_id);
1211}
1212
1213const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1214 const Core::HID::SixAxisSensorHandle& device_handle) const {
1215 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1216 return GetControllerFromNpadIdType(npad_id);
1217}
1218
1219Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1220 const Core::HID::VibrationDeviceHandle& device_handle) {
1221 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1222 return GetControllerFromNpadIdType(npad_id);
1223}
1224
1225const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1226 const Core::HID::VibrationDeviceHandle& device_handle) const {
1227 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1228 return GetControllerFromNpadIdType(npad_id);
1229}
1230
1231Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
1232 Core::HID::NpadIdType npad_id) {
1233 if (!IsNpadIdValid(npad_id)) {
1234 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1235 npad_id = Core::HID::NpadIdType::Player1;
1236 }
1237 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
1238 return controller_data[npad_index];
1239}
1240
1241const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
1242 Core::HID::NpadIdType npad_id) const {
1243 if (!IsNpadIdValid(npad_id)) {
1244 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1245 npad_id = Core::HID::NpadIdType::Player1;
1246 }
1247 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
1248 return controller_data[npad_index];
1249}
1250
1230} // namespace Service::HID 1251} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9ee146caf..9fa113bb6 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,9 +11,14 @@
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/quaternion.h" 13#include "common/quaternion.h"
14#include "common/settings.h" 14#include "core/hid/hid_types.h"
15#include "core/frontend/input.h"
16#include "core/hle/service/hid/controllers/controller_base.h" 15#include "core/hle/service/hid/controllers/controller_base.h"
16#include "core/hle/service/hid/ring_lifo.h"
17
18namespace Core::HID {
19class EmulatedController;
20enum class ControllerTriggerType;
21} // namespace Core::HID
17 22
18namespace Kernel { 23namespace Kernel {
19class KEvent; 24class KEvent;
@@ -26,12 +31,9 @@ class ServiceContext;
26 31
27namespace Service::HID { 32namespace Service::HID {
28 33
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31
32class Controller_NPad final : public ControllerBase { 34class Controller_NPad final : public ControllerBase {
33public: 35public:
34 explicit Controller_NPad(Core::System& system_, 36 explicit Controller_NPad(Core::HID::HIDCore& hid_core_,
35 KernelHelpers::ServiceContext& service_context_); 37 KernelHelpers::ServiceContext& service_context_);
36 ~Controller_NPad() override; 38 ~Controller_NPad() override;
37 39
@@ -48,60 +50,39 @@ public:
48 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 50 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
49 std::size_t size) override; 51 std::size_t size) override;
50 52
51 // Called when input devices should be loaded 53 // This is nn::hid::GyroscopeZeroDriftMode
52 void OnLoadInputDevices() override;
53
54 enum class NPadControllerType {
55 None,
56 ProController,
57 Handheld,
58 JoyDual,
59 JoyLeft,
60 JoyRight,
61 GameCube,
62 Pokeball,
63 };
64
65 enum class NpadType : u8 {
66 ProController = 3,
67 Handheld = 4,
68 JoyconDual = 5,
69 JoyconLeft = 6,
70 JoyconRight = 7,
71 GameCube = 8,
72 Pokeball = 9,
73 MaxNpadType = 10,
74 };
75
76 enum class DeviceIndex : u8 {
77 Left = 0,
78 Right = 1,
79 None = 2,
80 MaxDeviceIndex = 3,
81 };
82
83 enum class GyroscopeZeroDriftMode : u32 { 54 enum class GyroscopeZeroDriftMode : u32 {
84 Loose = 0, 55 Loose = 0,
85 Standard = 1, 56 Standard = 1,
86 Tight = 2, 57 Tight = 2,
87 }; 58 };
88 59
89 enum class NpadHoldType : u64 { 60 // This is nn::hid::NpadJoyHoldType
61 enum class NpadJoyHoldType : u64 {
90 Vertical = 0, 62 Vertical = 0,
91 Horizontal = 1, 63 Horizontal = 1,
92 }; 64 };
93 65
94 enum class NpadAssignments : u32 { 66 // This is nn::hid::NpadJoyAssignmentMode
67 enum class NpadJoyAssignmentMode : u32 {
95 Dual = 0, 68 Dual = 0,
96 Single = 1, 69 Single = 1,
97 }; 70 };
98 71
72 // This is nn::hid::NpadJoyDeviceType
73 enum class NpadJoyDeviceType : s64 {
74 Left = 0,
75 Right = 1,
76 };
77
78 // This is nn::hid::NpadHandheldActivationMode
99 enum class NpadHandheldActivationMode : u64 { 79 enum class NpadHandheldActivationMode : u64 {
100 Dual = 0, 80 Dual = 0,
101 Single = 1, 81 Single = 1,
102 None = 2, 82 None = 2,
103 }; 83 };
104 84
85 // This is nn::hid::NpadCommunicationMode
105 enum class NpadCommunicationMode : u64 { 86 enum class NpadCommunicationMode : u64 {
106 Mode_5ms = 0, 87 Mode_5ms = 0,
107 Mode_10ms = 1, 88 Mode_10ms = 1,
@@ -109,74 +90,22 @@ public:
109 Default = 3, 90 Default = 3,
110 }; 91 };
111 92
112 struct DeviceHandle { 93 static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
113 NpadType npad_type; 94 .low_amplitude = 0.0f,
114 u8 npad_id; 95 .low_frequency = 160.0f,
115 DeviceIndex device_index; 96 .high_amplitude = 0.0f,
116 INSERT_PADDING_BYTES_NOINIT(1); 97 .high_frequency = 320.0f,
117 }; 98 };
118 static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
119 99
120 struct NpadStyleSet { 100 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
121 union { 101 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
122 u32_le raw{};
123
124 BitField<0, 1, u32> fullkey;
125 BitField<1, 1, u32> handheld;
126 BitField<2, 1, u32> joycon_dual;
127 BitField<3, 1, u32> joycon_left;
128 BitField<4, 1, u32> joycon_right;
129 BitField<5, 1, u32> gamecube;
130 BitField<6, 1, u32> palma;
131 BitField<7, 1, u32> lark;
132 BitField<8, 1, u32> handheld_lark;
133 BitField<9, 1, u32> lucia;
134 BitField<29, 1, u32> system_ext;
135 BitField<30, 1, u32> system;
136 };
137 };
138 static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
139
140 struct VibrationValue {
141 f32 amp_low;
142 f32 freq_low;
143 f32 amp_high;
144 f32 freq_high;
145 };
146 static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
147
148 static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
149 .amp_low = 0.0f,
150 .freq_low = 160.0f,
151 .amp_high = 0.0f,
152 .freq_high = 320.0f,
153 };
154
155 struct LedPattern {
156 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
157 position1.Assign(light1);
158 position2.Assign(light2);
159 position3.Assign(light3);
160 position4.Assign(light4);
161 }
162 union {
163 u64 raw{};
164 BitField<0, 1, u64> position1;
165 BitField<1, 1, u64> position2;
166 BitField<2, 1, u64> position3;
167 BitField<3, 1, u64> position4;
168 };
169 };
170
171 void SetSupportedStyleSet(NpadStyleSet style_set);
172 NpadStyleSet GetSupportedStyleSet() const;
173 102
174 void SetSupportedNpadIdTypes(u8* data, std::size_t length); 103 void SetSupportedNpadIdTypes(u8* data, std::size_t length);
175 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 104 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
176 std::size_t GetSupportedNpadIdTypesSize() const; 105 std::size_t GetSupportedNpadIdTypesSize() const;
177 106
178 void SetHoldType(NpadHoldType joy_hold_type); 107 void SetHoldType(NpadJoyHoldType joy_hold_type);
179 NpadHoldType GetHoldType() const; 108 NpadJoyHoldType GetHoldType() const;
180 109
181 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); 110 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
182 NpadHandheldActivationMode GetNpadHandheldActivationMode() const; 111 NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
@@ -184,162 +113,106 @@ public:
184 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); 113 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
185 NpadCommunicationMode GetNpadCommunicationMode() const; 114 NpadCommunicationMode GetNpadCommunicationMode() const;
186 115
187 void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); 116 void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyAssignmentMode assignment_mode);
188 117
189 bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, 118 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
190 const VibrationValue& vibration_value); 119 const Core::HID::VibrationValue& vibration_value);
191 120
192 void VibrateController(const DeviceHandle& vibration_device_handle, 121 void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
193 const VibrationValue& vibration_value); 122 const Core::HID::VibrationValue& vibration_value);
194 123
195 void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 124 void VibrateControllers(
196 const std::vector<VibrationValue>& vibration_values); 125 const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
126 const std::vector<Core::HID::VibrationValue>& vibration_values);
197 127
198 VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; 128 Core::HID::VibrationValue GetLastVibration(
129 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
199 130
200 void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle); 131 void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
201 132
202 void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); 133 void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
203 134
204 void SetPermitVibrationSession(bool permit_vibration_session); 135 void SetPermitVibrationSession(bool permit_vibration_session);
205 136
206 bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; 137 bool IsVibrationDeviceMounted(
138 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
207 139
208 Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id); 140 Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
209 void SignalStyleSetChangedEvent(u32 npad_id) const; 141 void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
210 142
211 // Adds a new controller at an index. 143 // Adds a new controller at an index.
212 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); 144 void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
213 // Adds a new controller at an index with connection status. 145 // Adds a new controller at an index with connection status.
214 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); 146 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
215 147 bool connected);
216 void DisconnectNpad(u32 npad_id); 148
217 void DisconnectNpadAtIndex(std::size_t index); 149 void DisconnectNpad(Core::HID::NpadIdType npad_id);
218 150
219 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 151 void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
220 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 152 GyroscopeZeroDriftMode drift_mode);
221 bool IsSixAxisSensorAtRest() const; 153 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
222 void SetSixAxisEnabled(bool six_axis_status); 154 Core::HID::SixAxisSensorHandle sixaxis_handle) const;
223 void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2); 155 bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
224 std::pair<f32, f32> GetSixAxisFusionParameters(); 156 void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
225 void ResetSixAxisFusionParameters(); 157 void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
226 LedPattern GetLedPattern(u32 npad_id); 158 bool sixaxis_fusion_status);
227 bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; 159 void SetSixAxisFusionParameters(
228 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); 160 Core::HID::SixAxisSensorHandle sixaxis_handle,
161 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
162 Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
163 Core::HID::SixAxisSensorHandle sixaxis_handle);
164 void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
165 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
166 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
167 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
168 Core::HID::NpadIdType npad_id);
229 void SetAnalogStickUseCenterClamp(bool use_center_clamp); 169 void SetAnalogStickUseCenterClamp(bool use_center_clamp);
230 void ClearAllConnectedControllers(); 170 void ClearAllConnectedControllers();
231 void DisconnectAllConnectedControllers(); 171 void DisconnectAllConnectedControllers();
232 void ConnectAllDisconnectedControllers(); 172 void ConnectAllDisconnectedControllers();
233 void ClearAllControllers(); 173 void ClearAllControllers();
234 174
235 void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2); 175 void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
236 void StartLRAssignmentMode(); 176 void StartLRAssignmentMode();
237 void StopLRAssignmentMode(); 177 void StopLRAssignmentMode();
238 bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); 178 bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
239 179
240 // Logical OR for all buttons presses on all controllers 180 // Logical OR for all buttons presses on all controllers
241 // Specifically for cheat engine and other features. 181 // Specifically for cheat engine and other features.
242 u32 GetAndResetPressState(); 182 u32 GetAndResetPressState();
243 183
244 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); 184 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
245 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); 185 static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
246 static std::size_t NPadIdToIndex(u32 npad_id); 186 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
247 static u32 IndexToNPad(std::size_t index);
248 static bool IsNpadIdValid(u32 npad_id);
249 static bool IsDeviceHandleValid(const DeviceHandle& device_handle);
250 187
251private: 188private:
252 struct CommonHeader { 189 // This is nn::hid::detail::ColorAttribute
253 s64_le timestamp; 190 enum class ColorAttribute : u32 {
254 s64_le total_entry_count;
255 s64_le last_entry_index;
256 s64_le entry_count;
257 };
258 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
259
260 enum class ColorAttributes : u32_le {
261 Ok = 0, 191 Ok = 0,
262 ReadError = 1, 192 ReadError = 1,
263 NoController = 2, 193 NoController = 2,
264 }; 194 };
265 static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size"); 195 static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
266 196
267 struct ControllerColor { 197 // This is nn::hid::detail::NpadFullKeyColorState
268 u32_le body; 198 struct NpadFullKeyColorState {
269 u32_le button; 199 ColorAttribute attribute;
200 Core::HID::NpadControllerColor fullkey;
270 }; 201 };
271 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size"); 202 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
272 203
273 struct FullKeyColor { 204 // This is nn::hid::detail::NpadJoyColorState
274 ColorAttributes attribute; 205 struct NpadJoyColorState {
275 ControllerColor fullkey; 206 ColorAttribute attribute;
207 Core::HID::NpadControllerColor left;
208 Core::HID::NpadControllerColor right;
276 }; 209 };
277 static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size"); 210 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
278 211
279 struct JoyconColor { 212 // This is nn::hid::NpadAttribute
280 ColorAttributes attribute; 213 struct NpadAttribute {
281 ControllerColor left;
282 ControllerColor right;
283 };
284 static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
285
286 struct ControllerPadState {
287 union {
288 u64_le raw{};
289 // Button states
290 BitField<0, 1, u64> a;
291 BitField<1, 1, u64> b;
292 BitField<2, 1, u64> x;
293 BitField<3, 1, u64> y;
294 BitField<4, 1, u64> l_stick;
295 BitField<5, 1, u64> r_stick;
296 BitField<6, 1, u64> l;
297 BitField<7, 1, u64> r;
298 BitField<8, 1, u64> zl;
299 BitField<9, 1, u64> zr;
300 BitField<10, 1, u64> plus;
301 BitField<11, 1, u64> minus;
302
303 // D-Pad
304 BitField<12, 1, u64> d_left;
305 BitField<13, 1, u64> d_up;
306 BitField<14, 1, u64> d_right;
307 BitField<15, 1, u64> d_down;
308
309 // Left JoyStick
310 BitField<16, 1, u64> l_stick_left;
311 BitField<17, 1, u64> l_stick_up;
312 BitField<18, 1, u64> l_stick_right;
313 BitField<19, 1, u64> l_stick_down;
314
315 // Right JoyStick
316 BitField<20, 1, u64> r_stick_left;
317 BitField<21, 1, u64> r_stick_up;
318 BitField<22, 1, u64> r_stick_right;
319 BitField<23, 1, u64> r_stick_down;
320
321 // Not always active?
322 BitField<24, 1, u64> left_sl;
323 BitField<25, 1, u64> left_sr;
324
325 BitField<26, 1, u64> right_sl;
326 BitField<27, 1, u64> right_sr;
327
328 BitField<28, 1, u64> palma;
329 BitField<30, 1, u64> handheld_left_b;
330 };
331 };
332 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
333
334 struct AnalogPosition {
335 s32_le x;
336 s32_le y;
337 };
338 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
339
340 struct ConnectionState {
341 union { 214 union {
342 u32_le raw{}; 215 u32 raw{};
343 BitField<0, 1, u32> is_connected; 216 BitField<0, 1, u32> is_connected;
344 BitField<1, 1, u32> is_wired; 217 BitField<1, 1, u32> is_wired;
345 BitField<2, 1, u32> is_left_connected; 218 BitField<2, 1, u32> is_left_connected;
@@ -348,79 +221,60 @@ private:
348 BitField<5, 1, u32> is_right_wired; 221 BitField<5, 1, u32> is_right_wired;
349 }; 222 };
350 }; 223 };
351 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 224 static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
352 225
353 struct ControllerPad { 226 // This is nn::hid::NpadFullKeyState
354 ControllerPadState pad_states; 227 // This is nn::hid::NpadHandheldState
355 AnalogPosition l_stick; 228 // This is nn::hid::NpadJoyDualState
356 AnalogPosition r_stick; 229 // This is nn::hid::NpadJoyLeftState
357 }; 230 // This is nn::hid::NpadJoyRightState
358 static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size"); 231 // This is nn::hid::NpadPalmaState
359 232 // This is nn::hid::NpadSystemExtState
360 struct GenericStates { 233 struct NPadGenericState {
361 s64_le timestamp; 234 s64_le sampling_number;
362 s64_le timestamp2; 235 Core::HID::NpadButtonState npad_buttons;
363 ControllerPad pad; 236 Core::HID::AnalogStickState l_stick;
364 ConnectionState connection_status; 237 Core::HID::AnalogStickState r_stick;
365 }; 238 NpadAttribute connection_status;
366 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); 239 INSERT_PADDING_BYTES(4); // Reserved
367
368 struct NPadGeneric {
369 CommonHeader common;
370 std::array<GenericStates, 17> npad;
371 }; 240 };
372 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); 241 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
373 242
374 struct SixAxisAttributes { 243 // This is nn::hid::SixAxisSensorAttribute
244 struct SixAxisSensorAttribute {
375 union { 245 union {
376 u32_le raw{}; 246 u32 raw{};
377 BitField<0, 1, u32> is_connected; 247 BitField<0, 1, u32> is_connected;
378 BitField<1, 1, u32> is_interpolated; 248 BitField<1, 1, u32> is_interpolated;
379 }; 249 };
380 }; 250 };
381 static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size"); 251 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
382 252
383 struct SixAxisStates { 253 // This is nn::hid::SixAxisSensorState
384 s64_le timestamp{}; 254 struct SixAxisSensorState {
385 INSERT_PADDING_WORDS(2); 255 s64 delta_time{};
386 s64_le timestamp2{}; 256 s64 sampling_number{};
387 Common::Vec3f accel{}; 257 Common::Vec3f accel{};
388 Common::Vec3f gyro{}; 258 Common::Vec3f gyro{};
389 Common::Vec3f rotation{}; 259 Common::Vec3f rotation{};
390 std::array<Common::Vec3f, 3> orientation{}; 260 std::array<Common::Vec3f, 3> orientation{};
391 SixAxisAttributes attribute; 261 SixAxisSensorAttribute attribute;
392 INSERT_PADDING_BYTES(4); // Reserved 262 INSERT_PADDING_BYTES(4); // Reserved
393 }; 263 };
394 static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size"); 264 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
395
396 struct SixAxisGeneric {
397 CommonHeader common{};
398 std::array<SixAxisStates, 17> sixaxis{};
399 };
400 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
401 265
402 struct TriggerState { 266 // This is nn::hid::server::NpadGcTriggerState
403 s64_le timestamp{}; 267 struct NpadGcTriggerState {
404 s64_le timestamp2{}; 268 s64 sampling_number{};
405 s32_le l_analog{}; 269 s32 l_analog{};
406 s32_le r_analog{}; 270 s32 r_analog{};
407 }; 271 };
408 static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size"); 272 static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
409
410 struct TriggerGeneric {
411 INSERT_PADDING_BYTES(0x4);
412 s64_le timestamp;
413 INSERT_PADDING_BYTES(0x4);
414 s64_le total_entry_count;
415 s64_le last_entry_index;
416 s64_le entry_count;
417 std::array<TriggerState, 17> trigger{};
418 };
419 static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
420 273
274 // This is nn::hid::NpadSystemProperties
421 struct NPadSystemProperties { 275 struct NPadSystemProperties {
422 union { 276 union {
423 s64_le raw{}; 277 s64 raw{};
424 BitField<0, 1, s64> is_charging_joy_dual; 278 BitField<0, 1, s64> is_charging_joy_dual;
425 BitField<1, 1, s64> is_charging_joy_left; 279 BitField<1, 1, s64> is_charging_joy_left;
426 BitField<2, 1, s64> is_charging_joy_right; 280 BitField<2, 1, s64> is_charging_joy_right;
@@ -438,17 +292,20 @@ private:
438 }; 292 };
439 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); 293 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
440 294
441 struct NPadButtonProperties { 295 // This is nn::hid::NpadSystemButtonProperties
296 struct NpadSystemButtonProperties {
442 union { 297 union {
443 s32_le raw{}; 298 s32 raw{};
444 BitField<0, 1, s32> is_home_button_protection_enabled; 299 BitField<0, 1, s32> is_home_button_protection_enabled;
445 }; 300 };
446 }; 301 };
447 static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); 302 static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
303 "NPadButtonProperties is an invalid size");
448 304
449 struct NPadDevice { 305 // This is nn::hid::system::DeviceType
306 struct DeviceType {
450 union { 307 union {
451 u32_le raw{}; 308 u32 raw{};
452 BitField<0, 1, s32> fullkey; 309 BitField<0, 1, s32> fullkey;
453 BitField<1, 1, s32> debug_pad; 310 BitField<1, 1, s32> debug_pad;
454 BitField<2, 1, s32> handheld_left; 311 BitField<2, 1, s32> handheld_left;
@@ -465,26 +322,29 @@ private:
465 BitField<13, 1, s32> handheld_lark_nes_left; 322 BitField<13, 1, s32> handheld_lark_nes_left;
466 BitField<14, 1, s32> handheld_lark_nes_right; 323 BitField<14, 1, s32> handheld_lark_nes_right;
467 BitField<15, 1, s32> lucia; 324 BitField<15, 1, s32> lucia;
325 BitField<16, 1, s32> lagon;
326 BitField<17, 1, s32> lager;
468 BitField<31, 1, s32> system; 327 BitField<31, 1, s32> system;
469 }; 328 };
470 }; 329 };
471 330
472 struct MotionDevice { 331 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
473 Common::Vec3f accel; 332 struct NfcXcdDeviceHandleStateImpl {
474 Common::Vec3f gyro; 333 u64 handle;
475 Common::Vec3f rotation; 334 bool is_available;
476 std::array<Common::Vec3f, 3> orientation; 335 bool is_activated;
477 Common::Quaternion<f32> quaternion; 336 INSERT_PADDING_BYTES(0x6); // Reserved
478 }; 337 u64 sampling_number;
479
480 struct NfcXcdHandle {
481 INSERT_PADDING_BYTES(0x60);
482 }; 338 };
339 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
340 "NfcXcdDeviceHandleStateImpl is an invalid size");
483 341
342 // This is nn::hid::system::AppletFooterUiAttributesSet
484 struct AppletFooterUiAttributes { 343 struct AppletFooterUiAttributes {
485 INSERT_PADDING_BYTES(0x4); 344 INSERT_PADDING_BYTES(0x4);
486 }; 345 };
487 346
347 // This is nn::hid::system::AppletFooterUiType
488 enum class AppletFooterUiType : u8 { 348 enum class AppletFooterUiType : u8 {
489 None = 0, 349 None = 0,
490 HandheldNone = 1, 350 HandheldNone = 1,
@@ -510,95 +370,150 @@ private:
510 Lagon = 21, 370 Lagon = 21,
511 }; 371 };
512 372
513 struct NPadEntry { 373 struct AppletFooterUi {
514 NpadStyleSet style_set; 374 AppletFooterUiAttributes attributes;
515 NpadAssignments assignment_mode; 375 AppletFooterUiType type;
516 FullKeyColor fullkey_color; 376 INSERT_PADDING_BYTES(0x5B); // Reserved
517 JoyconColor joycon_color; 377 };
518 378 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
519 NPadGeneric fullkey_states; 379
520 NPadGeneric handheld_states; 380 // This is nn::hid::NpadLarkType
521 NPadGeneric joy_dual_states; 381 enum class NpadLarkType : u32 {
522 NPadGeneric joy_left_states; 382 Invalid,
523 NPadGeneric joy_right_states; 383 H1,
524 NPadGeneric palma_states; 384 H2,
525 NPadGeneric system_ext_states; 385 NL,
526 SixAxisGeneric sixaxis_fullkey; 386 NR,
527 SixAxisGeneric sixaxis_handheld; 387 };
528 SixAxisGeneric sixaxis_dual_left; 388
529 SixAxisGeneric sixaxis_dual_right; 389 // This is nn::hid::NpadLuciaType
530 SixAxisGeneric sixaxis_left; 390 enum class NpadLuciaType : u32 {
531 SixAxisGeneric sixaxis_right; 391 Invalid,
532 NPadDevice device_type; 392 J,
533 INSERT_PADDING_BYTES(0x4); // reserved 393 E,
394 U,
395 };
396
397 // This is nn::hid::NpadLagonType
398 enum class NpadLagonType : u32 {
399 Invalid,
400 };
401
402 // This is nn::hid::NpadLagerType
403 enum class NpadLagerType : u32 {
404 Invalid,
405 J,
406 E,
407 U,
408 };
409
410 // This is nn::hid::detail::NpadInternalState
411 struct NpadInternalState {
412 Core::HID::NpadStyleTag style_tag;
413 NpadJoyAssignmentMode assignment_mode;
414 NpadFullKeyColorState fullkey_color;
415 NpadJoyColorState joycon_color;
416 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
417 Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
418 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
419 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
420 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
421 Lifo<NPadGenericState, hid_entry_count> palma_lifo;
422 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
423 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
424 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
425 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
426 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
427 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
428 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
429 DeviceType device_type;
430 INSERT_PADDING_BYTES(0x4); // Reserved
534 NPadSystemProperties system_properties; 431 NPadSystemProperties system_properties;
535 NPadButtonProperties button_properties; 432 NpadSystemButtonProperties button_properties;
536 u32 battery_level_dual; 433 Core::HID::NpadBatteryLevel battery_level_dual;
537 u32 battery_level_left; 434 Core::HID::NpadBatteryLevel battery_level_left;
538 u32 battery_level_right; 435 Core::HID::NpadBatteryLevel battery_level_right;
539 AppletFooterUiAttributes footer_attributes; 436 union {
540 AppletFooterUiType footer_type; 437 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
541 // nfc_states needs to be checked switchbrew does not match with HW 438 AppletFooterUi applet_footer;
542 NfcXcdHandle nfc_states; 439 };
543 INSERT_PADDING_BYTES(0x8); // Mutex 440 INSERT_PADDING_BYTES(0x20); // Unknown
544 TriggerGeneric gc_trigger_states; 441 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
545 INSERT_PADDING_BYTES(0xc1f); 442 NpadLarkType lark_type_l_and_main;
546 }; 443 NpadLarkType lark_type_r;
547 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 444 NpadLuciaType lucia_type;
548 445 NpadLagonType lagon_type;
549 struct ControllerHolder { 446 NpadLagerType lager_type;
550 NPadControllerType type; 447 // FW 13.x Investigate there is some sort of bitflag related to joycons
551 bool is_connected; 448 INSERT_PADDING_BYTES(0x4);
552 }; 449 INSERT_PADDING_BYTES(0xc08); // Unknown
553 450 };
554 void InitNewlyAddedController(std::size_t controller_idx); 451 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
555 bool IsControllerSupported(NPadControllerType controller) const; 452
556 void RequestPadStateUpdate(u32 npad_id); 453 struct VibrationData {
454 bool device_mounted{};
455 Core::HID::VibrationValue latest_vibration_value{};
456 std::chrono::steady_clock::time_point last_vibration_timepoint{};
457 };
458
459 struct NpadControllerData {
460 Core::HID::EmulatedController* device;
461 Kernel::KEvent* styleset_changed_event{};
462 NpadInternalState shared_memory_entry{};
463
464 std::array<VibrationData, 2> vibration{};
465 bool unintended_home_button_input_protection{};
466 bool is_connected{};
467 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
468
469 // Motion parameters
470 bool sixaxis_at_rest{true};
471 bool sixaxis_sensor_enabled{true};
472 bool sixaxis_fusion_enabled{false};
473 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
474 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
475
476 // Current pad state
477 NPadGenericState npad_pad_state{};
478 NPadGenericState npad_libnx_state{};
479 NpadGcTriggerState npad_trigger_state{};
480 SixAxisSensorState sixaxis_fullkey_state{};
481 SixAxisSensorState sixaxis_handheld_state{};
482 SixAxisSensorState sixaxis_dual_left_state{};
483 SixAxisSensorState sixaxis_dual_right_state{};
484 SixAxisSensorState sixaxis_left_lifo_state{};
485 SixAxisSensorState sixaxis_right_lifo_state{};
486 int callback_key;
487 };
488
489 void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
490 void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
491 bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
492 void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
493 void WriteEmptyEntry(NpadInternalState& npad);
494
495 NpadControllerData& GetControllerFromHandle(
496 const Core::HID::SixAxisSensorHandle& device_handle);
497 const NpadControllerData& GetControllerFromHandle(
498 const Core::HID::SixAxisSensorHandle& device_handle) const;
499 NpadControllerData& GetControllerFromHandle(
500 const Core::HID::VibrationDeviceHandle& device_handle);
501 const NpadControllerData& GetControllerFromHandle(
502 const Core::HID::VibrationDeviceHandle& device_handle) const;
503 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
504 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
557 505
558 std::atomic<u32> press_state{}; 506 std::atomic<u32> press_state{};
559 507
560 NpadStyleSet style{}; 508 std::array<NpadControllerData, 10> controller_data{};
561 std::array<NPadEntry, 10> shared_memory_entries{};
562 using ButtonArray = std::array<
563 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
564 10>;
565 using StickArray = std::array<
566 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
567 10>;
568 using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
569 Settings::NativeVibration::NUM_VIBRATIONS_HID>,
570 10>;
571 using MotionArray = std::array<
572 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
573 10>;
574
575 KernelHelpers::ServiceContext& service_context; 509 KernelHelpers::ServiceContext& service_context;
576 std::mutex mutex; 510 std::mutex mutex;
577 ButtonArray buttons; 511 std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
578 StickArray sticks; 512 NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
579 VibrationArray vibrations;
580 MotionArray motions;
581 std::vector<u32> supported_npad_id_types{};
582 NpadHoldType hold_type{NpadHoldType::Vertical};
583 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 513 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
584 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 514 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
585 // Each controller should have their own styleset changed event
586 std::array<Kernel::KEvent*, 10> styleset_changed_events{};
587 std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
588 last_vibration_timepoints{};
589 std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
590 bool permit_vibration_session_enabled{false}; 515 bool permit_vibration_session_enabled{false};
591 std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
592 std::array<ControllerHolder, 10> connected_controllers{};
593 std::array<bool, 10> unintended_home_button_input_protection{};
594 bool analog_stick_use_center_clamp{}; 516 bool analog_stick_use_center_clamp{};
595 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
596 bool sixaxis_sensors_enabled{true};
597 f32 sixaxis_fusion_parameter1{};
598 f32 sixaxis_fusion_parameter2{};
599 bool sixaxis_at_rest{true};
600 std::array<ControllerPad, 10> npad_pad_states{};
601 std::array<TriggerState, 10> npad_trigger_states{};
602 bool is_in_lr_assignment_mode{false}; 517 bool is_in_lr_assignment_mode{false};
603}; 518};
604} // namespace Service::HID 519} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 772c20453..b7d7a5756 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -5,11 +5,12 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/stubbed.h" 9#include "core/hle/service/hid/controllers/stubbed.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11 12
12Controller_Stubbed::Controller_Stubbed(Core::System& system_) : ControllerBase{system_} {} 13Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
13Controller_Stubbed::~Controller_Stubbed() = default; 14Controller_Stubbed::~Controller_Stubbed() = default;
14 15
15void Controller_Stubbed::OnInit() {} 16void Controller_Stubbed::OnInit() {}
@@ -31,10 +32,9 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
31 std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); 32 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
32} 33}
33 34
34void Controller_Stubbed::OnLoadInputDevices() {}
35
36void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { 35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
37 common_offset = off; 36 common_offset = off;
38 smart_update = true; 37 smart_update = true;
39} 38}
39
40} // namespace Service::HID 40} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 21092af0d..0044a4efa 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
12public: 12public:
13 explicit Controller_Stubbed(Core::System& system_); 13 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_);
14 ~Controller_Stubbed() override; 14 ~Controller_Stubbed() override;
15 15
16 // Called when the controller is initialized 16 // Called when the controller is initialized
@@ -22,12 +22,17 @@ public:
22 // When the controller is requesting an update for the shared memory 22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
24 24
25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override;
27
28 void SetCommonHeaderOffset(std::size_t off); 25 void SetCommonHeaderOffset(std::size_t off);
29 26
30private: 27private:
28 struct CommonHeader {
29 s64 timestamp;
30 s64 total_entry_count;
31 s64 last_entry_index;
32 s64 entry_count;
33 };
34 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
35
31 bool smart_update{}; 36 bool smart_update{};
32 std::size_t common_offset{}; 37 std::size_t common_offset{};
33}; 38};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 6ef17acc5..48978e5c6 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -7,72 +7,82 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/hid/emulated_console.h"
14#include "core/hid/hid_core.h"
13#include "core/hle/service/hid/controllers/touchscreen.h" 15#include "core/hle/service/hid/controllers/touchscreen.h"
14 16
15namespace Service::HID { 17namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 18constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 19
18Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {} 20Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_)
21 : ControllerBase{hid_core_} {
22 console = hid_core.GetEmulatedConsole();
23}
24
19Controller_Touchscreen::~Controller_Touchscreen() = default; 25Controller_Touchscreen::~Controller_Touchscreen() = default;
20 26
21void Controller_Touchscreen::OnInit() { 27void Controller_Touchscreen::OnInit() {}
22 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
23 mouse_finger_id[id] = MAX_FINGERS;
24 keyboard_finger_id[id] = MAX_FINGERS;
25 udp_finger_id[id] = MAX_FINGERS;
26 }
27}
28 28
29void Controller_Touchscreen::OnRelease() {} 29void Controller_Touchscreen::OnRelease() {}
30 30
31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
32 std::size_t size) { 32 std::size_t size) {
33 shared_memory.header.timestamp = core_timing.GetCPUTicks(); 33 touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
34 shared_memory.header.total_entry_count = 17;
35 34
36 if (!IsControllerActivated()) { 35 if (!IsControllerActivated()) {
37 shared_memory.header.entry_count = 0; 36 touch_screen_lifo.buffer_count = 0;
38 shared_memory.header.last_entry_index = 0; 37 touch_screen_lifo.buffer_tail = 0;
38 std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
39 return; 39 return;
40 } 40 }
41 shared_memory.header.entry_count = 16;
42 41
43 const auto& last_entry = 42 const auto touch_status = console->GetTouch();
44 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 43 for (std::size_t id = 0; id < MAX_FINGERS; id++) {
45 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 44 const auto& current_touch = touch_status[id];
46 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 45 auto& finger = fingers[id];
46 finger.position = current_touch.position;
47 finger.id = current_touch.id;
47 48
48 cur_entry.sampling_number = last_entry.sampling_number + 1; 49 if (finger.attribute.start_touch) {
49 cur_entry.sampling_number2 = cur_entry.sampling_number; 50 finger.attribute.raw = 0;
51 continue;
52 }
50 53
51 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 54 if (finger.attribute.end_touch) {
52 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 55 finger.attribute.raw = 0;
53 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 56 finger.pressed = false;
54 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); 57 continue;
55 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); 58 }
56 } 59
60 if (!finger.pressed && current_touch.pressed) {
61 finger.attribute.start_touch.Assign(1);
62 finger.pressed = true;
63 continue;
64 }
57 65
58 if (Settings::values.use_touch_from_button) { 66 if (finger.pressed && !current_touch.pressed) {
59 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); 67 finger.attribute.raw = 0;
60 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 68 finger.attribute.end_touch.Assign(1);
61 keyboard_finger_id[id] =
62 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
63 } 69 }
64 } 70 }
65 71
66 std::array<Finger, 16> active_fingers; 72 std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
67 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 73 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
68 [](const auto& finger) { return finger.pressed; }); 74 [](const auto& finger) { return finger.pressed; });
69 const auto active_fingers_count = 75 const auto active_fingers_count =
70 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 76 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
71 77
72 const u64 tick = core_timing.GetCPUTicks(); 78 const u64 tick = core_timing.GetCPUTicks();
73 cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); 79 const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
80
81 next_state.sampling_number = last_entry.sampling_number + 1;
82 next_state.entry_count = static_cast<s32>(active_fingers_count);
83
74 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 84 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
75 auto& touch_entry = cur_entry.states[id]; 85 auto& touch_entry = next_state.states[id];
76 if (id < active_fingers_count) { 86 if (id < active_fingers_count) {
77 const auto& [active_x, active_y] = active_fingers[id].position; 87 const auto& [active_x, active_y] = active_fingers[id].position;
78 touch_entry.position = { 88 touch_entry.position = {
@@ -97,66 +107,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
97 touch_entry.finger = 0; 107 touch_entry.finger = 0;
98 } 108 }
99 } 109 }
100 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
101}
102
103void Controller_Touchscreen::OnLoadInputDevices() {
104 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
105 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
106 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
107}
108
109std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
110 // Dont assign any touch input to a finger if disabled
111 if (!Settings::values.touchscreen.enabled) {
112 return std::nullopt;
113 }
114 std::size_t first_free_id = 0;
115 while (first_free_id < MAX_FINGERS) {
116 if (!fingers[first_free_id].pressed) {
117 return first_free_id;
118 } else {
119 first_free_id++;
120 }
121 }
122 return std::nullopt;
123}
124
125std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
126 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
127 const auto& [x, y, pressed] = touch_input;
128 if (finger_id > MAX_FINGERS) {
129 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
130 return MAX_FINGERS;
131 }
132 if (pressed) {
133 Attributes attribute{};
134 if (finger_id == MAX_FINGERS) {
135 const auto first_free_id = GetUnusedFingerID();
136 if (!first_free_id) {
137 // Invalid finger id do nothing
138 return MAX_FINGERS;
139 }
140 finger_id = first_free_id.value();
141 fingers[finger_id].pressed = true;
142 fingers[finger_id].id = static_cast<u32_le>(finger_id);
143 attribute.start_touch.Assign(1);
144 }
145 fingers[finger_id].position = {x, y};
146 fingers[finger_id].attribute = attribute;
147 return finger_id;
148 }
149
150 if (finger_id != MAX_FINGERS) {
151 if (!fingers[finger_id].attribute.end_touch) {
152 fingers[finger_id].attribute.end_touch.Assign(1);
153 fingers[finger_id].attribute.start_touch.Assign(0);
154 return finger_id;
155 }
156 fingers[finger_id].pressed = false;
157 }
158 110
159 return MAX_FINGERS; 111 touch_screen_lifo.WriteNextEntry(next_state);
112 std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
160} 113}
161 114
162} // namespace Service::HID 115} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 8e9b40c0a..708dde4f0 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -9,18 +9,25 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/ring_lifo.h"
15
16namespace Core::HID {
17class EmulatedConsole;
18} // namespace Core::HID
14 19
15namespace Service::HID { 20namespace Service::HID {
16class Controller_Touchscreen final : public ControllerBase { 21class Controller_Touchscreen final : public ControllerBase {
17public: 22public:
23 // This is nn::hid::TouchScreenModeForNx
18 enum class TouchScreenModeForNx : u8 { 24 enum class TouchScreenModeForNx : u8 {
19 UseSystemSetting, 25 UseSystemSetting,
20 Finger, 26 Finger,
21 Heat2, 27 Heat2,
22 }; 28 };
23 29
30 // This is nn::hid::TouchScreenConfigurationForNx
24 struct TouchScreenConfigurationForNx { 31 struct TouchScreenConfigurationForNx {
25 TouchScreenModeForNx mode; 32 TouchScreenModeForNx mode;
26 INSERT_PADDING_BYTES_NOINIT(0x7); 33 INSERT_PADDING_BYTES_NOINIT(0x7);
@@ -29,7 +36,7 @@ public:
29 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, 36 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
30 "TouchScreenConfigurationForNx is an invalid size"); 37 "TouchScreenConfigurationForNx is an invalid size");
31 38
32 explicit Controller_Touchscreen(Core::System& system_); 39 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_);
33 ~Controller_Touchscreen() override; 40 ~Controller_Touchscreen() override;
34 41
35 // Called when the controller is initialized 42 // Called when the controller is initialized
@@ -41,73 +48,24 @@ public:
41 // When the controller is requesting an update for the shared memory 48 // When the controller is requesting an update for the shared memory
42 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 49 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
43 50
44 // Called when input devices should be loaded
45 void OnLoadInputDevices() override;
46
47private: 51private:
48 static constexpr std::size_t MAX_FINGERS = 16; 52 static constexpr std::size_t MAX_FINGERS = 16;
49 53
50 // Returns an unused finger id, if there is no fingers available std::nullopt will be returned 54 // This is nn::hid::TouchScreenState
51 std::optional<std::size_t> GetUnusedFingerID() const; 55 struct TouchScreenState {
52 56 s64 sampling_number;
53 // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no 57 s32 entry_count;
54 // changes will be made. Updates the coordinates if the finger id it's already set. If the touch 58 INSERT_PADDING_BYTES(4); // Reserved
55 // ends delays the output by one frame to set the end_touch flag before finally freeing the 59 std::array<Core::HID::TouchState, MAX_FINGERS> states;
56 // finger id
57 std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
58 std::size_t finger_id);
59
60 struct Attributes {
61 union {
62 u32 raw{};
63 BitField<0, 1, u32> start_touch;
64 BitField<1, 1, u32> end_touch;
65 };
66 };
67 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
68
69 struct TouchState {
70 u64_le delta_time;
71 Attributes attribute;
72 u32_le finger;
73 Common::Point<u32_le> position;
74 u32_le diameter_x;
75 u32_le diameter_y;
76 u32_le rotation_angle;
77 }; 60 };
78 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 61 static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
79 62
80 struct TouchScreenEntry { 63 // This is nn::hid::detail::TouchScreenLifo
81 s64_le sampling_number; 64 Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
82 s64_le sampling_number2; 65 static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
83 s32_le entry_count; 66 TouchScreenState next_state{};
84 std::array<TouchState, MAX_FINGERS> states;
85 };
86 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
87
88 struct TouchScreenSharedMemory {
89 CommonHeader header;
90 std::array<TouchScreenEntry, 17> shared_memory_entries{};
91 INSERT_PADDING_BYTES(0x3c8);
92 };
93 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
94 "TouchScreenSharedMemory is an invalid size");
95
96 struct Finger {
97 u64_le last_touch{};
98 Common::Point<float> position;
99 u32_le id{};
100 bool pressed{};
101 Attributes attribute;
102 };
103 67
104 TouchScreenSharedMemory shared_memory{}; 68 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
105 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 69 Core::HID::EmulatedConsole* console;
106 std::unique_ptr<Input::TouchDevice> touch_udp_device;
107 std::unique_ptr<Input::TouchDevice> touch_btn_device;
108 std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
109 std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
110 std::array<std::size_t, MAX_FINGERS> udp_finger_id;
111 std::array<Finger, MAX_FINGERS> fingers;
112}; 70};
113} // namespace Service::HID 71} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 41dc22cf9..e4da16466 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -5,12 +5,13 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/xpad.h" 9#include "core/hle/service/hid/controllers/xpad.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 13
13Controller_XPad::Controller_XPad(Core::System& system_) : ControllerBase{system_} {} 14Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
14Controller_XPad::~Controller_XPad() = default; 15Controller_XPad::~Controller_XPad() = default;
15 16
16void Controller_XPad::OnInit() {} 17void Controller_XPad::OnInit() {}
@@ -19,28 +20,19 @@ void Controller_XPad::OnRelease() {}
19 20
20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 21void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 22 std::size_t size) {
22 for (auto& xpad_entry : shared_memory.shared_memory_entries) { 23 if (!IsControllerActivated()) {
23 xpad_entry.header.timestamp = core_timing.GetCPUTicks(); 24 basic_xpad_lifo.buffer_count = 0;
24 xpad_entry.header.total_entry_count = 17; 25 basic_xpad_lifo.buffer_tail = 0;
25 26 std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
26 if (!IsControllerActivated()) { 27 return;
27 xpad_entry.header.entry_count = 0;
28 xpad_entry.header.last_entry_index = 0;
29 return;
30 }
31 xpad_entry.header.entry_count = 16;
32
33 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
34 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
35 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
36
37 cur_entry.sampling_number = last_entry.sampling_number + 1;
38 cur_entry.sampling_number2 = cur_entry.sampling_number;
39 } 28 }
29
30 const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
31 next_state.sampling_number = last_entry.sampling_number + 1;
40 // TODO(ogniK): Update xpad states 32 // TODO(ogniK): Update xpad states
41 33
42 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 34 basic_xpad_lifo.WriteNextEntry(next_state);
35 std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
43} 36}
44 37
45void Controller_XPad::OnLoadInputDevices() {}
46} // namespace Service::HID 38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index f9ab5facf..ba8db8d9d 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -8,12 +8,14 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hid/hid_types.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14class Controller_XPad final : public ControllerBase { 16class Controller_XPad final : public ControllerBase {
15public: 17public:
16 explicit Controller_XPad(Core::System& system_); 18 explicit Controller_XPad(Core::HID::HIDCore& hid_core_);
17 ~Controller_XPad() override; 19 ~Controller_XPad() override;
18 20
19 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -25,13 +27,11 @@ public:
25 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
27 29
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private: 30private:
32 struct Attributes { 31 // This is nn::hid::BasicXpadAttributeSet
32 struct BasicXpadAttributeSet {
33 union { 33 union {
34 u32_le raw{}; 34 u32 raw{};
35 BitField<0, 1, u32> is_connected; 35 BitField<0, 1, u32> is_connected;
36 BitField<1, 1, u32> is_wired; 36 BitField<1, 1, u32> is_wired;
37 BitField<2, 1, u32> is_left_connected; 37 BitField<2, 1, u32> is_left_connected;
@@ -40,11 +40,12 @@ private:
40 BitField<5, 1, u32> is_right_wired; 40 BitField<5, 1, u32> is_right_wired;
41 }; 41 };
42 }; 42 };
43 static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size"); 43 static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
44 44
45 struct Buttons { 45 // This is nn::hid::BasicXpadButtonSet
46 struct BasicXpadButtonSet {
46 union { 47 union {
47 u32_le raw{}; 48 u32 raw{};
48 // Button states 49 // Button states
49 BitField<0, 1, u32> a; 50 BitField<0, 1, u32> a;
50 BitField<1, 1, u32> b; 51 BitField<1, 1, u32> b;
@@ -88,35 +89,21 @@ private:
88 BitField<30, 1, u32> handheld_left_b; 89 BitField<30, 1, u32> handheld_left_b;
89 }; 90 };
90 }; 91 };
91 static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size"); 92 static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
92 93
93 struct AnalogStick { 94 // This is nn::hid::detail::BasicXpadState
94 s32_le x; 95 struct BasicXpadState {
95 s32_le y; 96 s64 sampling_number;
96 }; 97 BasicXpadAttributeSet attributes;
97 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size"); 98 BasicXpadButtonSet pad_states;
98 99 Core::HID::AnalogStickState l_stick;
99 struct XPadState { 100 Core::HID::AnalogStickState r_stick;
100 s64_le sampling_number;
101 s64_le sampling_number2;
102 Attributes attributes;
103 Buttons pad_states;
104 AnalogStick l_stick;
105 AnalogStick r_stick;
106 }; 101 };
107 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size"); 102 static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
108 103
109 struct XPadEntry { 104 // This is nn::hid::detail::BasicXpadLifo
110 CommonHeader header; 105 Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
111 std::array<XPadState, 17> pad_states{}; 106 static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
112 INSERT_PADDING_BYTES(0x138); 107 BasicXpadState next_state{};
113 };
114 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
115
116 struct SharedMemory {
117 std::array<XPadEntry, 4> shared_memory_entries{};
118 };
119 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
120 SharedMemory shared_memory{};
121}; 108};
122} // namespace Service::HID 109} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 10c64d41a..b36689552 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -8,7 +8,7 @@
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/frontend/input.h" 11#include "core/hid/hid_core.h"
12#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h" 14#include "core/hle/kernel/k_shared_memory.h"
@@ -34,10 +34,10 @@
34namespace Service::HID { 34namespace Service::HID {
35 35
36// Updating period for each HID device. 36// Updating period for each HID device.
37// HID is polled every 15ms, this value was derived from 37// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
38// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet 38constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
39constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) 39constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
40constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) 40constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42 42
43IAppletResource::IAppletResource(Core::System& system_, 43IAppletResource::IAppletResource(Core::System& system_,
@@ -79,17 +79,24 @@ IAppletResource::IAppletResource(Core::System& system_,
79 const auto guard = LockService(); 79 const auto guard = LockService();
80 UpdateControllers(user_data, ns_late); 80 UpdateControllers(user_data, ns_late);
81 }); 81 });
82 mouse_keyboard_update_event = Core::Timing::CreateEvent(
83 "HID::UpdateMouseKeyboardCallback",
84 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
85 const auto guard = LockService();
86 UpdateMouseKeyboard(user_data, ns_late);
87 });
82 motion_update_event = Core::Timing::CreateEvent( 88 motion_update_event = Core::Timing::CreateEvent(
83 "HID::MotionPadCallback", 89 "HID::UpdateMotionCallback",
84 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 90 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
85 const auto guard = LockService(); 91 const auto guard = LockService();
86 UpdateMotion(user_data, ns_late); 92 UpdateMotion(user_data, ns_late);
87 }); 93 });
88 94
89 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); 95 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
96 system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
90 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); 97 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
91 98
92 ReloadInputDevices(); 99 system.HIDCore().ReloadInputDevices();
93} 100}
94 101
95void IAppletResource::ActivateController(HidController controller) { 102void IAppletResource::ActivateController(HidController controller) {
@@ -102,6 +109,7 @@ void IAppletResource::DeactivateController(HidController controller) {
102 109
103IAppletResource::~IAppletResource() { 110IAppletResource::~IAppletResource() {
104 system.CoreTiming().UnscheduleEvent(pad_update_event, 0); 111 system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
112 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
105 system.CoreTiming().UnscheduleEvent(motion_update_event, 0); 113 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
106} 114}
107 115
@@ -117,23 +125,44 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
117 std::chrono::nanoseconds ns_late) { 125 std::chrono::nanoseconds ns_late) {
118 auto& core_timing = system.CoreTiming(); 126 auto& core_timing = system.CoreTiming();
119 127
120 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
121 for (const auto& controller : controllers) { 128 for (const auto& controller : controllers) {
122 if (should_reload) { 129 // Keyboard has it's own update event
123 controller->OnLoadInputDevices(); 130 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
131 continue;
132 }
133 // Mouse has it's own update event
134 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
135 continue;
124 } 136 }
125 controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), 137 controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
126 SHARED_MEMORY_SIZE); 138 SHARED_MEMORY_SIZE);
127 } 139 }
128 140
129 // If ns_late is higher than the update rate ignore the delay 141 // If ns_late is higher than the update rate ignore the delay
130 if (ns_late > motion_update_ns) { 142 if (ns_late > pad_update_ns) {
131 ns_late = {}; 143 ns_late = {};
132 } 144 }
133 145
134 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); 146 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
135} 147}
136 148
149void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
150 std::chrono::nanoseconds ns_late) {
151 auto& core_timing = system.CoreTiming();
152
153 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(
154 core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
155 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(
156 core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
157
158 // If ns_late is higher than the update rate ignore the delay
159 if (ns_late > mouse_keyboard_update_ns) {
160 ns_late = {};
161 }
162
163 core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
164}
165
137void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 166void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
138 auto& core_timing = system.CoreTiming(); 167 auto& core_timing = system.CoreTiming();
139 168
@@ -166,7 +195,7 @@ public:
166private: 195private:
167 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { 196 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
168 IPC::RequestParser rp{ctx}; 197 IPC::RequestParser rp{ctx};
169 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 198 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
170 199
171 if (applet_resource != nullptr) { 200 if (applet_resource != nullptr) {
172 applet_resource->GetController<Controller_NPad>(HidController::NPad) 201 applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -422,6 +451,7 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
422 INSERT_PADDING_WORDS_NOINIT(1); 451 INSERT_PADDING_WORDS_NOINIT(1);
423 u64 applet_resource_user_id; 452 u64 applet_resource_user_id;
424 }; 453 };
454 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
425 455
426 const auto parameters{rp.PopRaw<Parameters>()}; 456 const auto parameters{rp.PopRaw<Parameters>()};
427 457
@@ -448,19 +478,18 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
448void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 478void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
449 IPC::RequestParser rp{ctx}; 479 IPC::RequestParser rp{ctx};
450 struct Parameters { 480 struct Parameters {
451 Controller_NPad::DeviceHandle sixaxis_handle; 481 u32 basic_xpad_id;
452 INSERT_PADDING_WORDS_NOINIT(1); 482 INSERT_PADDING_WORDS_NOINIT(1);
453 u64 applet_resource_user_id; 483 u64 applet_resource_user_id;
454 }; 484 };
485 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
455 486
456 const auto parameters{rp.PopRaw<Parameters>()}; 487 const auto parameters{rp.PopRaw<Parameters>()};
457 488
458 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); 489 // This function does nothing on 10.0.0+
459 490
460 LOG_DEBUG(Service_HID, 491 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
461 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 492 parameters.basic_xpad_id, parameters.applet_resource_user_id);
462 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
463 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
464 493
465 IPC::ResponseBuilder rb{ctx, 2}; 494 IPC::ResponseBuilder rb{ctx, 2};
466 rb.Push(ResultSuccess); 495 rb.Push(ResultSuccess);
@@ -469,19 +498,18 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
469void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 498void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
470 IPC::RequestParser rp{ctx}; 499 IPC::RequestParser rp{ctx};
471 struct Parameters { 500 struct Parameters {
472 Controller_NPad::DeviceHandle sixaxis_handle; 501 u32 basic_xpad_id;
473 INSERT_PADDING_WORDS_NOINIT(1); 502 INSERT_PADDING_WORDS_NOINIT(1);
474 u64 applet_resource_user_id; 503 u64 applet_resource_user_id;
475 }; 504 };
505 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
476 506
477 const auto parameters{rp.PopRaw<Parameters>()}; 507 const auto parameters{rp.PopRaw<Parameters>()};
478 508
479 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); 509 // This function does nothing on 10.0.0+
480 510
481 LOG_DEBUG(Service_HID, 511 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
482 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 512 parameters.basic_xpad_id, parameters.applet_resource_user_id);
483 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
484 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
485 513
486 IPC::ResponseBuilder rb{ctx, 2}; 514 IPC::ResponseBuilder rb{ctx, 2};
487 rb.Push(ResultSuccess); 515 rb.Push(ResultSuccess);
@@ -490,14 +518,16 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
490void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 518void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
491 IPC::RequestParser rp{ctx}; 519 IPC::RequestParser rp{ctx};
492 struct Parameters { 520 struct Parameters {
493 Controller_NPad::DeviceHandle sixaxis_handle; 521 Core::HID::SixAxisSensorHandle sixaxis_handle;
494 INSERT_PADDING_WORDS_NOINIT(1); 522 INSERT_PADDING_WORDS_NOINIT(1);
495 u64 applet_resource_user_id; 523 u64 applet_resource_user_id;
496 }; 524 };
525 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
497 526
498 const auto parameters{rp.PopRaw<Parameters>()}; 527 const auto parameters{rp.PopRaw<Parameters>()};
499 528
500 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); 529 applet_resource->GetController<Controller_NPad>(HidController::NPad)
530 .SetSixAxisEnabled(parameters.sixaxis_handle, true);
501 531
502 LOG_DEBUG(Service_HID, 532 LOG_DEBUG(Service_HID,
503 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 533 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -511,14 +541,16 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
511void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 541void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
512 IPC::RequestParser rp{ctx}; 542 IPC::RequestParser rp{ctx};
513 struct Parameters { 543 struct Parameters {
514 Controller_NPad::DeviceHandle sixaxis_handle; 544 Core::HID::SixAxisSensorHandle sixaxis_handle;
515 INSERT_PADDING_WORDS_NOINIT(1); 545 INSERT_PADDING_WORDS_NOINIT(1);
516 u64 applet_resource_user_id; 546 u64 applet_resource_user_id;
517 }; 547 };
548 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
518 549
519 const auto parameters{rp.PopRaw<Parameters>()}; 550 const auto parameters{rp.PopRaw<Parameters>()};
520 551
521 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); 552 applet_resource->GetController<Controller_NPad>(HidController::NPad)
553 .SetSixAxisEnabled(parameters.sixaxis_handle, false);
522 554
523 LOG_DEBUG(Service_HID, 555 LOG_DEBUG(Service_HID,
524 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 556 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -534,19 +566,23 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
534 struct Parameters { 566 struct Parameters {
535 bool enable_sixaxis_sensor_fusion; 567 bool enable_sixaxis_sensor_fusion;
536 INSERT_PADDING_BYTES_NOINIT(3); 568 INSERT_PADDING_BYTES_NOINIT(3);
537 Controller_NPad::DeviceHandle sixaxis_handle; 569 Core::HID::SixAxisSensorHandle sixaxis_handle;
538 u64 applet_resource_user_id; 570 u64 applet_resource_user_id;
539 }; 571 };
540 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 572 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
541 573
542 const auto parameters{rp.PopRaw<Parameters>()}; 574 const auto parameters{rp.PopRaw<Parameters>()};
543 575
544 LOG_WARNING(Service_HID, 576 applet_resource->GetController<Controller_NPad>(HidController::NPad)
545 "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 577 .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
546 "device_index={}, applet_resource_user_id={}", 578 parameters.enable_sixaxis_sensor_fusion);
547 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, 579
548 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, 580 LOG_DEBUG(Service_HID,
549 parameters.applet_resource_user_id); 581 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
582 "device_index={}, applet_resource_user_id={}",
583 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
584 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
585 parameters.applet_resource_user_id);
550 586
551 IPC::ResponseBuilder rb{ctx, 2}; 587 IPC::ResponseBuilder rb{ctx, 2};
552 rb.Push(ResultSuccess); 588 rb.Push(ResultSuccess);
@@ -555,9 +591,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
555void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 591void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
556 IPC::RequestParser rp{ctx}; 592 IPC::RequestParser rp{ctx};
557 struct Parameters { 593 struct Parameters {
558 Controller_NPad::DeviceHandle sixaxis_handle; 594 Core::HID::SixAxisSensorHandle sixaxis_handle;
559 f32 parameter1; 595 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
560 f32 parameter2; 596 INSERT_PADDING_WORDS_NOINIT(1);
561 u64 applet_resource_user_id; 597 u64 applet_resource_user_id;
562 }; 598 };
563 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 599 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -565,14 +601,14 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
565 const auto parameters{rp.PopRaw<Parameters>()}; 601 const auto parameters{rp.PopRaw<Parameters>()};
566 602
567 applet_resource->GetController<Controller_NPad>(HidController::NPad) 603 applet_resource->GetController<Controller_NPad>(HidController::NPad)
568 .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2); 604 .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
569 605
570 LOG_WARNING(Service_HID, 606 LOG_DEBUG(Service_HID,
571 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 607 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
572 "parameter2={}, applet_resource_user_id={}", 608 "parameter2={}, applet_resource_user_id={}",
573 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 609 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
574 parameters.sixaxis_handle.device_index, parameters.parameter1, 610 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
575 parameters.parameter2, parameters.applet_resource_user_id); 611 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
576 612
577 IPC::ResponseBuilder rb{ctx, 2}; 613 IPC::ResponseBuilder rb{ctx, 2};
578 rb.Push(ResultSuccess); 614 rb.Push(ResultSuccess);
@@ -581,35 +617,33 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
581void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 617void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
582 IPC::RequestParser rp{ctx}; 618 IPC::RequestParser rp{ctx};
583 struct Parameters { 619 struct Parameters {
584 Controller_NPad::DeviceHandle sixaxis_handle; 620 Core::HID::SixAxisSensorHandle sixaxis_handle;
621 INSERT_PADDING_WORDS_NOINIT(1);
585 u64 applet_resource_user_id; 622 u64 applet_resource_user_id;
586 }; 623 };
587 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 624 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
588 625
589 f32 parameter1 = 0;
590 f32 parameter2 = 0;
591 const auto parameters{rp.PopRaw<Parameters>()}; 626 const auto parameters{rp.PopRaw<Parameters>()};
592 627
593 std::tie(parameter1, parameter2) = 628 const auto sixaxis_fusion_parameters =
594 applet_resource->GetController<Controller_NPad>(HidController::NPad) 629 applet_resource->GetController<Controller_NPad>(HidController::NPad)
595 .GetSixAxisFusionParameters(); 630 .GetSixAxisFusionParameters(parameters.sixaxis_handle);
596 631
597 LOG_WARNING( 632 LOG_DEBUG(Service_HID,
598 Service_HID, 633 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
599 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 634 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
600 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 635 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
601 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
602 636
603 IPC::ResponseBuilder rb{ctx, 4}; 637 IPC::ResponseBuilder rb{ctx, 4};
604 rb.Push(ResultSuccess); 638 rb.Push(ResultSuccess);
605 rb.Push(parameter1); 639 rb.PushRaw(sixaxis_fusion_parameters);
606 rb.Push(parameter2);
607} 640}
608 641
609void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 642void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
610 IPC::RequestParser rp{ctx}; 643 IPC::RequestParser rp{ctx};
611 struct Parameters { 644 struct Parameters {
612 Controller_NPad::DeviceHandle sixaxis_handle; 645 Core::HID::SixAxisSensorHandle sixaxis_handle;
646 INSERT_PADDING_WORDS_NOINIT(1);
613 u64 applet_resource_user_id; 647 u64 applet_resource_user_id;
614 }; 648 };
615 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 649 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -617,13 +651,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
617 const auto parameters{rp.PopRaw<Parameters>()}; 651 const auto parameters{rp.PopRaw<Parameters>()};
618 652
619 applet_resource->GetController<Controller_NPad>(HidController::NPad) 653 applet_resource->GetController<Controller_NPad>(HidController::NPad)
620 .ResetSixAxisFusionParameters(); 654 .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
621 655
622 LOG_WARNING( 656 LOG_DEBUG(Service_HID,
623 Service_HID, 657 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
624 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 658 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
625 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 659 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
626 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
627 660
628 IPC::ResponseBuilder rb{ctx, 2}; 661 IPC::ResponseBuilder rb{ctx, 2};
629 rb.Push(ResultSuccess); 662 rb.Push(ResultSuccess);
@@ -631,12 +664,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
631 664
632void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 665void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
633 IPC::RequestParser rp{ctx}; 666 IPC::RequestParser rp{ctx};
634 const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 667 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
635 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; 668 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
636 const auto applet_resource_user_id{rp.Pop<u64>()}; 669 const auto applet_resource_user_id{rp.Pop<u64>()};
637 670
638 applet_resource->GetController<Controller_NPad>(HidController::NPad) 671 applet_resource->GetController<Controller_NPad>(HidController::NPad)
639 .SetGyroscopeZeroDriftMode(drift_mode); 672 .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
640 673
641 LOG_DEBUG(Service_HID, 674 LOG_DEBUG(Service_HID,
642 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 675 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -651,10 +684,11 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
651void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 684void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
652 IPC::RequestParser rp{ctx}; 685 IPC::RequestParser rp{ctx};
653 struct Parameters { 686 struct Parameters {
654 Controller_NPad::DeviceHandle sixaxis_handle; 687 Core::HID::SixAxisSensorHandle sixaxis_handle;
655 INSERT_PADDING_WORDS_NOINIT(1); 688 INSERT_PADDING_WORDS_NOINIT(1);
656 u64 applet_resource_user_id; 689 u64 applet_resource_user_id;
657 }; 690 };
691 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
658 692
659 const auto parameters{rp.PopRaw<Parameters>()}; 693 const auto parameters{rp.PopRaw<Parameters>()};
660 694
@@ -666,21 +700,23 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
666 IPC::ResponseBuilder rb{ctx, 3}; 700 IPC::ResponseBuilder rb{ctx, 3};
667 rb.Push(ResultSuccess); 701 rb.Push(ResultSuccess);
668 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) 702 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
669 .GetGyroscopeZeroDriftMode()); 703 .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
670} 704}
671 705
672void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 706void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
673 IPC::RequestParser rp{ctx}; 707 IPC::RequestParser rp{ctx};
674 struct Parameters { 708 struct Parameters {
675 Controller_NPad::DeviceHandle sixaxis_handle; 709 Core::HID::SixAxisSensorHandle sixaxis_handle;
676 INSERT_PADDING_WORDS_NOINIT(1); 710 INSERT_PADDING_WORDS_NOINIT(1);
677 u64 applet_resource_user_id; 711 u64 applet_resource_user_id;
678 }; 712 };
713 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
679 714
680 const auto parameters{rp.PopRaw<Parameters>()}; 715 const auto parameters{rp.PopRaw<Parameters>()};
716 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
681 717
682 applet_resource->GetController<Controller_NPad>(HidController::NPad) 718 applet_resource->GetController<Controller_NPad>(HidController::NPad)
683 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); 719 .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
684 720
685 LOG_DEBUG(Service_HID, 721 LOG_DEBUG(Service_HID,
686 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 722 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -694,10 +730,11 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
694void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 730void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
695 IPC::RequestParser rp{ctx}; 731 IPC::RequestParser rp{ctx};
696 struct Parameters { 732 struct Parameters {
697 Controller_NPad::DeviceHandle sixaxis_handle; 733 Core::HID::SixAxisSensorHandle sixaxis_handle;
698 INSERT_PADDING_WORDS_NOINIT(1); 734 INSERT_PADDING_WORDS_NOINIT(1);
699 u64 applet_resource_user_id; 735 u64 applet_resource_user_id;
700 }; 736 };
737 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
701 738
702 const auto parameters{rp.PopRaw<Parameters>()}; 739 const auto parameters{rp.PopRaw<Parameters>()};
703 740
@@ -709,16 +746,17 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
709 IPC::ResponseBuilder rb{ctx, 3}; 746 IPC::ResponseBuilder rb{ctx, 3};
710 rb.Push(ResultSuccess); 747 rb.Push(ResultSuccess);
711 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 748 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
712 .IsSixAxisSensorAtRest()); 749 .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
713} 750}
714 751
715void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { 752void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
716 IPC::RequestParser rp{ctx}; 753 IPC::RequestParser rp{ctx};
717 struct Parameters { 754 struct Parameters {
718 Controller_NPad::DeviceHandle sixaxis_handle; 755 Core::HID::SixAxisSensorHandle sixaxis_handle;
719 INSERT_PADDING_WORDS_NOINIT(1); 756 INSERT_PADDING_WORDS_NOINIT(1);
720 u64 applet_resource_user_id; 757 u64 applet_resource_user_id;
721 }; 758 };
759 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
722 760
723 const auto parameters{rp.PopRaw<Parameters>()}; 761 const auto parameters{rp.PopRaw<Parameters>()};
724 762
@@ -740,13 +778,14 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
740 INSERT_PADDING_WORDS_NOINIT(1); 778 INSERT_PADDING_WORDS_NOINIT(1);
741 u64 applet_resource_user_id; 779 u64 applet_resource_user_id;
742 }; 780 };
781 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
743 782
744 const auto parameters{rp.PopRaw<Parameters>()}; 783 const auto parameters{rp.PopRaw<Parameters>()};
745 784
746 applet_resource->ActivateController(HidController::Gesture); 785 applet_resource->ActivateController(HidController::Gesture);
747 786
748 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, 787 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
749 parameters.applet_resource_user_id); 788 parameters.unknown, parameters.applet_resource_user_id);
750 789
751 IPC::ResponseBuilder rb{ctx, 2}; 790 IPC::ResponseBuilder rb{ctx, 2};
752 rb.Push(ResultSuccess); 791 rb.Push(ResultSuccess);
@@ -754,12 +793,20 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
754 793
755void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 794void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
756 IPC::RequestParser rp{ctx}; 795 IPC::RequestParser rp{ctx};
757 const auto supported_styleset{rp.Pop<u32>()}; 796 struct Parameters {
797 Core::HID::NpadStyleSet supported_styleset;
798 INSERT_PADDING_WORDS_NOINIT(1);
799 u64 applet_resource_user_id;
800 };
801 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
802
803 const auto parameters{rp.PopRaw<Parameters>()};
758 804
759 applet_resource->GetController<Controller_NPad>(HidController::NPad) 805 applet_resource->GetController<Controller_NPad>(HidController::NPad)
760 .SetSupportedStyleSet({supported_styleset}); 806 .SetSupportedStyleSet({parameters.supported_styleset});
761 807
762 LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); 808 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
809 parameters.supported_styleset, parameters.applet_resource_user_id);
763 810
764 IPC::ResponseBuilder rb{ctx, 2}; 811 IPC::ResponseBuilder rb{ctx, 2};
765 rb.Push(ResultSuccess); 812 rb.Push(ResultSuccess);
@@ -773,9 +820,9 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
773 820
774 IPC::ResponseBuilder rb{ctx, 3}; 821 IPC::ResponseBuilder rb{ctx, 3};
775 rb.Push(ResultSuccess); 822 rb.Push(ResultSuccess);
776 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 823 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
777 .GetSupportedStyleSet() 824 .GetSupportedStyleSet()
778 .raw); 825 .raw);
779} 826}
780 827
781void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 828void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
@@ -818,11 +865,12 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
818void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 865void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
819 IPC::RequestParser rp{ctx}; 866 IPC::RequestParser rp{ctx};
820 struct Parameters { 867 struct Parameters {
821 u32 npad_id; 868 Core::HID::NpadIdType npad_id;
822 INSERT_PADDING_WORDS_NOINIT(1); 869 INSERT_PADDING_WORDS_NOINIT(1);
823 u64 applet_resource_user_id; 870 u64 applet_resource_user_id;
824 u64 unknown; 871 u64 unknown;
825 }; 872 };
873 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
826 874
827 const auto parameters{rp.PopRaw<Parameters>()}; 875 const auto parameters{rp.PopRaw<Parameters>()};
828 876
@@ -838,10 +886,11 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
838void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { 886void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
839 IPC::RequestParser rp{ctx}; 887 IPC::RequestParser rp{ctx};
840 struct Parameters { 888 struct Parameters {
841 u32 npad_id; 889 Core::HID::NpadIdType npad_id;
842 INSERT_PADDING_WORDS_NOINIT(1); 890 INSERT_PADDING_WORDS_NOINIT(1);
843 u64 applet_resource_user_id; 891 u64 applet_resource_user_id;
844 }; 892 };
893 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
845 894
846 const auto parameters{rp.PopRaw<Parameters>()}; 895 const auto parameters{rp.PopRaw<Parameters>()};
847 896
@@ -857,7 +906,7 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
857 906
858void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 907void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
859 IPC::RequestParser rp{ctx}; 908 IPC::RequestParser rp{ctx};
860 const auto npad_id{rp.Pop<u32>()}; 909 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
861 910
862 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); 911 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
863 912
@@ -872,16 +921,17 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
872 // Should have no effect with how our npad sets up the data 921 // Should have no effect with how our npad sets up the data
873 IPC::RequestParser rp{ctx}; 922 IPC::RequestParser rp{ctx};
874 struct Parameters { 923 struct Parameters {
875 u32 unknown; 924 s32 revision;
876 INSERT_PADDING_WORDS_NOINIT(1); 925 INSERT_PADDING_WORDS_NOINIT(1);
877 u64 applet_resource_user_id; 926 u64 applet_resource_user_id;
878 }; 927 };
928 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
879 929
880 const auto parameters{rp.PopRaw<Parameters>()}; 930 const auto parameters{rp.PopRaw<Parameters>()};
881 931
882 applet_resource->ActivateController(HidController::NPad); 932 applet_resource->ActivateController(HidController::NPad);
883 933
884 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, 934 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
885 parameters.applet_resource_user_id); 935 parameters.applet_resource_user_id);
886 936
887 IPC::ResponseBuilder rb{ctx, 2}; 937 IPC::ResponseBuilder rb{ctx, 2};
@@ -891,7 +941,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
891void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 941void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx}; 942 IPC::RequestParser rp{ctx};
893 const auto applet_resource_user_id{rp.Pop<u64>()}; 943 const auto applet_resource_user_id{rp.Pop<u64>()};
894 const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()}; 944 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
895 945
896 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); 946 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
897 947
@@ -916,15 +966,16 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
916void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 966void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
917 IPC::RequestParser rp{ctx}; 967 IPC::RequestParser rp{ctx};
918 struct Parameters { 968 struct Parameters {
919 u32 npad_id; 969 Core::HID::NpadIdType npad_id;
920 INSERT_PADDING_WORDS_NOINIT(1); 970 INSERT_PADDING_WORDS_NOINIT(1);
921 u64 applet_resource_user_id; 971 u64 applet_resource_user_id;
922 }; 972 };
973 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
923 974
924 const auto parameters{rp.PopRaw<Parameters>()}; 975 const auto parameters{rp.PopRaw<Parameters>()};
925 976
926 applet_resource->GetController<Controller_NPad>(HidController::NPad) 977 applet_resource->GetController<Controller_NPad>(HidController::NPad)
927 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); 978 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
928 979
929 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 980 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
930 parameters.npad_id, parameters.applet_resource_user_id); 981 parameters.npad_id, parameters.applet_resource_user_id);
@@ -937,16 +988,17 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
937 // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault 988 // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
938 IPC::RequestParser rp{ctx}; 989 IPC::RequestParser rp{ctx};
939 struct Parameters { 990 struct Parameters {
940 u32 npad_id; 991 Core::HID::NpadIdType npad_id;
941 INSERT_PADDING_WORDS_NOINIT(1); 992 INSERT_PADDING_WORDS_NOINIT(1);
942 u64 applet_resource_user_id; 993 u64 applet_resource_user_id;
943 u64 npad_joy_device_type; 994 u64 npad_joy_device_type;
944 }; 995 };
996 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
945 997
946 const auto parameters{rp.PopRaw<Parameters>()}; 998 const auto parameters{rp.PopRaw<Parameters>()};
947 999
948 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1000 applet_resource->GetController<Controller_NPad>(HidController::NPad)
949 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); 1001 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
950 1002
951 LOG_WARNING(Service_HID, 1003 LOG_WARNING(Service_HID,
952 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1004 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
@@ -960,15 +1012,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
960void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 1012void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
961 IPC::RequestParser rp{ctx}; 1013 IPC::RequestParser rp{ctx};
962 struct Parameters { 1014 struct Parameters {
963 u32 npad_id; 1015 Core::HID::NpadIdType npad_id;
964 INSERT_PADDING_WORDS_NOINIT(1); 1016 INSERT_PADDING_WORDS_NOINIT(1);
965 u64 applet_resource_user_id; 1017 u64 applet_resource_user_id;
966 }; 1018 };
1019 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
967 1020
968 const auto parameters{rp.PopRaw<Parameters>()}; 1021 const auto parameters{rp.PopRaw<Parameters>()};
969 1022
970 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1023 applet_resource->GetController<Controller_NPad>(HidController::NPad)
971 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual); 1024 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual);
972 1025
973 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1026 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
974 parameters.npad_id, parameters.applet_resource_user_id); 1027 parameters.npad_id, parameters.applet_resource_user_id);
@@ -979,8 +1032,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
979 1032
980void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 1033void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
981 IPC::RequestParser rp{ctx}; 1034 IPC::RequestParser rp{ctx};
982 const auto npad_id_1{rp.Pop<u32>()}; 1035 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
983 const auto npad_id_2{rp.Pop<u32>()}; 1036 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
984 const auto applet_resource_user_id{rp.Pop<u64>()}; 1037 const auto applet_resource_user_id{rp.Pop<u64>()};
985 1038
986 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1039 applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1046,8 +1099,8 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
1046 1099
1047void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { 1100void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1048 IPC::RequestParser rp{ctx}; 1101 IPC::RequestParser rp{ctx};
1049 const auto npad_id_1{rp.Pop<u32>()}; 1102 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1050 const auto npad_id_2{rp.Pop<u32>()}; 1103 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1051 const auto applet_resource_user_id{rp.Pop<u64>()}; 1104 const auto applet_resource_user_id{rp.Pop<u64>()};
1052 1105
1053 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) 1106 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1068,10 +1121,11 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1068void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { 1121void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
1069 IPC::RequestParser rp{ctx}; 1122 IPC::RequestParser rp{ctx};
1070 struct Parameters { 1123 struct Parameters {
1071 u32 npad_id; 1124 Core::HID::NpadIdType npad_id;
1072 INSERT_PADDING_WORDS_NOINIT(1); 1125 INSERT_PADDING_WORDS_NOINIT(1);
1073 u64 applet_resource_user_id; 1126 u64 applet_resource_user_id;
1074 }; 1127 };
1128 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1075 1129
1076 const auto parameters{rp.PopRaw<Parameters>()}; 1130 const auto parameters{rp.PopRaw<Parameters>()};
1077 1131
@@ -1089,9 +1143,10 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1089 struct Parameters { 1143 struct Parameters {
1090 bool unintended_home_button_input_protection; 1144 bool unintended_home_button_input_protection;
1091 INSERT_PADDING_BYTES_NOINIT(3); 1145 INSERT_PADDING_BYTES_NOINIT(3);
1092 u32 npad_id; 1146 Core::HID::NpadIdType npad_id;
1093 u64 applet_resource_user_id; 1147 u64 applet_resource_user_id;
1094 }; 1148 };
1149 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1095 1150
1096 const auto parameters{rp.PopRaw<Parameters>()}; 1151 const auto parameters{rp.PopRaw<Parameters>()};
1097 1152
@@ -1113,6 +1168,7 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
1113 IPC::RequestParser rp{ctx}; 1168 IPC::RequestParser rp{ctx};
1114 struct Parameters { 1169 struct Parameters {
1115 bool analog_stick_use_center_clamp; 1170 bool analog_stick_use_center_clamp;
1171 INSERT_PADDING_BYTES_NOINIT(7);
1116 u64 applet_resource_user_id; 1172 u64 applet_resource_user_id;
1117 }; 1173 };
1118 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 1174 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -1132,38 +1188,38 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
1132 1188
1133void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 1189void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1134 IPC::RequestParser rp{ctx}; 1190 IPC::RequestParser rp{ctx};
1135 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 1191 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1136 1192
1137 VibrationDeviceInfo vibration_device_info; 1193 Core::HID::VibrationDeviceInfo vibration_device_info;
1138 1194
1139 switch (vibration_device_handle.npad_type) { 1195 switch (vibration_device_handle.npad_type) {
1140 case Controller_NPad::NpadType::ProController: 1196 case Core::HID::NpadStyleIndex::ProController:
1141 case Controller_NPad::NpadType::Handheld: 1197 case Core::HID::NpadStyleIndex::Handheld:
1142 case Controller_NPad::NpadType::JoyconDual: 1198 case Core::HID::NpadStyleIndex::JoyconDual:
1143 case Controller_NPad::NpadType::JoyconLeft: 1199 case Core::HID::NpadStyleIndex::JoyconLeft:
1144 case Controller_NPad::NpadType::JoyconRight: 1200 case Core::HID::NpadStyleIndex::JoyconRight:
1145 default: 1201 default:
1146 vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; 1202 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1147 break; 1203 break;
1148 case Controller_NPad::NpadType::GameCube: 1204 case Core::HID::NpadStyleIndex::GameCube:
1149 vibration_device_info.type = VibrationDeviceType::GcErm; 1205 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1150 break; 1206 break;
1151 case Controller_NPad::NpadType::Pokeball: 1207 case Core::HID::NpadStyleIndex::Pokeball:
1152 vibration_device_info.type = VibrationDeviceType::Unknown; 1208 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1153 break; 1209 break;
1154 } 1210 }
1155 1211
1156 switch (vibration_device_handle.device_index) { 1212 switch (vibration_device_handle.device_index) {
1157 case Controller_NPad::DeviceIndex::Left: 1213 case Core::HID::DeviceIndex::Left:
1158 vibration_device_info.position = VibrationDevicePosition::Left; 1214 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1159 break; 1215 break;
1160 case Controller_NPad::DeviceIndex::Right: 1216 case Core::HID::DeviceIndex::Right:
1161 vibration_device_info.position = VibrationDevicePosition::Right; 1217 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1162 break; 1218 break;
1163 case Controller_NPad::DeviceIndex::None: 1219 case Core::HID::DeviceIndex::None:
1164 default: 1220 default:
1165 UNREACHABLE_MSG("DeviceIndex should never be None!"); 1221 UNREACHABLE_MSG("DeviceIndex should never be None!");
1166 vibration_device_info.position = VibrationDevicePosition::None; 1222 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1167 break; 1223 break;
1168 } 1224 }
1169 1225
@@ -1178,11 +1234,12 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1178void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { 1234void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1179 IPC::RequestParser rp{ctx}; 1235 IPC::RequestParser rp{ctx};
1180 struct Parameters { 1236 struct Parameters {
1181 Controller_NPad::DeviceHandle vibration_device_handle; 1237 Core::HID::VibrationDeviceHandle vibration_device_handle;
1182 Controller_NPad::VibrationValue vibration_value; 1238 Core::HID::VibrationValue vibration_value;
1183 INSERT_PADDING_WORDS_NOINIT(1); 1239 INSERT_PADDING_WORDS_NOINIT(1);
1184 u64 applet_resource_user_id; 1240 u64 applet_resource_user_id;
1185 }; 1241 };
1242 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1186 1243
1187 const auto parameters{rp.PopRaw<Parameters>()}; 1244 const auto parameters{rp.PopRaw<Parameters>()};
1188 1245
@@ -1202,10 +1259,11 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1202void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 1259void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
1203 IPC::RequestParser rp{ctx}; 1260 IPC::RequestParser rp{ctx};
1204 struct Parameters { 1261 struct Parameters {
1205 Controller_NPad::DeviceHandle vibration_device_handle; 1262 Core::HID::VibrationDeviceHandle vibration_device_handle;
1206 INSERT_PADDING_WORDS_NOINIT(1); 1263 INSERT_PADDING_WORDS_NOINIT(1);
1207 u64 applet_resource_user_id; 1264 u64 applet_resource_user_id;
1208 }; 1265 };
1266 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1209 1267
1210 const auto parameters{rp.PopRaw<Parameters>()}; 1268 const auto parameters{rp.PopRaw<Parameters>()};
1211 1269
@@ -1256,10 +1314,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
1256 const auto handles = ctx.ReadBuffer(0); 1314 const auto handles = ctx.ReadBuffer(0);
1257 const auto vibrations = ctx.ReadBuffer(1); 1315 const auto vibrations = ctx.ReadBuffer(1);
1258 1316
1259 std::vector<Controller_NPad::DeviceHandle> vibration_device_handles( 1317 std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
1260 handles.size() / sizeof(Controller_NPad::DeviceHandle)); 1318 handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
1261 std::vector<Controller_NPad::VibrationValue> vibration_values( 1319 std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
1262 vibrations.size() / sizeof(Controller_NPad::VibrationValue)); 1320 sizeof(Core::HID::VibrationValue));
1263 1321
1264 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); 1322 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
1265 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); 1323 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
@@ -1276,9 +1334,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
1276void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { 1334void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1277 IPC::RequestParser rp{ctx}; 1335 IPC::RequestParser rp{ctx};
1278 struct Parameters { 1336 struct Parameters {
1279 Controller_NPad::DeviceHandle vibration_device_handle; 1337 Core::HID::VibrationDeviceHandle vibration_device_handle;
1338 INSERT_PADDING_WORDS_NOINIT(1);
1280 u64 applet_resource_user_id; 1339 u64 applet_resource_user_id;
1281 VibrationGcErmCommand gc_erm_command; 1340 Core::HID::VibrationGcErmCommand gc_erm_command;
1282 }; 1341 };
1283 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1342 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1284 1343
@@ -1292,26 +1351,26 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1292 */ 1351 */
1293 const auto vibration_value = [parameters] { 1352 const auto vibration_value = [parameters] {
1294 switch (parameters.gc_erm_command) { 1353 switch (parameters.gc_erm_command) {
1295 case VibrationGcErmCommand::Stop: 1354 case Core::HID::VibrationGcErmCommand::Stop:
1296 return Controller_NPad::VibrationValue{ 1355 return Core::HID::VibrationValue{
1297 .amp_low = 0.0f, 1356 .low_amplitude = 0.0f,
1298 .freq_low = 160.0f, 1357 .low_frequency = 160.0f,
1299 .amp_high = 0.0f, 1358 .high_amplitude = 0.0f,
1300 .freq_high = 320.0f, 1359 .high_frequency = 320.0f,
1301 }; 1360 };
1302 case VibrationGcErmCommand::Start: 1361 case Core::HID::VibrationGcErmCommand::Start:
1303 return Controller_NPad::VibrationValue{ 1362 return Core::HID::VibrationValue{
1304 .amp_low = 1.0f, 1363 .low_amplitude = 1.0f,
1305 .freq_low = 160.0f, 1364 .low_frequency = 160.0f,
1306 .amp_high = 1.0f, 1365 .high_amplitude = 1.0f,
1307 .freq_high = 320.0f, 1366 .high_frequency = 320.0f,
1308 }; 1367 };
1309 case VibrationGcErmCommand::StopHard: 1368 case Core::HID::VibrationGcErmCommand::StopHard:
1310 return Controller_NPad::VibrationValue{ 1369 return Core::HID::VibrationValue{
1311 .amp_low = 0.0f, 1370 .low_amplitude = 0.0f,
1312 .freq_low = 0.0f, 1371 .low_frequency = 0.0f,
1313 .amp_high = 0.0f, 1372 .high_amplitude = 0.0f,
1314 .freq_high = 0.0f, 1373 .high_frequency = 0.0f,
1315 }; 1374 };
1316 default: 1375 default:
1317 return Controller_NPad::DEFAULT_VIBRATION_VALUE; 1376 return Controller_NPad::DEFAULT_VIBRATION_VALUE;
@@ -1336,7 +1395,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1336void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { 1395void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1337 IPC::RequestParser rp{ctx}; 1396 IPC::RequestParser rp{ctx};
1338 struct Parameters { 1397 struct Parameters {
1339 Controller_NPad::DeviceHandle vibration_device_handle; 1398 Core::HID::VibrationDeviceHandle vibration_device_handle;
1340 INSERT_PADDING_WORDS_NOINIT(1); 1399 INSERT_PADDING_WORDS_NOINIT(1);
1341 u64 applet_resource_user_id; 1400 u64 applet_resource_user_id;
1342 }; 1401 };
@@ -1347,8 +1406,8 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1347 .GetLastVibration(parameters.vibration_device_handle); 1406 .GetLastVibration(parameters.vibration_device_handle);
1348 1407
1349 const auto gc_erm_command = [last_vibration] { 1408 const auto gc_erm_command = [last_vibration] {
1350 if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) { 1409 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1351 return VibrationGcErmCommand::Start; 1410 return Core::HID::VibrationGcErmCommand::Start;
1352 } 1411 }
1353 1412
1354 /** 1413 /**
@@ -1357,11 +1416,11 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1357 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. 1416 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1358 * This is done to reuse the controller vibration functions made for regular controllers. 1417 * This is done to reuse the controller vibration functions made for regular controllers.
1359 */ 1418 */
1360 if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) { 1419 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1361 return VibrationGcErmCommand::StopHard; 1420 return Core::HID::VibrationGcErmCommand::StopHard;
1362 } 1421 }
1363 1422
1364 return VibrationGcErmCommand::Stop; 1423 return Core::HID::VibrationGcErmCommand::Stop;
1365 }(); 1424 }();
1366 1425
1367 LOG_DEBUG(Service_HID, 1426 LOG_DEBUG(Service_HID,
@@ -1401,10 +1460,11 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
1401void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { 1460void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
1402 IPC::RequestParser rp{ctx}; 1461 IPC::RequestParser rp{ctx};
1403 struct Parameters { 1462 struct Parameters {
1404 Controller_NPad::DeviceHandle vibration_device_handle; 1463 Core::HID::VibrationDeviceHandle vibration_device_handle;
1405 INSERT_PADDING_WORDS_NOINIT(1); 1464 INSERT_PADDING_WORDS_NOINIT(1);
1406 u64 applet_resource_user_id; 1465 u64 applet_resource_user_id;
1407 }; 1466 };
1467 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1408 1468
1409 const auto parameters{rp.PopRaw<Parameters>()}; 1469 const auto parameters{rp.PopRaw<Parameters>()};
1410 1470
@@ -1435,18 +1495,18 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1435void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1495void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1436 IPC::RequestParser rp{ctx}; 1496 IPC::RequestParser rp{ctx};
1437 struct Parameters { 1497 struct Parameters {
1438 Controller_NPad::DeviceHandle sixaxis_handle; 1498 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1439 INSERT_PADDING_WORDS_NOINIT(1); 1499 INSERT_PADDING_WORDS_NOINIT(1);
1440 u64 applet_resource_user_id; 1500 u64 applet_resource_user_id;
1441 }; 1501 };
1502 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1442 1503
1443 const auto parameters{rp.PopRaw<Parameters>()}; 1504 const auto parameters{rp.PopRaw<Parameters>()};
1444 1505
1445 LOG_WARNING( 1506 LOG_WARNING(Service_HID,
1446 Service_HID, 1507 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1447 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1508 parameters.console_sixaxis_handle.unknown_1,
1448 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 1509 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1449 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
1450 1510
1451 IPC::ResponseBuilder rb{ctx, 2}; 1511 IPC::ResponseBuilder rb{ctx, 2};
1452 rb.Push(ResultSuccess); 1512 rb.Push(ResultSuccess);
@@ -1455,18 +1515,18 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1455void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1515void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1456 IPC::RequestParser rp{ctx}; 1516 IPC::RequestParser rp{ctx};
1457 struct Parameters { 1517 struct Parameters {
1458 Controller_NPad::DeviceHandle sixaxis_handle; 1518 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1459 INSERT_PADDING_WORDS_NOINIT(1); 1519 INSERT_PADDING_WORDS_NOINIT(1);
1460 u64 applet_resource_user_id; 1520 u64 applet_resource_user_id;
1461 }; 1521 };
1522 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1462 1523
1463 const auto parameters{rp.PopRaw<Parameters>()}; 1524 const auto parameters{rp.PopRaw<Parameters>()};
1464 1525
1465 LOG_WARNING( 1526 LOG_WARNING(Service_HID,
1466 Service_HID, 1527 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1467 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1528 parameters.console_sixaxis_handle.unknown_1,
1468 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 1529 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1469 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
1470 1530
1471 IPC::ResponseBuilder rb{ctx, 2}; 1531 IPC::ResponseBuilder rb{ctx, 2};
1472 rb.Push(ResultSuccess); 1532 rb.Push(ResultSuccess);
@@ -1620,10 +1680,8 @@ void Hid::SetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1620 1680
1621void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { 1681void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1622 IPC::RequestParser rp{ctx}; 1682 IPC::RequestParser rp{ctx};
1623 const auto applet_resource_user_id{rp.Pop<u64>()};
1624 1683
1625 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 1684 LOG_WARNING(Service_HID, "(STUBBED) called");
1626 applet_resource_user_id);
1627 1685
1628 IPC::ResponseBuilder rb{ctx, 4}; 1686 IPC::ResponseBuilder rb{ctx, 4};
1629 rb.Push(ResultSuccess); 1687 rb.Push(ResultSuccess);
@@ -1825,7 +1883,7 @@ public:
1825 {317, nullptr, "GetNpadLeftRightInterfaceType"}, 1883 {317, nullptr, "GetNpadLeftRightInterfaceType"},
1826 {318, nullptr, "HasBattery"}, 1884 {318, nullptr, "HasBattery"},
1827 {319, nullptr, "HasLeftRightBattery"}, 1885 {319, nullptr, "HasLeftRightBattery"},
1828 {321, nullptr, "GetUniquePadsFromNpad"}, 1886 {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
1829 {322, nullptr, "GetIrSensorState"}, 1887 {322, nullptr, "GetIrSensorState"},
1830 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 1888 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
1831 {324, nullptr, "GetUniquePadButtonSet"}, 1889 {324, nullptr, "GetUniquePadButtonSet"},
@@ -1996,6 +2054,18 @@ private:
1996 IPC::ResponseBuilder rb{ctx, 2}; 2054 IPC::ResponseBuilder rb{ctx, 2};
1997 rb.Push(ResultSuccess); 2055 rb.Push(ResultSuccess);
1998 } 2056 }
2057
2058 void GetUniquePadsFromNpad(Kernel::HLERequestContext& ctx) {
2059 IPC::RequestParser rp{ctx};
2060 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2061
2062 const s64 total_entries = 0;
2063 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2064
2065 IPC::ResponseBuilder rb{ctx, 3};
2066 rb.Push(ResultSuccess);
2067 rb.Push(total_entries);
2068 }
1999}; 2069};
2000 2070
2001class HidTmp final : public ServiceFramework<HidTmp> { 2071class HidTmp final : public ServiceFramework<HidTmp> {
@@ -2037,10 +2107,6 @@ public:
2037 } 2107 }
2038}; 2108};
2039 2109
2040void ReloadInputDevices() {
2041 Settings::values.is_device_reload_pending.store(true);
2042}
2043
2044void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2110void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2045 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2111 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2046 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2112 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index b1fe75e94..ab0084118 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -60,21 +60,23 @@ public:
60private: 60private:
61 template <typename T> 61 template <typename T>
62 void MakeController(HidController controller) { 62 void MakeController(HidController controller) {
63 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); 63 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore());
64 } 64 }
65 template <typename T> 65 template <typename T>
66 void MakeControllerWithServiceContext(HidController controller) { 66 void MakeControllerWithServiceContext(HidController controller) {
67 controllers[static_cast<std::size_t>(controller)] = 67 controllers[static_cast<std::size_t>(controller)] =
68 std::make_unique<T>(system, service_context); 68 std::make_unique<T>(system.HIDCore(), service_context);
69 } 69 }
70 70
71 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 71 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
72 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 72 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
73 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
73 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 74 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
74 75
75 KernelHelpers::ServiceContext& service_context; 76 KernelHelpers::ServiceContext& service_context;
76 77
77 std::shared_ptr<Core::Timing::EventType> pad_update_event; 78 std::shared_ptr<Core::Timing::EventType> pad_update_event;
79 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
78 std::shared_ptr<Core::Timing::EventType> motion_update_event; 80 std::shared_ptr<Core::Timing::EventType> motion_update_event;
79 81
80 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 82 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -161,38 +163,11 @@ private:
161 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 163 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
162 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); 164 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
163 165
164 enum class VibrationDeviceType : u32 {
165 Unknown = 0,
166 LinearResonantActuator = 1,
167 GcErm = 2,
168 };
169
170 enum class VibrationDevicePosition : u32 {
171 None = 0,
172 Left = 1,
173 Right = 2,
174 };
175
176 enum class VibrationGcErmCommand : u64 {
177 Stop = 0,
178 Start = 1,
179 StopHard = 2,
180 };
181
182 struct VibrationDeviceInfo {
183 VibrationDeviceType type{};
184 VibrationDevicePosition position{};
185 };
186 static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
187
188 std::shared_ptr<IAppletResource> applet_resource; 166 std::shared_ptr<IAppletResource> applet_resource;
189 167
190 KernelHelpers::ServiceContext service_context; 168 KernelHelpers::ServiceContext service_context;
191}; 169};
192 170
193/// Reload input devices. Used when input configuration changed
194void ReloadInputDevices();
195
196/// Registers all HID services with the specified service manager. 171/// Registers all HID services with the specified service manager.
197void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 172void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
198 173
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h
new file mode 100644
index 000000000..44c20d967
--- /dev/null
+++ b/src/core/hle/service/hid/ring_lifo.h
@@ -0,0 +1,54 @@
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 <array>
8
9#include "common/common_types.h"
10
11namespace Service::HID {
12
13template <typename State>
14struct AtomicStorage {
15 s64 sampling_number;
16 State state;
17};
18
19template <typename State, std::size_t max_buffer_size>
20struct Lifo {
21 s64 timestamp{};
22 s64 total_buffer_count = static_cast<s64>(max_buffer_size);
23 s64 buffer_tail{};
24 s64 buffer_count{};
25 std::array<AtomicStorage<State>, max_buffer_size> entries{};
26
27 const AtomicStorage<State>& ReadCurrentEntry() const {
28 return entries[buffer_tail];
29 }
30
31 const AtomicStorage<State>& ReadPreviousEntry() const {
32 return entries[GetPreviousEntryIndex()];
33 }
34
35 std::size_t GetPreviousEntryIndex() const {
36 return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
37 }
38
39 std::size_t GetNextEntryIndex() const {
40 return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
41 }
42
43 void WriteNextEntry(const State& new_state) {
44 if (buffer_count < total_buffer_count - 1) {
45 buffer_count++;
46 }
47 buffer_tail = GetNextEntryIndex();
48 const auto& previous_entry = ReadPreviousEntry();
49 entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
50 entries[buffer_tail].state = new_state;
51 }
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 32eff3b2a..3782703d2 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -396,12 +396,12 @@ public:
396 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, 396 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
397 nro_header.segment_headers[DATA_INDEX].memory_size); 397 nro_header.segment_headers[DATA_INDEX].memory_size);
398 398
399 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( 399 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
400 text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute)); 400 text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
401 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start, 401 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
402 Kernel::KMemoryPermission::Read)); 402 ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read));
403 403
404 return process->PageTable().SetCodeMemoryPermission( 404 return process->PageTable().SetProcessMemoryPermission(
405 data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); 405 data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
406 } 406 }
407 407
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 64ffc8572..382ddcae5 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/service/ns/errors.h" 12#include "core/hle/service/ns/errors.h"
13#include "core/hle/service/ns/language.h" 13#include "core/hle/service/ns/language.h"
14#include "core/hle/service/ns/ns.h" 14#include "core/hle/service/ns/ns.h"
15#include "core/hle/service/ns/pdm_qry.h"
15#include "core/hle/service/ns/pl_u.h" 16#include "core/hle/service/ns/pl_u.h"
16#include "core/hle/service/set/set.h" 17#include "core/hle/service/set/set.h"
17 18
@@ -570,11 +571,29 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
570 571
571IFactoryResetInterface::~IFactoryResetInterface() = default; 572IFactoryResetInterface::~IFactoryResetInterface() = default;
572 573
574IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
575 Core::System& system_)
576 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
577 // clang-format off
578 static const FunctionInfo functions[] = {
579 {0, nullptr, "GetApplicationControlData"},
580 {1, nullptr, "GetApplicationDesiredLanguage"},
581 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
582 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
583 {4, nullptr, "SelectApplicationDesiredLanguage"},
584 };
585 // clang-format on
586
587 RegisterHandlers(functions);
588}
589
590IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
591
573NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { 592NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
574 // clang-format off 593 // clang-format off
575 static const FunctionInfo functions[] = { 594 static const FunctionInfo functions[] = {
576 {7988, nullptr, "GetDynamicRightsInterface"}, 595 {7988, nullptr, "GetDynamicRightsInterface"},
577 {7989, nullptr, "GetReadOnlyApplicationControlDataInterface"}, 596 {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
578 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, 597 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"},
579 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, 598 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
580 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, 599 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
@@ -738,6 +757,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
738 std::make_shared<NS_SU>(system)->InstallAsService(service_manager); 757 std::make_shared<NS_SU>(system)->InstallAsService(service_manager);
739 std::make_shared<NS_VM>(system)->InstallAsService(service_manager); 758 std::make_shared<NS_VM>(system)->InstallAsService(service_manager);
740 759
760 std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
761
741 std::make_shared<PL_U>(system)->InstallAsService(service_manager); 762 std::make_shared<PL_U>(system)->InstallAsService(service_manager);
742} 763}
743 764
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 218eec3ec..43540b0fb 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -74,6 +74,13 @@ public:
74 ~IFactoryResetInterface() override; 74 ~IFactoryResetInterface() override;
75}; 75};
76 76
77class IReadOnlyApplicationControlDataInterface final
78 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
79public:
80 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
81 ~IReadOnlyApplicationControlDataInterface() override;
82};
83
77class NS final : public ServiceFramework<NS> { 84class NS final : public ServiceFramework<NS> {
78public: 85public:
79 explicit NS(const char* name, Core::System& system_); 86 explicit NS(const char* name, Core::System& system_);
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
new file mode 100644
index 000000000..e2fab5c3f
--- /dev/null
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -0,0 +1,69 @@
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 <memory>
6
7#include "common/logging/log.h"
8#include "common/uuid.h"
9#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/ns/pdm_qry.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::NS {
15
16PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
17 // clang-format off
18 static const FunctionInfo functions[] = {
19 {0, nullptr, "QueryAppletEvent"},
20 {1, nullptr, "QueryPlayStatistics"},
21 {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
22 {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
23 {4, nullptr, "QueryPlayStatisticsByApplicationId"},
24 {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
25 {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
26 {7, nullptr, "QueryLastPlayTimeV0"},
27 {8, nullptr, "QueryPlayEvent"},
28 {9, nullptr, "GetAvailablePlayEventRange"},
29 {10, nullptr, "QueryAccountEvent"},
30 {11, nullptr, "QueryAccountPlayEvent"},
31 {12, nullptr, "GetAvailableAccountPlayEventRange"},
32 {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
33 {14, nullptr, "QueryRecentlyPlayedApplication"},
34 {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
35 {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
36 {17, nullptr, "QueryLastPlayTime"},
37 {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
38 {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43}
44
45PDM_QRY::~PDM_QRY() = default;
46
47void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx) {
48 IPC::RequestParser rp{ctx};
49 const auto unknown = rp.Pop<bool>();
50 rp.Pop<u8>(); // Padding
51 const auto application_id = rp.Pop<u64>();
52 const auto user_account_uid = rp.PopRaw<Common::UUID>();
53
54 // TODO(German77): Read statistics of the game
55 PlayStatistics statistics{
56 .application_id = application_id,
57 .total_launches = 1,
58 };
59
60 LOG_WARNING(Service_NS,
61 "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
62 unknown, application_id, user_account_uid.Format());
63
64 IPC::ResponseBuilder rb{ctx, 12};
65 rb.Push(ResultSuccess);
66 rb.PushRaw(statistics);
67}
68
69} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h
new file mode 100644
index 000000000..516136314
--- /dev/null
+++ b/src/core/hle/service/ns/pdm_qry.h
@@ -0,0 +1,33 @@
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 "core/hle/service/service.h"
8
9namespace Service::NS {
10
11struct PlayStatistics {
12 u64 application_id{};
13 u32 first_entry_index{};
14 u32 first_timestamp_user{};
15 u32 first_timestamp_network{};
16 u32 last_entry_index{};
17 u32 last_timestamp_user{};
18 u32 last_timestamp_network{};
19 u32 play_time_in_minutes{};
20 u32 total_launches{};
21};
22static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
23
24class PDM_QRY final : public ServiceFramework<PDM_QRY> {
25public:
26 explicit PDM_QRY(Core::System& system_);
27 ~PDM_QRY() override;
28
29private:
30 void QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx);
31};
32
33} // namespace Service::NS
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 88fc5b5cc..277abc17a 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -13,7 +13,12 @@ namespace Service::PM {
13 13
14namespace { 14namespace {
15 15
16constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1}; 16constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
17[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
18[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
19[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
20[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
21[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
17 22
18constexpr u64 NO_PROCESS_FOUND_PID{0}; 23constexpr u64 NO_PROCESS_FOUND_PID{0};
19 24
@@ -95,18 +100,18 @@ public:
95private: 100private:
96 void GetProcessId(Kernel::HLERequestContext& ctx) { 101 void GetProcessId(Kernel::HLERequestContext& ctx) {
97 IPC::RequestParser rp{ctx}; 102 IPC::RequestParser rp{ctx};
98 const auto title_id = rp.PopRaw<u64>(); 103 const auto program_id = rp.PopRaw<u64>();
99 104
100 LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id); 105 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
101 106
102 const auto process = 107 const auto process =
103 SearchProcessList(kernel.GetProcessList(), [title_id](const auto& proc) { 108 SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) {
104 return proc->GetProgramID() == title_id; 109 return proc->GetProgramID() == program_id;
105 }); 110 });
106 111
107 if (!process.has_value()) { 112 if (!process.has_value()) {
108 IPC::ResponseBuilder rb{ctx, 2}; 113 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ERROR_PROCESS_NOT_FOUND); 114 rb.Push(ResultProcessNotFound);
110 return; 115 return;
111 } 116 }
112 117
@@ -128,13 +133,16 @@ public:
128 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) 133 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
129 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { 134 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
130 static const FunctionInfo functions[] = { 135 static const FunctionInfo functions[] = {
131 {0, &Info::GetTitleId, "GetTitleId"}, 136 {0, &Info::GetProgramId, "GetProgramId"},
137 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
138 {65001, nullptr, "AtmosphereHasLaunchedProgram"},
139 {65002, nullptr, "AtmosphereGetProcessInfo"},
132 }; 140 };
133 RegisterHandlers(functions); 141 RegisterHandlers(functions);
134 } 142 }
135 143
136private: 144private:
137 void GetTitleId(Kernel::HLERequestContext& ctx) { 145 void GetProgramId(Kernel::HLERequestContext& ctx) {
138 IPC::RequestParser rp{ctx}; 146 IPC::RequestParser rp{ctx};
139 const auto process_id = rp.PopRaw<u64>(); 147 const auto process_id = rp.PopRaw<u64>();
140 148
@@ -146,7 +154,7 @@ private:
146 154
147 if (!process.has_value()) { 155 if (!process.has_value()) {
148 IPC::ResponseBuilder rb{ctx, 2}; 156 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ERROR_PROCESS_NOT_FOUND); 157 rb.Push(ResultProcessNotFound);
150 return; 158 return;
151 } 159 }
152 160
@@ -155,6 +163,27 @@ private:
155 rb.Push((*process)->GetProgramID()); 163 rb.Push((*process)->GetProgramID());
156 } 164 }
157 165
166 void AtmosphereGetProcessId(Kernel::HLERequestContext& ctx) {
167 IPC::RequestParser rp{ctx};
168 const auto program_id = rp.PopRaw<u64>();
169
170 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
171
172 const auto process = SearchProcessList(process_list, [program_id](const auto& proc) {
173 return proc->GetProgramID() == program_id;
174 });
175
176 if (!process.has_value()) {
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(ResultProcessNotFound);
179 return;
180 }
181
182 IPC::ResponseBuilder rb{ctx, 4};
183 rb.Push(ResultSuccess);
184 rb.Push((*process)->GetProcessID());
185 }
186
158 const std::vector<Kernel::KProcess*>& process_list; 187 const std::vector<Kernel::KProcess*>& process_list;
159}; 188};
160 189
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index dd13d948f..d4fa69a77 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -1,36 +1,32 @@
1add_library(input_common STATIC 1add_library(input_common STATIC
2 analog_from_button.cpp 2 drivers/gc_adapter.cpp
3 analog_from_button.h 3 drivers/gc_adapter.h
4 keyboard.cpp 4 drivers/keyboard.cpp
5 keyboard.h 5 drivers/keyboard.h
6 drivers/mouse.cpp
7 drivers/mouse.h
8 drivers/sdl_driver.cpp
9 drivers/sdl_driver.h
10 drivers/tas_input.cpp
11 drivers/tas_input.h
12 drivers/touch_screen.cpp
13 drivers/touch_screen.h
14 drivers/udp_client.cpp
15 drivers/udp_client.h
16 helpers/stick_from_buttons.cpp
17 helpers/stick_from_buttons.h
18 helpers/touch_from_buttons.cpp
19 helpers/touch_from_buttons.h
20 helpers/udp_protocol.cpp
21 helpers/udp_protocol.h
22 input_engine.cpp
23 input_engine.h
24 input_mapping.cpp
25 input_mapping.h
26 input_poller.cpp
27 input_poller.h
6 main.cpp 28 main.cpp
7 main.h 29 main.h
8 motion_from_button.cpp
9 motion_from_button.h
10 motion_input.cpp
11 motion_input.h
12 touch_from_button.cpp
13 touch_from_button.h
14 gcadapter/gc_adapter.cpp
15 gcadapter/gc_adapter.h
16 gcadapter/gc_poller.cpp
17 gcadapter/gc_poller.h
18 mouse/mouse_input.cpp
19 mouse/mouse_input.h
20 mouse/mouse_poller.cpp
21 mouse/mouse_poller.h
22 sdl/sdl.cpp
23 sdl/sdl.h
24 tas/tas_input.cpp
25 tas/tas_input.h
26 tas/tas_poller.cpp
27 tas/tas_poller.h
28 udp/client.cpp
29 udp/client.h
30 udp/protocol.cpp
31 udp/protocol.h
32 udp/udp.cpp
33 udp/udp.h
34) 30)
35 31
36if (MSVC) 32if (MSVC)
@@ -57,8 +53,8 @@ endif()
57 53
58if (ENABLE_SDL2) 54if (ENABLE_SDL2)
59 target_sources(input_common PRIVATE 55 target_sources(input_common PRIVATE
60 sdl/sdl_impl.cpp 56 drivers/sdl_driver.cpp
61 sdl/sdl_impl.h 57 drivers/sdl_driver.h
62 ) 58 )
63 target_link_libraries(input_common PRIVATE SDL2) 59 target_link_libraries(input_common PRIVATE SDL2)
64 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 60 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
deleted file mode 100755
index 2fafd077f..000000000
--- a/src/input_common/analog_from_button.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <chrono>
7#include <cmath>
8#include <thread>
9#include "common/math_util.h"
10#include "common/settings.h"
11#include "input_common/analog_from_button.h"
12
13namespace InputCommon {
14
15class Analog final : public Input::AnalogDevice {
16public:
17 using Button = std::unique_ptr<Input::ButtonDevice>;
18
19 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
20 float modifier_scale_, float modifier_angle_)
21 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
22 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
23 modifier_angle(modifier_angle_) {
24 Input::InputCallback<bool> callbacks{
25 [this]([[maybe_unused]] bool status) { UpdateStatus(); }};
26 up->SetCallback(callbacks);
27 down->SetCallback(callbacks);
28 left->SetCallback(callbacks);
29 right->SetCallback(callbacks);
30 modifier->SetCallback(callbacks);
31 }
32
33 bool IsAngleGreater(float old_angle, float new_angle) const {
34 constexpr float TAU = Common::PI * 2.0f;
35 // Use wider angle to ease the transition.
36 constexpr float aperture = TAU * 0.15f;
37 const float top_limit = new_angle + aperture;
38 return (old_angle > new_angle && old_angle <= top_limit) ||
39 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
40 }
41
42 bool IsAngleSmaller(float old_angle, float new_angle) const {
43 constexpr float TAU = Common::PI * 2.0f;
44 // Use wider angle to ease the transition.
45 constexpr float aperture = TAU * 0.15f;
46 const float bottom_limit = new_angle - aperture;
47 return (old_angle >= bottom_limit && old_angle < new_angle) ||
48 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
49 }
50
51 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
52 constexpr float TAU = Common::PI * 2.0f;
53 float new_angle = angle;
54
55 auto time_difference = static_cast<float>(
56 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
57 time_difference /= 1000.0f * 1000.0f;
58 if (time_difference > 0.5f) {
59 time_difference = 0.5f;
60 }
61
62 if (IsAngleGreater(new_angle, goal_angle)) {
63 new_angle -= modifier_angle * time_difference;
64 if (new_angle < 0) {
65 new_angle += TAU;
66 }
67 if (!IsAngleGreater(new_angle, goal_angle)) {
68 return goal_angle;
69 }
70 } else if (IsAngleSmaller(new_angle, goal_angle)) {
71 new_angle += modifier_angle * time_difference;
72 if (new_angle >= TAU) {
73 new_angle -= TAU;
74 }
75 if (!IsAngleSmaller(new_angle, goal_angle)) {
76 return goal_angle;
77 }
78 } else {
79 return goal_angle;
80 }
81 return new_angle;
82 }
83
84 void SetGoalAngle(bool r, bool l, bool u, bool d) {
85 // Move to the right
86 if (r && !u && !d) {
87 goal_angle = 0.0f;
88 }
89
90 // Move to the upper right
91 if (r && u && !d) {
92 goal_angle = Common::PI * 0.25f;
93 }
94
95 // Move up
96 if (u && !l && !r) {
97 goal_angle = Common::PI * 0.5f;
98 }
99
100 // Move to the upper left
101 if (l && u && !d) {
102 goal_angle = Common::PI * 0.75f;
103 }
104
105 // Move to the left
106 if (l && !u && !d) {
107 goal_angle = Common::PI;
108 }
109
110 // Move to the bottom left
111 if (l && !u && d) {
112 goal_angle = Common::PI * 1.25f;
113 }
114
115 // Move down
116 if (d && !l && !r) {
117 goal_angle = Common::PI * 1.5f;
118 }
119
120 // Move to the bottom right
121 if (r && !u && d) {
122 goal_angle = Common::PI * 1.75f;
123 }
124 }
125
126 void UpdateStatus() {
127 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
128
129 bool r = right->GetStatus();
130 bool l = left->GetStatus();
131 bool u = up->GetStatus();
132 bool d = down->GetStatus();
133
134 // Eliminate contradictory movements
135 if (r && l) {
136 r = false;
137 l = false;
138 }
139 if (u && d) {
140 u = false;
141 d = false;
142 }
143
144 // Move if a key is pressed
145 if (r || l || u || d) {
146 amplitude = coef;
147 } else {
148 amplitude = 0;
149 }
150
151 const auto now = std::chrono::steady_clock::now();
152 const auto time_difference = static_cast<u64>(
153 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
154
155 if (time_difference < 10) {
156 // Disable analog mode if inputs are too fast
157 SetGoalAngle(r, l, u, d);
158 angle = goal_angle;
159 } else {
160 angle = GetAngle(now);
161 SetGoalAngle(r, l, u, d);
162 }
163
164 last_update = now;
165 }
166
167 std::tuple<float, float> GetStatus() const override {
168 if (Settings::values.emulate_analog_keyboard) {
169 const auto now = std::chrono::steady_clock::now();
170 float angle_ = GetAngle(now);
171 return std::make_tuple(std::cos(angle_) * amplitude, std::sin(angle_) * amplitude);
172 }
173 constexpr float SQRT_HALF = 0.707106781f;
174 int x = 0, y = 0;
175 if (right->GetStatus()) {
176 ++x;
177 }
178 if (left->GetStatus()) {
179 --x;
180 }
181 if (up->GetStatus()) {
182 ++y;
183 }
184 if (down->GetStatus()) {
185 --y;
186 }
187 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
188 return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
189 static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
190 }
191
192 Input::AnalogProperties GetAnalogProperties() const override {
193 return {modifier_scale, 1.0f, 0.5f};
194 }
195
196 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
197 switch (direction) {
198 case Input::AnalogDirection::RIGHT:
199 return right->GetStatus();
200 case Input::AnalogDirection::LEFT:
201 return left->GetStatus();
202 case Input::AnalogDirection::UP:
203 return up->GetStatus();
204 case Input::AnalogDirection::DOWN:
205 return down->GetStatus();
206 }
207 return false;
208 }
209
210private:
211 Button up;
212 Button down;
213 Button left;
214 Button right;
215 Button modifier;
216 float modifier_scale;
217 float modifier_angle;
218 float angle{};
219 float goal_angle{};
220 float amplitude{};
221 std::chrono::time_point<std::chrono::steady_clock> last_update;
222};
223
224std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
225 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
226 auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
227 auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
228 auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
229 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
230 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
231 auto modifier_scale = params.Get("modifier_scale", 0.5f);
232 auto modifier_angle = params.Get("modifier_angle", 5.5f);
233 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
234 std::move(right), std::move(modifier), modifier_scale,
235 modifier_angle);
236}
237
238} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index a2f1bb67c..8b6574223 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -2,47 +2,103 @@
2// Licensed under GPLv2+ 2// Licensed under GPLv2+
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono> 5#include <fmt/format.h>
6#include <thread>
7
8#include <libusb.h> 6#include <libusb.h>
9 7
10#include "common/logging/log.h" 8#include "common/logging/log.h"
11#include "common/param_package.h" 9#include "common/param_package.h"
12#include "common/settings_input.h" 10#include "common/settings_input.h"
13#include "input_common/gcadapter/gc_adapter.h" 11#include "common/thread.h"
12#include "input_common/drivers/gc_adapter.h"
13
14namespace InputCommon {
15
16class LibUSBContext {
17public:
18 explicit LibUSBContext() {
19 init_result = libusb_init(&ctx);
20 }
21
22 ~LibUSBContext() {
23 libusb_exit(ctx);
24 }
25
26 LibUSBContext& operator=(const LibUSBContext&) = delete;
27 LibUSBContext(const LibUSBContext&) = delete;
28
29 LibUSBContext& operator=(LibUSBContext&&) noexcept = delete;
30 LibUSBContext(LibUSBContext&&) noexcept = delete;
31
32 [[nodiscard]] int InitResult() const noexcept {
33 return init_result;
34 }
35
36 [[nodiscard]] libusb_context* get() noexcept {
37 return ctx;
38 }
39
40private:
41 libusb_context* ctx;
42 int init_result{};
43};
44
45class LibUSBDeviceHandle {
46public:
47 explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept {
48 handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
49 }
50
51 ~LibUSBDeviceHandle() noexcept {
52 if (handle) {
53 libusb_release_interface(handle, 1);
54 libusb_close(handle);
55 }
56 }
14 57
15namespace GCAdapter { 58 LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete;
59 LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete;
16 60
17Adapter::Adapter() { 61 LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete;
18 if (usb_adapter_handle != nullptr) { 62 LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete;
63
64 [[nodiscard]] libusb_device_handle* get() noexcept {
65 return handle;
66 }
67
68private:
69 libusb_device_handle* handle{};
70};
71
72GCAdapter::GCAdapter(const std::string& input_engine_) : InputEngine(input_engine_) {
73 if (usb_adapter_handle) {
19 return; 74 return;
20 } 75 }
21 LOG_INFO(Input, "GC Adapter Initialization started"); 76 LOG_DEBUG(Input, "Initialization started");
22 77
23 const int init_res = libusb_init(&libusb_ctx); 78 libusb_ctx = std::make_unique<LibUSBContext>();
79 const int init_res = libusb_ctx->InitResult();
24 if (init_res == LIBUSB_SUCCESS) { 80 if (init_res == LIBUSB_SUCCESS) {
25 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 81 adapter_scan_thread =
82 std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); });
26 } else { 83 } else {
27 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 84 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
28 } 85 }
29} 86}
30 87
31Adapter::~Adapter() { 88GCAdapter::~GCAdapter() {
32 Reset(); 89 Reset();
33} 90}
34 91
35void Adapter::AdapterInputThread() { 92void GCAdapter::AdapterInputThread(std::stop_token stop_token) {
36 LOG_DEBUG(Input, "GC Adapter input thread started"); 93 LOG_DEBUG(Input, "Input thread started");
94 Common::SetCurrentThreadName("yuzu:input:GCAdapter");
37 s32 payload_size{}; 95 s32 payload_size{};
38 AdapterPayload adapter_payload{}; 96 AdapterPayload adapter_payload{};
39 97
40 if (adapter_scan_thread.joinable()) { 98 adapter_scan_thread = {};
41 adapter_scan_thread.join();
42 }
43 99
44 while (adapter_input_thread_running) { 100 while (!stop_token.stop_requested()) {
45 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 101 libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(),
46 static_cast<s32>(adapter_payload.size()), &payload_size, 16); 102 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
47 if (IsPayloadCorrect(adapter_payload, payload_size)) { 103 if (IsPayloadCorrect(adapter_payload, payload_size)) {
48 UpdateControllers(adapter_payload); 104 UpdateControllers(adapter_payload);
@@ -52,19 +108,20 @@ void Adapter::AdapterInputThread() {
52 } 108 }
53 109
54 if (restart_scan_thread) { 110 if (restart_scan_thread) {
55 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 111 adapter_scan_thread =
112 std::jthread([this](std::stop_token token) { AdapterScanThread(token); });
56 restart_scan_thread = false; 113 restart_scan_thread = false;
57 } 114 }
58} 115}
59 116
60bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) { 117bool GCAdapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
61 if (payload_size != static_cast<s32>(adapter_payload.size()) || 118 if (payload_size != static_cast<s32>(adapter_payload.size()) ||
62 adapter_payload[0] != LIBUSB_DT_HID) { 119 adapter_payload[0] != LIBUSB_DT_HID) {
63 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size, 120 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
64 adapter_payload[0]); 121 adapter_payload[0]);
65 if (input_error_counter++ > 20) { 122 if (input_error_counter++ > 20) {
66 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?"); 123 LOG_ERROR(Input, "Timeout, Is the adapter connected?");
67 adapter_input_thread_running = false; 124 adapter_input_thread.request_stop();
68 restart_scan_thread = true; 125 restart_scan_thread = true;
69 } 126 }
70 return false; 127 return false;
@@ -74,7 +131,7 @@ bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payloa
74 return true; 131 return true;
75} 132}
76 133
77void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) { 134void GCAdapter::UpdateControllers(const AdapterPayload& adapter_payload) {
78 for (std::size_t port = 0; port < pads.size(); ++port) { 135 for (std::size_t port = 0; port < pads.size(); ++port) {
79 const std::size_t offset = 1 + (9 * port); 136 const std::size_t offset = 1 + (9 * port);
80 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); 137 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
@@ -84,23 +141,24 @@ void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
84 const u8 b2 = adapter_payload[offset + 2]; 141 const u8 b2 = adapter_payload[offset + 2];
85 UpdateStateButtons(port, b1, b2); 142 UpdateStateButtons(port, b1, b2);
86 UpdateStateAxes(port, adapter_payload); 143 UpdateStateAxes(port, adapter_payload);
87 if (configuring) {
88 UpdateYuzuSettings(port);
89 }
90 } 144 }
91 } 145 }
92} 146}
93 147
94void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) { 148void GCAdapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
95 if (pads[port].type == pad_type) { 149 if (pads[port].type == pad_type) {
96 return; 150 return;
97 } 151 }
98 // Device changed reset device and set new type 152 // Device changed reset device and set new type
99 ResetDevice(port); 153 pads[port].axis_origin = {};
154 pads[port].reset_origin_counter = {};
155 pads[port].enable_vibration = {};
156 pads[port].rumble_amplitude = {};
100 pads[port].type = pad_type; 157 pads[port].type = pad_type;
101} 158}
102 159
103void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) { 160void GCAdapter::UpdateStateButtons(std::size_t port, [[maybe_unused]] u8 b1,
161 [[maybe_unused]] u8 b2) {
104 if (port >= pads.size()) { 162 if (port >= pads.size()) {
105 return; 163 return;
106 } 164 }
@@ -116,25 +174,21 @@ void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
116 PadButton::TriggerR, 174 PadButton::TriggerR,
117 PadButton::TriggerL, 175 PadButton::TriggerL,
118 }; 176 };
119 pads[port].buttons = 0; 177
120 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 178 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
121 if ((b1 & (1U << i)) != 0) { 179 const bool button_status = (b1 & (1U << i)) != 0;
122 pads[port].buttons = 180 const int button = static_cast<int>(b1_buttons[i]);
123 static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i])); 181 SetButton(pads[port].identifier, button, button_status);
124 pads[port].last_button = b1_buttons[i];
125 }
126 } 182 }
127 183
128 for (std::size_t j = 0; j < b2_buttons.size(); ++j) { 184 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
129 if ((b2 & (1U << j)) != 0) { 185 const bool button_status = (b2 & (1U << j)) != 0;
130 pads[port].buttons = 186 const int button = static_cast<int>(b2_buttons[j]);
131 static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j])); 187 SetButton(pads[port].identifier, button, button_status);
132 pads[port].last_button = b2_buttons[j];
133 }
134 } 188 }
135} 189}
136 190
137void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) { 191void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
138 if (port >= pads.size()) { 192 if (port >= pads.size()) {
139 return; 193 return;
140 } 194 }
@@ -155,134 +209,63 @@ void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_pa
155 pads[port].axis_origin[index] = axis_value; 209 pads[port].axis_origin[index] = axis_value;
156 pads[port].reset_origin_counter++; 210 pads[port].reset_origin_counter++;
157 } 211 }
158 pads[port].axis_values[index] = 212 const f32 axis_status = (axis_value - pads[port].axis_origin[index]) / 100.0f;
159 static_cast<s16>(axis_value - pads[port].axis_origin[index]); 213 SetAxis(pads[port].identifier, static_cast<int>(index), axis_status);
160 }
161}
162
163void Adapter::UpdateYuzuSettings(std::size_t port) {
164 if (port >= pads.size()) {
165 return;
166 }
167
168 constexpr u8 axis_threshold = 50;
169 GCPadStatus pad_status = {.port = port};
170
171 if (pads[port].buttons != 0) {
172 pad_status.button = pads[port].last_button;
173 pad_queue.Push(pad_status);
174 }
175
176 // Accounting for a threshold here to ensure an intentional press
177 for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
178 const s16 value = pads[port].axis_values[i];
179
180 if (value > axis_threshold || value < -axis_threshold) {
181 pad_status.axis = static_cast<PadAxes>(i);
182 pad_status.axis_value = value;
183 pad_status.axis_threshold = axis_threshold;
184 pad_queue.Push(pad_status);
185 }
186 }
187}
188
189void Adapter::UpdateVibrations() {
190 // Use 8 states to keep the switching between on/off fast enough for
191 // a human to not notice the difference between switching from on/off
192 // More states = more rumble strengths = slower update time
193 constexpr u8 vibration_states = 8;
194
195 vibration_counter = (vibration_counter + 1) % vibration_states;
196
197 for (GCController& pad : pads) {
198 const bool vibrate = pad.rumble_amplitude > vibration_counter;
199 vibration_changed |= vibrate != pad.enable_vibration;
200 pad.enable_vibration = vibrate;
201 }
202 SendVibrations();
203}
204
205void Adapter::SendVibrations() {
206 if (!rumble_enabled || !vibration_changed) {
207 return;
208 }
209 s32 size{};
210 constexpr u8 rumble_command = 0x11;
211 const u8 p1 = pads[0].enable_vibration;
212 const u8 p2 = pads[1].enable_vibration;
213 const u8 p3 = pads[2].enable_vibration;
214 const u8 p4 = pads[3].enable_vibration;
215 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
216 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
217 static_cast<s32>(payload.size()), &size, 16);
218 if (err) {
219 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
220 if (output_error_counter++ > 5) {
221 LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
222 rumble_enabled = false;
223 }
224 return;
225 } 214 }
226 output_error_counter = 0;
227 vibration_changed = false;
228} 215}
229 216
230bool Adapter::RumblePlay(std::size_t port, u8 amplitude) { 217void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
231 pads[port].rumble_amplitude = amplitude; 218 Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter");
232 219 usb_adapter_handle = nullptr;
233 return rumble_enabled; 220 pads = {};
234} 221 while (!stop_token.stop_requested() && !Setup()) {
235 222 std::this_thread::sleep_for(std::chrono::seconds(2));
236void Adapter::AdapterScanThread() {
237 adapter_scan_thread_running = true;
238 adapter_input_thread_running = false;
239 if (adapter_input_thread.joinable()) {
240 adapter_input_thread.join();
241 }
242 ClearLibusbHandle();
243 ResetDevices();
244 while (adapter_scan_thread_running && !adapter_input_thread_running) {
245 Setup();
246 std::this_thread::sleep_for(std::chrono::seconds(1));
247 } 223 }
248} 224}
249 225
250void Adapter::Setup() { 226bool GCAdapter::Setup() {
251 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337); 227 constexpr u16 nintendo_vid = 0x057e;
252 228 constexpr u16 gc_adapter_pid = 0x0337;
253 if (usb_adapter_handle == NULL) { 229 usb_adapter_handle =
254 return; 230 std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), nintendo_vid, gc_adapter_pid);
231 if (!usb_adapter_handle->get()) {
232 return false;
255 } 233 }
256 if (!CheckDeviceAccess()) { 234 if (!CheckDeviceAccess()) {
257 ClearLibusbHandle(); 235 usb_adapter_handle = nullptr;
258 return; 236 return false;
259 } 237 }
260 238
261 libusb_device* device = libusb_get_device(usb_adapter_handle); 239 libusb_device* const device = libusb_get_device(usb_adapter_handle->get());
262 240
263 LOG_INFO(Input, "GC adapter is now connected"); 241 LOG_INFO(Input, "GC adapter is now connected");
264 // GC Adapter found and accessible, registering it 242 // GC Adapter found and accessible, registering it
265 if (GetGCEndpoint(device)) { 243 if (GetGCEndpoint(device)) {
266 adapter_scan_thread_running = false;
267 adapter_input_thread_running = true;
268 rumble_enabled = true; 244 rumble_enabled = true;
269 input_error_counter = 0; 245 input_error_counter = 0;
270 output_error_counter = 0; 246 output_error_counter = 0;
271 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
272 }
273}
274 247
275bool Adapter::CheckDeviceAccess() { 248 std::size_t port = 0;
276 // This fixes payload problems from offbrand GCAdapters 249 for (GCController& pad : pads) {
277 const s32 control_transfer_error = 250 pad.identifier = {
278 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000); 251 .guid = Common::UUID{Common::INVALID_UUID},
279 if (control_transfer_error < 0) { 252 .port = port++,
280 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); 253 .pad = 0,
254 };
255 PreSetController(pad.identifier);
256 }
257
258 adapter_input_thread =
259 std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); });
260 return true;
281 } 261 }
262 return false;
263}
282 264
283 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 265bool GCAdapter::CheckDeviceAccess() {
266 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0);
284 if (kernel_driver_error == 1) { 267 if (kernel_driver_error == 1) {
285 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 268 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0);
286 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 269 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
287 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", 270 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
288 kernel_driver_error); 271 kernel_driver_error);
@@ -290,23 +273,28 @@ bool Adapter::CheckDeviceAccess() {
290 } 273 }
291 274
292 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 275 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
293 libusb_close(usb_adapter_handle);
294 usb_adapter_handle = nullptr; 276 usb_adapter_handle = nullptr;
295 return false; 277 return false;
296 } 278 }
297 279
298 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); 280 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0);
299 if (interface_claim_error) { 281 if (interface_claim_error) {
300 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); 282 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
301 libusb_close(usb_adapter_handle);
302 usb_adapter_handle = nullptr; 283 usb_adapter_handle = nullptr;
303 return false; 284 return false;
304 } 285 }
305 286
287 // This fixes payload problems from offbrand GCAdapters
288 const s32 control_transfer_error =
289 libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
290 if (control_transfer_error < 0) {
291 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
292 }
293
306 return true; 294 return true;
307} 295}
308 296
309bool Adapter::GetGCEndpoint(libusb_device* device) { 297bool GCAdapter::GetGCEndpoint(libusb_device* device) {
310 libusb_config_descriptor* config = nullptr; 298 libusb_config_descriptor* config = nullptr;
311 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); 299 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
312 if (config_descriptor_return != LIBUSB_SUCCESS) { 300 if (config_descriptor_return != LIBUSB_SUCCESS) {
@@ -332,77 +320,95 @@ bool Adapter::GetGCEndpoint(libusb_device* device) {
332 // This transfer seems to be responsible for clearing the state of the adapter 320 // This transfer seems to be responsible for clearing the state of the adapter
333 // Used to clear the "busy" state of when the device is unexpectedly unplugged 321 // Used to clear the "busy" state of when the device is unexpectedly unplugged
334 unsigned char clear_payload = 0x13; 322 unsigned char clear_payload = 0x13;
335 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 323 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload,
336 sizeof(clear_payload), nullptr, 16); 324 sizeof(clear_payload), nullptr, 16);
337 return true; 325 return true;
338} 326}
339 327
340void Adapter::JoinThreads() { 328Common::Input::VibrationError GCAdapter::SetRumble(const PadIdentifier& identifier,
341 restart_scan_thread = false; 329 const Common::Input::VibrationStatus vibration) {
342 adapter_input_thread_running = false; 330 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
343 adapter_scan_thread_running = false; 331 const auto processed_amplitude =
332 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
344 333
345 if (adapter_scan_thread.joinable()) { 334 pads[identifier.port].rumble_amplitude = processed_amplitude;
346 adapter_scan_thread.join();
347 }
348 335
349 if (adapter_input_thread.joinable()) { 336 if (!rumble_enabled) {
350 adapter_input_thread.join(); 337 return Common::Input::VibrationError::Disabled;
351 } 338 }
339 return Common::Input::VibrationError::None;
352} 340}
353 341
354void Adapter::ClearLibusbHandle() { 342void GCAdapter::UpdateVibrations() {
355 if (usb_adapter_handle) { 343 // Use 8 states to keep the switching between on/off fast enough for
356 libusb_release_interface(usb_adapter_handle, 1); 344 // a human to feel different vibration strenght
357 libusb_close(usb_adapter_handle); 345 // More states == more rumble strengths == slower update time
358 usb_adapter_handle = nullptr; 346 constexpr u8 vibration_states = 8;
347
348 vibration_counter = (vibration_counter + 1) % vibration_states;
349
350 for (GCController& pad : pads) {
351 const bool vibrate = pad.rumble_amplitude > vibration_counter;
352 vibration_changed |= vibrate != pad.enable_vibration;
353 pad.enable_vibration = vibrate;
359 } 354 }
355 SendVibrations();
360} 356}
361 357
362void Adapter::ResetDevices() { 358void GCAdapter::SendVibrations() {
363 for (std::size_t i = 0; i < pads.size(); ++i) { 359 if (!rumble_enabled || !vibration_changed) {
364 ResetDevice(i); 360 return;
361 }
362 s32 size{};
363 constexpr u8 rumble_command = 0x11;
364 const u8 p1 = pads[0].enable_vibration;
365 const u8 p2 = pads[1].enable_vibration;
366 const u8 p3 = pads[2].enable_vibration;
367 const u8 p4 = pads[3].enable_vibration;
368 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
369 const int err =
370 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(),
371 static_cast<s32>(payload.size()), &size, 16);
372 if (err) {
373 LOG_DEBUG(Input, "Libusb write failed: {}", libusb_error_name(err));
374 if (output_error_counter++ > 5) {
375 LOG_ERROR(Input, "Output timeout, Rumble disabled");
376 rumble_enabled = false;
377 }
378 return;
365 } 379 }
380 output_error_counter = 0;
381 vibration_changed = false;
366} 382}
367 383
368void Adapter::ResetDevice(std::size_t port) { 384bool GCAdapter::DeviceConnected(std::size_t port) const {
369 pads[port].type = ControllerTypes::None; 385 return pads[port].type != ControllerTypes::None;
370 pads[port].enable_vibration = false;
371 pads[port].rumble_amplitude = 0;
372 pads[port].buttons = 0;
373 pads[port].last_button = PadButton::Undefined;
374 pads[port].axis_values.fill(0);
375 pads[port].reset_origin_counter = 0;
376} 386}
377 387
378void Adapter::Reset() { 388void GCAdapter::Reset() {
379 JoinThreads(); 389 adapter_scan_thread = {};
380 ClearLibusbHandle(); 390 adapter_input_thread = {};
381 ResetDevices(); 391 usb_adapter_handle = nullptr;
382 392 pads = {};
383 if (libusb_ctx) { 393 libusb_ctx = nullptr;
384 libusb_exit(libusb_ctx);
385 }
386} 394}
387 395
388std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 396std::vector<Common::ParamPackage> GCAdapter::GetInputDevices() const {
389 std::vector<Common::ParamPackage> devices; 397 std::vector<Common::ParamPackage> devices;
390 for (std::size_t port = 0; port < pads.size(); ++port) { 398 for (std::size_t port = 0; port < pads.size(); ++port) {
391 if (!DeviceConnected(port)) { 399 if (!DeviceConnected(port)) {
392 continue; 400 continue;
393 } 401 }
394 std::string name = fmt::format("Gamecube Controller {}", port + 1); 402 Common::ParamPackage identifier{};
395 devices.emplace_back(Common::ParamPackage{ 403 identifier.Set("engine", GetEngineName());
396 {"class", "gcpad"}, 404 identifier.Set("display", fmt::format("Gamecube Controller {}", port + 1));
397 {"display", std::move(name)}, 405 identifier.Set("port", static_cast<int>(port));
398 {"port", std::to_string(port)}, 406 devices.emplace_back(identifier);
399 });
400 } 407 }
401 return devices; 408 return devices;
402} 409}
403 410
404InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice( 411ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& params) {
405 const Common::ParamPackage& params) const {
406 // This list is missing ZL/ZR since those are not considered buttons. 412 // This list is missing ZL/ZR since those are not considered buttons.
407 // We will add those afterwards 413 // We will add those afterwards
408 // This list also excludes any button that can't be really mapped 414 // This list also excludes any button that can't be really mapped
@@ -425,47 +431,49 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
425 return {}; 431 return {};
426 } 432 }
427 433
428 InputCommon::ButtonMapping mapping{}; 434 ButtonMapping mapping{};
429 for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) { 435 for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
430 Common::ParamPackage button_params({{"engine", "gcpad"}}); 436 Common::ParamPackage button_params{};
437 button_params.Set("engine", GetEngineName());
431 button_params.Set("port", params.Get("port", 0)); 438 button_params.Set("port", params.Get("port", 0));
432 button_params.Set("button", static_cast<int>(gcadapter_button)); 439 button_params.Set("button", static_cast<int>(gcadapter_button));
433 mapping.insert_or_assign(switch_button, std::move(button_params)); 440 mapping.insert_or_assign(switch_button, std::move(button_params));
434 } 441 }
435 442
436 // Add the missing bindings for ZL/ZR 443 // Add the missing bindings for ZL/ZR
437 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2> 444 static constexpr std::array<std::tuple<Settings::NativeButton::Values, PadButton, PadAxes>, 2>
438 switch_to_gcadapter_axis = { 445 switch_to_gcadapter_axis = {
439 std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft}, 446 std::tuple{Settings::NativeButton::ZL, PadButton::TriggerL, PadAxes::TriggerLeft},
440 {Settings::NativeButton::ZR, PadAxes::TriggerRight}, 447 {Settings::NativeButton::ZR, PadButton::TriggerR, PadAxes::TriggerRight},
441 }; 448 };
442 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { 449 for (const auto& [switch_button, gcadapter_buton, gcadapter_axis] : switch_to_gcadapter_axis) {
443 Common::ParamPackage button_params({{"engine", "gcpad"}}); 450 Common::ParamPackage button_params{};
451 button_params.Set("engine", GetEngineName());
444 button_params.Set("port", params.Get("port", 0)); 452 button_params.Set("port", params.Get("port", 0));
445 button_params.Set("button", static_cast<s32>(PadButton::Stick)); 453 button_params.Set("button", static_cast<s32>(gcadapter_buton));
446 button_params.Set("axis", static_cast<s32>(gcadapter_axis)); 454 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
447 button_params.Set("threshold", 0.5f); 455 button_params.Set("threshold", 0.5f);
456 button_params.Set("range", 1.9f);
448 button_params.Set("direction", "+"); 457 button_params.Set("direction", "+");
449 mapping.insert_or_assign(switch_button, std::move(button_params)); 458 mapping.insert_or_assign(switch_button, std::move(button_params));
450 } 459 }
451 return mapping; 460 return mapping;
452} 461}
453 462
454InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice( 463AnalogMapping GCAdapter::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
455 const Common::ParamPackage& params) const {
456 if (!params.Has("port")) { 464 if (!params.Has("port")) {
457 return {}; 465 return {};
458 } 466 }
459 467
460 InputCommon::AnalogMapping mapping = {}; 468 AnalogMapping mapping = {};
461 Common::ParamPackage left_analog_params; 469 Common::ParamPackage left_analog_params;
462 left_analog_params.Set("engine", "gcpad"); 470 left_analog_params.Set("engine", GetEngineName());
463 left_analog_params.Set("port", params.Get("port", 0)); 471 left_analog_params.Set("port", params.Get("port", 0));
464 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX)); 472 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
465 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY)); 473 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
466 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); 474 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
467 Common::ParamPackage right_analog_params; 475 Common::ParamPackage right_analog_params;
468 right_analog_params.Set("engine", "gcpad"); 476 right_analog_params.Set("engine", GetEngineName());
469 right_analog_params.Set("port", params.Get("port", 0)); 477 right_analog_params.Set("port", params.Get("port", 0));
470 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX)); 478 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
471 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY)); 479 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
@@ -473,34 +481,47 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
473 return mapping; 481 return mapping;
474} 482}
475 483
476bool Adapter::DeviceConnected(std::size_t port) const { 484Common::Input::ButtonNames GCAdapter::GetUIButtonName(const Common::ParamPackage& params) const {
477 return pads[port].type != ControllerTypes::None; 485 PadButton button = static_cast<PadButton>(params.Get("button", 0));
478} 486 switch (button) {
479 487 case PadButton::ButtonLeft:
480void Adapter::BeginConfiguration() { 488 return Common::Input::ButtonNames::ButtonLeft;
481 pad_queue.Clear(); 489 case PadButton::ButtonRight:
482 configuring = true; 490 return Common::Input::ButtonNames::ButtonRight;
483} 491 case PadButton::ButtonDown:
484 492 return Common::Input::ButtonNames::ButtonDown;
485void Adapter::EndConfiguration() { 493 case PadButton::ButtonUp:
486 pad_queue.Clear(); 494 return Common::Input::ButtonNames::ButtonUp;
487 configuring = false; 495 case PadButton::TriggerZ:
488} 496 return Common::Input::ButtonNames::TriggerZ;
489 497 case PadButton::TriggerR:
490Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() { 498 return Common::Input::ButtonNames::TriggerR;
491 return pad_queue; 499 case PadButton::TriggerL:
492} 500 return Common::Input::ButtonNames::TriggerL;
493 501 case PadButton::ButtonA:
494const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const { 502 return Common::Input::ButtonNames::ButtonA;
495 return pad_queue; 503 case PadButton::ButtonB:
504 return Common::Input::ButtonNames::ButtonB;
505 case PadButton::ButtonX:
506 return Common::Input::ButtonNames::ButtonX;
507 case PadButton::ButtonY:
508 return Common::Input::ButtonNames::ButtonY;
509 case PadButton::ButtonStart:
510 return Common::Input::ButtonNames::ButtonStart;
511 default:
512 return Common::Input::ButtonNames::Undefined;
513 }
496} 514}
497 515
498GCController& Adapter::GetPadState(std::size_t port) { 516Common::Input::ButtonNames GCAdapter::GetUIName(const Common::ParamPackage& params) const {
499 return pads.at(port); 517 if (params.Has("button")) {
500} 518 return GetUIButtonName(params);
519 }
520 if (params.Has("axis")) {
521 return Common::Input::ButtonNames::Value;
522 }
501 523
502const GCController& Adapter::GetPadState(std::size_t port) const { 524 return Common::Input::ButtonNames::Invalid;
503 return pads.at(port);
504} 525}
505 526
506} // namespace GCAdapter 527} // namespace InputCommon
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
new file mode 100644
index 000000000..8dc51d2e5
--- /dev/null
+++ b/src/input_common/drivers/gc_adapter.h
@@ -0,0 +1,135 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <mutex>
10#include <stop_token>
11#include <string>
12#include <thread>
13
14#include "input_common/input_engine.h"
15
16struct libusb_context;
17struct libusb_device;
18struct libusb_device_handle;
19
20namespace InputCommon {
21
22class LibUSBContext;
23class LibUSBDeviceHandle;
24
25class GCAdapter : public InputCommon::InputEngine {
26public:
27 explicit GCAdapter(const std::string& input_engine_);
28 ~GCAdapter();
29
30 Common::Input::VibrationError SetRumble(
31 const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
32
33 /// Used for automapping features
34 std::vector<Common::ParamPackage> GetInputDevices() const override;
35 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
36 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
37 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
38
39private:
40 enum class PadButton {
41 Undefined = 0x0000,
42 ButtonLeft = 0x0001,
43 ButtonRight = 0x0002,
44 ButtonDown = 0x0004,
45 ButtonUp = 0x0008,
46 TriggerZ = 0x0010,
47 TriggerR = 0x0020,
48 TriggerL = 0x0040,
49 ButtonA = 0x0100,
50 ButtonB = 0x0200,
51 ButtonX = 0x0400,
52 ButtonY = 0x0800,
53 ButtonStart = 0x1000,
54 };
55
56 enum class PadAxes : u8 {
57 StickX,
58 StickY,
59 SubstickX,
60 SubstickY,
61 TriggerLeft,
62 TriggerRight,
63 Undefined,
64 };
65
66 enum class ControllerTypes {
67 None,
68 Wired,
69 Wireless,
70 };
71
72 struct GCController {
73 ControllerTypes type = ControllerTypes::None;
74 PadIdentifier identifier{};
75 bool enable_vibration = false;
76 u8 rumble_amplitude{};
77 std::array<u8, 6> axis_origin{};
78 u8 reset_origin_counter{};
79 };
80
81 using AdapterPayload = std::array<u8, 37>;
82
83 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
84 void UpdateControllers(const AdapterPayload& adapter_payload);
85 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
86 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
87
88 void AdapterInputThread(std::stop_token stop_token);
89
90 void AdapterScanThread(std::stop_token stop_token);
91
92 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
93
94 /// For use in initialization, querying devices to find the adapter
95 bool Setup();
96
97 /// Returns true if we successfully gain access to GC Adapter
98 bool CheckDeviceAccess();
99
100 /// Captures GC Adapter endpoint address
101 /// Returns true if the endpoint was set correctly
102 bool GetGCEndpoint(libusb_device* device);
103
104 /// Returns true if there is a device connected to port
105 bool DeviceConnected(std::size_t port) const;
106
107 /// For shutting down, clear all data, join all threads, release usb
108 void Reset();
109
110 void UpdateVibrations();
111
112 /// Updates vibration state of all controllers
113 void SendVibrations();
114
115 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
116
117 std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
118 std::array<GCController, 4> pads;
119
120 std::jthread adapter_input_thread;
121 std::jthread adapter_scan_thread;
122 bool restart_scan_thread{};
123
124 std::unique_ptr<LibUSBContext> libusb_ctx;
125
126 u8 input_endpoint{0};
127 u8 output_endpoint{0};
128 u8 input_error_counter{0};
129 u8 output_error_counter{0};
130 int vibration_counter{0};
131
132 bool rumble_enabled{true};
133 bool vibration_changed{true};
134};
135} // namespace InputCommon
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp
new file mode 100644
index 000000000..23b0c0ccf
--- /dev/null
+++ b/src/input_common/drivers/keyboard.cpp
@@ -0,0 +1,112 @@
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/param_package.h"
6#include "common/settings_input.h"
7#include "input_common/drivers/keyboard.h"
8
9namespace InputCommon {
10
11constexpr PadIdentifier key_identifier = {
12 .guid = Common::UUID{Common::INVALID_UUID},
13 .port = 0,
14 .pad = 0,
15};
16constexpr PadIdentifier keyboard_key_identifier = {
17 .guid = Common::UUID{Common::INVALID_UUID},
18 .port = 1,
19 .pad = 0,
20};
21constexpr PadIdentifier keyboard_modifier_identifier = {
22 .guid = Common::UUID{Common::INVALID_UUID},
23 .port = 1,
24 .pad = 1,
25};
26
27Keyboard::Keyboard(const std::string& input_engine_) : InputEngine(input_engine_) {
28 // Keyboard is broken into 3 diferent sets:
29 // key: Unfiltered intended for controllers.
30 // keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
31 // keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard
32 // emulation.
33 PreSetController(key_identifier);
34 PreSetController(keyboard_key_identifier);
35 PreSetController(keyboard_modifier_identifier);
36}
37
38void Keyboard::PressKey(int key_code) {
39 SetButton(key_identifier, key_code, true);
40}
41
42void Keyboard::ReleaseKey(int key_code) {
43 SetButton(key_identifier, key_code, false);
44}
45
46void Keyboard::PressKeyboardKey(int key_index) {
47 if (key_index == Settings::NativeKeyboard::None) {
48 return;
49 }
50 SetButton(keyboard_key_identifier, key_index, true);
51}
52
53void Keyboard::ReleaseKeyboardKey(int key_index) {
54 if (key_index == Settings::NativeKeyboard::None) {
55 return;
56 }
57 SetButton(keyboard_key_identifier, key_index, false);
58}
59
60void Keyboard::SetKeyboardModifiers(int key_modifiers) {
61 for (int i = 0; i < 32; ++i) {
62 bool key_value = ((key_modifiers >> i) & 0x1) != 0;
63 SetButton(keyboard_modifier_identifier, i, key_value);
64 // Use the modifier to press the key button equivalent
65 switch (i) {
66 case Settings::NativeKeyboard::LeftControl:
67 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftControlKey, key_value);
68 break;
69 case Settings::NativeKeyboard::LeftShift:
70 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftShiftKey, key_value);
71 break;
72 case Settings::NativeKeyboard::LeftAlt:
73 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftAltKey, key_value);
74 break;
75 case Settings::NativeKeyboard::LeftMeta:
76 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftMetaKey, key_value);
77 break;
78 case Settings::NativeKeyboard::RightControl:
79 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightControlKey,
80 key_value);
81 break;
82 case Settings::NativeKeyboard::RightShift:
83 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightShiftKey, key_value);
84 break;
85 case Settings::NativeKeyboard::RightAlt:
86 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightAltKey, key_value);
87 break;
88 case Settings::NativeKeyboard::RightMeta:
89 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightMetaKey, key_value);
90 break;
91 default:
92 // Other modifier keys should be pressed with PressKey since they stay enabled until
93 // next press
94 break;
95 }
96 }
97}
98
99void Keyboard::ReleaseAllKeys() {
100 ResetButtonState();
101}
102
103std::vector<Common::ParamPackage> Keyboard::GetInputDevices() const {
104 std::vector<Common::ParamPackage> devices;
105 devices.emplace_back(Common::ParamPackage{
106 {"engine", GetEngineName()},
107 {"display", "Keyboard Only"},
108 });
109 return devices;
110}
111
112} // namespace InputCommon
diff --git a/src/input_common/drivers/keyboard.h b/src/input_common/drivers/keyboard.h
new file mode 100644
index 000000000..ad123b136
--- /dev/null
+++ b/src/input_common/drivers/keyboard.h
@@ -0,0 +1,56 @@
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 "input_common/input_engine.h"
8
9namespace InputCommon {
10
11/**
12 * A button device factory representing a keyboard. It receives keyboard events and forward them
13 * to all button devices it created.
14 */
15class Keyboard final : public InputCommon::InputEngine {
16public:
17 explicit Keyboard(const std::string& input_engine_);
18
19 /**
20 * Sets the status of all buttons bound with the key to pressed
21 * @param key_code the code of the key to press
22 */
23 void PressKey(int key_code);
24
25 /**
26 * Sets the status of all buttons bound with the key to released
27 * @param key_code the code of the key to release
28 */
29 void ReleaseKey(int key_code);
30
31 /**
32 * Sets the status of the keyboard key to pressed
33 * @param key_index index of the key to press
34 */
35 void PressKeyboardKey(int key_index);
36
37 /**
38 * Sets the status of the keyboard key to released
39 * @param key_index index of the key to release
40 */
41 void ReleaseKeyboardKey(int key_index);
42
43 /**
44 * Sets the status of all keyboard modifier keys
45 * @param key_modifiers the code of the key to release
46 */
47 void SetKeyboardModifiers(int key_modifiers);
48
49 /// Sets all keys to the non pressed state
50 void ReleaseAllKeys();
51
52 /// Used for automapping features
53 std::vector<Common::ParamPackage> GetInputDevices() const override;
54};
55
56} // namespace InputCommon
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
new file mode 100644
index 000000000..752118e97
--- /dev/null
+++ b/src/input_common/drivers/mouse.cpp
@@ -0,0 +1,185 @@
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 <stop_token>
6#include <thread>
7#include <fmt/format.h>
8
9#include "common/param_package.h"
10#include "common/settings.h"
11#include "common/thread.h"
12#include "input_common/drivers/mouse.h"
13
14namespace InputCommon {
15constexpr int mouse_axis_x = 0;
16constexpr int mouse_axis_y = 1;
17constexpr int wheel_axis_x = 2;
18constexpr int wheel_axis_y = 3;
19constexpr int touch_axis_x = 10;
20constexpr int touch_axis_y = 11;
21constexpr PadIdentifier identifier = {
22 .guid = Common::UUID{Common::INVALID_UUID},
23 .port = 0,
24 .pad = 0,
25};
26
27Mouse::Mouse(const std::string& input_engine_) : InputEngine(input_engine_) {
28 PreSetController(identifier);
29 PreSetAxis(identifier, mouse_axis_x);
30 PreSetAxis(identifier, mouse_axis_y);
31 PreSetAxis(identifier, wheel_axis_x);
32 PreSetAxis(identifier, wheel_axis_y);
33 PreSetAxis(identifier, touch_axis_x);
34 PreSetAxis(identifier, touch_axis_x);
35 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
36}
37
38void Mouse::UpdateThread(std::stop_token stop_token) {
39 Common::SetCurrentThreadName("yuzu:input:Mouse");
40 constexpr int update_time = 10;
41 while (!stop_token.stop_requested()) {
42 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
43 // Slow movement by 4%
44 last_mouse_change *= 0.96f;
45 const float sensitivity =
46 Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f;
47 SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity);
48 SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity);
49 }
50
51 if (mouse_panning_timout++ > 20) {
52 StopPanning();
53 }
54 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
55 }
56}
57
58void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) {
59 // If native mouse is enabled just set the screen coordinates
60 if (Settings::values.mouse_enabled) {
61 SetAxis(identifier, mouse_axis_x, touch_x);
62 SetAxis(identifier, mouse_axis_y, touch_y);
63 return;
64 }
65
66 SetAxis(identifier, touch_axis_x, touch_x);
67 SetAxis(identifier, touch_axis_y, touch_y);
68
69 if (Settings::values.mouse_panning) {
70 auto mouse_change =
71 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
72 mouse_panning_timout = 0;
73
74 const auto move_distance = mouse_change.Length();
75 if (move_distance == 0) {
76 return;
77 }
78
79 // Make slow movements at least 3 units on lenght
80 if (move_distance < 3.0f) {
81 // Normalize value
82 mouse_change /= move_distance;
83 mouse_change *= 3.0f;
84 }
85
86 // Average mouse movements
87 last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f);
88
89 const auto last_move_distance = last_mouse_change.Length();
90
91 // Make fast movements clamp to 8 units on lenght
92 if (last_move_distance > 8.0f) {
93 // Normalize value
94 last_mouse_change /= last_move_distance;
95 last_mouse_change *= 8.0f;
96 }
97
98 // Ignore average if it's less than 1 unit and use current movement value
99 if (last_move_distance < 1.0f) {
100 last_mouse_change = mouse_change / mouse_change.Length();
101 }
102
103 return;
104 }
105
106 if (button_pressed) {
107 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
108 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f;
109 SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity);
110 SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity);
111 }
112}
113
114void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) {
115 SetAxis(identifier, touch_axis_x, touch_x);
116 SetAxis(identifier, touch_axis_y, touch_y);
117 SetButton(identifier, static_cast<int>(button), true);
118 // Set initial analog parameters
119 mouse_origin = {x, y};
120 last_mouse_position = {x, y};
121 button_pressed = true;
122}
123
124void Mouse::ReleaseButton(MouseButton button) {
125 SetButton(identifier, static_cast<int>(button), false);
126
127 if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
128 SetAxis(identifier, mouse_axis_x, 0);
129 SetAxis(identifier, mouse_axis_y, 0);
130 }
131 button_pressed = false;
132}
133
134void Mouse::MouseWheelChange(int x, int y) {
135 wheel_position.x += x;
136 wheel_position.y += y;
137 SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
138 SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
139}
140
141void Mouse::ReleaseAllButtons() {
142 ResetButtonState();
143 button_pressed = false;
144}
145
146void Mouse::StopPanning() {
147 last_mouse_change = {};
148}
149
150std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
151 std::vector<Common::ParamPackage> devices;
152 devices.emplace_back(Common::ParamPackage{
153 {"engine", GetEngineName()},
154 {"display", "Keyboard/Mouse"},
155 });
156 return devices;
157}
158
159AnalogMapping Mouse::GetAnalogMappingForDevice(
160 [[maybe_unused]] const Common::ParamPackage& params) {
161 // Only overwrite different buttons from default
162 AnalogMapping mapping = {};
163 Common::ParamPackage right_analog_params;
164 right_analog_params.Set("engine", GetEngineName());
165 right_analog_params.Set("axis_x", 0);
166 right_analog_params.Set("axis_y", 1);
167 right_analog_params.Set("threshold", 0.5f);
168 right_analog_params.Set("range", 1.0f);
169 right_analog_params.Set("deadzone", 0.0f);
170 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
171 return mapping;
172}
173
174Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const {
175 if (params.Has("button")) {
176 return Common::Input::ButtonNames::Value;
177 }
178 if (params.Has("axis")) {
179 return Common::Input::ButtonNames::Value;
180 }
181
182 return Common::Input::ButtonNames::Invalid;
183}
184
185} // namespace InputCommon
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
new file mode 100644
index 000000000..4a1fd2fd9
--- /dev/null
+++ b/src/input_common/drivers/mouse.h
@@ -0,0 +1,81 @@
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 <stop_token>
8#include <thread>
9
10#include "common/vector_math.h"
11#include "input_common/input_engine.h"
12
13namespace InputCommon {
14
15enum class MouseButton {
16 Left,
17 Right,
18 Wheel,
19 Backward,
20 Forward,
21 Task,
22 Extra,
23 Undefined,
24};
25
26/**
27 * A button device factory representing a keyboard. It receives keyboard events and forward them
28 * to all button devices it created.
29 */
30class Mouse final : public InputCommon::InputEngine {
31public:
32 explicit Mouse(const std::string& input_engine_);
33
34 /**
35 * Signals that mouse has moved.
36 * @param x the x-coordinate of the cursor
37 * @param y the y-coordinate of the cursor
38 * @param center_x the x-coordinate of the middle of the screen
39 * @param center_y the y-coordinate of the middle of the screen
40 */
41 void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y);
42
43 /**
44 * Sets the status of all buttons bound with the key to pressed
45 * @param key_code the code of the key to press
46 */
47 void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button);
48
49 /**
50 * Sets the status of all buttons bound with the key to released
51 * @param key_code the code of the key to release
52 */
53 void ReleaseButton(MouseButton button);
54
55 /**
56 * Sets the status of the mouse wheel
57 * @param x delta movement in the x direction
58 * @param y delta movement in the y direction
59 */
60 void MouseWheelChange(int x, int y);
61
62 void ReleaseAllButtons();
63
64 std::vector<Common::ParamPackage> GetInputDevices() const override;
65 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
66 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
67
68private:
69 void UpdateThread(std::stop_token stop_token);
70 void StopPanning();
71
72 Common::Vec2<int> mouse_origin;
73 Common::Vec2<int> last_mouse_position;
74 Common::Vec2<float> last_mouse_change;
75 Common::Vec2<int> wheel_position;
76 bool button_pressed;
77 int mouse_panning_timout{};
78 std::jthread update_thread;
79};
80
81} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
new file mode 100644
index 000000000..90128b6cf
--- /dev/null
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -0,0 +1,924 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/math_util.h"
7#include "common/param_package.h"
8#include "common/settings.h"
9#include "common/thread.h"
10#include "common/vector_math.h"
11#include "input_common/drivers/sdl_driver.h"
12
13namespace InputCommon {
14
15namespace {
16std::string GetGUID(SDL_Joystick* joystick) {
17 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
18 char guid_str[33];
19 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
20 return guid_str;
21}
22} // Anonymous namespace
23
24static int SDLEventWatcher(void* user_data, SDL_Event* event) {
25 auto* const sdl_state = static_cast<SDLDriver*>(user_data);
26
27 sdl_state->HandleGameControllerEvent(*event);
28
29 return 0;
30}
31
32class SDLJoystick {
33public:
34 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
35 SDL_GameController* game_controller)
36 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
37 sdl_controller{game_controller, &SDL_GameControllerClose} {
38 EnableMotion();
39 }
40
41 void EnableMotion() {
42 if (sdl_controller) {
43 SDL_GameController* controller = sdl_controller.get();
44 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) {
45 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
46 has_accel = true;
47 }
48 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) {
49 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
50 has_gyro = true;
51 }
52 }
53 }
54
55 bool HasGyro() const {
56 return has_gyro;
57 }
58
59 bool HasAccel() const {
60 return has_accel;
61 }
62
63 bool UpdateMotion(SDL_ControllerSensorEvent event) {
64 constexpr float gravity_constant = 9.80665f;
65 std::lock_guard lock{mutex};
66 const u64 time_difference = event.timestamp - last_motion_update;
67 last_motion_update = event.timestamp;
68 switch (event.sensor) {
69 case SDL_SENSOR_ACCEL: {
70 motion.accel_x = -event.data[0] / gravity_constant;
71 motion.accel_y = event.data[2] / gravity_constant;
72 motion.accel_z = -event.data[1] / gravity_constant;
73 break;
74 }
75 case SDL_SENSOR_GYRO: {
76 motion.gyro_x = event.data[0] / (Common::PI * 2);
77 motion.gyro_y = -event.data[2] / (Common::PI * 2);
78 motion.gyro_z = event.data[1] / (Common::PI * 2);
79 break;
80 }
81 }
82
83 // Ignore duplicated timestamps
84 if (time_difference == 0) {
85 return false;
86 }
87 motion.delta_timestamp = time_difference * 1000;
88 return true;
89 }
90
91 BasicMotion GetMotion() {
92 return motion;
93 }
94
95 bool RumblePlay(const Common::Input::VibrationStatus vibration) {
96 constexpr u32 rumble_max_duration_ms = 1000;
97 if (sdl_controller) {
98 return SDL_GameControllerRumble(
99 sdl_controller.get(), static_cast<u16>(vibration.low_amplitude),
100 static_cast<u16>(vibration.high_amplitude), rumble_max_duration_ms) != -1;
101 } else if (sdl_joystick) {
102 return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(vibration.low_amplitude),
103 static_cast<u16>(vibration.high_amplitude),
104 rumble_max_duration_ms) != -1;
105 }
106
107 return false;
108 }
109
110 bool HasHDRumble() const {
111 if (sdl_controller) {
112 return (SDL_GameControllerGetType(sdl_controller.get()) ==
113 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
114 }
115 return false;
116 }
117 /**
118 * The Pad identifier of the joystick
119 */
120 const PadIdentifier GetPadIdentifier() const {
121 return {
122 .guid = Common::UUID{guid},
123 .port = static_cast<std::size_t>(port),
124 .pad = 0,
125 };
126 }
127
128 /**
129 * The guid of the joystick
130 */
131 const std::string& GetGUID() const {
132 return guid;
133 }
134
135 /**
136 * The number of joystick from the same type that were connected before this joystick
137 */
138 int GetPort() const {
139 return port;
140 }
141
142 SDL_Joystick* GetSDLJoystick() const {
143 return sdl_joystick.get();
144 }
145
146 SDL_GameController* GetSDLGameController() const {
147 return sdl_controller.get();
148 }
149
150 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
151 sdl_joystick.reset(joystick);
152 sdl_controller.reset(controller);
153 }
154
155 bool IsJoyconLeft() const {
156 const std::string controller_name = GetControllerName();
157 if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) {
158 return true;
159 }
160 if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) {
161 return true;
162 }
163 return false;
164 }
165
166 bool IsJoyconRight() const {
167 const std::string controller_name = GetControllerName();
168 if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) {
169 return true;
170 }
171 if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) {
172 return true;
173 }
174 return false;
175 }
176
177 BatteryLevel GetBatteryLevel() {
178 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
179 switch (level) {
180 case SDL_JOYSTICK_POWER_EMPTY:
181 return BatteryLevel::Empty;
182 case SDL_JOYSTICK_POWER_LOW:
183 return BatteryLevel::Critical;
184 case SDL_JOYSTICK_POWER_MEDIUM:
185 return BatteryLevel::Low;
186 case SDL_JOYSTICK_POWER_FULL:
187 return BatteryLevel::Medium;
188 case SDL_JOYSTICK_POWER_MAX:
189 return BatteryLevel::Full;
190 case SDL_JOYSTICK_POWER_UNKNOWN:
191 case SDL_JOYSTICK_POWER_WIRED:
192 default:
193 return BatteryLevel::Charging;
194 }
195 }
196
197 std::string GetControllerName() const {
198 if (sdl_controller) {
199 switch (SDL_GameControllerGetType(sdl_controller.get())) {
200 case SDL_CONTROLLER_TYPE_XBOX360:
201 return "XBox 360 Controller";
202 case SDL_CONTROLLER_TYPE_XBOXONE:
203 return "XBox One Controller";
204 default:
205 break;
206 }
207 const auto name = SDL_GameControllerName(sdl_controller.get());
208 if (name) {
209 return name;
210 }
211 }
212
213 if (sdl_joystick) {
214 const auto name = SDL_JoystickName(sdl_joystick.get());
215 if (name) {
216 return name;
217 }
218 }
219
220 return "Unknown";
221 }
222
223private:
224 std::string guid;
225 int port;
226 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
227 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
228 mutable std::mutex mutex;
229
230 u64 last_motion_update{};
231 bool has_gyro{false};
232 bool has_accel{false};
233 BasicMotion motion;
234};
235
236std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
237 std::lock_guard lock{joystick_map_mutex};
238 const auto it = joystick_map.find(guid);
239
240 if (it != joystick_map.end()) {
241 while (it->second.size() <= static_cast<std::size_t>(port)) {
242 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
243 nullptr, nullptr);
244 it->second.emplace_back(std::move(joystick));
245 }
246
247 return it->second[static_cast<std::size_t>(port)];
248 }
249
250 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
251
252 return joystick_map[guid].emplace_back(std::move(joystick));
253}
254
255std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
256 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
257 const std::string guid = GetGUID(sdl_joystick);
258
259 std::lock_guard lock{joystick_map_mutex};
260 const auto map_it = joystick_map.find(guid);
261
262 if (map_it == joystick_map.end()) {
263 return nullptr;
264 }
265
266 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
267 [&sdl_joystick](const auto& joystick) {
268 return joystick->GetSDLJoystick() == sdl_joystick;
269 });
270
271 if (vec_it == map_it->second.end()) {
272 return nullptr;
273 }
274
275 return *vec_it;
276}
277
278void SDLDriver::InitJoystick(int joystick_index) {
279 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
280 SDL_GameController* sdl_gamecontroller = nullptr;
281
282 if (SDL_IsGameController(joystick_index)) {
283 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
284 }
285
286 if (!sdl_joystick) {
287 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
288 return;
289 }
290
291 const std::string guid = GetGUID(sdl_joystick);
292
293 std::lock_guard lock{joystick_map_mutex};
294 if (joystick_map.find(guid) == joystick_map.end()) {
295 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
296 PreSetController(joystick->GetPadIdentifier());
297 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
298 joystick_map[guid].emplace_back(std::move(joystick));
299 return;
300 }
301
302 auto& joystick_guid_list = joystick_map[guid];
303 const auto joystick_it =
304 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
305 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
306
307 if (joystick_it != joystick_guid_list.end()) {
308 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
309 return;
310 }
311
312 const int port = static_cast<int>(joystick_guid_list.size());
313 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
314 PreSetController(joystick->GetPadIdentifier());
315 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
316 joystick_guid_list.emplace_back(std::move(joystick));
317}
318
319void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
320 const std::string guid = GetGUID(sdl_joystick);
321
322 std::lock_guard lock{joystick_map_mutex};
323 // This call to guid is safe since the joystick is guaranteed to be in the map
324 const auto& joystick_guid_list = joystick_map[guid];
325 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
326 [&sdl_joystick](const auto& joystick) {
327 return joystick->GetSDLJoystick() == sdl_joystick;
328 });
329
330 if (joystick_it != joystick_guid_list.end()) {
331 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
332 }
333}
334
335void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
336 switch (event.type) {
337 case SDL_JOYBUTTONUP: {
338 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
339 const PadIdentifier identifier = joystick->GetPadIdentifier();
340 SetButton(identifier, event.jbutton.button, false);
341 }
342 break;
343 }
344 case SDL_JOYBUTTONDOWN: {
345 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
346 const PadIdentifier identifier = joystick->GetPadIdentifier();
347 SetButton(identifier, event.jbutton.button, true);
348 }
349 break;
350 }
351 case SDL_JOYHATMOTION: {
352 if (const auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
353 const PadIdentifier identifier = joystick->GetPadIdentifier();
354 SetHatButton(identifier, event.jhat.hat, event.jhat.value);
355 }
356 break;
357 }
358 case SDL_JOYAXISMOTION: {
359 if (const auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
360 const PadIdentifier identifier = joystick->GetPadIdentifier();
361 SetAxis(identifier, event.jaxis.axis, event.jaxis.value / 32767.0f);
362 }
363 break;
364 }
365 case SDL_CONTROLLERSENSORUPDATE: {
366 if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
367 if (joystick->UpdateMotion(event.csensor)) {
368 const PadIdentifier identifier = joystick->GetPadIdentifier();
369 SetMotion(identifier, 0, joystick->GetMotion());
370 };
371 }
372 break;
373 }
374 case SDL_JOYDEVICEREMOVED:
375 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
376 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
377 break;
378 case SDL_JOYDEVICEADDED:
379 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
380 InitJoystick(event.jdevice.which);
381 break;
382 }
383}
384
385void SDLDriver::CloseJoysticks() {
386 std::lock_guard lock{joystick_map_mutex};
387 joystick_map.clear();
388}
389
390SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engine_) {
391 Common::SetCurrentThreadName("yuzu:input:SDL");
392
393 if (!Settings::values.enable_raw_input) {
394 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
395 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
396 }
397
398 // Prevent SDL from adding undesired axis
399 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
400
401 // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
402 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
403 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
404 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
405
406 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and
407 // not a generic one
408 SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
409
410 // Turn off Pro controller home led
411 SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
412
413 // If the frontend is going to manage the event loop, then we don't start one here
414 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0;
415 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
416 LOG_CRITICAL(Input, "SDL_Init failed with: {}", SDL_GetError());
417 return;
418 }
419
420 SDL_AddEventWatch(&SDLEventWatcher, this);
421
422 initialized = true;
423 if (start_thread) {
424 poll_thread = std::thread([this] {
425 using namespace std::chrono_literals;
426 while (initialized) {
427 SDL_PumpEvents();
428 std::this_thread::sleep_for(1ms);
429 }
430 });
431 }
432 // Because the events for joystick connection happens before we have our event watcher added, we
433 // can just open all the joysticks right here
434 for (int i = 0; i < SDL_NumJoysticks(); ++i) {
435 InitJoystick(i);
436 }
437}
438
439SDLDriver::~SDLDriver() {
440 CloseJoysticks();
441 SDL_DelEventWatch(&SDLEventWatcher, this);
442
443 initialized = false;
444 if (start_thread) {
445 poll_thread.join();
446 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
447 }
448}
449
450std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
451 std::vector<Common::ParamPackage> devices;
452 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
453 for (const auto& [key, value] : joystick_map) {
454 for (const auto& joystick : value) {
455 if (!joystick->GetSDLJoystick()) {
456 continue;
457 }
458 const std::string name =
459 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
460 devices.emplace_back(Common::ParamPackage{
461 {"engine", GetEngineName()},
462 {"display", std::move(name)},
463 {"guid", joystick->GetGUID()},
464 {"port", std::to_string(joystick->GetPort())},
465 });
466 if (joystick->IsJoyconLeft()) {
467 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
468 }
469 }
470 }
471
472 // Add dual controllers
473 for (const auto& [key, value] : joystick_map) {
474 for (const auto& joystick : value) {
475 if (joystick->IsJoyconRight()) {
476 if (!joycon_pairs.contains(joystick->GetPort())) {
477 continue;
478 }
479 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
480
481 const std::string name =
482 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
483 devices.emplace_back(Common::ParamPackage{
484 {"engine", GetEngineName()},
485 {"display", std::move(name)},
486 {"guid", joystick->GetGUID()},
487 {"guid2", joystick2->GetGUID()},
488 {"port", std::to_string(joystick->GetPort())},
489 });
490 }
491 }
492 }
493 return devices;
494}
495Common::Input::VibrationError SDLDriver::SetRumble(const PadIdentifier& identifier,
496 const Common::Input::VibrationStatus vibration) {
497 const auto joystick =
498 GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port));
499 const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
500 return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF;
501 };
502
503 // Default exponential curve for rumble
504 f32 factor = 0.35f;
505
506 // If vibration is set as a linear output use a flatter value
507 if (vibration.type == Common::Input::VibrationAmplificationType::Linear) {
508 factor = 0.5f;
509 }
510
511 // Amplitude for HD rumble needs no modification
512 if (joystick->HasHDRumble()) {
513 factor = 1.0f;
514 }
515
516 const Common::Input::VibrationStatus new_vibration{
517 .low_amplitude = process_amplitude_exp(vibration.low_amplitude, factor),
518 .low_frequency = vibration.low_frequency,
519 .high_amplitude = process_amplitude_exp(vibration.high_amplitude, factor),
520 .high_frequency = vibration.high_frequency,
521 .type = Common::Input::VibrationAmplificationType::Exponential,
522 };
523
524 if (!joystick->RumblePlay(new_vibration)) {
525 return Common::Input::VibrationError::Unknown;
526 }
527
528 return Common::Input::VibrationError::None;
529}
530Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
531 s32 axis, float value) const {
532 Common::ParamPackage params{};
533 params.Set("engine", GetEngineName());
534 params.Set("port", port);
535 params.Set("guid", std::move(guid));
536 params.Set("axis", axis);
537 params.Set("threshold", "0.5");
538 params.Set("invert", value < 0 ? "-" : "+");
539 return params;
540}
541
542Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, std::string guid,
543 s32 button) const {
544 Common::ParamPackage params{};
545 params.Set("engine", GetEngineName());
546 params.Set("port", port);
547 params.Set("guid", std::move(guid));
548 params.Set("button", button);
549 return params;
550}
551
552Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, std::string guid, s32 hat,
553 u8 value) const {
554 Common::ParamPackage params{};
555 params.Set("engine", GetEngineName());
556 params.Set("port", port);
557 params.Set("guid", std::move(guid));
558 params.Set("hat", hat);
559 params.Set("direction", GetHatButtonName(value));
560 return params;
561}
562
563Common::ParamPackage SDLDriver::BuildMotionParam(int port, std::string guid) const {
564 Common::ParamPackage params{};
565 params.Set("engine", GetEngineName());
566 params.Set("motion", 0);
567 params.Set("port", port);
568 params.Set("guid", std::move(guid));
569 return params;
570}
571
572Common::ParamPackage SDLDriver::BuildParamPackageForBinding(
573 int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const {
574 switch (binding.bindType) {
575 case SDL_CONTROLLER_BINDTYPE_NONE:
576 break;
577 case SDL_CONTROLLER_BINDTYPE_AXIS:
578 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
579 case SDL_CONTROLLER_BINDTYPE_BUTTON:
580 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
581 case SDL_CONTROLLER_BINDTYPE_HAT:
582 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
583 static_cast<u8>(binding.value.hat.hat_mask));
584 }
585 return {};
586}
587
588Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
589 int axis_y, float offset_x,
590 float offset_y) const {
591 Common::ParamPackage params;
592 params.Set("engine", GetEngineName());
593 params.Set("port", static_cast<int>(identifier.port));
594 params.Set("guid", identifier.guid.Format());
595 params.Set("axis_x", axis_x);
596 params.Set("axis_y", axis_y);
597 params.Set("offset_x", offset_x);
598 params.Set("offset_y", offset_y);
599 params.Set("invert_x", "+");
600 params.Set("invert_y", "+");
601 return params;
602}
603
604ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& params) {
605 if (!params.Has("guid") || !params.Has("port")) {
606 return {};
607 }
608 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
609
610 auto* controller = joystick->GetSDLGameController();
611 if (controller == nullptr) {
612 return {};
613 }
614
615 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
616 // We will add those afterwards
617 // This list also excludes Screenshot since theres not really a mapping for that
618 ButtonBindings switch_to_sdl_button;
619
620 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
621 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
622 } else {
623 switch_to_sdl_button = GetDefaultButtonBinding();
624 }
625
626 // Add the missing bindings for ZL/ZR
627 static constexpr ZButtonBindings switch_to_sdl_axis{{
628 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
629 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
630 }};
631
632 // Parameters contain two joysticks return dual
633 if (params.Has("guid2")) {
634 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
635
636 if (joystick2->GetSDLGameController() != nullptr) {
637 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
638 switch_to_sdl_axis);
639 }
640 }
641
642 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
643}
644
645ButtonBindings SDLDriver::GetDefaultButtonBinding() const {
646 return {
647 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
648 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
649 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
650 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
651 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
652 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
653 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
654 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
655 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
656 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
657 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
658 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
659 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
660 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
661 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
662 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
663 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
664 };
665}
666
667ButtonBindings SDLDriver::GetNintendoButtonBinding(
668 const std::shared_ptr<SDLJoystick>& joystick) const {
669 // Default SL/SR mapping for pro controllers
670 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
671 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
672
673 if (joystick->IsJoyconLeft()) {
674 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
675 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
676 }
677 if (joystick->IsJoyconRight()) {
678 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
679 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
680 }
681
682 return {
683 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
684 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
685 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
686 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
687 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
688 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
689 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
690 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
691 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
692 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
693 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
694 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
695 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
696 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
697 {Settings::NativeButton::SL, sl_button},
698 {Settings::NativeButton::SR, sr_button},
699 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
700 };
701}
702
703ButtonMapping SDLDriver::GetSingleControllerMapping(
704 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
705 const ZButtonBindings& switch_to_sdl_axis) const {
706 ButtonMapping mapping;
707 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
708 auto* controller = joystick->GetSDLGameController();
709
710 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
711 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
712 mapping.insert_or_assign(
713 switch_button,
714 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
715 }
716 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
717 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
718 mapping.insert_or_assign(
719 switch_button,
720 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
721 }
722
723 return mapping;
724}
725
726ButtonMapping SDLDriver::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
727 const std::shared_ptr<SDLJoystick>& joystick2,
728 const ButtonBindings& switch_to_sdl_button,
729 const ZButtonBindings& switch_to_sdl_axis) const {
730 ButtonMapping mapping;
731 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
732 auto* controller = joystick->GetSDLGameController();
733 auto* controller2 = joystick2->GetSDLGameController();
734
735 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
736 if (IsButtonOnLeftSide(switch_button)) {
737 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
738 mapping.insert_or_assign(
739 switch_button,
740 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
741 continue;
742 }
743 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
744 mapping.insert_or_assign(
745 switch_button,
746 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
747 }
748 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
749 if (IsButtonOnLeftSide(switch_button)) {
750 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
751 mapping.insert_or_assign(
752 switch_button,
753 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
754 continue;
755 }
756 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
757 mapping.insert_or_assign(
758 switch_button,
759 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
760 }
761
762 return mapping;
763}
764
765bool SDLDriver::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
766 switch (button) {
767 case Settings::NativeButton::DDown:
768 case Settings::NativeButton::DLeft:
769 case Settings::NativeButton::DRight:
770 case Settings::NativeButton::DUp:
771 case Settings::NativeButton::L:
772 case Settings::NativeButton::LStick:
773 case Settings::NativeButton::Minus:
774 case Settings::NativeButton::Screenshot:
775 case Settings::NativeButton::ZL:
776 return true;
777 default:
778 return false;
779 }
780}
781
782AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
783 if (!params.Has("guid") || !params.Has("port")) {
784 return {};
785 }
786 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
787 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
788 auto* controller = joystick->GetSDLGameController();
789 if (controller == nullptr) {
790 return {};
791 }
792
793 AnalogMapping mapping = {};
794 const auto& binding_left_x =
795 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
796 const auto& binding_left_y =
797 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
798 if (params.Has("guid2")) {
799 const auto identifier = joystick2->GetPadIdentifier();
800 PreSetController(identifier);
801 PreSetAxis(identifier, binding_left_x.value.axis);
802 PreSetAxis(identifier, binding_left_y.value.axis);
803 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
804 const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
805 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
806 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
807 binding_left_y.value.axis,
808 left_offset_x, left_offset_y));
809 } else {
810 const auto identifier = joystick->GetPadIdentifier();
811 PreSetController(identifier);
812 PreSetAxis(identifier, binding_left_x.value.axis);
813 PreSetAxis(identifier, binding_left_y.value.axis);
814 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
815 const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
816 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
817 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
818 binding_left_y.value.axis,
819 left_offset_x, left_offset_y));
820 }
821 const auto& binding_right_x =
822 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
823 const auto& binding_right_y =
824 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
825 const auto identifier = joystick->GetPadIdentifier();
826 PreSetController(identifier);
827 PreSetAxis(identifier, binding_right_x.value.axis);
828 PreSetAxis(identifier, binding_right_y.value.axis);
829 const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis);
830 const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis);
831 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
832 BuildParamPackageForAnalog(identifier, binding_right_x.value.axis,
833 binding_right_y.value.axis, right_offset_x,
834 right_offset_y));
835 return mapping;
836}
837
838MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& params) {
839 if (!params.Has("guid") || !params.Has("port")) {
840 return {};
841 }
842 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
843 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
844 auto* controller = joystick->GetSDLGameController();
845 if (controller == nullptr) {
846 return {};
847 }
848
849 MotionMapping mapping = {};
850 joystick->EnableMotion();
851
852 if (joystick->HasGyro() || joystick->HasAccel()) {
853 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
854 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
855 }
856 if (params.Has("guid2")) {
857 joystick2->EnableMotion();
858 if (joystick2->HasGyro() || joystick2->HasAccel()) {
859 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
860 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
861 }
862 } else {
863 if (joystick->HasGyro() || joystick->HasAccel()) {
864 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
865 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
866 }
867 }
868
869 return mapping;
870}
871
872Common::Input::ButtonNames SDLDriver::GetUIName(const Common::ParamPackage& params) const {
873 if (params.Has("button")) {
874 // TODO(German77): Find how to substitue the values for real button names
875 return Common::Input::ButtonNames::Value;
876 }
877 if (params.Has("hat")) {
878 return Common::Input::ButtonNames::Value;
879 }
880 if (params.Has("axis")) {
881 return Common::Input::ButtonNames::Value;
882 }
883 if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
884 return Common::Input::ButtonNames::Value;
885 }
886 if (params.Has("motion")) {
887 return Common::Input::ButtonNames::Engine;
888 }
889
890 return Common::Input::ButtonNames::Invalid;
891}
892
893std::string SDLDriver::GetHatButtonName(u8 direction_value) const {
894 switch (direction_value) {
895 case SDL_HAT_UP:
896 return "up";
897 case SDL_HAT_DOWN:
898 return "down";
899 case SDL_HAT_LEFT:
900 return "left";
901 case SDL_HAT_RIGHT:
902 return "right";
903 default:
904 return {};
905 }
906}
907
908u8 SDLDriver::GetHatButtonId(const std::string& direction_name) const {
909 Uint8 direction;
910 if (direction_name == "up") {
911 direction = SDL_HAT_UP;
912 } else if (direction_name == "down") {
913 direction = SDL_HAT_DOWN;
914 } else if (direction_name == "left") {
915 direction = SDL_HAT_LEFT;
916 } else if (direction_name == "right") {
917 direction = SDL_HAT_RIGHT;
918 } else {
919 direction = 0;
920 }
921 return direction;
922}
923
924} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/drivers/sdl_driver.h
index 7a9ad6346..d03ff4b84 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <memory>
9#include <mutex> 8#include <mutex>
10#include <thread> 9#include <thread>
11#include <unordered_map> 10#include <unordered_map>
@@ -13,8 +12,7 @@
13#include <SDL.h> 12#include <SDL.h>
14 13
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "common/threadsafe_queue.h" 15#include "input_common/input_engine.h"
17#include "input_common/sdl/sdl.h"
18 16
19union SDL_Event; 17union SDL_Event;
20using SDL_GameController = struct _SDL_GameController; 18using SDL_GameController = struct _SDL_GameController;
@@ -26,21 +24,17 @@ using ButtonBindings =
26using ZButtonBindings = 24using ZButtonBindings =
27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 25 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
28 26
29namespace InputCommon::SDL { 27namespace InputCommon {
30 28
31class SDLAnalogFactory;
32class SDLButtonFactory;
33class SDLMotionFactory;
34class SDLVibrationFactory;
35class SDLJoystick; 29class SDLJoystick;
36 30
37class SDLState : public State { 31class SDLDriver : public InputCommon::InputEngine {
38public: 32public:
39 /// Initializes and registers SDL device factories 33 /// Initializes and registers SDL device factories
40 SDLState(); 34 SDLDriver(const std::string& input_engine_);
41 35
42 /// Unregisters SDL device factories and shut them down. 36 /// Unregisters SDL device factories and shut them down.
43 ~SDLState() override; 37 ~SDLDriver() override;
44 38
45 /// Handle SDL_Events for joysticks from SDL_PollEvent 39 /// Handle SDL_Events for joysticks from SDL_PollEvent
46 void HandleGameControllerEvent(const SDL_Event& event); 40 void HandleGameControllerEvent(const SDL_Event& event);
@@ -54,18 +48,18 @@ public:
54 */ 48 */
55 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); 49 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
56 50
57 /// Get all DevicePoller that use the SDL backend for a specific device type 51 std::vector<Common::ParamPackage> GetInputDevices() const override;
58 Pollers GetPollers(Polling::DeviceType type) override;
59
60 /// Used by the Pollers during config
61 std::atomic<bool> polling = false;
62 Common::SPSCQueue<SDL_Event> event_queue;
63
64 std::vector<Common::ParamPackage> GetInputDevices() override;
65 52
66 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; 53 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
67 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; 54 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
68 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; 55 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
56 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
57
58 std::string GetHatButtonName(u8 direction_value) const override;
59 u8 GetHatButtonId(const std::string& direction_name) const override;
60
61 Common::Input::VibrationError SetRumble(
62 const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
69 63
70private: 64private:
71 void InitJoystick(int joystick_index); 65 void InitJoystick(int joystick_index);
@@ -74,6 +68,23 @@ private:
74 /// Needs to be called before SDL_QuitSubSystem. 68 /// Needs to be called before SDL_QuitSubSystem.
75 void CloseJoysticks(); 69 void CloseJoysticks();
76 70
71 Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
72 float value = 0.1f) const;
73 Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid,
74 s32 button) const;
75
76 Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat,
77 u8 value) const;
78
79 Common::ParamPackage BuildMotionParam(int port, std::string guid) const;
80
81 Common::ParamPackage BuildParamPackageForBinding(
82 int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const;
83
84 Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
85 int axis_y, float offset_x,
86 float offset_y) const;
87
77 /// Returns the default button bindings list for generic controllers 88 /// Returns the default button bindings list for generic controllers
78 ButtonBindings GetDefaultButtonBinding() const; 89 ButtonBindings GetDefaultButtonBinding() const;
79 90
@@ -94,21 +105,13 @@ private:
94 /// Returns true if the button is on the left joycon 105 /// Returns true if the button is on the left joycon
95 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; 106 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
96 107
97 // Set to true if SDL supports game controller subsystem
98 bool has_gamecontroller = false;
99
100 /// Map of GUID of a list of corresponding virtual Joysticks 108 /// Map of GUID of a list of corresponding virtual Joysticks
101 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 109 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
102 std::mutex joystick_map_mutex; 110 std::mutex joystick_map_mutex;
103 111
104 std::shared_ptr<SDLButtonFactory> button_factory;
105 std::shared_ptr<SDLAnalogFactory> analog_factory;
106 std::shared_ptr<SDLVibrationFactory> vibration_factory;
107 std::shared_ptr<SDLMotionFactory> motion_factory;
108
109 bool start_thread = false; 112 bool start_thread = false;
110 std::atomic<bool> initialized = false; 113 std::atomic<bool> initialized = false;
111 114
112 std::thread poll_thread; 115 std::thread poll_thread;
113}; 116};
114} // namespace InputCommon::SDL 117} // namespace InputCommon
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
new file mode 100644
index 000000000..0e01fb0d9
--- /dev/null
+++ b/src/input_common/drivers/tas_input.cpp
@@ -0,0 +1,315 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <regex>
7#include <fmt/format.h>
8
9#include "common/fs/file.h"
10#include "common/fs/fs_types.h"
11#include "common/fs/path_util.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "input_common/drivers/tas_input.h"
15
16namespace InputCommon::TasInput {
17
18enum TasAxes : u8 {
19 StickX,
20 StickY,
21 SubstickX,
22 SubstickY,
23 Undefined,
24};
25
26// Supported keywords and buttons from a TAS file
27constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
28 std::pair{"KEY_A", TasButton::BUTTON_A},
29 {"KEY_B", TasButton::BUTTON_B},
30 {"KEY_X", TasButton::BUTTON_X},
31 {"KEY_Y", TasButton::BUTTON_Y},
32 {"KEY_LSTICK", TasButton::STICK_L},
33 {"KEY_RSTICK", TasButton::STICK_R},
34 {"KEY_L", TasButton::TRIGGER_L},
35 {"KEY_R", TasButton::TRIGGER_R},
36 {"KEY_PLUS", TasButton::BUTTON_PLUS},
37 {"KEY_MINUS", TasButton::BUTTON_MINUS},
38 {"KEY_DLEFT", TasButton::BUTTON_LEFT},
39 {"KEY_DUP", TasButton::BUTTON_UP},
40 {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
41 {"KEY_DDOWN", TasButton::BUTTON_DOWN},
42 {"KEY_SL", TasButton::BUTTON_SL},
43 {"KEY_SR", TasButton::BUTTON_SR},
44 {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
45 {"KEY_HOME", TasButton::BUTTON_HOME},
46 {"KEY_ZL", TasButton::TRIGGER_ZL},
47 {"KEY_ZR", TasButton::TRIGGER_ZR},
48};
49
50Tas::Tas(const std::string& input_engine_) : InputCommon::InputEngine(input_engine_) {
51 for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) {
52 PadIdentifier identifier{
53 .guid = Common::UUID{},
54 .port = player_index,
55 .pad = 0,
56 };
57 PreSetController(identifier);
58 }
59 ClearInput();
60 if (!Settings::values.tas_enable) {
61 needs_reset = true;
62 return;
63 }
64 LoadTasFiles();
65}
66
67Tas::~Tas() {
68 Stop();
69};
70
71void Tas::LoadTasFiles() {
72 script_length = 0;
73 for (size_t i = 0; i < commands.size(); i++) {
74 LoadTasFile(i, 0);
75 if (commands[i].size() > script_length) {
76 script_length = commands[i].size();
77 }
78 }
79}
80
81void Tas::LoadTasFile(size_t player_index, size_t file_index) {
82 if (!commands[player_index].empty()) {
83 commands[player_index].clear();
84 }
85 std::string file = Common::FS::ReadStringFromFile(
86 Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
87 fmt::format("script{}-{}.txt", file_index, player_index + 1),
88 Common::FS::FileType::BinaryFile);
89 std::stringstream command_line(file);
90 std::string line;
91 int frame_no = 0;
92 while (std::getline(command_line, line, '\n')) {
93 if (line.empty()) {
94 continue;
95 }
96 std::smatch m;
97
98 std::stringstream linestream(line);
99 std::string segment;
100 std::vector<std::string> seglist;
101
102 while (std::getline(linestream, segment, ' ')) {
103 seglist.push_back(segment);
104 }
105
106 if (seglist.size() < 4) {
107 continue;
108 }
109
110 while (frame_no < std::stoi(seglist.at(0))) {
111 commands[player_index].push_back({});
112 frame_no++;
113 }
114
115 TASCommand command = {
116 .buttons = ReadCommandButtons(seglist.at(1)),
117 .l_axis = ReadCommandAxis(seglist.at(2)),
118 .r_axis = ReadCommandAxis(seglist.at(3)),
119 };
120 commands[player_index].push_back(command);
121 frame_no++;
122 }
123 LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
124}
125
126void Tas::WriteTasFile(std::u8string file_name) {
127 std::string output_text;
128 for (size_t frame = 0; frame < record_commands.size(); frame++) {
129 const TASCommand& line = record_commands[frame];
130 output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
131 WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
132 }
133 const auto bytes_written = Common::FS::WriteStringToFile(
134 Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
135 Common::FS::FileType::TextFile, output_text);
136 if (bytes_written == output_text.size()) {
137 LOG_INFO(Input, "TAS file written to file!");
138 } else {
139 LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
140 output_text.size());
141 }
142}
143
144void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
145 last_input = {
146 .buttons = buttons,
147 .l_axis = left_axis,
148 .r_axis = right_axis,
149 };
150}
151
152std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
153 TasState state;
154 if (is_recording) {
155 return {TasState::Recording, 0, record_commands.size()};
156 }
157
158 if (is_running) {
159 state = TasState::Running;
160 } else {
161 state = TasState::Stopped;
162 }
163
164 return {state, current_command, script_length};
165}
166
167void Tas::UpdateThread() {
168 if (!Settings::values.tas_enable) {
169 if (is_running) {
170 Stop();
171 }
172 return;
173 }
174
175 if (is_recording) {
176 record_commands.push_back(last_input);
177 }
178 if (needs_reset) {
179 current_command = 0;
180 needs_reset = false;
181 LoadTasFiles();
182 LOG_DEBUG(Input, "tas_reset done");
183 }
184
185 if (!is_running) {
186 ClearInput();
187 return;
188 }
189 if (current_command < script_length) {
190 LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
191 const size_t frame = current_command++;
192 for (size_t player_index = 0; player_index < commands.size(); player_index++) {
193 TASCommand command{};
194 if (frame < commands[player_index].size()) {
195 command = commands[player_index][frame];
196 }
197
198 PadIdentifier identifier{
199 .guid = Common::UUID{},
200 .port = player_index,
201 .pad = 0,
202 };
203 for (std::size_t i = 0; i < sizeof(command.buttons) * 8; ++i) {
204 const bool button_status = (command.buttons & (1LLU << i)) != 0;
205 const int button = static_cast<int>(i);
206 SetButton(identifier, button, button_status);
207 }
208 SetAxis(identifier, TasAxes::StickX, command.l_axis.x);
209 SetAxis(identifier, TasAxes::StickY, command.l_axis.y);
210 SetAxis(identifier, TasAxes::SubstickX, command.r_axis.x);
211 SetAxis(identifier, TasAxes::SubstickY, command.r_axis.y);
212 }
213 } else {
214 is_running = Settings::values.tas_loop.GetValue();
215 LoadTasFiles();
216 current_command = 0;
217 ClearInput();
218 }
219}
220
221void Tas::ClearInput() {
222 ResetButtonState();
223 ResetAnalogState();
224}
225
226TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
227 std::stringstream linestream(line);
228 std::string segment;
229 std::vector<std::string> seglist;
230
231 while (std::getline(linestream, segment, ';')) {
232 seglist.push_back(segment);
233 }
234
235 const float x = std::stof(seglist.at(0)) / 32767.0f;
236 const float y = std::stof(seglist.at(1)) / 32767.0f;
237
238 return {x, y};
239}
240
241u64 Tas::ReadCommandButtons(const std::string& data) const {
242 std::stringstream button_text(data);
243 std::string line;
244 u64 buttons = 0;
245 while (std::getline(button_text, line, ';')) {
246 for (auto [text, tas_button] : text_to_tas_button) {
247 if (text == line) {
248 buttons |= static_cast<u64>(tas_button);
249 break;
250 }
251 }
252 }
253 return buttons;
254}
255
256std::string Tas::WriteCommandButtons(u64 buttons) const {
257 std::string returns = "";
258 for (auto [text_button, tas_button] : text_to_tas_button) {
259 if ((buttons & static_cast<u64>(tas_button)) != 0) {
260 returns += fmt::format("{};", text_button);
261 }
262 }
263 return returns.empty() ? "NONE" : returns;
264}
265
266std::string Tas::WriteCommandAxis(TasAnalog analog) const {
267 return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
268}
269
270void Tas::StartStop() {
271 if (!Settings::values.tas_enable) {
272 return;
273 }
274 if (is_running) {
275 Stop();
276 } else {
277 is_running = true;
278 }
279}
280
281void Tas::Stop() {
282 is_running = false;
283}
284
285void Tas::Reset() {
286 if (!Settings::values.tas_enable) {
287 return;
288 }
289 needs_reset = true;
290}
291
292bool Tas::Record() {
293 if (!Settings::values.tas_enable) {
294 return true;
295 }
296 is_recording = !is_recording;
297 return is_recording;
298}
299
300void Tas::SaveRecording(bool overwrite_file) {
301 if (is_recording) {
302 return;
303 }
304 if (record_commands.empty()) {
305 return;
306 }
307 WriteTasFile(u8"record.txt");
308 if (overwrite_file) {
309 WriteTasFile(u8"script0-1.txt");
310 }
311 needs_reset = true;
312 record_commands.clear();
313}
314
315} // namespace InputCommon::TasInput
diff --git a/src/input_common/tas/tas_input.h b/src/input_common/drivers/tas_input.h
index 3e2db8f00..c95a130fc 100644
--- a/src/input_common/tas/tas_input.h
+++ b/src/input_common/drivers/tas_input.h
@@ -8,7 +8,7 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/settings_input.h" 10#include "common/settings_input.h"
11#include "core/frontend/input.h" 11#include "input_common/input_engine.h"
12#include "input_common/main.h" 12#include "input_common/main.h"
13 13
14/* 14/*
@@ -43,19 +43,11 @@ For debugging purposes, the common controller debugger can be used (View -> Debu
43P1). 43P1).
44*/ 44*/
45 45
46namespace TasInput { 46namespace InputCommon::TasInput {
47 47
48constexpr size_t PLAYER_NUMBER = 8; 48constexpr size_t PLAYER_NUMBER = 10;
49 49
50using TasAnalog = std::pair<float, float>; 50enum class TasButton : u64 {
51
52enum class TasState {
53 Running,
54 Recording,
55 Stopped,
56};
57
58enum class TasButton : u32 {
59 BUTTON_A = 1U << 0, 51 BUTTON_A = 1U << 0,
60 BUTTON_B = 1U << 1, 52 BUTTON_B = 1U << 1,
61 BUTTON_X = 1U << 2, 53 BUTTON_X = 1U << 2,
@@ -78,26 +70,29 @@ enum class TasButton : u32 {
78 BUTTON_CAPTURE = 1U << 19, 70 BUTTON_CAPTURE = 1U << 19,
79}; 71};
80 72
81enum class TasAxes : u8 { 73struct TasAnalog {
82 StickX, 74 float x{};
83 StickY, 75 float y{};
84 SubstickX,
85 SubstickY,
86 Undefined,
87}; 76};
88 77
89struct TasData { 78enum class TasState {
90 u32 buttons{}; 79 Running,
91 std::array<float, 4> axis{}; 80 Recording,
81 Stopped,
92}; 82};
93 83
94class Tas { 84class Tas final : public InputCommon::InputEngine {
95public: 85public:
96 Tas(); 86 explicit Tas(const std::string& input_engine_);
97 ~Tas(); 87 ~Tas();
98 88
99 // Changes the input status that will be stored in each frame 89 /**
100 void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes); 90 * Changes the input status that will be stored in each frame
91 * @param buttons: bitfield with the status of the buttons
92 * @param left_axis: value of the left axis
93 * @param right_axis: value of the right axis
94 */
95 void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
101 96
102 // Main loop that records or executes input 97 // Main loop that records or executes input
103 void UpdateThread(); 98 void UpdateThread();
@@ -117,112 +112,77 @@ public:
117 */ 112 */
118 bool Record(); 113 bool Record();
119 114
120 // Saves contents of record_commands on a file if overwrite is enabled player 1 will be 115 /**
121 // overwritten with the recorded commands 116 * Saves contents of record_commands on a file
117 * @param overwrite_file: Indicates if player 1 should be overwritten
118 */
122 void SaveRecording(bool overwrite_file); 119 void SaveRecording(bool overwrite_file);
123 120
124 /** 121 /**
125 * Returns the current status values of TAS playback/recording 122 * Returns the current status values of TAS playback/recording
126 * @return Tuple of 123 * @return Tuple of
127 * TasState indicating the current state out of Running, Recording or Stopped ; 124 * TasState indicating the current state out of Running ;
128 * Current playback progress or amount of frames (so far) for Recording ; 125 * Current playback progress ;
129 * Total length of script file currently loaded or amount of frames (so far) for Recording 126 * Total length of script file currently loaded or being recorded
130 */ 127 */
131 std::tuple<TasState, size_t, size_t> GetStatus() const; 128 std::tuple<TasState, size_t, size_t> GetStatus() const;
132 129
133 // Retuns an array of the default button mappings
134 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
135
136 // Retuns an array of the default analog mappings
137 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
138 [[nodiscard]] const TasData& GetTasState(std::size_t pad) const;
139
140private: 130private:
141 struct TASCommand { 131 struct TASCommand {
142 u32 buttons{}; 132 u64 buttons{};
143 TasAnalog l_axis{}; 133 TasAnalog l_axis{};
144 TasAnalog r_axis{}; 134 TasAnalog r_axis{};
145 }; 135 };
146 136
147 // Loads TAS files from all players 137 /// Loads TAS files from all players
148 void LoadTasFiles(); 138 void LoadTasFiles();
149 139
150 // Loads TAS file from the specified player 140 /** Loads TAS file from the specified player
151 void LoadTasFile(size_t player_index); 141 * @param player_index: player number to save the script
142 * @param file_index: script number of the file
143 */
144 void LoadTasFile(size_t player_index, size_t file_index);
152 145
153 // Writes a TAS file from the recorded commands 146 /** Writes a TAS file from the recorded commands
147 * @param file_name: name of the file to be written
148 */
154 void WriteTasFile(std::u8string file_name); 149 void WriteTasFile(std::u8string file_name);
155 150
156 /** 151 /**
157 * Parses a string containing the axis values with the following format "x;y" 152 * Parses a string containing the axis values. X and Y have a range from -32767 to 32767
158 * X and Y have a range from -32767 to 32767 153 * @param line: string containing axis values with the following format "x;y"
159 * @return Returns a TAS analog object with axis values with range from -1.0 to 1.0 154 * @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
160 */ 155 */
161 TasAnalog ReadCommandAxis(const std::string& line) const; 156 TasAnalog ReadCommandAxis(const std::string& line) const;
162 157
163 /** 158 /**
164 * Parses a string containing the button values with the following format "a;b;c;d..." 159 * Parses a string containing the button values. Each button is represented by it's text format
165 * Each button is represented by it's text format specified in text_to_tas_button array 160 * specified in text_to_tas_button array
166 * @return Returns a u32 with each bit representing the status of a button 161 * @param line: string containing button name with the following format "a;b;c;d..."
167 */ 162 * @return Returns a u64 with each bit representing the status of a button
168 u32 ReadCommandButtons(const std::string& line) const;
169
170 /**
171 * Converts an u32 containing the button status into the text equivalent
172 * @return Returns a string with the name of the buttons to be written to the file
173 */ 163 */
174 std::string WriteCommandButtons(u32 data) const; 164 u64 ReadCommandButtons(const std::string& line) const;
175 165
176 /** 166 /**
177 * Converts an TAS analog object containing the axis status into the text equivalent 167 * Reset state of all players
178 * @return Returns a string with the value of the axis to be written to the file
179 */ 168 */
180 std::string WriteCommandAxis(TasAnalog data) const; 169 void ClearInput();
181
182 // Inverts the Y axis polarity
183 std::pair<float, float> FlipAxisY(std::pair<float, float> old);
184 170
185 /** 171 /**
186 * Converts an u32 containing the button status into the text equivalent 172 * Converts an u64 containing the button status into the text equivalent
187 * @return Returns a string with the name of the buttons to be printed on console 173 * @param buttons: bitfield with the status of the buttons
174 * @return Returns a string with the name of the buttons to be written to the file
188 */ 175 */
189 std::string DebugButtons(u32 buttons) const; 176 std::string WriteCommandButtons(u64 buttons) const;
190 177
191 /** 178 /**
192 * Converts an TAS analog object containing the axis status into the text equivalent 179 * Converts an TAS analog object containing the axis status into the text equivalent
193 * @return Returns a string with the value of the axis to be printed on console 180 * @param data: value of the axis
194 */ 181 * @return A string with the value of the axis to be written to the file
195 std::string DebugJoystick(float x, float y) const;
196
197 /**
198 * Converts the given TAS status into the text equivalent
199 * @return Returns a string with the value of the TAS status to be printed on console
200 */ 182 */
201 std::string DebugInput(const TasData& data) const; 183 std::string WriteCommandAxis(TasAnalog data) const;
202
203 /**
204 * Converts the given TAS status of multiple players into the text equivalent
205 * @return Returns a string with the value of the status of all TAS players to be printed on
206 * console
207 */
208 std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const;
209
210 /**
211 * Converts an u32 containing the button status into the text equivalent
212 * @return Returns a string with the name of the buttons
213 */
214 std::string ButtonsToString(u32 button) const;
215
216 // Stores current controller configuration and sets a TAS controller for every active controller
217 // to the current config
218 void SwapToTasController();
219
220 // Sets the stored controller configuration to the current config
221 void SwapToStoredController();
222 184
223 size_t script_length{0}; 185 size_t script_length{0};
224 std::array<TasData, PLAYER_NUMBER> tas_data;
225 bool is_old_input_saved{false};
226 bool is_recording{false}; 186 bool is_recording{false};
227 bool is_running{false}; 187 bool is_running{false};
228 bool needs_reset{false}; 188 bool needs_reset{false};
@@ -230,8 +190,5 @@ private:
230 std::vector<TASCommand> record_commands{}; 190 std::vector<TASCommand> record_commands{};
231 size_t current_command{0}; 191 size_t current_command{0};
232 TASCommand last_input{}; // only used for recording 192 TASCommand last_input{}; // only used for recording
233
234 // Old settings for swapping controllers
235 std::array<Settings::PlayerInput, 10> player_mappings;
236}; 193};
237} // namespace TasInput 194} // namespace InputCommon::TasInput
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp
new file mode 100644
index 000000000..45b3086f6
--- /dev/null
+++ b/src/input_common/drivers/touch_screen.cpp
@@ -0,0 +1,53 @@
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/param_package.h"
6#include "input_common/drivers/touch_screen.h"
7
8namespace InputCommon {
9
10constexpr PadIdentifier identifier = {
11 .guid = Common::UUID{Common::INVALID_UUID},
12 .port = 0,
13 .pad = 0,
14};
15
16TouchScreen::TouchScreen(const std::string& input_engine_) : InputEngine(input_engine_) {
17 PreSetController(identifier);
18}
19
20void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
21 if (finger >= 16) {
22 return;
23 }
24 TouchPressed(x, y, finger);
25}
26
27void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
28 if (finger >= 16) {
29 return;
30 }
31 SetButton(identifier, static_cast<int>(finger), true);
32 SetAxis(identifier, static_cast<int>(finger * 2), x);
33 SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
34}
35
36void TouchScreen::TouchReleased(std::size_t finger) {
37 if (finger >= 16) {
38 return;
39 }
40 SetButton(identifier, static_cast<int>(finger), false);
41 SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
42 SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
43}
44
45void TouchScreen::ReleaseAllTouch() {
46 for (int index = 0; index < 16; ++index) {
47 SetButton(identifier, index, false);
48 SetAxis(identifier, index * 2, 0.0f);
49 SetAxis(identifier, index * 2 + 1, 0.0f);
50 }
51}
52
53} // namespace InputCommon
diff --git a/src/input_common/drivers/touch_screen.h b/src/input_common/drivers/touch_screen.h
new file mode 100644
index 000000000..25c11e8bf
--- /dev/null
+++ b/src/input_common/drivers/touch_screen.h
@@ -0,0 +1,44 @@
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 "input_common/input_engine.h"
8
9namespace InputCommon {
10
11/**
12 * A button device factory representing a keyboard. It receives keyboard events and forward them
13 * to all button devices it created.
14 */
15class TouchScreen final : public InputCommon::InputEngine {
16public:
17 explicit TouchScreen(const std::string& input_engine_);
18
19 /**
20 * Signals that mouse has moved.
21 * @param x the x-coordinate of the cursor
22 * @param y the y-coordinate of the cursor
23 * @param center_x the x-coordinate of the middle of the screen
24 * @param center_y the y-coordinate of the middle of the screen
25 */
26 void TouchMoved(float x, float y, std::size_t finger);
27
28 /**
29 * Sets the status of all buttons bound with the key to pressed
30 * @param key_code the code of the key to press
31 */
32 void TouchPressed(float x, float y, std::size_t finger);
33
34 /**
35 * Sets the status of all buttons bound with the key to released
36 * @param key_code the code of the key to release
37 */
38 void TouchReleased(std::size_t finger);
39
40 /// Resets all inputs to their initial value
41 void ReleaseAllTouch();
42};
43
44} // namespace InputCommon
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
new file mode 100644
index 000000000..fdee0f2d5
--- /dev/null
+++ b/src/input_common/drivers/udp_client.cpp
@@ -0,0 +1,591 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <random>
6#include <boost/asio.hpp>
7#include <fmt/format.h>
8
9#include "common/logging/log.h"
10#include "common/param_package.h"
11#include "common/settings.h"
12#include "input_common/drivers/udp_client.h"
13#include "input_common/helpers/udp_protocol.h"
14
15using boost::asio::ip::udp;
16
17namespace InputCommon::CemuhookUDP {
18
19struct SocketCallback {
20 std::function<void(Response::Version)> version;
21 std::function<void(Response::PortInfo)> port_info;
22 std::function<void(Response::PadData)> pad_data;
23};
24
25class Socket {
26public:
27 using clock = std::chrono::system_clock;
28
29 explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
30 : callback(std::move(callback_)), timer(io_service),
31 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
32 boost::system::error_code ec{};
33 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
34 if (ec.value() != boost::system::errc::success) {
35 LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
36 ipv4 = boost::asio::ip::address_v4{};
37 }
38
39 send_endpoint = {udp::endpoint(ipv4, port)};
40 }
41
42 void Stop() {
43 io_service.stop();
44 }
45
46 void Loop() {
47 io_service.run();
48 }
49
50 void StartSend(const clock::time_point& from) {
51 timer.expires_at(from + std::chrono::seconds(3));
52 timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); });
53 }
54
55 void StartReceive() {
56 socket.async_receive_from(
57 boost::asio::buffer(receive_buffer), receive_endpoint,
58 [this](const boost::system::error_code& error, std::size_t bytes_transferred) {
59 HandleReceive(error, bytes_transferred);
60 });
61 }
62
63private:
64 u32 GenerateRandomClientId() const {
65 std::random_device device;
66 return device();
67 }
68
69 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
70 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
71 switch (*type) {
72 case Type::Version: {
73 Response::Version version;
74 std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version));
75 callback.version(std::move(version));
76 break;
77 }
78 case Type::PortInfo: {
79 Response::PortInfo port_info;
80 std::memcpy(&port_info, &receive_buffer[sizeof(Header)],
81 sizeof(Response::PortInfo));
82 callback.port_info(std::move(port_info));
83 break;
84 }
85 case Type::PadData: {
86 Response::PadData pad_data;
87 std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData));
88 callback.pad_data(std::move(pad_data));
89 break;
90 }
91 }
92 }
93 StartReceive();
94 }
95
96 void HandleSend(const boost::system::error_code&) {
97 boost::system::error_code _ignored{};
98 // Send a request for getting port info for the pad
99 const Request::PortInfo port_info{4, {0, 1, 2, 3}};
100 const auto port_message = Request::Create(port_info, client_id);
101 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
102 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
103
104 // Send a request for getting pad data for the pad
105 const Request::PadData pad_data{
106 Request::RegisterFlags::AllPads,
107 0,
108 EMPTY_MAC_ADDRESS,
109 };
110 const auto pad_message = Request::Create(pad_data, client_id);
111 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
112 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
113 StartSend(timer.expiry());
114 }
115
116 SocketCallback callback;
117 boost::asio::io_service io_service;
118 boost::asio::basic_waitable_timer<clock> timer;
119 udp::socket socket;
120
121 const u32 client_id;
122
123 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
124 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
125 std::array<u8, PORT_INFO_SIZE> send_buffer1;
126 std::array<u8, PAD_DATA_SIZE> send_buffer2;
127 udp::endpoint send_endpoint;
128
129 std::array<u8, MAX_PACKET_SIZE> receive_buffer;
130 udp::endpoint receive_endpoint;
131};
132
133static void SocketLoop(Socket* socket) {
134 socket->StartReceive();
135 socket->StartSend(Socket::clock::now());
136 socket->Loop();
137}
138
139UDPClient::UDPClient(const std::string& input_engine_) : InputEngine(input_engine_) {
140 LOG_INFO(Input, "Udp Initialization started");
141 ReloadSockets();
142}
143
144UDPClient::~UDPClient() {
145 Reset();
146}
147
148UDPClient::ClientConnection::ClientConnection() = default;
149
150UDPClient::ClientConnection::~ClientConnection() = default;
151
152void UDPClient::ReloadSockets() {
153 Reset();
154
155 std::stringstream servers_ss(Settings::values.udp_input_servers.GetValue());
156 std::string server_token;
157 std::size_t client = 0;
158 while (std::getline(servers_ss, server_token, ',')) {
159 if (client == MAX_UDP_CLIENTS) {
160 break;
161 }
162 std::stringstream server_ss(server_token);
163 std::string token;
164 std::getline(server_ss, token, ':');
165 std::string udp_input_address = token;
166 std::getline(server_ss, token, ':');
167 char* temp;
168 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
169 if (*temp != '\0') {
170 LOG_ERROR(Input, "Port number is not valid {}", token);
171 continue;
172 }
173
174 const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port);
175 if (client_number != MAX_UDP_CLIENTS) {
176 LOG_ERROR(Input, "Duplicated UDP servers found");
177 continue;
178 }
179 StartCommunication(client++, udp_input_address, udp_input_port);
180 }
181}
182
183std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
184 for (std::size_t client = 0; client < clients.size(); client++) {
185 if (clients[client].active == -1) {
186 continue;
187 }
188 if (clients[client].host == host && clients[client].port == port) {
189 return client;
190 }
191 }
192 return MAX_UDP_CLIENTS;
193}
194
195void UDPClient::OnVersion([[maybe_unused]] Response::Version data) {
196 LOG_TRACE(Input, "Version packet received: {}", data.version);
197}
198
199void UDPClient::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
200 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
201}
202
203void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
204 const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id;
205
206 if (pad_index >= pads.size()) {
207 LOG_ERROR(Input, "Invalid pad id {}", data.info.id);
208 return;
209 }
210
211 LOG_TRACE(Input, "PadData packet received");
212 if (data.packet_counter == pads[pad_index].packet_sequence) {
213 LOG_WARNING(
214 Input,
215 "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
216 pads[pad_index].packet_sequence, data.packet_counter);
217 pads[pad_index].connected = false;
218 return;
219 }
220
221 clients[client].active = 1;
222 pads[pad_index].connected = true;
223 pads[pad_index].packet_sequence = data.packet_counter;
224
225 const auto now = std::chrono::steady_clock::now();
226 const auto time_difference = static_cast<u64>(
227 std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update)
228 .count());
229 pads[pad_index].last_update = now;
230
231 // Gyroscope values are not it the correct scale from better joy.
232 // Dividing by 312 allows us to make one full turn = 1 turn
233 // This must be a configurable valued called sensitivity
234 const float gyro_scale = 1.0f / 312.0f;
235
236 const BasicMotion motion{
237 .gyro_x = data.gyro.pitch * gyro_scale,
238 .gyro_y = data.gyro.roll * gyro_scale,
239 .gyro_z = -data.gyro.yaw * gyro_scale,
240 .accel_x = data.accel.x,
241 .accel_y = -data.accel.z,
242 .accel_z = data.accel.y,
243 .delta_timestamp = time_difference,
244 };
245 const PadIdentifier identifier = GetPadIdentifier(pad_index);
246 SetMotion(identifier, 0, motion);
247
248 for (std::size_t id = 0; id < data.touch.size(); ++id) {
249 const auto touch_pad = data.touch[id];
250 const auto touch_axis_x_id =
251 static_cast<int>(id == 0 ? PadAxes::Touch1X : PadAxes::Touch2X);
252 const auto touch_axis_y_id =
253 static_cast<int>(id == 0 ? PadAxes::Touch1Y : PadAxes::Touch2Y);
254 const auto touch_button_id =
255 static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::touch2);
256
257 // TODO: Use custom calibration per device
258 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
259 const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
260 const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
261 const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
262 const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850));
263
264 const f32 x =
265 static_cast<f32>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) /
266 static_cast<f32>(max_x - min_x);
267 const f32 y =
268 static_cast<f32>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) /
269 static_cast<f32>(max_y - min_y);
270
271 if (touch_pad.is_active) {
272 SetAxis(identifier, touch_axis_x_id, x);
273 SetAxis(identifier, touch_axis_y_id, y);
274 SetButton(identifier, touch_button_id, true);
275 continue;
276 }
277 SetAxis(identifier, touch_axis_x_id, 0);
278 SetAxis(identifier, touch_axis_y_id, 0);
279 SetButton(identifier, touch_button_id, false);
280 }
281
282 SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX),
283 (data.left_stick_x - 127.0f) / 127.0f);
284 SetAxis(identifier, static_cast<int>(PadAxes::LeftStickY),
285 (data.left_stick_y - 127.0f) / 127.0f);
286 SetAxis(identifier, static_cast<int>(PadAxes::RightStickX),
287 (data.right_stick_x - 127.0f) / 127.0f);
288 SetAxis(identifier, static_cast<int>(PadAxes::RightStickY),
289 (data.right_stick_y - 127.0f) / 127.0f);
290
291 static constexpr std::array<PadButton, 16> buttons{
292 PadButton::Share, PadButton::L3, PadButton::R3, PadButton::Options,
293 PadButton::Up, PadButton::Right, PadButton::Down, PadButton::Left,
294 PadButton::L2, PadButton::R2, PadButton::L1, PadButton::R1,
295 PadButton::Triangle, PadButton::Circle, PadButton::Cross, PadButton::Square};
296
297 for (std::size_t i = 0; i < buttons.size(); ++i) {
298 const bool button_status = (data.digital_button & (1U << i)) != 0;
299 const int button = static_cast<int>(buttons[i]);
300 SetButton(identifier, button, button_status);
301 }
302}
303
304void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
305 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
306 [this](Response::PortInfo info) { OnPortInfo(info); },
307 [this, client](Response::PadData data) { OnPadData(data, client); }};
308 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
309 clients[client].uuid = GetHostUUID(host);
310 clients[client].host = host;
311 clients[client].port = port;
312 clients[client].active = 0;
313 clients[client].socket = std::make_unique<Socket>(host, port, callback);
314 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
315 for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
316 const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
317 PreSetController(identifier);
318 }
319}
320
321const PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const {
322 const std::size_t client = pad_index / PADS_PER_CLIENT;
323 return {
324 .guid = clients[client].uuid,
325 .port = static_cast<std::size_t>(clients[client].port),
326 .pad = pad_index,
327 };
328}
329
330const Common::UUID UDPClient::GetHostUUID(const std::string host) const {
331 const auto ip = boost::asio::ip::address_v4::from_string(host);
332 const auto hex_host = fmt::format("{:06x}", ip.to_ulong());
333 return Common::UUID{hex_host};
334}
335
336void UDPClient::Reset() {
337 for (auto& client : clients) {
338 if (client.thread.joinable()) {
339 client.active = -1;
340 client.socket->Stop();
341 client.thread.join();
342 }
343 }
344}
345
346std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
347 std::vector<Common::ParamPackage> devices;
348 if (!Settings::values.enable_udp_controller) {
349 return devices;
350 }
351 for (std::size_t client = 0; client < clients.size(); client++) {
352 if (clients[client].active != 1) {
353 continue;
354 }
355 for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
356 const std::size_t pad_index = client * PADS_PER_CLIENT + index;
357 if (!pads[pad_index].connected) {
358 continue;
359 }
360 const auto pad_identifier = GetPadIdentifier(pad_index);
361 Common::ParamPackage identifier{};
362 identifier.Set("engine", GetEngineName());
363 identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad));
364 identifier.Set("guid", pad_identifier.guid.Format());
365 identifier.Set("port", static_cast<int>(pad_identifier.port));
366 identifier.Set("pad", static_cast<int>(pad_identifier.pad));
367 devices.emplace_back(identifier);
368 }
369 }
370 return devices;
371}
372
373ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) {
374 // This list excludes any button that can't be really mapped
375 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 18>
376 switch_to_dsu_button = {
377 std::pair{Settings::NativeButton::A, PadButton::Circle},
378 {Settings::NativeButton::B, PadButton::Cross},
379 {Settings::NativeButton::X, PadButton::Triangle},
380 {Settings::NativeButton::Y, PadButton::Square},
381 {Settings::NativeButton::Plus, PadButton::Options},
382 {Settings::NativeButton::Minus, PadButton::Share},
383 {Settings::NativeButton::DLeft, PadButton::Left},
384 {Settings::NativeButton::DUp, PadButton::Up},
385 {Settings::NativeButton::DRight, PadButton::Right},
386 {Settings::NativeButton::DDown, PadButton::Down},
387 {Settings::NativeButton::L, PadButton::L1},
388 {Settings::NativeButton::R, PadButton::R1},
389 {Settings::NativeButton::ZL, PadButton::L2},
390 {Settings::NativeButton::ZR, PadButton::R2},
391 {Settings::NativeButton::SL, PadButton::L2},
392 {Settings::NativeButton::SR, PadButton::R2},
393 {Settings::NativeButton::LStick, PadButton::L3},
394 {Settings::NativeButton::RStick, PadButton::R3},
395 };
396 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
397 return {};
398 }
399
400 ButtonMapping mapping{};
401 for (const auto& [switch_button, dsu_button] : switch_to_dsu_button) {
402 Common::ParamPackage button_params{};
403 button_params.Set("engine", GetEngineName());
404 button_params.Set("guid", params.Get("guid", ""));
405 button_params.Set("port", params.Get("port", 0));
406 button_params.Set("pad", params.Get("pad", 0));
407 button_params.Set("button", static_cast<int>(dsu_button));
408 mapping.insert_or_assign(switch_button, std::move(button_params));
409 }
410
411 return mapping;
412}
413
414AnalogMapping UDPClient::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
415 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
416 return {};
417 }
418
419 AnalogMapping mapping = {};
420 Common::ParamPackage left_analog_params;
421 left_analog_params.Set("engine", GetEngineName());
422 left_analog_params.Set("guid", params.Get("guid", ""));
423 left_analog_params.Set("port", params.Get("port", 0));
424 left_analog_params.Set("pad", params.Get("pad", 0));
425 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::LeftStickX));
426 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::LeftStickY));
427 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
428 Common::ParamPackage right_analog_params;
429 right_analog_params.Set("engine", GetEngineName());
430 right_analog_params.Set("guid", params.Get("guid", ""));
431 right_analog_params.Set("port", params.Get("port", 0));
432 right_analog_params.Set("pad", params.Get("pad", 0));
433 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::RightStickX));
434 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::RightStickY));
435 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
436 return mapping;
437}
438
439MotionMapping UDPClient::GetMotionMappingForDevice(const Common::ParamPackage& params) {
440 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
441 return {};
442 }
443
444 MotionMapping mapping = {};
445 Common::ParamPackage motion_params;
446 motion_params.Set("engine", GetEngineName());
447 motion_params.Set("guid", params.Get("guid", ""));
448 motion_params.Set("port", params.Get("port", 0));
449 motion_params.Set("pad", params.Get("pad", 0));
450 motion_params.Set("motion", 0);
451 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(motion_params));
452 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(motion_params));
453 return mapping;
454}
455
456Common::Input::ButtonNames UDPClient::GetUIButtonName(const Common::ParamPackage& params) const {
457 PadButton button = static_cast<PadButton>(params.Get("button", 0));
458 switch (button) {
459 case PadButton::Left:
460 return Common::Input::ButtonNames::ButtonLeft;
461 case PadButton::Right:
462 return Common::Input::ButtonNames::ButtonRight;
463 case PadButton::Down:
464 return Common::Input::ButtonNames::ButtonDown;
465 case PadButton::Up:
466 return Common::Input::ButtonNames::ButtonUp;
467 case PadButton::L1:
468 return Common::Input::ButtonNames::L1;
469 case PadButton::L2:
470 return Common::Input::ButtonNames::L2;
471 case PadButton::L3:
472 return Common::Input::ButtonNames::L3;
473 case PadButton::R1:
474 return Common::Input::ButtonNames::R1;
475 case PadButton::R2:
476 return Common::Input::ButtonNames::R2;
477 case PadButton::R3:
478 return Common::Input::ButtonNames::R3;
479 case PadButton::Circle:
480 return Common::Input::ButtonNames::Circle;
481 case PadButton::Cross:
482 return Common::Input::ButtonNames::Cross;
483 case PadButton::Square:
484 return Common::Input::ButtonNames::Square;
485 case PadButton::Triangle:
486 return Common::Input::ButtonNames::Triangle;
487 case PadButton::Share:
488 return Common::Input::ButtonNames::Share;
489 case PadButton::Options:
490 return Common::Input::ButtonNames::Options;
491 default:
492 return Common::Input::ButtonNames::Undefined;
493 }
494}
495
496Common::Input::ButtonNames UDPClient::GetUIName(const Common::ParamPackage& params) const {
497 if (params.Has("button")) {
498 return GetUIButtonName(params);
499 }
500 if (params.Has("axis")) {
501 return Common::Input::ButtonNames::Value;
502 }
503 if (params.Has("motion")) {
504 return Common::Input::ButtonNames::Engine;
505 }
506
507 return Common::Input::ButtonNames::Invalid;
508}
509
510void TestCommunication(const std::string& host, u16 port,
511 const std::function<void()>& success_callback,
512 const std::function<void()>& failure_callback) {
513 std::thread([=] {
514 Common::Event success_event;
515 SocketCallback callback{
516 .version = [](Response::Version) {},
517 .port_info = [](Response::PortInfo) {},
518 .pad_data = [&](Response::PadData) { success_event.Set(); },
519 };
520 Socket socket{host, port, std::move(callback)};
521 std::thread worker_thread{SocketLoop, &socket};
522 const bool result =
523 success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10));
524 socket.Stop();
525 worker_thread.join();
526 if (result) {
527 success_callback();
528 } else {
529 failure_callback();
530 }
531 }).detach();
532}
533
534CalibrationConfigurationJob::CalibrationConfigurationJob(
535 const std::string& host, u16 port, std::function<void(Status)> status_callback,
536 std::function<void(u16, u16, u16, u16)> data_callback) {
537
538 std::thread([=, this] {
539 Status current_status{Status::Initialized};
540 SocketCallback callback{
541 [](Response::Version) {}, [](Response::PortInfo) {},
542 [&](Response::PadData data) {
543 static constexpr u16 CALIBRATION_THRESHOLD = 100;
544 static constexpr u16 MAX_VALUE = UINT16_MAX;
545
546 if (current_status == Status::Initialized) {
547 // Receiving data means the communication is ready now
548 current_status = Status::Ready;
549 status_callback(current_status);
550 }
551 const auto& touchpad_0 = data.touch[0];
552 if (touchpad_0.is_active == 0) {
553 return;
554 }
555 LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
556 const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
557 const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
558 if (current_status == Status::Ready) {
559 // First touch - min data (min_x/min_y)
560 current_status = Status::Stage1Completed;
561 status_callback(current_status);
562 }
563 if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
564 touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
565 // Set the current position as max value and finishes configuration
566 const u16 max_x = touchpad_0.x;
567 const u16 max_y = touchpad_0.y;
568 current_status = Status::Completed;
569 data_callback(min_x, min_y, max_x, max_y);
570 status_callback(current_status);
571
572 complete_event.Set();
573 }
574 }};
575 Socket socket{host, port, std::move(callback)};
576 std::thread worker_thread{SocketLoop, &socket};
577 complete_event.Wait();
578 socket.Stop();
579 worker_thread.join();
580 }).detach();
581}
582
583CalibrationConfigurationJob::~CalibrationConfigurationJob() {
584 Stop();
585}
586
587void CalibrationConfigurationJob::Stop() {
588 complete_event.Set();
589}
590
591} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/client.h b/src/input_common/drivers/udp_client.h
index 380f9bb76..5d483f26b 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/drivers/udp_client.h
@@ -4,20 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8#include <memory>
9#include <mutex>
10#include <optional> 7#include <optional>
11#include <string> 8
12#include <thread>
13#include <tuple>
14#include "common/common_types.h" 9#include "common/common_types.h"
15#include "common/param_package.h"
16#include "common/thread.h" 10#include "common/thread.h"
17#include "common/threadsafe_queue.h" 11#include "input_common/input_engine.h"
18#include "common/vector_math.h"
19#include "core/frontend/input.h"
20#include "input_common/motion_input.h"
21 12
22namespace InputCommon::CemuhookUDP { 13namespace InputCommon::CemuhookUDP {
23 14
@@ -30,16 +21,6 @@ struct TouchPad;
30struct Version; 21struct Version;
31} // namespace Response 22} // namespace Response
32 23
33enum class PadMotion {
34 GyroX,
35 GyroY,
36 GyroZ,
37 AccX,
38 AccY,
39 AccZ,
40 Undefined,
41};
42
43enum class PadTouch { 24enum class PadTouch {
44 Click, 25 Click,
45 Undefined, 26 Undefined,
@@ -49,14 +30,10 @@ struct UDPPadStatus {
49 std::string host{"127.0.0.1"}; 30 std::string host{"127.0.0.1"};
50 u16 port{26760}; 31 u16 port{26760};
51 std::size_t pad_index{}; 32 std::size_t pad_index{};
52 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f};
54}; 33};
55 34
56struct DeviceStatus { 35struct DeviceStatus {
57 std::mutex update_mutex; 36 std::mutex update_mutex;
58 Input::MotionStatus motion_status;
59 std::tuple<float, float, bool> touch_status;
60 37
61 // calibration data for scaling the device's touch area to 3ds 38 // calibration data for scaling the device's touch area to 3ds
62 struct CalibrationData { 39 struct CalibrationData {
@@ -68,48 +45,85 @@ struct DeviceStatus {
68 std::optional<CalibrationData> touch_calibration; 45 std::optional<CalibrationData> touch_calibration;
69}; 46};
70 47
71class Client { 48/**
49 * A button device factory representing a keyboard. It receives keyboard events and forward them
50 * to all button devices it created.
51 */
52class UDPClient final : public InputCommon::InputEngine {
72public: 53public:
73 // Initialize the UDP client capture and read sequence 54 explicit UDPClient(const std::string& input_engine_);
74 Client(); 55 ~UDPClient();
75
76 // Close and release the client
77 ~Client();
78
79 // Used for polling
80 void BeginConfiguration();
81 void EndConfiguration();
82
83 std::vector<Common::ParamPackage> GetInputDevices() const;
84 56
85 bool DeviceConnected(std::size_t pad) const;
86 void ReloadSockets(); 57 void ReloadSockets();
87 58
88 Common::SPSCQueue<UDPPadStatus>& GetPadQueue(); 59 /// Used for automapping features
89 const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const; 60 std::vector<Common::ParamPackage> GetInputDevices() const override;
61 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
62 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
63 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
64 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
90 65
91 DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); 66private:
92 const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; 67 enum class PadButton {
68 Undefined = 0x0000,
69 Share = 0x0001,
70 L3 = 0x0002,
71 R3 = 0x0004,
72 Options = 0x0008,
73 Up = 0x0010,
74 Right = 0x0020,
75 Down = 0x0040,
76 Left = 0x0080,
77 L2 = 0x0100,
78 R2 = 0x0200,
79 L1 = 0x0400,
80 R1 = 0x0800,
81 Triangle = 0x1000,
82 Circle = 0x2000,
83 Cross = 0x4000,
84 Square = 0x8000,
85 Touch1 = 0x10000,
86 touch2 = 0x20000,
87 };
93 88
94 Input::TouchStatus& GetTouchState(); 89 enum class PadAxes : u8 {
95 const Input::TouchStatus& GetTouchState() const; 90 LeftStickX,
91 LeftStickY,
92 RightStickX,
93 RightStickY,
94 AnalogLeft,
95 AnalogDown,
96 AnalogRight,
97 AnalogUp,
98 AnalogSquare,
99 AnalogCross,
100 AnalogCircle,
101 AnalogTriangle,
102 AnalogR1,
103 AnalogL1,
104 AnalogR2,
105 AnalogL3,
106 AnalogR3,
107 Touch1X,
108 Touch1Y,
109 Touch2X,
110 Touch2Y,
111 Undefined,
112 };
96 113
97private:
98 struct PadData { 114 struct PadData {
99 std::size_t pad_index{}; 115 std::size_t pad_index{};
100 bool connected{}; 116 bool connected{};
101 DeviceStatus status; 117 DeviceStatus status;
102 u64 packet_sequence{}; 118 u64 packet_sequence{};
103 119
104 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::steady_clock> last_update; 120 std::chrono::time_point<std::chrono::steady_clock> last_update;
108 }; 121 };
109 122
110 struct ClientConnection { 123 struct ClientConnection {
111 ClientConnection(); 124 ClientConnection();
112 ~ClientConnection(); 125 ~ClientConnection();
126 Common::UUID uuid{"7F000001"};
113 std::string host{"127.0.0.1"}; 127 std::string host{"127.0.0.1"};
114 u16 port{26760}; 128 u16 port{26760};
115 s8 active{-1}; 129 s8 active{-1};
@@ -127,28 +141,16 @@ private:
127 void OnPortInfo(Response::PortInfo); 141 void OnPortInfo(Response::PortInfo);
128 void OnPadData(Response::PadData, std::size_t client); 142 void OnPadData(Response::PadData, std::size_t client);
129 void StartCommunication(std::size_t client, const std::string& host, u16 port); 143 void StartCommunication(std::size_t client, const std::string& host, u16 port);
130 void UpdateYuzuSettings(std::size_t client, std::size_t pad_index, 144 const PadIdentifier GetPadIdentifier(std::size_t pad_index) const;
131 const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro); 145 const Common::UUID GetHostUUID(const std::string host) const;
132
133 // Returns an unused finger id, if there is no fingers available std::nullopt will be
134 // returned
135 std::optional<std::size_t> GetUnusedFingerID() const;
136
137 // Merges and updates all touch inputs into the touch_status array
138 void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id);
139 146
140 bool configuring = false; 147 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
141 148
142 // Allocate clients for 8 udp servers 149 // Allocate clients for 8 udp servers
143 static constexpr std::size_t MAX_UDP_CLIENTS = 8; 150 static constexpr std::size_t MAX_UDP_CLIENTS = 8;
144 static constexpr std::size_t PADS_PER_CLIENT = 4; 151 static constexpr std::size_t PADS_PER_CLIENT = 4;
145 // Each client can have up 2 touch inputs
146 static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2;
147 std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{}; 152 std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{};
148 std::array<ClientConnection, MAX_UDP_CLIENTS> clients{}; 153 std::array<ClientConnection, MAX_UDP_CLIENTS> clients{};
149 Common::SPSCQueue<UDPPadStatus> pad_queue{};
150 Input::TouchStatus touch_status{};
151 std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
152}; 154};
153 155
154/// An async job allowing configuration of the touchpad calibration. 156/// An async job allowing configuration of the touchpad calibration.
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
deleted file mode 100644
index e5de5e94f..000000000
--- a/src/input_common/gcadapter/gc_adapter.h
+++ /dev/null
@@ -1,168 +0,0 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6#include <algorithm>
7#include <functional>
8#include <mutex>
9#include <thread>
10#include <unordered_map>
11#include "common/common_types.h"
12#include "common/threadsafe_queue.h"
13#include "input_common/main.h"
14
15struct libusb_context;
16struct libusb_device;
17struct libusb_device_handle;
18
19namespace GCAdapter {
20
21enum class PadButton {
22 Undefined = 0x0000,
23 ButtonLeft = 0x0001,
24 ButtonRight = 0x0002,
25 ButtonDown = 0x0004,
26 ButtonUp = 0x0008,
27 TriggerZ = 0x0010,
28 TriggerR = 0x0020,
29 TriggerL = 0x0040,
30 ButtonA = 0x0100,
31 ButtonB = 0x0200,
32 ButtonX = 0x0400,
33 ButtonY = 0x0800,
34 ButtonStart = 0x1000,
35 // Below is for compatibility with "AxisButton" type
36 Stick = 0x2000,
37};
38
39enum class PadAxes : u8 {
40 StickX,
41 StickY,
42 SubstickX,
43 SubstickY,
44 TriggerLeft,
45 TriggerRight,
46 Undefined,
47};
48
49enum class ControllerTypes {
50 None,
51 Wired,
52 Wireless,
53};
54
55struct GCPadStatus {
56 std::size_t port{};
57
58 PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
59
60 PadAxes axis{PadAxes::Undefined};
61 s16 axis_value{};
62 u8 axis_threshold{50};
63};
64
65struct GCController {
66 ControllerTypes type{};
67 bool enable_vibration{};
68 u8 rumble_amplitude{};
69 u16 buttons{};
70 PadButton last_button{};
71 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{};
73 u8 reset_origin_counter{};
74};
75
76class Adapter {
77public:
78 Adapter();
79 ~Adapter();
80
81 /// Request a vibration for a controller
82 bool RumblePlay(std::size_t port, u8 amplitude);
83
84 /// Used for polling
85 void BeginConfiguration();
86 void EndConfiguration();
87
88 Common::SPSCQueue<GCPadStatus>& GetPadQueue();
89 const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
90
91 GCController& GetPadState(std::size_t port);
92 const GCController& GetPadState(std::size_t port) const;
93
94 /// Returns true if there is a device connected to port
95 bool DeviceConnected(std::size_t port) const;
96
97 /// Used for automapping features
98 std::vector<Common::ParamPackage> GetInputDevices() const;
99 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
100 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
101
102private:
103 using AdapterPayload = std::array<u8, 37>;
104
105 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
106 void UpdateControllers(const AdapterPayload& adapter_payload);
107 void UpdateYuzuSettings(std::size_t port);
108 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
109 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
110 void UpdateVibrations();
111
112 void AdapterInputThread();
113
114 void AdapterScanThread();
115
116 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
117
118 // Updates vibration state of all controllers
119 void SendVibrations();
120
121 /// For use in initialization, querying devices to find the adapter
122 void Setup();
123
124 /// Resets status of all GC controller devices to a disconnected state
125 void ResetDevices();
126
127 /// Resets status of device connected to a disconnected state
128 void ResetDevice(std::size_t port);
129
130 /// Returns true if we successfully gain access to GC Adapter
131 bool CheckDeviceAccess();
132
133 /// Captures GC Adapter endpoint address
134 /// Returns true if the endpoint was set correctly
135 bool GetGCEndpoint(libusb_device* device);
136
137 /// For shutting down, clear all data, join all threads, release usb
138 void Reset();
139
140 // Join all threads
141 void JoinThreads();
142
143 // Release usb handles
144 void ClearLibusbHandle();
145
146 libusb_device_handle* usb_adapter_handle = nullptr;
147 std::array<GCController, 4> pads;
148 Common::SPSCQueue<GCPadStatus> pad_queue;
149
150 std::thread adapter_input_thread;
151 std::thread adapter_scan_thread;
152 bool adapter_input_thread_running;
153 bool adapter_scan_thread_running;
154 bool restart_scan_thread;
155
156 libusb_context* libusb_ctx;
157
158 u8 input_endpoint{0};
159 u8 output_endpoint{0};
160 u8 input_error_counter{0};
161 u8 output_error_counter{0};
162 int vibration_counter{0};
163
164 bool configuring{false};
165 bool rumble_enabled{true};
166 bool vibration_changed{true};
167};
168} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
deleted file mode 100644
index 1b6ded8d6..000000000
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
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 <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "common/assert.h"
10#include "common/threadsafe_queue.h"
11#include "input_common/gcadapter/gc_adapter.h"
12#include "input_common/gcadapter/gc_poller.h"
13
14namespace InputCommon {
15
16class GCButton final : public Input::ButtonDevice {
17public:
18 explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {}
20
21 ~GCButton() override;
22
23 bool GetStatus() const override {
24 if (gcadapter->DeviceConnected(port)) {
25 return (gcadapter->GetPadState(port).buttons & button) != 0;
26 }
27 return false;
28 }
29
30private:
31 const u32 port;
32 const s32 button;
33 const GCAdapter::Adapter* gcadapter;
34};
35
36class GCAxisButton final : public Input::ButtonDevice {
37public:
38 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
39 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter) {}
42
43 bool GetStatus() const override {
44 if (gcadapter->DeviceConnected(port)) {
45 const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
46 const float axis_value = current_axis_value / 128.0f;
47 if (trigger_if_greater) {
48 // TODO: Might be worthwile to set a slider for the trigger threshold. It is
49 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
50 return axis_value > threshold;
51 }
52 return axis_value < -threshold;
53 }
54 return false;
55 }
56
57private:
58 const u32 port;
59 const u32 axis;
60 float threshold;
61 bool trigger_if_greater;
62 const GCAdapter::Adapter* gcadapter;
63};
64
65GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
66 : adapter(std::move(adapter_)) {}
67
68GCButton::~GCButton() = default;
69
70std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
71 const auto button_id = params.Get("button", 0);
72 const auto port = static_cast<u32>(params.Get("port", 0));
73
74 constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
75
76 // button is not an axis/stick button
77 if (button_id != PAD_STICK_ID) {
78 return std::make_unique<GCButton>(port, button_id, adapter.get());
79 }
80
81 // For Axis buttons, used by the binary sticks.
82 if (button_id == PAD_STICK_ID) {
83 const int axis = params.Get("axis", 0);
84 const float threshold = params.Get("threshold", 0.25f);
85 const std::string direction_name = params.Get("direction", "");
86 bool trigger_if_greater;
87 if (direction_name == "+") {
88 trigger_if_greater = true;
89 } else if (direction_name == "-") {
90 trigger_if_greater = false;
91 } else {
92 trigger_if_greater = true;
93 LOG_ERROR(Input, "Unknown direction {}", direction_name);
94 }
95 return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
96 adapter.get());
97 }
98
99 return nullptr;
100}
101
102Common::ParamPackage GCButtonFactory::GetNextInput() const {
103 Common::ParamPackage params;
104 GCAdapter::GCPadStatus pad;
105 auto& queue = adapter->GetPadQueue();
106 while (queue.Pop(pad)) {
107 // This while loop will break on the earliest detected button
108 params.Set("engine", "gcpad");
109 params.Set("port", static_cast<s32>(pad.port));
110 if (pad.button != GCAdapter::PadButton::Undefined) {
111 params.Set("button", static_cast<u16>(pad.button));
112 }
113
114 // For Axis button implementation
115 if (pad.axis != GCAdapter::PadAxes::Undefined) {
116 params.Set("axis", static_cast<u8>(pad.axis));
117 params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
118 params.Set("threshold", "0.25");
119 if (pad.axis_value > 0) {
120 params.Set("direction", "+");
121 } else {
122 params.Set("direction", "-");
123 }
124 break;
125 }
126 }
127 return params;
128}
129
130void GCButtonFactory::BeginConfiguration() {
131 polling = true;
132 adapter->BeginConfiguration();
133}
134
135void GCButtonFactory::EndConfiguration() {
136 polling = false;
137 adapter->EndConfiguration();
138}
139
140class GCAnalog final : public Input::AnalogDevice {
141public:
142 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
143 float deadzone_, float range_, const GCAdapter::Adapter* adapter)
144 : port(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
145 deadzone(deadzone_), range(range_), gcadapter(adapter) {}
146
147 float GetAxis(u32 axis) const {
148 if (gcadapter->DeviceConnected(port)) {
149 std::lock_guard lock{mutex};
150 const auto axis_value =
151 static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
152 return (axis_value) / (100.0f * range);
153 }
154 return 0.0f;
155 }
156
157 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
158 float x = GetAxis(analog_axis_x);
159 float y = GetAxis(analog_axis_y);
160 if (invert_x) {
161 x = -x;
162 }
163 if (invert_y) {
164 y = -y;
165 }
166 // Make sure the coordinates are in the unit circle,
167 // otherwise normalize it.
168 float r = x * x + y * y;
169 if (r > 1.0f) {
170 r = std::sqrt(r);
171 x /= r;
172 y /= r;
173 }
174
175 return {x, y};
176 }
177
178 std::tuple<float, float> GetStatus() const override {
179 const auto [x, y] = GetAnalog(axis_x, axis_y);
180 const float r = std::sqrt((x * x) + (y * y));
181 if (r > deadzone) {
182 return {x / r * (r - deadzone) / (1 - deadzone),
183 y / r * (r - deadzone) / (1 - deadzone)};
184 }
185 return {0.0f, 0.0f};
186 }
187
188 std::tuple<float, float> GetRawStatus() const override {
189 const float x = GetAxis(axis_x);
190 const float y = GetAxis(axis_y);
191 return {x, y};
192 }
193
194 Input::AnalogProperties GetAnalogProperties() const override {
195 return {deadzone, range, 0.5f};
196 }
197
198 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
199 const auto [x, y] = GetStatus();
200 const float directional_deadzone = 0.5f;
201 switch (direction) {
202 case Input::AnalogDirection::RIGHT:
203 return x > directional_deadzone;
204 case Input::AnalogDirection::LEFT:
205 return x < -directional_deadzone;
206 case Input::AnalogDirection::UP:
207 return y > directional_deadzone;
208 case Input::AnalogDirection::DOWN:
209 return y < -directional_deadzone;
210 }
211 return false;
212 }
213
214private:
215 const u32 port;
216 const u32 axis_x;
217 const u32 axis_y;
218 const bool invert_x;
219 const bool invert_y;
220 const float deadzone;
221 const float range;
222 const GCAdapter::Adapter* gcadapter;
223 mutable std::mutex mutex;
224};
225
226/// An analog device factory that creates analog devices from GC Adapter
227GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
228 : adapter(std::move(adapter_)) {}
229
230/**
231 * Creates analog device from joystick axes
232 * @param params contains parameters for creating the device:
233 * - "port": the nth gcpad on the adapter
234 * - "axis_x": the index of the axis to be bind as x-axis
235 * - "axis_y": the index of the axis to be bind as y-axis
236 */
237std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
238 const auto port = static_cast<u32>(params.Get("port", 0));
239 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
240 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
241 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
242 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
243 const std::string invert_x_value = params.Get("invert_x", "+");
244 const std::string invert_y_value = params.Get("invert_y", "+");
245 const bool invert_x = invert_x_value == "-";
246 const bool invert_y = invert_y_value == "-";
247
248 return std::make_unique<GCAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
249 adapter.get());
250}
251
252void GCAnalogFactory::BeginConfiguration() {
253 polling = true;
254 adapter->BeginConfiguration();
255}
256
257void GCAnalogFactory::EndConfiguration() {
258 polling = false;
259 adapter->EndConfiguration();
260}
261
262Common::ParamPackage GCAnalogFactory::GetNextInput() {
263 GCAdapter::GCPadStatus pad;
264 Common::ParamPackage params;
265 auto& queue = adapter->GetPadQueue();
266 while (queue.Pop(pad)) {
267 if (pad.button != GCAdapter::PadButton::Undefined) {
268 params.Set("engine", "gcpad");
269 params.Set("port", static_cast<s32>(pad.port));
270 params.Set("button", static_cast<u16>(pad.button));
271 return params;
272 }
273 if (pad.axis == GCAdapter::PadAxes::Undefined ||
274 std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
275 continue;
276 }
277 // An analog device needs two axes, so we need to store the axis for later and wait for
278 // a second input event. The axes also must be from the same joystick.
279 const u8 axis = static_cast<u8>(pad.axis);
280 if (axis == 0 || axis == 1) {
281 analog_x_axis = 0;
282 analog_y_axis = 1;
283 controller_number = static_cast<s32>(pad.port);
284 break;
285 }
286 if (axis == 2 || axis == 3) {
287 analog_x_axis = 2;
288 analog_y_axis = 3;
289 controller_number = static_cast<s32>(pad.port);
290 break;
291 }
292
293 if (analog_x_axis == -1) {
294 analog_x_axis = axis;
295 controller_number = static_cast<s32>(pad.port);
296 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
297 controller_number == static_cast<s32>(pad.port)) {
298 analog_y_axis = axis;
299 break;
300 }
301 }
302 if (analog_x_axis != -1 && analog_y_axis != -1) {
303 params.Set("engine", "gcpad");
304 params.Set("port", controller_number);
305 params.Set("axis_x", analog_x_axis);
306 params.Set("axis_y", analog_y_axis);
307 params.Set("invert_x", "+");
308 params.Set("invert_y", "+");
309 analog_x_axis = -1;
310 analog_y_axis = -1;
311 controller_number = -1;
312 return params;
313 }
314 return params;
315}
316
317class GCVibration final : public Input::VibrationDevice {
318public:
319 explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
320 : port(port_), gcadapter(adapter) {}
321
322 u8 GetStatus() const override {
323 return gcadapter->RumblePlay(port, 0);
324 }
325
326 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
327 [[maybe_unused]] f32 freq_high) const override {
328 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
329 const auto processed_amplitude =
330 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
331
332 return gcadapter->RumblePlay(port, processed_amplitude);
333 }
334
335private:
336 const u32 port;
337 GCAdapter::Adapter* gcadapter;
338};
339
340/// An vibration device factory that creates vibration devices from GC Adapter
341GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
342 : adapter(std::move(adapter_)) {}
343
344/**
345 * Creates a vibration device from a joystick
346 * @param params contains parameters for creating the device:
347 * - "port": the nth gcpad on the adapter
348 */
349std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
350 const Common::ParamPackage& params) {
351 const auto port = static_cast<u32>(params.Get("port", 0));
352
353 return std::make_unique<GCVibration>(port, adapter.get());
354}
355
356} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
deleted file mode 100644
index d1271e3ea..000000000
--- a/src/input_common/gcadapter/gc_poller.h
+++ /dev/null
@@ -1,78 +0,0 @@
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 <memory>
8#include "core/frontend/input.h"
9#include "input_common/gcadapter/gc_adapter.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a gcpad. It receives gcpad events and forward them
15 * to all button devices it created.
16 */
17class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput() const;
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<GCAdapter::Adapter> adapter;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from GC Adapter
44class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49 Common::ParamPackage GetNextInput();
50
51 /// For device input configuration/polling
52 void BeginConfiguration();
53 void EndConfiguration();
54
55 bool IsPolling() const {
56 return polling;
57 }
58
59private:
60 std::shared_ptr<GCAdapter::Adapter> adapter;
61 int analog_x_axis = -1;
62 int analog_y_axis = -1;
63 int controller_number = -1;
64 bool polling = false;
65};
66
67/// A vibration device factory creates vibration devices from GC Adapter
68class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
69public:
70 explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
71
72 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
73
74private:
75 std::shared_ptr<GCAdapter::Adapter> adapter;
76};
77
78} // namespace InputCommon
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
new file mode 100644
index 000000000..77fcd655e
--- /dev/null
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -0,0 +1,304 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <cmath>
7#include "common/math_util.h"
8#include "common/settings.h"
9#include "input_common/helpers/stick_from_buttons.h"
10
11namespace InputCommon {
12
13class Stick final : public Common::Input::InputDevice {
14public:
15 using Button = std::unique_ptr<Common::Input::InputDevice>;
16
17 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
18 float modifier_scale_, float modifier_angle_)
19 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
20 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
21 modifier_angle(modifier_angle_) {
22 Common::Input::InputCallback button_up_callback{
23 [this](Common::Input::CallbackStatus callback_) { UpdateUpButtonStatus(callback_); }};
24 Common::Input::InputCallback button_down_callback{
25 [this](Common::Input::CallbackStatus callback_) { UpdateDownButtonStatus(callback_); }};
26 Common::Input::InputCallback button_left_callback{
27 [this](Common::Input::CallbackStatus callback_) { UpdateLeftButtonStatus(callback_); }};
28 Common::Input::InputCallback button_right_callback{
29 [this](Common::Input::CallbackStatus callback_) {
30 UpdateRightButtonStatus(callback_);
31 }};
32 Common::Input::InputCallback button_modifier_callback{
33 [this](Common::Input::CallbackStatus callback_) { UpdateModButtonStatus(callback_); }};
34 up->SetCallback(button_up_callback);
35 down->SetCallback(button_down_callback);
36 left->SetCallback(button_left_callback);
37 right->SetCallback(button_right_callback);
38 modifier->SetCallback(button_modifier_callback);
39 last_x_axis_value = 0.0f;
40 last_y_axis_value = 0.0f;
41 }
42
43 bool IsAngleGreater(float old_angle, float new_angle) const {
44 constexpr float TAU = Common::PI * 2.0f;
45 // Use wider angle to ease the transition.
46 constexpr float aperture = TAU * 0.15f;
47 const float top_limit = new_angle + aperture;
48 return (old_angle > new_angle && old_angle <= top_limit) ||
49 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
50 }
51
52 bool IsAngleSmaller(float old_angle, float new_angle) const {
53 constexpr float TAU = Common::PI * 2.0f;
54 // Use wider angle to ease the transition.
55 constexpr float aperture = TAU * 0.15f;
56 const float bottom_limit = new_angle - aperture;
57 return (old_angle >= bottom_limit && old_angle < new_angle) ||
58 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
59 }
60
61 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
62 constexpr float TAU = Common::PI * 2.0f;
63 float new_angle = angle;
64
65 auto time_difference = static_cast<float>(
66 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
67 time_difference /= 1000.0f * 1000.0f;
68 if (time_difference > 0.5f) {
69 time_difference = 0.5f;
70 }
71
72 if (IsAngleGreater(new_angle, goal_angle)) {
73 new_angle -= modifier_angle * time_difference;
74 if (new_angle < 0) {
75 new_angle += TAU;
76 }
77 if (!IsAngleGreater(new_angle, goal_angle)) {
78 return goal_angle;
79 }
80 } else if (IsAngleSmaller(new_angle, goal_angle)) {
81 new_angle += modifier_angle * time_difference;
82 if (new_angle >= TAU) {
83 new_angle -= TAU;
84 }
85 if (!IsAngleSmaller(new_angle, goal_angle)) {
86 return goal_angle;
87 }
88 } else {
89 return goal_angle;
90 }
91 return new_angle;
92 }
93
94 void SetGoalAngle(bool r, bool l, bool u, bool d) {
95 // Move to the right
96 if (r && !u && !d) {
97 goal_angle = 0.0f;
98 }
99
100 // Move to the upper right
101 if (r && u && !d) {
102 goal_angle = Common::PI * 0.25f;
103 }
104
105 // Move up
106 if (u && !l && !r) {
107 goal_angle = Common::PI * 0.5f;
108 }
109
110 // Move to the upper left
111 if (l && u && !d) {
112 goal_angle = Common::PI * 0.75f;
113 }
114
115 // Move to the left
116 if (l && !u && !d) {
117 goal_angle = Common::PI;
118 }
119
120 // Move to the bottom left
121 if (l && !u && d) {
122 goal_angle = Common::PI * 1.25f;
123 }
124
125 // Move down
126 if (d && !l && !r) {
127 goal_angle = Common::PI * 1.5f;
128 }
129
130 // Move to the bottom right
131 if (r && !u && d) {
132 goal_angle = Common::PI * 1.75f;
133 }
134 }
135
136 void UpdateUpButtonStatus(Common::Input::CallbackStatus button_callback) {
137 up_status = button_callback.button_status.value;
138 UpdateStatus();
139 }
140
141 void UpdateDownButtonStatus(Common::Input::CallbackStatus button_callback) {
142 down_status = button_callback.button_status.value;
143 UpdateStatus();
144 }
145
146 void UpdateLeftButtonStatus(Common::Input::CallbackStatus button_callback) {
147 left_status = button_callback.button_status.value;
148 UpdateStatus();
149 }
150
151 void UpdateRightButtonStatus(Common::Input::CallbackStatus button_callback) {
152 right_status = button_callback.button_status.value;
153 UpdateStatus();
154 }
155
156 void UpdateModButtonStatus(Common::Input::CallbackStatus button_callback) {
157 modifier_status = button_callback.button_status.value;
158 UpdateStatus();
159 }
160
161 void UpdateStatus() {
162 const float coef = modifier_status ? modifier_scale : 1.0f;
163
164 bool r = right_status;
165 bool l = left_status;
166 bool u = up_status;
167 bool d = down_status;
168
169 // Eliminate contradictory movements
170 if (r && l) {
171 r = false;
172 l = false;
173 }
174 if (u && d) {
175 u = false;
176 d = false;
177 }
178
179 // Move if a key is pressed
180 if (r || l || u || d) {
181 amplitude = coef;
182 } else {
183 amplitude = 0;
184 }
185
186 const auto now = std::chrono::steady_clock::now();
187 const auto time_difference = static_cast<u64>(
188 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
189
190 if (time_difference < 10) {
191 // Disable analog mode if inputs are too fast
192 SetGoalAngle(r, l, u, d);
193 angle = goal_angle;
194 } else {
195 angle = GetAngle(now);
196 SetGoalAngle(r, l, u, d);
197 }
198
199 last_update = now;
200 Common::Input::CallbackStatus status{
201 .type = Common::Input::InputType::Stick,
202 .stick_status = GetStatus(),
203 };
204 last_x_axis_value = status.stick_status.x.raw_value;
205 last_y_axis_value = status.stick_status.y.raw_value;
206 TriggerOnChange(status);
207 }
208
209 void ForceUpdate() override {
210 up->ForceUpdate();
211 down->ForceUpdate();
212 left->ForceUpdate();
213 right->ForceUpdate();
214 modifier->ForceUpdate();
215 }
216
217 void SoftUpdate() override {
218 Common::Input::CallbackStatus status{
219 .type = Common::Input::InputType::Stick,
220 .stick_status = GetStatus(),
221 };
222 if (last_x_axis_value == status.stick_status.x.raw_value &&
223 last_y_axis_value == status.stick_status.y.raw_value) {
224 return;
225 }
226 last_x_axis_value = status.stick_status.x.raw_value;
227 last_y_axis_value = status.stick_status.y.raw_value;
228 TriggerOnChange(status);
229 }
230
231 Common::Input::StickStatus GetStatus() const {
232 Common::Input::StickStatus status{};
233 status.x.properties = properties;
234 status.y.properties = properties;
235 if (Settings::values.emulate_analog_keyboard) {
236 const auto now = std::chrono::steady_clock::now();
237 float angle_ = GetAngle(now);
238 status.x.raw_value = std::cos(angle_) * amplitude;
239 status.y.raw_value = std::sin(angle_) * amplitude;
240 return status;
241 }
242 constexpr float SQRT_HALF = 0.707106781f;
243 int x = 0, y = 0;
244 if (right_status) {
245 ++x;
246 }
247 if (left_status) {
248 --x;
249 }
250 if (up_status) {
251 ++y;
252 }
253 if (down_status) {
254 --y;
255 }
256 const float coef = modifier_status ? modifier_scale : 1.0f;
257 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
258 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
259 return status;
260 }
261
262private:
263 Button up;
264 Button down;
265 Button left;
266 Button right;
267 Button modifier;
268 float modifier_scale;
269 float modifier_angle;
270 float angle{};
271 float goal_angle{};
272 float amplitude{};
273 bool up_status;
274 bool down_status;
275 bool left_status;
276 bool right_status;
277 bool modifier_status;
278 float last_x_axis_value;
279 float last_y_axis_value;
280 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
281 std::chrono::time_point<std::chrono::steady_clock> last_update;
282};
283
284std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
285 const Common::ParamPackage& params) {
286 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
287 auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
288 params.Get("up", null_engine));
289 auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
290 params.Get("down", null_engine));
291 auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
292 params.Get("left", null_engine));
293 auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
294 params.Get("right", null_engine));
295 auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
296 params.Get("modifier", null_engine));
297 auto modifier_scale = params.Get("modifier_scale", 0.5f);
298 auto modifier_angle = params.Get("modifier_angle", 5.5f);
299 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
300 std::move(right), std::move(modifier), modifier_scale,
301 modifier_angle);
302}
303
304} // namespace InputCommon
diff --git a/src/input_common/analog_from_button.h b/src/input_common/helpers/stick_from_buttons.h
index bbd583dd9..437ace4f7 100755..100644
--- a/src/input_common/analog_from_button.h
+++ b/src/input_common/helpers/stick_from_buttons.h
@@ -4,8 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include "common/input.h"
8#include "core/frontend/input.h"
9 8
10namespace InputCommon { 9namespace InputCommon {
11 10
@@ -13,7 +12,7 @@ namespace InputCommon {
13 * An analog device factory that takes direction button devices and combines them into a analog 12 * An analog device factory that takes direction button devices and combines them into a analog
14 * device. 13 * device.
15 */ 14 */
16class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> { 15class StickFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
17public: 16public:
18 /** 17 /**
19 * Creates an analog device from direction button devices 18 * Creates an analog device from direction button devices
@@ -25,7 +24,7 @@ public:
25 * - "modifier": a serialized ParamPackage for creating a button device as the modifier 24 * - "modifier": a serialized ParamPackage for creating a button device as the modifier
26 * - "modifier_scale": a float for the multiplier the modifier gives to the position 25 * - "modifier_scale": a float for the multiplier the modifier gives to the position
27 */ 26 */
28 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; 27 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
29}; 28};
30 29
31} // namespace InputCommon 30} // namespace InputCommon
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
new file mode 100644
index 000000000..35d60bc90
--- /dev/null
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -0,0 +1,81 @@
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 <algorithm>
6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/helpers/touch_from_buttons.h"
9
10namespace InputCommon {
11
12class TouchFromButtonDevice final : public Common::Input::InputDevice {
13public:
14 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
16 : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
17 Common::Input::InputCallback button_up_callback{
18 [this](Common::Input::CallbackStatus callback_) { UpdateButtonStatus(callback_); }};
19 last_button_value = false;
20 button->SetCallback(button_up_callback);
21 button->ForceUpdate();
22 }
23
24 void ForceUpdate() override {
25 button->ForceUpdate();
26 }
27
28 Common::Input::TouchStatus GetStatus(bool pressed) const {
29 const Common::Input::ButtonStatus button_status{
30 .value = pressed,
31 };
32 Common::Input::TouchStatus status{
33 .pressed = button_status,
34 .x = {},
35 .y = {},
36 .id = touch_id,
37 };
38 status.x.properties = properties;
39 status.y.properties = properties;
40
41 if (!pressed) {
42 return status;
43 }
44
45 status.x.raw_value = x;
46 status.y.raw_value = y;
47 return status;
48 }
49
50 void UpdateButtonStatus(Common::Input::CallbackStatus button_callback) {
51 const Common::Input::CallbackStatus status{
52 .type = Common::Input::InputType::Touch,
53 .touch_status = GetStatus(button_callback.button_status.value),
54 };
55 if (last_button_value != button_callback.button_status.value) {
56 last_button_value = button_callback.button_status.value;
57 TriggerOnChange(status);
58 }
59 }
60
61private:
62 Button button;
63 bool last_button_value;
64 const int touch_id;
65 const float x;
66 const float y;
67 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
68};
69
70std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
71 const Common::ParamPackage& params) {
72 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
73 auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
74 params.Get("button", null_engine));
75 const auto touch_id = params.Get("touch_id", 0);
76 const float x = params.Get("x", 0.0f) / 1280.0f;
77 const float y = params.Get("y", 0.0f) / 720.0f;
78 return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
79}
80
81} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.h b/src/input_common/helpers/touch_from_buttons.h
index 8b4d1aa96..628f18215 100644
--- a/src/input_common/touch_from_button.h
+++ b/src/input_common/helpers/touch_from_buttons.h
@@ -4,20 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include "common/input.h"
8#include "core/frontend/input.h"
9 8
10namespace InputCommon { 9namespace InputCommon {
11 10
12/** 11/**
13 * A touch device factory that takes a list of button devices and combines them into a touch device. 12 * A touch device factory that takes a list of button devices and combines them into a touch device.
14 */ 13 */
15class TouchFromButtonFactory final : public Input::Factory<Input::TouchDevice> { 14class TouchFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
16public: 15public:
17 /** 16 /**
18 * Creates a touch device from a list of button devices 17 * Creates a touch device from a list of button devices
19 */ 18 */
20 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; 19 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
21}; 20};
22 21
23} // namespace InputCommon 22} // namespace InputCommon
diff --git a/src/input_common/udp/protocol.cpp b/src/input_common/helpers/udp_protocol.cpp
index 5e50bd612..cdeab7e11 100644
--- a/src/input_common/udp/protocol.cpp
+++ b/src/input_common/helpers/udp_protocol.cpp
@@ -5,7 +5,7 @@
5#include <cstddef> 5#include <cstddef>
6#include <cstring> 6#include <cstring>
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "input_common/udp/protocol.h" 8#include "input_common/helpers/udp_protocol.h"
9 9
10namespace InputCommon::CemuhookUDP { 10namespace InputCommon::CemuhookUDP {
11 11
diff --git a/src/input_common/udp/protocol.h b/src/input_common/helpers/udp_protocol.h
index 1bdc9209e..bcba12c58 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -56,6 +56,12 @@ constexpr Type GetMessageType();
56 56
57namespace Request { 57namespace Request {
58 58
59enum RegisterFlags : u8 {
60 AllPads,
61 PadID,
62 PadMACAdddress,
63};
64
59struct Version {}; 65struct Version {};
60/** 66/**
61 * Requests the server to send information about what controllers are plugged into the ports 67 * Requests the server to send information about what controllers are plugged into the ports
@@ -77,13 +83,8 @@ static_assert(std::is_trivially_copyable_v<PortInfo>,
77 * timeout seems to be 5 seconds. 83 * timeout seems to be 5 seconds.
78 */ 84 */
79struct PadData { 85struct PadData {
80 enum class Flags : u8 {
81 AllPorts,
82 Id,
83 Mac,
84 };
85 /// Determines which method will be used as a look up for the controller 86 /// Determines which method will be used as a look up for the controller
86 Flags flags{}; 87 RegisterFlags flags{};
87 /// Index of the port of the controller to retrieve data about 88 /// Index of the port of the controller to retrieve data about
88 u8 port_id{}; 89 u8 port_id{};
89 /// Mac address of the controller to retrieve data about 90 /// Mac address of the controller to retrieve data about
@@ -113,6 +114,36 @@ Message<T> Create(const T data, const u32 client_id = 0) {
113 114
114namespace Response { 115namespace Response {
115 116
117enum class ConnectionType : u8 {
118 None,
119 Usb,
120 Bluetooth,
121};
122
123enum class State : u8 {
124 Disconnected,
125 Reserved,
126 Connected,
127};
128
129enum class Model : u8 {
130 None,
131 PartialGyro,
132 FullGyro,
133 Generic,
134};
135
136enum class Battery : u8 {
137 None = 0x00,
138 Dying = 0x01,
139 Low = 0x02,
140 Medium = 0x03,
141 High = 0x04,
142 Full = 0x05,
143 Charging = 0xEE,
144 Charged = 0xEF,
145};
146
116struct Version { 147struct Version {
117 u16_le version{}; 148 u16_le version{};
118}; 149};
@@ -122,11 +153,11 @@ static_assert(std::is_trivially_copyable_v<Version>,
122 153
123struct PortInfo { 154struct PortInfo {
124 u8 id{}; 155 u8 id{};
125 u8 state{}; 156 State state{};
126 u8 model{}; 157 Model model{};
127 u8 connection_type{}; 158 ConnectionType connection_type{};
128 MacAddress mac; 159 MacAddress mac;
129 u8 battery{}; 160 Battery battery{};
130 u8 is_pad_active{}; 161 u8 is_pad_active{};
131}; 162};
132static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); 163static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
@@ -177,18 +208,18 @@ struct PadData {
177 u8 right_stick_y{}; 208 u8 right_stick_y{};
178 209
179 struct AnalogButton { 210 struct AnalogButton {
180 u8 button_8{}; 211 u8 button_dpad_left_analog{};
181 u8 button_7{}; 212 u8 button_dpad_down_analog{};
182 u8 button_6{}; 213 u8 button_dpad_right_analog{};
183 u8 button_5{}; 214 u8 button_dpad_up_analog{};
184 u8 button_12{}; 215 u8 button_square_analog{};
185 u8 button_11{}; 216 u8 button_cross_analog{};
186 u8 button_10{}; 217 u8 button_circle_analog{};
187 u8 button_9{}; 218 u8 button_triangle_analog{};
188 u8 button_16{}; 219 u8 button_r1_analog{};
189 u8 button_15{}; 220 u8 button_l1_analog{};
190 u8 button_14{}; 221 u8 trigger_r2{};
191 u8 button_13{}; 222 u8 trigger_l2{};
192 } analog_button; 223 } analog_button;
193 224
194 std::array<TouchPad, 2> touch; 225 std::array<TouchPad, 2> touch;
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
new file mode 100644
index 000000000..2b2105376
--- /dev/null
+++ b/src/input_common/input_engine.cpp
@@ -0,0 +1,364 @@
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/logging/log.h"
6#include "common/param_package.h"
7#include "input_common/input_engine.h"
8
9namespace InputCommon {
10
11void InputEngine::PreSetController(const PadIdentifier& identifier) {
12 std::lock_guard lock{mutex};
13 if (!controller_list.contains(identifier)) {
14 controller_list.insert_or_assign(identifier, ControllerData{});
15 }
16}
17
18void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
19 std::lock_guard lock{mutex};
20 ControllerData& controller = controller_list.at(identifier);
21 if (!controller.buttons.contains(button)) {
22 controller.buttons.insert_or_assign(button, false);
23 }
24}
25
26void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
27 std::lock_guard lock{mutex};
28 ControllerData& controller = controller_list.at(identifier);
29 if (!controller.hat_buttons.contains(button)) {
30 controller.hat_buttons.insert_or_assign(button, u8{0});
31 }
32}
33
34void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
35 std::lock_guard lock{mutex};
36 ControllerData& controller = controller_list.at(identifier);
37 if (!controller.axes.contains(axis)) {
38 controller.axes.insert_or_assign(axis, 0.0f);
39 }
40}
41
42void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
43 std::lock_guard lock{mutex};
44 ControllerData& controller = controller_list.at(identifier);
45 if (!controller.motions.contains(motion)) {
46 controller.motions.insert_or_assign(motion, BasicMotion{});
47 }
48}
49
50void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
51 {
52 std::lock_guard lock{mutex};
53 ControllerData& controller = controller_list.at(identifier);
54 if (!configuring) {
55 controller.buttons.insert_or_assign(button, value);
56 }
57 }
58 TriggerOnButtonChange(identifier, button, value);
59}
60
61void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
62 {
63 std::lock_guard lock{mutex};
64 ControllerData& controller = controller_list.at(identifier);
65 if (!configuring) {
66 controller.hat_buttons.insert_or_assign(button, value);
67 }
68 }
69 TriggerOnHatButtonChange(identifier, button, value);
70}
71
72void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
73 {
74 std::lock_guard lock{mutex};
75 ControllerData& controller = controller_list.at(identifier);
76 if (!configuring) {
77 controller.axes.insert_or_assign(axis, value);
78 }
79 }
80 TriggerOnAxisChange(identifier, axis, value);
81}
82
83void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) {
84 {
85 std::lock_guard lock{mutex};
86 ControllerData& controller = controller_list.at(identifier);
87 if (!configuring) {
88 controller.battery = value;
89 }
90 }
91 TriggerOnBatteryChange(identifier, value);
92}
93
94void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value) {
95 {
96 std::lock_guard lock{mutex};
97 ControllerData& controller = controller_list.at(identifier);
98 if (!configuring) {
99 controller.motions.insert_or_assign(motion, value);
100 }
101 }
102 TriggerOnMotionChange(identifier, motion, value);
103}
104
105bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
106 std::lock_guard lock{mutex};
107 if (!controller_list.contains(identifier)) {
108 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
109 identifier.pad, identifier.port);
110 return false;
111 }
112 ControllerData controller = controller_list.at(identifier);
113 if (!controller.buttons.contains(button)) {
114 LOG_ERROR(Input, "Invalid button {}", button);
115 return false;
116 }
117 return controller.buttons.at(button);
118}
119
120bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
121 std::lock_guard lock{mutex};
122 if (!controller_list.contains(identifier)) {
123 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
124 identifier.pad, identifier.port);
125 return false;
126 }
127 ControllerData controller = controller_list.at(identifier);
128 if (!controller.hat_buttons.contains(button)) {
129 LOG_ERROR(Input, "Invalid hat button {}", button);
130 return false;
131 }
132 return (controller.hat_buttons.at(button) & direction) != 0;
133}
134
135f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
136 std::lock_guard lock{mutex};
137 if (!controller_list.contains(identifier)) {
138 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
139 identifier.pad, identifier.port);
140 return 0.0f;
141 }
142 ControllerData controller = controller_list.at(identifier);
143 if (!controller.axes.contains(axis)) {
144 LOG_ERROR(Input, "Invalid axis {}", axis);
145 return 0.0f;
146 }
147 return controller.axes.at(axis);
148}
149
150BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
151 std::lock_guard lock{mutex};
152 if (!controller_list.contains(identifier)) {
153 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
154 identifier.pad, identifier.port);
155 return BatteryLevel::Charging;
156 }
157 ControllerData controller = controller_list.at(identifier);
158 return controller.battery;
159}
160
161BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
162 std::lock_guard lock{mutex};
163 if (!controller_list.contains(identifier)) {
164 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
165 identifier.pad, identifier.port);
166 return {};
167 }
168 ControllerData controller = controller_list.at(identifier);
169 return controller.motions.at(motion);
170}
171
172void InputEngine::ResetButtonState() {
173 for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
174 for (std::pair<int, bool> button : controller.second.buttons) {
175 SetButton(controller.first, button.first, false);
176 }
177 for (std::pair<int, bool> button : controller.second.hat_buttons) {
178 SetHatButton(controller.first, button.first, false);
179 }
180 }
181}
182
183void InputEngine::ResetAnalogState() {
184 for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
185 for (std::pair<int, float> axis : controller.second.axes) {
186 SetAxis(controller.first, axis.first, 0.0);
187 }
188 }
189}
190
191void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
192 std::lock_guard lock{mutex_callback};
193 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
194 const InputIdentifier& poller = poller_pair.second;
195 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
196 continue;
197 }
198 if (poller.callback.on_change) {
199 poller.callback.on_change();
200 }
201 }
202 if (!configuring || !mapping_callback.on_data) {
203 return;
204 }
205
206 PreSetButton(identifier, button);
207 if (value == GetButton(identifier, button)) {
208 return;
209 }
210 mapping_callback.on_data(MappingData{
211 .engine = GetEngineName(),
212 .pad = identifier,
213 .type = EngineInputType::Button,
214 .index = button,
215 .button_value = value,
216 });
217}
218
219void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
220 std::lock_guard lock{mutex_callback};
221 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
222 const InputIdentifier& poller = poller_pair.second;
223 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
224 continue;
225 }
226 if (poller.callback.on_change) {
227 poller.callback.on_change();
228 }
229 }
230 if (!configuring || !mapping_callback.on_data) {
231 return;
232 }
233 for (std::size_t index = 1; index < 0xff; index <<= 1) {
234 bool button_value = (value & index) != 0;
235 if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) {
236 continue;
237 }
238 mapping_callback.on_data(MappingData{
239 .engine = GetEngineName(),
240 .pad = identifier,
241 .type = EngineInputType::HatButton,
242 .index = button,
243 .hat_name = GetHatButtonName(static_cast<u8>(index)),
244 });
245 }
246}
247
248void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
249 std::lock_guard lock{mutex_callback};
250 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
251 const InputIdentifier& poller = poller_pair.second;
252 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
253 continue;
254 }
255 if (poller.callback.on_change) {
256 poller.callback.on_change();
257 }
258 }
259 if (!configuring || !mapping_callback.on_data) {
260 return;
261 }
262 if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) {
263 return;
264 }
265 mapping_callback.on_data(MappingData{
266 .engine = GetEngineName(),
267 .pad = identifier,
268 .type = EngineInputType::Analog,
269 .index = axis,
270 .axis_value = value,
271 });
272}
273
274void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
275 [[maybe_unused]] BatteryLevel value) {
276 std::lock_guard lock{mutex_callback};
277 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
278 const InputIdentifier& poller = poller_pair.second;
279 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
280 continue;
281 }
282 if (poller.callback.on_change) {
283 poller.callback.on_change();
284 }
285 }
286}
287
288void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
289 BasicMotion value) {
290 std::lock_guard lock{mutex_callback};
291 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
292 const InputIdentifier& poller = poller_pair.second;
293 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
294 continue;
295 }
296 if (poller.callback.on_change) {
297 poller.callback.on_change();
298 }
299 }
300 if (!configuring || !mapping_callback.on_data) {
301 return;
302 }
303 if (std::abs(value.gyro_x) < 0.6f && std::abs(value.gyro_y) < 0.6f &&
304 std::abs(value.gyro_z) < 0.6f) {
305 return;
306 }
307 mapping_callback.on_data(MappingData{
308 .engine = GetEngineName(),
309 .pad = identifier,
310 .type = EngineInputType::Motion,
311 .index = motion,
312 .motion_value = value,
313 });
314}
315
316bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
317 const PadIdentifier& identifier, EngineInputType type,
318 int index) const {
319 if (input_identifier.type != type) {
320 return false;
321 }
322 if (input_identifier.index != index) {
323 return false;
324 }
325 if (input_identifier.identifier != identifier) {
326 return false;
327 }
328 return true;
329}
330
331void InputEngine::BeginConfiguration() {
332 configuring = true;
333}
334
335void InputEngine::EndConfiguration() {
336 configuring = false;
337}
338
339const std::string& InputEngine::GetEngineName() const {
340 return input_engine;
341}
342
343int InputEngine::SetCallback(InputIdentifier input_identifier) {
344 std::lock_guard lock{mutex_callback};
345 callback_list.insert_or_assign(last_callback_key, input_identifier);
346 return last_callback_key++;
347}
348
349void InputEngine::SetMappingCallback(MappingCallback callback) {
350 std::lock_guard lock{mutex_callback};
351 mapping_callback = std::move(callback);
352}
353
354void InputEngine::DeleteCallback(int key) {
355 std::lock_guard lock{mutex_callback};
356 const auto& iterator = callback_list.find(key);
357 if (iterator == callback_list.end()) {
358 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
359 return;
360 }
361 callback_list.erase(iterator);
362}
363
364} // namespace InputCommon
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
new file mode 100644
index 000000000..02272b3f8
--- /dev/null
+++ b/src/input_common/input_engine.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/common_types.h"
12#include "common/input.h"
13#include "common/param_package.h"
14#include "common/uuid.h"
15#include "input_common/main.h"
16
17// Pad Identifier of data source
18struct PadIdentifier {
19 Common::UUID guid{};
20 std::size_t port{};
21 std::size_t pad{};
22
23 friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
24};
25
26// Basic motion data containing data from the sensors and a timestamp in microsecons
27struct BasicMotion {
28 float gyro_x;
29 float gyro_y;
30 float gyro_z;
31 float accel_x;
32 float accel_y;
33 float accel_z;
34 u64 delta_timestamp;
35};
36
37// Stages of a battery charge
38enum class BatteryLevel {
39 Empty,
40 Critical,
41 Low,
42 Medium,
43 Full,
44 Charging,
45};
46
47// Types of input that are stored in the engine
48enum class EngineInputType {
49 None,
50 Button,
51 HatButton,
52 Analog,
53 Motion,
54 Battery,
55};
56
57namespace std {
58// Hash used to create lists from PadIdentifier data
59template <>
60struct hash<PadIdentifier> {
61 size_t operator()(const PadIdentifier& pad_id) const noexcept {
62 u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0];
63 hash_value ^= (static_cast<u64>(pad_id.port) << 32);
64 hash_value ^= static_cast<u64>(pad_id.pad);
65 return static_cast<size_t>(hash_value);
66 }
67};
68
69} // namespace std
70
71namespace InputCommon {
72
73// Data from the engine and device needed for creating a ParamPackage
74struct MappingData {
75 std::string engine{};
76 PadIdentifier pad{};
77 EngineInputType type{};
78 int index{};
79 bool button_value{};
80 std::string hat_name{};
81 f32 axis_value{};
82 BasicMotion motion_value{};
83};
84
85// Triggered if data changed on the controller
86struct UpdateCallback {
87 std::function<void()> on_change;
88};
89
90// Triggered if data changed on the controller and the engine is on configuring mode
91struct MappingCallback {
92 std::function<void(MappingData)> on_data;
93};
94
95// Input Identifier of data source
96struct InputIdentifier {
97 PadIdentifier identifier;
98 EngineInputType type;
99 int index;
100 UpdateCallback callback;
101};
102
103class InputEngine {
104public:
105 explicit InputEngine(const std::string& input_engine_) : input_engine(input_engine_) {
106 callback_list.clear();
107 }
108
109 virtual ~InputEngine() = default;
110
111 // Enable configuring mode for mapping
112 void BeginConfiguration();
113
114 // Disable configuring mode for mapping
115 void EndConfiguration();
116
117 // Sets a led pattern for a controller
118 virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
119 [[maybe_unused]] const Common::Input::LedStatus led_status) {
120 return;
121 }
122
123 // Sets rumble to a controller
124 virtual Common::Input::VibrationError SetRumble(
125 [[maybe_unused]] const PadIdentifier& identifier,
126 [[maybe_unused]] const Common::Input::VibrationStatus vibration) {
127 return Common::Input::VibrationError::NotSupported;
128 }
129
130 // Sets polling mode to a controller
131 virtual Common::Input::PollingError SetPollingMode(
132 [[maybe_unused]] const PadIdentifier& identifier,
133 [[maybe_unused]] const Common::Input::PollingMode vibration) {
134 return Common::Input::PollingError::NotSupported;
135 }
136
137 // Returns the engine name
138 [[nodiscard]] const std::string& GetEngineName() const;
139
140 /// Used for automapping features
141 virtual std::vector<Common::ParamPackage> GetInputDevices() const {
142 return {};
143 };
144
145 /// Retrieves the button mappings for the given device
146 virtual InputCommon::ButtonMapping GetButtonMappingForDevice(
147 [[maybe_unused]] const Common::ParamPackage& params) {
148 return {};
149 };
150
151 /// Retrieves the analog mappings for the given device
152 virtual InputCommon::AnalogMapping GetAnalogMappingForDevice(
153 [[maybe_unused]] const Common::ParamPackage& params) {
154 return {};
155 };
156
157 /// Retrieves the motion mappings for the given device
158 virtual InputCommon::MotionMapping GetMotionMappingForDevice(
159 [[maybe_unused]] const Common::ParamPackage& params) {
160 return {};
161 };
162
163 /// Retrieves the name of the given input.
164 virtual Common::Input::ButtonNames GetUIName(
165 [[maybe_unused]] const Common::ParamPackage& params) const {
166 return Common::Input::ButtonNames::Engine;
167 };
168
169 /// Retrieves the index number of the given hat button direction
170 virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const {
171 return 0;
172 };
173
174 void PreSetController(const PadIdentifier& identifier);
175 void PreSetButton(const PadIdentifier& identifier, int button);
176 void PreSetHatButton(const PadIdentifier& identifier, int button);
177 void PreSetAxis(const PadIdentifier& identifier, int axis);
178 void PreSetMotion(const PadIdentifier& identifier, int motion);
179 void ResetButtonState();
180 void ResetAnalogState();
181
182 bool GetButton(const PadIdentifier& identifier, int button) const;
183 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
184 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
185 BatteryLevel GetBattery(const PadIdentifier& identifier) const;
186 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
187
188 int SetCallback(InputIdentifier input_identifier);
189 void SetMappingCallback(MappingCallback callback);
190 void DeleteCallback(int key);
191
192protected:
193 void SetButton(const PadIdentifier& identifier, int button, bool value);
194 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
195 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
196 void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
197 void SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value);
198
199 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
200 return "Unknown";
201 }
202
203private:
204 struct ControllerData {
205 std::unordered_map<int, bool> buttons;
206 std::unordered_map<int, u8> hat_buttons;
207 std::unordered_map<int, float> axes;
208 std::unordered_map<int, BasicMotion> motions;
209 BatteryLevel battery;
210 };
211
212 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
213 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
214 void TriggerOnAxisChange(const PadIdentifier& identifier, int button, f32 value);
215 void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
216 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, BasicMotion value);
217
218 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
219 const PadIdentifier& identifier, EngineInputType type,
220 int index) const;
221
222 mutable std::mutex mutex;
223 mutable std::mutex mutex_callback;
224 bool configuring{false};
225 const std::string input_engine;
226 int last_callback_key = 0;
227 std::unordered_map<PadIdentifier, ControllerData> controller_list;
228 std::unordered_map<int, InputIdentifier> callback_list;
229 MappingCallback mapping_callback;
230};
231
232} // namespace InputCommon
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
new file mode 100644
index 000000000..6e0024b2d
--- /dev/null
+++ b/src/input_common/input_mapping.cpp
@@ -0,0 +1,207 @@
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/common_types.h"
6#include "common/settings.h"
7#include "input_common/input_engine.h"
8#include "input_common/input_mapping.h"
9
10namespace InputCommon {
11
12MappingFactory::MappingFactory() {}
13
14void MappingFactory::BeginMapping(Polling::InputType type) {
15 is_enabled = true;
16 input_type = type;
17 input_queue.Clear();
18 first_axis = -1;
19 second_axis = -1;
20}
21
22[[nodiscard]] const Common::ParamPackage MappingFactory::GetNextInput() {
23 Common::ParamPackage input;
24 input_queue.Pop(input);
25 return input;
26}
27
28void MappingFactory::RegisterInput(const MappingData& data) {
29 if (!is_enabled) {
30 return;
31 }
32 if (!IsDriverValid(data)) {
33 return;
34 }
35
36 switch (input_type) {
37 case Polling::InputType::Button:
38 RegisterButton(data);
39 return;
40 case Polling::InputType::Stick:
41 RegisterStick(data);
42 return;
43 case Polling::InputType::Motion:
44 RegisterMotion(data);
45 return;
46 default:
47 return;
48 }
49}
50
51void MappingFactory::StopMapping() {
52 is_enabled = false;
53 input_type = Polling::InputType::None;
54 input_queue.Clear();
55}
56
57void MappingFactory::RegisterButton(const MappingData& data) {
58 Common::ParamPackage new_input;
59 new_input.Set("engine", data.engine);
60 if (data.pad.guid != Common::UUID{}) {
61 new_input.Set("guid", data.pad.guid.Format());
62 }
63 new_input.Set("port", static_cast<int>(data.pad.port));
64 new_input.Set("pad", static_cast<int>(data.pad.pad));
65
66 switch (data.type) {
67 case EngineInputType::Button:
68 // Workaround for old compatibility
69 if (data.engine == "keyboard") {
70 new_input.Set("code", data.index);
71 break;
72 }
73 new_input.Set("button", data.index);
74 break;
75 case EngineInputType::HatButton:
76 new_input.Set("hat", data.index);
77 new_input.Set("direction", data.hat_name);
78 break;
79 case EngineInputType::Analog:
80 // Ignore mouse axis when mapping buttons
81 if (data.engine == "mouse") {
82 return;
83 }
84 new_input.Set("axis", data.index);
85 new_input.Set("threshold", 0.5f);
86 break;
87 default:
88 return;
89 }
90 input_queue.Push(new_input);
91}
92
93void MappingFactory::RegisterStick(const MappingData& data) {
94 Common::ParamPackage new_input;
95 new_input.Set("engine", data.engine);
96 if (data.pad.guid != Common::UUID{}) {
97 new_input.Set("guid", data.pad.guid.Format());
98 }
99 new_input.Set("port", static_cast<int>(data.pad.port));
100 new_input.Set("pad", static_cast<int>(data.pad.pad));
101
102 // If engine is mouse map the mouse position as a joystick
103 if (data.engine == "mouse") {
104 new_input.Set("axis_x", 0);
105 new_input.Set("axis_y", 1);
106 new_input.Set("threshold", 0.5f);
107 new_input.Set("range", 1.0f);
108 new_input.Set("deadzone", 0.0f);
109 input_queue.Push(new_input);
110 return;
111 }
112
113 switch (data.type) {
114 case EngineInputType::Button:
115 case EngineInputType::HatButton:
116 RegisterButton(data);
117 return;
118 case EngineInputType::Analog:
119 if (first_axis == data.index) {
120 return;
121 }
122 if (first_axis == -1) {
123 first_axis = data.index;
124 return;
125 }
126 new_input.Set("axis_x", first_axis);
127 new_input.Set("axis_y", data.index);
128 new_input.Set("threshold", 0.5f);
129 new_input.Set("range", 0.95f);
130 new_input.Set("deadzone", 0.15f);
131 break;
132 default:
133 return;
134 }
135 input_queue.Push(new_input);
136}
137
138void MappingFactory::RegisterMotion(const MappingData& data) {
139 Common::ParamPackage new_input;
140 new_input.Set("engine", data.engine);
141 if (data.pad.guid != Common::UUID{}) {
142 new_input.Set("guid", data.pad.guid.Format());
143 }
144 new_input.Set("port", static_cast<int>(data.pad.port));
145 new_input.Set("pad", static_cast<int>(data.pad.pad));
146 switch (data.type) {
147 case EngineInputType::Button:
148 case EngineInputType::HatButton:
149 RegisterButton(data);
150 return;
151 case EngineInputType::Analog:
152 if (first_axis == data.index) {
153 return;
154 }
155 if (second_axis == data.index) {
156 return;
157 }
158 if (first_axis == -1) {
159 first_axis = data.index;
160 return;
161 }
162 if (second_axis == -1) {
163 second_axis = data.index;
164 return;
165 }
166 new_input.Set("axis_x", first_axis);
167 new_input.Set("axis_y", second_axis);
168 new_input.Set("axis_z", data.index);
169 new_input.Set("range", 1.0f);
170 new_input.Set("deadzone", 0.20f);
171 break;
172 case EngineInputType::Motion:
173 new_input.Set("motion", data.index);
174 break;
175 default:
176 return;
177 }
178 input_queue.Push(new_input);
179}
180
181bool MappingFactory::IsDriverValid(const MappingData& data) const {
182 // Only port 0 can be mapped on the keyboard
183 if (data.engine == "keyboard" && data.pad.port != 0) {
184 return false;
185 }
186 // To prevent mapping with two devices we disable any UDP except motion
187 if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" &&
188 data.type != EngineInputType::Motion) {
189 return false;
190 }
191 // The following drivers don't need to be mapped
192 if (data.engine == "tas") {
193 return false;
194 }
195 if (data.engine == "touch") {
196 return false;
197 }
198 if (data.engine == "touch_from_button") {
199 return false;
200 }
201 if (data.engine == "analog_from_button") {
202 return false;
203 }
204 return true;
205}
206
207} // namespace InputCommon
diff --git a/src/input_common/input_mapping.h b/src/input_common/input_mapping.h
new file mode 100644
index 000000000..44eb8ad9a
--- /dev/null
+++ b/src/input_common/input_mapping.h
@@ -0,0 +1,83 @@
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#include "common/threadsafe_queue.h"
7
8namespace InputCommon {
9class InputEngine;
10struct MappingData;
11
12class MappingFactory {
13public:
14 MappingFactory();
15
16 /**
17 * Resets all varables to beggin the mapping process
18 * @param "type": type of input desired to be returned
19 */
20 void BeginMapping(Polling::InputType type);
21
22 /// Returns an input event with mapping information from the input_queue
23 [[nodiscard]] const Common::ParamPackage GetNextInput();
24
25 /**
26 * Registers mapping input data from the driver
27 * @param "data": An struct containing all the information needed to create a proper
28 * ParamPackage
29 */
30 void RegisterInput(const MappingData& data);
31
32 /// Stop polling from all backends
33 void StopMapping();
34
35private:
36 /**
37 * If provided data satisfies the requeriments it will push an element to the input_queue
38 * Supported input:
39 * - Button: Creates a basic button ParamPackage
40 * - HatButton: Creates a basic hat button ParamPackage
41 * - Analog: Creates a basic analog ParamPackage
42 * @param "data": An struct containing all the information needed to create a proper
43 * ParamPackage
44 */
45 void RegisterButton(const MappingData& data);
46
47 /**
48 * If provided data satisfies the requeriments it will push an element to the input_queue
49 * Supported input:
50 * - Button, HatButton: Pass the data to RegisterButton
51 * - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
52 * @param "data": An struct containing all the information needed to create a proper
53 * ParamPackage
54 */
55 void RegisterStick(const MappingData& data);
56
57 /**
58 * If provided data satisfies the requeriments it will push an element to the input_queue
59 * Supported input:
60 * - Button, HatButton: Pass the data to RegisterButton
61 * - Analog: Stores the first two axis and on the third axis creates a basic Motion
62 * ParamPackage
63 * - Motion: Creates a basic Motion ParamPackage
64 * @param "data": An struct containing all the information needed to create a proper
65 * ParamPackage
66 */
67 void RegisterMotion(const MappingData& data);
68
69 /**
70 * Returns true if driver can be mapped
71 * @param "data": An struct containing all the information needed to create a proper
72 * ParamPackage
73 */
74 bool IsDriverValid(const MappingData& data) const;
75
76 Common::SPSCQueue<Common::ParamPackage> input_queue;
77 Polling::InputType input_type{Polling::InputType::None};
78 bool is_enabled{};
79 int first_axis = -1;
80 int second_axis = -1;
81};
82
83} // namespace InputCommon
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
new file mode 100644
index 000000000..7e4eafded
--- /dev/null
+++ b/src/input_common/input_poller.cpp
@@ -0,0 +1,971 @@
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/common_types.h"
6#include "common/input.h"
7
8#include "input_common/input_engine.h"
9#include "input_common/input_poller.h"
10
11namespace InputCommon {
12
13class DummyInput final : public Common::Input::InputDevice {
14public:
15 explicit DummyInput() {}
16 ~DummyInput() {}
17};
18
19class InputFromButton final : public Common::Input::InputDevice {
20public:
21 explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
22 InputEngine* input_engine_)
23 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
24 input_engine(input_engine_) {
25 UpdateCallback engine_callback{[this]() { OnChange(); }};
26 const InputIdentifier input_identifier{
27 .identifier = identifier,
28 .type = EngineInputType::Button,
29 .index = button,
30 .callback = engine_callback,
31 };
32 last_button_value = false;
33 callback_key = input_engine->SetCallback(input_identifier);
34 }
35
36 ~InputFromButton() {
37 input_engine->DeleteCallback(callback_key);
38 }
39
40 Common::Input::ButtonStatus GetStatus() const {
41 return {
42 .value = input_engine->GetButton(identifier, button),
43 .inverted = inverted,
44 .toggle = toggle,
45 };
46 }
47
48 void ForceUpdate() {
49 const Common::Input::CallbackStatus status{
50 .type = Common::Input::InputType::Button,
51 .button_status = GetStatus(),
52 };
53
54 last_button_value = status.button_status.value;
55 TriggerOnChange(status);
56 }
57
58 void OnChange() {
59 const Common::Input::CallbackStatus status{
60 .type = Common::Input::InputType::Button,
61 .button_status = GetStatus(),
62 };
63
64 if (status.button_status.value != last_button_value) {
65 last_button_value = status.button_status.value;
66 TriggerOnChange(status);
67 }
68 }
69
70private:
71 const PadIdentifier identifier;
72 const int button;
73 const bool toggle;
74 const bool inverted;
75 int callback_key;
76 bool last_button_value;
77 InputEngine* input_engine;
78};
79
80class InputFromHatButton final : public Common::Input::InputDevice {
81public:
82 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_,
83 bool inverted_, InputEngine* input_engine_)
84 : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_),
85 inverted(inverted_), input_engine(input_engine_) {
86 UpdateCallback engine_callback{[this]() { OnChange(); }};
87 const InputIdentifier input_identifier{
88 .identifier = identifier,
89 .type = EngineInputType::HatButton,
90 .index = button,
91 .callback = engine_callback,
92 };
93 last_button_value = false;
94 callback_key = input_engine->SetCallback(input_identifier);
95 }
96
97 ~InputFromHatButton() {
98 input_engine->DeleteCallback(callback_key);
99 }
100
101 Common::Input::ButtonStatus GetStatus() const {
102 return {
103 .value = input_engine->GetHatButton(identifier, button, direction),
104 .inverted = inverted,
105 .toggle = toggle,
106 };
107 }
108
109 void ForceUpdate() {
110 const Common::Input::CallbackStatus status{
111 .type = Common::Input::InputType::Button,
112 .button_status = GetStatus(),
113 };
114
115 last_button_value = status.button_status.value;
116 TriggerOnChange(status);
117 }
118
119 void OnChange() {
120 const Common::Input::CallbackStatus status{
121 .type = Common::Input::InputType::Button,
122 .button_status = GetStatus(),
123 };
124
125 if (status.button_status.value != last_button_value) {
126 last_button_value = status.button_status.value;
127 TriggerOnChange(status);
128 }
129 }
130
131private:
132 const PadIdentifier identifier;
133 const int button;
134 const u8 direction;
135 const bool toggle;
136 const bool inverted;
137 int callback_key;
138 bool last_button_value;
139 InputEngine* input_engine;
140};
141
142class InputFromStick final : public Common::Input::InputDevice {
143public:
144 explicit InputFromStick(PadIdentifier identifier_, int axis_x_, int axis_y_,
145 Common::Input::AnalogProperties properties_x_,
146 Common::Input::AnalogProperties properties_y_,
147 InputEngine* input_engine_)
148 : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
149 properties_y(properties_y_),
150 input_engine(input_engine_), invert_axis_y{input_engine_->GetEngineName() == "sdl"} {
151 UpdateCallback engine_callback{[this]() { OnChange(); }};
152 const InputIdentifier x_input_identifier{
153 .identifier = identifier,
154 .type = EngineInputType::Analog,
155 .index = axis_x,
156 .callback = engine_callback,
157 };
158 const InputIdentifier y_input_identifier{
159 .identifier = identifier,
160 .type = EngineInputType::Analog,
161 .index = axis_y,
162 .callback = engine_callback,
163 };
164 last_axis_x_value = 0.0f;
165 last_axis_y_value = 0.0f;
166 callback_key_x = input_engine->SetCallback(x_input_identifier);
167 callback_key_y = input_engine->SetCallback(y_input_identifier);
168 }
169
170 ~InputFromStick() {
171 input_engine->DeleteCallback(callback_key_x);
172 input_engine->DeleteCallback(callback_key_y);
173 }
174
175 Common::Input::StickStatus GetStatus() const {
176 Common::Input::StickStatus status;
177 status.x = {
178 .raw_value = input_engine->GetAxis(identifier, axis_x),
179 .properties = properties_x,
180 };
181 status.y = {
182 .raw_value = input_engine->GetAxis(identifier, axis_y),
183 .properties = properties_y,
184 };
185 // This is a workaround too keep compatibility with old yuzu versions. Vertical axis is
186 // inverted on SDL compared to Nintendo
187 if (invert_axis_y) {
188 status.y.raw_value = -status.y.raw_value;
189 }
190 return status;
191 }
192
193 void ForceUpdate() {
194 const Common::Input::CallbackStatus status{
195 .type = Common::Input::InputType::Stick,
196 .stick_status = GetStatus(),
197 };
198
199 last_axis_x_value = status.stick_status.x.raw_value;
200 last_axis_y_value = status.stick_status.y.raw_value;
201 TriggerOnChange(status);
202 }
203
204 void OnChange() {
205 const Common::Input::CallbackStatus status{
206 .type = Common::Input::InputType::Stick,
207 .stick_status = GetStatus(),
208 };
209
210 if (status.stick_status.x.raw_value != last_axis_x_value ||
211 status.stick_status.y.raw_value != last_axis_y_value) {
212 last_axis_x_value = status.stick_status.x.raw_value;
213 last_axis_y_value = status.stick_status.y.raw_value;
214 TriggerOnChange(status);
215 }
216 }
217
218private:
219 const PadIdentifier identifier;
220 const int axis_x;
221 const int axis_y;
222 const Common::Input::AnalogProperties properties_x;
223 const Common::Input::AnalogProperties properties_y;
224 int callback_key_x;
225 int callback_key_y;
226 float last_axis_x_value;
227 float last_axis_y_value;
228 InputEngine* input_engine;
229 const bool invert_axis_y;
230};
231
232class InputFromTouch final : public Common::Input::InputDevice {
233public:
234 explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_,
235 bool inverted_, int axis_x_, int axis_y_,
236 Common::Input::AnalogProperties properties_x_,
237 Common::Input::AnalogProperties properties_y_,
238 InputEngine* input_engine_)
239 : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_),
240 inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
241 properties_y(properties_y_), input_engine(input_engine_) {
242 UpdateCallback engine_callback{[this]() { OnChange(); }};
243 const InputIdentifier button_input_identifier{
244 .identifier = identifier,
245 .type = EngineInputType::Button,
246 .index = button,
247 .callback = engine_callback,
248 };
249 const InputIdentifier x_input_identifier{
250 .identifier = identifier,
251 .type = EngineInputType::Analog,
252 .index = axis_x,
253 .callback = engine_callback,
254 };
255 const InputIdentifier y_input_identifier{
256 .identifier = identifier,
257 .type = EngineInputType::Analog,
258 .index = axis_y,
259 .callback = engine_callback,
260 };
261 last_axis_x_value = 0.0f;
262 last_axis_y_value = 0.0f;
263 last_button_value = false;
264 callback_key_button = input_engine->SetCallback(button_input_identifier);
265 callback_key_x = input_engine->SetCallback(x_input_identifier);
266 callback_key_y = input_engine->SetCallback(y_input_identifier);
267 }
268
269 ~InputFromTouch() {
270 input_engine->DeleteCallback(callback_key_button);
271 input_engine->DeleteCallback(callback_key_x);
272 input_engine->DeleteCallback(callback_key_y);
273 }
274
275 Common::Input::TouchStatus GetStatus() const {
276 Common::Input::TouchStatus status;
277 status.id = touch_id;
278 status.pressed = {
279 .value = input_engine->GetButton(identifier, button),
280 .inverted = inverted,
281 .toggle = toggle,
282 };
283 status.x = {
284 .raw_value = input_engine->GetAxis(identifier, axis_x),
285 .properties = properties_x,
286 };
287 status.y = {
288 .raw_value = input_engine->GetAxis(identifier, axis_y),
289 .properties = properties_y,
290 };
291 return status;
292 }
293
294 void OnChange() {
295 const Common::Input::CallbackStatus status{
296 .type = Common::Input::InputType::Touch,
297 .touch_status = GetStatus(),
298 };
299
300 if (status.touch_status.x.raw_value != last_axis_x_value ||
301 status.touch_status.y.raw_value != last_axis_y_value ||
302 status.touch_status.pressed.value != last_button_value) {
303 last_axis_x_value = status.touch_status.x.raw_value;
304 last_axis_y_value = status.touch_status.y.raw_value;
305 last_button_value = status.touch_status.pressed.value;
306 TriggerOnChange(status);
307 }
308 }
309
310private:
311 const PadIdentifier identifier;
312 const int touch_id;
313 const int button;
314 const bool toggle;
315 const bool inverted;
316 const int axis_x;
317 const int axis_y;
318 const Common::Input::AnalogProperties properties_x;
319 const Common::Input::AnalogProperties properties_y;
320 int callback_key_button;
321 int callback_key_x;
322 int callback_key_y;
323 bool last_button_value;
324 float last_axis_x_value;
325 float last_axis_y_value;
326 InputEngine* input_engine;
327};
328
329class InputFromTrigger final : public Common::Input::InputDevice {
330public:
331 explicit InputFromTrigger(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
332 int axis_, Common::Input::AnalogProperties properties_,
333 InputEngine* input_engine_)
334 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
335 axis(axis_), properties(properties_), input_engine(input_engine_) {
336 UpdateCallback engine_callback{[this]() { OnChange(); }};
337 const InputIdentifier button_input_identifier{
338 .identifier = identifier,
339 .type = EngineInputType::Button,
340 .index = button,
341 .callback = engine_callback,
342 };
343 const InputIdentifier axis_input_identifier{
344 .identifier = identifier,
345 .type = EngineInputType::Analog,
346 .index = axis,
347 .callback = engine_callback,
348 };
349 last_axis_value = 0.0f;
350 last_button_value = false;
351 callback_key_button = input_engine->SetCallback(button_input_identifier);
352 axis_callback_key = input_engine->SetCallback(axis_input_identifier);
353 }
354
355 ~InputFromTrigger() {
356 input_engine->DeleteCallback(callback_key_button);
357 input_engine->DeleteCallback(axis_callback_key);
358 }
359
360 Common::Input::TriggerStatus GetStatus() const {
361 const Common::Input::AnalogStatus analog_status{
362 .raw_value = input_engine->GetAxis(identifier, axis),
363 .properties = properties,
364 };
365 const Common::Input::ButtonStatus button_status{
366 .value = input_engine->GetButton(identifier, button),
367 .inverted = inverted,
368 .toggle = toggle,
369 };
370 return {
371 .analog = analog_status,
372 .pressed = button_status,
373 };
374 }
375
376 void OnChange() {
377 const Common::Input::CallbackStatus status{
378 .type = Common::Input::InputType::Trigger,
379 .trigger_status = GetStatus(),
380 };
381
382 if (status.trigger_status.analog.raw_value != last_axis_value ||
383 status.trigger_status.pressed.value != last_button_value) {
384 last_axis_value = status.trigger_status.analog.raw_value;
385 last_button_value = status.trigger_status.pressed.value;
386 TriggerOnChange(status);
387 }
388 }
389
390private:
391 const PadIdentifier identifier;
392 const int button;
393 const bool toggle;
394 const bool inverted;
395 const int axis;
396 const Common::Input::AnalogProperties properties;
397 int callback_key_button;
398 int axis_callback_key;
399 bool last_button_value;
400 float last_axis_value;
401 InputEngine* input_engine;
402};
403
404class InputFromAnalog final : public Common::Input::InputDevice {
405public:
406 explicit InputFromAnalog(PadIdentifier identifier_, int axis_,
407 Common::Input::AnalogProperties properties_,
408 InputEngine* input_engine_)
409 : identifier(identifier_), axis(axis_), properties(properties_),
410 input_engine(input_engine_) {
411 UpdateCallback engine_callback{[this]() { OnChange(); }};
412 const InputIdentifier input_identifier{
413 .identifier = identifier,
414 .type = EngineInputType::Analog,
415 .index = axis,
416 .callback = engine_callback,
417 };
418 last_axis_value = 0.0f;
419 callback_key = input_engine->SetCallback(input_identifier);
420 }
421
422 ~InputFromAnalog() {
423 input_engine->DeleteCallback(callback_key);
424 }
425
426 Common::Input::AnalogStatus GetStatus() const {
427 return {
428 .raw_value = input_engine->GetAxis(identifier, axis),
429 .properties = properties,
430 };
431 }
432
433 void OnChange() {
434 const Common::Input::CallbackStatus status{
435 .type = Common::Input::InputType::Analog,
436 .analog_status = GetStatus(),
437 };
438
439 if (status.analog_status.raw_value != last_axis_value) {
440 last_axis_value = status.analog_status.raw_value;
441 TriggerOnChange(status);
442 }
443 }
444
445private:
446 const PadIdentifier identifier;
447 const int axis;
448 const Common::Input::AnalogProperties properties;
449 int callback_key;
450 float last_axis_value;
451 InputEngine* input_engine;
452};
453
454class InputFromBattery final : public Common::Input::InputDevice {
455public:
456 explicit InputFromBattery(PadIdentifier identifier_, InputEngine* input_engine_)
457 : identifier(identifier_), input_engine(input_engine_) {
458 UpdateCallback engine_callback{[this]() { OnChange(); }};
459 const InputIdentifier input_identifier{
460 .identifier = identifier,
461 .type = EngineInputType::Battery,
462 .index = 0,
463 .callback = engine_callback,
464 };
465 last_battery_value = Common::Input::BatteryStatus::Charging;
466 callback_key = input_engine->SetCallback(input_identifier);
467 }
468
469 ~InputFromBattery() {
470 input_engine->DeleteCallback(callback_key);
471 }
472
473 Common::Input::BatteryStatus GetStatus() const {
474 return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier));
475 }
476
477 void ForceUpdate() {
478 const Common::Input::CallbackStatus status{
479 .type = Common::Input::InputType::Battery,
480 .battery_status = GetStatus(),
481 };
482
483 last_battery_value = status.battery_status;
484 TriggerOnChange(status);
485 }
486
487 void OnChange() {
488 const Common::Input::CallbackStatus status{
489 .type = Common::Input::InputType::Battery,
490 .battery_status = GetStatus(),
491 };
492
493 if (status.battery_status != last_battery_value) {
494 last_battery_value = status.battery_status;
495 TriggerOnChange(status);
496 }
497 }
498
499private:
500 const PadIdentifier identifier;
501 int callback_key;
502 Common::Input::BatteryStatus last_battery_value;
503 InputEngine* input_engine;
504};
505
506class InputFromMotion final : public Common::Input::InputDevice {
507public:
508 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_,
509 InputEngine* input_engine_)
510 : identifier(identifier_), motion_sensor(motion_sensor_), input_engine(input_engine_) {
511 UpdateCallback engine_callback{[this]() { OnChange(); }};
512 const InputIdentifier input_identifier{
513 .identifier = identifier,
514 .type = EngineInputType::Motion,
515 .index = motion_sensor,
516 .callback = engine_callback,
517 };
518 callback_key = input_engine->SetCallback(input_identifier);
519 }
520
521 ~InputFromMotion() {
522 input_engine->DeleteCallback(callback_key);
523 }
524
525 Common::Input::MotionStatus GetStatus() const {
526 const auto basic_motion = input_engine->GetMotion(identifier, motion_sensor);
527 Common::Input::MotionStatus status{};
528 const Common::Input::AnalogProperties properties = {
529 .deadzone = 0.001f,
530 .range = 1.0f,
531 .offset = 0.0f,
532 };
533 status.accel.x = {.raw_value = basic_motion.accel_x, .properties = properties};
534 status.accel.y = {.raw_value = basic_motion.accel_y, .properties = properties};
535 status.accel.z = {.raw_value = basic_motion.accel_z, .properties = properties};
536 status.gyro.x = {.raw_value = basic_motion.gyro_x, .properties = properties};
537 status.gyro.y = {.raw_value = basic_motion.gyro_y, .properties = properties};
538 status.gyro.z = {.raw_value = basic_motion.gyro_z, .properties = properties};
539 status.delta_timestamp = basic_motion.delta_timestamp;
540 return status;
541 }
542
543 void OnChange() {
544 const Common::Input::CallbackStatus status{
545 .type = Common::Input::InputType::Motion,
546 .motion_status = GetStatus(),
547 };
548
549 TriggerOnChange(status);
550 }
551
552private:
553 const PadIdentifier identifier;
554 const int motion_sensor;
555 int callback_key;
556 InputEngine* input_engine;
557};
558
559class InputFromAxisMotion final : public Common::Input::InputDevice {
560public:
561 explicit InputFromAxisMotion(PadIdentifier identifier_, int axis_x_, int axis_y_, int axis_z_,
562 Common::Input::AnalogProperties properties_x_,
563 Common::Input::AnalogProperties properties_y_,
564 Common::Input::AnalogProperties properties_z_,
565 InputEngine* input_engine_)
566 : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), axis_z(axis_z_),
567 properties_x(properties_x_), properties_y(properties_y_), properties_z(properties_z_),
568 input_engine(input_engine_) {
569 UpdateCallback engine_callback{[this]() { OnChange(); }};
570 const InputIdentifier x_input_identifier{
571 .identifier = identifier,
572 .type = EngineInputType::Analog,
573 .index = axis_x,
574 .callback = engine_callback,
575 };
576 const InputIdentifier y_input_identifier{
577 .identifier = identifier,
578 .type = EngineInputType::Analog,
579 .index = axis_y,
580 .callback = engine_callback,
581 };
582 const InputIdentifier z_input_identifier{
583 .identifier = identifier,
584 .type = EngineInputType::Analog,
585 .index = axis_z,
586 .callback = engine_callback,
587 };
588 last_axis_x_value = 0.0f;
589 last_axis_y_value = 0.0f;
590 last_axis_z_value = 0.0f;
591 callback_key_x = input_engine->SetCallback(x_input_identifier);
592 callback_key_y = input_engine->SetCallback(y_input_identifier);
593 callback_key_z = input_engine->SetCallback(z_input_identifier);
594 }
595
596 ~InputFromAxisMotion() {
597 input_engine->DeleteCallback(callback_key_x);
598 input_engine->DeleteCallback(callback_key_y);
599 input_engine->DeleteCallback(callback_key_z);
600 }
601
602 Common::Input::MotionStatus GetStatus() const {
603 Common::Input::MotionStatus status{};
604 status.gyro.x = {
605 .raw_value = input_engine->GetAxis(identifier, axis_x),
606 .properties = properties_x,
607 };
608 status.gyro.y = {
609 .raw_value = input_engine->GetAxis(identifier, axis_y),
610 .properties = properties_y,
611 };
612 status.gyro.z = {
613 .raw_value = input_engine->GetAxis(identifier, axis_z),
614 .properties = properties_z,
615 };
616 status.delta_timestamp = 5000;
617 status.force_update = true;
618 return status;
619 }
620
621 void ForceUpdate() {
622 const Common::Input::CallbackStatus status{
623 .type = Common::Input::InputType::Motion,
624 .motion_status = GetStatus(),
625 };
626
627 last_axis_x_value = status.motion_status.gyro.x.raw_value;
628 last_axis_y_value = status.motion_status.gyro.y.raw_value;
629 last_axis_z_value = status.motion_status.gyro.z.raw_value;
630 TriggerOnChange(status);
631 }
632
633 void OnChange() {
634 const Common::Input::CallbackStatus status{
635 .type = Common::Input::InputType::Motion,
636 .motion_status = GetStatus(),
637 };
638
639 if (status.motion_status.gyro.x.raw_value != last_axis_x_value ||
640 status.motion_status.gyro.y.raw_value != last_axis_y_value ||
641 status.motion_status.gyro.z.raw_value != last_axis_z_value) {
642 last_axis_x_value = status.motion_status.gyro.x.raw_value;
643 last_axis_y_value = status.motion_status.gyro.y.raw_value;
644 last_axis_z_value = status.motion_status.gyro.z.raw_value;
645 TriggerOnChange(status);
646 }
647 }
648
649private:
650 const PadIdentifier identifier;
651 const int axis_x;
652 const int axis_y;
653 const int axis_z;
654 const Common::Input::AnalogProperties properties_x;
655 const Common::Input::AnalogProperties properties_y;
656 const Common::Input::AnalogProperties properties_z;
657 int callback_key_x;
658 int callback_key_y;
659 int callback_key_z;
660 float last_axis_x_value;
661 float last_axis_y_value;
662 float last_axis_z_value;
663 InputEngine* input_engine;
664};
665
666class OutputFromIdentifier final : public Common::Input::OutputDevice {
667public:
668 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
669 : identifier(identifier_), input_engine(input_engine_) {}
670
671 virtual void SetLED(Common::Input::LedStatus led_status) {
672 input_engine->SetLeds(identifier, led_status);
673 }
674
675 virtual Common::Input::VibrationError SetVibration(
676 Common::Input::VibrationStatus vibration_status) {
677 return input_engine->SetRumble(identifier, vibration_status);
678 }
679
680 virtual Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) {
681 return input_engine->SetPollingMode(identifier, polling_mode);
682 }
683
684private:
685 const PadIdentifier identifier;
686 InputEngine* input_engine;
687};
688
689std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
690 const Common::ParamPackage& params) {
691 const PadIdentifier identifier = {
692 .guid = Common::UUID{params.Get("guid", "")},
693 .port = static_cast<std::size_t>(params.Get("port", 0)),
694 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
695 };
696
697 const auto button_id = params.Get("button", 0);
698 const auto keyboard_key = params.Get("code", 0);
699 const auto toggle = params.Get("toggle", false);
700 const auto inverted = params.Get("inverted", false);
701 input_engine->PreSetController(identifier);
702 input_engine->PreSetButton(identifier, button_id);
703 input_engine->PreSetButton(identifier, keyboard_key);
704 if (keyboard_key != 0) {
705 return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted,
706 input_engine.get());
707 }
708 return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted,
709 input_engine.get());
710}
711
712std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
713 const Common::ParamPackage& params) {
714 const PadIdentifier identifier = {
715 .guid = Common::UUID{params.Get("guid", "")},
716 .port = static_cast<std::size_t>(params.Get("port", 0)),
717 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
718 };
719
720 const auto button_id = params.Get("hat", 0);
721 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
722 const auto toggle = params.Get("toggle", false);
723 const auto inverted = params.Get("inverted", false);
724
725 input_engine->PreSetController(identifier);
726 input_engine->PreSetHatButton(identifier, button_id);
727 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted,
728 input_engine.get());
729}
730
731std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
732 const Common::ParamPackage& params) {
733 const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
734 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
735 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
736 const PadIdentifier identifier = {
737 .guid = Common::UUID{params.Get("guid", "")},
738 .port = static_cast<std::size_t>(params.Get("port", 0)),
739 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
740 };
741
742 const auto axis_x = params.Get("axis_x", 0);
743 const Common::Input::AnalogProperties properties_x = {
744 .deadzone = deadzone,
745 .range = range,
746 .threshold = threshold,
747 .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
748 .inverted = params.Get("invert_x", "+") == "-",
749 };
750
751 const auto axis_y = params.Get("axis_y", 1);
752 const Common::Input::AnalogProperties properties_y = {
753 .deadzone = deadzone,
754 .range = range,
755 .threshold = threshold,
756 .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
757 .inverted = params.Get("invert_y", "+") != "+",
758 };
759 input_engine->PreSetController(identifier);
760 input_engine->PreSetAxis(identifier, axis_x);
761 input_engine->PreSetAxis(identifier, axis_y);
762 return std::make_unique<InputFromStick>(identifier, axis_x, axis_y, properties_x, properties_y,
763 input_engine.get());
764}
765
766std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
767 const Common::ParamPackage& params) {
768 const PadIdentifier identifier = {
769 .guid = Common::UUID{params.Get("guid", "")},
770 .port = static_cast<std::size_t>(params.Get("port", 0)),
771 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
772 };
773
774 const auto axis = params.Get("axis", 0);
775 const Common::Input::AnalogProperties properties = {
776 .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f),
777 .range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f),
778 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
779 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
780 .inverted = params.Get("invert", "+") == "-",
781 };
782 input_engine->PreSetController(identifier);
783 input_engine->PreSetAxis(identifier, axis);
784 return std::make_unique<InputFromAnalog>(identifier, axis, properties, input_engine.get());
785}
786
787std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
788 const Common::ParamPackage& params) {
789 const PadIdentifier identifier = {
790 .guid = Common::UUID{params.Get("guid", "")},
791 .port = static_cast<std::size_t>(params.Get("port", 0)),
792 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
793 };
794
795 const auto button = params.Get("button", 0);
796 const auto toggle = params.Get("toggle", false);
797 const auto inverted = params.Get("inverted", false);
798
799 const auto axis = params.Get("axis", 0);
800 const Common::Input::AnalogProperties properties = {
801 .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f),
802 .range = std::clamp(params.Get("range", 1.0f), 0.25f, 2.50f),
803 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
804 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
805 .inverted = params.Get("invert", false) != 0,
806 };
807 input_engine->PreSetController(identifier);
808 input_engine->PreSetAxis(identifier, axis);
809 input_engine->PreSetButton(identifier, button);
810 return std::make_unique<InputFromTrigger>(identifier, button, toggle, inverted, axis,
811 properties, input_engine.get());
812}
813
814std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
815 const Common::ParamPackage& params) {
816 const auto touch_id = params.Get("touch_id", 0);
817 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
818 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
819 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
820 const PadIdentifier identifier = {
821 .guid = Common::UUID{params.Get("guid", "")},
822 .port = static_cast<std::size_t>(params.Get("port", 0)),
823 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
824 };
825
826 const auto button = params.Get("button", 0);
827 const auto toggle = params.Get("toggle", false);
828 const auto inverted = params.Get("inverted", false);
829
830 const auto axis_x = params.Get("axis_x", 0);
831 const Common::Input::AnalogProperties properties_x = {
832 .deadzone = deadzone,
833 .range = range,
834 .threshold = threshold,
835 .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
836 .inverted = params.Get("invert_x", "+") == "-",
837 };
838
839 const auto axis_y = params.Get("axis_y", 1);
840 const Common::Input::AnalogProperties properties_y = {
841 .deadzone = deadzone,
842 .range = range,
843 .threshold = threshold,
844 .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
845 .inverted = params.Get("invert_y", false) != 0,
846 };
847 input_engine->PreSetController(identifier);
848 input_engine->PreSetAxis(identifier, axis_x);
849 input_engine->PreSetAxis(identifier, axis_y);
850 input_engine->PreSetButton(identifier, button);
851 return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x,
852 axis_y, properties_x, properties_y, input_engine.get());
853}
854
855std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
856 const Common::ParamPackage& params) {
857 const PadIdentifier identifier = {
858 .guid = Common::UUID{params.Get("guid", "")},
859 .port = static_cast<std::size_t>(params.Get("port", 0)),
860 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
861 };
862
863 input_engine->PreSetController(identifier);
864 return std::make_unique<InputFromBattery>(identifier, input_engine.get());
865}
866
867std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
868 Common::ParamPackage params) {
869 const PadIdentifier identifier = {
870 .guid = Common::UUID{params.Get("guid", "")},
871 .port = static_cast<std::size_t>(params.Get("port", 0)),
872 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
873 };
874
875 if (params.Has("motion")) {
876 const auto motion_sensor = params.Get("motion", 0);
877 input_engine->PreSetController(identifier);
878 input_engine->PreSetMotion(identifier, motion_sensor);
879 return std::make_unique<InputFromMotion>(identifier, motion_sensor, input_engine.get());
880 }
881
882 const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
883 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
884 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
885
886 const auto axis_x = params.Get("axis_x", 0);
887 const Common::Input::AnalogProperties properties_x = {
888 .deadzone = deadzone,
889 .range = range,
890 .threshold = threshold,
891 .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
892 .inverted = params.Get("invert_x", "+") == "-",
893 };
894
895 const auto axis_y = params.Get("axis_y", 1);
896 const Common::Input::AnalogProperties properties_y = {
897 .deadzone = deadzone,
898 .range = range,
899 .threshold = threshold,
900 .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
901 .inverted = params.Get("invert_y", "+") != "+",
902 };
903
904 const auto axis_z = params.Get("axis_z", 1);
905 const Common::Input::AnalogProperties properties_z = {
906 .deadzone = deadzone,
907 .range = range,
908 .threshold = threshold,
909 .offset = std::clamp(params.Get("offset_z", 0.0f), -1.0f, 1.0f),
910 .inverted = params.Get("invert_z", "+") != "+",
911 };
912 input_engine->PreSetController(identifier);
913 input_engine->PreSetAxis(identifier, axis_x);
914 input_engine->PreSetAxis(identifier, axis_y);
915 input_engine->PreSetAxis(identifier, axis_z);
916 return std::make_unique<InputFromAxisMotion>(identifier, axis_x, axis_y, axis_z, properties_x,
917 properties_y, properties_z, input_engine.get());
918}
919
920InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
921 : input_engine(std::move(input_engine_)) {}
922
923std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
924 const Common::ParamPackage& params) {
925 if (params.Has("battery")) {
926 return CreateBatteryDevice(params);
927 }
928 if (params.Has("button") && params.Has("axis")) {
929 return CreateTriggerDevice(params);
930 }
931 if (params.Has("button") && params.Has("axis_x") && params.Has("axis_y")) {
932 return CreateTouchDevice(params);
933 }
934 if (params.Has("button") || params.Has("code")) {
935 return CreateButtonDevice(params);
936 }
937 if (params.Has("hat")) {
938 return CreateHatButtonDevice(params);
939 }
940 if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
941 return CreateMotionDevice(params);
942 }
943 if (params.Has("motion")) {
944 return CreateMotionDevice(params);
945 }
946 if (params.Has("axis_x") && params.Has("axis_y")) {
947 return CreateStickDevice(params);
948 }
949 if (params.Has("axis")) {
950 return CreateAnalogDevice(params);
951 }
952 LOG_ERROR(Input, "Invalid parameters given");
953 return std::make_unique<DummyInput>();
954}
955
956OutputFactory::OutputFactory(std::shared_ptr<InputEngine> input_engine_)
957 : input_engine(std::move(input_engine_)) {}
958
959std::unique_ptr<Common::Input::OutputDevice> OutputFactory::Create(
960 const Common::ParamPackage& params) {
961 const PadIdentifier identifier = {
962 .guid = Common::UUID{params.Get("guid", "")},
963 .port = static_cast<std::size_t>(params.Get("port", 0)),
964 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
965 };
966
967 input_engine->PreSetController(identifier);
968 return std::make_unique<OutputFromIdentifier>(identifier, input_engine.get());
969}
970
971} // namespace InputCommon
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
new file mode 100644
index 000000000..573f09fde
--- /dev/null
+++ b/src/input_common/input_poller.h
@@ -0,0 +1,217 @@
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
7namespace Input {
8class InputDevice;
9
10template <typename InputDevice>
11class Factory;
12}; // namespace Input
13
14namespace InputCommon {
15class InputEngine;
16/**
17 * An Input factory. It receives input events and forward them to all input devices it created.
18 */
19
20class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
21public:
22 explicit OutputFactory(std::shared_ptr<InputEngine> input_engine_);
23
24 /**
25 * Creates an output device from the parameters given.
26 * @param params contains parameters for creating the device:
27 * @param - "guid": text string for identifing controllers
28 * @param - "port": port of the connected device
29 * @param - "pad": slot of the connected controller
30 * @return an unique ouput device with the parameters specified
31 */
32 std::unique_ptr<Common::Input::OutputDevice> Create(
33 const Common::ParamPackage& params) override;
34
35private:
36 std::shared_ptr<InputEngine> input_engine;
37};
38
39class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
40public:
41 explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);
42
43 /**
44 * Creates an input device from the parameters given. Identifies the type of input to be
45 * returned if it contains the following parameters:
46 * - button: Contains "button" or "code"
47 * - hat_button: Contains "hat"
48 * - analog: Contains "axis"
49 * - trigger: Contains "button" and "axis"
50 * - stick: Contains "axis_x" and "axis_y"
51 * - motion: Contains "axis_x", "axis_y" and "axis_z"
52 * - motion: Contains "motion"
53 * - touch: Contains "button", "axis_x" and "axis_y"
54 * - battery: Contains "battery"
55 * - output: Contains "output"
56 * @param params contains parameters for creating the device:
57 * @param - "code": the code of the keyboard key to bind with the input
58 * @param - "button": same as "code" but for controller buttons
59 * @param - "hat": similar as "button" but it's a group of hat buttons from SDL
60 * @param - "axis": the axis number of the axis to bind with the input
61 * @param - "motion": the motion number of the motion to bind with the input
62 * @param - "axis_x": same as axis but specifing horizontal direction
63 * @param - "axis_y": same as axis but specifing vertical direction
64 * @param - "axis_z": same as axis but specifing forward direction
65 * @param - "battery": Only used as a placeholder to set the input type
66 * @return an unique input device with the parameters specified
67 */
68 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
69
70private:
71 /**
72 * Creates a button device from the parameters given.
73 * @param params contains parameters for creating the device:
74 * @param - "code": the code of the keyboard key to bind with the input
75 * @param - "button": same as "code" but for controller buttons
76 * @param - "toggle": press once to enable, press again to disable
77 * @param - "inverted": inverts the output of the button
78 * @param - "guid": text string for identifing controllers
79 * @param - "port": port of the connected device
80 * @param - "pad": slot of the connected controller
81 * @return an unique input device with the parameters specified
82 */
83 std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
84 const Common::ParamPackage& params);
85
86 /**
87 * Creates a hat button device from the parameters given.
88 * @param params contains parameters for creating the device:
89 * @param - "button": the controller hat id to bind with the input
90 * @param - "direction": the direction id to be detected
91 * @param - "toggle": press once to enable, press again to disable
92 * @param - "inverted": inverts the output of the button
93 * @param - "guid": text string for identifing controllers
94 * @param - "port": port of the connected device
95 * @param - "pad": slot of the connected controller
96 * @return an unique input device with the parameters specified
97 */
98 std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
99 const Common::ParamPackage& params);
100
101 /**
102 * Creates a stick device from the parameters given.
103 * @param params contains parameters for creating the device:
104 * @param - "axis_x": the controller horizontal axis id to bind with the input
105 * @param - "axis_y": the controller vertical axis id to bind with the input
106 * @param - "deadzone": the mimimum required value to be detected
107 * @param - "range": the maximum value required to reach 100%
108 * @param - "threshold": the mimimum required value to considered pressed
109 * @param - "offset_x": the amount of offset in the x axis
110 * @param - "offset_y": the amount of offset in the y axis
111 * @param - "invert_x": inverts the sign of the horizontal axis
112 * @param - "invert_y": inverts the sign of the vertical axis
113 * @param - "guid": text string for identifing controllers
114 * @param - "port": port of the connected device
115 * @param - "pad": slot of the connected controller
116 * @return an unique input device with the parameters specified
117 */
118 std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
119 const Common::ParamPackage& params);
120
121 /**
122 * Creates an analog device from the parameters given.
123 * @param params contains parameters for creating the device:
124 * @param - "axis": the controller axis id to bind with the input
125 * @param - "deadzone": the mimimum required value to be detected
126 * @param - "range": the maximum value required to reach 100%
127 * @param - "threshold": the mimimum required value to considered pressed
128 * @param - "offset": the amount of offset in the axis
129 * @param - "invert": inverts the sign of the axis
130 * @param - "guid": text string for identifing controllers
131 * @param - "port": port of the connected device
132 * @param - "pad": slot of the connected controller
133 * @return an unique input device with the parameters specified
134 */
135 std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
136 const Common::ParamPackage& params);
137
138 /**
139 * Creates a trigger device from the parameters given.
140 * @param params contains parameters for creating the device:
141 * @param - "button": the controller hat id to bind with the input
142 * @param - "direction": the direction id to be detected
143 * @param - "toggle": press once to enable, press again to disable
144 * @param - "inverted": inverts the output of the button
145 * @param - "axis": the controller axis id to bind with the input
146 * @param - "deadzone": the mimimum required value to be detected
147 * @param - "range": the maximum value required to reach 100%
148 * @param - "threshold": the mimimum required value to considered pressed
149 * @param - "offset": the amount of offset in the axis
150 * @param - "invert": inverts the sign of the axis
151 * @param - "guid": text string for identifing controllers
152 * @param - "port": port of the connected device
153 * @param - "pad": slot of the connected controller
154 * @return an unique input device with the parameters specified
155 */
156 std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
157 const Common::ParamPackage& params);
158
159 /**
160 * Creates a touch device from the parameters given.
161 * @param params contains parameters for creating the device:
162 * @param - "button": the controller hat id to bind with the input
163 * @param - "direction": the direction id to be detected
164 * @param - "toggle": press once to enable, press again to disable
165 * @param - "inverted": inverts the output of the button
166 * @param - "axis_x": the controller horizontal axis id to bind with the input
167 * @param - "axis_y": the controller vertical axis id to bind with the input
168 * @param - "deadzone": the mimimum required value to be detected
169 * @param - "range": the maximum value required to reach 100%
170 * @param - "threshold": the mimimum required value to considered pressed
171 * @param - "offset_x": the amount of offset in the x axis
172 * @param - "offset_y": the amount of offset in the y axis
173 * @param - "invert_x": inverts the sign of the horizontal axis
174 * @param - "invert_y": inverts the sign of the vertical axis
175 * @param - "guid": text string for identifing controllers
176 * @param - "port": port of the connected device
177 * @param - "pad": slot of the connected controller
178 * @return an unique input device with the parameters specified
179 */
180 std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
181 const Common::ParamPackage& params);
182
183 /**
184 * Creates a battery device from the parameters given.
185 * @param params contains parameters for creating the device:
186 * @param - "guid": text string for identifing controllers
187 * @param - "port": port of the connected device
188 * @param - "pad": slot of the connected controller
189 * @return an unique input device with the parameters specified
190 */
191 std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
192 const Common::ParamPackage& params);
193
194 /**
195 * Creates a motion device from the parameters given.
196 * @param params contains parameters for creating the device:
197 * @param - "axis_x": the controller horizontal axis id to bind with the input
198 * @param - "axis_y": the controller vertical axis id to bind with the input
199 * @param - "axis_z": the controller fordward axis id to bind with the input
200 * @param - "deadzone": the mimimum required value to be detected
201 * @param - "range": the maximum value required to reach 100%
202 * @param - "offset_x": the amount of offset in the x axis
203 * @param - "offset_y": the amount of offset in the y axis
204 * @param - "offset_z": the amount of offset in the z axis
205 * @param - "invert_x": inverts the sign of the horizontal axis
206 * @param - "invert_y": inverts the sign of the vertical axis
207 * @param - "invert_z": inverts the sign of the fordward axis
208 * @param - "guid": text string for identifing controllers
209 * @param - "port": port of the connected device
210 * @param - "pad": slot of the connected controller
211 * @return an unique input device with the parameters specified
212 */
213 std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
214
215 std::shared_ptr<InputEngine> input_engine;
216};
217} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
deleted file mode 100644
index 8261e76fd..000000000
--- a/src/input_common/keyboard.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "input_common/keyboard.h"
10
11namespace InputCommon {
12
13class KeyButton final : public Input::ButtonDevice {
14public:
15 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_)
16 : key_button_list(std::move(key_button_list_)), toggle(toggle_) {}
17
18 ~KeyButton() override;
19
20 bool GetStatus() const override {
21 if (toggle) {
22 return toggled_status.load(std::memory_order_relaxed);
23 }
24 return status.load();
25 }
26
27 void ToggleButton() {
28 if (lock) {
29 return;
30 }
31 lock = true;
32 const bool old_toggle_status = toggled_status.load();
33 toggled_status.store(!old_toggle_status);
34 }
35
36 void UnlockButton() {
37 lock = false;
38 }
39
40 friend class KeyButtonList;
41
42private:
43 std::shared_ptr<KeyButtonList> key_button_list;
44 std::atomic<bool> status{false};
45 std::atomic<bool> toggled_status{false};
46 bool lock{false};
47 const bool toggle;
48};
49
50struct KeyButtonPair {
51 int key_code;
52 KeyButton* key_button;
53};
54
55class KeyButtonList {
56public:
57 void AddKeyButton(int key_code, KeyButton* key_button) {
58 std::lock_guard guard{mutex};
59 list.push_back(KeyButtonPair{key_code, key_button});
60 }
61
62 void RemoveKeyButton(const KeyButton* key_button) {
63 std::lock_guard guard{mutex};
64 list.remove_if(
65 [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
66 }
67
68 void ChangeKeyStatus(int key_code, bool pressed) {
69 std::lock_guard guard{mutex};
70 for (const KeyButtonPair& pair : list) {
71 if (pair.key_code == key_code) {
72 pair.key_button->status.store(pressed);
73 if (pressed) {
74 pair.key_button->ToggleButton();
75 } else {
76 pair.key_button->UnlockButton();
77 }
78 pair.key_button->TriggerOnChange();
79 }
80 }
81 }
82
83 void ChangeAllKeyStatus(bool pressed) {
84 std::lock_guard guard{mutex};
85 for (const KeyButtonPair& pair : list) {
86 pair.key_button->status.store(pressed);
87 }
88 }
89
90private:
91 std::mutex mutex;
92 std::list<KeyButtonPair> list;
93};
94
95Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {}
96
97KeyButton::~KeyButton() {
98 key_button_list->RemoveKeyButton(this);
99}
100
101std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
102 const int key_code = params.Get("code", 0);
103 const bool toggle = params.Get("toggle", false);
104 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle);
105 key_button_list->AddKeyButton(key_code, button.get());
106 return button;
107}
108
109void Keyboard::PressKey(int key_code) {
110 key_button_list->ChangeKeyStatus(key_code, true);
111}
112
113void Keyboard::ReleaseKey(int key_code) {
114 key_button_list->ChangeKeyStatus(key_code, false);
115}
116
117void Keyboard::ReleaseAllKeys() {
118 key_button_list->ChangeAllKeyStatus(false);
119}
120
121} // namespace InputCommon
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h
deleted file mode 100644
index 861950472..000000000
--- a/src/input_common/keyboard.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2017 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
12class KeyButtonList;
13
14/**
15 * A button device factory representing a keyboard. It receives keyboard events and forward them
16 * to all button devices it created.
17 */
18class Keyboard final : public Input::Factory<Input::ButtonDevice> {
19public:
20 Keyboard();
21
22 /**
23 * Creates a button device from a keyboard key
24 * @param params contains parameters for creating the device:
25 * - "code": the code of the key to bind with the button
26 */
27 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
28
29 /**
30 * Sets the status of all buttons bound with the key to pressed
31 * @param key_code the code of the key to press
32 */
33 void PressKey(int key_code);
34
35 /**
36 * Sets the status of all buttons bound with the key to released
37 * @param key_code the code of the key to release
38 */
39 void ReleaseKey(int key_code);
40
41 void ReleaseAllKeys();
42
43private:
44 std::shared_ptr<KeyButtonList> key_button_list;
45};
46
47} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index f3907c65a..940744c5f 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,146 +4,173 @@
4 4
5#include <memory> 5#include <memory>
6#include <thread> 6#include <thread>
7#include "common/input.h"
7#include "common/param_package.h" 8#include "common/param_package.h"
8#include "common/settings.h" 9#include "input_common/drivers/gc_adapter.h"
9#include "input_common/analog_from_button.h" 10#include "input_common/drivers/keyboard.h"
10#include "input_common/gcadapter/gc_adapter.h" 11#include "input_common/drivers/mouse.h"
11#include "input_common/gcadapter/gc_poller.h" 12#include "input_common/drivers/tas_input.h"
12#include "input_common/keyboard.h" 13#include "input_common/drivers/touch_screen.h"
14#include "input_common/drivers/udp_client.h"
15#include "input_common/helpers/stick_from_buttons.h"
16#include "input_common/helpers/touch_from_buttons.h"
17#include "input_common/input_engine.h"
18#include "input_common/input_mapping.h"
19#include "input_common/input_poller.h"
13#include "input_common/main.h" 20#include "input_common/main.h"
14#include "input_common/motion_from_button.h"
15#include "input_common/mouse/mouse_input.h"
16#include "input_common/mouse/mouse_poller.h"
17#include "input_common/tas/tas_input.h"
18#include "input_common/tas/tas_poller.h"
19#include "input_common/touch_from_button.h"
20#include "input_common/udp/client.h"
21#include "input_common/udp/udp.h"
22#ifdef HAVE_SDL2 21#ifdef HAVE_SDL2
23#include "input_common/sdl/sdl.h" 22#include "input_common/drivers/sdl_driver.h"
24#endif 23#endif
25 24
26namespace InputCommon { 25namespace InputCommon {
27 26
28struct InputSubsystem::Impl { 27struct InputSubsystem::Impl {
29 void Initialize() { 28 void Initialize() {
30 gcadapter = std::make_shared<GCAdapter::Adapter>(); 29 mapping_factory = std::make_shared<MappingFactory>();
31 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); 30 MappingCallback mapping_callback{[this](MappingData data) { RegisterInput(data); }};
32 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); 31
33 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); 32 keyboard = std::make_shared<Keyboard>("keyboard");
34 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); 33 keyboard->SetMappingCallback(mapping_callback);
35 gcvibration = std::make_shared<GCVibrationFactory>(gcadapter); 34 keyboard_factory = std::make_shared<InputFactory>(keyboard);
36 Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration); 35 keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
37 36 Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(),
38 keyboard = std::make_shared<Keyboard>(); 37 keyboard_factory);
39 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 38 Common::Input::RegisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName(),
40 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 39 keyboard_output_factory);
41 std::make_shared<AnalogFromButton>()); 40
42 Input::RegisterFactory<Input::MotionDevice>("keyboard", 41 mouse = std::make_shared<Mouse>("mouse");
43 std::make_shared<MotionFromButton>()); 42 mouse->SetMappingCallback(mapping_callback);
44 Input::RegisterFactory<Input::TouchDevice>("touch_from_button", 43 mouse_factory = std::make_shared<InputFactory>(mouse);
45 std::make_shared<TouchFromButtonFactory>()); 44 mouse_output_factory = std::make_shared<OutputFactory>(mouse);
45 Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(),
46 mouse_factory);
47 Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(),
48 mouse_output_factory);
49
50 touch_screen = std::make_shared<TouchScreen>("touch");
51 touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
52 Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(),
53 touch_screen_factory);
54
55 gcadapter = std::make_shared<GCAdapter>("gcpad");
56 gcadapter->SetMappingCallback(mapping_callback);
57 gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
58 gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
59 Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(),
60 gcadapter_input_factory);
61 Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(),
62 gcadapter_output_factory);
63
64 udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
65 udp_client->SetMappingCallback(mapping_callback);
66 udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
67 udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
68 Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(),
69 udp_client_input_factory);
70 Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(),
71 udp_client_output_factory);
72
73 tas_input = std::make_shared<TasInput::Tas>("tas");
74 tas_input->SetMappingCallback(mapping_callback);
75 tas_input_factory = std::make_shared<InputFactory>(tas_input);
76 tas_output_factory = std::make_shared<OutputFactory>(tas_input);
77 Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(),
78 tas_input_factory);
79 Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
80 tas_output_factory);
46 81
47#ifdef HAVE_SDL2 82#ifdef HAVE_SDL2
48 sdl = SDL::Init(); 83 sdl = std::make_shared<SDLDriver>("sdl");
84 sdl->SetMappingCallback(mapping_callback);
85 sdl_input_factory = std::make_shared<InputFactory>(sdl);
86 sdl_output_factory = std::make_shared<OutputFactory>(sdl);
87 Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(),
88 sdl_input_factory);
89 Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
90 sdl_output_factory);
49#endif 91#endif
50 92
51 udp = std::make_shared<InputCommon::CemuhookUDP::Client>(); 93 Common::Input::RegisterFactory<Common::Input::InputDevice>(
52 udpmotion = std::make_shared<UDPMotionFactory>(udp); 94 "touch_from_button", std::make_shared<TouchFromButton>());
53 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); 95 Common::Input::RegisterFactory<Common::Input::InputDevice>(
54 udptouch = std::make_shared<UDPTouchFactory>(udp); 96 "analog_from_button", std::make_shared<StickFromButton>());
55 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
56
57 mouse = std::make_shared<MouseInput::Mouse>();
58 mousebuttons = std::make_shared<MouseButtonFactory>(mouse);
59 Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons);
60 mouseanalog = std::make_shared<MouseAnalogFactory>(mouse);
61 Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog);
62 mousemotion = std::make_shared<MouseMotionFactory>(mouse);
63 Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
64 mousetouch = std::make_shared<MouseTouchFactory>(mouse);
65 Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
66
67 tas = std::make_shared<TasInput::Tas>();
68 tasbuttons = std::make_shared<TasButtonFactory>(tas);
69 Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons);
70 tasanalog = std::make_shared<TasAnalogFactory>(tas);
71 Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog);
72 } 97 }
73 98
74 void Shutdown() { 99 void Shutdown() {
75 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 100 Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName());
76 Input::UnregisterFactory<Input::MotionDevice>("keyboard"); 101 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName());
77 keyboard.reset(); 102 keyboard.reset();
78 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
79 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
80#ifdef HAVE_SDL2
81 sdl.reset();
82#endif
83 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
84 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
85 Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
86 103
87 gcbuttons.reset(); 104 Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName());
88 gcanalog.reset(); 105 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName());
89 gcvibration.reset(); 106 mouse.reset();
90 107
91 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 108 Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName());
92 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 109 touch_screen.reset();
93 110
94 udpmotion.reset(); 111 Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName());
95 udptouch.reset(); 112 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName());
113 gcadapter.reset();
96 114
97 Input::UnregisterFactory<Input::ButtonDevice>("mouse"); 115 Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName());
98 Input::UnregisterFactory<Input::AnalogDevice>("mouse"); 116 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName());
99 Input::UnregisterFactory<Input::MotionDevice>("mouse"); 117 udp_client.reset();
100 Input::UnregisterFactory<Input::TouchDevice>("mouse");
101 118
102 mousebuttons.reset(); 119 Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName());
103 mouseanalog.reset(); 120 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName());
104 mousemotion.reset(); 121 tas_input.reset();
105 mousetouch.reset();
106 122
107 Input::UnregisterFactory<Input::ButtonDevice>("tas"); 123#ifdef HAVE_SDL2
108 Input::UnregisterFactory<Input::AnalogDevice>("tas"); 124 Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
125 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
126 sdl.reset();
127#endif
109 128
110 tasbuttons.reset(); 129 Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
111 tasanalog.reset(); 130 Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button");
112 } 131 }
113 132
114 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 133 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
115 std::vector<Common::ParamPackage> devices = { 134 std::vector<Common::ParamPackage> devices = {
116 Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, 135 Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
117 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
118 }; 136 };
119 if (Settings::values.tas_enable) { 137
120 devices.emplace_back( 138 auto keyboard_devices = keyboard->GetInputDevices();
121 Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}}); 139 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
122 } 140 auto mouse_devices = mouse->GetInputDevices();
141 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
142 auto gcadapter_devices = gcadapter->GetInputDevices();
143 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
144 auto udp_devices = udp_client->GetInputDevices();
145 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
123#ifdef HAVE_SDL2 146#ifdef HAVE_SDL2
124 auto sdl_devices = sdl->GetInputDevices(); 147 auto sdl_devices = sdl->GetInputDevices();
125 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); 148 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
126#endif 149#endif
127 auto udp_devices = udp->GetInputDevices(); 150
128 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
129 auto gcpad_devices = gcadapter->GetInputDevices();
130 devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end());
131 return devices; 151 return devices;
132 } 152 }
133 153
134 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( 154 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
135 const Common::ParamPackage& params) const { 155 const Common::ParamPackage& params) const {
136 if (!params.Has("class") || params.Get("class", "") == "any") { 156 if (!params.Has("engine") || params.Get("engine", "") == "any") {
137 return {}; 157 return {};
138 } 158 }
139 if (params.Get("class", "") == "gcpad") { 159 const std::string engine = params.Get("engine", "");
160 if (engine == mouse->GetEngineName()) {
161 return mouse->GetAnalogMappingForDevice(params);
162 }
163 if (engine == gcadapter->GetEngineName()) {
140 return gcadapter->GetAnalogMappingForDevice(params); 164 return gcadapter->GetAnalogMappingForDevice(params);
141 } 165 }
142 if (params.Get("class", "") == "tas") { 166 if (engine == udp_client->GetEngineName()) {
143 return tas->GetAnalogMappingForDevice(params); 167 return udp_client->GetAnalogMappingForDevice(params);
168 }
169 if (engine == tas_input->GetEngineName()) {
170 return tas_input->GetAnalogMappingForDevice(params);
144 } 171 }
145#ifdef HAVE_SDL2 172#ifdef HAVE_SDL2
146 if (params.Get("class", "") == "sdl") { 173 if (engine == sdl->GetEngineName()) {
147 return sdl->GetAnalogMappingForDevice(params); 174 return sdl->GetAnalogMappingForDevice(params);
148 } 175 }
149#endif 176#endif
@@ -152,17 +179,21 @@ struct InputSubsystem::Impl {
152 179
153 [[nodiscard]] ButtonMapping GetButtonMappingForDevice( 180 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
154 const Common::ParamPackage& params) const { 181 const Common::ParamPackage& params) const {
155 if (!params.Has("class") || params.Get("class", "") == "any") { 182 if (!params.Has("engine") || params.Get("engine", "") == "any") {
156 return {}; 183 return {};
157 } 184 }
158 if (params.Get("class", "") == "gcpad") { 185 const std::string engine = params.Get("engine", "");
186 if (engine == gcadapter->GetEngineName()) {
159 return gcadapter->GetButtonMappingForDevice(params); 187 return gcadapter->GetButtonMappingForDevice(params);
160 } 188 }
161 if (params.Get("class", "") == "tas") { 189 if (engine == udp_client->GetEngineName()) {
162 return tas->GetButtonMappingForDevice(params); 190 return udp_client->GetButtonMappingForDevice(params);
191 }
192 if (engine == tas_input->GetEngineName()) {
193 return tas_input->GetButtonMappingForDevice(params);
163 } 194 }
164#ifdef HAVE_SDL2 195#ifdef HAVE_SDL2
165 if (params.Get("class", "") == "sdl") { 196 if (engine == sdl->GetEngineName()) {
166 return sdl->GetButtonMappingForDevice(params); 197 return sdl->GetButtonMappingForDevice(params);
167 } 198 }
168#endif 199#endif
@@ -171,40 +202,119 @@ struct InputSubsystem::Impl {
171 202
172 [[nodiscard]] MotionMapping GetMotionMappingForDevice( 203 [[nodiscard]] MotionMapping GetMotionMappingForDevice(
173 const Common::ParamPackage& params) const { 204 const Common::ParamPackage& params) const {
174 if (!params.Has("class") || params.Get("class", "") == "any") { 205 if (!params.Has("engine") || params.Get("engine", "") == "any") {
175 return {}; 206 return {};
176 } 207 }
177 if (params.Get("class", "") == "cemuhookudp") { 208 const std::string engine = params.Get("engine", "");
178 // TODO return the correct motion device 209 if (engine == udp_client->GetEngineName()) {
179 return {}; 210 return udp_client->GetMotionMappingForDevice(params);
180 } 211 }
181#ifdef HAVE_SDL2 212#ifdef HAVE_SDL2
182 if (params.Get("class", "") == "sdl") { 213 if (engine == sdl->GetEngineName()) {
183 return sdl->GetMotionMappingForDevice(params); 214 return sdl->GetMotionMappingForDevice(params);
184 } 215 }
185#endif 216#endif
186 return {}; 217 return {};
187 } 218 }
188 219
220 Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
221 if (!params.Has("engine") || params.Get("engine", "") == "any") {
222 return Common::Input::ButtonNames::Undefined;
223 }
224 const std::string engine = params.Get("engine", "");
225 if (engine == mouse->GetEngineName()) {
226 return mouse->GetUIName(params);
227 }
228 if (engine == gcadapter->GetEngineName()) {
229 return gcadapter->GetUIName(params);
230 }
231 if (engine == udp_client->GetEngineName()) {
232 return udp_client->GetUIName(params);
233 }
234 if (engine == tas_input->GetEngineName()) {
235 return tas_input->GetUIName(params);
236 }
237#ifdef HAVE_SDL2
238 if (engine == sdl->GetEngineName()) {
239 return sdl->GetUIName(params);
240 }
241#endif
242 return Common::Input::ButtonNames::Invalid;
243 }
244
245 bool IsController(const Common::ParamPackage& params) {
246 const std::string engine = params.Get("engine", "");
247 if (engine == mouse->GetEngineName()) {
248 return true;
249 }
250 if (engine == gcadapter->GetEngineName()) {
251 return true;
252 }
253 if (engine == udp_client->GetEngineName()) {
254 return true;
255 }
256 if (engine == tas_input->GetEngineName()) {
257 return true;
258 }
259#ifdef HAVE_SDL2
260 if (engine == sdl->GetEngineName()) {
261 return true;
262 }
263#endif
264 return false;
265 }
266
267 void BeginConfiguration() {
268 keyboard->BeginConfiguration();
269 mouse->BeginConfiguration();
270 gcadapter->BeginConfiguration();
271 udp_client->BeginConfiguration();
272#ifdef HAVE_SDL2
273 sdl->BeginConfiguration();
274#endif
275 }
276
277 void EndConfiguration() {
278 keyboard->EndConfiguration();
279 mouse->EndConfiguration();
280 gcadapter->EndConfiguration();
281 udp_client->EndConfiguration();
282#ifdef HAVE_SDL2
283 sdl->EndConfiguration();
284#endif
285 }
286
287 void RegisterInput(MappingData data) {
288 mapping_factory->RegisterInput(data);
289 }
290
291 std::shared_ptr<MappingFactory> mapping_factory;
292
189 std::shared_ptr<Keyboard> keyboard; 293 std::shared_ptr<Keyboard> keyboard;
294 std::shared_ptr<Mouse> mouse;
295 std::shared_ptr<GCAdapter> gcadapter;
296 std::shared_ptr<TouchScreen> touch_screen;
297 std::shared_ptr<TasInput::Tas> tas_input;
298 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
299
300 std::shared_ptr<InputFactory> keyboard_factory;
301 std::shared_ptr<InputFactory> mouse_factory;
302 std::shared_ptr<InputFactory> gcadapter_input_factory;
303 std::shared_ptr<InputFactory> touch_screen_factory;
304 std::shared_ptr<InputFactory> udp_client_input_factory;
305 std::shared_ptr<InputFactory> tas_input_factory;
306
307 std::shared_ptr<OutputFactory> keyboard_output_factory;
308 std::shared_ptr<OutputFactory> mouse_output_factory;
309 std::shared_ptr<OutputFactory> gcadapter_output_factory;
310 std::shared_ptr<OutputFactory> udp_client_output_factory;
311 std::shared_ptr<OutputFactory> tas_output_factory;
312
190#ifdef HAVE_SDL2 313#ifdef HAVE_SDL2
191 std::unique_ptr<SDL::State> sdl; 314 std::shared_ptr<SDLDriver> sdl;
315 std::shared_ptr<InputFactory> sdl_input_factory;
316 std::shared_ptr<OutputFactory> sdl_output_factory;
192#endif 317#endif
193 std::shared_ptr<GCButtonFactory> gcbuttons;
194 std::shared_ptr<GCAnalogFactory> gcanalog;
195 std::shared_ptr<GCVibrationFactory> gcvibration;
196 std::shared_ptr<UDPMotionFactory> udpmotion;
197 std::shared_ptr<UDPTouchFactory> udptouch;
198 std::shared_ptr<MouseButtonFactory> mousebuttons;
199 std::shared_ptr<MouseAnalogFactory> mouseanalog;
200 std::shared_ptr<MouseMotionFactory> mousemotion;
201 std::shared_ptr<MouseTouchFactory> mousetouch;
202 std::shared_ptr<TasButtonFactory> tasbuttons;
203 std::shared_ptr<TasAnalogFactory> tasanalog;
204 std::shared_ptr<CemuhookUDP::Client> udp;
205 std::shared_ptr<GCAdapter::Adapter> gcadapter;
206 std::shared_ptr<MouseInput::Mouse> mouse;
207 std::shared_ptr<TasInput::Tas> tas;
208}; 318};
209 319
210InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 320InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -227,20 +337,28 @@ const Keyboard* InputSubsystem::GetKeyboard() const {
227 return impl->keyboard.get(); 337 return impl->keyboard.get();
228} 338}
229 339
230MouseInput::Mouse* InputSubsystem::GetMouse() { 340Mouse* InputSubsystem::GetMouse() {
231 return impl->mouse.get(); 341 return impl->mouse.get();
232} 342}
233 343
234const MouseInput::Mouse* InputSubsystem::GetMouse() const { 344const Mouse* InputSubsystem::GetMouse() const {
235 return impl->mouse.get(); 345 return impl->mouse.get();
236} 346}
237 347
348TouchScreen* InputSubsystem::GetTouchScreen() {
349 return impl->touch_screen.get();
350}
351
352const TouchScreen* InputSubsystem::GetTouchScreen() const {
353 return impl->touch_screen.get();
354}
355
238TasInput::Tas* InputSubsystem::GetTas() { 356TasInput::Tas* InputSubsystem::GetTas() {
239 return impl->tas.get(); 357 return impl->tas_input.get();
240} 358}
241 359
242const TasInput::Tas* InputSubsystem::GetTas() const { 360const TasInput::Tas* InputSubsystem::GetTas() const {
243 return impl->tas.get(); 361 return impl->tas_input.get();
244} 362}
245 363
246std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 364std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
@@ -259,100 +377,30 @@ MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPacka
259 return impl->GetMotionMappingForDevice(device); 377 return impl->GetMotionMappingForDevice(device);
260} 378}
261 379
262GCAnalogFactory* InputSubsystem::GetGCAnalogs() { 380Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
263 return impl->gcanalog.get(); 381 return impl->GetButtonName(params);
264}
265
266const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
267 return impl->gcanalog.get();
268}
269
270GCButtonFactory* InputSubsystem::GetGCButtons() {
271 return impl->gcbuttons.get();
272}
273
274const GCButtonFactory* InputSubsystem::GetGCButtons() const {
275 return impl->gcbuttons.get();
276}
277
278UDPMotionFactory* InputSubsystem::GetUDPMotions() {
279 return impl->udpmotion.get();
280}
281
282const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
283 return impl->udpmotion.get();
284}
285
286UDPTouchFactory* InputSubsystem::GetUDPTouch() {
287 return impl->udptouch.get();
288}
289
290const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
291 return impl->udptouch.get();
292}
293
294MouseButtonFactory* InputSubsystem::GetMouseButtons() {
295 return impl->mousebuttons.get();
296} 382}
297 383
298const MouseButtonFactory* InputSubsystem::GetMouseButtons() const { 384bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
299 return impl->mousebuttons.get(); 385 return impl->IsController(params);
300} 386}
301 387
302MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() { 388void InputSubsystem::ReloadInputDevices() {
303 return impl->mouseanalog.get(); 389 impl->udp_client.get()->ReloadSockets();
304}
305
306const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const {
307 return impl->mouseanalog.get();
308}
309
310MouseMotionFactory* InputSubsystem::GetMouseMotions() {
311 return impl->mousemotion.get();
312}
313
314const MouseMotionFactory* InputSubsystem::GetMouseMotions() const {
315 return impl->mousemotion.get();
316}
317
318MouseTouchFactory* InputSubsystem::GetMouseTouch() {
319 return impl->mousetouch.get();
320}
321
322const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
323 return impl->mousetouch.get();
324}
325
326TasButtonFactory* InputSubsystem::GetTasButtons() {
327 return impl->tasbuttons.get();
328}
329
330const TasButtonFactory* InputSubsystem::GetTasButtons() const {
331 return impl->tasbuttons.get();
332}
333
334TasAnalogFactory* InputSubsystem::GetTasAnalogs() {
335 return impl->tasanalog.get();
336} 390}
337 391
338const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const { 392void InputSubsystem::BeginMapping(Polling::InputType type) {
339 return impl->tasanalog.get(); 393 impl->BeginConfiguration();
394 impl->mapping_factory->BeginMapping(type);
340} 395}
341 396
342void InputSubsystem::ReloadInputDevices() { 397const Common::ParamPackage InputSubsystem::GetNextInput() const {
343 if (!impl->udp) { 398 return impl->mapping_factory->GetNextInput();
344 return;
345 }
346 impl->udp->ReloadSockets();
347} 399}
348 400
349std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( 401void InputSubsystem::StopMapping() const {
350 [[maybe_unused]] Polling::DeviceType type) const { 402 impl->EndConfiguration();
351#ifdef HAVE_SDL2 403 impl->mapping_factory->StopMapping();
352 return impl->sdl->GetPollers(type);
353#else
354 return {};
355#endif
356} 404}
357 405
358std::string GenerateKeyboardParam(int key_code) { 406std::string GenerateKeyboardParam(int key_code) {
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 6390d3f09..c6f97f691 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -13,6 +13,10 @@ namespace Common {
13class ParamPackage; 13class ParamPackage;
14} 14}
15 15
16namespace Common::Input {
17enum class ButtonNames;
18}
19
16namespace Settings::NativeAnalog { 20namespace Settings::NativeAnalog {
17enum Values : int; 21enum Values : int;
18} 22}
@@ -25,56 +29,26 @@ namespace Settings::NativeMotion {
25enum Values : int; 29enum Values : int;
26} 30}
27 31
28namespace MouseInput { 32namespace InputCommon {
33class Keyboard;
29class Mouse; 34class Mouse;
30} 35class TouchScreen;
36struct MappingData;
37} // namespace InputCommon
31 38
32namespace TasInput { 39namespace InputCommon::TasInput {
33class Tas; 40class Tas;
34} 41} // namespace InputCommon::TasInput
35 42
36namespace InputCommon { 43namespace InputCommon {
37namespace Polling { 44namespace Polling {
38 45/// Type of input desired for mapping purposes
39enum class DeviceType { Button, AnalogPreferred, Motion }; 46enum class InputType { None, Button, Stick, Motion, Touch };
40
41/**
42 * A class that can be used to get inputs from an input device like controllers without having to
43 * poll the device's status yourself
44 */
45class DevicePoller {
46public:
47 virtual ~DevicePoller() = default;
48 /// Setup and start polling for inputs, should be called before GetNextInput
49 /// If a device_id is provided, events should be filtered to only include events from this
50 /// device id
51 virtual void Start(const std::string& device_id = "") = 0;
52 /// Stop polling
53 virtual void Stop() = 0;
54 /**
55 * Every call to this function returns the next input recorded since calling Start
56 * @return A ParamPackage of the recorded input, which can be used to create an InputDevice.
57 * If there has been no input, the package is empty
58 */
59 virtual Common::ParamPackage GetNextInput() = 0;
60};
61} // namespace Polling 47} // namespace Polling
62 48
63class GCAnalogFactory;
64class GCButtonFactory;
65class UDPMotionFactory;
66class UDPTouchFactory;
67class MouseButtonFactory;
68class MouseAnalogFactory;
69class MouseMotionFactory;
70class MouseTouchFactory;
71class TasButtonFactory;
72class TasAnalogFactory;
73class Keyboard;
74
75/** 49/**
76 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default 50 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
77 * mapping for the device. This is currently only implemented for the SDL backend devices. 51 * mapping for the device.
78 */ 52 */
79using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; 53using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
80using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; 54using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
@@ -104,20 +78,27 @@ public:
104 [[nodiscard]] const Keyboard* GetKeyboard() const; 78 [[nodiscard]] const Keyboard* GetKeyboard() const;
105 79
106 /// Retrieves the underlying mouse device. 80 /// Retrieves the underlying mouse device.
107 [[nodiscard]] MouseInput::Mouse* GetMouse(); 81 [[nodiscard]] Mouse* GetMouse();
108 82
109 /// Retrieves the underlying mouse device. 83 /// Retrieves the underlying mouse device.
110 [[nodiscard]] const MouseInput::Mouse* GetMouse() const; 84 [[nodiscard]] const Mouse* GetMouse() const;
85
86 /// Retrieves the underlying touch screen device.
87 [[nodiscard]] TouchScreen* GetTouchScreen();
111 88
112 /// Retrieves the underlying tas device. 89 /// Retrieves the underlying touch screen device.
90 [[nodiscard]] const TouchScreen* GetTouchScreen() const;
91
92 /// Retrieves the underlying tas input device.
113 [[nodiscard]] TasInput::Tas* GetTas(); 93 [[nodiscard]] TasInput::Tas* GetTas();
114 94
115 /// Retrieves the underlying tas device. 95 /// Retrieves the underlying tas input device.
116 [[nodiscard]] const TasInput::Tas* GetTas() const; 96 [[nodiscard]] const TasInput::Tas* GetTas() const;
97
117 /** 98 /**
118 * Returns all available input devices that this Factory can create a new device with. 99 * Returns all available input devices that this Factory can create a new device with.
119 * Each returned ParamPackage should have a `display` field used for display, a class field for 100 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
120 * backends to determine if this backend is meant to service the request and any other 101 * for backends to determine if this backend is meant to service the request and any other
121 * information needed to identify this in the backend later. 102 * information needed to identify this in the backend later.
122 */ 103 */
123 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const; 104 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
@@ -131,83 +112,34 @@ public:
131 /// Retrieves the motion mappings for the given device. 112 /// Retrieves the motion mappings for the given device.
132 [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const; 113 [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
133 114
134 /// Retrieves the underlying GameCube analog handler. 115 /// Returns an enum contaning the name to be displayed from the input engine.
135 [[nodiscard]] GCAnalogFactory* GetGCAnalogs(); 116 [[nodiscard]] Common::Input::ButtonNames GetButtonName(
117 const Common::ParamPackage& params) const;
136 118
137 /// Retrieves the underlying GameCube analog handler. 119 /// Returns true if device is a controller.
138 [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const; 120 [[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
139 121
140 /// Retrieves the underlying GameCube button handler. 122 /// Reloads the input devices.
141 [[nodiscard]] GCButtonFactory* GetGCButtons(); 123 void ReloadInputDevices();
142
143 /// Retrieves the underlying GameCube button handler.
144 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
145
146 /// Retrieves the underlying udp motion handler.
147 [[nodiscard]] UDPMotionFactory* GetUDPMotions();
148
149 /// Retrieves the underlying udp motion handler.
150 [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
151
152 /// Retrieves the underlying udp touch handler.
153 [[nodiscard]] UDPTouchFactory* GetUDPTouch();
154
155 /// Retrieves the underlying udp touch handler.
156 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
157
158 /// Retrieves the underlying mouse button handler.
159 [[nodiscard]] MouseButtonFactory* GetMouseButtons();
160
161 /// Retrieves the underlying mouse button handler.
162 [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
163
164 /// Retrieves the underlying mouse analog handler.
165 [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
166
167 /// Retrieves the underlying mouse analog handler.
168 [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
169
170 /// Retrieves the underlying mouse motion handler.
171 [[nodiscard]] MouseMotionFactory* GetMouseMotions();
172
173 /// Retrieves the underlying mouse motion handler.
174 [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
175
176 /// Retrieves the underlying mouse touch handler.
177 [[nodiscard]] MouseTouchFactory* GetMouseTouch();
178
179 /// Retrieves the underlying mouse touch handler.
180 [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
181
182 /// Retrieves the underlying tas button handler.
183 [[nodiscard]] TasButtonFactory* GetTasButtons();
184
185 /// Retrieves the underlying tas button handler.
186 [[nodiscard]] const TasButtonFactory* GetTasButtons() const;
187
188 /// Retrieves the underlying tas analogs handler.
189 [[nodiscard]] TasAnalogFactory* GetTasAnalogs();
190 124
191 /// Retrieves the underlying tas analogs handler. 125 /// Start polling from all backends for a desired input type.
192 [[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const; 126 void BeginMapping(Polling::InputType type);
193 127
194 /// Reloads the input devices 128 /// Returns an input event with mapping information.
195 void ReloadInputDevices(); 129 [[nodiscard]] const Common::ParamPackage GetNextInput() const;
196 130
197 /// Get all DevicePoller from all backends for a specific device type 131 /// Stop polling from all backends.
198 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers( 132 void StopMapping() const;
199 Polling::DeviceType type) const;
200 133
201private: 134private:
202 struct Impl; 135 struct Impl;
203 std::unique_ptr<Impl> impl; 136 std::unique_ptr<Impl> impl;
204}; 137};
205 138
206/// Generates a serialized param package for creating a keyboard button device 139/// Generates a serialized param package for creating a keyboard button device.
207std::string GenerateKeyboardParam(int key_code); 140std::string GenerateKeyboardParam(int key_code);
208 141
209/// Generates a serialized param package for creating an analog device taking input from keyboard 142/// Generates a serialized param package for creating an analog device taking input from keyboard.
210std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 143std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
211 int key_modifier, float modifier_scale); 144 int key_modifier, float modifier_scale);
212
213} // namespace InputCommon 145} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.cpp b/src/input_common/motion_from_button.cpp
deleted file mode 100644
index 29045a673..000000000
--- a/src/input_common/motion_from_button.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
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 "input_common/motion_from_button.h"
6#include "input_common/motion_input.h"
7
8namespace InputCommon {
9
10class MotionKey final : public Input::MotionDevice {
11public:
12 using Button = std::unique_ptr<Input::ButtonDevice>;
13
14 explicit MotionKey(Button key_) : key(std::move(key_)) {}
15
16 Input::MotionStatus GetStatus() const override {
17
18 if (key->GetStatus()) {
19 return motion.GetRandomMotion(2, 6);
20 }
21 return motion.GetRandomMotion(0, 0);
22 }
23
24private:
25 Button key;
26 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
27};
28
29std::unique_ptr<Input::MotionDevice> MotionFromButton::Create(const Common::ParamPackage& params) {
30 auto key = Input::CreateDevice<Input::ButtonDevice>(params.Serialize());
31 return std::make_unique<MotionKey>(std::move(key));
32}
33
34} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.h b/src/input_common/motion_from_button.h
deleted file mode 100644
index a959046fb..000000000
--- a/src/input_common/motion_from_button.h
+++ /dev/null
@@ -1,25 +0,0 @@
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 "core/frontend/input.h"
8
9namespace InputCommon {
10
11/**
12 * An motion device factory that takes a keyboard button and uses it as a random
13 * motion device.
14 */
15class MotionFromButton final : public Input::Factory<Input::MotionDevice> {
16public:
17 /**
18 * Creates an motion device from button devices
19 * @param params contains parameters for creating the device:
20 * - "key": a serialized ParamPackage for creating a button device
21 */
22 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
23};
24
25} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
deleted file mode 100644
index 3b052ffb2..000000000
--- a/src/input_common/mouse/mouse_input.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <stop_token>
6#include <thread>
7
8#include "common/settings.h"
9#include "input_common/mouse/mouse_input.h"
10
11namespace MouseInput {
12
13Mouse::Mouse() {
14 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
15}
16
17Mouse::~Mouse() = default;
18
19void Mouse::UpdateThread(std::stop_token stop_token) {
20 constexpr int update_time = 10;
21 while (!stop_token.stop_requested()) {
22 for (MouseInfo& info : mouse_info) {
23 const Common::Vec3f angular_direction{
24 -info.tilt_direction.y,
25 0.0f,
26 -info.tilt_direction.x,
27 };
28
29 info.motion.SetGyroscope(angular_direction * info.tilt_speed);
30 info.motion.UpdateRotation(update_time * 1000);
31 info.motion.UpdateOrientation(update_time * 1000);
32 info.tilt_speed = 0;
33 info.data.motion = info.motion.GetMotion();
34 if (Settings::values.mouse_panning) {
35 info.last_mouse_change *= 0.96f;
36 info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x),
37 static_cast<int>(16 * -info.last_mouse_change.y)};
38 }
39 }
40 if (configuring) {
41 UpdateYuzuSettings();
42 }
43 if (mouse_panning_timout++ > 20) {
44 StopPanning();
45 }
46 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
47 }
48}
49
50void Mouse::UpdateYuzuSettings() {
51 if (buttons == 0) {
52 return;
53 }
54
55 mouse_queue.Push(MouseStatus{
56 .button = last_button,
57 });
58}
59
60void Mouse::PressButton(int x, int y, MouseButton button_) {
61 const auto button_index = static_cast<std::size_t>(button_);
62 if (button_index >= mouse_info.size()) {
63 return;
64 }
65
66 const auto button = 1U << button_index;
67 buttons |= static_cast<u16>(button);
68 last_button = button_;
69
70 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
71 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
72 mouse_info[button_index].data.pressed = true;
73}
74
75void Mouse::StopPanning() {
76 for (MouseInfo& info : mouse_info) {
77 if (Settings::values.mouse_panning) {
78 info.data.axis = {};
79 info.tilt_speed = 0;
80 info.last_mouse_change = {};
81 }
82 }
83}
84
85void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
86 for (MouseInfo& info : mouse_info) {
87 if (Settings::values.mouse_panning) {
88 auto mouse_change =
89 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
90 mouse_panning_timout = 0;
91
92 if (mouse_change.y == 0 && mouse_change.x == 0) {
93 continue;
94 }
95 const auto mouse_change_length = mouse_change.Length();
96 if (mouse_change_length < 3.0f) {
97 mouse_change /= mouse_change_length / 3.0f;
98 }
99
100 info.last_mouse_change = (info.last_mouse_change * 0.91f) + (mouse_change * 0.09f);
101
102 const auto last_mouse_change_length = info.last_mouse_change.Length();
103 if (last_mouse_change_length > 8.0f) {
104 info.last_mouse_change /= last_mouse_change_length / 8.0f;
105 } else if (last_mouse_change_length < 1.0f) {
106 info.last_mouse_change = mouse_change / mouse_change.Length();
107 }
108
109 info.tilt_direction = info.last_mouse_change;
110 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
111 continue;
112 }
113
114 if (info.data.pressed) {
115 const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
116 const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;
117 info.last_mouse_position = Common::MakeVec(x, y);
118 info.data.axis = {mouse_move.x, -mouse_move.y};
119
120 if (mouse_change.x == 0 && mouse_change.y == 0) {
121 info.tilt_speed = 0;
122 } else {
123 info.tilt_direction = mouse_change.Cast<float>();
124 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
125 }
126 }
127 }
128}
129
130void Mouse::ReleaseButton(MouseButton button_) {
131 const auto button_index = static_cast<std::size_t>(button_);
132 if (button_index >= mouse_info.size()) {
133 return;
134 }
135
136 const auto button = 1U << button_index;
137 buttons &= static_cast<u16>(0xFF - button);
138
139 mouse_info[button_index].tilt_speed = 0;
140 mouse_info[button_index].data.pressed = false;
141 mouse_info[button_index].data.axis = {0, 0};
142}
143
144void Mouse::ReleaseAllButtons() {
145 buttons = 0;
146 for (auto& info : mouse_info) {
147 info.tilt_speed = 0;
148 info.data.pressed = false;
149 info.data.axis = {0, 0};
150 }
151}
152
153void Mouse::BeginConfiguration() {
154 buttons = 0;
155 last_button = MouseButton::Undefined;
156 mouse_queue.Clear();
157 configuring = true;
158}
159
160void Mouse::EndConfiguration() {
161 buttons = 0;
162 for (MouseInfo& info : mouse_info) {
163 info.tilt_speed = 0;
164 info.data.pressed = false;
165 info.data.axis = {0, 0};
166 }
167 last_button = MouseButton::Undefined;
168 mouse_queue.Clear();
169 configuring = false;
170}
171
172bool Mouse::ToggleButton(std::size_t button_) {
173 if (button_ >= mouse_info.size()) {
174 return false;
175 }
176 const auto button = 1U << button_;
177 const bool button_state = (toggle_buttons & button) != 0;
178 const bool button_lock = (lock_buttons & button) != 0;
179
180 if (button_lock) {
181 return button_state;
182 }
183
184 lock_buttons |= static_cast<u16>(button);
185
186 if (button_state) {
187 toggle_buttons &= static_cast<u16>(0xFF - button);
188 } else {
189 toggle_buttons |= static_cast<u16>(button);
190 }
191
192 return !button_state;
193}
194
195bool Mouse::UnlockButton(std::size_t button_) {
196 if (button_ >= mouse_info.size()) {
197 return false;
198 }
199
200 const auto button = 1U << button_;
201 const bool button_state = (toggle_buttons & button) != 0;
202
203 lock_buttons &= static_cast<u16>(0xFF - button);
204
205 return button_state;
206}
207
208Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
209 return mouse_queue;
210}
211
212const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
213 return mouse_queue;
214}
215
216MouseData& Mouse::GetMouseState(std::size_t button) {
217 return mouse_info[button].data;
218}
219
220const MouseData& Mouse::GetMouseState(std::size_t button) const {
221 return mouse_info[button].data;
222}
223} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
deleted file mode 100644
index c8bae99c1..000000000
--- a/src/input_common/mouse/mouse_input.h
+++ /dev/null
@@ -1,116 +0,0 @@
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 <array>
8#include <mutex>
9#include <stop_token>
10#include <thread>
11
12#include "common/common_types.h"
13#include "common/threadsafe_queue.h"
14#include "common/vector_math.h"
15#include "core/frontend/input.h"
16#include "input_common/motion_input.h"
17
18namespace MouseInput {
19
20enum class MouseButton {
21 Left,
22 Right,
23 Wheel,
24 Backward,
25 Forward,
26 Task,
27 Extra,
28 Undefined,
29};
30
31struct MouseStatus {
32 MouseButton button{MouseButton::Undefined};
33};
34
35struct MouseData {
36 bool pressed{};
37 std::array<int, 2> axis{};
38 Input::MotionStatus motion{};
39 Input::TouchStatus touch{};
40};
41
42class Mouse {
43public:
44 Mouse();
45 ~Mouse();
46
47 /// Used for polling
48 void BeginConfiguration();
49 void EndConfiguration();
50
51 /**
52 * Signals that a button is pressed.
53 * @param x the x-coordinate of the cursor
54 * @param y the y-coordinate of the cursor
55 * @param button_ the button pressed
56 */
57 void PressButton(int x, int y, MouseButton button_);
58
59 /**
60 * Signals that mouse has moved.
61 * @param x the x-coordinate of the cursor
62 * @param y the y-coordinate of the cursor
63 * @param center_x the x-coordinate of the middle of the screen
64 * @param center_y the y-coordinate of the middle of the screen
65 */
66 void MouseMove(int x, int y, int center_x, int center_y);
67
68 /**
69 * Signals that a button is released.
70 * @param button_ the button pressed
71 */
72 void ReleaseButton(MouseButton button_);
73
74 /**
75 * Signals that all buttons are released
76 */
77 void ReleaseAllButtons();
78
79 [[nodiscard]] bool ToggleButton(std::size_t button_);
80 [[nodiscard]] bool UnlockButton(std::size_t button_);
81
82 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
83 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
84
85 [[nodiscard]] MouseData& GetMouseState(std::size_t button);
86 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
87
88private:
89 void UpdateThread(std::stop_token stop_token);
90 void UpdateYuzuSettings();
91 void StopPanning();
92
93 struct MouseInfo {
94 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
95 Common::Vec2<int> mouse_origin;
96 Common::Vec2<int> last_mouse_position;
97 Common::Vec2<float> last_mouse_change;
98 bool is_tilting = false;
99 float sensitivity{0.120f};
100
101 float tilt_speed = 0;
102 Common::Vec2<float> tilt_direction;
103 MouseData data;
104 };
105
106 u16 buttons{};
107 u16 toggle_buttons{};
108 u16 lock_buttons{};
109 std::jthread update_thread;
110 MouseButton last_button{MouseButton::Undefined};
111 std::array<MouseInfo, 7> mouse_info;
112 Common::SPSCQueue<MouseStatus> mouse_queue;
113 bool configuring{false};
114 int mouse_panning_timout{};
115};
116} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
deleted file mode 100644
index 090b26972..000000000
--- a/src/input_common/mouse/mouse_poller.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
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 <algorithm>
6#include <memory>
7#include <mutex>
8#include <utility>
9
10#include "common/settings.h"
11#include "common/threadsafe_queue.h"
12#include "input_common/mouse/mouse_input.h"
13#include "input_common/mouse/mouse_poller.h"
14
15namespace InputCommon {
16
17class MouseButton final : public Input::ButtonDevice {
18public:
19 explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_)
20 : button(button_), toggle(toggle_), mouse_input(mouse_input_) {}
21
22 bool GetStatus() const override {
23 const bool button_state = mouse_input->GetMouseState(button).pressed;
24 if (!toggle) {
25 return button_state;
26 }
27
28 if (button_state) {
29 return mouse_input->ToggleButton(button);
30 }
31 return mouse_input->UnlockButton(button);
32 }
33
34private:
35 const u32 button;
36 const bool toggle;
37 MouseInput::Mouse* mouse_input;
38};
39
40MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
41 : mouse_input(std::move(mouse_input_)) {}
42
43std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
44 const Common::ParamPackage& params) {
45 const auto button_id = params.Get("button", 0);
46 const auto toggle = params.Get("toggle", false);
47
48 return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get());
49}
50
51Common::ParamPackage MouseButtonFactory::GetNextInput() const {
52 MouseInput::MouseStatus pad;
53 Common::ParamPackage params;
54 auto& queue = mouse_input->GetMouseQueue();
55 while (queue.Pop(pad)) {
56 // This while loop will break on the earliest detected button
57 if (pad.button != MouseInput::MouseButton::Undefined) {
58 params.Set("engine", "mouse");
59 params.Set("button", static_cast<u16>(pad.button));
60 params.Set("toggle", false);
61 return params;
62 }
63 }
64 return params;
65}
66
67void MouseButtonFactory::BeginConfiguration() {
68 polling = true;
69 mouse_input->BeginConfiguration();
70}
71
72void MouseButtonFactory::EndConfiguration() {
73 polling = false;
74 mouse_input->EndConfiguration();
75}
76
77class MouseAnalog final : public Input::AnalogDevice {
78public:
79 explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
80 float deadzone_, float range_, const MouseInput::Mouse* mouse_input_)
81 : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
82 deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {}
83
84 float GetAxis(u32 axis) const {
85 std::lock_guard lock{mutex};
86 const auto axis_value =
87 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
88 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.10f;
89 return axis_value * sensitivity / (100.0f * range);
90 }
91
92 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
93 float x = GetAxis(analog_axis_x);
94 float y = GetAxis(analog_axis_y);
95 if (invert_x) {
96 x = -x;
97 }
98 if (invert_y) {
99 y = -y;
100 }
101
102 // Make sure the coordinates are in the unit circle,
103 // otherwise normalize it.
104 float r = x * x + y * y;
105 if (r > 1.0f) {
106 r = std::sqrt(r);
107 x /= r;
108 y /= r;
109 }
110
111 return {x, y};
112 }
113
114 std::tuple<float, float> GetStatus() const override {
115 const auto [x, y] = GetAnalog(axis_x, axis_y);
116 const float r = std::sqrt((x * x) + (y * y));
117 if (r > deadzone) {
118 return {x / r * (r - deadzone) / (1 - deadzone),
119 y / r * (r - deadzone) / (1 - deadzone)};
120 }
121 return {0.0f, 0.0f};
122 }
123
124 std::tuple<float, float> GetRawStatus() const override {
125 const float x = GetAxis(axis_x);
126 const float y = GetAxis(axis_y);
127 return {x, y};
128 }
129
130 Input::AnalogProperties GetAnalogProperties() const override {
131 return {deadzone, range, 0.5f};
132 }
133
134private:
135 const u32 button;
136 const u32 axis_x;
137 const u32 axis_y;
138 const bool invert_x;
139 const bool invert_y;
140 const float deadzone;
141 const float range;
142 const MouseInput::Mouse* mouse_input;
143 mutable std::mutex mutex;
144};
145
146/// An analog device factory that creates analog devices from GC Adapter
147MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
148 : mouse_input(std::move(mouse_input_)) {}
149
150/**
151 * Creates analog device from joystick axes
152 * @param params contains parameters for creating the device:
153 * - "port": the nth gcpad on the adapter
154 * - "axis_x": the index of the axis to be bind as x-axis
155 * - "axis_y": the index of the axis to be bind as y-axis
156 */
157std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create(
158 const Common::ParamPackage& params) {
159 const auto port = static_cast<u32>(params.Get("port", 0));
160 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
161 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
162 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
163 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
164 const std::string invert_x_value = params.Get("invert_x", "+");
165 const std::string invert_y_value = params.Get("invert_y", "+");
166 const bool invert_x = invert_x_value == "-";
167 const bool invert_y = invert_y_value == "-";
168
169 return std::make_unique<MouseAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
170 mouse_input.get());
171}
172
173void MouseAnalogFactory::BeginConfiguration() {
174 polling = true;
175 mouse_input->BeginConfiguration();
176}
177
178void MouseAnalogFactory::EndConfiguration() {
179 polling = false;
180 mouse_input->EndConfiguration();
181}
182
183Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
184 MouseInput::MouseStatus pad;
185 Common::ParamPackage params;
186 auto& queue = mouse_input->GetMouseQueue();
187 while (queue.Pop(pad)) {
188 // This while loop will break on the earliest detected button
189 if (pad.button != MouseInput::MouseButton::Undefined) {
190 params.Set("engine", "mouse");
191 params.Set("port", static_cast<u16>(pad.button));
192 params.Set("axis_x", 0);
193 params.Set("axis_y", 1);
194 params.Set("invert_x", "+");
195 params.Set("invert_y", "+");
196 return params;
197 }
198 }
199 return params;
200}
201
202class MouseMotion final : public Input::MotionDevice {
203public:
204 explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_)
205 : button(button_), mouse_input(mouse_input_) {}
206
207 Input::MotionStatus GetStatus() const override {
208 return mouse_input->GetMouseState(button).motion;
209 }
210
211private:
212 const u32 button;
213 const MouseInput::Mouse* mouse_input;
214};
215
216MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
217 : mouse_input(std::move(mouse_input_)) {}
218
219std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create(
220 const Common::ParamPackage& params) {
221 const auto button_id = params.Get("button", 0);
222
223 return std::make_unique<MouseMotion>(button_id, mouse_input.get());
224}
225
226Common::ParamPackage MouseMotionFactory::GetNextInput() const {
227 MouseInput::MouseStatus pad;
228 Common::ParamPackage params;
229 auto& queue = mouse_input->GetMouseQueue();
230 while (queue.Pop(pad)) {
231 // This while loop will break on the earliest detected button
232 if (pad.button != MouseInput::MouseButton::Undefined) {
233 params.Set("engine", "mouse");
234 params.Set("button", static_cast<u16>(pad.button));
235 return params;
236 }
237 }
238 return params;
239}
240
241void MouseMotionFactory::BeginConfiguration() {
242 polling = true;
243 mouse_input->BeginConfiguration();
244}
245
246void MouseMotionFactory::EndConfiguration() {
247 polling = false;
248 mouse_input->EndConfiguration();
249}
250
251class MouseTouch final : public Input::TouchDevice {
252public:
253 explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_)
254 : button(button_), mouse_input(mouse_input_) {}
255
256 Input::TouchStatus GetStatus() const override {
257 return mouse_input->GetMouseState(button).touch;
258 }
259
260private:
261 const u32 button;
262 const MouseInput::Mouse* mouse_input;
263};
264
265MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
266 : mouse_input(std::move(mouse_input_)) {}
267
268std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) {
269 const auto button_id = params.Get("button", 0);
270
271 return std::make_unique<MouseTouch>(button_id, mouse_input.get());
272}
273
274Common::ParamPackage MouseTouchFactory::GetNextInput() const {
275 MouseInput::MouseStatus pad;
276 Common::ParamPackage params;
277 auto& queue = mouse_input->GetMouseQueue();
278 while (queue.Pop(pad)) {
279 // This while loop will break on the earliest detected button
280 if (pad.button != MouseInput::MouseButton::Undefined) {
281 params.Set("engine", "mouse");
282 params.Set("button", static_cast<u16>(pad.button));
283 return params;
284 }
285 }
286 return params;
287}
288
289void MouseTouchFactory::BeginConfiguration() {
290 polling = true;
291 mouse_input->BeginConfiguration();
292}
293
294void MouseTouchFactory::EndConfiguration() {
295 polling = false;
296 mouse_input->EndConfiguration();
297}
298
299} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_poller.h b/src/input_common/mouse/mouse_poller.h
deleted file mode 100644
index cf331293b..000000000
--- a/src/input_common/mouse/mouse_poller.h
+++ /dev/null
@@ -1,109 +0,0 @@
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 <memory>
8#include "core/frontend/input.h"
9#include "input_common/mouse/mouse_input.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a mouse. It receives mouse events and forward them
15 * to all button devices it created.
16 */
17class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput() const;
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<MouseInput::Mouse> mouse_input;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from mouse
44class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49
50 Common::ParamPackage GetNextInput() const;
51
52 /// For device input configuration/polling
53 void BeginConfiguration();
54 void EndConfiguration();
55
56 bool IsPolling() const {
57 return polling;
58 }
59
60private:
61 std::shared_ptr<MouseInput::Mouse> mouse_input;
62 bool polling = false;
63};
64
65/// A motion device factory that creates motion devices from mouse
66class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> {
67public:
68 explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
69
70 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
71
72 Common::ParamPackage GetNextInput() const;
73
74 /// For device input configuration/polling
75 void BeginConfiguration();
76 void EndConfiguration();
77
78 bool IsPolling() const {
79 return polling;
80 }
81
82private:
83 std::shared_ptr<MouseInput::Mouse> mouse_input;
84 bool polling = false;
85};
86
87/// An touch device factory that creates touch devices from mouse
88class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> {
89public:
90 explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
91
92 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
93
94 Common::ParamPackage GetNextInput() const;
95
96 /// For device input configuration/polling
97 void BeginConfiguration();
98 void EndConfiguration();
99
100 bool IsPolling() const {
101 return polling;
102 }
103
104private:
105 std::shared_ptr<MouseInput::Mouse> mouse_input;
106 bool polling = false;
107};
108
109} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
deleted file mode 100644
index 644db3448..000000000
--- a/src/input_common/sdl/sdl.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/sdl/sdl.h"
6#ifdef HAVE_SDL2
7#include "input_common/sdl/sdl_impl.h"
8#endif
9
10namespace InputCommon::SDL {
11
12std::unique_ptr<State> Init() {
13#ifdef HAVE_SDL2
14 return std::make_unique<SDLState>();
15#else
16 return std::make_unique<NullState>();
17#endif
18}
19} // namespace InputCommon::SDL
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
deleted file mode 100644
index b5d41bba4..000000000
--- a/src/input_common/sdl/sdl.h
+++ /dev/null
@@ -1,51 +0,0 @@
1// Copyright 2018 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 <vector>
9#include "common/param_package.h"
10#include "input_common/main.h"
11
12namespace InputCommon::Polling {
13class DevicePoller;
14enum class DeviceType;
15} // namespace InputCommon::Polling
16
17namespace InputCommon::SDL {
18
19class State {
20public:
21 using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
22
23 /// Unregisters SDL device factories and shut them down.
24 virtual ~State() = default;
25
26 virtual Pollers GetPollers(Polling::DeviceType) {
27 return {};
28 }
29
30 virtual std::vector<Common::ParamPackage> GetInputDevices() {
31 return {};
32 }
33
34 virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
35 return {};
36 }
37 virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
38 return {};
39 }
40 virtual MotionMapping GetMotionMappingForDevice(const Common::ParamPackage&) {
41 return {};
42 }
43};
44
45class NullState : public State {
46public:
47};
48
49std::unique_ptr<State> Init();
50
51} // namespace InputCommon::SDL
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
deleted file mode 100644
index ecb00d428..000000000
--- a/src/input_common/sdl/sdl_impl.cpp
+++ /dev/null
@@ -1,1658 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <atomic>
8#include <chrono>
9#include <cmath>
10#include <functional>
11#include <mutex>
12#include <optional>
13#include <sstream>
14#include <string>
15#include <thread>
16#include <tuple>
17#include <unordered_map>
18#include <utility>
19#include <vector>
20
21#include "common/logging/log.h"
22#include "common/math_util.h"
23#include "common/param_package.h"
24#include "common/settings.h"
25#include "common/threadsafe_queue.h"
26#include "core/frontend/input.h"
27#include "input_common/motion_input.h"
28#include "input_common/sdl/sdl_impl.h"
29
30namespace InputCommon::SDL {
31
32namespace {
33std::string GetGUID(SDL_Joystick* joystick) {
34 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
35 char guid_str[33];
36 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
37 return guid_str;
38}
39
40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
41Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
42} // Anonymous namespace
43
44static int SDLEventWatcher(void* user_data, SDL_Event* event) {
45 auto* const sdl_state = static_cast<SDLState*>(user_data);
46
47 // Don't handle the event if we are configuring
48 if (sdl_state->polling) {
49 sdl_state->event_queue.Push(*event);
50 } else {
51 sdl_state->HandleGameControllerEvent(*event);
52 }
53
54 return 0;
55}
56
57class SDLJoystick {
58public:
59 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
60 SDL_GameController* game_controller)
61 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
62 sdl_controller{game_controller, &SDL_GameControllerClose} {
63 EnableMotion();
64 }
65
66 void EnableMotion() {
67 if (sdl_controller) {
68 SDL_GameController* controller = sdl_controller.get();
69 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) {
70 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
71 has_accel = true;
72 }
73 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) {
74 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
75 has_gyro = true;
76 }
77 }
78 }
79
80 void SetButton(int button, bool value) {
81 std::lock_guard lock{mutex};
82 state.buttons.insert_or_assign(button, value);
83 }
84
85 void PreSetButton(int button) {
86 if (!state.buttons.contains(button)) {
87 SetButton(button, false);
88 }
89 }
90
91 void SetMotion(SDL_ControllerSensorEvent event) {
92 constexpr float gravity_constant = 9.80665f;
93 std::lock_guard lock{mutex};
94 u64 time_difference = event.timestamp - last_motion_update;
95 last_motion_update = event.timestamp;
96 switch (event.sensor) {
97 case SDL_SENSOR_ACCEL: {
98 const Common::Vec3f acceleration = {-event.data[0], event.data[2], -event.data[1]};
99 motion.SetAcceleration(acceleration / gravity_constant);
100 break;
101 }
102 case SDL_SENSOR_GYRO: {
103 const Common::Vec3f gyroscope = {event.data[0], -event.data[2], event.data[1]};
104 motion.SetGyroscope(gyroscope / (Common::PI * 2));
105 break;
106 }
107 }
108
109 // Ignore duplicated timestamps
110 if (time_difference == 0) {
111 return;
112 }
113
114 motion.SetGyroThreshold(0.0001f);
115 motion.UpdateRotation(time_difference * 1000);
116 motion.UpdateOrientation(time_difference * 1000);
117 }
118
119 bool GetButton(int button) const {
120 std::lock_guard lock{mutex};
121 return state.buttons.at(button);
122 }
123
124 bool ToggleButton(int button) {
125 std::lock_guard lock{mutex};
126
127 if (!state.toggle_buttons.contains(button) || !state.lock_buttons.contains(button)) {
128 state.toggle_buttons.insert_or_assign(button, false);
129 state.lock_buttons.insert_or_assign(button, false);
130 }
131
132 const bool button_state = state.toggle_buttons.at(button);
133 const bool button_lock = state.lock_buttons.at(button);
134
135 if (button_lock) {
136 return button_state;
137 }
138
139 state.lock_buttons.insert_or_assign(button, true);
140
141 if (button_state) {
142 state.toggle_buttons.insert_or_assign(button, false);
143 } else {
144 state.toggle_buttons.insert_or_assign(button, true);
145 }
146
147 return !button_state;
148 }
149
150 bool UnlockButton(int button) {
151 std::lock_guard lock{mutex};
152 if (!state.toggle_buttons.contains(button)) {
153 return false;
154 }
155 state.lock_buttons.insert_or_assign(button, false);
156 return state.toggle_buttons.at(button);
157 }
158
159 void SetAxis(int axis, Sint16 value) {
160 std::lock_guard lock{mutex};
161 state.axes.insert_or_assign(axis, value);
162 }
163
164 void PreSetAxis(int axis) {
165 if (!state.axes.contains(axis)) {
166 SetAxis(axis, 0);
167 }
168 }
169
170 float GetAxis(int axis, float range, float offset) const {
171 std::lock_guard lock{mutex};
172 const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f;
173 const float offset_scale = (value + offset) > 0.0f ? 1.0f + offset : 1.0f - offset;
174 return (value + offset) / range / offset_scale;
175 }
176
177 bool RumblePlay(u16 amp_low, u16 amp_high) {
178 constexpr u32 rumble_max_duration_ms = 1000;
179
180 if (sdl_controller) {
181 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high,
182 rumble_max_duration_ms) != -1;
183 } else if (sdl_joystick) {
184 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high,
185 rumble_max_duration_ms) != -1;
186 }
187
188 return false;
189 }
190
191 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x,
192 float offset_y) const {
193 float x = GetAxis(axis_x, range, offset_x);
194 float y = GetAxis(axis_y, range, offset_y);
195 y = -y; // 3DS uses an y-axis inverse from SDL
196
197 // Make sure the coordinates are in the unit circle,
198 // otherwise normalize it.
199 float r = x * x + y * y;
200 if (r > 1.0f) {
201 r = std::sqrt(r);
202 x /= r;
203 y /= r;
204 }
205
206 return std::make_tuple(x, y);
207 }
208
209 bool HasGyro() const {
210 return has_gyro;
211 }
212
213 bool HasAccel() const {
214 return has_accel;
215 }
216
217 const MotionInput& GetMotion() const {
218 return motion;
219 }
220
221 void SetHat(int hat, Uint8 direction) {
222 std::lock_guard lock{mutex};
223 state.hats.insert_or_assign(hat, direction);
224 }
225
226 bool GetHatDirection(int hat, Uint8 direction) const {
227 std::lock_guard lock{mutex};
228 return (state.hats.at(hat) & direction) != 0;
229 }
230 /**
231 * The guid of the joystick
232 */
233 const std::string& GetGUID() const {
234 return guid;
235 }
236
237 /**
238 * The number of joystick from the same type that were connected before this joystick
239 */
240 int GetPort() const {
241 return port;
242 }
243
244 SDL_Joystick* GetSDLJoystick() const {
245 return sdl_joystick.get();
246 }
247
248 SDL_GameController* GetSDLGameController() const {
249 return sdl_controller.get();
250 }
251
252 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
253 sdl_joystick.reset(joystick);
254 sdl_controller.reset(controller);
255 }
256
257 bool IsJoyconLeft() const {
258 const std::string controller_name = GetControllerName();
259 if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) {
260 return true;
261 }
262 if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) {
263 return true;
264 }
265 return false;
266 }
267
268 bool IsJoyconRight() const {
269 const std::string controller_name = GetControllerName();
270 if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) {
271 return true;
272 }
273 if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) {
274 return true;
275 }
276 return false;
277 }
278
279 std::string GetControllerName() const {
280 if (sdl_controller) {
281 switch (SDL_GameControllerGetType(sdl_controller.get())) {
282 case SDL_CONTROLLER_TYPE_XBOX360:
283 return "XBox 360 Controller";
284 case SDL_CONTROLLER_TYPE_XBOXONE:
285 return "XBox One Controller";
286 default:
287 break;
288 }
289 const auto name = SDL_GameControllerName(sdl_controller.get());
290 if (name) {
291 return name;
292 }
293 }
294
295 if (sdl_joystick) {
296 const auto name = SDL_JoystickName(sdl_joystick.get());
297 if (name) {
298 return name;
299 }
300 }
301
302 return "Unknown";
303 }
304
305private:
306 struct State {
307 std::unordered_map<int, bool> buttons;
308 std::unordered_map<int, bool> toggle_buttons{};
309 std::unordered_map<int, bool> lock_buttons{};
310 std::unordered_map<int, Sint16> axes;
311 std::unordered_map<int, Uint8> hats;
312 } state;
313 std::string guid;
314 int port;
315 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
316 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
317 mutable std::mutex mutex;
318
319 // Motion is initialized with the PID values
320 MotionInput motion{0.3f, 0.005f, 0.0f};
321 u64 last_motion_update{};
322 bool has_gyro{false};
323 bool has_accel{false};
324};
325
326std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
327 std::lock_guard lock{joystick_map_mutex};
328 const auto it = joystick_map.find(guid);
329
330 if (it != joystick_map.end()) {
331 while (it->second.size() <= static_cast<std::size_t>(port)) {
332 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
333 nullptr, nullptr);
334 it->second.emplace_back(std::move(joystick));
335 }
336
337 return it->second[static_cast<std::size_t>(port)];
338 }
339
340 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
341
342 return joystick_map[guid].emplace_back(std::move(joystick));
343}
344
345std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
346 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
347 const std::string guid = GetGUID(sdl_joystick);
348
349 std::lock_guard lock{joystick_map_mutex};
350 const auto map_it = joystick_map.find(guid);
351
352 if (map_it == joystick_map.end()) {
353 return nullptr;
354 }
355
356 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
357 [&sdl_joystick](const auto& joystick) {
358 return joystick->GetSDLJoystick() == sdl_joystick;
359 });
360
361 if (vec_it == map_it->second.end()) {
362 return nullptr;
363 }
364
365 return *vec_it;
366}
367
368void SDLState::InitJoystick(int joystick_index) {
369 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
370 SDL_GameController* sdl_gamecontroller = nullptr;
371
372 if (SDL_IsGameController(joystick_index)) {
373 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
374 }
375
376 if (!sdl_joystick) {
377 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
378 return;
379 }
380
381 const std::string guid = GetGUID(sdl_joystick);
382
383 std::lock_guard lock{joystick_map_mutex};
384 if (joystick_map.find(guid) == joystick_map.end()) {
385 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
386 joystick_map[guid].emplace_back(std::move(joystick));
387 return;
388 }
389
390 auto& joystick_guid_list = joystick_map[guid];
391 const auto joystick_it =
392 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
393 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
394
395 if (joystick_it != joystick_guid_list.end()) {
396 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
397 return;
398 }
399
400 const int port = static_cast<int>(joystick_guid_list.size());
401 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
402 joystick_guid_list.emplace_back(std::move(joystick));
403}
404
405void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
406 const std::string guid = GetGUID(sdl_joystick);
407
408 std::lock_guard lock{joystick_map_mutex};
409 // This call to guid is safe since the joystick is guaranteed to be in the map
410 const auto& joystick_guid_list = joystick_map[guid];
411 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
412 [&sdl_joystick](const auto& joystick) {
413 return joystick->GetSDLJoystick() == sdl_joystick;
414 });
415
416 if (joystick_it != joystick_guid_list.end()) {
417 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
418 }
419}
420
421void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
422 switch (event.type) {
423 case SDL_JOYBUTTONUP: {
424 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
425 joystick->SetButton(event.jbutton.button, false);
426 }
427 break;
428 }
429 case SDL_JOYBUTTONDOWN: {
430 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
431 joystick->SetButton(event.jbutton.button, true);
432 }
433 break;
434 }
435 case SDL_JOYHATMOTION: {
436 if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
437 joystick->SetHat(event.jhat.hat, event.jhat.value);
438 }
439 break;
440 }
441 case SDL_JOYAXISMOTION: {
442 if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
443 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
444 }
445 break;
446 }
447 case SDL_CONTROLLERSENSORUPDATE: {
448 if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
449 joystick->SetMotion(event.csensor);
450 }
451 break;
452 }
453 case SDL_JOYDEVICEREMOVED:
454 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
455 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
456 break;
457 case SDL_JOYDEVICEADDED:
458 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
459 InitJoystick(event.jdevice.which);
460 break;
461 }
462}
463
464void SDLState::CloseJoysticks() {
465 std::lock_guard lock{joystick_map_mutex};
466 joystick_map.clear();
467}
468
469class SDLButton final : public Input::ButtonDevice {
470public:
471 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_, bool toggle_)
472 : joystick(std::move(joystick_)), button(button_), toggle(toggle_) {}
473
474 bool GetStatus() const override {
475 const bool button_state = joystick->GetButton(button);
476 if (!toggle) {
477 return button_state;
478 }
479
480 if (button_state) {
481 return joystick->ToggleButton(button);
482 }
483 return joystick->UnlockButton(button);
484 }
485
486private:
487 std::shared_ptr<SDLJoystick> joystick;
488 int button;
489 bool toggle;
490};
491
492class SDLDirectionButton final : public Input::ButtonDevice {
493public:
494 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
495 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
496
497 bool GetStatus() const override {
498 return joystick->GetHatDirection(hat, direction);
499 }
500
501private:
502 std::shared_ptr<SDLJoystick> joystick;
503 int hat;
504 Uint8 direction;
505};
506
507class SDLAxisButton final : public Input::ButtonDevice {
508public:
509 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
510 bool trigger_if_greater_)
511 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
512 trigger_if_greater(trigger_if_greater_) {}
513
514 bool GetStatus() const override {
515 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
516 if (trigger_if_greater) {
517 return axis_value > threshold;
518 }
519 return axis_value < threshold;
520 }
521
522private:
523 std::shared_ptr<SDLJoystick> joystick;
524 int axis;
525 float threshold;
526 bool trigger_if_greater;
527};
528
529class SDLAnalog final : public Input::AnalogDevice {
530public:
531 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
532 bool invert_x_, bool invert_y_, float deadzone_, float range_,
533 float offset_x_, float offset_y_)
534 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
535 invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_),
536 offset_y(offset_y_) {}
537
538 std::tuple<float, float> GetStatus() const override {
539 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y);
540 const float r = std::sqrt((x * x) + (y * y));
541 if (invert_x) {
542 x = -x;
543 }
544 if (invert_y) {
545 y = -y;
546 }
547
548 if (r > deadzone) {
549 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
550 y / r * (r - deadzone) / (1 - deadzone));
551 }
552 return {};
553 }
554
555 std::tuple<float, float> GetRawStatus() const override {
556 const float x = joystick->GetAxis(axis_x, range, offset_x);
557 const float y = joystick->GetAxis(axis_y, range, offset_y);
558 return {x, -y};
559 }
560
561 Input::AnalogProperties GetAnalogProperties() const override {
562 return {deadzone, range, 0.5f};
563 }
564
565 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
566 const auto [x, y] = GetStatus();
567 const float directional_deadzone = 0.5f;
568 switch (direction) {
569 case Input::AnalogDirection::RIGHT:
570 return x > directional_deadzone;
571 case Input::AnalogDirection::LEFT:
572 return x < -directional_deadzone;
573 case Input::AnalogDirection::UP:
574 return y > directional_deadzone;
575 case Input::AnalogDirection::DOWN:
576 return y < -directional_deadzone;
577 }
578 return false;
579 }
580
581private:
582 std::shared_ptr<SDLJoystick> joystick;
583 const int axis_x;
584 const int axis_y;
585 const bool invert_x;
586 const bool invert_y;
587 const float deadzone;
588 const float range;
589 const float offset_x;
590 const float offset_y;
591};
592
593class SDLVibration final : public Input::VibrationDevice {
594public:
595 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
596 : joystick(std::move(joystick_)) {}
597
598 u8 GetStatus() const override {
599 joystick->RumblePlay(1, 1);
600 return joystick->RumblePlay(0, 0);
601 }
602
603 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
604 [[maybe_unused]] f32 freq_high) const override {
605 const auto process_amplitude = [](f32 amplitude) {
606 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
607 };
608
609 const auto processed_amp_low = process_amplitude(amp_low);
610 const auto processed_amp_high = process_amplitude(amp_high);
611
612 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
613 }
614
615private:
616 std::shared_ptr<SDLJoystick> joystick;
617};
618
619class SDLMotion final : public Input::MotionDevice {
620public:
621 explicit SDLMotion(std::shared_ptr<SDLJoystick> joystick_) : joystick(std::move(joystick_)) {}
622
623 Input::MotionStatus GetStatus() const override {
624 return joystick->GetMotion().GetMotion();
625 }
626
627private:
628 std::shared_ptr<SDLJoystick> joystick;
629};
630
631class SDLDirectionMotion final : public Input::MotionDevice {
632public:
633 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
634 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
635
636 Input::MotionStatus GetStatus() const override {
637 if (joystick->GetHatDirection(hat, direction)) {
638 return joystick->GetMotion().GetRandomMotion(2, 6);
639 }
640 return joystick->GetMotion().GetRandomMotion(0, 0);
641 }
642
643private:
644 std::shared_ptr<SDLJoystick> joystick;
645 int hat;
646 Uint8 direction;
647};
648
649class SDLAxisMotion final : public Input::MotionDevice {
650public:
651 explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
652 bool trigger_if_greater_)
653 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
654 trigger_if_greater(trigger_if_greater_) {}
655
656 Input::MotionStatus GetStatus() const override {
657 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
658 bool trigger = axis_value < threshold;
659 if (trigger_if_greater) {
660 trigger = axis_value > threshold;
661 }
662
663 if (trigger) {
664 return joystick->GetMotion().GetRandomMotion(2, 6);
665 }
666 return joystick->GetMotion().GetRandomMotion(0, 0);
667 }
668
669private:
670 std::shared_ptr<SDLJoystick> joystick;
671 int axis;
672 float threshold;
673 bool trigger_if_greater;
674};
675
676class SDLButtonMotion final : public Input::MotionDevice {
677public:
678 explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
679 : joystick(std::move(joystick_)), button(button_) {}
680
681 Input::MotionStatus GetStatus() const override {
682 if (joystick->GetButton(button)) {
683 return joystick->GetMotion().GetRandomMotion(2, 6);
684 }
685 return joystick->GetMotion().GetRandomMotion(0, 0);
686 }
687
688private:
689 std::shared_ptr<SDLJoystick> joystick;
690 int button;
691};
692
693/// A button device factory that creates button devices from SDL joystick
694class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
695public:
696 explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
697
698 /**
699 * Creates a button device from a joystick button
700 * @param params contains parameters for creating the device:
701 * - "guid": the guid of the joystick to bind
702 * - "port": the nth joystick of the same type to bind
703 * - "button"(optional): the index of the button to bind
704 * - "hat"(optional): the index of the hat to bind as direction buttons
705 * - "axis"(optional): the index of the axis to bind
706 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
707 * "down", "left" or "right"
708 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
709 * triggered if the axis value crosses
710 * - "direction"(only used for axis): "+" means the button is triggered when the axis
711 * value is greater than the threshold; "-" means the button is triggered when the axis
712 * value is smaller than the threshold
713 */
714 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
715 const std::string guid = params.Get("guid", "0");
716 const int port = params.Get("port", 0);
717 const auto toggle = params.Get("toggle", false);
718
719 auto joystick = state.GetSDLJoystickByGUID(guid, port);
720
721 if (params.Has("hat")) {
722 const int hat = params.Get("hat", 0);
723 const std::string direction_name = params.Get("direction", "");
724 Uint8 direction;
725 if (direction_name == "up") {
726 direction = SDL_HAT_UP;
727 } else if (direction_name == "down") {
728 direction = SDL_HAT_DOWN;
729 } else if (direction_name == "left") {
730 direction = SDL_HAT_LEFT;
731 } else if (direction_name == "right") {
732 direction = SDL_HAT_RIGHT;
733 } else {
734 direction = 0;
735 }
736 // This is necessary so accessing GetHat with hat won't crash
737 joystick->SetHat(hat, SDL_HAT_CENTERED);
738 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
739 }
740
741 if (params.Has("axis")) {
742 const int axis = params.Get("axis", 0);
743 // Convert range from (0.0, 1.0) to (-1.0, 1.0)
744 const float threshold = (params.Get("threshold", 0.5f) - 0.5f) * 2.0f;
745 const std::string direction_name = params.Get("direction", "");
746 bool trigger_if_greater;
747 if (direction_name == "+") {
748 trigger_if_greater = true;
749 } else if (direction_name == "-") {
750 trigger_if_greater = false;
751 } else {
752 trigger_if_greater = true;
753 LOG_ERROR(Input, "Unknown direction {}", direction_name);
754 }
755 // This is necessary so accessing GetAxis with axis won't crash
756 joystick->PreSetAxis(axis);
757 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
758 }
759
760 const int button = params.Get("button", 0);
761 // This is necessary so accessing GetButton with button won't crash
762 joystick->PreSetButton(button);
763 return std::make_unique<SDLButton>(joystick, button, toggle);
764 }
765
766private:
767 SDLState& state;
768};
769
770/// An analog device factory that creates analog devices from SDL joystick
771class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
772public:
773 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
774 /**
775 * Creates an analog device from joystick axes
776 * @param params contains parameters for creating the device:
777 * - "guid": the guid of the joystick to bind
778 * - "port": the nth joystick of the same type
779 * - "axis_x": the index of the axis to be bind as x-axis
780 * - "axis_y": the index of the axis to be bind as y-axis
781 */
782 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
783 const std::string guid = params.Get("guid", "0");
784 const int port = params.Get("port", 0);
785 const int axis_x = params.Get("axis_x", 0);
786 const int axis_y = params.Get("axis_y", 1);
787 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
788 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
789 const std::string invert_x_value = params.Get("invert_x", "+");
790 const std::string invert_y_value = params.Get("invert_y", "+");
791 const bool invert_x = invert_x_value == "-";
792 const bool invert_y = invert_y_value == "-";
793 const float offset_x = std::clamp(params.Get("offset_x", 0.0f), -0.99f, 0.99f);
794 const float offset_y = std::clamp(params.Get("offset_y", 0.0f), -0.99f, 0.99f);
795 auto joystick = state.GetSDLJoystickByGUID(guid, port);
796
797 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
798 joystick->PreSetAxis(axis_x);
799 joystick->PreSetAxis(axis_y);
800 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
801 range, offset_x, offset_y);
802 }
803
804private:
805 SDLState& state;
806};
807
808/// An vibration device factory that creates vibration devices from SDL joystick
809class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
810public:
811 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
812 /**
813 * Creates a vibration device from a joystick
814 * @param params contains parameters for creating the device:
815 * - "guid": the guid of the joystick to bind
816 * - "port": the nth joystick of the same type
817 */
818 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
819 const std::string guid = params.Get("guid", "0");
820 const int port = params.Get("port", 0);
821 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
822 }
823
824private:
825 SDLState& state;
826};
827
828/// A motion device factory that creates motion devices from SDL joystick
829class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
830public:
831 explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
832 /**
833 * Creates motion device from joystick axes
834 * @param params contains parameters for creating the device:
835 * - "guid": the guid of the joystick to bind
836 * - "port": the nth joystick of the same type
837 */
838 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
839 const std::string guid = params.Get("guid", "0");
840 const int port = params.Get("port", 0);
841
842 auto joystick = state.GetSDLJoystickByGUID(guid, port);
843
844 if (params.Has("motion")) {
845 return std::make_unique<SDLMotion>(joystick);
846 }
847
848 if (params.Has("hat")) {
849 const int hat = params.Get("hat", 0);
850 const std::string direction_name = params.Get("direction", "");
851 Uint8 direction;
852 if (direction_name == "up") {
853 direction = SDL_HAT_UP;
854 } else if (direction_name == "down") {
855 direction = SDL_HAT_DOWN;
856 } else if (direction_name == "left") {
857 direction = SDL_HAT_LEFT;
858 } else if (direction_name == "right") {
859 direction = SDL_HAT_RIGHT;
860 } else {
861 direction = 0;
862 }
863 // This is necessary so accessing GetHat with hat won't crash
864 joystick->SetHat(hat, SDL_HAT_CENTERED);
865 return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
866 }
867
868 if (params.Has("axis")) {
869 const int axis = params.Get("axis", 0);
870 const float threshold = params.Get("threshold", 0.5f);
871 const std::string direction_name = params.Get("direction", "");
872 bool trigger_if_greater;
873 if (direction_name == "+") {
874 trigger_if_greater = true;
875 } else if (direction_name == "-") {
876 trigger_if_greater = false;
877 } else {
878 trigger_if_greater = true;
879 LOG_ERROR(Input, "Unknown direction {}", direction_name);
880 }
881 // This is necessary so accessing GetAxis with axis won't crash
882 joystick->PreSetAxis(axis);
883 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
884 }
885
886 const int button = params.Get("button", 0);
887 // This is necessary so accessing GetButton with button won't crash
888 joystick->PreSetButton(button);
889 return std::make_unique<SDLButtonMotion>(joystick, button);
890 }
891
892private:
893 SDLState& state;
894};
895
896SDLState::SDLState() {
897 using namespace Input;
898 button_factory = std::make_shared<SDLButtonFactory>(*this);
899 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
900 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
901 motion_factory = std::make_shared<SDLMotionFactory>(*this);
902 RegisterFactory<ButtonDevice>("sdl", button_factory);
903 RegisterFactory<AnalogDevice>("sdl", analog_factory);
904 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
905 RegisterFactory<MotionDevice>("sdl", motion_factory);
906
907 if (!Settings::values.enable_raw_input) {
908 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
909 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
910 }
911
912 // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
913 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
914 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
915
916 // Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
917 // GameController and not a generic one
918 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
919
920 // Turn off Pro controller home led
921 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
922
923 // If the frontend is going to manage the event loop, then we don't start one here
924 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
925 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
926 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
927 return;
928 }
929 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
930 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
931 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
932 }
933
934 SDL_AddEventWatch(&SDLEventWatcher, this);
935
936 initialized = true;
937 if (start_thread) {
938 poll_thread = std::thread([this] {
939 using namespace std::chrono_literals;
940 while (initialized) {
941 SDL_PumpEvents();
942 std::this_thread::sleep_for(1ms);
943 }
944 });
945 }
946 // Because the events for joystick connection happens before we have our event watcher added, we
947 // can just open all the joysticks right here
948 for (int i = 0; i < SDL_NumJoysticks(); ++i) {
949 InitJoystick(i);
950 }
951}
952
953SDLState::~SDLState() {
954 using namespace Input;
955 UnregisterFactory<ButtonDevice>("sdl");
956 UnregisterFactory<AnalogDevice>("sdl");
957 UnregisterFactory<VibrationDevice>("sdl");
958 UnregisterFactory<MotionDevice>("sdl");
959
960 CloseJoysticks();
961 SDL_DelEventWatch(&SDLEventWatcher, this);
962
963 initialized = false;
964 if (start_thread) {
965 poll_thread.join();
966 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
967 }
968}
969
970std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
971 std::scoped_lock lock(joystick_map_mutex);
972 std::vector<Common::ParamPackage> devices;
973 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
974 for (const auto& [key, value] : joystick_map) {
975 for (const auto& joystick : value) {
976 if (!joystick->GetSDLJoystick()) {
977 continue;
978 }
979 std::string name =
980 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
981 devices.emplace_back(Common::ParamPackage{
982 {"class", "sdl"},
983 {"display", std::move(name)},
984 {"guid", joystick->GetGUID()},
985 {"port", std::to_string(joystick->GetPort())},
986 });
987 if (joystick->IsJoyconLeft()) {
988 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
989 }
990 }
991 }
992
993 // Add dual controllers
994 for (const auto& [key, value] : joystick_map) {
995 for (const auto& joystick : value) {
996 if (joystick->IsJoyconRight()) {
997 if (!joycon_pairs.contains(joystick->GetPort())) {
998 continue;
999 }
1000 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
1001
1002 std::string name =
1003 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
1004 devices.emplace_back(Common::ParamPackage{
1005 {"class", "sdl"},
1006 {"display", std::move(name)},
1007 {"guid", joystick->GetGUID()},
1008 {"guid2", joystick2->GetGUID()},
1009 {"port", std::to_string(joystick->GetPort())},
1010 });
1011 }
1012 }
1013 }
1014 return devices;
1015}
1016
1017namespace {
1018Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
1019 float value = 0.1f) {
1020 Common::ParamPackage params({{"engine", "sdl"}});
1021 params.Set("port", port);
1022 params.Set("guid", std::move(guid));
1023 params.Set("axis", axis);
1024 params.Set("threshold", "0.5");
1025 if (value > 0) {
1026 params.Set("direction", "+");
1027 } else {
1028 params.Set("direction", "-");
1029 }
1030 return params;
1031}
1032
1033Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
1034 Common::ParamPackage params({{"engine", "sdl"}});
1035 params.Set("port", port);
1036 params.Set("guid", std::move(guid));
1037 params.Set("button", button);
1038 params.Set("toggle", false);
1039 return params;
1040}
1041
1042Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
1043 Common::ParamPackage params({{"engine", "sdl"}});
1044
1045 params.Set("port", port);
1046 params.Set("guid", std::move(guid));
1047 params.Set("hat", hat);
1048 switch (value) {
1049 case SDL_HAT_UP:
1050 params.Set("direction", "up");
1051 break;
1052 case SDL_HAT_DOWN:
1053 params.Set("direction", "down");
1054 break;
1055 case SDL_HAT_LEFT:
1056 params.Set("direction", "left");
1057 break;
1058 case SDL_HAT_RIGHT:
1059 params.Set("direction", "right");
1060 break;
1061 default:
1062 return {};
1063 }
1064 return params;
1065}
1066
1067Common::ParamPackage BuildMotionParam(int port, std::string guid) {
1068 Common::ParamPackage params({{"engine", "sdl"}, {"motion", "0"}});
1069 params.Set("port", port);
1070 params.Set("guid", std::move(guid));
1071 return params;
1072}
1073
1074Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
1075 switch (event.type) {
1076 case SDL_JOYAXISMOTION: {
1077 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1078 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1079 static_cast<s32>(event.jaxis.axis),
1080 event.jaxis.value);
1081 }
1082 break;
1083 }
1084 case SDL_JOYBUTTONUP: {
1085 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
1086 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1087 static_cast<s32>(event.jbutton.button));
1088 }
1089 break;
1090 }
1091 case SDL_JOYHATMOTION: {
1092 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
1093 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1094 static_cast<s32>(event.jhat.hat),
1095 static_cast<s32>(event.jhat.value));
1096 }
1097 break;
1098 }
1099 }
1100 return {};
1101}
1102
1103Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
1104 switch (event.type) {
1105 case SDL_JOYAXISMOTION: {
1106 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1107 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1108 static_cast<s32>(event.jaxis.axis),
1109 event.jaxis.value);
1110 }
1111 break;
1112 }
1113 case SDL_JOYBUTTONUP: {
1114 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
1115 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1116 static_cast<s32>(event.jbutton.button));
1117 }
1118 break;
1119 }
1120 case SDL_JOYHATMOTION: {
1121 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
1122 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1123 static_cast<s32>(event.jhat.hat),
1124 static_cast<s32>(event.jhat.value));
1125 }
1126 break;
1127 }
1128 case SDL_CONTROLLERSENSORUPDATE: {
1129 bool is_motion_shaking = false;
1130 constexpr float gyro_threshold = 5.0f;
1131 constexpr float accel_threshold = 11.0f;
1132 if (event.csensor.sensor == SDL_SENSOR_ACCEL) {
1133 const Common::Vec3f acceleration = {-event.csensor.data[0], event.csensor.data[2],
1134 -event.csensor.data[1]};
1135 if (acceleration.Length() > accel_threshold) {
1136 is_motion_shaking = true;
1137 }
1138 }
1139
1140 if (event.csensor.sensor == SDL_SENSOR_GYRO) {
1141 const Common::Vec3f gyroscope = {event.csensor.data[0], -event.csensor.data[2],
1142 event.csensor.data[1]};
1143 if (gyroscope.Length() > gyro_threshold) {
1144 is_motion_shaking = true;
1145 }
1146 }
1147
1148 if (!is_motion_shaking) {
1149 break;
1150 }
1151
1152 if (const auto joystick = state.GetSDLJoystickBySDLID(event.csensor.which)) {
1153 return BuildMotionParam(joystick->GetPort(), joystick->GetGUID());
1154 }
1155 break;
1156 }
1157 }
1158 return {};
1159}
1160
1161Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
1162 const SDL_GameControllerButtonBind& binding) {
1163 switch (binding.bindType) {
1164 case SDL_CONTROLLER_BINDTYPE_NONE:
1165 break;
1166 case SDL_CONTROLLER_BINDTYPE_AXIS:
1167 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
1168 case SDL_CONTROLLER_BINDTYPE_BUTTON:
1169 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
1170 case SDL_CONTROLLER_BINDTYPE_HAT:
1171 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
1172 binding.value.hat.hat_mask);
1173 }
1174 return {};
1175}
1176
1177Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
1178 int axis_y, float offset_x, float offset_y) {
1179 Common::ParamPackage params;
1180 params.Set("engine", "sdl");
1181 params.Set("port", port);
1182 params.Set("guid", guid);
1183 params.Set("axis_x", axis_x);
1184 params.Set("axis_y", axis_y);
1185 params.Set("offset_x", offset_x);
1186 params.Set("offset_y", offset_y);
1187 params.Set("invert_x", "+");
1188 params.Set("invert_y", "+");
1189 return params;
1190}
1191} // Anonymous namespace
1192
1193ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
1194 if (!params.Has("guid") || !params.Has("port")) {
1195 return {};
1196 }
1197 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1198
1199 auto* controller = joystick->GetSDLGameController();
1200 if (controller == nullptr) {
1201 return {};
1202 }
1203
1204 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
1205 // We will add those afterwards
1206 // This list also excludes Screenshot since theres not really a mapping for that
1207 ButtonBindings switch_to_sdl_button;
1208
1209 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
1210 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
1211 } else {
1212 switch_to_sdl_button = GetDefaultButtonBinding();
1213 }
1214
1215 // Add the missing bindings for ZL/ZR
1216 static constexpr ZButtonBindings switch_to_sdl_axis{{
1217 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
1218 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
1219 }};
1220
1221 // Parameters contain two joysticks return dual
1222 if (params.Has("guid2")) {
1223 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1224
1225 if (joystick2->GetSDLGameController() != nullptr) {
1226 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
1227 switch_to_sdl_axis);
1228 }
1229 }
1230
1231 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
1232}
1233
1234ButtonBindings SDLState::GetDefaultButtonBinding() const {
1235 return {
1236 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
1237 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
1238 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
1239 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
1240 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1241 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1242 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1243 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1244 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1245 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1246 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1247 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1248 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1249 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1250 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1251 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1252 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1253 };
1254}
1255
1256ButtonBindings SDLState::GetNintendoButtonBinding(
1257 const std::shared_ptr<SDLJoystick>& joystick) const {
1258 // Default SL/SR mapping for pro controllers
1259 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
1260 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
1261
1262 if (joystick->IsJoyconLeft()) {
1263 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
1264 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
1265 }
1266 if (joystick->IsJoyconRight()) {
1267 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
1268 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
1269 }
1270
1271 return {
1272 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
1273 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
1274 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
1275 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
1276 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1277 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1278 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1279 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1280 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1281 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1282 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1283 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1284 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1285 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1286 {Settings::NativeButton::SL, sl_button},
1287 {Settings::NativeButton::SR, sr_button},
1288 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1289 };
1290}
1291
1292ButtonMapping SDLState::GetSingleControllerMapping(
1293 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
1294 const ZButtonBindings& switch_to_sdl_axis) const {
1295 ButtonMapping mapping;
1296 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1297 auto* controller = joystick->GetSDLGameController();
1298
1299 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1300 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1301 mapping.insert_or_assign(
1302 switch_button,
1303 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1304 }
1305 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1306 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1307 mapping.insert_or_assign(
1308 switch_button,
1309 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1310 }
1311
1312 return mapping;
1313}
1314
1315ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
1316 const std::shared_ptr<SDLJoystick>& joystick2,
1317 const ButtonBindings& switch_to_sdl_button,
1318 const ZButtonBindings& switch_to_sdl_axis) const {
1319 ButtonMapping mapping;
1320 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1321 auto* controller = joystick->GetSDLGameController();
1322 auto* controller2 = joystick2->GetSDLGameController();
1323
1324 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1325 if (IsButtonOnLeftSide(switch_button)) {
1326 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
1327 mapping.insert_or_assign(
1328 switch_button,
1329 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1330 continue;
1331 }
1332 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1333 mapping.insert_or_assign(
1334 switch_button,
1335 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1336 }
1337 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1338 if (IsButtonOnLeftSide(switch_button)) {
1339 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
1340 mapping.insert_or_assign(
1341 switch_button,
1342 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1343 continue;
1344 }
1345 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1346 mapping.insert_or_assign(
1347 switch_button,
1348 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1349 }
1350
1351 return mapping;
1352}
1353
1354bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
1355 switch (button) {
1356 case Settings::NativeButton::DDown:
1357 case Settings::NativeButton::DLeft:
1358 case Settings::NativeButton::DRight:
1359 case Settings::NativeButton::DUp:
1360 case Settings::NativeButton::L:
1361 case Settings::NativeButton::LStick:
1362 case Settings::NativeButton::Minus:
1363 case Settings::NativeButton::Screenshot:
1364 case Settings::NativeButton::ZL:
1365 return true;
1366 default:
1367 return false;
1368 }
1369}
1370
1371AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
1372 if (!params.Has("guid") || !params.Has("port")) {
1373 return {};
1374 }
1375 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1376 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1377 auto* controller = joystick->GetSDLGameController();
1378 if (controller == nullptr) {
1379 return {};
1380 }
1381
1382 AnalogMapping mapping = {};
1383 const auto& binding_left_x =
1384 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
1385 const auto& binding_left_y =
1386 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1387 if (params.Has("guid2")) {
1388 joystick2->PreSetAxis(binding_left_x.value.axis);
1389 joystick2->PreSetAxis(binding_left_y.value.axis);
1390 const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1391 const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1392 mapping.insert_or_assign(
1393 Settings::NativeAnalog::LStick,
1394 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1395 binding_left_x.value.axis, binding_left_y.value.axis,
1396 left_offset_x, left_offset_y));
1397 } else {
1398 joystick->PreSetAxis(binding_left_x.value.axis);
1399 joystick->PreSetAxis(binding_left_y.value.axis);
1400 const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1401 const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1402 mapping.insert_or_assign(
1403 Settings::NativeAnalog::LStick,
1404 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1405 binding_left_x.value.axis, binding_left_y.value.axis,
1406 left_offset_x, left_offset_y));
1407 }
1408 const auto& binding_right_x =
1409 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1410 const auto& binding_right_y =
1411 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
1412 joystick->PreSetAxis(binding_right_x.value.axis);
1413 joystick->PreSetAxis(binding_right_y.value.axis);
1414 const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
1415 const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
1416 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
1417 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1418 binding_right_x.value.axis,
1419 binding_right_y.value.axis, right_offset_x,
1420 right_offset_y));
1421 return mapping;
1422}
1423
1424MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& params) {
1425 if (!params.Has("guid") || !params.Has("port")) {
1426 return {};
1427 }
1428 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1429 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1430 auto* controller = joystick->GetSDLGameController();
1431 if (controller == nullptr) {
1432 return {};
1433 }
1434
1435 MotionMapping mapping = {};
1436 joystick->EnableMotion();
1437
1438 if (joystick->HasGyro() || joystick->HasAccel()) {
1439 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
1440 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1441 }
1442 if (params.Has("guid2")) {
1443 joystick2->EnableMotion();
1444 if (joystick2->HasGyro() || joystick2->HasAccel()) {
1445 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1446 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
1447 }
1448 } else {
1449 if (joystick->HasGyro() || joystick->HasAccel()) {
1450 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1451 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1452 }
1453 }
1454
1455 return mapping;
1456}
1457namespace Polling {
1458class SDLPoller : public InputCommon::Polling::DevicePoller {
1459public:
1460 explicit SDLPoller(SDLState& state_) : state(state_) {}
1461
1462 void Start([[maybe_unused]] const std::string& device_id) override {
1463 state.event_queue.Clear();
1464 state.polling = true;
1465 }
1466
1467 void Stop() override {
1468 state.polling = false;
1469 }
1470
1471protected:
1472 SDLState& state;
1473};
1474
1475class SDLButtonPoller final : public SDLPoller {
1476public:
1477 explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
1478
1479 Common::ParamPackage GetNextInput() override {
1480 SDL_Event event;
1481 while (state.event_queue.Pop(event)) {
1482 const auto package = FromEvent(event);
1483 if (package) {
1484 return *package;
1485 }
1486 }
1487 return {};
1488 }
1489 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
1490 switch (event.type) {
1491 case SDL_JOYAXISMOTION:
1492 if (!axis_memory.count(event.jaxis.which) ||
1493 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
1494 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
1495 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
1496 break;
1497 } else {
1498 axis_event_count[event.jaxis.which][event.jaxis.axis]++;
1499 // The joystick and axis exist in our map if we take this branch, so no checks
1500 // needed
1501 if (std::abs(
1502 (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
1503 32767.0) < 0.5) {
1504 break;
1505 } else {
1506 if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
1507 IsAxisAtPole(event.jaxis.value) &&
1508 IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
1509 // If we have exactly two events and both are near a pole, this is
1510 // likely a digital input masquerading as an analog axis; Instead of
1511 // trying to look at the direction the axis travelled, assume the first
1512 // event was press and the second was release; This should handle most
1513 // digital axes while deferring to the direction of travel for analog
1514 // axes
1515 event.jaxis.value = static_cast<Sint16>(
1516 std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
1517 } else {
1518 // There are more than two events, so this is likely a true analog axis,
1519 // check the direction it travelled
1520 event.jaxis.value = static_cast<Sint16>(std::copysign(
1521 32767,
1522 event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
1523 }
1524 axis_memory.clear();
1525 axis_event_count.clear();
1526 }
1527 }
1528 [[fallthrough]];
1529 case SDL_JOYBUTTONUP:
1530 case SDL_JOYHATMOTION:
1531 return {SDLEventToButtonParamPackage(state, event)};
1532 }
1533 return std::nullopt;
1534 }
1535
1536private:
1537 // Determine whether an axis value is close to an extreme or center
1538 // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
1539 // axis, which is why the center must be considered a pole
1540 bool IsAxisAtPole(int16_t value) const {
1541 return std::abs(value) >= 32767 || std::abs(value) < 327;
1542 }
1543 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
1544 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
1545};
1546
1547class SDLMotionPoller final : public SDLPoller {
1548public:
1549 explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
1550
1551 Common::ParamPackage GetNextInput() override {
1552 SDL_Event event;
1553 while (state.event_queue.Pop(event)) {
1554 const auto package = FromEvent(event);
1555 if (package) {
1556 return *package;
1557 }
1558 }
1559 return {};
1560 }
1561 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
1562 switch (event.type) {
1563 case SDL_JOYAXISMOTION:
1564 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
1565 break;
1566 }
1567 [[fallthrough]];
1568 case SDL_JOYBUTTONUP:
1569 case SDL_JOYHATMOTION:
1570 case SDL_CONTROLLERSENSORUPDATE:
1571 return {SDLEventToMotionParamPackage(state, event)};
1572 }
1573 return std::nullopt;
1574 }
1575};
1576
1577/**
1578 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
1579 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
1580 * instead
1581 */
1582class SDLAnalogPreferredPoller final : public SDLPoller {
1583public:
1584 explicit SDLAnalogPreferredPoller(SDLState& state_)
1585 : SDLPoller(state_), button_poller(state_) {}
1586
1587 void Start(const std::string& device_id) override {
1588 SDLPoller::Start(device_id);
1589 // Reset stored axes
1590 first_axis = -1;
1591 }
1592
1593 Common::ParamPackage GetNextInput() override {
1594 SDL_Event event;
1595 while (state.event_queue.Pop(event)) {
1596 if (event.type != SDL_JOYAXISMOTION) {
1597 // Check for a button press
1598 auto button_press = button_poller.FromEvent(event);
1599 if (button_press) {
1600 return *button_press;
1601 }
1602 continue;
1603 }
1604 const auto axis = event.jaxis.axis;
1605
1606 // Filter out axis events that are below a threshold
1607 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
1608 continue;
1609 }
1610
1611 // Filter out axis events that are the same
1612 if (first_axis == axis) {
1613 continue;
1614 }
1615
1616 // In order to return a complete analog param, we need inputs for both axes.
1617 // If the first axis isn't set we set the value then wait till next event
1618 if (first_axis == -1) {
1619 first_axis = axis;
1620 continue;
1621 }
1622
1623 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1624 // Set offset to zero since the joystick is not on center
1625 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1626 first_axis, axis, 0, 0);
1627 first_axis = -1;
1628 return params;
1629 }
1630 }
1631 return {};
1632 }
1633
1634private:
1635 int first_axis = -1;
1636 SDLButtonPoller button_poller;
1637};
1638} // namespace Polling
1639
1640SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
1641 Pollers pollers;
1642
1643 switch (type) {
1644 case InputCommon::Polling::DeviceType::AnalogPreferred:
1645 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
1646 break;
1647 case InputCommon::Polling::DeviceType::Button:
1648 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
1649 break;
1650 case InputCommon::Polling::DeviceType::Motion:
1651 pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
1652 break;
1653 }
1654
1655 return pollers;
1656}
1657
1658} // namespace InputCommon::SDL
diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp
deleted file mode 100644
index 1598092b6..000000000
--- a/src/input_common/tas/tas_input.cpp
+++ /dev/null
@@ -1,455 +0,0 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <regex>
7
8#include "common/fs/file.h"
9#include "common/fs/fs_types.h"
10#include "common/fs/path_util.h"
11#include "common/logging/log.h"
12#include "common/settings.h"
13#include "input_common/tas/tas_input.h"
14
15namespace TasInput {
16
17// Supported keywords and buttons from a TAS file
18constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
19 std::pair{"KEY_A", TasButton::BUTTON_A},
20 {"KEY_B", TasButton::BUTTON_B},
21 {"KEY_X", TasButton::BUTTON_X},
22 {"KEY_Y", TasButton::BUTTON_Y},
23 {"KEY_LSTICK", TasButton::STICK_L},
24 {"KEY_RSTICK", TasButton::STICK_R},
25 {"KEY_L", TasButton::TRIGGER_L},
26 {"KEY_R", TasButton::TRIGGER_R},
27 {"KEY_PLUS", TasButton::BUTTON_PLUS},
28 {"KEY_MINUS", TasButton::BUTTON_MINUS},
29 {"KEY_DLEFT", TasButton::BUTTON_LEFT},
30 {"KEY_DUP", TasButton::BUTTON_UP},
31 {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
32 {"KEY_DDOWN", TasButton::BUTTON_DOWN},
33 {"KEY_SL", TasButton::BUTTON_SL},
34 {"KEY_SR", TasButton::BUTTON_SR},
35 {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
36 {"KEY_HOME", TasButton::BUTTON_HOME},
37 {"KEY_ZL", TasButton::TRIGGER_ZL},
38 {"KEY_ZR", TasButton::TRIGGER_ZR},
39};
40
41Tas::Tas() {
42 if (!Settings::values.tas_enable) {
43 needs_reset = true;
44 return;
45 }
46 LoadTasFiles();
47}
48
49Tas::~Tas() {
50 Stop();
51};
52
53void Tas::LoadTasFiles() {
54 script_length = 0;
55 for (size_t i = 0; i < commands.size(); i++) {
56 LoadTasFile(i);
57 if (commands[i].size() > script_length) {
58 script_length = commands[i].size();
59 }
60 }
61}
62
63void Tas::LoadTasFile(size_t player_index) {
64 if (!commands[player_index].empty()) {
65 commands[player_index].clear();
66 }
67 std::string file =
68 Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
69 fmt::format("script0-{}.txt", player_index + 1),
70 Common::FS::FileType::BinaryFile);
71 std::stringstream command_line(file);
72 std::string line;
73 int frame_no = 0;
74 while (std::getline(command_line, line, '\n')) {
75 if (line.empty()) {
76 continue;
77 }
78 LOG_DEBUG(Input, "Loading line: {}", line);
79 std::smatch m;
80
81 std::stringstream linestream(line);
82 std::string segment;
83 std::vector<std::string> seglist;
84
85 while (std::getline(linestream, segment, ' ')) {
86 seglist.push_back(segment);
87 }
88
89 if (seglist.size() < 4) {
90 continue;
91 }
92
93 while (frame_no < std::stoi(seglist.at(0))) {
94 commands[player_index].push_back({});
95 frame_no++;
96 }
97
98 TASCommand command = {
99 .buttons = ReadCommandButtons(seglist.at(1)),
100 .l_axis = ReadCommandAxis(seglist.at(2)),
101 .r_axis = ReadCommandAxis(seglist.at(3)),
102 };
103 commands[player_index].push_back(command);
104 frame_no++;
105 }
106 LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
107}
108
109void Tas::WriteTasFile(std::u8string file_name) {
110 std::string output_text;
111 for (size_t frame = 0; frame < record_commands.size(); frame++) {
112 if (!output_text.empty()) {
113 output_text += "\n";
114 }
115 const TASCommand& line = record_commands[frame];
116 output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " +
117 WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis);
118 }
119 const auto bytes_written = Common::FS::WriteStringToFile(
120 Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
121 Common::FS::FileType::TextFile, output_text);
122 if (bytes_written == output_text.size()) {
123 LOG_INFO(Input, "TAS file written to file!");
124 } else {
125 LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
126 output_text.size());
127 }
128}
129
130std::pair<float, float> Tas::FlipAxisY(std::pair<float, float> old) {
131 auto [x, y] = old;
132 return {x, -y};
133}
134
135void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) {
136 last_input = {buttons, FlipAxisY(axes[0]), FlipAxisY(axes[1])};
137}
138
139std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
140 TasState state;
141 if (is_recording) {
142 return {TasState::Recording, 0, record_commands.size()};
143 }
144
145 if (is_running) {
146 state = TasState::Running;
147 } else {
148 state = TasState::Stopped;
149 }
150
151 return {state, current_command, script_length};
152}
153
154std::string Tas::DebugButtons(u32 buttons) const {
155 return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons));
156}
157
158std::string Tas::DebugJoystick(float x, float y) const {
159 return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y));
160}
161
162std::string Tas::DebugInput(const TasData& data) const {
163 return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons),
164 DebugJoystick(data.axis[0], data.axis[1]),
165 DebugJoystick(data.axis[2], data.axis[3]));
166}
167
168std::string Tas::DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const {
169 std::string returns = "[ ";
170 for (size_t i = 0; i < arr.size(); i++) {
171 returns += DebugInput(arr[i]);
172 if (i != arr.size() - 1) {
173 returns += " , ";
174 }
175 }
176 return returns + "]";
177}
178
179std::string Tas::ButtonsToString(u32 button) const {
180 std::string returns;
181 for (auto [text_button, tas_button] : text_to_tas_button) {
182 if ((button & static_cast<u32>(tas_button)) != 0)
183 returns += fmt::format(", {}", text_button.substr(4));
184 }
185 return returns.empty() ? "" : returns.substr(2);
186}
187
188void Tas::UpdateThread() {
189 if (!Settings::values.tas_enable) {
190 if (is_running) {
191 Stop();
192 }
193 return;
194 }
195
196 if (is_recording) {
197 record_commands.push_back(last_input);
198 }
199 if (needs_reset) {
200 current_command = 0;
201 needs_reset = false;
202 LoadTasFiles();
203 LOG_DEBUG(Input, "tas_reset done");
204 }
205
206 if (!is_running) {
207 tas_data.fill({});
208 return;
209 }
210 if (current_command < script_length) {
211 LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
212 size_t frame = current_command++;
213 for (size_t i = 0; i < commands.size(); i++) {
214 if (frame < commands[i].size()) {
215 TASCommand command = commands[i][frame];
216 tas_data[i].buttons = command.buttons;
217 auto [l_axis_x, l_axis_y] = command.l_axis;
218 tas_data[i].axis[0] = l_axis_x;
219 tas_data[i].axis[1] = l_axis_y;
220 auto [r_axis_x, r_axis_y] = command.r_axis;
221 tas_data[i].axis[2] = r_axis_x;
222 tas_data[i].axis[3] = r_axis_y;
223 } else {
224 tas_data[i] = {};
225 }
226 }
227 } else {
228 is_running = Settings::values.tas_loop.GetValue();
229 current_command = 0;
230 tas_data.fill({});
231 if (!is_running) {
232 SwapToStoredController();
233 }
234 }
235 LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
236}
237
238TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
239 std::stringstream linestream(line);
240 std::string segment;
241 std::vector<std::string> seglist;
242
243 while (std::getline(linestream, segment, ';')) {
244 seglist.push_back(segment);
245 }
246
247 const float x = std::stof(seglist.at(0)) / 32767.0f;
248 const float y = std::stof(seglist.at(1)) / 32767.0f;
249
250 return {x, y};
251}
252
253u32 Tas::ReadCommandButtons(const std::string& data) const {
254 std::stringstream button_text(data);
255 std::string line;
256 u32 buttons = 0;
257 while (std::getline(button_text, line, ';')) {
258 for (auto [text, tas_button] : text_to_tas_button) {
259 if (text == line) {
260 buttons |= static_cast<u32>(tas_button);
261 break;
262 }
263 }
264 }
265 return buttons;
266}
267
268std::string Tas::WriteCommandAxis(TasAnalog data) const {
269 auto [x, y] = data;
270 std::string line;
271 line += std::to_string(static_cast<int>(x * 32767));
272 line += ";";
273 line += std::to_string(static_cast<int>(y * 32767));
274 return line;
275}
276
277std::string Tas::WriteCommandButtons(u32 data) const {
278 if (data == 0) {
279 return "NONE";
280 }
281
282 std::string line;
283 u32 index = 0;
284 while (data > 0) {
285 if ((data & 1) == 1) {
286 for (auto [text, tas_button] : text_to_tas_button) {
287 if (tas_button == static_cast<TasButton>(1 << index)) {
288 if (line.size() > 0) {
289 line += ";";
290 }
291 line += text;
292 break;
293 }
294 }
295 }
296 index++;
297 data >>= 1;
298 }
299 return line;
300}
301
302void Tas::StartStop() {
303 if (!Settings::values.tas_enable) {
304 return;
305 }
306 if (is_running) {
307 Stop();
308 } else {
309 is_running = true;
310 SwapToTasController();
311 }
312}
313
314void Tas::Stop() {
315 is_running = false;
316 SwapToStoredController();
317}
318
319void Tas::SwapToTasController() {
320 if (!Settings::values.tas_swap_controllers) {
321 return;
322 }
323 auto& players = Settings::values.players.GetValue();
324 for (std::size_t index = 0; index < players.size(); index++) {
325 auto& player = players[index];
326 player_mappings[index] = player;
327
328 // Only swap active controllers
329 if (!player.connected) {
330 continue;
331 }
332
333 Common::ParamPackage tas_param;
334 tas_param.Set("pad", static_cast<u8>(index));
335 auto button_mapping = GetButtonMappingForDevice(tas_param);
336 auto analog_mapping = GetAnalogMappingForDevice(tas_param);
337 auto& buttons = player.buttons;
338 auto& analogs = player.analogs;
339
340 for (std::size_t i = 0; i < buttons.size(); ++i) {
341 buttons[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)].Serialize();
342 }
343 for (std::size_t i = 0; i < analogs.size(); ++i) {
344 analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize();
345 }
346 }
347 is_old_input_saved = true;
348 Settings::values.is_device_reload_pending.store(true);
349}
350
351void Tas::SwapToStoredController() {
352 if (!is_old_input_saved) {
353 return;
354 }
355 auto& players = Settings::values.players.GetValue();
356 for (std::size_t index = 0; index < players.size(); index++) {
357 players[index] = player_mappings[index];
358 }
359 is_old_input_saved = false;
360 Settings::values.is_device_reload_pending.store(true);
361}
362
363void Tas::Reset() {
364 if (!Settings::values.tas_enable) {
365 return;
366 }
367 needs_reset = true;
368}
369
370bool Tas::Record() {
371 if (!Settings::values.tas_enable) {
372 return true;
373 }
374 is_recording = !is_recording;
375 return is_recording;
376}
377
378void Tas::SaveRecording(bool overwrite_file) {
379 if (is_recording) {
380 return;
381 }
382 if (record_commands.empty()) {
383 return;
384 }
385 WriteTasFile(u8"record.txt");
386 if (overwrite_file) {
387 WriteTasFile(u8"script0-1.txt");
388 }
389 needs_reset = true;
390 record_commands.clear();
391}
392
393InputCommon::ButtonMapping Tas::GetButtonMappingForDevice(
394 const Common::ParamPackage& params) const {
395 // This list is missing ZL/ZR since those are not considered buttons.
396 // We will add those afterwards
397 // This list also excludes any button that can't be really mapped
398 static constexpr std::array<std::pair<Settings::NativeButton::Values, TasButton>, 20>
399 switch_to_tas_button = {
400 std::pair{Settings::NativeButton::A, TasButton::BUTTON_A},
401 {Settings::NativeButton::B, TasButton::BUTTON_B},
402 {Settings::NativeButton::X, TasButton::BUTTON_X},
403 {Settings::NativeButton::Y, TasButton::BUTTON_Y},
404 {Settings::NativeButton::LStick, TasButton::STICK_L},
405 {Settings::NativeButton::RStick, TasButton::STICK_R},
406 {Settings::NativeButton::L, TasButton::TRIGGER_L},
407 {Settings::NativeButton::R, TasButton::TRIGGER_R},
408 {Settings::NativeButton::Plus, TasButton::BUTTON_PLUS},
409 {Settings::NativeButton::Minus, TasButton::BUTTON_MINUS},
410 {Settings::NativeButton::DLeft, TasButton::BUTTON_LEFT},
411 {Settings::NativeButton::DUp, TasButton::BUTTON_UP},
412 {Settings::NativeButton::DRight, TasButton::BUTTON_RIGHT},
413 {Settings::NativeButton::DDown, TasButton::BUTTON_DOWN},
414 {Settings::NativeButton::SL, TasButton::BUTTON_SL},
415 {Settings::NativeButton::SR, TasButton::BUTTON_SR},
416 {Settings::NativeButton::Screenshot, TasButton::BUTTON_CAPTURE},
417 {Settings::NativeButton::Home, TasButton::BUTTON_HOME},
418 {Settings::NativeButton::ZL, TasButton::TRIGGER_ZL},
419 {Settings::NativeButton::ZR, TasButton::TRIGGER_ZR},
420 };
421
422 InputCommon::ButtonMapping mapping{};
423 for (const auto& [switch_button, tas_button] : switch_to_tas_button) {
424 Common::ParamPackage button_params({{"engine", "tas"}});
425 button_params.Set("pad", params.Get("pad", 0));
426 button_params.Set("button", static_cast<int>(tas_button));
427 mapping.insert_or_assign(switch_button, std::move(button_params));
428 }
429
430 return mapping;
431}
432
433InputCommon::AnalogMapping Tas::GetAnalogMappingForDevice(
434 const Common::ParamPackage& params) const {
435
436 InputCommon::AnalogMapping mapping = {};
437 Common::ParamPackage left_analog_params;
438 left_analog_params.Set("engine", "tas");
439 left_analog_params.Set("pad", params.Get("pad", 0));
440 left_analog_params.Set("axis_x", static_cast<int>(TasAxes::StickX));
441 left_analog_params.Set("axis_y", static_cast<int>(TasAxes::StickY));
442 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
443 Common::ParamPackage right_analog_params;
444 right_analog_params.Set("engine", "tas");
445 right_analog_params.Set("pad", params.Get("pad", 0));
446 right_analog_params.Set("axis_x", static_cast<int>(TasAxes::SubstickX));
447 right_analog_params.Set("axis_y", static_cast<int>(TasAxes::SubstickY));
448 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
449 return mapping;
450}
451
452const TasData& Tas::GetTasState(std::size_t pad) const {
453 return tas_data[pad];
454}
455} // namespace TasInput
diff --git a/src/input_common/tas/tas_poller.cpp b/src/input_common/tas/tas_poller.cpp
deleted file mode 100644
index 15810d6b0..000000000
--- a/src/input_common/tas/tas_poller.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
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 <mutex>
6#include <utility>
7
8#include "common/settings.h"
9#include "common/threadsafe_queue.h"
10#include "input_common/tas/tas_input.h"
11#include "input_common/tas/tas_poller.h"
12
13namespace InputCommon {
14
15class TasButton final : public Input::ButtonDevice {
16public:
17 explicit TasButton(u32 button_, u32 pad_, const TasInput::Tas* tas_input_)
18 : button(button_), pad(pad_), tas_input(tas_input_) {}
19
20 bool GetStatus() const override {
21 return (tas_input->GetTasState(pad).buttons & button) != 0;
22 }
23
24private:
25 const u32 button;
26 const u32 pad;
27 const TasInput::Tas* tas_input;
28};
29
30TasButtonFactory::TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_)
31 : tas_input(std::move(tas_input_)) {}
32
33std::unique_ptr<Input::ButtonDevice> TasButtonFactory::Create(const Common::ParamPackage& params) {
34 const auto button_id = params.Get("button", 0);
35 const auto pad = params.Get("pad", 0);
36
37 return std::make_unique<TasButton>(button_id, pad, tas_input.get());
38}
39
40class TasAnalog final : public Input::AnalogDevice {
41public:
42 explicit TasAnalog(u32 pad_, u32 axis_x_, u32 axis_y_, const TasInput::Tas* tas_input_)
43 : pad(pad_), axis_x(axis_x_), axis_y(axis_y_), tas_input(tas_input_) {}
44
45 float GetAxis(u32 axis) const {
46 std::lock_guard lock{mutex};
47 return tas_input->GetTasState(pad).axis.at(axis);
48 }
49
50 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
51 float x = GetAxis(analog_axis_x);
52 float y = GetAxis(analog_axis_y);
53
54 // Make sure the coordinates are in the unit circle,
55 // otherwise normalize it.
56 float r = x * x + y * y;
57 if (r > 1.0f) {
58 r = std::sqrt(r);
59 x /= r;
60 y /= r;
61 }
62
63 return {x, y};
64 }
65
66 std::tuple<float, float> GetStatus() const override {
67 return GetAnalog(axis_x, axis_y);
68 }
69
70 Input::AnalogProperties GetAnalogProperties() const override {
71 return {0.0f, 1.0f, 0.5f};
72 }
73
74private:
75 const u32 pad;
76 const u32 axis_x;
77 const u32 axis_y;
78 const TasInput::Tas* tas_input;
79 mutable std::mutex mutex;
80};
81
82/// An analog device factory that creates analog devices from GC Adapter
83TasAnalogFactory::TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_)
84 : tas_input(std::move(tas_input_)) {}
85
86/**
87 * Creates analog device from joystick axes
88 * @param params contains parameters for creating the device:
89 * - "port": the nth gcpad on the adapter
90 * - "axis_x": the index of the axis to be bind as x-axis
91 * - "axis_y": the index of the axis to be bind as y-axis
92 */
93std::unique_ptr<Input::AnalogDevice> TasAnalogFactory::Create(const Common::ParamPackage& params) {
94 const auto pad = static_cast<u32>(params.Get("pad", 0));
95 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
96 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
97
98 return std::make_unique<TasAnalog>(pad, axis_x, axis_y, tas_input.get());
99}
100
101} // namespace InputCommon
diff --git a/src/input_common/tas/tas_poller.h b/src/input_common/tas/tas_poller.h
deleted file mode 100644
index 09e426cef..000000000
--- a/src/input_common/tas/tas_poller.h
+++ /dev/null
@@ -1,43 +0,0 @@
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#include "core/frontend/input.h"
9#include "input_common/tas/tas_input.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a tas bot. It receives tas events and forward them
15 * to all button devices it created.
16 */
17class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28private:
29 std::shared_ptr<TasInput::Tas> tas_input;
30};
31
32/// An analog device factory that creates analog devices from tas
33class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
34public:
35 explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_);
36
37 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
38
39private:
40 std::shared_ptr<TasInput::Tas> tas_input;
41};
42
43} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
deleted file mode 100644
index 7878a56d7..000000000
--- a/src/input_common/touch_from_button.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
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 <algorithm>
6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/touch_from_button.h"
9
10namespace InputCommon {
11
12class TouchFromButtonDevice final : public Input::TouchDevice {
13public:
14 TouchFromButtonDevice() {
15 const auto button_index =
16 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
17 const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
18
19 for (const auto& config_entry : buttons) {
20 const Common::ParamPackage package{config_entry};
21 map.emplace_back(
22 Input::CreateDevice<Input::ButtonDevice>(config_entry),
23 std::clamp(package.Get("x", 0), 0, static_cast<int>(Layout::ScreenUndocked::Width)),
24 std::clamp(package.Get("y", 0), 0,
25 static_cast<int>(Layout::ScreenUndocked::Height)));
26 }
27 }
28
29 Input::TouchStatus GetStatus() const override {
30 Input::TouchStatus touch_status{};
31 for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) {
32 const bool state = std::get<0>(map[id])->GetStatus();
33 if (state) {
34 const float x = static_cast<float>(std::get<1>(map[id])) /
35 static_cast<int>(Layout::ScreenUndocked::Width);
36 const float y = static_cast<float>(std::get<2>(map[id])) /
37 static_cast<int>(Layout::ScreenUndocked::Height);
38 touch_status[id] = {x, y, true};
39 }
40 }
41 return touch_status;
42 }
43
44private:
45 // A vector of the mapped button, its x and its y-coordinate
46 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
47};
48
49std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) {
50 return std::make_unique<TouchFromButtonDevice>();
51}
52
53} // namespace InputCommon
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
deleted file mode 100644
index b9512aa2e..000000000
--- a/src/input_common/udp/client.cpp
+++ /dev/null
@@ -1,526 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <cstring>
7#include <functional>
8#include <random>
9#include <thread>
10#include <boost/asio.hpp>
11#include "common/logging/log.h"
12#include "common/settings.h"
13#include "input_common/udp/client.h"
14#include "input_common/udp/protocol.h"
15
16using boost::asio::ip::udp;
17
18namespace InputCommon::CemuhookUDP {
19
20struct SocketCallback {
21 std::function<void(Response::Version)> version;
22 std::function<void(Response::PortInfo)> port_info;
23 std::function<void(Response::PadData)> pad_data;
24};
25
26class Socket {
27public:
28 using clock = std::chrono::system_clock;
29
30 explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
31 : callback(std::move(callback_)), timer(io_service),
32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
33 boost::system::error_code ec{};
34 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
35 if (ec.value() != boost::system::errc::success) {
36 LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
37 ipv4 = boost::asio::ip::address_v4{};
38 }
39
40 send_endpoint = {udp::endpoint(ipv4, port)};
41 }
42
43 void Stop() {
44 io_service.stop();
45 }
46
47 void Loop() {
48 io_service.run();
49 }
50
51 void StartSend(const clock::time_point& from) {
52 timer.expires_at(from + std::chrono::seconds(3));
53 timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); });
54 }
55
56 void StartReceive() {
57 socket.async_receive_from(
58 boost::asio::buffer(receive_buffer), receive_endpoint,
59 [this](const boost::system::error_code& error, std::size_t bytes_transferred) {
60 HandleReceive(error, bytes_transferred);
61 });
62 }
63
64private:
65 u32 GenerateRandomClientId() const {
66 std::random_device device;
67 return device();
68 }
69
70 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
71 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
72 switch (*type) {
73 case Type::Version: {
74 Response::Version version;
75 std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version));
76 callback.version(std::move(version));
77 break;
78 }
79 case Type::PortInfo: {
80 Response::PortInfo port_info;
81 std::memcpy(&port_info, &receive_buffer[sizeof(Header)],
82 sizeof(Response::PortInfo));
83 callback.port_info(std::move(port_info));
84 break;
85 }
86 case Type::PadData: {
87 Response::PadData pad_data;
88 std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData));
89 SanitizeMotion(pad_data);
90 callback.pad_data(std::move(pad_data));
91 break;
92 }
93 }
94 }
95 StartReceive();
96 }
97
98 void HandleSend(const boost::system::error_code&) {
99 boost::system::error_code _ignored{};
100 // Send a request for getting port info for the pad
101 const Request::PortInfo port_info{4, {0, 1, 2, 3}};
102 const auto port_message = Request::Create(port_info, client_id);
103 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
104 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
105
106 // Send a request for getting pad data for the pad
107 const Request::PadData pad_data{
108 Request::PadData::Flags::AllPorts,
109 0,
110 EMPTY_MAC_ADDRESS,
111 };
112 const auto pad_message = Request::Create(pad_data, client_id);
113 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
114 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
115 StartSend(timer.expiry());
116 }
117
118 void SanitizeMotion(Response::PadData& data) {
119 // Zero out any non number value
120 if (!std::isnormal(data.gyro.pitch)) {
121 data.gyro.pitch = 0;
122 }
123 if (!std::isnormal(data.gyro.roll)) {
124 data.gyro.roll = 0;
125 }
126 if (!std::isnormal(data.gyro.yaw)) {
127 data.gyro.yaw = 0;
128 }
129 if (!std::isnormal(data.accel.x)) {
130 data.accel.x = 0;
131 }
132 if (!std::isnormal(data.accel.y)) {
133 data.accel.y = 0;
134 }
135 if (!std::isnormal(data.accel.z)) {
136 data.accel.z = 0;
137 }
138 }
139
140 SocketCallback callback;
141 boost::asio::io_service io_service;
142 boost::asio::basic_waitable_timer<clock> timer;
143 udp::socket socket;
144
145 const u32 client_id;
146
147 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
148 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
149 std::array<u8, PORT_INFO_SIZE> send_buffer1;
150 std::array<u8, PAD_DATA_SIZE> send_buffer2;
151 udp::endpoint send_endpoint;
152
153 std::array<u8, MAX_PACKET_SIZE> receive_buffer;
154 udp::endpoint receive_endpoint;
155};
156
157static void SocketLoop(Socket* socket) {
158 socket->StartReceive();
159 socket->StartSend(Socket::clock::now());
160 socket->Loop();
161}
162
163Client::Client() {
164 LOG_INFO(Input, "Udp Initialization started");
165 finger_id.fill(MAX_TOUCH_FINGERS);
166 ReloadSockets();
167}
168
169Client::~Client() {
170 Reset();
171}
172
173Client::ClientConnection::ClientConnection() = default;
174
175Client::ClientConnection::~ClientConnection() = default;
176
177std::vector<Common::ParamPackage> Client::GetInputDevices() const {
178 std::vector<Common::ParamPackage> devices;
179 for (std::size_t pad = 0; pad < pads.size(); pad++) {
180 if (!DeviceConnected(pad)) {
181 continue;
182 }
183 std::string name = fmt::format("UDP Controller {}", pad);
184 devices.emplace_back(Common::ParamPackage{
185 {"class", "cemuhookudp"},
186 {"display", std::move(name)},
187 {"port", std::to_string(pad)},
188 });
189 }
190 return devices;
191}
192
193bool Client::DeviceConnected(std::size_t pad) const {
194 // Use last timestamp to detect if the socket has stopped sending data
195 const auto now = std::chrono::steady_clock::now();
196 const auto time_difference = static_cast<u64>(
197 std::chrono::duration_cast<std::chrono::milliseconds>(now - pads[pad].last_update).count());
198 return time_difference < 1000 && pads[pad].connected;
199}
200
201void Client::ReloadSockets() {
202 Reset();
203
204 std::stringstream servers_ss(static_cast<std::string>(Settings::values.udp_input_servers));
205 std::string server_token;
206 std::size_t client = 0;
207 while (std::getline(servers_ss, server_token, ',')) {
208 if (client == MAX_UDP_CLIENTS) {
209 break;
210 }
211 std::stringstream server_ss(server_token);
212 std::string token;
213 std::getline(server_ss, token, ':');
214 std::string udp_input_address = token;
215 std::getline(server_ss, token, ':');
216 char* temp;
217 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
218 if (*temp != '\0') {
219 LOG_ERROR(Input, "Port number is not valid {}", token);
220 continue;
221 }
222
223 const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port);
224 if (client_number != MAX_UDP_CLIENTS) {
225 LOG_ERROR(Input, "Duplicated UDP servers found");
226 continue;
227 }
228 StartCommunication(client++, udp_input_address, udp_input_port);
229 }
230}
231
232std::size_t Client::GetClientNumber(std::string_view host, u16 port) const {
233 for (std::size_t client = 0; client < clients.size(); client++) {
234 if (clients[client].active == -1) {
235 continue;
236 }
237 if (clients[client].host == host && clients[client].port == port) {
238 return client;
239 }
240 }
241 return MAX_UDP_CLIENTS;
242}
243
244void Client::OnVersion([[maybe_unused]] Response::Version data) {
245 LOG_TRACE(Input, "Version packet received: {}", data.version);
246}
247
248void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
249 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
250}
251
252void Client::OnPadData(Response::PadData data, std::size_t client) {
253 const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id;
254
255 if (pad_index >= pads.size()) {
256 LOG_ERROR(Input, "Invalid pad id {}", data.info.id);
257 return;
258 }
259
260 LOG_TRACE(Input, "PadData packet received");
261 if (data.packet_counter == pads[pad_index].packet_sequence) {
262 LOG_WARNING(
263 Input,
264 "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
265 pads[pad_index].packet_sequence, data.packet_counter);
266 pads[pad_index].connected = false;
267 return;
268 }
269
270 clients[client].active = 1;
271 pads[pad_index].connected = true;
272 pads[pad_index].packet_sequence = data.packet_counter;
273
274 const auto now = std::chrono::steady_clock::now();
275 const auto time_difference = static_cast<u64>(
276 std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update)
277 .count());
278 pads[pad_index].last_update = now;
279
280 const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
281 pads[pad_index].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
282 // Gyroscope values are not it the correct scale from better joy.
283 // Dividing by 312 allows us to make one full turn = 1 turn
284 // This must be a configurable valued called sensitivity
285 pads[pad_index].motion.SetGyroscope(raw_gyroscope / 312.0f);
286 pads[pad_index].motion.UpdateRotation(time_difference);
287 pads[pad_index].motion.UpdateOrientation(time_difference);
288
289 {
290 std::lock_guard guard(pads[pad_index].status.update_mutex);
291 pads[pad_index].status.motion_status = pads[pad_index].motion.GetMotion();
292
293 for (std::size_t id = 0; id < data.touch.size(); ++id) {
294 UpdateTouchInput(data.touch[id], client, id);
295 }
296
297 if (configuring) {
298 const Common::Vec3f gyroscope = pads[pad_index].motion.GetGyroscope();
299 const Common::Vec3f accelerometer = pads[pad_index].motion.GetAcceleration();
300 UpdateYuzuSettings(client, data.info.id, accelerometer, gyroscope);
301 }
302 }
303}
304
305void Client::StartCommunication(std::size_t client, const std::string& host, u16 port) {
306 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
307 [this](Response::PortInfo info) { OnPortInfo(info); },
308 [this, client](Response::PadData data) { OnPadData(data, client); }};
309 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
310 clients[client].host = host;
311 clients[client].port = port;
312 clients[client].active = 0;
313 clients[client].socket = std::make_unique<Socket>(host, port, callback);
314 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
315
316 // Set motion parameters
317 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
318 // Real HW values are unknown, 0.0001 is an approximate to Standard
319 for (std::size_t pad = 0; pad < PADS_PER_CLIENT; pad++) {
320 pads[client * PADS_PER_CLIENT + pad].motion.SetGyroThreshold(0.0001f);
321 }
322}
323
324void Client::Reset() {
325 for (auto& client : clients) {
326 if (client.thread.joinable()) {
327 client.active = -1;
328 client.socket->Stop();
329 client.thread.join();
330 }
331 }
332}
333
334void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index,
335 const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro) {
336 if (gyro.Length() > 0.2f) {
337 LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client,
338 gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]);
339 }
340 UDPPadStatus pad{
341 .host = clients[client].host,
342 .port = clients[client].port,
343 .pad_index = pad_index,
344 };
345 for (std::size_t i = 0; i < 3; ++i) {
346 if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
347 pad.motion = static_cast<PadMotion>(i);
348 pad.motion_value = gyro[i];
349 pad_queue.Push(pad);
350 }
351 if (acc[i] > 1.75f || acc[i] < -1.75f) {
352 pad.motion = static_cast<PadMotion>(i + 3);
353 pad.motion_value = acc[i];
354 pad_queue.Push(pad);
355 }
356 }
357}
358
359std::optional<std::size_t> Client::GetUnusedFingerID() const {
360 std::size_t first_free_id = 0;
361 while (first_free_id < MAX_TOUCH_FINGERS) {
362 if (!std::get<2>(touch_status[first_free_id])) {
363 return first_free_id;
364 } else {
365 first_free_id++;
366 }
367 }
368 return std::nullopt;
369}
370
371void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) {
372 // TODO: Use custom calibration per device
373 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
374 const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
375 const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
376 const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
377 const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850));
378 const std::size_t touch_id = client * 2 + id;
379 if (touch_pad.is_active) {
380 if (finger_id[touch_id] == MAX_TOUCH_FINGERS) {
381 const auto first_free_id = GetUnusedFingerID();
382 if (!first_free_id) {
383 // Invalid finger id skip to next input
384 return;
385 }
386 finger_id[touch_id] = *first_free_id;
387 }
388 auto& [x, y, pressed] = touch_status[finger_id[touch_id]];
389 x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) /
390 static_cast<float>(max_x - min_x);
391 y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) /
392 static_cast<float>(max_y - min_y);
393 pressed = true;
394 return;
395 }
396
397 if (finger_id[touch_id] != MAX_TOUCH_FINGERS) {
398 touch_status[finger_id[touch_id]] = {};
399 finger_id[touch_id] = MAX_TOUCH_FINGERS;
400 }
401}
402
403void Client::BeginConfiguration() {
404 pad_queue.Clear();
405 configuring = true;
406}
407
408void Client::EndConfiguration() {
409 pad_queue.Clear();
410 configuring = false;
411}
412
413DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
414 const std::size_t client_number = GetClientNumber(host, port);
415 if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) {
416 return pads[0].status;
417 }
418 return pads[(client_number * PADS_PER_CLIENT) + pad].status;
419}
420
421const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
422 const std::size_t client_number = GetClientNumber(host, port);
423 if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) {
424 return pads[0].status;
425 }
426 return pads[(client_number * PADS_PER_CLIENT) + pad].status;
427}
428
429Input::TouchStatus& Client::GetTouchState() {
430 return touch_status;
431}
432
433const Input::TouchStatus& Client::GetTouchState() const {
434 return touch_status;
435}
436
437Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
438 return pad_queue;
439}
440
441const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
442 return pad_queue;
443}
444
445void TestCommunication(const std::string& host, u16 port,
446 const std::function<void()>& success_callback,
447 const std::function<void()>& failure_callback) {
448 std::thread([=] {
449 Common::Event success_event;
450 SocketCallback callback{
451 .version = [](Response::Version) {},
452 .port_info = [](Response::PortInfo) {},
453 .pad_data = [&](Response::PadData) { success_event.Set(); },
454 };
455 Socket socket{host, port, std::move(callback)};
456 std::thread worker_thread{SocketLoop, &socket};
457 const bool result =
458 success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10));
459 socket.Stop();
460 worker_thread.join();
461 if (result) {
462 success_callback();
463 } else {
464 failure_callback();
465 }
466 }).detach();
467}
468
469CalibrationConfigurationJob::CalibrationConfigurationJob(
470 const std::string& host, u16 port, std::function<void(Status)> status_callback,
471 std::function<void(u16, u16, u16, u16)> data_callback) {
472
473 std::thread([=, this] {
474 Status current_status{Status::Initialized};
475 SocketCallback callback{
476 [](Response::Version) {}, [](Response::PortInfo) {},
477 [&](Response::PadData data) {
478 static constexpr u16 CALIBRATION_THRESHOLD = 100;
479 static constexpr u16 MAX_VALUE = UINT16_MAX;
480
481 if (current_status == Status::Initialized) {
482 // Receiving data means the communication is ready now
483 current_status = Status::Ready;
484 status_callback(current_status);
485 }
486 const auto& touchpad_0 = data.touch[0];
487 if (touchpad_0.is_active == 0) {
488 return;
489 }
490 LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
491 const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
492 const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
493 if (current_status == Status::Ready) {
494 // First touch - min data (min_x/min_y)
495 current_status = Status::Stage1Completed;
496 status_callback(current_status);
497 }
498 if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
499 touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
500 // Set the current position as max value and finishes configuration
501 const u16 max_x = touchpad_0.x;
502 const u16 max_y = touchpad_0.y;
503 current_status = Status::Completed;
504 data_callback(min_x, min_y, max_x, max_y);
505 status_callback(current_status);
506
507 complete_event.Set();
508 }
509 }};
510 Socket socket{host, port, std::move(callback)};
511 std::thread worker_thread{SocketLoop, &socket};
512 complete_event.Wait();
513 socket.Stop();
514 worker_thread.join();
515 }).detach();
516}
517
518CalibrationConfigurationJob::~CalibrationConfigurationJob() {
519 Stop();
520}
521
522void CalibrationConfigurationJob::Stop() {
523 complete_event.Set();
524}
525
526} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
deleted file mode 100644
index 9829da6f0..000000000
--- a/src/input_common/udp/udp.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
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 <mutex>
6#include <utility>
7#include "common/assert.h"
8#include "common/threadsafe_queue.h"
9#include "input_common/udp/client.h"
10#include "input_common/udp/udp.h"
11
12namespace InputCommon {
13
14class UDPMotion final : public Input::MotionDevice {
15public:
16 explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
17 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
18
19 Input::MotionStatus GetStatus() const override {
20 return client->GetPadState(ip, port, pad).motion_status;
21 }
22
23private:
24 const std::string ip;
25 const u16 port;
26 const u16 pad;
27 CemuhookUDP::Client* client;
28 mutable std::mutex mutex;
29};
30
31/// A motion device factory that creates motion devices from a UDP client
32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
33 : client(std::move(client_)) {}
34
35/**
36 * Creates motion device
37 * @param params contains parameters for creating the device:
38 * - "port": the UDP port number
39 */
40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
41 auto ip = params.Get("ip", "127.0.0.1");
42 const auto port = static_cast<u16>(params.Get("port", 26760));
43 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
44
45 return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get());
46}
47
48void UDPMotionFactory::BeginConfiguration() {
49 polling = true;
50 client->BeginConfiguration();
51}
52
53void UDPMotionFactory::EndConfiguration() {
54 polling = false;
55 client->EndConfiguration();
56}
57
58Common::ParamPackage UDPMotionFactory::GetNextInput() {
59 Common::ParamPackage params;
60 CemuhookUDP::UDPPadStatus pad;
61 auto& queue = client->GetPadQueue();
62 while (queue.Pop(pad)) {
63 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
64 continue;
65 }
66 params.Set("engine", "cemuhookudp");
67 params.Set("ip", pad.host);
68 params.Set("port", static_cast<u16>(pad.port));
69 params.Set("pad_index", static_cast<u16>(pad.pad_index));
70 params.Set("motion", static_cast<u16>(pad.motion));
71 return params;
72 }
73 return params;
74}
75
76class UDPTouch final : public Input::TouchDevice {
77public:
78 explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
79 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
80
81 Input::TouchStatus GetStatus() const override {
82 return client->GetTouchState();
83 }
84
85private:
86 const std::string ip;
87 [[maybe_unused]] const u16 port;
88 [[maybe_unused]] const u16 pad;
89 CemuhookUDP::Client* client;
90 mutable std::mutex mutex;
91};
92
93/// A motion device factory that creates motion devices from a UDP client
94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
95 : client(std::move(client_)) {}
96
97/**
98 * Creates motion device
99 * @param params contains parameters for creating the device:
100 * - "port": the UDP port number
101 */
102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
103 auto ip = params.Get("ip", "127.0.0.1");
104 const auto port = static_cast<u16>(params.Get("port", 26760));
105 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
106
107 return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
108}
109
110} // namespace InputCommon
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
deleted file mode 100644
index ea3fd4175..000000000
--- a/src/input_common/udp/udp.h
+++ /dev/null
@@ -1,57 +0,0 @@
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 <memory>
8#include "core/frontend/input.h"
9#include "input_common/udp/client.h"
10
11namespace InputCommon {
12
13/// A motion device factory that creates motion devices from udp clients
14class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
15public:
16 explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_);
17
18 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
19
20 Common::ParamPackage GetNextInput();
21
22 /// For device input configuration/polling
23 void BeginConfiguration();
24 void EndConfiguration();
25
26 bool IsPolling() const {
27 return polling;
28 }
29
30private:
31 std::shared_ptr<CemuhookUDP::Client> client;
32 bool polling = false;
33};
34
35/// A touch device factory that creates touch devices from udp clients
36class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
37public:
38 explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_);
39
40 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
41
42 Common::ParamPackage GetNextInput();
43
44 /// For device input configuration/polling
45 void BeginConfiguration();
46 void EndConfiguration();
47
48 bool IsPolling() const {
49 return polling;
50 }
51
52private:
53 std::shared_ptr<CemuhookUDP::Client> client;
54 bool polling = false;
55};
56
57} // namespace InputCommon
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 916277811..02d309170 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <cstdio>
5#include <fstream> 7#include <fstream>
6#include <vector> 8#include <vector>
7#include "common/assert.h" 9#include "common/assert.h"
@@ -59,6 +61,34 @@ Codec::~Codec() {
59 av_buffer_unref(&av_gpu_decoder); 61 av_buffer_unref(&av_gpu_decoder);
60} 62}
61 63
64#ifdef LIBVA_FOUND
65// List all the currently loaded Linux modules
66static std::vector<std::string> ListLinuxKernelModules() {
67 using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
68 auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose};
69 std::vector<std::string> modules{};
70 if (!module_listing) {
71 LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules");
72 return modules;
73 }
74 char* buffer = nullptr;
75 size_t buf_len = 0;
76 while (getline(&buffer, &buf_len, module_listing.get()) != -1) {
77 // format for the module listing file (sysfs)
78 // <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address>
79 auto line = std::string(buffer);
80 // we are only interested in module names
81 auto name_pos = line.find_first_of(" ");
82 if (name_pos == std::string::npos) {
83 continue;
84 }
85 modules.push_back(line.erase(name_pos));
86 }
87 free(buffer);
88 return modules;
89}
90#endif
91
62bool Codec::CreateGpuAvDevice() { 92bool Codec::CreateGpuAvDevice() {
63#if defined(LIBVA_FOUND) 93#if defined(LIBVA_FOUND)
64 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { 94 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
@@ -67,8 +97,16 @@ bool Codec::CreateGpuAvDevice() {
67 "amdgpu", 97 "amdgpu",
68 }; 98 };
69 AVDictionary* hwdevice_options = nullptr; 99 AVDictionary* hwdevice_options = nullptr;
100 const auto loaded_modules = ListLinuxKernelModules();
70 av_dict_set(&hwdevice_options, "connection_type", "drm", 0); 101 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
71 for (const auto& driver : VAAPI_DRIVERS) { 102 for (const auto& driver : VAAPI_DRIVERS) {
103 // first check if the target driver is loaded in the kernel
104 bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(),
105 [&driver](const auto& module) { return module == driver; });
106 if (!found) {
107 LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver);
108 continue;
109 }
72 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); 110 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
73 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, 111 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
74 nullptr, hwdevice_options, 0); 112 nullptr, hwdevice_options, 0);
@@ -85,11 +123,12 @@ bool Codec::CreateGpuAvDevice() {
85#endif 123#endif
86 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; 124 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
87 static constexpr std::array GPU_DECODER_TYPES{ 125 static constexpr std::array GPU_DECODER_TYPES{
126#ifdef linux
127 AV_HWDEVICE_TYPE_VDPAU,
128#endif
88 AV_HWDEVICE_TYPE_CUDA, 129 AV_HWDEVICE_TYPE_CUDA,
89#ifdef _WIN32 130#ifdef _WIN32
90 AV_HWDEVICE_TYPE_D3D11VA, 131 AV_HWDEVICE_TYPE_D3D11VA,
91#else
92 AV_HWDEVICE_TYPE_VDPAU,
93#endif 132#endif
94 }; 133 };
95 for (const auto& type : GPU_DECODER_TYPES) { 134 for (const auto& type : GPU_DECODER_TYPES) {
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 2e69e270f..9a38b6b34 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -751,8 +751,9 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
751 }); 751 });
752} 752}
753 753
754void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 754void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
755 vk::ShaderModule& module, bool single_texture) { 755 vk::ShaderModule& module, bool is_target_depth,
756 bool single_texture) {
756 if (pipeline) { 757 if (pipeline) {
757 return; 758 return;
758 } 759 }
@@ -769,7 +770,7 @@ void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRen
769 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, 770 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
770 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 771 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
771 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 772 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
772 .pDepthStencilState = nullptr, 773 .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
773 .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, 774 .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
774 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, 775 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
775 .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout, 776 .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
@@ -780,33 +781,14 @@ void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRen
780 }); 781 });
781} 782}
782 783
784void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
785 vk::ShaderModule& module, bool single_texture) {
786 ConvertPipelineEx(pipeline, renderpass, module, false, single_texture);
787}
788
783void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 789void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
784 vk::ShaderModule& module, bool single_texture) { 790 vk::ShaderModule& module, bool single_texture) {
785 if (pipeline) { 791 ConvertPipelineEx(pipeline, renderpass, module, true, single_texture);
786 return;
787 }
788 const std::array stages = MakeStages(*full_screen_vert, *module);
789 pipeline = device.GetLogical().CreateGraphicsPipeline({
790 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
791 .pNext = nullptr,
792 .flags = 0,
793 .stageCount = static_cast<u32>(stages.size()),
794 .pStages = stages.data(),
795 .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
796 .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
797 .pTessellationState = nullptr,
798 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
799 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
800 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
801 .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
802 .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
803 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
804 .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
805 .renderPass = renderpass,
806 .subpass = 0,
807 .basePipelineHandle = VK_NULL_HANDLE,
808 .basePipelineIndex = 0,
809 });
810} 792}
811 793
812} // namespace Vulkan 794} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 0b73cf444..b1a717090 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -80,6 +80,9 @@ private:
80 80
81 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 81 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
82 82
83 void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
84 vk::ShaderModule& module, bool is_target_depth, bool single_texture);
85
83 void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 86 void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
84 vk::ShaderModule& module, bool single_texture); 87 vk::ShaderModule& module, bool single_texture);
85 88
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 51246d46f..197cba8e3 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -797,9 +797,9 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
797 return *buffers[level]; 797 return *buffers[level];
798 } 798 }
799 const auto new_size = Common::NextPow2(needed_size); 799 const auto new_size = Common::NextPow2(needed_size);
800 VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 800 static constexpr VkBufferUsageFlags flags =
801 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | 801 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
802 VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; 802 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
803 buffers[level] = device.GetLogical().CreateBuffer({ 803 buffers[level] = device.GetLogical().CreateBuffer({
804 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 804 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
805 .pNext = nullptr, 805 .pNext = nullptr,
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index e852c817e..329bf4def 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -55,10 +55,4 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
55 } 55 }
56} 56}
57 57
58float GetResolutionScaleFactor(const RendererBase& renderer) {
59 return Settings::values.resolution_info.active
60 ? Settings::values.resolution_info.up_factor
61 : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio();
62}
63
64} // namespace VideoCore 58} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index f86877e86..084df641f 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -25,6 +25,4 @@ class RendererBase;
25/// Creates an emulated GPU instance using the given system context. 25/// Creates an emulated GPU instance using the given system context.
26std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); 26std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
27 27
28float GetResolutionScaleFactor(const RendererBase& renderer);
29
30} // namespace VideoCore 28} // namespace VideoCore
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index d62fd566f..a44815e71 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -90,9 +90,6 @@ add_executable(yuzu
90 configuration/configure_motion_touch.cpp 90 configuration/configure_motion_touch.cpp
91 configuration/configure_motion_touch.h 91 configuration/configure_motion_touch.h
92 configuration/configure_motion_touch.ui 92 configuration/configure_motion_touch.ui
93 configuration/configure_mouse_advanced.cpp
94 configuration/configure_mouse_advanced.h
95 configuration/configure_mouse_advanced.ui
96 configuration/configure_per_game.cpp 93 configuration/configure_per_game.cpp
97 configuration/configure_per_game.h 94 configuration/configure_per_game.h
98 configuration/configure_per_game.ui 95 configuration/configure_per_game.ui
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index bf8445a89..589e0577a 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -6,8 +6,12 @@
6#include <thread> 6#include <thread>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/param_package.h"
9#include "common/string_util.h" 10#include "common/string_util.h"
10#include "core/core.h" 11#include "core/core.h"
12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h"
11#include "core/hle/lock.h" 15#include "core/hle/lock.h"
12#include "core/hle/service/hid/controllers/npad.h" 16#include "core/hle/service/hid/controllers/npad.h"
13#include "core/hle/service/hid/hid.h" 17#include "core/hle/service/hid/hid.h"
@@ -23,49 +27,32 @@
23 27
24namespace { 28namespace {
25 29
26constexpr std::size_t HANDHELD_INDEX = 8; 30void UpdateController(Core::HID::EmulatedController* controller,
27 31 Core::HID::NpadStyleIndex controller_type, bool connected) {
28constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ 32 if (controller->IsConnected(true)) {
29 {true, false, false, false}, 33 controller->Disconnect();
30 {true, true, false, false}, 34 }
31 {true, true, true, false}, 35 controller->SetNpadStyleIndex(controller_type);
32 {true, true, true, true}, 36 if (connected) {
33 {true, false, false, true}, 37 controller->Connect();
34 {true, false, true, false},
35 {true, false, true, true},
36 {false, true, true, false},
37}};
38
39void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
40 bool connected, Core::System& system) {
41 if (!system.IsPoweredOn()) {
42 return;
43 } 38 }
44
45 auto& npad =
46 system.ServiceManager()
47 .GetService<Service::HID::Hid>("hid")
48 ->GetAppletResource()
49 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
50
51 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
52} 39}
53 40
54// Returns true if the given controller type is compatible with the given parameters. 41// Returns true if the given controller type is compatible with the given parameters.
55bool IsControllerCompatible(Settings::ControllerType controller_type, 42bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type,
56 Core::Frontend::ControllerParameters parameters) { 43 Core::Frontend::ControllerParameters parameters) {
57 switch (controller_type) { 44 switch (controller_type) {
58 case Settings::ControllerType::ProController: 45 case Core::HID::NpadStyleIndex::ProController:
59 return parameters.allow_pro_controller; 46 return parameters.allow_pro_controller;
60 case Settings::ControllerType::DualJoyconDetached: 47 case Core::HID::NpadStyleIndex::JoyconDual:
61 return parameters.allow_dual_joycons; 48 return parameters.allow_dual_joycons;
62 case Settings::ControllerType::LeftJoycon: 49 case Core::HID::NpadStyleIndex::JoyconLeft:
63 return parameters.allow_left_joycon; 50 return parameters.allow_left_joycon;
64 case Settings::ControllerType::RightJoycon: 51 case Core::HID::NpadStyleIndex::JoyconRight:
65 return parameters.allow_right_joycon; 52 return parameters.allow_right_joycon;
66 case Settings::ControllerType::Handheld: 53 case Core::HID::NpadStyleIndex::Handheld:
67 return parameters.enable_single_mode && parameters.allow_handheld; 54 return parameters.enable_single_mode && parameters.allow_handheld;
68 case Settings::ControllerType::GameCube: 55 case Core::HID::NpadStyleIndex::GameCube:
69 return parameters.allow_gamecube_controller; 56 return parameters.allow_gamecube_controller;
70 default: 57 default:
71 return false; 58 return false;
@@ -152,6 +139,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
152 DisableUnsupportedPlayers(); 139 DisableUnsupportedPlayers();
153 140
154 for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) { 141 for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
142 system.HIDCore().GetEmulatedControllerByIndex(player_index)->EnableConfiguration();
155 SetEmulatedControllers(player_index); 143 SetEmulatedControllers(player_index);
156 } 144 }
157 145
@@ -196,7 +184,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
196 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), 184 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
197 [this, i](int index) { 185 [this, i](int index) {
198 UpdateDockedState(GetControllerTypeFromIndex(index, i) == 186 UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
199 Settings::ControllerType::Handheld); 187 Core::HID::NpadStyleIndex::Handheld);
200 }); 188 });
201 } 189 }
202 } 190 }
@@ -246,20 +234,24 @@ void QtControllerSelectorDialog::ApplyConfiguration() {
246 234
247 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); 235 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
248 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); 236 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
237 for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
238 system.HIDCore().GetEmulatedControllerByIndex(player_index)->DisableConfiguration();
239 }
249} 240}
250 241
251void QtControllerSelectorDialog::LoadConfiguration() { 242void QtControllerSelectorDialog::LoadConfiguration() {
243 const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
252 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { 244 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
245 const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
253 const auto connected = 246 const auto connected =
254 Settings::values.players.GetValue()[index].connected || 247 controller->IsConnected(true) || (index == 0 && handheld->IsConnected(true));
255 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
256 player_groupboxes[index]->setChecked(connected); 248 player_groupboxes[index]->setChecked(connected);
257 connected_controller_checkboxes[index]->setChecked(connected); 249 connected_controller_checkboxes[index]->setChecked(connected);
258 emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType( 250 emulated_controllers[index]->setCurrentIndex(
259 Settings::values.players.GetValue()[index].controller_type, index)); 251 GetIndexFromControllerType(controller->GetNpadStyleIndex(true), index));
260 } 252 }
261 253
262 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); 254 UpdateDockedState(handheld->IsConnected(true));
263 255
264 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); 256 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
265 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); 257 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -415,33 +407,33 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
415 emulated_controllers[player_index]->clear(); 407 emulated_controllers[player_index]->clear();
416 408
417 pairs.emplace_back(emulated_controllers[player_index]->count(), 409 pairs.emplace_back(emulated_controllers[player_index]->count(),
418 Settings::ControllerType::ProController); 410 Core::HID::NpadStyleIndex::ProController);
419 emulated_controllers[player_index]->addItem(tr("Pro Controller")); 411 emulated_controllers[player_index]->addItem(tr("Pro Controller"));
420 412
421 pairs.emplace_back(emulated_controllers[player_index]->count(), 413 pairs.emplace_back(emulated_controllers[player_index]->count(),
422 Settings::ControllerType::DualJoyconDetached); 414 Core::HID::NpadStyleIndex::JoyconDual);
423 emulated_controllers[player_index]->addItem(tr("Dual Joycons")); 415 emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
424 416
425 pairs.emplace_back(emulated_controllers[player_index]->count(), 417 pairs.emplace_back(emulated_controllers[player_index]->count(),
426 Settings::ControllerType::LeftJoycon); 418 Core::HID::NpadStyleIndex::JoyconLeft);
427 emulated_controllers[player_index]->addItem(tr("Left Joycon")); 419 emulated_controllers[player_index]->addItem(tr("Left Joycon"));
428 420
429 pairs.emplace_back(emulated_controllers[player_index]->count(), 421 pairs.emplace_back(emulated_controllers[player_index]->count(),
430 Settings::ControllerType::RightJoycon); 422 Core::HID::NpadStyleIndex::JoyconRight);
431 emulated_controllers[player_index]->addItem(tr("Right Joycon")); 423 emulated_controllers[player_index]->addItem(tr("Right Joycon"));
432 424
433 if (player_index == 0) { 425 if (player_index == 0) {
434 pairs.emplace_back(emulated_controllers[player_index]->count(), 426 pairs.emplace_back(emulated_controllers[player_index]->count(),
435 Settings::ControllerType::Handheld); 427 Core::HID::NpadStyleIndex::Handheld);
436 emulated_controllers[player_index]->addItem(tr("Handheld")); 428 emulated_controllers[player_index]->addItem(tr("Handheld"));
437 } 429 }
438 430
439 pairs.emplace_back(emulated_controllers[player_index]->count(), 431 pairs.emplace_back(emulated_controllers[player_index]->count(),
440 Settings::ControllerType::GameCube); 432 Core::HID::NpadStyleIndex::GameCube);
441 emulated_controllers[player_index]->addItem(tr("GameCube Controller")); 433 emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
442} 434}
443 435
444Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( 436Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(
445 int index, std::size_t player_index) const { 437 int index, std::size_t player_index) const {
446 const auto& pairs = index_controller_type_pairs[player_index]; 438 const auto& pairs = index_controller_type_pairs[player_index];
447 439
@@ -449,13 +441,13 @@ Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
449 [index](const auto& pair) { return pair.first == index; }); 441 [index](const auto& pair) { return pair.first == index; });
450 442
451 if (it == pairs.end()) { 443 if (it == pairs.end()) {
452 return Settings::ControllerType::ProController; 444 return Core::HID::NpadStyleIndex::ProController;
453 } 445 }
454 446
455 return it->second; 447 return it->second;
456} 448}
457 449
458int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type, 450int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadStyleIndex type,
459 std::size_t player_index) const { 451 std::size_t player_index) const {
460 const auto& pairs = index_controller_type_pairs[player_index]; 452 const auto& pairs = index_controller_type_pairs[player_index];
461 453
@@ -479,16 +471,16 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
479 const QString stylesheet = [this, player_index] { 471 const QString stylesheet = [this, player_index] {
480 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), 472 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
481 player_index)) { 473 player_index)) {
482 case Settings::ControllerType::ProController: 474 case Core::HID::NpadStyleIndex::ProController:
483 case Settings::ControllerType::GameCube: 475 case Core::HID::NpadStyleIndex::GameCube:
484 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 476 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
485 case Settings::ControllerType::DualJoyconDetached: 477 case Core::HID::NpadStyleIndex::JoyconDual:
486 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); 478 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
487 case Settings::ControllerType::LeftJoycon: 479 case Core::HID::NpadStyleIndex::JoyconLeft:
488 return QStringLiteral("image: url(:/controller/applet_joycon_left%0); "); 480 return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
489 case Settings::ControllerType::RightJoycon: 481 case Core::HID::NpadStyleIndex::JoyconRight:
490 return QStringLiteral("image: url(:/controller/applet_joycon_right%0); "); 482 return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
491 case Settings::ControllerType::Handheld: 483 case Core::HID::NpadStyleIndex::Handheld:
492 return QStringLiteral("image: url(:/controller/applet_handheld%0); "); 484 return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
493 default: 485 default:
494 return QString{}; 486 return QString{};
@@ -516,54 +508,38 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
516} 508}
517 509
518void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { 510void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
519 auto& player = Settings::values.players.GetValue()[player_index]; 511 auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);
520 512
521 const auto controller_type = GetControllerTypeFromIndex( 513 const auto controller_type = GetControllerTypeFromIndex(
522 emulated_controllers[player_index]->currentIndex(), player_index); 514 emulated_controllers[player_index]->currentIndex(), player_index);
523 const auto player_connected = player_groupboxes[player_index]->isChecked() && 515 const auto player_connected = player_groupboxes[player_index]->isChecked() &&
524 controller_type != Settings::ControllerType::Handheld; 516 controller_type != Core::HID::NpadStyleIndex::Handheld;
525 517
526 if (player.controller_type == controller_type && player.connected == player_connected) { 518 if (controller->GetNpadStyleIndex(true) == controller_type &&
527 // Set vibration devices in the event that the input device has changed. 519 controller->IsConnected(true) == player_connected) {
528 ConfigureVibration::SetVibrationDevices(player_index);
529 return; 520 return;
530 } 521 }
531 522
532 // Disconnect the controller first. 523 // Disconnect the controller first.
533 UpdateController(controller_type, player_index, false, system); 524 UpdateController(controller, controller_type, false);
534
535 player.controller_type = controller_type;
536 player.connected = player_connected;
537
538 ConfigureVibration::SetVibrationDevices(player_index);
539 525
540 // Handheld 526 // Handheld
541 if (player_index == 0) { 527 if (player_index == 0) {
542 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 528 if (controller_type == Core::HID::NpadStyleIndex::Handheld) {
543 if (controller_type == Settings::ControllerType::Handheld) { 529 auto* handheld =
544 handheld = player; 530 system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
531 UpdateController(handheld, Core::HID::NpadStyleIndex::Handheld,
532 player_groupboxes[player_index]->isChecked());
545 } 533 }
546 handheld.connected = player_groupboxes[player_index]->isChecked() &&
547 controller_type == Settings::ControllerType::Handheld;
548 UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected, system);
549 }
550
551 if (!player.connected) {
552 return;
553 } 534 }
554 535
555 // This emulates a delay between disconnecting and reconnecting controllers as some games 536 UpdateController(controller, controller_type, player_connected);
556 // do not respond to a change in controller type if it was instantaneous.
557 using namespace std::chrono_literals;
558 std::this_thread::sleep_for(60ms);
559
560 UpdateController(controller_type, player_index, player_connected, system);
561} 537}
562 538
563void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { 539void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
564 if (!player_groupboxes[player_index]->isChecked() || 540 if (!player_groupboxes[player_index]->isChecked() ||
565 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), 541 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
566 player_index) == Settings::ControllerType::Handheld) { 542 player_index) == Core::HID::NpadStyleIndex::Handheld) {
567 led_patterns_boxes[player_index][0]->setChecked(false); 543 led_patterns_boxes[player_index][0]->setChecked(false);
568 led_patterns_boxes[player_index][1]->setChecked(false); 544 led_patterns_boxes[player_index][1]->setChecked(false);
569 led_patterns_boxes[player_index][2]->setChecked(false); 545 led_patterns_boxes[player_index][2]->setChecked(false);
@@ -571,10 +547,12 @@ void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
571 return; 547 return;
572 } 548 }
573 549
574 led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]); 550 const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);
575 led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]); 551 const auto led_pattern = controller->GetLedPattern();
576 led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]); 552 led_patterns_boxes[player_index][0]->setChecked(led_pattern.position1);
577 led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]); 553 led_patterns_boxes[player_index][1]->setChecked(led_pattern.position2);
554 led_patterns_boxes[player_index][2]->setChecked(led_pattern.position3);
555 led_patterns_boxes[player_index][3]->setChecked(led_pattern.position4);
578} 556}
579 557
580void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) { 558void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
@@ -654,10 +632,9 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
654 } 632 }
655 633
656 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { 634 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
635 auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
657 // Disconnect any unsupported players here and disable or hide them if applicable. 636 // Disconnect any unsupported players here and disable or hide them if applicable.
658 Settings::values.players.GetValue()[index].connected = false; 637 UpdateController(controller, controller->GetNpadStyleIndex(true), false);
659 UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false,
660 system);
661 // Hide the player widgets when max_supported_controllers is less than or equal to 4. 638 // Hide the player widgets when max_supported_controllers is less than or equal to 4.
662 if (max_supported_players <= 4) { 639 if (max_supported_players <= 4) {
663 player_widgets[index]->hide(); 640 player_widgets[index]->hide();
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h
index 037325f50..cc343e5ae 100644
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -23,14 +23,18 @@ namespace InputCommon {
23class InputSubsystem; 23class InputSubsystem;
24} 24}
25 25
26namespace Settings {
27enum class ControllerType;
28}
29
30namespace Ui { 26namespace Ui {
31class QtControllerSelectorDialog; 27class QtControllerSelectorDialog;
32} 28}
33 29
30namespace Core {
31class System;
32}
33
34namespace Core::HID {
35enum class NpadStyleIndex : u8;
36}
37
34class QtControllerSelectorDialog final : public QDialog { 38class QtControllerSelectorDialog final : public QDialog {
35 Q_OBJECT 39 Q_OBJECT
36 40
@@ -70,10 +74,10 @@ private:
70 void SetEmulatedControllers(std::size_t player_index); 74 void SetEmulatedControllers(std::size_t player_index);
71 75
72 // Gets the Controller Type for a given controller combobox index per player. 76 // Gets the Controller Type for a given controller combobox index per player.
73 Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const; 77 Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index, std::size_t player_index) const;
74 78
75 // Gets the controller combobox index for a given Controller Type per player. 79 // Gets the controller combobox index for a given Controller Type per player.
76 int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const; 80 int GetIndexFromControllerType(Core::HID::NpadStyleIndex type, std::size_t player_index) const;
77 81
78 // Updates the controller icons per player. 82 // Updates the controller icons per player.
79 void UpdateControllerIcon(std::size_t player_index); 83 void UpdateControllerIcon(std::size_t player_index);
@@ -135,7 +139,7 @@ private:
135 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; 139 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
136 140
137 /// Pairs of emulated controller index and Controller Type enum per player. 141 /// Pairs of emulated controller index and Controller Type enum per player.
138 std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS> 142 std::array<std::vector<std::pair<int, Core::HID::NpadStyleIndex>>, NUM_PLAYERS>
139 index_controller_type_pairs; 143 index_controller_type_pairs;
140 144
141 // Labels representing the number of connected controllers 145 // Labels representing the number of connected controllers
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index a83a11a95..de7f98c4f 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -10,7 +10,10 @@
10#include "common/settings.h" 10#include "common/settings.h"
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/frontend/input_interpreter.h" 13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
15#include "core/hid/hid_types.h"
16#include "core/hid/input_interpreter.h"
14#include "ui_qt_software_keyboard.h" 17#include "ui_qt_software_keyboard.h"
15#include "yuzu/applets/qt_software_keyboard.h" 18#include "yuzu/applets/qt_software_keyboard.h"
16#include "yuzu/main.h" 19#include "yuzu/main.h"
@@ -484,7 +487,7 @@ void QtSoftwareKeyboardDialog::open() {
484void QtSoftwareKeyboardDialog::reject() { 487void QtSoftwareKeyboardDialog::reject() {
485 // Pressing the ESC key in a dialog calls QDialog::reject(). 488 // Pressing the ESC key in a dialog calls QDialog::reject().
486 // We will override this behavior to the "Cancel" action on the software keyboard. 489 // We will override this behavior to the "Cancel" action on the software keyboard.
487 TranslateButtonPress(HIDButton::X); 490 TranslateButtonPress(Core::HID::NpadButton::X);
488} 491}
489 492
490void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { 493void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
@@ -722,7 +725,7 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
722 725
723 connect( 726 connect(
724 ui->line_edit_osk, &QLineEdit::returnPressed, this, 727 ui->line_edit_osk, &QLineEdit::returnPressed, this,
725 [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection); 728 [this] { TranslateButtonPress(Core::HID::NpadButton::Plus); }, Qt::QueuedConnection);
726 729
727 ui->line_edit_osk->setPlaceholderText( 730 ui->line_edit_osk->setPlaceholderText(
728 QString::fromStdU16String(initialize_parameters.guide_text)); 731 QString::fromStdU16String(initialize_parameters.guide_text));
@@ -795,9 +798,10 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
795} 798}
796 799
797void QtSoftwareKeyboardDialog::SetControllerImage() { 800void QtSoftwareKeyboardDialog::SetControllerImage() {
798 const auto controller_type = Settings::values.players.GetValue()[8].connected 801 const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
799 ? Settings::values.players.GetValue()[8].controller_type 802 const auto* player_1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
800 : Settings::values.players.GetValue()[0].controller_type; 803 const auto controller_type =
804 handheld->IsConnected() ? handheld->GetNpadStyleIndex() : player_1->GetNpadStyleIndex();
801 805
802 const QString theme = [] { 806 const QString theme = [] {
803 if (QIcon::themeName().contains(QStringLiteral("dark")) || 807 if (QIcon::themeName().contains(QStringLiteral("dark")) ||
@@ -809,8 +813,8 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
809 }(); 813 }();
810 814
811 switch (controller_type) { 815 switch (controller_type) {
812 case Settings::ControllerType::ProController: 816 case Core::HID::NpadStyleIndex::ProController:
813 case Settings::ControllerType::GameCube: 817 case Core::HID::NpadStyleIndex::GameCube:
814 ui->icon_controller->setStyleSheet( 818 ui->icon_controller->setStyleSheet(
815 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); 819 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
816 ui->icon_controller_shift->setStyleSheet( 820 ui->icon_controller_shift->setStyleSheet(
@@ -818,7 +822,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
818 ui->icon_controller_num->setStyleSheet( 822 ui->icon_controller_num->setStyleSheet(
819 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); 823 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
820 break; 824 break;
821 case Settings::ControllerType::DualJoyconDetached: 825 case Core::HID::NpadStyleIndex::JoyconDual:
822 ui->icon_controller->setStyleSheet( 826 ui->icon_controller->setStyleSheet(
823 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); 827 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
824 ui->icon_controller_shift->setStyleSheet( 828 ui->icon_controller_shift->setStyleSheet(
@@ -826,7 +830,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
826 ui->icon_controller_num->setStyleSheet( 830 ui->icon_controller_num->setStyleSheet(
827 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); 831 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
828 break; 832 break;
829 case Settings::ControllerType::LeftJoycon: 833 case Core::HID::NpadStyleIndex::JoyconLeft:
830 ui->icon_controller->setStyleSheet( 834 ui->icon_controller->setStyleSheet(
831 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") 835 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
832 .arg(theme)); 836 .arg(theme));
@@ -837,7 +841,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
837 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") 841 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
838 .arg(theme)); 842 .arg(theme));
839 break; 843 break;
840 case Settings::ControllerType::RightJoycon: 844 case Core::HID::NpadStyleIndex::JoyconRight:
841 ui->icon_controller->setStyleSheet( 845 ui->icon_controller->setStyleSheet(
842 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") 846 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
843 .arg(theme)); 847 .arg(theme));
@@ -848,7 +852,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
848 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") 852 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
849 .arg(theme)); 853 .arg(theme));
850 break; 854 break;
851 case Settings::ControllerType::Handheld: 855 case Core::HID::NpadStyleIndex::Handheld:
852 ui->icon_controller->setStyleSheet( 856 ui->icon_controller->setStyleSheet(
853 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); 857 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
854 ui->icon_controller_shift->setStyleSheet( 858 ui->icon_controller_shift->setStyleSheet(
@@ -1208,9 +1212,9 @@ void QtSoftwareKeyboardDialog::SetupMouseHover() {
1208 } 1212 }
1209} 1213}
1210 1214
1211template <HIDButton... T> 1215template <Core::HID::NpadButton... T>
1212void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { 1216void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
1213 const auto f = [this](HIDButton button) { 1217 const auto f = [this](Core::HID::NpadButton button) {
1214 if (input_interpreter->IsButtonPressedOnce(button)) { 1218 if (input_interpreter->IsButtonPressedOnce(button)) {
1215 TranslateButtonPress(button); 1219 TranslateButtonPress(button);
1216 } 1220 }
@@ -1219,9 +1223,9 @@ void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
1219 (f(T), ...); 1223 (f(T), ...);
1220} 1224}
1221 1225
1222template <HIDButton... T> 1226template <Core::HID::NpadButton... T>
1223void QtSoftwareKeyboardDialog::HandleButtonHold() { 1227void QtSoftwareKeyboardDialog::HandleButtonHold() {
1224 const auto f = [this](HIDButton button) { 1228 const auto f = [this](Core::HID::NpadButton button) {
1225 if (input_interpreter->IsButtonHeld(button)) { 1229 if (input_interpreter->IsButtonHeld(button)) {
1226 TranslateButtonPress(button); 1230 TranslateButtonPress(button);
1227 } 1231 }
@@ -1230,9 +1234,9 @@ void QtSoftwareKeyboardDialog::HandleButtonHold() {
1230 (f(T), ...); 1234 (f(T), ...);
1231} 1235}
1232 1236
1233void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { 1237void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button) {
1234 switch (button) { 1238 switch (button) {
1235 case HIDButton::A: 1239 case Core::HID::NpadButton::A:
1236 switch (bottom_osk_index) { 1240 switch (bottom_osk_index) {
1237 case BottomOSKIndex::LowerCase: 1241 case BottomOSKIndex::LowerCase:
1238 case BottomOSKIndex::UpperCase: 1242 case BottomOSKIndex::UpperCase:
@@ -1245,7 +1249,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1245 break; 1249 break;
1246 } 1250 }
1247 break; 1251 break;
1248 case HIDButton::B: 1252 case Core::HID::NpadButton::B:
1249 switch (bottom_osk_index) { 1253 switch (bottom_osk_index) {
1250 case BottomOSKIndex::LowerCase: 1254 case BottomOSKIndex::LowerCase:
1251 ui->button_backspace->click(); 1255 ui->button_backspace->click();
@@ -1260,7 +1264,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1260 break; 1264 break;
1261 } 1265 }
1262 break; 1266 break;
1263 case HIDButton::X: 1267 case Core::HID::NpadButton::X:
1264 if (is_inline) { 1268 if (is_inline) {
1265 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); 1269 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
1266 } else { 1270 } else {
@@ -1271,7 +1275,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1271 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); 1275 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
1272 } 1276 }
1273 break; 1277 break;
1274 case HIDButton::Y: 1278 case Core::HID::NpadButton::Y:
1275 switch (bottom_osk_index) { 1279 switch (bottom_osk_index) {
1276 case BottomOSKIndex::LowerCase: 1280 case BottomOSKIndex::LowerCase:
1277 ui->button_space->click(); 1281 ui->button_space->click();
@@ -1284,8 +1288,8 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1284 break; 1288 break;
1285 } 1289 }
1286 break; 1290 break;
1287 case HIDButton::LStick: 1291 case Core::HID::NpadButton::StickL:
1288 case HIDButton::RStick: 1292 case Core::HID::NpadButton::StickR:
1289 switch (bottom_osk_index) { 1293 switch (bottom_osk_index) {
1290 case BottomOSKIndex::LowerCase: 1294 case BottomOSKIndex::LowerCase:
1291 ui->button_shift->click(); 1295 ui->button_shift->click();
@@ -1298,13 +1302,13 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1298 break; 1302 break;
1299 } 1303 }
1300 break; 1304 break;
1301 case HIDButton::L: 1305 case Core::HID::NpadButton::L:
1302 MoveTextCursorDirection(Direction::Left); 1306 MoveTextCursorDirection(Direction::Left);
1303 break; 1307 break;
1304 case HIDButton::R: 1308 case Core::HID::NpadButton::R:
1305 MoveTextCursorDirection(Direction::Right); 1309 MoveTextCursorDirection(Direction::Right);
1306 break; 1310 break;
1307 case HIDButton::Plus: 1311 case Core::HID::NpadButton::Plus:
1308 switch (bottom_osk_index) { 1312 switch (bottom_osk_index) {
1309 case BottomOSKIndex::LowerCase: 1313 case BottomOSKIndex::LowerCase:
1310 ui->button_ok->click(); 1314 ui->button_ok->click();
@@ -1319,24 +1323,24 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1319 break; 1323 break;
1320 } 1324 }
1321 break; 1325 break;
1322 case HIDButton::DLeft: 1326 case Core::HID::NpadButton::Left:
1323 case HIDButton::LStickLeft: 1327 case Core::HID::NpadButton::StickLLeft:
1324 case HIDButton::RStickLeft: 1328 case Core::HID::NpadButton::StickRLeft:
1325 MoveButtonDirection(Direction::Left); 1329 MoveButtonDirection(Direction::Left);
1326 break; 1330 break;
1327 case HIDButton::DUp: 1331 case Core::HID::NpadButton::Up:
1328 case HIDButton::LStickUp: 1332 case Core::HID::NpadButton::StickLUp:
1329 case HIDButton::RStickUp: 1333 case Core::HID::NpadButton::StickRUp:
1330 MoveButtonDirection(Direction::Up); 1334 MoveButtonDirection(Direction::Up);
1331 break; 1335 break;
1332 case HIDButton::DRight: 1336 case Core::HID::NpadButton::Right:
1333 case HIDButton::LStickRight: 1337 case Core::HID::NpadButton::StickLRight:
1334 case HIDButton::RStickRight: 1338 case Core::HID::NpadButton::StickRRight:
1335 MoveButtonDirection(Direction::Right); 1339 MoveButtonDirection(Direction::Right);
1336 break; 1340 break;
1337 case HIDButton::DDown: 1341 case Core::HID::NpadButton::Down:
1338 case HIDButton::LStickDown: 1342 case Core::HID::NpadButton::StickLDown:
1339 case HIDButton::RStickDown: 1343 case Core::HID::NpadButton::StickRDown:
1340 MoveButtonDirection(Direction::Down); 1344 MoveButtonDirection(Direction::Down);
1341 break; 1345 break;
1342 default: 1346 default:
@@ -1467,19 +1471,25 @@ void QtSoftwareKeyboardDialog::InputThread() {
1467 while (input_thread_running) { 1471 while (input_thread_running) {
1468 input_interpreter->PollInput(); 1472 input_interpreter->PollInput();
1469 1473
1470 HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, 1474 HandleButtonPressedOnce<
1471 HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R, 1475 Core::HID::NpadButton::A, Core::HID::NpadButton::B, Core::HID::NpadButton::X,
1472 HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp, 1476 Core::HID::NpadButton::Y, Core::HID::NpadButton::StickL, Core::HID::NpadButton::StickR,
1473 HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, 1477 Core::HID::NpadButton::L, Core::HID::NpadButton::R, Core::HID::NpadButton::Plus,
1474 HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, 1478 Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
1475 HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, 1479 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
1476 HIDButton::RStickDown>(); 1480 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
1477 1481 Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft,
1478 HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp, 1482 Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight,
1479 HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, 1483 Core::HID::NpadButton::StickRDown>();
1480 HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, 1484
1481 HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, 1485 HandleButtonHold<Core::HID::NpadButton::B, Core::HID::NpadButton::L,
1482 HIDButton::RStickDown>(); 1486 Core::HID::NpadButton::R, Core::HID::NpadButton::Left,
1487 Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
1488 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
1489 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
1490 Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft,
1491 Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight,
1492 Core::HID::NpadButton::StickRDown>();
1483 1493
1484 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 1494 std::this_thread::sleep_for(std::chrono::milliseconds(50));
1485 } 1495 }
diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h
index 592d9c085..b030cdcf7 100644
--- a/src/yuzu/applets/qt_software_keyboard.h
+++ b/src/yuzu/applets/qt_software_keyboard.h
@@ -14,14 +14,16 @@
14 14
15#include "core/frontend/applets/software_keyboard.h" 15#include "core/frontend/applets/software_keyboard.h"
16 16
17enum class HIDButton : u8;
18
19class InputInterpreter; 17class InputInterpreter;
20 18
21namespace Core { 19namespace Core {
22class System; 20class System;
23} 21}
24 22
23namespace Core::HID {
24enum class NpadButton : u64;
25}
26
25namespace Ui { 27namespace Ui {
26class QtSoftwareKeyboardDialog; 28class QtSoftwareKeyboardDialog;
27} 29}
@@ -146,7 +148,7 @@ private:
146 * 148 *
147 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 149 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
148 */ 150 */
149 template <HIDButton... T> 151 template <Core::HID::NpadButton... T>
150 void HandleButtonPressedOnce(); 152 void HandleButtonPressedOnce();
151 153
152 /** 154 /**
@@ -154,7 +156,7 @@ private:
154 * 156 *
155 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 157 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
156 */ 158 */
157 template <HIDButton... T> 159 template <Core::HID::NpadButton... T>
158 void HandleButtonHold(); 160 void HandleButtonHold();
159 161
160 /** 162 /**
@@ -162,7 +164,7 @@ private:
162 * 164 *
163 * @param button The button press to process. 165 * @param button The button press to process.
164 */ 166 */
165 void TranslateButtonPress(HIDButton button); 167 void TranslateButtonPress(Core::HID::NpadButton button);
166 168
167 /** 169 /**
168 * Moves the focus of a button in a certain direction. 170 * Moves the focus of a button in a certain direction.
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index da8c6882a..cb3c5d826 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -14,9 +14,11 @@
14#endif 14#endif
15 15
16#include "common/fs/path_util.h" 16#include "common/fs/path_util.h"
17#include "common/param_package.h"
17#include "core/core.h" 18#include "core/core.h"
18#include "core/frontend/input_interpreter.h" 19#include "core/hid/hid_types.h"
19#include "input_common/keyboard.h" 20#include "core/hid/input_interpreter.h"
21#include "input_common/drivers/keyboard.h"
20#include "input_common/main.h" 22#include "input_common/main.h"
21#include "yuzu/applets/qt_web_browser.h" 23#include "yuzu/applets/qt_web_browser.h"
22#include "yuzu/applets/qt_web_browser_scripts.h" 24#include "yuzu/applets/qt_web_browser_scripts.h"
@@ -27,19 +29,19 @@
27 29
28namespace { 30namespace {
29 31
30constexpr int HIDButtonToKey(HIDButton button) { 32constexpr int HIDButtonToKey(Core::HID::NpadButton button) {
31 switch (button) { 33 switch (button) {
32 case HIDButton::DLeft: 34 case Core::HID::NpadButton::Left:
33 case HIDButton::LStickLeft: 35 case Core::HID::NpadButton::StickLLeft:
34 return Qt::Key_Left; 36 return Qt::Key_Left;
35 case HIDButton::DUp: 37 case Core::HID::NpadButton::Up:
36 case HIDButton::LStickUp: 38 case Core::HID::NpadButton::StickLUp:
37 return Qt::Key_Up; 39 return Qt::Key_Up;
38 case HIDButton::DRight: 40 case Core::HID::NpadButton::Right:
39 case HIDButton::LStickRight: 41 case Core::HID::NpadButton::StickLRight:
40 return Qt::Key_Right; 42 return Qt::Key_Right;
41 case HIDButton::DDown: 43 case Core::HID::NpadButton::Down:
42 case HIDButton::LStickDown: 44 case Core::HID::NpadButton::StickLDown:
43 return Qt::Key_Down; 45 return Qt::Key_Down;
44 default: 46 default:
45 return 0; 47 return 0;
@@ -208,25 +210,25 @@ void QtNXWebEngineView::keyReleaseEvent(QKeyEvent* event) {
208 } 210 }
209} 211}
210 212
211template <HIDButton... T> 213template <Core::HID::NpadButton... T>
212void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { 214void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
213 const auto f = [this](HIDButton button) { 215 const auto f = [this](Core::HID::NpadButton button) {
214 if (input_interpreter->IsButtonPressedOnce(button)) { 216 if (input_interpreter->IsButtonPressedOnce(button)) {
215 page()->runJavaScript( 217 page()->runJavaScript(
216 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), 218 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
217 [this, button](const QVariant& variant) { 219 [this, button](const QVariant& variant) {
218 if (variant.toBool()) { 220 if (variant.toBool()) {
219 switch (button) { 221 switch (button) {
220 case HIDButton::A: 222 case Core::HID::NpadButton::A:
221 SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>(); 223 SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>();
222 break; 224 break;
223 case HIDButton::B: 225 case Core::HID::NpadButton::B:
224 SendKeyPressEvent(Qt::Key_B); 226 SendKeyPressEvent(Qt::Key_B);
225 break; 227 break;
226 case HIDButton::X: 228 case Core::HID::NpadButton::X:
227 SendKeyPressEvent(Qt::Key_X); 229 SendKeyPressEvent(Qt::Key_X);
228 break; 230 break;
229 case HIDButton::Y: 231 case Core::HID::NpadButton::Y:
230 SendKeyPressEvent(Qt::Key_Y); 232 SendKeyPressEvent(Qt::Key_Y);
231 break; 233 break;
232 default: 234 default:
@@ -244,9 +246,9 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
244 (f(T), ...); 246 (f(T), ...);
245} 247}
246 248
247template <HIDButton... T> 249template <Core::HID::NpadButton... T>
248void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() { 250void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
249 const auto f = [this](HIDButton button) { 251 const auto f = [this](Core::HID::NpadButton button) {
250 if (input_interpreter->IsButtonPressedOnce(button)) { 252 if (input_interpreter->IsButtonPressedOnce(button)) {
251 SendKeyPressEvent(HIDButtonToKey(button)); 253 SendKeyPressEvent(HIDButtonToKey(button));
252 } 254 }
@@ -255,9 +257,9 @@ void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
255 (f(T), ...); 257 (f(T), ...);
256} 258}
257 259
258template <HIDButton... T> 260template <Core::HID::NpadButton... T>
259void QtNXWebEngineView::HandleWindowKeyButtonHold() { 261void QtNXWebEngineView::HandleWindowKeyButtonHold() {
260 const auto f = [this](HIDButton button) { 262 const auto f = [this](Core::HID::NpadButton button) {
261 if (input_interpreter->IsButtonHeld(button)) { 263 if (input_interpreter->IsButtonHeld(button)) {
262 SendKeyPressEvent(HIDButtonToKey(button)); 264 SendKeyPressEvent(HIDButtonToKey(button));
263 } 265 }
@@ -308,17 +310,21 @@ void QtNXWebEngineView::InputThread() {
308 while (input_thread_running) { 310 while (input_thread_running) {
309 input_interpreter->PollInput(); 311 input_interpreter->PollInput();
310 312
311 HandleWindowFooterButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, 313 HandleWindowFooterButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
312 HIDButton::L, HIDButton::R>(); 314 Core::HID::NpadButton::X, Core::HID::NpadButton::Y,
313 315 Core::HID::NpadButton::L, Core::HID::NpadButton::R>();
314 HandleWindowKeyButtonPressedOnce<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight, 316
315 HIDButton::DDown, HIDButton::LStickLeft, 317 HandleWindowKeyButtonPressedOnce<
316 HIDButton::LStickUp, HIDButton::LStickRight, 318 Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
317 HIDButton::LStickDown>(); 319 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
318 320 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
319 HandleWindowKeyButtonHold<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight, 321 Core::HID::NpadButton::StickLDown>();
320 HIDButton::DDown, HIDButton::LStickLeft, HIDButton::LStickUp, 322
321 HIDButton::LStickRight, HIDButton::LStickDown>(); 323 HandleWindowKeyButtonHold<
324 Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
325 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
326 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
327 Core::HID::NpadButton::StickLDown>();
322 328
323 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 329 std::this_thread::sleep_for(std::chrono::milliseconds(50));
324 } 330 }
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 7e9f703fc..fa18aecac 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -16,8 +16,6 @@
16 16
17#include "core/frontend/applets/web_browser.h" 17#include "core/frontend/applets/web_browser.h"
18 18
19enum class HIDButton : u8;
20
21class GMainWindow; 19class GMainWindow;
22class InputInterpreter; 20class InputInterpreter;
23class UrlRequestInterceptor; 21class UrlRequestInterceptor;
@@ -26,6 +24,10 @@ namespace Core {
26class System; 24class System;
27} 25}
28 26
27namespace Core::HID {
28enum class NpadButton : u64;
29}
30
29namespace InputCommon { 31namespace InputCommon {
30class InputSubsystem; 32class InputSubsystem;
31} 33}
@@ -114,7 +116,7 @@ private:
114 * 116 *
115 * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks 117 * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks
116 */ 118 */
117 template <HIDButton... T> 119 template <Core::HID::NpadButton... T>
118 void HandleWindowFooterButtonPressedOnce(); 120 void HandleWindowFooterButtonPressedOnce();
119 121
120 /** 122 /**
@@ -123,7 +125,7 @@ private:
123 * 125 *
124 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 126 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
125 */ 127 */
126 template <HIDButton... T> 128 template <Core::HID::NpadButton... T>
127 void HandleWindowKeyButtonPressedOnce(); 129 void HandleWindowKeyButtonPressedOnce();
128 130
129 /** 131 /**
@@ -132,7 +134,7 @@ private:
132 * 134 *
133 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 135 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
134 */ 136 */
135 template <HIDButton... T> 137 template <Core::HID::NpadButton... T>
136 void HandleWindowKeyButtonHold(); 138 void HandleWindowKeyButtonHold();
137 139
138 /** 140 /**
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 976acd176..114f17c06 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -32,10 +32,11 @@
32#include "common/settings.h" 32#include "common/settings.h"
33#include "core/core.h" 33#include "core/core.h"
34#include "core/frontend/framebuffer_layout.h" 34#include "core/frontend/framebuffer_layout.h"
35#include "input_common/keyboard.h" 35#include "input_common/drivers/keyboard.h"
36#include "input_common/drivers/mouse.h"
37#include "input_common/drivers/tas_input.h"
38#include "input_common/drivers/touch_screen.h"
36#include "input_common/main.h" 39#include "input_common/main.h"
37#include "input_common/mouse/mouse_input.h"
38#include "input_common/tas/tas_input.h"
39#include "video_core/renderer_base.h" 40#include "video_core/renderer_base.h"
40#include "video_core/video_core.h" 41#include "video_core/video_core.h"
41#include "yuzu/bootmanager.h" 42#include "yuzu/bootmanager.h"
@@ -296,13 +297,13 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
296 layout->setContentsMargins(0, 0, 0, 0); 297 layout->setContentsMargins(0, 0, 0, 0);
297 setLayout(layout); 298 setLayout(layout);
298 input_subsystem->Initialize(); 299 input_subsystem->Initialize();
299
300 this->setMouseTracking(true); 300 this->setMouseTracking(true);
301 301
302 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 302 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
303 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, 303 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
304 Qt::QueuedConnection); 304 Qt::QueuedConnection);
305 connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); 305 connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
306 connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
306} 307}
307 308
308void GRenderWindow::ExecuteProgram(std::size_t program_index) { 309void GRenderWindow::ExecuteProgram(std::size_t program_index) {
@@ -319,10 +320,19 @@ GRenderWindow::~GRenderWindow() {
319 320
320void GRenderWindow::OnFrameDisplayed() { 321void GRenderWindow::OnFrameDisplayed() {
321 input_subsystem->GetTas()->UpdateThread(); 322 input_subsystem->GetTas()->UpdateThread();
323 const InputCommon::TasInput::TasState new_tas_state =
324 std::get<0>(input_subsystem->GetTas()->GetStatus());
325
322 if (!first_frame) { 326 if (!first_frame) {
327 last_tas_state = new_tas_state;
323 first_frame = true; 328 first_frame = true;
324 emit FirstFrameDisplayed(); 329 emit FirstFrameDisplayed();
325 } 330 }
331
332 if (new_tas_state != last_tas_state) {
333 last_tas_state = new_tas_state;
334 emit TasPlaybackStateChanged();
335 }
326} 336}
327 337
328bool GRenderWindow::IsShown() const { 338bool GRenderWindow::IsShown() const {
@@ -383,34 +393,329 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
383 QWidget::closeEvent(event); 393 QWidget::closeEvent(event);
384} 394}
385 395
396int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
397 switch (qt_key) {
398 case Qt::Key_A:
399 return Settings::NativeKeyboard::A;
400 case Qt::Key_B:
401 return Settings::NativeKeyboard::B;
402 case Qt::Key_C:
403 return Settings::NativeKeyboard::C;
404 case Qt::Key_D:
405 return Settings::NativeKeyboard::D;
406 case Qt::Key_E:
407 return Settings::NativeKeyboard::E;
408 case Qt::Key_F:
409 return Settings::NativeKeyboard::F;
410 case Qt::Key_G:
411 return Settings::NativeKeyboard::G;
412 case Qt::Key_H:
413 return Settings::NativeKeyboard::H;
414 case Qt::Key_I:
415 return Settings::NativeKeyboard::I;
416 case Qt::Key_J:
417 return Settings::NativeKeyboard::J;
418 case Qt::Key_K:
419 return Settings::NativeKeyboard::K;
420 case Qt::Key_L:
421 return Settings::NativeKeyboard::L;
422 case Qt::Key_M:
423 return Settings::NativeKeyboard::M;
424 case Qt::Key_N:
425 return Settings::NativeKeyboard::N;
426 case Qt::Key_O:
427 return Settings::NativeKeyboard::O;
428 case Qt::Key_P:
429 return Settings::NativeKeyboard::P;
430 case Qt::Key_Q:
431 return Settings::NativeKeyboard::Q;
432 case Qt::Key_R:
433 return Settings::NativeKeyboard::R;
434 case Qt::Key_S:
435 return Settings::NativeKeyboard::S;
436 case Qt::Key_T:
437 return Settings::NativeKeyboard::T;
438 case Qt::Key_U:
439 return Settings::NativeKeyboard::U;
440 case Qt::Key_V:
441 return Settings::NativeKeyboard::V;
442 case Qt::Key_W:
443 return Settings::NativeKeyboard::W;
444 case Qt::Key_X:
445 return Settings::NativeKeyboard::X;
446 case Qt::Key_Y:
447 return Settings::NativeKeyboard::Y;
448 case Qt::Key_Z:
449 return Settings::NativeKeyboard::Z;
450 case Qt::Key_1:
451 return Settings::NativeKeyboard::N1;
452 case Qt::Key_2:
453 return Settings::NativeKeyboard::N2;
454 case Qt::Key_3:
455 return Settings::NativeKeyboard::N3;
456 case Qt::Key_4:
457 return Settings::NativeKeyboard::N4;
458 case Qt::Key_5:
459 return Settings::NativeKeyboard::N5;
460 case Qt::Key_6:
461 return Settings::NativeKeyboard::N6;
462 case Qt::Key_7:
463 return Settings::NativeKeyboard::N7;
464 case Qt::Key_8:
465 return Settings::NativeKeyboard::N8;
466 case Qt::Key_9:
467 return Settings::NativeKeyboard::N9;
468 case Qt::Key_0:
469 return Settings::NativeKeyboard::N0;
470 case Qt::Key_Return:
471 return Settings::NativeKeyboard::Return;
472 case Qt::Key_Escape:
473 return Settings::NativeKeyboard::Escape;
474 case Qt::Key_Backspace:
475 return Settings::NativeKeyboard::Backspace;
476 case Qt::Key_Tab:
477 return Settings::NativeKeyboard::Tab;
478 case Qt::Key_Space:
479 return Settings::NativeKeyboard::Space;
480 case Qt::Key_Minus:
481 return Settings::NativeKeyboard::Minus;
482 case Qt::Key_Plus:
483 case Qt::Key_questiondown:
484 return Settings::NativeKeyboard::Plus;
485 case Qt::Key_BracketLeft:
486 case Qt::Key_BraceLeft:
487 return Settings::NativeKeyboard::OpenBracket;
488 case Qt::Key_BracketRight:
489 case Qt::Key_BraceRight:
490 return Settings::NativeKeyboard::CloseBracket;
491 case Qt::Key_Bar:
492 return Settings::NativeKeyboard::Pipe;
493 case Qt::Key_Dead_Tilde:
494 return Settings::NativeKeyboard::Tilde;
495 case Qt::Key_Ntilde:
496 case Qt::Key_Semicolon:
497 return Settings::NativeKeyboard::Semicolon;
498 case Qt::Key_Apostrophe:
499 return Settings::NativeKeyboard::Quote;
500 case Qt::Key_Dead_Grave:
501 return Settings::NativeKeyboard::Backquote;
502 case Qt::Key_Comma:
503 return Settings::NativeKeyboard::Comma;
504 case Qt::Key_Period:
505 return Settings::NativeKeyboard::Period;
506 case Qt::Key_Slash:
507 return Settings::NativeKeyboard::Slash;
508 case Qt::Key_CapsLock:
509 return Settings::NativeKeyboard::CapsLock;
510 case Qt::Key_F1:
511 return Settings::NativeKeyboard::F1;
512 case Qt::Key_F2:
513 return Settings::NativeKeyboard::F2;
514 case Qt::Key_F3:
515 return Settings::NativeKeyboard::F3;
516 case Qt::Key_F4:
517 return Settings::NativeKeyboard::F4;
518 case Qt::Key_F5:
519 return Settings::NativeKeyboard::F5;
520 case Qt::Key_F6:
521 return Settings::NativeKeyboard::F6;
522 case Qt::Key_F7:
523 return Settings::NativeKeyboard::F7;
524 case Qt::Key_F8:
525 return Settings::NativeKeyboard::F8;
526 case Qt::Key_F9:
527 return Settings::NativeKeyboard::F9;
528 case Qt::Key_F10:
529 return Settings::NativeKeyboard::F10;
530 case Qt::Key_F11:
531 return Settings::NativeKeyboard::F11;
532 case Qt::Key_F12:
533 return Settings::NativeKeyboard::F12;
534 case Qt::Key_Print:
535 return Settings::NativeKeyboard::PrintScreen;
536 case Qt::Key_ScrollLock:
537 return Settings::NativeKeyboard::ScrollLock;
538 case Qt::Key_Pause:
539 return Settings::NativeKeyboard::Pause;
540 case Qt::Key_Insert:
541 return Settings::NativeKeyboard::Insert;
542 case Qt::Key_Home:
543 return Settings::NativeKeyboard::Home;
544 case Qt::Key_PageUp:
545 return Settings::NativeKeyboard::PageUp;
546 case Qt::Key_Delete:
547 return Settings::NativeKeyboard::Delete;
548 case Qt::Key_End:
549 return Settings::NativeKeyboard::End;
550 case Qt::Key_PageDown:
551 return Settings::NativeKeyboard::PageDown;
552 case Qt::Key_Right:
553 return Settings::NativeKeyboard::Right;
554 case Qt::Key_Left:
555 return Settings::NativeKeyboard::Left;
556 case Qt::Key_Down:
557 return Settings::NativeKeyboard::Down;
558 case Qt::Key_Up:
559 return Settings::NativeKeyboard::Up;
560 case Qt::Key_NumLock:
561 return Settings::NativeKeyboard::NumLock;
562 // Numpad keys are missing here
563 case Qt::Key_F13:
564 return Settings::NativeKeyboard::F13;
565 case Qt::Key_F14:
566 return Settings::NativeKeyboard::F14;
567 case Qt::Key_F15:
568 return Settings::NativeKeyboard::F15;
569 case Qt::Key_F16:
570 return Settings::NativeKeyboard::F16;
571 case Qt::Key_F17:
572 return Settings::NativeKeyboard::F17;
573 case Qt::Key_F18:
574 return Settings::NativeKeyboard::F18;
575 case Qt::Key_F19:
576 return Settings::NativeKeyboard::F19;
577 case Qt::Key_F20:
578 return Settings::NativeKeyboard::F20;
579 case Qt::Key_F21:
580 return Settings::NativeKeyboard::F21;
581 case Qt::Key_F22:
582 return Settings::NativeKeyboard::F22;
583 case Qt::Key_F23:
584 return Settings::NativeKeyboard::F23;
585 case Qt::Key_F24:
586 return Settings::NativeKeyboard::F24;
587 // case Qt:::
588 // return Settings::NativeKeyboard::KPComma;
589 // case Qt:::
590 // return Settings::NativeKeyboard::Ro;
591 case Qt::Key_Hiragana_Katakana:
592 return Settings::NativeKeyboard::KatakanaHiragana;
593 case Qt::Key_yen:
594 return Settings::NativeKeyboard::Yen;
595 case Qt::Key_Henkan:
596 return Settings::NativeKeyboard::Henkan;
597 case Qt::Key_Muhenkan:
598 return Settings::NativeKeyboard::Muhenkan;
599 // case Qt:::
600 // return Settings::NativeKeyboard::NumPadCommaPc98;
601 case Qt::Key_Hangul:
602 return Settings::NativeKeyboard::HangulEnglish;
603 case Qt::Key_Hangul_Hanja:
604 return Settings::NativeKeyboard::Hanja;
605 case Qt::Key_Katakana:
606 return Settings::NativeKeyboard::KatakanaKey;
607 case Qt::Key_Hiragana:
608 return Settings::NativeKeyboard::HiraganaKey;
609 case Qt::Key_Zenkaku_Hankaku:
610 return Settings::NativeKeyboard::ZenkakuHankaku;
611 // Modifier keys are handled by the modifier property
612 default:
613 return Settings::NativeKeyboard::None;
614 }
615}
616
617int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) {
618 int modifier = 0;
619
620 if ((qt_modifiers & Qt::KeyboardModifier::ShiftModifier) != 0) {
621 modifier |= 1 << Settings::NativeKeyboard::LeftShift;
622 }
623 if ((qt_modifiers & Qt::KeyboardModifier::ControlModifier) != 0) {
624 modifier |= 1 << Settings::NativeKeyboard::LeftControl;
625 }
626 if ((qt_modifiers & Qt::KeyboardModifier::AltModifier) != 0) {
627 modifier |= 1 << Settings::NativeKeyboard::LeftAlt;
628 }
629 if ((qt_modifiers & Qt::KeyboardModifier::MetaModifier) != 0) {
630 modifier |= 1 << Settings::NativeKeyboard::LeftMeta;
631 }
632
633 // TODO: These keys can't be obtained with Qt::KeyboardModifier
634
635 // if ((qt_modifiers & 0x10) != 0) {
636 // modifier |= 1 << Settings::NativeKeyboard::RightShift;
637 // }
638 // if ((qt_modifiers & 0x20) != 0) {
639 // modifier |= 1 << Settings::NativeKeyboard::RightControl;
640 // }
641 // if ((qt_modifiers & 0x40) != 0) {
642 // modifier |= 1 << Settings::NativeKeyboard::RightAlt;
643 // }
644 // if ((qt_modifiers & 0x80) != 0) {
645 // modifier |= 1 << Settings::NativeKeyboard::RightMeta;
646 // }
647 // if ((qt_modifiers & 0x100) != 0) {
648 // modifier |= 1 << Settings::NativeKeyboard::CapsLock;
649 // }
650 // if ((qt_modifiers & 0x200) != 0) {
651 // modifier |= 1 << Settings::NativeKeyboard::NumLock;
652 // }
653 // if ((qt_modifiers & ???) != 0) {
654 // modifier |= 1 << Settings::NativeKeyboard::ScrollLock;
655 // }
656 // if ((qt_modifiers & ???) != 0) {
657 // modifier |= 1 << Settings::NativeKeyboard::Katakana;
658 // }
659 // if ((qt_modifiers & ???) != 0) {
660 // modifier |= 1 << Settings::NativeKeyboard::Hiragana;
661 // }
662 return modifier;
663}
664
386void GRenderWindow::keyPressEvent(QKeyEvent* event) { 665void GRenderWindow::keyPressEvent(QKeyEvent* event) {
666 /**
667 * This feature can be enhanced with the following functions, but they do not provide
668 * cross-platform behavior.
669 *
670 * event->nativeVirtualKey() can distinguish between keys on the numpad.
671 * event->nativeModifiers() can distinguish between left and right keys and numlock,
672 * capslock, scroll lock.
673 */
387 if (!event->isAutoRepeat()) { 674 if (!event->isAutoRepeat()) {
675 const auto modifier = QtModifierToSwitchModifier(event->modifiers());
676 const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
677 input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier);
678 input_subsystem->GetKeyboard()->PressKeyboardKey(key);
679 // This is used for gamepads that can have any key mapped
388 input_subsystem->GetKeyboard()->PressKey(event->key()); 680 input_subsystem->GetKeyboard()->PressKey(event->key());
389 } 681 }
390} 682}
391 683
392void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 684void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
685 /**
686 * This feature can be enhanced with the following functions, but they do not provide
687 * cross-platform behavior.
688 *
689 * event->nativeVirtualKey() can distinguish between keys on the numpad.
690 * event->nativeModifiers() can distinguish between left and right buttons and numlock,
691 * capslock, scroll lock.
692 */
393 if (!event->isAutoRepeat()) { 693 if (!event->isAutoRepeat()) {
694 const auto modifier = QtModifierToSwitchModifier(event->modifiers());
695 const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
696 input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier);
697 input_subsystem->GetKeyboard()->ReleaseKeyboardKey(key);
698 // This is used for gamepads that can have any key mapped
394 input_subsystem->GetKeyboard()->ReleaseKey(event->key()); 699 input_subsystem->GetKeyboard()->ReleaseKey(event->key());
395 } 700 }
396} 701}
397 702
398MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) { 703InputCommon::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
399 switch (button) { 704 switch (button) {
400 case Qt::LeftButton: 705 case Qt::LeftButton:
401 return MouseInput::MouseButton::Left; 706 return InputCommon::MouseButton::Left;
402 case Qt::RightButton: 707 case Qt::RightButton:
403 return MouseInput::MouseButton::Right; 708 return InputCommon::MouseButton::Right;
404 case Qt::MiddleButton: 709 case Qt::MiddleButton:
405 return MouseInput::MouseButton::Wheel; 710 return InputCommon::MouseButton::Wheel;
406 case Qt::BackButton: 711 case Qt::BackButton:
407 return MouseInput::MouseButton::Backward; 712 return InputCommon::MouseButton::Backward;
408 case Qt::ForwardButton: 713 case Qt::ForwardButton:
409 return MouseInput::MouseButton::Forward; 714 return InputCommon::MouseButton::Forward;
410 case Qt::TaskButton: 715 case Qt::TaskButton:
411 return MouseInput::MouseButton::Task; 716 return InputCommon::MouseButton::Task;
412 default: 717 default:
413 return MouseInput::MouseButton::Extra; 718 return InputCommon::MouseButton::Extra;
414 } 719 }
415} 720}
416 721
@@ -423,12 +728,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
423 // coordinates and map them to the current render area 728 // coordinates and map them to the current render area
424 const auto pos = mapFromGlobal(QCursor::pos()); 729 const auto pos = mapFromGlobal(QCursor::pos());
425 const auto [x, y] = ScaleTouch(pos); 730 const auto [x, y] = ScaleTouch(pos);
731 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
426 const auto button = QtButtonToMouseButton(event->button()); 732 const auto button = QtButtonToMouseButton(event->button());
427 input_subsystem->GetMouse()->PressButton(x, y, button); 733 input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, button);
428
429 if (event->button() == Qt::LeftButton) {
430 this->TouchPressed(x, y, 0);
431 }
432 734
433 emit MouseActivity(); 735 emit MouseActivity();
434} 736}
@@ -442,12 +744,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
442 // coordinates and map them to the current render area 744 // coordinates and map them to the current render area
443 const auto pos = mapFromGlobal(QCursor::pos()); 745 const auto pos = mapFromGlobal(QCursor::pos());
444 const auto [x, y] = ScaleTouch(pos); 746 const auto [x, y] = ScaleTouch(pos);
747 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
445 const int center_x = width() / 2; 748 const int center_x = width() / 2;
446 const int center_y = height() / 2; 749 const int center_y = height() / 2;
447 input_subsystem->GetMouse()->MouseMove(x, y, center_x, center_y); 750 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
448 this->TouchMoved(x, y, 0);
449 751
450 if (Settings::values.mouse_panning) { 752 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
451 QCursor::setPos(mapToGlobal({center_x, center_y})); 753 QCursor::setPos(mapToGlobal({center_x, center_y}));
452 } 754 }
453 755
@@ -462,10 +764,12 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
462 764
463 const auto button = QtButtonToMouseButton(event->button()); 765 const auto button = QtButtonToMouseButton(event->button());
464 input_subsystem->GetMouse()->ReleaseButton(button); 766 input_subsystem->GetMouse()->ReleaseButton(button);
767}
465 768
466 if (event->button() == Qt::LeftButton) { 769void GRenderWindow::wheelEvent(QWheelEvent* event) {
467 this->TouchReleased(0); 770 const int x = event->angleDelta().x();
468 } 771 const int y = event->angleDelta().y();
772 input_subsystem->GetMouse()->MouseWheelChange(x, y);
469} 773}
470 774
471void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 775void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
@@ -488,7 +792,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
488 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 792 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
489 if (!TouchExist(touch_ids[id], touch_points)) { 793 if (!TouchExist(touch_ids[id], touch_points)) {
490 touch_ids[id] = 0; 794 touch_ids[id] = 0;
491 this->TouchReleased(id + 1); 795 input_subsystem->GetTouchScreen()->TouchReleased(id);
492 } 796 }
493 } 797 }
494} 798}
@@ -497,28 +801,28 @@ void GRenderWindow::TouchEndEvent() {
497 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 801 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
498 if (touch_ids[id] != 0) { 802 if (touch_ids[id] != 0) {
499 touch_ids[id] = 0; 803 touch_ids[id] = 0;
500 this->TouchReleased(id + 1); 804 input_subsystem->GetTouchScreen()->TouchReleased(id);
501 } 805 }
502 } 806 }
503} 807}
504 808
505bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) { 809void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
506 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 810 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
507 if (touch_ids[id] == 0) { 811 if (touch_ids[id] == 0) {
508 touch_ids[id] = touch_point.id() + 1; 812 touch_ids[id] = touch_point.id() + 1;
509 const auto [x, y] = ScaleTouch(touch_point.pos()); 813 const auto [x, y] = ScaleTouch(touch_point.pos());
510 this->TouchPressed(x, y, id + 1); 814 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
511 return true; 815 input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
512 } 816 }
513 } 817 }
514 return false;
515} 818}
516 819
517bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) { 820bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
518 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 821 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
519 if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) { 822 if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
520 const auto [x, y] = ScaleTouch(touch_point.pos()); 823 const auto [x, y] = ScaleTouch(touch_point.pos());
521 this->TouchMoved(x, y, id + 1); 824 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
825 input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
522 return true; 826 return true;
523 } 827 }
524 } 828 }
@@ -551,7 +855,7 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
551 QWidget::focusOutEvent(event); 855 QWidget::focusOutEvent(event);
552 input_subsystem->GetKeyboard()->ReleaseAllKeys(); 856 input_subsystem->GetKeyboard()->ReleaseAllKeys();
553 input_subsystem->GetMouse()->ReleaseAllButtons(); 857 input_subsystem->GetMouse()->ReleaseAllButtons();
554 this->TouchReleased(0); 858 input_subsystem->GetTouchScreen()->ReleaseAllTouch();
555} 859}
556 860
557void GRenderWindow::resizeEvent(QResizeEvent* event) { 861void GRenderWindow::resizeEvent(QResizeEvent* event) {
@@ -630,7 +934,7 @@ void GRenderWindow::ReleaseRenderTarget() {
630 934
631void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { 935void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
632 auto& renderer = system.Renderer(); 936 auto& renderer = system.Renderer();
633 const f32 res_scale = VideoCore::GetResolutionScaleFactor(renderer); 937 const f32 res_scale = Settings::values.resolution_info.up_factor;
634 938
635 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; 939 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
636 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); 940 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
@@ -760,7 +1064,7 @@ void GRenderWindow::showEvent(QShowEvent* event) {
760 1064
761bool GRenderWindow::eventFilter(QObject* object, QEvent* event) { 1065bool GRenderWindow::eventFilter(QObject* object, QEvent* event) {
762 if (event->type() == QEvent::HoverMove) { 1066 if (event->type() == QEvent::HoverMove) {
763 if (Settings::values.mouse_panning) { 1067 if (Settings::values.mouse_panning || Settings::values.mouse_enabled) {
764 auto* hover_event = static_cast<QMouseEvent*>(event); 1068 auto* hover_event = static_cast<QMouseEvent*>(event);
765 mouseMoveEvent(hover_event); 1069 mouseMoveEvent(hover_event);
766 return false; 1070 return false;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 40fd4a9d6..92297a43b 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -30,11 +30,12 @@ class System;
30 30
31namespace InputCommon { 31namespace InputCommon {
32class InputSubsystem; 32class InputSubsystem;
33}
34
35namespace MouseInput {
36enum class MouseButton; 33enum class MouseButton;
37} 34} // namespace InputCommon
35
36namespace InputCommon::TasInput {
37enum class TasState;
38} // namespace InputCommon::TasInput
38 39
39namespace VideoCore { 40namespace VideoCore {
40enum class LoadCallbackStage; 41enum class LoadCallbackStage;
@@ -157,15 +158,22 @@ public:
157 158
158 void resizeEvent(QResizeEvent* event) override; 159 void resizeEvent(QResizeEvent* event) override;
159 160
161 /// Converts a Qt keybard key into NativeKeyboard key
162 static int QtKeyToSwitchKey(Qt::Key qt_keys);
163
164 /// Converts a Qt modifier keys into NativeKeyboard modifier keys
165 static int QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers);
166
160 void keyPressEvent(QKeyEvent* event) override; 167 void keyPressEvent(QKeyEvent* event) override;
161 void keyReleaseEvent(QKeyEvent* event) override; 168 void keyReleaseEvent(QKeyEvent* event) override;
162 169
163 /// Converts a Qt mouse button into MouseInput mouse button 170 /// Converts a Qt mouse button into MouseInput mouse button
164 static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button); 171 static InputCommon::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
165 172
166 void mousePressEvent(QMouseEvent* event) override; 173 void mousePressEvent(QMouseEvent* event) override;
167 void mouseMoveEvent(QMouseEvent* event) override; 174 void mouseMoveEvent(QMouseEvent* event) override;
168 void mouseReleaseEvent(QMouseEvent* event) override; 175 void mouseReleaseEvent(QMouseEvent* event) override;
176 void wheelEvent(QWheelEvent* event) override;
169 177
170 bool event(QEvent* event) override; 178 bool event(QEvent* event) override;
171 179
@@ -203,13 +211,14 @@ signals:
203 void ExecuteProgramSignal(std::size_t program_index); 211 void ExecuteProgramSignal(std::size_t program_index);
204 void ExitSignal(); 212 void ExitSignal();
205 void MouseActivity(); 213 void MouseActivity();
214 void TasPlaybackStateChanged();
206 215
207private: 216private:
208 void TouchBeginEvent(const QTouchEvent* event); 217 void TouchBeginEvent(const QTouchEvent* event);
209 void TouchUpdateEvent(const QTouchEvent* event); 218 void TouchUpdateEvent(const QTouchEvent* event);
210 void TouchEndEvent(); 219 void TouchEndEvent();
211 220
212 bool TouchStart(const QTouchEvent::TouchPoint& touch_point); 221 void TouchStart(const QTouchEvent::TouchPoint& touch_point);
213 bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point); 222 bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
214 bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const; 223 bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
215 224
@@ -236,6 +245,7 @@ private:
236 QWidget* child_widget = nullptr; 245 QWidget* child_widget = nullptr;
237 246
238 bool first_frame = false; 247 bool first_frame = false;
248 InputCommon::TasInput::TasState last_tas_state;
239 249
240 std::array<std::size_t, 16> touch_ids{}; 250 std::array<std::size_t, 16> touch_ids{};
241 251
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8227d06bc..463d500c2 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -11,7 +11,6 @@
11#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/hid/controllers/npad.h" 12#include "core/hle/service/hid/controllers/npad.h"
13#include "input_common/main.h" 13#include "input_common/main.h"
14#include "input_common/udp/client.h"
15#include "yuzu/configuration/config.h" 14#include "yuzu/configuration/config.h"
16 15
17namespace FS = Common::FS; 16namespace FS = Common::FS;
@@ -61,162 +60,6 @@ const std::array<int, 2> Config::default_stick_mod = {
61 0, 60 0,
62}; 61};
63 62
64const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
65 {
66 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
67};
68
69const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
70 0,
71 0,
72 0,
73 0,
74 Qt::Key_A,
75 Qt::Key_B,
76 Qt::Key_C,
77 Qt::Key_D,
78 Qt::Key_E,
79 Qt::Key_F,
80 Qt::Key_G,
81 Qt::Key_H,
82 Qt::Key_I,
83 Qt::Key_J,
84 Qt::Key_K,
85 Qt::Key_L,
86 Qt::Key_M,
87 Qt::Key_N,
88 Qt::Key_O,
89 Qt::Key_P,
90 Qt::Key_Q,
91 Qt::Key_R,
92 Qt::Key_S,
93 Qt::Key_T,
94 Qt::Key_U,
95 Qt::Key_V,
96 Qt::Key_W,
97 Qt::Key_X,
98 Qt::Key_Y,
99 Qt::Key_Z,
100 Qt::Key_1,
101 Qt::Key_2,
102 Qt::Key_3,
103 Qt::Key_4,
104 Qt::Key_5,
105 Qt::Key_6,
106 Qt::Key_7,
107 Qt::Key_8,
108 Qt::Key_9,
109 Qt::Key_0,
110 Qt::Key_Enter,
111 Qt::Key_Escape,
112 Qt::Key_Backspace,
113 Qt::Key_Tab,
114 Qt::Key_Space,
115 Qt::Key_Minus,
116 Qt::Key_Equal,
117 Qt::Key_BracketLeft,
118 Qt::Key_BracketRight,
119 Qt::Key_Backslash,
120 Qt::Key_Dead_Tilde,
121 Qt::Key_Semicolon,
122 Qt::Key_Apostrophe,
123 Qt::Key_Dead_Grave,
124 Qt::Key_Comma,
125 Qt::Key_Period,
126 Qt::Key_Slash,
127 Qt::Key_CapsLock,
128
129 Qt::Key_F1,
130 Qt::Key_F2,
131 Qt::Key_F3,
132 Qt::Key_F4,
133 Qt::Key_F5,
134 Qt::Key_F6,
135 Qt::Key_F7,
136 Qt::Key_F8,
137 Qt::Key_F9,
138 Qt::Key_F10,
139 Qt::Key_F11,
140 Qt::Key_F12,
141
142 Qt::Key_SysReq,
143 Qt::Key_ScrollLock,
144 Qt::Key_Pause,
145 Qt::Key_Insert,
146 Qt::Key_Home,
147 Qt::Key_PageUp,
148 Qt::Key_Delete,
149 Qt::Key_End,
150 Qt::Key_PageDown,
151 Qt::Key_Right,
152 Qt::Key_Left,
153 Qt::Key_Down,
154 Qt::Key_Up,
155
156 Qt::Key_NumLock,
157 Qt::Key_Slash,
158 Qt::Key_Asterisk,
159 Qt::Key_Minus,
160 Qt::Key_Plus,
161 Qt::Key_Enter,
162 Qt::Key_1,
163 Qt::Key_2,
164 Qt::Key_3,
165 Qt::Key_4,
166 Qt::Key_5,
167 Qt::Key_6,
168 Qt::Key_7,
169 Qt::Key_8,
170 Qt::Key_9,
171 Qt::Key_0,
172 Qt::Key_Period,
173
174 0,
175 0,
176 Qt::Key_PowerOff,
177 Qt::Key_Equal,
178
179 Qt::Key_F13,
180 Qt::Key_F14,
181 Qt::Key_F15,
182 Qt::Key_F16,
183 Qt::Key_F17,
184 Qt::Key_F18,
185 Qt::Key_F19,
186 Qt::Key_F20,
187 Qt::Key_F21,
188 Qt::Key_F22,
189 Qt::Key_F23,
190 Qt::Key_F24,
191
192 Qt::Key_Open,
193 Qt::Key_Help,
194 Qt::Key_Menu,
195 0,
196 Qt::Key_Stop,
197 Qt::Key_AudioRepeat,
198 Qt::Key_Undo,
199 Qt::Key_Cut,
200 Qt::Key_Copy,
201 Qt::Key_Paste,
202 Qt::Key_Find,
203 Qt::Key_VolumeMute,
204 Qt::Key_VolumeUp,
205 Qt::Key_VolumeDown,
206 Qt::Key_CapsLock,
207 Qt::Key_NumLock,
208 Qt::Key_ScrollLock,
209 Qt::Key_Comma,
210
211 Qt::Key_ParenLeft,
212 Qt::Key_ParenRight,
213};
214
215const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
216 Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
217 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
218};
219
220// This shouldn't have anything except static initializers (no functions). So 63// This shouldn't have anything except static initializers (no functions). So
221// QKeySequence(...).toString() is NOT ALLOWED HERE. 64// QKeySequence(...).toString() is NOT ALLOWED HERE.
222// This must be in alphabetical order according to action name as it must have the same order as 65// This must be in alphabetical order according to action name as it must have the same order as
@@ -430,18 +273,6 @@ void Config::ReadPlayerValue(std::size_t player_index) {
430 } 273 }
431 } 274 }
432 275
433 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
434 auto& player_vibrations = player.vibrations[i];
435
436 player_vibrations =
437 qt_config
438 ->value(QStringLiteral("%1").arg(player_prefix) +
439 QString::fromUtf8(Settings::NativeVibration::mapping[i]),
440 QString{})
441 .toString()
442 .toStdString();
443 }
444
445 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 276 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
446 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 277 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
447 auto& player_motions = player.motions[i]; 278 auto& player_motions = player.motions[i];
@@ -496,35 +327,10 @@ void Config::ReadDebugValues() {
496 327
497void Config::ReadKeyboardValues() { 328void Config::ReadKeyboardValues() {
498 ReadBasicSetting(Settings::values.keyboard_enabled); 329 ReadBasicSetting(Settings::values.keyboard_enabled);
499
500 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
501 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
502 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
503 Settings::values.keyboard_keys.begin() +
504 Settings::NativeKeyboard::LeftControlKey,
505 InputCommon::GenerateKeyboardParam);
506 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
507 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
508} 330}
509 331
510void Config::ReadMouseValues() { 332void Config::ReadMouseValues() {
511 ReadBasicSetting(Settings::values.mouse_enabled); 333 ReadBasicSetting(Settings::values.mouse_enabled);
512
513 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
514 const std::string default_param =
515 InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
516 auto& mouse_buttons = Settings::values.mouse_buttons[i];
517
518 mouse_buttons = qt_config
519 ->value(QStringLiteral("mouse_") +
520 QString::fromUtf8(Settings::NativeMouseButton::mapping[i]),
521 QString::fromStdString(default_param))
522 .toString()
523 .toStdString();
524 if (mouse_buttons.empty()) {
525 mouse_buttons = default_param;
526 }
527 }
528} 334}
529 335
530void Config::ReadTouchscreenValues() { 336void Config::ReadTouchscreenValues() {
@@ -574,7 +380,6 @@ void Config::ReadControlValues() {
574 380
575 ReadBasicSetting(Settings::values.tas_enable); 381 ReadBasicSetting(Settings::values.tas_enable);
576 ReadBasicSetting(Settings::values.tas_loop); 382 ReadBasicSetting(Settings::values.tas_loop);
577 ReadBasicSetting(Settings::values.tas_swap_controllers);
578 ReadBasicSetting(Settings::values.pause_tas_on_load); 383 ReadBasicSetting(Settings::values.pause_tas_on_load);
579 384
580 ReadGlobalSetting(Settings::values.use_docked_mode); 385 ReadGlobalSetting(Settings::values.use_docked_mode);
@@ -625,13 +430,12 @@ void Config::ReadMotionTouchValues() {
625 } 430 }
626 qt_config->endArray(); 431 qt_config->endArray();
627 432
628 ReadBasicSetting(Settings::values.motion_device);
629 ReadBasicSetting(Settings::values.touch_device); 433 ReadBasicSetting(Settings::values.touch_device);
630 ReadBasicSetting(Settings::values.use_touch_from_button);
631 ReadBasicSetting(Settings::values.touch_from_button_map_index); 434 ReadBasicSetting(Settings::values.touch_from_button_map_index);
632 Settings::values.touch_from_button_map_index = std::clamp( 435 Settings::values.touch_from_button_map_index = std::clamp(
633 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); 436 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
634 ReadBasicSetting(Settings::values.udp_input_servers); 437 ReadBasicSetting(Settings::values.udp_input_servers);
438 ReadBasicSetting(Settings::values.enable_udp_controller);
635} 439}
636 440
637void Config::ReadCoreValues() { 441void Config::ReadCoreValues() {
@@ -704,6 +508,7 @@ void Config::ReadDebuggingValues() {
704 ReadBasicSetting(Settings::values.extended_logging); 508 ReadBasicSetting(Settings::values.extended_logging);
705 ReadBasicSetting(Settings::values.use_debug_asserts); 509 ReadBasicSetting(Settings::values.use_debug_asserts);
706 ReadBasicSetting(Settings::values.use_auto_stub); 510 ReadBasicSetting(Settings::values.use_auto_stub);
511 ReadBasicSetting(Settings::values.enable_all_controllers);
707 512
708 qt_config->endGroup(); 513 qt_config->endGroup();
709} 514}
@@ -830,6 +635,7 @@ void Config::ReadRendererValues() {
830 ReadGlobalSetting(Settings::values.max_anisotropy); 635 ReadGlobalSetting(Settings::values.max_anisotropy);
831 ReadGlobalSetting(Settings::values.use_speed_limit); 636 ReadGlobalSetting(Settings::values.use_speed_limit);
832 ReadGlobalSetting(Settings::values.speed_limit); 637 ReadGlobalSetting(Settings::values.speed_limit);
638 ReadGlobalSetting(Settings::values.fps_cap);
833 ReadGlobalSetting(Settings::values.use_disk_shader_cache); 639 ReadGlobalSetting(Settings::values.use_disk_shader_cache);
834 ReadGlobalSetting(Settings::values.gpu_accuracy); 640 ReadGlobalSetting(Settings::values.gpu_accuracy);
835 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); 641 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
@@ -844,7 +650,6 @@ void Config::ReadRendererValues() {
844 ReadGlobalSetting(Settings::values.bg_blue); 650 ReadGlobalSetting(Settings::values.bg_blue);
845 651
846 if (global) { 652 if (global) {
847 ReadBasicSetting(Settings::values.fps_cap);
848 ReadBasicSetting(Settings::values.renderer_debug); 653 ReadBasicSetting(Settings::values.renderer_debug);
849 ReadBasicSetting(Settings::values.renderer_shader_feedback); 654 ReadBasicSetting(Settings::values.renderer_shader_feedback);
850 ReadBasicSetting(Settings::values.enable_nsight_aftermath); 655 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
@@ -1075,11 +880,6 @@ void Config::SavePlayerValue(std::size_t player_index) {
1075 QString::fromStdString(player.analogs[i]), 880 QString::fromStdString(player.analogs[i]),
1076 QString::fromStdString(default_param)); 881 QString::fromStdString(default_param));
1077 } 882 }
1078 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
1079 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
1080 QString::fromStdString(Settings::NativeVibration::mapping[i]),
1081 QString::fromStdString(player.vibrations[i]), QString{});
1082 }
1083 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 883 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
1084 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 884 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
1085 WriteSetting(QStringLiteral("%1").arg(player_prefix) + 885 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
@@ -1111,15 +911,6 @@ void Config::SaveDebugValues() {
1111 911
1112void Config::SaveMouseValues() { 912void Config::SaveMouseValues() {
1113 WriteBasicSetting(Settings::values.mouse_enabled); 913 WriteBasicSetting(Settings::values.mouse_enabled);
1114
1115 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
1116 const std::string default_param =
1117 InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
1118 WriteSetting(QStringLiteral("mouse_") +
1119 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
1120 QString::fromStdString(Settings::values.mouse_buttons[i]),
1121 QString::fromStdString(default_param));
1122 }
1123} 914}
1124 915
1125void Config::SaveTouchscreenValues() { 916void Config::SaveTouchscreenValues() {
@@ -1133,11 +924,10 @@ void Config::SaveTouchscreenValues() {
1133} 924}
1134 925
1135void Config::SaveMotionTouchValues() { 926void Config::SaveMotionTouchValues() {
1136 WriteBasicSetting(Settings::values.motion_device);
1137 WriteBasicSetting(Settings::values.touch_device); 927 WriteBasicSetting(Settings::values.touch_device);
1138 WriteBasicSetting(Settings::values.use_touch_from_button);
1139 WriteBasicSetting(Settings::values.touch_from_button_map_index); 928 WriteBasicSetting(Settings::values.touch_from_button_map_index);
1140 WriteBasicSetting(Settings::values.udp_input_servers); 929 WriteBasicSetting(Settings::values.udp_input_servers);
930 WriteBasicSetting(Settings::values.enable_udp_controller);
1141 931
1142 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); 932 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
1143 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { 933 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
@@ -1210,7 +1000,6 @@ void Config::SaveControlValues() {
1210 1000
1211 WriteBasicSetting(Settings::values.tas_enable); 1001 WriteBasicSetting(Settings::values.tas_enable);
1212 WriteBasicSetting(Settings::values.tas_loop); 1002 WriteBasicSetting(Settings::values.tas_loop);
1213 WriteBasicSetting(Settings::values.tas_swap_controllers);
1214 WriteBasicSetting(Settings::values.pause_tas_on_load); 1003 WriteBasicSetting(Settings::values.pause_tas_on_load);
1215 1004
1216 qt_config->endGroup(); 1005 qt_config->endGroup();
@@ -1263,6 +1052,7 @@ void Config::SaveDebuggingValues() {
1263 WriteBasicSetting(Settings::values.quest_flag); 1052 WriteBasicSetting(Settings::values.quest_flag);
1264 WriteBasicSetting(Settings::values.use_debug_asserts); 1053 WriteBasicSetting(Settings::values.use_debug_asserts);
1265 WriteBasicSetting(Settings::values.disable_macro_jit); 1054 WriteBasicSetting(Settings::values.disable_macro_jit);
1055 WriteBasicSetting(Settings::values.enable_all_controllers);
1266 1056
1267 qt_config->endGroup(); 1057 qt_config->endGroup();
1268} 1058}
@@ -1382,6 +1172,7 @@ void Config::SaveRendererValues() {
1382 WriteGlobalSetting(Settings::values.max_anisotropy); 1172 WriteGlobalSetting(Settings::values.max_anisotropy);
1383 WriteGlobalSetting(Settings::values.use_speed_limit); 1173 WriteGlobalSetting(Settings::values.use_speed_limit);
1384 WriteGlobalSetting(Settings::values.speed_limit); 1174 WriteGlobalSetting(Settings::values.speed_limit);
1175 WriteGlobalSetting(Settings::values.fps_cap);
1385 WriteGlobalSetting(Settings::values.use_disk_shader_cache); 1176 WriteGlobalSetting(Settings::values.use_disk_shader_cache);
1386 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), 1177 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
1387 static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), 1178 static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
@@ -1405,7 +1196,6 @@ void Config::SaveRendererValues() {
1405 WriteGlobalSetting(Settings::values.bg_blue); 1196 WriteGlobalSetting(Settings::values.bg_blue);
1406 1197
1407 if (global) { 1198 if (global) {
1408 WriteBasicSetting(Settings::values.fps_cap);
1409 WriteBasicSetting(Settings::values.renderer_debug); 1199 WriteBasicSetting(Settings::values.renderer_debug);
1410 WriteBasicSetting(Settings::values.renderer_shader_feedback); 1200 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1411 WriteBasicSetting(Settings::values.enable_nsight_aftermath); 1201 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 07bfa0360..633fc295b 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -42,6 +42,7 @@ void ConfigureDebug::SetConfiguration() {
42 ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue()); 42 ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
43 ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue()); 43 ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
44 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); 44 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
45 ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
45 ui->enable_graphics_debugging->setEnabled(runtime_lock); 46 ui->enable_graphics_debugging->setEnabled(runtime_lock);
46 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 47 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
47 ui->enable_shader_feedback->setEnabled(runtime_lock); 48 ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -67,6 +68,7 @@ void ConfigureDebug::ApplyConfiguration() {
67 Settings::values.quest_flag = ui->quest_flag->isChecked(); 68 Settings::values.quest_flag = ui->quest_flag->isChecked();
68 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 69 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
69 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); 70 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
71 Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
70 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 72 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
71 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); 73 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
72 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 74 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index b884a56b0..0f3b51c8d 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -198,6 +198,13 @@
198 </property> 198 </property>
199 </widget> 199 </widget>
200 </item> 200 </item>
201 <item row="1" column="1">
202 <widget class="QCheckBox" name="enable_all_controllers">
203 <property name="text">
204 <string>Enable all Controller Types</string>
205 </property>
206 </widget>
207 </item>
201 </layout> 208 </layout>
202 </widget> 209 </widget>
203 </item> 210 </item>
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
index 31ec48384..9a8de92a1 100644
--- a/src/yuzu/configuration/configure_debug_controller.cpp
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -2,17 +2,18 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/hid/hid_core.h"
6#include "ui_configure_debug_controller.h" 6#include "ui_configure_debug_controller.h"
7#include "yuzu/configuration/configure_debug_controller.h" 7#include "yuzu/configuration/configure_debug_controller.h"
8#include "yuzu/configuration/configure_input_player.h" 8#include "yuzu/configuration/configure_input_player.h"
9 9
10ConfigureDebugController::ConfigureDebugController(QWidget* parent, 10ConfigureDebugController::ConfigureDebugController(QWidget* parent,
11 InputCommon::InputSubsystem* input_subsystem, 11 InputCommon::InputSubsystem* input_subsystem,
12 InputProfiles* profiles, Core::System& system) 12 InputProfiles* profiles,
13 Core::HID::HIDCore& hid_core, bool is_powered_on)
13 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), 14 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
14 debug_controller( 15 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles,
15 new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, true)) { 16 hid_core, is_powered_on, true)) {
16 ui->setupUi(this); 17 ui->setupUi(this);
17 18
18 ui->controllerLayout->addWidget(debug_controller); 19 ui->controllerLayout->addWidget(debug_controller);
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
index 6e17c5aa0..d716edbc2 100644
--- a/src/yuzu/configuration/configure_debug_controller.h
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -13,8 +13,8 @@ class ConfigureInputPlayer;
13 13
14class InputProfiles; 14class InputProfiles;
15 15
16namespace Core { 16namespace Core::HID {
17class System; 17class HIDCore;
18} 18}
19 19
20namespace InputCommon { 20namespace InputCommon {
@@ -30,7 +30,8 @@ class ConfigureDebugController : public QDialog {
30 30
31public: 31public:
32 explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, 32 explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
33 InputProfiles* profiles, Core::System& system); 33 InputProfiles* profiles, Core::HID::HIDCore& hid_core,
34 bool is_powered_on);
34 ~ConfigureDebugController() override; 35 ~ConfigureDebugController() override;
35 36
36 void ApplyConfiguration(); 37 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 7af3ea97e..566879317 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -30,6 +30,9 @@ ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent)
30 30
31 connect(ui->button_reset_defaults, &QPushButton::clicked, this, 31 connect(ui->button_reset_defaults, &QPushButton::clicked, this,
32 &ConfigureGeneral::ResetDefaults); 32 &ConfigureGeneral::ResetDefaults);
33
34 ui->fps_cap_label->setVisible(Settings::IsConfiguringGlobal());
35 ui->fps_cap_combobox->setVisible(!Settings::IsConfiguringGlobal());
33} 36}
34 37
35ConfigureGeneral::~ConfigureGeneral() = default; 38ConfigureGeneral::~ConfigureGeneral() = default;
@@ -57,6 +60,11 @@ void ConfigureGeneral::SetConfiguration() {
57 } else { 60 } else {
58 ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && 61 ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() &&
59 use_speed_limit != ConfigurationShared::CheckState::Global); 62 use_speed_limit != ConfigurationShared::CheckState::Global);
63
64 ui->fps_cap_combobox->setCurrentIndex(Settings::values.fps_cap.UsingGlobal() ? 0 : 1);
65 ui->fps_cap->setEnabled(!Settings::values.fps_cap.UsingGlobal());
66 ConfigurationShared::SetHighlight(ui->fps_cap_layout,
67 !Settings::values.fps_cap.UsingGlobal());
60 } 68 }
61} 69}
62 70
@@ -106,6 +114,13 @@ void ConfigureGeneral::ApplyConfiguration() {
106 Qt::Checked); 114 Qt::Checked);
107 Settings::values.speed_limit.SetValue(ui->speed_limit->value()); 115 Settings::values.speed_limit.SetValue(ui->speed_limit->value());
108 } 116 }
117
118 if (ui->fps_cap_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
119 Settings::values.fps_cap.SetGlobal(true);
120 } else {
121 Settings::values.fps_cap.SetGlobal(false);
122 Settings::values.fps_cap.SetValue(ui->fps_cap->value());
123 }
109 } 124 }
110} 125}
111 126
@@ -148,4 +163,9 @@ void ConfigureGeneral::SetupPerGameUI() {
148 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && 163 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
149 (use_speed_limit != ConfigurationShared::CheckState::Global)); 164 (use_speed_limit != ConfigurationShared::CheckState::Global));
150 }); 165 });
166
167 connect(ui->fps_cap_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
168 ui->fps_cap->setEnabled(index == 1);
169 ConfigurationShared::SetHighlight(ui->fps_cap_layout, index == 1);
170 });
151} 171}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index f9f0e3ebf..112dc72b3 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>329</width> 9 <width>744</width>
10 <height>407</height> 10 <height>568</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -28,34 +28,85 @@
28 <item> 28 <item>
29 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 29 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
30 <item> 30 <item>
31 <layout class="QHBoxLayout" name="horizontalLayout_2"> 31 <widget class="QWidget" name="fps_cap_layout" native="true">
32 <item> 32 <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
33 <widget class="QLabel" name="fps_cap_label"> 33 <property name="leftMargin">
34 <number>0</number>
35 </property>
36 <property name="topMargin">
37 <number>0</number>
38 </property>
39 <property name="rightMargin">
40 <number>0</number>
41 </property>
42 <property name="bottomMargin">
43 <number>0</number>
44 </property>
45 <item>
46 <layout class="QHBoxLayout" name="horizontalLayout_4">
47 <item>
48 <widget class="QComboBox" name="fps_cap_combobox">
49 <property name="currentText">
50 <string>Use global framerate cap</string>
51 </property>
52 <property name="currentIndex">
53 <number>0</number>
54 </property>
55 <item>
34 <property name="text"> 56 <property name="text">
35 <string>Framerate Cap</string> 57 <string>Use global framerate cap</string>
36 </property> 58 </property>
37 <property name="toolTip"> 59 </item>
38 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> 60 <item>
61 <property name="text">
62 <string>Set framerate cap:</string>
39 </property> 63 </property>
64 </item>
40 </widget> 65 </widget>
41 </item> 66 </item>
42 <item> 67 <item>
43 <widget class="QSpinBox" name="fps_cap"> 68 <widget class="QLabel" name="fps_cap_label">
44 <property name="suffix"> 69 <property name="toolTip">
45 <string>x</string> 70 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
46 </property> 71 </property>
47 <property name="minimum"> 72 <property name="text">
48 <number>1</number> 73 <string>Framerate Cap</string>
49 </property> 74 </property>
50 <property name="maximum">
51 <number>1000</number>
52 </property>
53 <property name="value">
54 <number>500</number>
55 </property>
56 </widget> 75 </widget>
57 </item> 76 </item>
77 <item>
78 <spacer name="horizontalSpacer">
79 <property name="orientation">
80 <enum>Qt::Horizontal</enum>
81 </property>
82 <property name="sizeHint" stdset="0">
83 <size>
84 <width>40</width>
85 <height>20</height>
86 </size>
87 </property>
88 </spacer>
89 </item>
90 </layout>
91 </item>
92 <item>
93 <widget class="QSpinBox" name="fps_cap">
94 <property name="suffix">
95 <string>x</string>
96 </property>
97 <property name="minimum">
98 <number>1</number>
99 </property>
100 <property name="maximum">
101 <number>1000</number>
102 </property>
103 <property name="value">
104 <number>500</number>
105 </property>
106 </widget>
107 </item>
58 </layout> 108 </layout>
109 </widget>
59 </item> 110 </item>
60 <item> 111 <item>
61 <layout class="QHBoxLayout" name="horizontalLayout_2"> 112 <layout class="QHBoxLayout" name="horizontalLayout_2">
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 1599299db..d53179dbb 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -10,6 +10,8 @@
10#include <QTimer> 10#include <QTimer>
11 11
12#include "core/core.h" 12#include "core/core.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
13#include "core/hle/service/am/am.h" 15#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applet_ae.h" 16#include "core/hle/service/am/applet_ae.h"
15#include "core/hle/service/am/applet_oe.h" 17#include "core/hle/service/am/applet_oe.h"
@@ -22,7 +24,6 @@
22#include "yuzu/configuration/configure_input_advanced.h" 24#include "yuzu/configuration/configure_input_advanced.h"
23#include "yuzu/configuration/configure_input_player.h" 25#include "yuzu/configuration/configure_input_player.h"
24#include "yuzu/configuration/configure_motion_touch.h" 26#include "yuzu/configuration/configure_motion_touch.h"
25#include "yuzu/configuration/configure_mouse_advanced.h"
26#include "yuzu/configuration/configure_touchscreen_advanced.h" 27#include "yuzu/configuration/configure_touchscreen_advanced.h"
27#include "yuzu/configuration/configure_vibration.h" 28#include "yuzu/configuration/configure_vibration.h"
28#include "yuzu/configuration/input_profiles.h" 29#include "yuzu/configuration/input_profiles.h"
@@ -75,23 +76,25 @@ ConfigureInput::~ConfigureInput() = default;
75 76
76void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, 77void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
77 std::size_t max_players) { 78 std::size_t max_players) {
79 const bool is_powered_on = system.IsPoweredOn();
80 auto& hid_core = system.HIDCore();
78 player_controllers = { 81 player_controllers = {
79 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(), 82 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(),
80 system), 83 hid_core, is_powered_on),
81 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(), 84 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(),
82 system), 85 hid_core, is_powered_on),
83 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(), 86 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(),
84 system), 87 hid_core, is_powered_on),
85 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(), 88 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(),
86 system), 89 hid_core, is_powered_on),
87 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(), 90 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(),
88 system), 91 hid_core, is_powered_on),
89 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(), 92 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(),
90 system), 93 hid_core, is_powered_on),
91 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(), 94 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(),
92 system), 95 hid_core, is_powered_on),
93 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(), 96 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(),
94 system), 97 hid_core, is_powered_on),
95 }; 98 };
96 99
97 player_tabs = { 100 player_tabs = {
@@ -114,6 +117,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
114 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); 117 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
115 player_tabs[i]->layout()->addWidget(player_controllers[i]); 118 player_tabs[i]->layout()->addWidget(player_controllers[i]);
116 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { 119 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
120 // Ensures that the controllers are always connected in sequential order
117 if (is_connected) { 121 if (is_connected) {
118 for (std::size_t index = 0; index <= i; ++index) { 122 for (std::size_t index = 0; index <= i; ++index) {
119 player_connected[index]->setChecked(is_connected); 123 player_connected[index]->setChecked(is_connected);
@@ -146,13 +150,12 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
146 advanced = new ConfigureInputAdvanced(this); 150 advanced = new ConfigureInputAdvanced(this);
147 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); 151 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
148 ui->tabAdvanced->layout()->addWidget(advanced); 152 ui->tabAdvanced->layout()->addWidget(advanced);
149 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { 153
150 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get(), 154 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog,
151 system); 155 [this, input_subsystem, &hid_core, is_powered_on] {
152 }); 156 CallConfigureDialog<ConfigureDebugController>(
153 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { 157 *this, input_subsystem, profiles.get(), hid_core, is_powered_on);
154 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); 158 });
155 });
156 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, 159 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
157 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); 160 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
158 connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog, 161 connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog,
@@ -184,22 +187,8 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {
184void ConfigureInput::ApplyConfiguration() { 187void ConfigureInput::ApplyConfiguration() {
185 for (auto* controller : player_controllers) { 188 for (auto* controller : player_controllers) {
186 controller->ApplyConfiguration(); 189 controller->ApplyConfiguration();
187 controller->TryDisconnectSelectedController();
188 }
189
190 // This emulates a delay between disconnecting and reconnecting controllers as some games
191 // do not respond to a change in controller type if it was instantaneous.
192 using namespace std::chrono_literals;
193 std::this_thread::sleep_for(150ms);
194
195 for (auto* controller : player_controllers) {
196 controller->TryConnectSelectedController();
197 } 190 }
198 191
199 // This emulates a delay between disconnecting and reconnecting controllers as some games
200 // do not respond to a change in controller type if it was instantaneous.
201 std::this_thread::sleep_for(150ms);
202
203 advanced->ApplyConfiguration(); 192 advanced->ApplyConfiguration();
204 193
205 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); 194 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
@@ -223,8 +212,10 @@ void ConfigureInput::RetranslateUI() {
223} 212}
224 213
225void ConfigureInput::LoadConfiguration() { 214void ConfigureInput::LoadConfiguration() {
215 const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
216
226 LoadPlayerControllerIndices(); 217 LoadPlayerControllerIndices();
227 UpdateDockedState(Settings::values.players.GetValue()[8].connected); 218 UpdateDockedState(handheld->IsConnected());
228 219
229 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); 220 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
230 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); 221 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -232,9 +223,16 @@ void ConfigureInput::LoadConfiguration() {
232 223
233void ConfigureInput::LoadPlayerControllerIndices() { 224void ConfigureInput::LoadPlayerControllerIndices() {
234 for (std::size_t i = 0; i < player_connected.size(); ++i) { 225 for (std::size_t i = 0; i < player_connected.size(); ++i) {
235 const auto connected = Settings::values.players.GetValue()[i].connected || 226 if (i == 0) {
236 (i == 0 && Settings::values.players.GetValue()[8].connected); 227 auto* handheld =
237 player_connected[i]->setChecked(connected); 228 system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
229 if (handheld->IsConnected()) {
230 player_connected[i]->setChecked(true);
231 continue;
232 }
233 }
234 const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i);
235 player_connected[i]->setChecked(controller->IsConnected());
238 } 236 }
239} 237}
240 238
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index b30f09013..65c8e59ac 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -82,7 +82,6 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
82 82
83 connect(ui->debug_configure, &QPushButton::clicked, this, 83 connect(ui->debug_configure, &QPushButton::clicked, this,
84 [this] { CallDebugControllerDialog(); }); 84 [this] { CallDebugControllerDialog(); });
85 connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
86 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 85 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
87 [this] { CallTouchscreenConfigDialog(); }); 86 [this] { CallTouchscreenConfigDialog(); });
88 connect(ui->buttonMotionTouch, &QPushButton::clicked, this, 87 connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
@@ -131,6 +130,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
131 static_cast<float>(ui->mouse_panning_sensitivity->value()); 130 static_cast<float>(ui->mouse_panning_sensitivity->value());
132 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); 131 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
133 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); 132 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
133 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
134} 134}
135 135
136void ConfigureInputAdvanced::LoadConfiguration() { 136void ConfigureInputAdvanced::LoadConfiguration() {
@@ -161,6 +161,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
161 ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue()); 161 ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
162 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); 162 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
163 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); 163 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
164 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
164 165
165 UpdateUIEnabled(); 166 UpdateUIEnabled();
166} 167}
@@ -178,7 +179,8 @@ void ConfigureInputAdvanced::RetranslateUI() {
178} 179}
179 180
180void ConfigureInputAdvanced::UpdateUIEnabled() { 181void ConfigureInputAdvanced::UpdateUIEnabled() {
181 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
182 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); 182 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
183 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); 183 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
184 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
185 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
184} 186}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 9095206a0..df0e4d602 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2528,11 +2528,11 @@
2528 <number>0</number> 2528 <number>0</number>
2529 </property> 2529 </property>
2530 <item> 2530 <item>
2531 <widget class="QGroupBox" name="gridGroupBox_3"> 2531 <widget class="QGroupBox" name="emulatedDevicesGroupBox">
2532 <property name="title"> 2532 <property name="title">
2533 <string>Other</string> 2533 <string>Emulated Devices</string>
2534 </property> 2534 </property>
2535 <layout class="QGridLayout" name="gridLayout_3"> 2535 <layout class="QGridLayout" name="emulatedDevicesGridLayout">
2536 <item row="0" column="0"> 2536 <item row="0" column="0">
2537 <widget class="QCheckBox" name="keyboard_enabled"> 2537 <widget class="QCheckBox" name="keyboard_enabled">
2538 <property name="minimumSize"> 2538 <property name="minimumSize">
@@ -2547,7 +2547,7 @@
2547 </widget> 2547 </widget>
2548 </item> 2548 </item>
2549 <item row="1" column="0"> 2549 <item row="1" column="0">
2550 <widget class="QCheckBox" name="emulate_analog_keyboard"> 2550 <widget class="QCheckBox" name="mouse_enabled">
2551 <property name="minimumSize"> 2551 <property name="minimumSize">
2552 <size> 2552 <size>
2553 <width>0</width> 2553 <width>0</width>
@@ -2555,53 +2555,18 @@
2555 </size> 2555 </size>
2556 </property> 2556 </property>
2557 <property name="text"> 2557 <property name="text">
2558 <string>Emulate Analog with Keyboard Input</string> 2558 <string>Mouse</string>
2559 </property> 2559 </property>
2560 </widget> 2560 </widget>
2561 </item> 2561 </item>
2562 <item row="2" column="0"> 2562 <item row="2" column="0">
2563 <widget class="QCheckBox" name="mouse_panning"> 2563 <widget class="QCheckBox" name="touchscreen_enabled">
2564 <property name="minimumSize">
2565 <size>
2566 <width>0</width>
2567 <height>23</height>
2568 </size>
2569 </property>
2570 <property name="text"> 2564 <property name="text">
2571 <string>Enable mouse panning</string> 2565 <string>Touchscreen</string>
2572 </property> 2566 </property>
2573 </widget> 2567 </widget>
2574 </item> 2568 </item>
2575 <item row="2" column="2"> 2569 <item row="2" column="1">
2576 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2577 <property name="toolTip">
2578 <string>Mouse sensitivity</string>
2579 </property>
2580 <property name="alignment">
2581 <set>Qt::AlignCenter</set>
2582 </property>
2583 <property name="suffix">
2584 <string>%</string>
2585 </property>
2586 <property name="minimum">
2587 <number>1</number>
2588 </property>
2589 <property name="maximum">
2590 <number>100</number>
2591 </property>
2592 <property name="value">
2593 <number>100</number>
2594 </property>
2595 </widget>
2596 </item>
2597 <item row="6" column="2">
2598 <widget class="QPushButton" name="touchscreen_advanced">
2599 <property name="text">
2600 <string>Advanced</string>
2601 </property>
2602 </widget>
2603 </item>
2604 <item row="3" column="1">
2605 <spacer name="horizontalSpacer_8"> 2570 <spacer name="horizontalSpacer_8">
2606 <property name="orientation"> 2571 <property name="orientation">
2607 <enum>Qt::Horizontal</enum> 2572 <enum>Qt::Horizontal</enum>
@@ -2617,80 +2582,130 @@
2617 </property> 2582 </property>
2618 </spacer> 2583 </spacer>
2619 </item> 2584 </item>
2620 <item row="3" column="2"> 2585 <item row="2" column="2">
2621 <widget class="QPushButton" name="mouse_advanced"> 2586 <widget class="QPushButton" name="touchscreen_advanced">
2622 <property name="text"> 2587 <property name="text">
2623 <string>Advanced</string> 2588 <string>Advanced</string>
2624 </property> 2589 </property>
2625 </widget> 2590 </widget>
2626 </item> 2591 </item>
2627 <item row="6" column="0">
2628 <widget class="QCheckBox" name="touchscreen_enabled">
2629 <property name="text">
2630 <string>Touchscreen</string>
2631 </property>
2632 </widget>
2633 </item>
2634 <item row="3" column="0"> 2592 <item row="3" column="0">
2635 <widget class="QCheckBox" name="mouse_enabled">
2636 <property name="minimumSize">
2637 <size>
2638 <width>0</width>
2639 <height>23</height>
2640 </size>
2641 </property>
2642 <property name="text">
2643 <string>Mouse</string>
2644 </property>
2645 </widget>
2646 </item>
2647 <item row="8" column="0">
2648 <widget class="QLabel" name="motion_touch">
2649 <property name="text">
2650 <string>Motion / Touch</string>
2651 </property>
2652 </widget>
2653 </item>
2654 <item row="8" column="2">
2655 <widget class="QPushButton" name="buttonMotionTouch">
2656 <property name="text">
2657 <string>Configure</string>
2658 </property>
2659 </widget>
2660 </item>
2661 <item row="7" column="0">
2662 <widget class="QCheckBox" name="debug_enabled"> 2593 <widget class="QCheckBox" name="debug_enabled">
2663 <property name="text"> 2594 <property name="text">
2664 <string>Debug Controller</string> 2595 <string>Debug Controller</string>
2665 </property> 2596 </property>
2666 </widget> 2597 </widget>
2667 </item> 2598 </item>
2668 <item row="7" column="2"> 2599 <item row="3" column="2">
2669 <widget class="QPushButton" name="debug_configure"> 2600 <widget class="QPushButton" name="debug_configure">
2670 <property name="text"> 2601 <property name="text">
2671 <string>Configure</string> 2602 <string>Configure</string>
2672 </property> 2603 </property>
2673 </widget> 2604 </widget>
2674 </item> 2605 </item>
2675 <item row="9" column="0">
2676 <widget class="QCheckBox" name="enable_raw_input">
2677 <property name="toolTip">
2678 <string>Requires restarting yuzu</string>
2679 </property>
2680 <property name="minimumSize">
2681 <size>
2682 <width>0</width>
2683 <height>23</height>
2684 </size>
2685 </property>
2686 <property name="text">
2687 <string>Enable XInput 8 player support (disables web applet)</string>
2688 </property>
2689 </widget>
2690 </item>
2691 </layout> 2606 </layout>
2692 </widget> 2607 </widget>
2693 </item> 2608 </item>
2609 <item>
2610 <widget class="QGroupBox" name="otherGroupBox">
2611 <property name="title">
2612 <string>Other</string>
2613 </property>
2614 <layout class="QGridLayout" name="OtherGridLayout">
2615 <item row="1" column="0">
2616 <widget class="QCheckBox" name="emulate_analog_keyboard">
2617 <property name="minimumSize">
2618 <size>
2619 <width>0</width>
2620 <height>23</height>
2621 </size>
2622 </property>
2623 <property name="text">
2624 <string>Emulate Analog with Keyboard Input</string>
2625 </property>
2626 </widget>
2627 </item>
2628 <item row="2" column="0">
2629 <widget class="QCheckBox" name="enable_raw_input">
2630 <property name="toolTip">
2631 <string>Requires restarting yuzu</string>
2632 </property>
2633 <property name="minimumSize">
2634 <size>
2635 <width>0</width>
2636 <height>23</height>
2637 </size>
2638 </property>
2639 <property name="text">
2640 <string>Enable XInput 8 player support (disables web applet)</string>
2641 </property>
2642 </widget>
2643 </item>
2644 <item row="3" column="0">
2645 <widget class="QCheckBox" name="enable_udp_controller">
2646 <property name="minimumSize">
2647 <size>
2648 <width>0</width>
2649 <height>23</height>
2650 </size>
2651 </property>
2652 <property name="text">
2653 <string>Enable UDP controllers (not needed for motion)</string>
2654 </property>
2655 </widget>
2656 </item>
2657 <item row="4" column="0">
2658 <widget class="QCheckBox" name="mouse_panning">
2659 <property name="minimumSize">
2660 <size>
2661 <width>0</width>
2662 <height>23</height>
2663 </size>
2664 </property>
2665 <property name="text">
2666 <string>Enable mouse panning</string>
2667 </property>
2668 </widget>
2669 </item>
2670 <item row="4" column="2">
2671 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2672 <property name="toolTip">
2673 <string>Mouse sensitivity</string>
2674 </property>
2675 <property name="alignment">
2676 <set>Qt::AlignCenter</set>
2677 </property>
2678 <property name="suffix">
2679 <string>%</string>
2680 </property>
2681 <property name="minimum">
2682 <number>1</number>
2683 </property>
2684 <property name="maximum">
2685 <number>100</number>
2686 </property>
2687 <property name="value">
2688 <number>100</number>
2689 </property>
2690 </widget>
2691 </item>
2692 <item row="5" column="0">
2693 <widget class="QLabel" name="motion_touch">
2694 <property name="text">
2695 <string>Motion / Touch</string>
2696 </property>
2697 </widget>
2698 </item>
2699 <item row="5" column="2">
2700 <widget class="QPushButton" name="buttonMotionTouch">
2701 <property name="text">
2702 <string>Configure</string>
2703 </property>
2704 </widget>
2705 </item>
2706 </layout>
2707 </widget>
2708 </item>
2694 <item> 2709 <item>
2695 <spacer name="verticalSpacer"> 2710 <spacer name="verticalSpacer">
2696 <property name="orientation"> 2711 <property name="orientation">
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 3aab5d5f8..16284d5a6 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -12,14 +12,12 @@
12#include <QMessageBox> 12#include <QMessageBox>
13#include <QTimer> 13#include <QTimer>
14#include "common/param_package.h" 14#include "common/param_package.h"
15#include "core/core.h" 15#include "core/hid/emulated_controller.h"
16#include "core/hle/service/hid/controllers/npad.h" 16#include "core/hid/hid_core.h"
17#include "core/hle/service/hid/hid.h" 17#include "core/hid/hid_types.h"
18#include "core/hle/service/sm/sm.h" 18#include "input_common/drivers/keyboard.h"
19#include "input_common/gcadapter/gc_poller.h" 19#include "input_common/drivers/mouse.h"
20#include "input_common/main.h" 20#include "input_common/main.h"
21#include "input_common/mouse/mouse_poller.h"
22#include "input_common/udp/udp.h"
23#include "ui_configure_input_player.h" 21#include "ui_configure_input_player.h"
24#include "yuzu/bootmanager.h" 22#include "yuzu/bootmanager.h"
25#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
@@ -29,8 +27,6 @@
29#include "yuzu/configuration/input_profiles.h" 27#include "yuzu/configuration/input_profiles.h"
30#include "yuzu/util/limitable_input_dialog.h" 28#include "yuzu/util/limitable_input_dialog.h"
31 29
32using namespace Service::HID;
33
34const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 30const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
35 ConfigureInputPlayer::analog_sub_buttons{{ 31 ConfigureInputPlayer::analog_sub_buttons{{
36 "up", 32 "up",
@@ -41,33 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
41 37
42namespace { 38namespace {
43 39
44constexpr std::size_t HANDHELD_INDEX = 8;
45
46void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
47 bool connected, Core::System& system) {
48 if (!system.IsPoweredOn()) {
49 return;
50 }
51 Service::SM::ServiceManager& sm = system.ServiceManager();
52
53 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
54 HidController::NPad);
55
56 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
57}
58
59QString GetKeyName(int key_code) { 40QString GetKeyName(int key_code) {
60 switch (key_code) { 41 switch (key_code) {
61 case Qt::LeftButton:
62 return QObject::tr("Click 0");
63 case Qt::RightButton:
64 return QObject::tr("Click 1");
65 case Qt::MiddleButton:
66 return QObject::tr("Click 2");
67 case Qt::BackButton:
68 return QObject::tr("Click 3");
69 case Qt::ForwardButton:
70 return QObject::tr("Click 4");
71 case Qt::Key_Shift: 42 case Qt::Key_Shift:
72 return QObject::tr("Shift"); 43 return QObject::tr("Shift");
73 case Qt::Key_Control: 44 case Qt::Key_Control:
@@ -81,6 +52,61 @@ QString GetKeyName(int key_code) {
81 } 52 }
82} 53}
83 54
55QString GetButtonName(Common::Input::ButtonNames button_name) {
56 switch (button_name) {
57 case Common::Input::ButtonNames::ButtonLeft:
58 return QObject::tr("Left");
59 case Common::Input::ButtonNames::ButtonRight:
60 return QObject::tr("Right");
61 case Common::Input::ButtonNames::ButtonDown:
62 return QObject::tr("Down");
63 case Common::Input::ButtonNames::ButtonUp:
64 return QObject::tr("Up");
65 case Common::Input::ButtonNames::TriggerZ:
66 return QObject::tr("Z");
67 case Common::Input::ButtonNames::TriggerR:
68 return QObject::tr("R");
69 case Common::Input::ButtonNames::TriggerL:
70 return QObject::tr("L");
71 case Common::Input::ButtonNames::ButtonA:
72 return QObject::tr("A");
73 case Common::Input::ButtonNames::ButtonB:
74 return QObject::tr("B");
75 case Common::Input::ButtonNames::ButtonX:
76 return QObject::tr("X");
77 case Common::Input::ButtonNames::ButtonY:
78 return QObject::tr("Y");
79 case Common::Input::ButtonNames::ButtonStart:
80 return QObject::tr("Start");
81 case Common::Input::ButtonNames::L1:
82 return QObject::tr("L1");
83 case Common::Input::ButtonNames::L2:
84 return QObject::tr("L2");
85 case Common::Input::ButtonNames::L3:
86 return QObject::tr("L3");
87 case Common::Input::ButtonNames::R1:
88 return QObject::tr("R1");
89 case Common::Input::ButtonNames::R2:
90 return QObject::tr("R2");
91 case Common::Input::ButtonNames::R3:
92 return QObject::tr("R3");
93 case Common::Input::ButtonNames::Circle:
94 return QObject::tr("Circle");
95 case Common::Input::ButtonNames::Cross:
96 return QObject::tr("Cross");
97 case Common::Input::ButtonNames::Square:
98 return QObject::tr("Square");
99 case Common::Input::ButtonNames::Triangle:
100 return QObject::tr("Triangle");
101 case Common::Input::ButtonNames::Share:
102 return QObject::tr("Share");
103 case Common::Input::ButtonNames::Options:
104 return QObject::tr("Options");
105 default:
106 return QObject::tr("[undefined]");
107 }
108}
109
84void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, 110void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
85 const std::string& button_name) { 111 const std::string& button_name) {
86 // The poller returned a complete axis, so set all the buttons 112 // The poller returned a complete axis, so set all the buttons
@@ -97,95 +123,75 @@ void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackag
97 } 123 }
98 analog_param.Set(button_name, input_param.Serialize()); 124 analog_param.Set(button_name, input_param.Serialize());
99} 125}
126} // namespace
100 127
101QString ButtonToText(const Common::ParamPackage& param) { 128QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
102 if (!param.Has("engine")) { 129 if (!param.Has("engine")) {
103 return QObject::tr("[not set]"); 130 return QObject::tr("[not set]");
104 } 131 }
105 132
133 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
134 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
135 const auto common_button_name = input_subsystem->GetButtonName(param);
136
137 // Retrieve the names from Qt
106 if (param.Get("engine", "") == "keyboard") { 138 if (param.Get("engine", "") == "keyboard") {
107 const QString button_str = GetKeyName(param.Get("code", 0)); 139 const QString button_str = GetKeyName(param.Get("code", 0));
108 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
109 return QObject::tr("%1%2").arg(toggle, button_str); 140 return QObject::tr("%1%2").arg(toggle, button_str);
110 } 141 }
111 142
112 if (param.Get("engine", "") == "gcpad") { 143 if (common_button_name == Common::Input::ButtonNames::Invalid) {
113 if (param.Has("axis")) { 144 return QObject::tr("[invalid]");
114 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
115 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
116
117 return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
118 }
119 if (param.Has("button")) {
120 const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
121 return QObject::tr("GC Button %1").arg(button_str);
122 }
123 return GetKeyName(param.Get("code", 0));
124 }
125
126 if (param.Get("engine", "") == "tas") {
127 if (param.Has("axis")) {
128 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
129
130 return QObject::tr("TAS Axis %1").arg(axis_str);
131 }
132 if (param.Has("button")) {
133 const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
134 return QObject::tr("TAS Btn %1").arg(button_str);
135 }
136 return GetKeyName(param.Get("code", 0));
137 } 145 }
138 146
139 if (param.Get("engine", "") == "cemuhookudp") { 147 if (common_button_name == Common::Input::ButtonNames::Engine) {
140 if (param.Has("pad_index")) { 148 return QString::fromStdString(param.Get("engine", ""));
141 const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
142 return QObject::tr("Motion %1").arg(motion_str);
143 }
144 return GetKeyName(param.Get("code", 0));
145 } 149 }
146 150
147 if (param.Get("engine", "") == "sdl") { 151 if (common_button_name == Common::Input::ButtonNames::Value) {
148 if (param.Has("hat")) { 152 if (param.Has("hat")) {
149 const QString hat_str = QString::fromStdString(param.Get("hat", "")); 153 const QString hat = QString::fromStdString(param.Get("direction", ""));
150 const QString direction_str = QString::fromStdString(param.Get("direction", "")); 154 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
151
152 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
153 } 155 }
154
155 if (param.Has("axis")) { 156 if (param.Has("axis")) {
156 const QString axis_str = QString::fromStdString(param.Get("axis", "")); 157 const QString axis = QString::fromStdString(param.Get("axis", ""));
157 const QString direction_str = QString::fromStdString(param.Get("direction", "")); 158 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
158
159 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
160 } 159 }
161 160 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
162 if (param.Has("button")) { 161 const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
163 const QString button_str = QString::fromStdString(param.Get("button", "")); 162 const QString axis_y = QString::fromStdString(param.Get("axis_y", ""));
164 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 163 const QString axis_z = QString::fromStdString(param.Get("axis_z", ""));
165 164 return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z);
166 return QObject::tr("%1Button %2").arg(toggle, button_str);
167 } 165 }
168
169 if (param.Has("motion")) { 166 if (param.Has("motion")) {
170 return QObject::tr("SDL Motion"); 167 const QString motion = QString::fromStdString(param.Get("motion", ""));
168 return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion);
171 } 169 }
172
173 return {};
174 }
175
176 if (param.Get("engine", "") == "mouse") {
177 if (param.Has("button")) { 170 if (param.Has("button")) {
178 const QString button_str = QString::number(int(param.Get("button", 0))); 171 const QString button = QString::fromStdString(param.Get("button", ""));
179 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 172 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
180 return QObject::tr("%1Click %2").arg(toggle, button_str);
181 } 173 }
182 return GetKeyName(param.Get("code", 0)); 174 }
175
176 QString button_name = GetButtonName(common_button_name);
177 if (param.Has("hat")) {
178 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
179 }
180 if (param.Has("axis")) {
181 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
182 }
183 if (param.Has("motion")) {
184 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
185 }
186 if (param.Has("button")) {
187 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
183 } 188 }
184 189
185 return QObject::tr("[unknown]"); 190 return QObject::tr("[unknown]");
186} 191}
187 192
188QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { 193QString ConfigureInputPlayer::AnalogToText(const Common::ParamPackage& param,
194 const std::string& dir) {
189 if (!param.Has("engine")) { 195 if (!param.Has("engine")) {
190 return QObject::tr("[not set]"); 196 return QObject::tr("[not set]");
191 } 197 }
@@ -194,49 +200,69 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
194 return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); 200 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
195 } 201 }
196 202
203 if (!param.Has("axis_x") || !param.Has("axis_y")) {
204 return QObject::tr("[unknown]");
205 }
206
197 const auto engine_str = param.Get("engine", ""); 207 const auto engine_str = param.Get("engine", "");
198 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); 208 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
199 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); 209 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
200 const bool invert_x = param.Get("invert_x", "+") == "-"; 210 const bool invert_x = param.Get("invert_x", "+") == "-";
201 const bool invert_y = param.Get("invert_y", "+") == "-"; 211 const bool invert_y = param.Get("invert_y", "+") == "-";
202 if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse" ||
203 engine_str == "tas") {
204 if (dir == "modifier") {
205 return QObject::tr("[unused]");
206 }
207 212
208 if (dir == "left") { 213 if (dir == "modifier") {
209 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-"); 214 return QObject::tr("[unused]");
210 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); 215 }
211 }
212 if (dir == "right") {
213 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
214 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
215 }
216 if (dir == "up") {
217 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
218 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
219 }
220 if (dir == "down") {
221 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
222 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
223 }
224 216
225 return {}; 217 if (dir == "left") {
218 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
219 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
220 }
221 if (dir == "right") {
222 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
223 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
224 }
225 if (dir == "up") {
226 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
227 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
226 } 228 }
229 if (dir == "down") {
230 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
231 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
232 }
233
227 return QObject::tr("[unknown]"); 234 return QObject::tr("[unknown]");
228} 235}
229} // namespace
230 236
231ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, 237ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
232 QWidget* bottom_row, 238 QWidget* bottom_row,
233 InputCommon::InputSubsystem* input_subsystem_, 239 InputCommon::InputSubsystem* input_subsystem_,
234 InputProfiles* profiles_, Core::System& system_, 240 InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
235 bool debug) 241 bool is_powered_on_, bool debug)
236 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 242 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
237 debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), 243 debug(debug), is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_},
238 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), 244 profiles(profiles_), timeout_timer(std::make_unique<QTimer>()),
239 bottom_row(bottom_row), system{system_} { 245 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row), hid_core{hid_core_} {
246 if (player_index == 0) {
247 auto* emulated_controller_p1 =
248 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
249 auto* emulated_controller_hanheld =
250 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
251 emulated_controller_p1->SaveCurrentConfig();
252 emulated_controller_p1->EnableConfiguration();
253 emulated_controller_hanheld->SaveCurrentConfig();
254 emulated_controller_hanheld->EnableConfiguration();
255 if (emulated_controller_hanheld->IsConnected(true)) {
256 emulated_controller_p1->Disconnect();
257 emulated_controller = emulated_controller_hanheld;
258 } else {
259 emulated_controller = emulated_controller_p1;
260 }
261 } else {
262 emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index);
263 emulated_controller->SaveCurrentConfig();
264 emulated_controller->EnableConfiguration();
265 }
240 ui->setupUi(this); 266 ui->setupUi(this);
241 267
242 setFocusPolicy(Qt::ClickFocus); 268 setFocusPolicy(Qt::ClickFocus);
@@ -278,31 +304,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
278 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; 304 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
279 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; 305 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
280 306
281 const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id, 307 ui->controllerFrame->SetController(emulated_controller);
282 Common::ParamPackage* param, int default_val,
283 InputCommon::Polling::DeviceType type) {
284 connect(button, &QPushButton::clicked, [=, this] {
285 HandleClick(
286 button, button_id,
287 [=, this](Common::ParamPackage params) {
288 // Workaround for ZL & ZR for analog triggers like on XBOX
289 // controllers. Analog triggers (from controllers like the XBOX
290 // controller) would not work due to a different range of their
291 // signals (from 0 to 255 on analog triggers instead of -32768 to
292 // 32768 on analog joysticks). The SDL driver misinterprets analog
293 // triggers as analog joysticks.
294 // TODO: reinterpret the signal range for analog triggers to map the
295 // values correctly. This is required for the correct emulation of
296 // the analog triggers of the GameCube controller.
297 if (button == ui->buttonZL || button == ui->buttonZR) {
298 params.Set("direction", "+");
299 params.Set("threshold", "0.5");
300 }
301 *param = std::move(params);
302 },
303 type);
304 });
305 };
306 308
307 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 309 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
308 auto* const button = button_map[button_id]; 310 auto* const button = button_map[button_id];
@@ -311,34 +313,52 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
311 continue; 313 continue;
312 } 314 }
313 315
314 ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id], 316 connect(button, &QPushButton::clicked, [=, this] {
315 Config::default_buttons[button_id], 317 HandleClick(
316 InputCommon::Polling::DeviceType::Button); 318 button, button_id,
319 [=, this](Common::ParamPackage params) {
320 emulated_controller->SetButtonParam(button_id, params);
321 },
322 InputCommon::Polling::InputType::Button);
323 });
317 324
318 button->setContextMenuPolicy(Qt::CustomContextMenu); 325 button->setContextMenuPolicy(Qt::CustomContextMenu);
319 connect(button, &QPushButton::customContextMenuRequested, 326 connect(button, &QPushButton::customContextMenuRequested,
320 [=, this](const QPoint& menu_location) { 327 [=, this](const QPoint& menu_location) {
321 QMenu context_menu; 328 QMenu context_menu;
329 Common::ParamPackage param = emulated_controller->GetButtonParam(button_id);
322 context_menu.addAction(tr("Clear"), [&] { 330 context_menu.addAction(tr("Clear"), [&] {
323 buttons_param[button_id].Clear(); 331 emulated_controller->SetButtonParam(button_id, {});
324 button_map[button_id]->setText(tr("[not set]")); 332 button_map[button_id]->setText(tr("[not set]"));
325 }); 333 });
326 if (buttons_param[button_id].Has("toggle")) { 334 if (param.Has("button") || param.Has("hat")) {
327 context_menu.addAction(tr("Toggle button"), [&] { 335 context_menu.addAction(tr("Toggle button"), [&] {
328 const bool toggle_value = 336 const bool toggle_value = !param.Get("toggle", false);
329 !buttons_param[button_id].Get("toggle", false); 337 param.Set("toggle", toggle_value);
330 buttons_param[button_id].Set("toggle", toggle_value); 338 button_map[button_id]->setText(ButtonToText(param));
331 button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); 339 emulated_controller->SetButtonParam(button_id, param);
340 });
341 context_menu.addAction(tr("Invert button"), [&] {
342 const bool toggle_value = !param.Get("inverted", false);
343 param.Set("inverted", toggle_value);
344 button_map[button_id]->setText(ButtonToText(param));
345 emulated_controller->SetButtonParam(button_id, param);
332 }); 346 });
333 } 347 }
334 if (buttons_param[button_id].Has("threshold")) { 348 if (param.Has("axis")) {
349 context_menu.addAction(tr("Invert axis"), [&] {
350 const bool toggle_value = !(param.Get("invert", "+") == "-");
351 param.Set("invert", toggle_value ? "-" : "+");
352 button_map[button_id]->setText(ButtonToText(param));
353 emulated_controller->SetButtonParam(button_id, param);
354 });
335 context_menu.addAction(tr("Set threshold"), [&] { 355 context_menu.addAction(tr("Set threshold"), [&] {
336 const int button_threshold = static_cast<int>( 356 const int button_threshold =
337 buttons_param[button_id].Get("threshold", 0.5f) * 100.0f); 357 static_cast<int>(param.Get("threshold", 0.5f) * 100.0f);
338 const int new_threshold = QInputDialog::getInt( 358 const int new_threshold = QInputDialog::getInt(
339 this, tr("Set threshold"), tr("Choose a value between 0% and 100%"), 359 this, tr("Set threshold"), tr("Choose a value between 0% and 100%"),
340 button_threshold, 0, 100); 360 button_threshold, 0, 100);
341 buttons_param[button_id].Set("threshold", new_threshold / 100.0f); 361 param.Set("threshold", new_threshold / 100.0f);
342 362
343 if (button_id == Settings::NativeButton::ZL) { 363 if (button_id == Settings::NativeButton::ZL) {
344 ui->sliderZLThreshold->setValue(new_threshold); 364 ui->sliderZLThreshold->setValue(new_threshold);
@@ -346,11 +366,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
346 if (button_id == Settings::NativeButton::ZR) { 366 if (button_id == Settings::NativeButton::ZR) {
347 ui->sliderZRThreshold->setValue(new_threshold); 367 ui->sliderZRThreshold->setValue(new_threshold);
348 } 368 }
369 emulated_controller->SetButtonParam(button_id, param);
349 }); 370 });
350 } 371 }
351
352 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); 372 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
353 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
354 }); 373 });
355 } 374 }
356 375
@@ -360,9 +379,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
360 continue; 379 continue;
361 } 380 }
362 381
363 ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id], 382 connect(button, &QPushButton::clicked, [=, this] {
364 Config::default_motions[motion_id], 383 HandleClick(
365 InputCommon::Polling::DeviceType::Motion); 384 button, motion_id,
385 [=, this](Common::ParamPackage params) {
386 emulated_controller->SetMotionParam(motion_id, params);
387 },
388 InputCommon::Polling::InputType::Motion);
389 });
366 390
367 button->setContextMenuPolicy(Qt::CustomContextMenu); 391 button->setContextMenuPolicy(Qt::CustomContextMenu);
368 392
@@ -370,7 +394,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
370 [=, this](const QPoint& menu_location) { 394 [=, this](const QPoint& menu_location) {
371 QMenu context_menu; 395 QMenu context_menu;
372 context_menu.addAction(tr("Clear"), [&] { 396 context_menu.addAction(tr("Clear"), [&] {
373 motions_param[motion_id].Clear(); 397 emulated_controller->SetMotionParam(motion_id, {});
374 motion_map[motion_id]->setText(tr("[not set]")); 398 motion_map[motion_id]->setText(tr("[not set]"));
375 }); 399 });
376 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); 400 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
@@ -378,16 +402,22 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
378 } 402 }
379 403
380 connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] { 404 connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] {
381 if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { 405 Common::ParamPackage param =
406 emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
407 if (param.Has("threshold")) {
382 const auto slider_value = ui->sliderZLThreshold->value(); 408 const auto slider_value = ui->sliderZLThreshold->value();
383 buttons_param[Settings::NativeButton::ZL].Set("threshold", slider_value / 100.0f); 409 param.Set("threshold", slider_value / 100.0f);
410 emulated_controller->SetButtonParam(Settings::NativeButton::ZL, param);
384 } 411 }
385 }); 412 });
386 413
387 connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] { 414 connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] {
388 if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { 415 Common::ParamPackage param =
416 emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
417 if (param.Has("threshold")) {
389 const auto slider_value = ui->sliderZRThreshold->value(); 418 const auto slider_value = ui->sliderZRThreshold->value();
390 buttons_param[Settings::NativeButton::ZR].Set("threshold", slider_value / 100.0f); 419 param.Set("threshold", slider_value / 100.0f);
420 emulated_controller->SetButtonParam(Settings::NativeButton::ZR, param);
391 } 421 }
392 }); 422 });
393 423
@@ -415,45 +445,45 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
415 HandleClick( 445 HandleClick(
416 analog_map_buttons[analog_id][sub_button_id], analog_id, 446 analog_map_buttons[analog_id][sub_button_id], analog_id,
417 [=, this](const Common::ParamPackage& params) { 447 [=, this](const Common::ParamPackage& params) {
418 SetAnalogParam(params, analogs_param[analog_id], 448 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
419 analog_sub_buttons[sub_button_id]); 449 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
450 emulated_controller->SetStickParam(analog_id, param);
420 }, 451 },
421 InputCommon::Polling::DeviceType::AnalogPreferred); 452 InputCommon::Polling::InputType::Stick);
422 }); 453 });
423 454
424 analog_button->setContextMenuPolicy(Qt::CustomContextMenu); 455 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
425 456
426 connect( 457 connect(analog_button, &QPushButton::customContextMenuRequested,
427 analog_button, &QPushButton::customContextMenuRequested, 458 [=, this](const QPoint& menu_location) {
428 [=, this](const QPoint& menu_location) { 459 QMenu context_menu;
429 QMenu context_menu; 460 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
430 context_menu.addAction(tr("Clear"), [&] { 461 context_menu.addAction(tr("Clear"), [&] {
431 analogs_param[analog_id].Clear(); 462 emulated_controller->SetStickParam(analog_id, {});
432 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); 463 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
433 }); 464 });
434 context_menu.addAction(tr("Invert axis"), [&] { 465 context_menu.addAction(tr("Invert axis"), [&] {
435 if (sub_button_id == 2 || sub_button_id == 3) { 466 if (sub_button_id == 2 || sub_button_id == 3) {
436 const bool invert_value = 467 const bool invert_value = param.Get("invert_x", "+") == "-";
437 analogs_param[analog_id].Get("invert_x", "+") == "-"; 468 const std::string invert_str = invert_value ? "+" : "-";
438 const std::string invert_str = invert_value ? "+" : "-"; 469 param.Set("invert_x", invert_str);
439 analogs_param[analog_id].Set("invert_x", invert_str); 470 emulated_controller->SetStickParam(analog_id, param);
440 } 471 }
441 if (sub_button_id == 0 || sub_button_id == 1) { 472 if (sub_button_id == 0 || sub_button_id == 1) {
442 const bool invert_value = 473 const bool invert_value = param.Get("invert_y", "+") == "-";
443 analogs_param[analog_id].Get("invert_y", "+") == "-"; 474 const std::string invert_str = invert_value ? "+" : "-";
444 const std::string invert_str = invert_value ? "+" : "-"; 475 param.Set("invert_y", invert_str);
445 analogs_param[analog_id].Set("invert_y", invert_str); 476 emulated_controller->SetStickParam(analog_id, param);
446 } 477 }
447 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; 478 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
448 ++sub_button_id) { 479 ++sub_button_id) {
449 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( 480 analog_map_buttons[analog_id][sub_button_id]->setText(
450 analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 481 AnalogToText(param, analog_sub_buttons[sub_button_id]));
451 } 482 }
483 });
484 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
485 menu_location));
452 }); 486 });
453 context_menu.exec(
454 analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(menu_location));
455 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
456 });
457 } 487 }
458 488
459 // Handle clicks for the modifier buttons as well. 489 // Handle clicks for the modifier buttons as well.
@@ -461,9 +491,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
461 HandleClick( 491 HandleClick(
462 analog_map_modifier_button[analog_id], analog_id, 492 analog_map_modifier_button[analog_id], analog_id,
463 [=, this](const Common::ParamPackage& params) { 493 [=, this](const Common::ParamPackage& params) {
464 analogs_param[analog_id].Set("modifier", params.Serialize()); 494 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
495 param.Set("modifier", params.Serialize());
496 emulated_controller->SetStickParam(analog_id, param);
465 }, 497 },
466 InputCommon::Polling::DeviceType::Button); 498 InputCommon::Polling::InputType::Button);
467 }); 499 });
468 500
469 analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu); 501 analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -471,18 +503,21 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
471 connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, 503 connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested,
472 [=, this](const QPoint& menu_location) { 504 [=, this](const QPoint& menu_location) {
473 QMenu context_menu; 505 QMenu context_menu;
506 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
474 context_menu.addAction(tr("Clear"), [&] { 507 context_menu.addAction(tr("Clear"), [&] {
475 analogs_param[analog_id].Set("modifier", ""); 508 param.Set("modifier", "");
476 analog_map_modifier_button[analog_id]->setText(tr("[not set]")); 509 analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
510 emulated_controller->SetStickParam(analog_id, param);
477 }); 511 });
478 context_menu.addAction(tr("Toggle button"), [&] { 512 context_menu.addAction(tr("Toggle button"), [&] {
479 Common::ParamPackage modifier_param = 513 Common::ParamPackage modifier_param =
480 Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")}; 514 Common::ParamPackage{param.Get("modifier", "")};
481 const bool toggle_value = !modifier_param.Get("toggle", false); 515 const bool toggle_value = !modifier_param.Get("toggle", false);
482 modifier_param.Set("toggle", toggle_value); 516 modifier_param.Set("toggle", toggle_value);
483 analogs_param[analog_id].Set("modifier", modifier_param.Serialize()); 517 param.Set("modifier", modifier_param.Serialize());
484 analog_map_modifier_button[analog_id]->setText( 518 analog_map_modifier_button[analog_id]->setText(
485 ButtonToText(modifier_param)); 519 ButtonToText(modifier_param));
520 emulated_controller->SetStickParam(analog_id, param);
486 }); 521 });
487 context_menu.exec( 522 context_menu.exec(
488 analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); 523 analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
@@ -490,37 +525,39 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
490 525
491 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), 526 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
492 [=, this] { 527 [=, this] {
528 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
493 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); 529 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
494 analogs_param[analog_id].Set("range", spinbox_value / 100.0f); 530 param.Set("range", spinbox_value / 100.0f);
495 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); 531 emulated_controller->SetStickParam(analog_id, param);
496 }); 532 });
497 533
498 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { 534 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
535 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
499 const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); 536 const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
500 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); 537 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
501 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); 538 param.Set("deadzone", slider_value / 100.0f);
502 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); 539 emulated_controller->SetStickParam(analog_id, param);
503 }); 540 });
504 541
505 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { 542 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
543 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
506 const auto slider_value = analog_map_modifier_slider[analog_id]->value(); 544 const auto slider_value = analog_map_modifier_slider[analog_id]->value();
507 analog_map_modifier_label[analog_id]->setText( 545 analog_map_modifier_label[analog_id]->setText(
508 tr("Modifier Range: %1%").arg(slider_value)); 546 tr("Modifier Range: %1%").arg(slider_value));
509 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); 547 param.Set("modifier_scale", slider_value / 100.0f);
548 emulated_controller->SetStickParam(analog_id, param);
510 }); 549 });
511 } 550 }
512 551
513 // Player Connected checkbox 552 // Player Connected checkbox
514 connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) { 553 connect(ui->groupConnectedController, &QGroupBox::toggled,
515 emit Connected(checked); 554 [this](bool checked) { emit Connected(checked); });
516 ui->controllerFrame->SetConnectedStatus(checked);
517 });
518 555
519 if (player_index == 0) { 556 if (player_index == 0) {
520 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), 557 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
521 [this](int index) { 558 [this](int index) {
522 emit HandheldStateChanged(GetControllerTypeFromIndex(index) == 559 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
523 Settings::ControllerType::Handheld); 560 Core::HID::NpadStyleIndex::Handheld);
524 }); 561 });
525 } 562 }
526 563
@@ -537,18 +574,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
537 SetConnectableControllers(); 574 SetConnectableControllers();
538 } 575 }
539 576
540 UpdateControllerIcon();
541 UpdateControllerAvailableButtons(); 577 UpdateControllerAvailableButtons();
542 UpdateControllerEnabledButtons(); 578 UpdateControllerEnabledButtons();
543 UpdateControllerButtonNames(); 579 UpdateControllerButtonNames();
544 UpdateMotionButtons(); 580 UpdateMotionButtons();
545 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { 581 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
546 UpdateControllerIcon(); 582 [this, player_index](int) {
547 UpdateControllerAvailableButtons(); 583 UpdateControllerAvailableButtons();
548 UpdateControllerEnabledButtons(); 584 UpdateControllerEnabledButtons();
549 UpdateControllerButtonNames(); 585 UpdateControllerButtonNames();
550 UpdateMotionButtons(); 586 UpdateMotionButtons();
551 }); 587 const Core::HID::NpadStyleIndex type =
588 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
589
590 if (player_index == 0) {
591 auto* emulated_controller_p1 =
592 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
593 auto* emulated_controller_hanheld =
594 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
595 bool is_connected = emulated_controller->IsConnected(true);
596
597 emulated_controller_p1->SetNpadStyleIndex(type);
598 emulated_controller_hanheld->SetNpadStyleIndex(type);
599 if (is_connected) {
600 if (type == Core::HID::NpadStyleIndex::Handheld) {
601 emulated_controller_p1->Disconnect();
602 emulated_controller_hanheld->Connect();
603 emulated_controller = emulated_controller_hanheld;
604 } else {
605 emulated_controller_hanheld->Disconnect();
606 emulated_controller_p1->Connect();
607 emulated_controller = emulated_controller_p1;
608 }
609 }
610 ui->controllerFrame->SetController(emulated_controller);
611 }
612 emulated_controller->SetNpadStyleIndex(type);
613 });
552 614
553 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, 615 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
554 &ConfigureInputPlayer::UpdateMappingWithDefaults); 616 &ConfigureInputPlayer::UpdateMappingWithDefaults);
@@ -563,62 +625,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
563 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 625 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
564 626
565 connect(poll_timer.get(), &QTimer::timeout, [this] { 627 connect(poll_timer.get(), &QTimer::timeout, [this] {
566 Common::ParamPackage params; 628 const auto& params = input_subsystem->GetNextInput();
567 if (input_subsystem->GetGCButtons()->IsPolling()) { 629 if (params.Has("engine") && IsInputAcceptable(params)) {
568 params = input_subsystem->GetGCButtons()->GetNextInput(); 630 SetPollingResult(params, false);
569 if (params.Has("engine") && IsInputAcceptable(params)) { 631 return;
570 SetPollingResult(params, false);
571 return;
572 }
573 }
574 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
575 params = input_subsystem->GetGCAnalogs()->GetNextInput();
576 if (params.Has("engine") && IsInputAcceptable(params)) {
577 SetPollingResult(params, false);
578 return;
579 }
580 }
581 if (input_subsystem->GetUDPMotions()->IsPolling()) {
582 params = input_subsystem->GetUDPMotions()->GetNextInput();
583 if (params.Has("engine")) {
584 SetPollingResult(params, false);
585 return;
586 }
587 }
588 if (input_subsystem->GetMouseButtons()->IsPolling()) {
589 params = input_subsystem->GetMouseButtons()->GetNextInput();
590 if (params.Has("engine") && IsInputAcceptable(params)) {
591 SetPollingResult(params, false);
592 return;
593 }
594 }
595 if (input_subsystem->GetMouseAnalogs()->IsPolling()) {
596 params = input_subsystem->GetMouseAnalogs()->GetNextInput();
597 if (params.Has("engine") && IsInputAcceptable(params)) {
598 SetPollingResult(params, false);
599 return;
600 }
601 }
602 if (input_subsystem->GetMouseMotions()->IsPolling()) {
603 params = input_subsystem->GetMouseMotions()->GetNextInput();
604 if (params.Has("engine") && IsInputAcceptable(params)) {
605 SetPollingResult(params, false);
606 return;
607 }
608 }
609 if (input_subsystem->GetMouseTouch()->IsPolling()) {
610 params = input_subsystem->GetMouseTouch()->GetNextInput();
611 if (params.Has("engine") && IsInputAcceptable(params)) {
612 SetPollingResult(params, false);
613 return;
614 }
615 }
616 for (auto& poller : device_pollers) {
617 params = poller->GetNextInput();
618 if (params.Has("engine") && IsInputAcceptable(params)) {
619 SetPollingResult(params, false);
620 return;
621 }
622 } 632 }
623 }); 633 });
624 634
@@ -634,110 +644,38 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
634 &ConfigureInputPlayer::SaveProfile); 644 &ConfigureInputPlayer::SaveProfile);
635 645
636 LoadConfiguration(); 646 LoadConfiguration();
637 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
638 ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
639} 647}
640 648
641ConfigureInputPlayer::~ConfigureInputPlayer() = default; 649ConfigureInputPlayer::~ConfigureInputPlayer() {
642
643void ConfigureInputPlayer::ApplyConfiguration() {
644 auto& player = Settings::values.players.GetValue()[player_index];
645 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
646 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
647
648 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
649 [](const Common::ParamPackage& param) { return param.Serialize(); });
650 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
651 [](const Common::ParamPackage& param) { return param.Serialize(); });
652
653 if (debug) {
654 return;
655 }
656
657 auto& motions = player.motions;
658
659 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
660 [](const Common::ParamPackage& param) { return param.Serialize(); });
661
662 // Apply configuration for handheld
663 if (player_index == 0) { 650 if (player_index == 0) {
664 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 651 auto* emulated_controller_p1 =
665 const auto handheld_connected = handheld.connected; 652 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
666 handheld = player; 653 auto* emulated_controller_hanheld =
667 handheld.connected = handheld_connected; 654 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
655 emulated_controller_p1->DisableConfiguration();
656 emulated_controller_hanheld->DisableConfiguration();
657 } else {
658 emulated_controller->DisableConfiguration();
668 } 659 }
669} 660}
670 661
671void ConfigureInputPlayer::TryConnectSelectedController() { 662void ConfigureInputPlayer::ApplyConfiguration() {
672 auto& player = Settings::values.players.GetValue()[player_index];
673
674 const auto controller_type =
675 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
676 const auto player_connected = ui->groupConnectedController->isChecked() &&
677 controller_type != Settings::ControllerType::Handheld;
678
679 // Connect Handheld depending on Player 1's controller configuration.
680 if (player_index == 0) { 663 if (player_index == 0) {
681 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 664 auto* emulated_controller_p1 =
682 const auto handheld_connected = ui->groupConnectedController->isChecked() && 665 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
683 controller_type == Settings::ControllerType::Handheld; 666 auto* emulated_controller_hanheld =
684 // Connect only if handheld is going from disconnected to connected 667 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
685 if (!handheld.connected && handheld_connected) { 668 emulated_controller_p1->DisableConfiguration();
686 UpdateController(controller_type, HANDHELD_INDEX, true, system); 669 emulated_controller_p1->SaveCurrentConfig();
687 } 670 emulated_controller_p1->EnableConfiguration();
688 handheld.connected = handheld_connected; 671 emulated_controller_hanheld->DisableConfiguration();
689 } 672 emulated_controller_hanheld->SaveCurrentConfig();
690 673 emulated_controller_hanheld->EnableConfiguration();
691 if (player.controller_type == controller_type && player.connected == player_connected) {
692 // Set vibration devices in the event that the input device has changed.
693 ConfigureVibration::SetVibrationDevices(player_index);
694 return;
695 }
696
697 player.controller_type = controller_type;
698 player.connected = player_connected;
699
700 ConfigureVibration::SetVibrationDevices(player_index);
701
702 if (!player.connected) {
703 return;
704 }
705
706 UpdateController(controller_type, player_index, true, system);
707}
708
709void ConfigureInputPlayer::TryDisconnectSelectedController() {
710 const auto& player = Settings::values.players.GetValue()[player_index];
711
712 const auto controller_type =
713 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
714 const auto player_connected = ui->groupConnectedController->isChecked() &&
715 controller_type != Settings::ControllerType::Handheld;
716
717 // Disconnect Handheld depending on Player 1's controller configuration.
718 if (player_index == 0 && player.controller_type == Settings::ControllerType::Handheld) {
719 const auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
720 const auto handheld_connected = ui->groupConnectedController->isChecked() &&
721 controller_type == Settings::ControllerType::Handheld;
722 // Disconnect only if handheld is going from connected to disconnected
723 if (handheld.connected && !handheld_connected) {
724 UpdateController(controller_type, HANDHELD_INDEX, false, system);
725 }
726 return;
727 }
728
729 // Do not do anything if the controller configuration has not changed.
730 if (player.controller_type == controller_type && player.connected == player_connected) {
731 return;
732 }
733
734 // Do not disconnect if the controller is already disconnected
735 if (!player.connected) {
736 return; 674 return;
737 } 675 }
738 676 emulated_controller->DisableConfiguration();
739 // Disconnect the controller first. 677 emulated_controller->SaveCurrentConfig();
740 UpdateController(controller_type, player_index, false, system); 678 emulated_controller->EnableConfiguration();
741} 679}
742 680
743void ConfigureInputPlayer::showEvent(QShowEvent* event) { 681void ConfigureInputPlayer::showEvent(QShowEvent* event) {
@@ -762,22 +700,7 @@ void ConfigureInputPlayer::RetranslateUI() {
762} 700}
763 701
764void ConfigureInputPlayer::LoadConfiguration() { 702void ConfigureInputPlayer::LoadConfiguration() {
765 auto& player = Settings::values.players.GetValue()[player_index]; 703 emulated_controller->ReloadFromSettings();
766 if (debug) {
767 std::transform(Settings::values.debug_pad_buttons.begin(),
768 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
769 [](const std::string& str) { return Common::ParamPackage(str); });
770 std::transform(Settings::values.debug_pad_analogs.begin(),
771 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
772 [](const std::string& str) { return Common::ParamPackage(str); });
773 } else {
774 std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
775 [](const std::string& str) { return Common::ParamPackage(str); });
776 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
777 [](const std::string& str) { return Common::ParamPackage(str); });
778 std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
779 [](const std::string& str) { return Common::ParamPackage(str); });
780 }
781 704
782 UpdateUI(); 705 UpdateUI();
783 UpdateInputDeviceCombobox(); 706 UpdateInputDeviceCombobox();
@@ -786,14 +709,19 @@ void ConfigureInputPlayer::LoadConfiguration() {
786 return; 709 return;
787 } 710 }
788 711
789 ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type)); 712 const int comboBoxIndex =
790 ui->groupConnectedController->setChecked( 713 GetIndexFromControllerType(emulated_controller->GetNpadStyleIndex(true));
791 player.connected || 714 ui->comboControllerType->setCurrentIndex(comboBoxIndex);
792 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); 715 ui->groupConnectedController->setChecked(emulated_controller->IsConnected(true));
793} 716}
794 717
795void ConfigureInputPlayer::ConnectPlayer(bool connected) { 718void ConfigureInputPlayer::ConnectPlayer(bool connected) {
796 ui->groupConnectedController->setChecked(connected); 719 ui->groupConnectedController->setChecked(connected);
720 if (connected) {
721 emulated_controller->Connect();
722 } else {
723 emulated_controller->Disconnect();
724 }
797} 725}
798 726
799void ConfigureInputPlayer::UpdateInputDeviceCombobox() { 727void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
@@ -803,48 +731,64 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
803 return; 731 return;
804 } 732 }
805 733
806 // Find the first button that isn't empty. 734 const auto devices =
807 const auto button_param = 735 emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices);
808 std::find_if(buttons_param.begin(), buttons_param.end(), 736 UpdateInputDevices();
809 [](const Common::ParamPackage param) { return param.Has("engine"); });
810 const bool buttons_empty = button_param == buttons_param.end();
811 737
812 const auto current_engine = button_param->Get("engine", ""); 738 if (devices.empty()) {
813 const auto current_guid = button_param->Get("guid", ""); 739 return;
814 const auto current_port = button_param->Get("port", ""); 740 }
741
742 if (devices.size() > 2) {
743 ui->comboDevices->setCurrentIndex(0);
744 return;
745 }
815 746
816 const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse"; 747 const auto first_engine = devices[0].Get("engine", "");
748 const auto first_guid = devices[0].Get("guid", "");
749 const auto first_port = devices[0].Get("port", 0);
817 750
818 UpdateInputDevices(); 751 if (devices.size() == 1) {
752 const auto devices_it =
753 std::find_if(input_devices.begin(), input_devices.end(),
754 [first_engine, first_guid, first_port](const Common::ParamPackage param) {
755 return param.Get("engine", "") == first_engine &&
756 param.Get("guid", "") == first_guid &&
757 param.Get("port", 0) == first_port;
758 });
759 const int device_index =
760 devices_it != input_devices.end()
761 ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
762 : 0;
763 ui->comboDevices->setCurrentIndex(device_index);
764 return;
765 }
819 766
820 if (buttons_empty) { 767 const auto second_engine = devices[1].Get("engine", "");
768 const auto second_guid = devices[1].Get("guid", "");
769 const auto second_port = devices[1].Get("port", 0);
770
771 const bool is_keyboard_mouse = (first_engine == "keyboard" || first_engine == "mouse") &&
772 (second_engine == "keyboard" || second_engine == "mouse");
773
774 if (is_keyboard_mouse) {
775 ui->comboDevices->setCurrentIndex(2);
821 return; 776 return;
822 } 777 }
823 778
824 const bool all_one_device = 779 const bool is_engine_equal = first_engine == second_engine;
825 std::all_of(buttons_param.begin(), buttons_param.end(), 780 const bool is_port_equal = first_port == second_port;
826 [current_engine, current_guid, current_port,
827 is_keyboard_mouse](const Common::ParamPackage param) {
828 if (is_keyboard_mouse) {
829 return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
830 param.Get("engine", "") == "mouse";
831 }
832 return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
833 param.Get("guid", "") == current_guid &&
834 param.Get("port", "") == current_port);
835 });
836 781
837 if (all_one_device) { 782 if (is_engine_equal && is_port_equal) {
838 if (is_keyboard_mouse) {
839 ui->comboDevices->setCurrentIndex(1);
840 return;
841 }
842 const auto devices_it = std::find_if( 783 const auto devices_it = std::find_if(
843 input_devices.begin(), input_devices.end(), 784 input_devices.begin(), input_devices.end(),
844 [current_engine, current_guid, current_port](const Common::ParamPackage param) { 785 [first_engine, first_guid, second_guid, first_port](const Common::ParamPackage param) {
845 return param.Get("class", "") == current_engine && 786 const bool is_guid_valid =
846 param.Get("guid", "") == current_guid && 787 (param.Get("guid", "") == first_guid &&
847 param.Get("port", "") == current_port; 788 param.Get("guid2", "") == second_guid) ||
789 (param.Get("guid", "") == second_guid && param.Get("guid2", "") == first_guid);
790 return param.Get("engine", "") == first_engine && is_guid_valid &&
791 param.Get("port", 0) == first_port;
848 }); 792 });
849 const int device_index = 793 const int device_index =
850 devices_it != input_devices.end() 794 devices_it != input_devices.end()
@@ -866,8 +810,7 @@ void ConfigureInputPlayer::ClearAll() {
866 if (button == nullptr) { 810 if (button == nullptr) {
867 continue; 811 continue;
868 } 812 }
869 813 emulated_controller->SetButtonParam(button_id, {});
870 buttons_param[button_id].Clear();
871 } 814 }
872 815
873 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 816 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
@@ -876,8 +819,7 @@ void ConfigureInputPlayer::ClearAll() {
876 if (analog_button == nullptr) { 819 if (analog_button == nullptr) {
877 continue; 820 continue;
878 } 821 }
879 822 emulated_controller->SetStickParam(analog_id, {});
880 analogs_param[analog_id].Clear();
881 } 823 }
882 } 824 }
883 825
@@ -886,8 +828,7 @@ void ConfigureInputPlayer::ClearAll() {
886 if (motion_button == nullptr) { 828 if (motion_button == nullptr) {
887 continue; 829 continue;
888 } 830 }
889 831 emulated_controller->SetMotionParam(motion_id, {});
890 motions_param[motion_id].Clear();
891 } 832 }
892 833
893 UpdateUI(); 834 UpdateUI();
@@ -896,26 +837,31 @@ void ConfigureInputPlayer::ClearAll() {
896 837
897void ConfigureInputPlayer::UpdateUI() { 838void ConfigureInputPlayer::UpdateUI() {
898 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) { 839 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
899 button_map[button]->setText(ButtonToText(buttons_param[button])); 840 const Common::ParamPackage param = emulated_controller->GetButtonParam(button);
841 button_map[button]->setText(ButtonToText(param));
900 } 842 }
901 843
902 if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { 844 const Common::ParamPackage ZL_param =
903 const int button_threshold = static_cast<int>( 845 emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
904 buttons_param[Settings::NativeButton::ZL].Get("threshold", 0.5f) * 100.0f); 846 if (ZL_param.Has("threshold")) {
847 const int button_threshold = static_cast<int>(ZL_param.Get("threshold", 0.5f) * 100.0f);
905 ui->sliderZLThreshold->setValue(button_threshold); 848 ui->sliderZLThreshold->setValue(button_threshold);
906 } 849 }
907 850
908 if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { 851 const Common::ParamPackage ZR_param =
909 const int button_threshold = static_cast<int>( 852 emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
910 buttons_param[Settings::NativeButton::ZR].Get("threshold", 0.5f) * 100.0f); 853 if (ZR_param.Has("threshold")) {
854 const int button_threshold = static_cast<int>(ZR_param.Get("threshold", 0.5f) * 100.0f);
911 ui->sliderZRThreshold->setValue(button_threshold); 855 ui->sliderZRThreshold->setValue(button_threshold);
912 } 856 }
913 857
914 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 858 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
915 motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id])); 859 const Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id);
860 motion_map[motion_id]->setText(ButtonToText(param));
916 } 861 }
917 862
918 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 863 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
864 const Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
919 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 865 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
920 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 866 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
921 867
@@ -923,12 +869,11 @@ void ConfigureInputPlayer::UpdateUI() {
923 continue; 869 continue;
924 } 870 }
925 871
926 analog_button->setText( 872 analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
927 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
928 } 873 }
929 874
930 analog_map_modifier_button[analog_id]->setText( 875 analog_map_modifier_button[analog_id]->setText(
931 ButtonToText(Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")})); 876 ButtonToText(Common::ParamPackage{param.Get("modifier", "")}));
932 877
933 const auto deadzone_label = analog_map_deadzone_label[analog_id]; 878 const auto deadzone_label = analog_map_deadzone_label[analog_id];
934 const auto deadzone_slider = analog_map_deadzone_slider[analog_id]; 879 const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
@@ -939,26 +884,14 @@ void ConfigureInputPlayer::UpdateUI() {
939 const auto range_spinbox = analog_map_range_spinbox[analog_id]; 884 const auto range_spinbox = analog_map_range_spinbox[analog_id];
940 885
941 int slider_value; 886 int slider_value;
942 auto& param = analogs_param[analog_id]; 887 const bool is_controller = input_subsystem->IsController(param);
943 const bool is_controller =
944 param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" ||
945 param.Get("engine", "") == "mouse" || param.Get("engine", "") == "tas";
946 888
947 if (is_controller) { 889 if (is_controller) {
948 if (!param.Has("deadzone")) { 890 slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
949 param.Set("deadzone", 0.1f);
950 }
951 slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
952 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value)); 891 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
953 deadzone_slider->setValue(slider_value); 892 deadzone_slider->setValue(slider_value);
954 if (!param.Has("range")) {
955 param.Set("range", 1.0f);
956 }
957 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100)); 893 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
958 } else { 894 } else {
959 if (!param.Has("modifier_scale")) {
960 param.Set("modifier_scale", 0.5f);
961 }
962 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100); 895 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
963 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value)); 896 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
964 modifier_slider->setValue(slider_value); 897 modifier_slider->setValue(slider_value);
@@ -970,79 +903,107 @@ void ConfigureInputPlayer::UpdateUI() {
970 modifier_label->setVisible(!is_controller); 903 modifier_label->setVisible(!is_controller);
971 modifier_slider->setVisible(!is_controller); 904 modifier_slider->setVisible(!is_controller);
972 range_groupbox->setVisible(is_controller); 905 range_groupbox->setVisible(is_controller);
973 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
974 } 906 }
975} 907}
976 908
977void ConfigureInputPlayer::SetConnectableControllers() { 909void ConfigureInputPlayer::SetConnectableControllers() {
978 const auto add_controllers = [this](bool enable_all, 910 const auto add_controllers = [this](bool enable_all,
979 Controller_NPad::NpadStyleSet npad_style_set = {}) { 911 Core::HID::NpadStyleTag npad_style_set = {}) {
980 index_controller_type_pairs.clear(); 912 index_controller_type_pairs.clear();
981 ui->comboControllerType->clear(); 913 ui->comboControllerType->clear();
982 914
983 if (enable_all || npad_style_set.fullkey == 1) { 915 if (enable_all || npad_style_set.fullkey == 1) {
984 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 916 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
985 Settings::ControllerType::ProController); 917 Core::HID::NpadStyleIndex::ProController);
986 ui->comboControllerType->addItem(tr("Pro Controller")); 918 ui->comboControllerType->addItem(tr("Pro Controller"));
987 } 919 }
988 920
989 if (enable_all || npad_style_set.joycon_dual == 1) { 921 if (enable_all || npad_style_set.joycon_dual == 1) {
990 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 922 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
991 Settings::ControllerType::DualJoyconDetached); 923 Core::HID::NpadStyleIndex::JoyconDual);
992 ui->comboControllerType->addItem(tr("Dual Joycons")); 924 ui->comboControllerType->addItem(tr("Dual Joycons"));
993 } 925 }
994 926
995 if (enable_all || npad_style_set.joycon_left == 1) { 927 if (enable_all || npad_style_set.joycon_left == 1) {
996 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 928 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
997 Settings::ControllerType::LeftJoycon); 929 Core::HID::NpadStyleIndex::JoyconLeft);
998 ui->comboControllerType->addItem(tr("Left Joycon")); 930 ui->comboControllerType->addItem(tr("Left Joycon"));
999 } 931 }
1000 932
1001 if (enable_all || npad_style_set.joycon_right == 1) { 933 if (enable_all || npad_style_set.joycon_right == 1) {
1002 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 934 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
1003 Settings::ControllerType::RightJoycon); 935 Core::HID::NpadStyleIndex::JoyconRight);
1004 ui->comboControllerType->addItem(tr("Right Joycon")); 936 ui->comboControllerType->addItem(tr("Right Joycon"));
1005 } 937 }
1006 938
1007 if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) { 939 if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) {
1008 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 940 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
1009 Settings::ControllerType::Handheld); 941 Core::HID::NpadStyleIndex::Handheld);
1010 ui->comboControllerType->addItem(tr("Handheld")); 942 ui->comboControllerType->addItem(tr("Handheld"));
1011 } 943 }
1012 944
1013 if (enable_all || npad_style_set.gamecube == 1) { 945 if (enable_all || npad_style_set.gamecube == 1) {
1014 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 946 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
1015 Settings::ControllerType::GameCube); 947 Core::HID::NpadStyleIndex::GameCube);
1016 ui->comboControllerType->addItem(tr("GameCube Controller")); 948 ui->comboControllerType->addItem(tr("GameCube Controller"));
1017 } 949 }
950
951 // Disable all unsupported controllers
952 if (!Settings::values.enable_all_controllers) {
953 return;
954 }
955 if (enable_all || npad_style_set.palma == 1) {
956 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
957 Core::HID::NpadStyleIndex::Pokeball);
958 ui->comboControllerType->addItem(tr("Poke Ball Plus"));
959 }
960
961 if (enable_all || npad_style_set.lark == 1) {
962 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
963 Core::HID::NpadStyleIndex::NES);
964 ui->comboControllerType->addItem(tr("NES Controller"));
965 }
966
967 if (enable_all || npad_style_set.lucia == 1) {
968 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
969 Core::HID::NpadStyleIndex::SNES);
970 ui->comboControllerType->addItem(tr("SNES Controller"));
971 }
972
973 if (enable_all || npad_style_set.lagoon == 1) {
974 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
975 Core::HID::NpadStyleIndex::N64);
976 ui->comboControllerType->addItem(tr("N64 Controller"));
977 }
978
979 if (enable_all || npad_style_set.lager == 1) {
980 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
981 Core::HID::NpadStyleIndex::SegaGenesis);
982 ui->comboControllerType->addItem(tr("Sega Genesis"));
983 }
1018 }; 984 };
1019 985
1020 if (!system.IsPoweredOn()) { 986 if (!is_powered_on) {
1021 add_controllers(true); 987 add_controllers(true);
1022 return; 988 return;
1023 } 989 }
1024 990
1025 Service::SM::ServiceManager& sm = system.ServiceManager(); 991 add_controllers(false, hid_core.GetSupportedStyleTag());
1026
1027 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
1028 HidController::NPad);
1029
1030 add_controllers(false, npad.GetSupportedStyleSet());
1031} 992}
1032 993
1033Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const { 994Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
1034 const auto it = 995 const auto it =
1035 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), 996 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
1036 [index](const auto& pair) { return pair.first == index; }); 997 [index](const auto& pair) { return pair.first == index; });
1037 998
1038 if (it == index_controller_type_pairs.end()) { 999 if (it == index_controller_type_pairs.end()) {
1039 return Settings::ControllerType::ProController; 1000 return Core::HID::NpadStyleIndex::ProController;
1040 } 1001 }
1041 1002
1042 return it->second; 1003 return it->second;
1043} 1004}
1044 1005
1045int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const { 1006int ConfigureInputPlayer::GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const {
1046 const auto it = 1007 const auto it =
1047 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), 1008 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
1048 [type](const auto& pair) { return pair.second == type; }); 1009 [type](const auto& pair) { return pair.second == type; });
@@ -1057,52 +1018,15 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType ty
1057void ConfigureInputPlayer::UpdateInputDevices() { 1018void ConfigureInputPlayer::UpdateInputDevices() {
1058 input_devices = input_subsystem->GetInputDevices(); 1019 input_devices = input_subsystem->GetInputDevices();
1059 ui->comboDevices->clear(); 1020 ui->comboDevices->clear();
1060 for (auto& device : input_devices) { 1021 for (auto device : input_devices) {
1061 const std::string display = device.Get("display", "Unknown"); 1022 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
1062 ui->comboDevices->addItem(QString::fromStdString(display), {});
1063 if (display == "TAS") {
1064 device.Set("pad", static_cast<u8>(player_index));
1065 }
1066 } 1023 }
1067} 1024}
1068 1025
1069void ConfigureInputPlayer::UpdateControllerIcon() {
1070 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
1071 // "nonstandard" to use an image through the icon support)
1072 const QString stylesheet = [this] {
1073 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
1074 case Settings::ControllerType::ProController:
1075 return QStringLiteral("image: url(:/controller/pro_controller%0)");
1076 case Settings::ControllerType::DualJoyconDetached:
1077 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
1078 case Settings::ControllerType::LeftJoycon:
1079 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
1080 case Settings::ControllerType::RightJoycon:
1081 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
1082 case Settings::ControllerType::Handheld:
1083 return QStringLiteral("image: url(:/controller/handheld%0)");
1084 default:
1085 return QString{};
1086 }
1087 }();
1088
1089 const QString theme = [] {
1090 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
1091 return QStringLiteral("_dark");
1092 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
1093 return QStringLiteral("_midnight");
1094 } else {
1095 return QString{};
1096 }
1097 }();
1098 ui->controllerFrame->SetControllerType(
1099 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()));
1100}
1101
1102void ConfigureInputPlayer::UpdateControllerAvailableButtons() { 1026void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1103 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1027 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1104 if (debug) { 1028 if (debug) {
1105 layout = Settings::ControllerType::ProController; 1029 layout = Core::HID::NpadStyleIndex::ProController;
1106 } 1030 }
1107 1031
1108 // List of all the widgets that will be hidden by any of the following layouts that need 1032 // List of all the widgets that will be hidden by any of the following layouts that need
@@ -1127,15 +1051,15 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1127 1051
1128 std::vector<QWidget*> layout_hidden; 1052 std::vector<QWidget*> layout_hidden;
1129 switch (layout) { 1053 switch (layout) {
1130 case Settings::ControllerType::ProController: 1054 case Core::HID::NpadStyleIndex::ProController:
1131 case Settings::ControllerType::DualJoyconDetached: 1055 case Core::HID::NpadStyleIndex::JoyconDual:
1132 case Settings::ControllerType::Handheld: 1056 case Core::HID::NpadStyleIndex::Handheld:
1133 layout_hidden = { 1057 layout_hidden = {
1134 ui->buttonShoulderButtonsSLSR, 1058 ui->buttonShoulderButtonsSLSR,
1135 ui->horizontalSpacerShoulderButtonsWidget2, 1059 ui->horizontalSpacerShoulderButtonsWidget2,
1136 }; 1060 };
1137 break; 1061 break;
1138 case Settings::ControllerType::LeftJoycon: 1062 case Core::HID::NpadStyleIndex::JoyconLeft:
1139 layout_hidden = { 1063 layout_hidden = {
1140 ui->horizontalSpacerShoulderButtonsWidget2, 1064 ui->horizontalSpacerShoulderButtonsWidget2,
1141 ui->buttonShoulderButtonsRight, 1065 ui->buttonShoulderButtonsRight,
@@ -1143,7 +1067,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1143 ui->bottomRight, 1067 ui->bottomRight,
1144 }; 1068 };
1145 break; 1069 break;
1146 case Settings::ControllerType::RightJoycon: 1070 case Core::HID::NpadStyleIndex::JoyconRight:
1147 layout_hidden = { 1071 layout_hidden = {
1148 ui->horizontalSpacerShoulderButtonsWidget, 1072 ui->horizontalSpacerShoulderButtonsWidget,
1149 ui->buttonShoulderButtonsLeft, 1073 ui->buttonShoulderButtonsLeft,
@@ -1151,7 +1075,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1151 ui->bottomLeft, 1075 ui->bottomLeft,
1152 }; 1076 };
1153 break; 1077 break;
1154 case Settings::ControllerType::GameCube: 1078 case Core::HID::NpadStyleIndex::GameCube:
1155 layout_hidden = { 1079 layout_hidden = {
1156 ui->buttonShoulderButtonsSLSR, 1080 ui->buttonShoulderButtonsSLSR,
1157 ui->horizontalSpacerShoulderButtonsWidget2, 1081 ui->horizontalSpacerShoulderButtonsWidget2,
@@ -1159,6 +1083,8 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1159 ui->buttonMiscButtonsScreenshotGroup, 1083 ui->buttonMiscButtonsScreenshotGroup,
1160 }; 1084 };
1161 break; 1085 break;
1086 default:
1087 break;
1162 } 1088 }
1163 1089
1164 for (auto* widget : layout_hidden) { 1090 for (auto* widget : layout_hidden) {
@@ -1169,13 +1095,12 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1169void ConfigureInputPlayer::UpdateControllerEnabledButtons() { 1095void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1170 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1096 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1171 if (debug) { 1097 if (debug) {
1172 layout = Settings::ControllerType::ProController; 1098 layout = Core::HID::NpadStyleIndex::ProController;
1173 } 1099 }
1174 1100
1175 // List of all the widgets that will be disabled by any of the following layouts that need 1101 // List of all the widgets that will be disabled by any of the following layouts that need
1176 // "enabled" after the controller type changes 1102 // "enabled" after the controller type changes
1177 const std::array<QWidget*, 4> layout_enable = { 1103 const std::array<QWidget*, 3> layout_enable = {
1178 ui->buttonHome,
1179 ui->buttonLStickPressedGroup, 1104 ui->buttonLStickPressedGroup,
1180 ui->groupRStickPressed, 1105 ui->groupRStickPressed,
1181 ui->buttonShoulderButtonsButtonLGroup, 1106 ui->buttonShoulderButtonsButtonLGroup,
@@ -1187,17 +1112,13 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1187 1112
1188 std::vector<QWidget*> layout_disable; 1113 std::vector<QWidget*> layout_disable;
1189 switch (layout) { 1114 switch (layout) {
1190 case Settings::ControllerType::ProController: 1115 case Core::HID::NpadStyleIndex::ProController:
1191 case Settings::ControllerType::DualJoyconDetached: 1116 case Core::HID::NpadStyleIndex::JoyconDual:
1192 case Settings::ControllerType::Handheld: 1117 case Core::HID::NpadStyleIndex::Handheld:
1193 case Settings::ControllerType::LeftJoycon: 1118 case Core::HID::NpadStyleIndex::JoyconLeft:
1194 case Settings::ControllerType::RightJoycon: 1119 case Core::HID::NpadStyleIndex::JoyconRight:
1195 // TODO(wwylele): enable this when we actually emulate it
1196 layout_disable = {
1197 ui->buttonHome,
1198 };
1199 break; 1120 break;
1200 case Settings::ControllerType::GameCube: 1121 case Core::HID::NpadStyleIndex::GameCube:
1201 layout_disable = { 1122 layout_disable = {
1202 ui->buttonHome, 1123 ui->buttonHome,
1203 ui->buttonLStickPressedGroup, 1124 ui->buttonLStickPressedGroup,
@@ -1205,6 +1126,8 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1205 ui->buttonShoulderButtonsButtonLGroup, 1126 ui->buttonShoulderButtonsButtonLGroup,
1206 }; 1127 };
1207 break; 1128 break;
1129 default:
1130 break;
1208 } 1131 }
1209 1132
1210 for (auto* widget : layout_disable) { 1133 for (auto* widget : layout_disable) {
@@ -1222,24 +1145,24 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1222 1145
1223 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. 1146 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
1224 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { 1147 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
1225 case Settings::ControllerType::ProController: 1148 case Core::HID::NpadStyleIndex::ProController:
1226 case Settings::ControllerType::LeftJoycon: 1149 case Core::HID::NpadStyleIndex::JoyconLeft:
1227 case Settings::ControllerType::Handheld: 1150 case Core::HID::NpadStyleIndex::Handheld:
1228 // Show "Motion 1" and hide "Motion 2". 1151 // Show "Motion 1" and hide "Motion 2".
1229 ui->buttonMotionLeftGroup->show(); 1152 ui->buttonMotionLeftGroup->show();
1230 ui->buttonMotionRightGroup->hide(); 1153 ui->buttonMotionRightGroup->hide();
1231 break; 1154 break;
1232 case Settings::ControllerType::RightJoycon: 1155 case Core::HID::NpadStyleIndex::JoyconRight:
1233 // Show "Motion 2" and hide "Motion 1". 1156 // Show "Motion 2" and hide "Motion 1".
1234 ui->buttonMotionLeftGroup->hide(); 1157 ui->buttonMotionLeftGroup->hide();
1235 ui->buttonMotionRightGroup->show(); 1158 ui->buttonMotionRightGroup->show();
1236 break; 1159 break;
1237 case Settings::ControllerType::GameCube: 1160 case Core::HID::NpadStyleIndex::GameCube:
1238 // Hide both "Motion 1/2". 1161 // Hide both "Motion 1/2".
1239 ui->buttonMotionLeftGroup->hide(); 1162 ui->buttonMotionLeftGroup->hide();
1240 ui->buttonMotionRightGroup->hide(); 1163 ui->buttonMotionRightGroup->hide();
1241 break; 1164 break;
1242 case Settings::ControllerType::DualJoyconDetached: 1165 case Core::HID::NpadStyleIndex::JoyconDual:
1243 default: 1166 default:
1244 // Show both "Motion 1/2". 1167 // Show both "Motion 1/2".
1245 ui->buttonMotionLeftGroup->show(); 1168 ui->buttonMotionLeftGroup->show();
@@ -1251,15 +1174,15 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1251void ConfigureInputPlayer::UpdateControllerButtonNames() { 1174void ConfigureInputPlayer::UpdateControllerButtonNames() {
1252 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1175 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1253 if (debug) { 1176 if (debug) {
1254 layout = Settings::ControllerType::ProController; 1177 layout = Core::HID::NpadStyleIndex::ProController;
1255 } 1178 }
1256 1179
1257 switch (layout) { 1180 switch (layout) {
1258 case Settings::ControllerType::ProController: 1181 case Core::HID::NpadStyleIndex::ProController:
1259 case Settings::ControllerType::DualJoyconDetached: 1182 case Core::HID::NpadStyleIndex::JoyconDual:
1260 case Settings::ControllerType::Handheld: 1183 case Core::HID::NpadStyleIndex::Handheld:
1261 case Settings::ControllerType::LeftJoycon: 1184 case Core::HID::NpadStyleIndex::JoyconLeft:
1262 case Settings::ControllerType::RightJoycon: 1185 case Core::HID::NpadStyleIndex::JoyconRight:
1263 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus")); 1186 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus"));
1264 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL")); 1187 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL"));
1265 ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR")); 1188 ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR"));
@@ -1267,7 +1190,7 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() {
1267 ui->LStick->setTitle(tr("Left Stick")); 1190 ui->LStick->setTitle(tr("Left Stick"));
1268 ui->RStick->setTitle(tr("Right Stick")); 1191 ui->RStick->setTitle(tr("Right Stick"));
1269 break; 1192 break;
1270 case Settings::ControllerType::GameCube: 1193 case Core::HID::NpadStyleIndex::GameCube:
1271 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause")); 1194 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause"));
1272 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L")); 1195 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L"));
1273 ui->buttonShoulderButtonsZRGroup->setTitle(tr("R")); 1196 ui->buttonShoulderButtonsZRGroup->setTitle(tr("R"));
@@ -1275,6 +1198,8 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() {
1275 ui->LStick->setTitle(tr("Control Stick")); 1198 ui->LStick->setTitle(tr("Control Stick"));
1276 ui->RStick->setTitle(tr("C-Stick")); 1199 ui->RStick->setTitle(tr("C-Stick"));
1277 break; 1200 break;
1201 default:
1202 break;
1278 } 1203 }
1279} 1204}
1280 1205
@@ -1283,45 +1208,82 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1283 return; 1208 return;
1284 } 1209 }
1285 1210
1286 if (ui->comboDevices->currentIndex() == 1) { 1211 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
1287 // Reset keyboard bindings 1212 const auto* const button = button_map[button_id];
1213 if (button == nullptr) {
1214 continue;
1215 }
1216 emulated_controller->SetButtonParam(button_id, {});
1217 }
1218
1219 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
1220 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
1221 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
1222 if (analog_button == nullptr) {
1223 continue;
1224 }
1225 emulated_controller->SetStickParam(analog_id, {});
1226 }
1227 }
1228
1229 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
1230 const auto* const motion_button = motion_map[motion_id];
1231 if (motion_button == nullptr) {
1232 continue;
1233 }
1234 emulated_controller->SetMotionParam(motion_id, {});
1235 }
1236
1237 // Reset keyboard or mouse bindings
1238 if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
1288 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 1239 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
1289 buttons_param[button_id] = Common::ParamPackage{ 1240 emulated_controller->SetButtonParam(
1290 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 1241 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1242 Config::default_buttons[button_id])});
1291 } 1243 }
1292 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 1244 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
1245 Common::ParamPackage analog_param{};
1293 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 1246 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
1294 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 1247 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
1295 Config::default_analogs[analog_id][sub_button_id])}; 1248 Config::default_analogs[analog_id][sub_button_id])};
1296 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 1249 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
1297 } 1250 }
1298 1251
1299 analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam( 1252 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
1300 Config::default_stick_mod[analog_id])); 1253 Config::default_stick_mod[analog_id]));
1254 emulated_controller->SetStickParam(analog_id, analog_param);
1301 } 1255 }
1302 1256
1303 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 1257 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
1304 motions_param[motion_id] = Common::ParamPackage{ 1258 emulated_controller->SetMotionParam(
1305 InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; 1259 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1260 Config::default_motions[motion_id])});
1306 } 1261 }
1307 1262
1308 UpdateUI(); 1263 // If mouse is selected we want to override with mappings from the driver
1309 return; 1264 if (ui->comboDevices->currentIndex() == 1) {
1265 UpdateUI();
1266 return;
1267 }
1310 } 1268 }
1311 1269
1312 // Reset controller bindings 1270 // Reset controller bindings
1313 const auto& device = input_devices[ui->comboDevices->currentIndex()]; 1271 const auto& device = input_devices[ui->comboDevices->currentIndex()];
1314 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); 1272 auto button_mappings = input_subsystem->GetButtonMappingForDevice(device);
1315 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); 1273 auto analog_mappings = input_subsystem->GetAnalogMappingForDevice(device);
1316 auto motion_mapping = input_subsystem->GetMotionMappingForDevice(device); 1274 auto motion_mappings = input_subsystem->GetMotionMappingForDevice(device);
1317 for (std::size_t i = 0; i < buttons_param.size(); ++i) { 1275
1318 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)]; 1276 for (const auto& button_mapping : button_mappings) {
1277 const std::size_t index = button_mapping.first;
1278 emulated_controller->SetButtonParam(index, button_mapping.second);
1319 } 1279 }
1320 for (std::size_t i = 0; i < analogs_param.size(); ++i) { 1280 for (const auto& analog_mapping : analog_mappings) {
1321 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)]; 1281 const std::size_t index = analog_mapping.first;
1282 emulated_controller->SetStickParam(index, analog_mapping.second);
1322 } 1283 }
1323 for (std::size_t i = 0; i < motions_param.size(); ++i) { 1284 for (const auto& motion_mapping : motion_mappings) {
1324 motions_param[i] = motion_mapping[static_cast<Settings::NativeMotion::Values>(i)]; 1285 const std::size_t index = motion_mapping.first;
1286 emulated_controller->SetMotionParam(index, motion_mapping.second);
1325 } 1287 }
1326 1288
1327 UpdateUI(); 1289 UpdateUI();
@@ -1330,7 +1292,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1330void ConfigureInputPlayer::HandleClick( 1292void ConfigureInputPlayer::HandleClick(
1331 QPushButton* button, std::size_t button_id, 1293 QPushButton* button, std::size_t button_id,
1332 std::function<void(const Common::ParamPackage&)> new_input_setter, 1294 std::function<void(const Common::ParamPackage&)> new_input_setter,
1333 InputCommon::Polling::DeviceType type) { 1295 InputCommon::Polling::InputType type) {
1334 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { 1296 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
1335 button->setText(tr("Shake!")); 1297 button->setText(tr("Shake!"));
1336 } else { 1298 } else {
@@ -1338,71 +1300,31 @@ void ConfigureInputPlayer::HandleClick(
1338 } 1300 }
1339 button->setFocus(); 1301 button->setFocus();
1340 1302
1341 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
1342 // controller, then they don't want keyboard/mouse input
1343 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
1344
1345 input_setter = new_input_setter; 1303 input_setter = new_input_setter;
1346 1304
1347 device_pollers = input_subsystem->GetPollers(type); 1305 input_subsystem->BeginMapping(type);
1348
1349 for (auto& poller : device_pollers) {
1350 poller->Start();
1351 }
1352 1306
1353 QWidget::grabMouse(); 1307 QWidget::grabMouse();
1354 QWidget::grabKeyboard(); 1308 QWidget::grabKeyboard();
1355 1309
1356 if (type == InputCommon::Polling::DeviceType::Button) { 1310 if (type == InputCommon::Polling::InputType::Button) {
1357 input_subsystem->GetGCButtons()->BeginConfiguration();
1358 } else {
1359 input_subsystem->GetGCAnalogs()->BeginConfiguration();
1360 }
1361
1362 if (type == InputCommon::Polling::DeviceType::Motion) {
1363 input_subsystem->GetUDPMotions()->BeginConfiguration();
1364 }
1365
1366 if (type == InputCommon::Polling::DeviceType::Button) {
1367 input_subsystem->GetMouseButtons()->BeginConfiguration();
1368 } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
1369 input_subsystem->GetMouseAnalogs()->BeginConfiguration();
1370 } else if (type == InputCommon::Polling::DeviceType::Motion) {
1371 input_subsystem->GetMouseMotions()->BeginConfiguration();
1372 } else {
1373 input_subsystem->GetMouseTouch()->BeginConfiguration();
1374 }
1375
1376 if (type == InputCommon::Polling::DeviceType::Button) {
1377 ui->controllerFrame->BeginMappingButton(button_id); 1311 ui->controllerFrame->BeginMappingButton(button_id);
1378 } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) { 1312 } else if (type == InputCommon::Polling::InputType::Stick) {
1379 ui->controllerFrame->BeginMappingAnalog(button_id); 1313 ui->controllerFrame->BeginMappingAnalog(button_id);
1380 } 1314 }
1381 1315
1382 timeout_timer->start(2500); // Cancel after 2.5 seconds 1316 timeout_timer->start(2500); // Cancel after 2.5 seconds
1383 poll_timer->start(50); // Check for new inputs every 50ms 1317 poll_timer->start(25); // Check for new inputs every 25ms
1384} 1318}
1385 1319
1386void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { 1320void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
1387 timeout_timer->stop(); 1321 timeout_timer->stop();
1388 poll_timer->stop(); 1322 poll_timer->stop();
1389 for (auto& poller : device_pollers) { 1323 input_subsystem->StopMapping();
1390 poller->Stop();
1391 }
1392 1324
1393 QWidget::releaseMouse(); 1325 QWidget::releaseMouse();
1394 QWidget::releaseKeyboard(); 1326 QWidget::releaseKeyboard();
1395 1327
1396 input_subsystem->GetGCButtons()->EndConfiguration();
1397 input_subsystem->GetGCAnalogs()->EndConfiguration();
1398
1399 input_subsystem->GetUDPMotions()->EndConfiguration();
1400
1401 input_subsystem->GetMouseButtons()->EndConfiguration();
1402 input_subsystem->GetMouseAnalogs()->EndConfiguration();
1403 input_subsystem->GetMouseMotions()->EndConfiguration();
1404 input_subsystem->GetMouseTouch()->EndConfiguration();
1405
1406 if (!abort) { 1328 if (!abort) {
1407 (*input_setter)(params); 1329 (*input_setter)(params);
1408 } 1330 }
@@ -1419,15 +1341,20 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params)
1419 return true; 1341 return true;
1420 } 1342 }
1421 1343
1344 if (params.Has("motion")) {
1345 return true;
1346 }
1347
1422 // Keyboard/Mouse 1348 // Keyboard/Mouse
1423 if (ui->comboDevices->currentIndex() == 1) { 1349 if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
1424 return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; 1350 return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
1425 } 1351 }
1426 1352
1427 const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; 1353 const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
1428 return params.Get("engine", "") == current_input_device.Get("class", "") && 1354 return params.Get("engine", "") == current_input_device.Get("engine", "") &&
1429 params.Get("guid", "") == current_input_device.Get("guid", "") && 1355 (params.Get("guid", "") == current_input_device.Get("guid", "") ||
1430 params.Get("port", "") == current_input_device.Get("port", ""); 1356 params.Get("guid", "") == current_input_device.Get("guid2", "")) &&
1357 params.Get("port", 0) == current_input_device.Get("port", 0);
1431} 1358}
1432 1359
1433void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { 1360void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
@@ -1436,25 +1363,17 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1436 } 1363 }
1437 1364
1438 const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); 1365 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
1439 input_subsystem->GetMouse()->PressButton(0, 0, button); 1366 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
1440} 1367}
1441 1368
1442void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1369void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1370 event->ignore();
1443 if (!input_setter || !event) { 1371 if (!input_setter || !event) {
1444 return; 1372 return;
1445 } 1373 }
1446
1447 if (event->key() != Qt::Key_Escape) { 1374 if (event->key() != Qt::Key_Escape) {
1448 if (want_keyboard_mouse) { 1375 input_subsystem->GetKeyboard()->PressKey(event->key());
1449 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
1450 false);
1451 } else {
1452 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
1453 return;
1454 }
1455 } 1376 }
1456
1457 SetPollingResult({}, true);
1458} 1377}
1459 1378
1460void ConfigureInputPlayer::CreateProfile() { 1379void ConfigureInputPlayer::CreateProfile() {
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 39b44b8a5..47df6b3d3 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -29,48 +29,37 @@ class QWidget;
29 29
30class InputProfiles; 30class InputProfiles;
31 31
32namespace Core {
33class System;
34}
35
36namespace InputCommon { 32namespace InputCommon {
37class InputSubsystem; 33class InputSubsystem;
38} 34}
39 35
40namespace InputCommon::Polling { 36namespace InputCommon::Polling {
41class DevicePoller; 37enum class InputType;
42enum class DeviceType;
43} // namespace InputCommon::Polling 38} // namespace InputCommon::Polling
44 39
45namespace Ui { 40namespace Ui {
46class ConfigureInputPlayer; 41class ConfigureInputPlayer;
47} 42}
48 43
44namespace Core::HID {
45class HIDCore;
46class EmulatedController;
47enum class NpadStyleIndex : u8;
48} // namespace Core::HID
49
49class ConfigureInputPlayer : public QWidget { 50class ConfigureInputPlayer : public QWidget {
50 Q_OBJECT 51 Q_OBJECT
51 52
52public: 53public:
53 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, 54 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
54 InputCommon::InputSubsystem* input_subsystem_, 55 InputCommon::InputSubsystem* input_subsystem_,
55 InputProfiles* profiles_, Core::System& system_, 56 InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
56 bool debug = false); 57 bool is_powered_on_, bool debug = false);
57 ~ConfigureInputPlayer() override; 58 ~ConfigureInputPlayer() override;
58 59
59 /// Save all button configurations to settings file. 60 /// Save all button configurations to settings file.
60 void ApplyConfiguration(); 61 void ApplyConfiguration();
61 62
62 /**
63 * Attempts to connect the currently selected controller in the HID backend.
64 * This function will not do anything if it is not connected in the frontend.
65 */
66 void TryConnectSelectedController();
67
68 /**
69 * Attempts to disconnect the currently selected controller in the HID backend.
70 * This function will not do anything if the configuration has not changed.
71 */
72 void TryDisconnectSelectedController();
73
74 /// Set the connection state checkbox (used to sync state). 63 /// Set the connection state checkbox (used to sync state).
75 void ConnectPlayer(bool connected); 64 void ConnectPlayer(bool connected);
76 65
@@ -104,6 +93,10 @@ protected:
104 void showEvent(QShowEvent* event) override; 93 void showEvent(QShowEvent* event) override;
105 94
106private: 95private:
96 QString ButtonToText(const Common::ParamPackage& param);
97
98 QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
99
107 void changeEvent(QEvent* event) override; 100 void changeEvent(QEvent* event) override;
108 void RetranslateUI(); 101 void RetranslateUI();
109 102
@@ -113,7 +106,7 @@ private:
113 /// Called when the button was pressed. 106 /// Called when the button was pressed.
114 void HandleClick(QPushButton* button, std::size_t button_id, 107 void HandleClick(QPushButton* button, std::size_t button_id,
115 std::function<void(const Common::ParamPackage&)> new_input_setter, 108 std::function<void(const Common::ParamPackage&)> new_input_setter,
116 InputCommon::Polling::DeviceType type); 109 InputCommon::Polling::InputType type);
117 110
118 /// Finish polling and configure input using the input_setter. 111 /// Finish polling and configure input using the input_setter.
119 void SetPollingResult(const Common::ParamPackage& params, bool abort); 112 void SetPollingResult(const Common::ParamPackage& params, bool abort);
@@ -134,17 +127,14 @@ private:
134 void SetConnectableControllers(); 127 void SetConnectableControllers();
135 128
136 /// Gets the Controller Type for a given controller combobox index. 129 /// Gets the Controller Type for a given controller combobox index.
137 Settings::ControllerType GetControllerTypeFromIndex(int index) const; 130 Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index) const;
138 131
139 /// Gets the controller combobox index for a given Controller Type. 132 /// Gets the controller combobox index for a given Controller Type.
140 int GetIndexFromControllerType(Settings::ControllerType type) const; 133 int GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const;
141 134
142 /// Update the available input devices. 135 /// Update the available input devices.
143 void UpdateInputDevices(); 136 void UpdateInputDevices();
144 137
145 /// Update the current controller icon.
146 void UpdateControllerIcon();
147
148 /// Hides and disables controller settings based on the current controller type. 138 /// Hides and disables controller settings based on the current controller type.
149 void UpdateControllerAvailableButtons(); 139 void UpdateControllerAvailableButtons();
150 140
@@ -176,6 +166,7 @@ private:
176 166
177 std::size_t player_index; 167 std::size_t player_index;
178 bool debug; 168 bool debug;
169 bool is_powered_on;
179 170
180 InputCommon::InputSubsystem* input_subsystem; 171 InputCommon::InputSubsystem* input_subsystem;
181 172
@@ -185,7 +176,7 @@ private:
185 std::unique_ptr<QTimer> poll_timer; 176 std::unique_ptr<QTimer> poll_timer;
186 177
187 /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. 178 /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
188 std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs; 179 std::vector<std::pair<int, Core::HID::NpadStyleIndex>> index_controller_type_pairs;
189 180
190 static constexpr int PLAYER_COUNT = 8; 181 static constexpr int PLAYER_COUNT = 8;
191 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; 182 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
@@ -193,9 +184,7 @@ private:
193 /// This will be the the setting function when an input is awaiting configuration. 184 /// This will be the the setting function when an input is awaiting configuration.
194 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 185 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
195 186
196 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 187 Core::HID::EmulatedController* emulated_controller;
197 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
198 std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
199 188
200 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; 189 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
201 190
@@ -221,15 +210,9 @@ private:
221 210
222 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 211 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
223 212
224 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
225
226 /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. 213 /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
227 bool map_analog_stick_accepted{}; 214 bool map_analog_stick_accepted{};
228 215
229 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
230 /// keyboard events are ignored.
231 bool want_keyboard_mouse{};
232
233 /// List of physical devices users can map with. If a SDL backed device is selected, then you 216 /// List of physical devices users can map with. If a SDL backed device is selected, then you
234 /// can use this device to get a default mapping. 217 /// can use this device to get a default mapping.
235 std::vector<Common::ParamPackage> input_devices; 218 std::vector<Common::ParamPackage> input_devices;
@@ -239,5 +222,5 @@ private:
239 /// parent of the widget to this widget (but thats fine). 222 /// parent of the widget to this widget (but thats fine).
240 QWidget* bottom_row; 223 QWidget* bottom_row;
241 224
242 Core::System& system; 225 Core::HID::HIDCore& hid_core;
243}; 226};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index e7433912b..756a414b5 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -89,31 +89,6 @@
89 <height>21</height> 89 <height>21</height>
90 </size> 90 </size>
91 </property> 91 </property>
92 <item>
93 <property name="text">
94 <string>Pro Controller</string>
95 </property>
96 </item>
97 <item>
98 <property name="text">
99 <string>Dual Joycons</string>
100 </property>
101 </item>
102 <item>
103 <property name="text">
104 <string>Left Joycon</string>
105 </property>
106 </item>
107 <item>
108 <property name="text">
109 <string>Right Joycon</string>
110 </property>
111 </item>
112 <item>
113 <property name="text">
114 <string>Handheld</string>
115 </property>
116 </item>
117 </widget> 92 </widget>
118 </item> 93 </item>
119 </layout> 94 </layout>
@@ -142,22 +117,9 @@
142 </property> 117 </property>
143 <item> 118 <item>
144 <widget class="QComboBox" name="comboDevices"> 119 <widget class="QComboBox" name="comboDevices">
145 <property name="minimumSize"> 120 <property name="minimumContentsLength">
146 <size> 121 <number>60</number>
147 <width>0</width>
148 <height>21</height>
149 </size>
150 </property> 122 </property>
151 <item>
152 <property name="text">
153 <string>Any</string>
154 </property>
155 </item>
156 <item>
157 <property name="text">
158 <string>Keyboard/Mouse</string>
159 </property>
160 </item>
161 </widget> 123 </widget>
162 </item> 124 </item>
163 <item> 125 <item>
@@ -342,7 +304,7 @@
342 <number>3</number> 304 <number>3</number>
343 </property> 305 </property>
344 <property name="topMargin"> 306 <property name="topMargin">
345 <number>0</number> 307 <number>6</number>
346 </property> 308 </property>
347 <property name="rightMargin"> 309 <property name="rightMargin">
348 <number>3</number> 310 <number>3</number>
@@ -918,7 +880,7 @@
918 <number>3</number> 880 <number>3</number>
919 </property> 881 </property>
920 <property name="topMargin"> 882 <property name="topMargin">
921 <number>0</number> 883 <number>6</number>
922 </property> 884 </property>
923 <property name="rightMargin"> 885 <property name="rightMargin">
924 <number>3</number> 886 <number>3</number>
@@ -2221,7 +2183,7 @@
2221 <number>3</number> 2183 <number>3</number>
2222 </property> 2184 </property>
2223 <property name="topMargin"> 2185 <property name="topMargin">
2224 <number>0</number> 2186 <number>6</number>
2225 </property> 2187 </property>
2226 <property name="rightMargin"> 2188 <property name="rightMargin">
2227 <number>3</number> 2189 <number>3</number>
@@ -2570,7 +2532,7 @@
2570 <number>3</number> 2532 <number>3</number>
2571 </property> 2533 </property>
2572 <property name="topMargin"> 2534 <property name="topMargin">
2573 <number>0</number> 2535 <number>6</number>
2574 </property> 2536 </property>
2575 <property name="rightMargin"> 2537 <property name="rightMargin">
2576 <number>3</number> 2538 <number>3</number>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index f31f86339..6630321cb 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -6,10 +6,12 @@
6#include <QMenu> 6#include <QMenu>
7#include <QPainter> 7#include <QPainter>
8#include <QTimer> 8#include <QTimer>
9
10#include "core/hid/emulated_controller.h"
9#include "yuzu/configuration/configure_input_player_widget.h" 11#include "yuzu/configuration/configure_input_player_widget.h"
10 12
11PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) { 13PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
12 UpdateColors(); 14 is_controller_set = false;
13 QTimer* timer = new QTimer(this); 15 QTimer* timer = new QTimer(this);
14 connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput)); 16 connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput));
15 17
@@ -17,91 +19,37 @@ PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
17 timer->start(16); 19 timer->start(16);
18} 20}
19 21
20PlayerControlPreview::~PlayerControlPreview() = default; 22PlayerControlPreview::~PlayerControlPreview() {
21 23 UnloadController();
22void PlayerControlPreview::SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, 24};
23 const AnalogParam& analogs_param) {
24 player_index = index;
25 Settings::ButtonsRaw buttonss;
26 Settings::AnalogsRaw analogs;
27 std::transform(buttons_param.begin(), buttons_param.end(), buttonss.begin(),
28 [](const Common::ParamPackage& param) { return param.Serialize(); });
29 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
30 [](const Common::ParamPackage& param) { return param.Serialize(); });
31
32 std::transform(buttonss.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
33 buttonss.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(),
34 Input::CreateDevice<Input::ButtonDevice>);
35 std::transform(analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
36 analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(),
37 Input::CreateDevice<Input::AnalogDevice>);
38 UpdateColors();
39}
40void PlayerControlPreview::SetPlayerInputRaw(std::size_t index,
41 const Settings::ButtonsRaw& buttons_,
42 Settings::AnalogsRaw analogs_) {
43 player_index = index;
44 std::transform(buttons_.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
45 buttons_.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(),
46 Input::CreateDevice<Input::ButtonDevice>);
47 std::transform(analogs_.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
48 analogs_.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(),
49 Input::CreateDevice<Input::AnalogDevice>);
50 UpdateColors();
51}
52
53PlayerControlPreview::LedPattern PlayerControlPreview::GetColorPattern(std::size_t index,
54 bool player_on) {
55 if (!player_on) {
56 return {0, 0, 0, 0};
57 }
58
59 switch (index) {
60 case 0:
61 return {1, 0, 0, 0};
62 case 1:
63 return {1, 1, 0, 0};
64 case 2:
65 return {1, 1, 1, 0};
66 case 3:
67 return {1, 1, 1, 1};
68 case 4:
69 return {1, 0, 0, 1};
70 case 5:
71 return {1, 0, 1, 0};
72 case 6:
73 return {1, 0, 1, 1};
74 case 7:
75 return {0, 1, 1, 0};
76 default:
77 return {0, 0, 0, 0};
78 }
79}
80
81void PlayerControlPreview::SetConnectedStatus(bool checked) {
82 LedPattern led_pattern = GetColorPattern(player_index, checked);
83 25
84 led_color[0] = led_pattern.position1 ? colors.led_on : colors.led_off; 26void PlayerControlPreview::SetController(Core::HID::EmulatedController* controller_) {
85 led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off; 27 UnloadController();
86 led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off; 28 is_controller_set = true;
87 led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off; 29 controller = controller_;
88 is_enabled = checked; 30 Core::HID::ControllerUpdateCallback engine_callback{
89 ResetInputs(); 31 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
32 .is_npad_service = false,
33 };
34 callback_key = controller->SetCallback(engine_callback);
35 ControllerUpdate(Core::HID::ControllerTriggerType::All);
90} 36}
91 37
92void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) { 38void PlayerControlPreview::UnloadController() {
93 controller_type = type; 39 if (is_controller_set) {
94 UpdateColors(); 40 controller->DeleteCallback(callback_key);
41 is_controller_set = false;
42 }
95} 43}
96 44
97void PlayerControlPreview::BeginMappingButton(std::size_t index) { 45void PlayerControlPreview::BeginMappingButton(std::size_t button_id) {
98 button_mapping_index = index; 46 button_mapping_index = button_id;
99 mapping_active = true; 47 mapping_active = true;
100} 48}
101 49
102void PlayerControlPreview::BeginMappingAnalog(std::size_t index) { 50void PlayerControlPreview::BeginMappingAnalog(std::size_t stick_id) {
103 button_mapping_index = Settings::NativeButton::LStick + index; 51 button_mapping_index = Settings::NativeButton::LStick + stick_id;
104 analog_mapping_index = index; 52 analog_mapping_index = stick_id;
105 mapping_active = true; 53 mapping_active = true;
106} 54}
107 55
@@ -157,84 +105,109 @@ void PlayerControlPreview::UpdateColors() {
157 colors.left = colors.primary; 105 colors.left = colors.primary;
158 colors.right = colors.primary; 106 colors.right = colors.primary;
159 // Possible alternative to set colors from settings 107 // Possible alternative to set colors from settings
160 // colors.left = QColor(Settings::values.players.GetValue()[player_index].body_color_left); 108 // colors.left = QColor(controller->GetColors().left.body);
161 // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right); 109 // colors.right = QColor(controller->GetColors().right.body);
162} 110}
163 111
164void PlayerControlPreview::ResetInputs() { 112void PlayerControlPreview::ResetInputs() {
165 for (std::size_t index = 0; index < button_values.size(); ++index) { 113 button_values.fill({
166 button_values[index] = false; 114 .value = false,
167 } 115 });
168 116 stick_values.fill({
169 for (std::size_t index = 0; index < axis_values.size(); ++index) { 117 .x = {.value = 0, .properties = {0, 1, 0}},
170 axis_values[index].properties = {0, 1, 0}; 118 .y = {.value = 0, .properties = {0, 1, 0}},
171 axis_values[index].value = {0, 0}; 119 });
172 axis_values[index].raw_value = {0, 0}; 120 trigger_values.fill({
173 } 121 .analog = {.value = 0, .properties = {0, 1, 0}},
122 .pressed = {.value = false},
123 });
174 update(); 124 update();
175} 125}
176 126
177void PlayerControlPreview::UpdateInput() { 127void PlayerControlPreview::ControllerUpdate(Core::HID::ControllerTriggerType type) {
178 if (!is_enabled && !mapping_active && !Settings::values.tas_enable) { 128 if (type == Core::HID::ControllerTriggerType::All) {
129 ControllerUpdate(Core::HID::ControllerTriggerType::Color);
130 ControllerUpdate(Core::HID::ControllerTriggerType::Type);
131 ControllerUpdate(Core::HID::ControllerTriggerType::Connected);
132 ControllerUpdate(Core::HID::ControllerTriggerType::Button);
133 ControllerUpdate(Core::HID::ControllerTriggerType::Stick);
134 ControllerUpdate(Core::HID::ControllerTriggerType::Trigger);
135 ControllerUpdate(Core::HID::ControllerTriggerType::Battery);
179 return; 136 return;
180 } 137 }
181 bool input_changed = false; 138
182 const auto& button_state = buttons; 139 switch (type) {
183 for (std::size_t index = 0; index < button_values.size(); ++index) { 140 case Core::HID::ControllerTriggerType::Connected:
184 bool value = false; 141 is_connected = true;
185 if (index < Settings::NativeButton::BUTTON_NS_END) { 142 led_pattern = controller->GetLedPattern();
186 value = button_state[index]->GetStatus(); 143 needs_redraw = true;
187 } 144 break;
188 bool blink = mapping_active && index == button_mapping_index; 145 case Core::HID::ControllerTriggerType::Disconnected:
189 if (analog_mapping_index == Settings::NativeAnalog::NUM_STICKS_HID) { 146 is_connected = false;
190 blink &= blink_counter > 25; 147 led_pattern.raw = 0;
191 } 148 needs_redraw = true;
192 if (button_values[index] != value || blink) { 149 break;
193 input_changed = true; 150 case Core::HID::ControllerTriggerType::Type:
194 } 151 controller_type = controller->GetNpadStyleIndex(true);
195 button_values[index] = value || blink; 152 needs_redraw = true;
153 break;
154 case Core::HID::ControllerTriggerType::Color:
155 UpdateColors();
156 needs_redraw = true;
157 break;
158 case Core::HID::ControllerTriggerType::Button:
159 button_values = controller->GetButtonsValues();
160 needs_redraw = true;
161 break;
162 case Core::HID::ControllerTriggerType::Stick:
163 using namespace Settings::NativeAnalog;
164 stick_values = controller->GetSticksValues();
165 // Y axis is inverted
166 stick_values[LStick].y.value = -stick_values[LStick].y.value;
167 stick_values[LStick].y.raw_value = -stick_values[LStick].y.raw_value;
168 stick_values[RStick].y.value = -stick_values[RStick].y.value;
169 stick_values[RStick].y.raw_value = -stick_values[RStick].y.raw_value;
170 needs_redraw = true;
171 break;
172 case Core::HID::ControllerTriggerType::Trigger:
173 trigger_values = controller->GetTriggersValues();
174 needs_redraw = true;
175 break;
176 case Core::HID::ControllerTriggerType::Battery:
177 battery_values = controller->GetBatteryValues();
178 needs_redraw = true;
179 break;
180 default:
181 break;
196 } 182 }
183}
197 184
198 const auto& analog_state = sticks; 185void PlayerControlPreview::UpdateInput() {
199 for (std::size_t index = 0; index < axis_values.size(); ++index) { 186 if (mapping_active) {
200 const auto [stick_x_f, stick_y_f] = analog_state[index]->GetStatus();
201 const auto [stick_x_rf, stick_y_rf] = analog_state[index]->GetRawStatus();
202 187
203 if (static_cast<int>(stick_x_rf * 45) != 188 for (std::size_t index = 0; index < button_values.size(); ++index) {
204 static_cast<int>(axis_values[index].raw_value.x() * 45) || 189 bool blink = index == button_mapping_index;
205 static_cast<int>(-stick_y_rf * 45) != 190 if (analog_mapping_index == Settings::NativeAnalog::NumAnalogs) {
206 static_cast<int>(axis_values[index].raw_value.y() * 45)) { 191 blink &= blink_counter > 25;
207 input_changed = true; 192 }
193 if (button_values[index].value != blink) {
194 needs_redraw = true;
195 }
196 button_values[index].value = blink;
208 } 197 }
209 198
210 axis_values[index].properties = analog_state[index]->GetAnalogProperties(); 199 for (std::size_t index = 0; index < stick_values.size(); ++index) {
211 axis_values[index].value = QPointF(stick_x_f, -stick_y_f); 200 const bool blink_analog = index == analog_mapping_index;
212 axis_values[index].raw_value = QPointF(stick_x_rf, -stick_y_rf); 201 if (blink_analog) {
213 202 needs_redraw = true;
214 const bool blink_analog = mapping_active && index == analog_mapping_index; 203 stick_values[index].x.value = blink_counter < 25 ? -blink_counter / 25.0f : 0;
215 if (blink_analog) { 204 stick_values[index].y.value =
216 input_changed = true; 205 blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0;
217 axis_values[index].value = 206 }
218 QPointF(blink_counter < 25 ? -blink_counter / 25.0f : 0,
219 blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0);
220 } 207 }
221 } 208 }
222 209 if (needs_redraw) {
223 if (input_changed) {
224 update(); 210 update();
225 if (controller_callback.input != nullptr) {
226 ControllerInput input{
227 .axis_values = {std::pair<float, float>{
228 axis_values[Settings::NativeAnalog::LStick].value.x(),
229 axis_values[Settings::NativeAnalog::LStick].value.y()},
230 std::pair<float, float>{
231 axis_values[Settings::NativeAnalog::RStick].value.x(),
232 axis_values[Settings::NativeAnalog::RStick].value.y()}},
233 .button_values = button_values,
234 .changed = true,
235 };
236 controller_callback.input(std::move(input));
237 }
238 } 211 }
239 212
240 if (mapping_active) { 213 if (mapping_active) {
@@ -242,10 +215,6 @@ void PlayerControlPreview::UpdateInput() {
242 } 215 }
243} 216}
244 217
245void PlayerControlPreview::SetCallBack(ControllerCallback callback_) {
246 controller_callback = std::move(callback_);
247}
248
249void PlayerControlPreview::paintEvent(QPaintEvent* event) { 218void PlayerControlPreview::paintEvent(QPaintEvent* event) {
250 QFrame::paintEvent(event); 219 QFrame::paintEvent(event);
251 QPainter p(this); 220 QPainter p(this);
@@ -253,22 +222,22 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
253 const QPointF center = rect().center(); 222 const QPointF center = rect().center();
254 223
255 switch (controller_type) { 224 switch (controller_type) {
256 case Settings::ControllerType::Handheld: 225 case Core::HID::NpadStyleIndex::Handheld:
257 DrawHandheldController(p, center); 226 DrawHandheldController(p, center);
258 break; 227 break;
259 case Settings::ControllerType::DualJoyconDetached: 228 case Core::HID::NpadStyleIndex::JoyconDual:
260 DrawDualController(p, center); 229 DrawDualController(p, center);
261 break; 230 break;
262 case Settings::ControllerType::LeftJoycon: 231 case Core::HID::NpadStyleIndex::JoyconLeft:
263 DrawLeftController(p, center); 232 DrawLeftController(p, center);
264 break; 233 break;
265 case Settings::ControllerType::RightJoycon: 234 case Core::HID::NpadStyleIndex::JoyconRight:
266 DrawRightController(p, center); 235 DrawRightController(p, center);
267 break; 236 break;
268 case Settings::ControllerType::GameCube: 237 case Core::HID::NpadStyleIndex::GameCube:
269 DrawGCController(p, center); 238 DrawGCController(p, center);
270 break; 239 break;
271 case Settings::ControllerType::ProController: 240 case Core::HID::NpadStyleIndex::ProController:
272 default: 241 default:
273 DrawProController(p, center); 242 DrawProController(p, center);
274 break; 243 break;
@@ -281,7 +250,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
281 250
282 // Sideview left joystick 251 // Sideview left joystick
283 DrawJoystickSideview(p, center + QPoint(142, -69), 252 DrawJoystickSideview(p, center + QPoint(142, -69),
284 -axis_values[Settings::NativeAnalog::LStick].value.y(), 1.15f, 253 -stick_values[Settings::NativeAnalog::LStick].y.value, 1.15f,
285 button_values[LStick]); 254 button_values[LStick]);
286 255
287 // Topview D-pad buttons 256 // Topview D-pad buttons
@@ -292,7 +261,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
292 261
293 // Topview left joystick 262 // Topview left joystick
294 DrawJoystickSideview(p, center + QPointF(-140.5f, -28), 263 DrawJoystickSideview(p, center + QPointF(-140.5f, -28),
295 -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1.15f, 264 -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1.15f,
296 button_values[LStick]); 265 button_values[LStick]);
297 266
298 // Topview minus button 267 // Topview minus button
@@ -334,8 +303,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
334 { 303 {
335 // Draw joysticks 304 // Draw joysticks
336 using namespace Settings::NativeAnalog; 305 using namespace Settings::NativeAnalog;
337 DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f, 306 DrawJoystick(p,
338 button_values[Settings::NativeButton::LStick]); 307 center + QPointF(9, -69) +
308 (QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value) * 8),
309 1.8f, button_values[Settings::NativeButton::LStick]);
339 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0)); 310 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
340 } 311 }
341 312
@@ -384,6 +355,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
384 p.setPen(colors.font2); 355 p.setPen(colors.font2);
385 p.setBrush(colors.font2); 356 p.setBrush(colors.font2);
386 DrawCircle(p, center + QPoint(26, 71), 5); 357 DrawCircle(p, center + QPoint(26, 71), 5);
358
359 // Draw battery
360 DrawBattery(p, center + QPoint(-170, -140),
361 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
387} 362}
388 363
389void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) { 364void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) {
@@ -392,20 +367,22 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
392 367
393 // Sideview right joystick 368 // Sideview right joystick
394 DrawJoystickSideview(p, center + QPoint(173 - 315, 11), 369 DrawJoystickSideview(p, center + QPoint(173 - 315, 11),
395 axis_values[Settings::NativeAnalog::RStick].value.y() + 10.0f, 1.15f, 370 stick_values[Settings::NativeAnalog::RStick].y.value + 10.0f, 1.15f,
396 button_values[Settings::NativeButton::RStick]); 371 button_values[Settings::NativeButton::RStick]);
397 372
373 // Topview right joystick
374 DrawJoystickSideview(p, center + QPointF(140, -28),
375 -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1.15f,
376 button_values[RStick]);
377
398 // Topview face buttons 378 // Topview face buttons
399 p.setPen(colors.outline); 379 p.setPen(colors.outline);
400 button_color = colors.button; 380 button_color = colors.button;
401 DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up); 381 DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up);
382 DrawRoundButton(p, center + QPoint(140, -21), button_values[B], 11, 5, Direction::Up);
383 DrawRoundButton(p, center + QPoint(140, -21), button_values[X], 11, 5, Direction::Up);
402 DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up); 384 DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up);
403 385
404 // Topview right joystick
405 DrawJoystickSideview(p, center + QPointF(140, -28),
406 -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1.15f,
407 button_values[RStick]);
408
409 // Topview plus button 386 // Topview plus button
410 p.setPen(colors.outline); 387 p.setPen(colors.outline);
411 button_color = colors.button; 388 button_color = colors.button;
@@ -448,8 +425,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
448 { 425 {
449 // Draw joysticks 426 // Draw joysticks
450 using namespace Settings::NativeAnalog; 427 using namespace Settings::NativeAnalog;
451 DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f, 428 DrawJoystick(p,
452 button_values[Settings::NativeButton::RStick]); 429 center + QPointF(-9, 11) +
430 (QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value) * 8),
431 1.8f, button_values[Settings::NativeButton::RStick]);
453 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90)); 432 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
454 } 433 }
455 434
@@ -503,6 +482,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
503 p.setPen(colors.transparent); 482 p.setPen(colors.transparent);
504 p.setBrush(colors.font2); 483 p.setBrush(colors.font2);
505 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); 484 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5);
485
486 // Draw battery
487 DrawBattery(p, center + QPoint(110, -140),
488 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
506} 489}
507 490
508void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) { 491void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) {
@@ -512,17 +495,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
512 // Left/Right trigger 495 // Left/Right trigger
513 DrawDualTriggers(p, center, button_values[L], button_values[R]); 496 DrawDualTriggers(p, center, button_values[L], button_values[R]);
514 497
498 // Topview right joystick
499 DrawJoystickSideview(p, center + QPointF(180, -78),
500 -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1,
501 button_values[RStick]);
502
515 // Topview face buttons 503 // Topview face buttons
516 p.setPen(colors.outline); 504 p.setPen(colors.outline);
517 button_color = colors.button; 505 button_color = colors.button;
518 DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up); 506 DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up);
507 DrawRoundButton(p, center + QPoint(180, -71), button_values[B], 10, 5, Direction::Up);
508 DrawRoundButton(p, center + QPoint(180, -71), button_values[X], 10, 5, Direction::Up);
519 DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up); 509 DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up);
520 510
521 // Topview right joystick
522 DrawJoystickSideview(p, center + QPointF(180, -78),
523 -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1,
524 button_values[RStick]);
525
526 // Topview plus button 511 // Topview plus button
527 p.setPen(colors.outline); 512 p.setPen(colors.outline);
528 button_color = colors.button; 513 button_color = colors.button;
@@ -538,7 +523,7 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
538 523
539 // Topview left joystick 524 // Topview left joystick
540 DrawJoystickSideview(p, center + QPointF(-180.5f, -78), 525 DrawJoystickSideview(p, center + QPointF(-180.5f, -78),
541 -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1, 526 -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1,
542 button_values[LStick]); 527 button_values[LStick]);
543 528
544 // Topview minus button 529 // Topview minus button
@@ -557,13 +542,13 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
557 { 542 {
558 // Draw joysticks 543 // Draw joysticks
559 using namespace Settings::NativeAnalog; 544 using namespace Settings::NativeAnalog;
560 const auto& l_stick = axis_values[LStick]; 545 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
561 const auto l_button = button_values[Settings::NativeButton::LStick]; 546 const auto l_button = button_values[Settings::NativeButton::LStick];
562 const auto& r_stick = axis_values[RStick]; 547 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
563 const auto r_button = button_values[Settings::NativeButton::RStick]; 548 const auto r_button = button_values[Settings::NativeButton::RStick];
564 549
565 DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button); 550 DrawJoystick(p, center + QPointF(-65, -65) + (l_stick * 7), 1.62f, l_button);
566 DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button); 551 DrawJoystick(p, center + QPointF(65, 12) + (r_stick * 7), 1.62f, r_button);
567 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90)); 552 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
568 } 553 }
569 554
@@ -634,6 +619,12 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
634 p.setPen(colors.transparent); 619 p.setPen(colors.transparent);
635 p.setBrush(colors.font2); 620 p.setBrush(colors.font2);
636 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); 621 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f);
622
623 // Draw battery
624 DrawBattery(p, center + QPoint(-100, -160),
625 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
626 DrawBattery(p, center + QPoint(40, -160),
627 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
637} 628}
638 629
639void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) { 630void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) {
@@ -643,13 +634,13 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
643 { 634 {
644 // Draw joysticks 635 // Draw joysticks
645 using namespace Settings::NativeAnalog; 636 using namespace Settings::NativeAnalog;
646 const auto& l_stick = axis_values[LStick]; 637 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
647 const auto l_button = button_values[Settings::NativeButton::LStick]; 638 const auto l_button = button_values[Settings::NativeButton::LStick];
648 const auto& r_stick = axis_values[RStick]; 639 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
649 const auto r_button = button_values[Settings::NativeButton::RStick]; 640 const auto r_button = button_values[Settings::NativeButton::RStick];
650 641
651 DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button); 642 DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button);
652 DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button); 643 DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button);
653 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0)); 644 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
654 } 645 }
655 646
@@ -732,6 +723,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
732 p.setPen(colors.transparent); 723 p.setPen(colors.transparent);
733 p.setBrush(colors.font2); 724 p.setBrush(colors.font2);
734 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); 725 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f);
726
727 // Draw battery
728 DrawBattery(p, center + QPoint(-200, 110),
729 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
730 DrawBattery(p, center + QPoint(130, 110),
731 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
735} 732}
736 733
737void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) { 734void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) {
@@ -741,9 +738,11 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
741 { 738 {
742 // Draw joysticks 739 // Draw joysticks
743 using namespace Settings::NativeAnalog; 740 using namespace Settings::NativeAnalog;
744 DrawProJoystick(p, center + QPointF(-111, -55), axis_values[LStick].value, 11, 741 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
742 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
743 DrawProJoystick(p, center + QPointF(-111, -55), l_stick, 11,
745 button_values[Settings::NativeButton::LStick]); 744 button_values[Settings::NativeButton::LStick]);
746 DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11, 745 DrawProJoystick(p, center + QPointF(51, 0), r_stick, 11,
747 button_values[Settings::NativeButton::RStick]); 746 button_values[Settings::NativeButton::RStick]);
748 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105)); 747 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
749 } 748 }
@@ -817,24 +816,27 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
817 p.setPen(colors.transparent); 816 p.setPen(colors.transparent);
818 p.setBrush(colors.font2); 817 p.setBrush(colors.font2);
819 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); 818 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f);
819
820 // Draw battery
821 DrawBattery(p, center + QPoint(-30, -160),
822 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
820} 823}
821 824
822void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { 825void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
823 DrawGCTriggers(p, center, button_values[Settings::NativeButton::ZL], 826 DrawGCTriggers(p, center, trigger_values[0], trigger_values[1]);
824 button_values[Settings::NativeButton::ZR]);
825 DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]); 827 DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]);
826 DrawGCBody(p, center); 828 DrawGCBody(p, center);
827 { 829 {
828 // Draw joysticks 830 // Draw joysticks
829 using namespace Settings::NativeAnalog; 831 using namespace Settings::NativeAnalog;
830 DrawGCJoystick(p, center + QPointF(-111, -44) + (axis_values[LStick].value * 10), false); 832 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
833 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
834 DrawGCJoystick(p, center + QPointF(-111, -44) + (l_stick * 10), {});
831 button_color = colors.button2; 835 button_color = colors.button2;
832 DrawCircleButton(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), false, 836 DrawCircleButton(p, center + QPointF(61, 37) + (r_stick * 9.5f), {}, 15);
833 15);
834 p.setPen(colors.transparent); 837 p.setPen(colors.transparent);
835 p.setBrush(colors.font); 838 p.setBrush(colors.font);
836 DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C, 839 DrawSymbol(p, center + QPointF(61, 37) + (r_stick * 9.5f), Symbol::C, 1.0f);
837 1.0f);
838 DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125)); 840 DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
839 } 841 }
840 842
@@ -871,6 +873,10 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
871 // Minus and Plus buttons 873 // Minus and Plus buttons
872 p.setPen(colors.outline); 874 p.setPen(colors.outline);
873 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); 875 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8);
876
877 // Draw battery
878 DrawBattery(p, center + QPoint(-30, -165),
879 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
874} 880}
875 881
876constexpr std::array<float, 13 * 2> symbol_a = { 882constexpr std::array<float, 13 * 2> symbol_a = {
@@ -1837,10 +1843,14 @@ void PlayerControlPreview::DrawLeftBody(QPainter& p, const QPointF center) {
1837 const float led_size = 5.0f; 1843 const float led_size = 5.0f;
1838 const QPointF led_position = sideview_center + QPointF(0, -36); 1844 const QPointF led_position = sideview_center + QPointF(0, -36);
1839 int led_count = 0; 1845 int led_count = 0;
1840 for (const auto& color : led_color) { 1846 p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off);
1841 p.setBrush(color); 1847 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1842 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); 1848 p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off);
1843 } 1849 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1850 p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off);
1851 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1852 p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off);
1853 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1844} 1854}
1845 1855
1846void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) { 1856void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) {
@@ -1933,14 +1943,19 @@ void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) {
1933 const float led_size = 5.0f; 1943 const float led_size = 5.0f;
1934 const QPointF led_position = sideview_center + QPointF(0, -36); 1944 const QPointF led_position = sideview_center + QPointF(0, -36);
1935 int led_count = 0; 1945 int led_count = 0;
1936 for (const auto& color : led_color) { 1946 p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off);
1937 p.setBrush(color); 1947 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1938 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); 1948 p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off);
1939 } 1949 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1940} 1950 p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off);
1941 1951 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1942void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bool left_pressed, 1952 p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off);
1943 bool right_pressed) { 1953 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1954}
1955
1956void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center,
1957 const Common::Input::ButtonStatus& left_pressed,
1958 const Common::Input::ButtonStatus& right_pressed) {
1944 std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger; 1959 std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger;
1945 std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger; 1960 std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger;
1946 std::array<QPointF, pro_body_top.size()> qbody_top; 1961 std::array<QPointF, pro_body_top.size()> qbody_top;
@@ -1949,8 +1964,10 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bo
1949 const float trigger_x = pro_left_trigger[point * 2 + 0]; 1964 const float trigger_x = pro_left_trigger[point * 2 + 0];
1950 const float trigger_y = pro_left_trigger[point * 2 + 1]; 1965 const float trigger_y = pro_left_trigger[point * 2 + 1];
1951 1966
1952 qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 2 : 0)); 1967 qleft_trigger[point] =
1953 qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 2 : 0)); 1968 center + QPointF(trigger_x, trigger_y + (left_pressed.value ? 2 : 0));
1969 qright_trigger[point] =
1970 center + QPointF(-trigger_x, trigger_y + (right_pressed.value ? 2 : 0));
1954 } 1971 }
1955 1972
1956 for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) { 1973 for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) {
@@ -1967,16 +1984,17 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bo
1967 DrawPolygon(p, qbody_top); 1984 DrawPolygon(p, qbody_top);
1968 1985
1969 // Left trigger 1986 // Left trigger
1970 p.setBrush(left_pressed ? colors.highlight : colors.button); 1987 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
1971 DrawPolygon(p, qleft_trigger); 1988 DrawPolygon(p, qleft_trigger);
1972 1989
1973 // Right trigger 1990 // Right trigger
1974 p.setBrush(right_pressed ? colors.highlight : colors.button); 1991 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
1975 DrawPolygon(p, qright_trigger); 1992 DrawPolygon(p, qright_trigger);
1976} 1993}
1977 1994
1978void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, bool left_pressed, 1995void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center,
1979 bool right_pressed) { 1996 Common::Input::TriggerStatus left_trigger,
1997 Common::Input::TriggerStatus right_trigger) {
1980 std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger; 1998 std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger;
1981 std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger; 1999 std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger;
1982 2000
@@ -1984,32 +2002,37 @@ void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, boo
1984 const float trigger_x = left_gc_trigger[point * 2 + 0]; 2002 const float trigger_x = left_gc_trigger[point * 2 + 0];
1985 const float trigger_y = left_gc_trigger[point * 2 + 1]; 2003 const float trigger_y = left_gc_trigger[point * 2 + 1];
1986 2004
1987 qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 10 : 0)); 2005 qleft_trigger[point] =
1988 qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 10 : 0)); 2006 center + QPointF(trigger_x, trigger_y + (left_trigger.analog.value * 10.0f));
2007 qright_trigger[point] =
2008 center + QPointF(-trigger_x, trigger_y + (right_trigger.analog.value * 10.0f));
1989 } 2009 }
1990 2010
1991 // Left trigger 2011 // Left trigger
1992 p.setPen(colors.outline); 2012 p.setPen(colors.outline);
1993 p.setBrush(left_pressed ? colors.highlight : colors.button); 2013 p.setBrush(left_trigger.pressed.value ? colors.highlight : colors.button);
1994 DrawPolygon(p, qleft_trigger); 2014 DrawPolygon(p, qleft_trigger);
1995 2015
1996 // Right trigger 2016 // Right trigger
1997 p.setBrush(right_pressed ? colors.highlight : colors.button); 2017 p.setBrush(right_trigger.pressed.value ? colors.highlight : colors.button);
1998 DrawPolygon(p, qright_trigger); 2018 DrawPolygon(p, qright_trigger);
1999 2019
2000 // Draw L text 2020 // Draw L text
2001 p.setPen(colors.transparent); 2021 p.setPen(colors.transparent);
2002 p.setBrush(colors.font); 2022 p.setBrush(colors.font);
2003 DrawSymbol(p, center + QPointF(-132, -119 + (left_pressed ? 10 : 0)), Symbol::L, 1.7f); 2023 DrawSymbol(p, center + QPointF(-132, -119 + (left_trigger.analog.value * 10.0f)), Symbol::L,
2024 1.7f);
2004 2025
2005 // Draw R text 2026 // Draw R text
2006 p.setPen(colors.transparent); 2027 p.setPen(colors.transparent);
2007 p.setBrush(colors.font); 2028 p.setBrush(colors.font);
2008 DrawSymbol(p, center + QPointF(121.5f, -119 + (right_pressed ? 10 : 0)), Symbol::R, 1.7f); 2029 DrawSymbol(p, center + QPointF(121.5f, -119 + (right_trigger.analog.value * 10.0f)), Symbol::R,
2030 1.7f);
2009} 2031}
2010 2032
2011void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center, 2033void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center,
2012 bool left_pressed, bool right_pressed) { 2034 const Common::Input::ButtonStatus& left_pressed,
2035 const Common::Input::ButtonStatus& right_pressed) {
2013 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; 2036 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
2014 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; 2037 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
2015 2038
@@ -2018,23 +2041,24 @@ void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF cente
2018 const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; 2041 const float left_trigger_y = left_joycon_trigger[point * 2 + 1];
2019 2042
2020 qleft_trigger[point] = 2043 qleft_trigger[point] =
2021 center + QPointF(left_trigger_x, left_trigger_y + (left_pressed ? 0.5f : 0)); 2044 center + QPointF(left_trigger_x, left_trigger_y + (left_pressed.value ? 0.5f : 0));
2022 qright_trigger[point] = 2045 qright_trigger[point] =
2023 center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed ? 0.5f : 0)); 2046 center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed.value ? 0.5f : 0));
2024 } 2047 }
2025 2048
2026 // Left trigger 2049 // Left trigger
2027 p.setPen(colors.outline); 2050 p.setPen(colors.outline);
2028 p.setBrush(left_pressed ? colors.highlight : colors.button); 2051 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2029 DrawPolygon(p, qleft_trigger); 2052 DrawPolygon(p, qleft_trigger);
2030 2053
2031 // Right trigger 2054 // Right trigger
2032 p.setBrush(right_pressed ? colors.highlight : colors.button); 2055 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2033 DrawPolygon(p, qright_trigger); 2056 DrawPolygon(p, qright_trigger);
2034} 2057}
2035 2058
2036void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, bool left_pressed, 2059void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center,
2037 bool right_pressed) { 2060 const Common::Input::ButtonStatus& left_pressed,
2061 const Common::Input::ButtonStatus& right_pressed) {
2038 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; 2062 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
2039 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; 2063 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
2040 constexpr float size = 1.62f; 2064 constexpr float size = 1.62f;
@@ -2043,25 +2067,27 @@ void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, b
2043 const float left_trigger_x = left_joycon_trigger[point * 2 + 0]; 2067 const float left_trigger_x = left_joycon_trigger[point * 2 + 0];
2044 const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; 2068 const float left_trigger_y = left_joycon_trigger[point * 2 + 1];
2045 2069
2046 qleft_trigger[point] = center + QPointF(left_trigger_x * size + offset, 2070 qleft_trigger[point] =
2047 left_trigger_y * size + (left_pressed ? 0.5f : 0)); 2071 center + QPointF(left_trigger_x * size + offset,
2072 left_trigger_y * size + (left_pressed.value ? 0.5f : 0));
2048 qright_trigger[point] = 2073 qright_trigger[point] =
2049 center + QPointF(-left_trigger_x * size - offset, 2074 center + QPointF(-left_trigger_x * size - offset,
2050 left_trigger_y * size + (right_pressed ? 0.5f : 0)); 2075 left_trigger_y * size + (right_pressed.value ? 0.5f : 0));
2051 } 2076 }
2052 2077
2053 // Left trigger 2078 // Left trigger
2054 p.setPen(colors.outline); 2079 p.setPen(colors.outline);
2055 p.setBrush(left_pressed ? colors.highlight : colors.button); 2080 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2056 DrawPolygon(p, qleft_trigger); 2081 DrawPolygon(p, qleft_trigger);
2057 2082
2058 // Right trigger 2083 // Right trigger
2059 p.setBrush(right_pressed ? colors.highlight : colors.button); 2084 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2060 DrawPolygon(p, qright_trigger); 2085 DrawPolygon(p, qright_trigger);
2061} 2086}
2062 2087
2063void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF center, 2088void PlayerControlPreview::DrawDualTriggersTopView(
2064 bool left_pressed, bool right_pressed) { 2089 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed,
2090 const Common::Input::ButtonStatus& right_pressed) {
2065 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; 2091 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger;
2066 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; 2092 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger;
2067 constexpr float size = 0.9f; 2093 constexpr float size = 0.9f;
@@ -2080,9 +2106,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF ce
2080 } 2106 }
2081 2107
2082 p.setPen(colors.outline); 2108 p.setPen(colors.outline);
2083 p.setBrush(left_pressed ? colors.highlight : colors.button); 2109 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2084 DrawPolygon(p, qleft_trigger); 2110 DrawPolygon(p, qleft_trigger);
2085 p.setBrush(right_pressed ? colors.highlight : colors.button); 2111 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2086 DrawPolygon(p, qright_trigger); 2112 DrawPolygon(p, qright_trigger);
2087 2113
2088 // Draw L text 2114 // Draw L text
@@ -2096,8 +2122,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF ce
2096 DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f); 2122 DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f);
2097} 2123}
2098 2124
2099void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF center, 2125void PlayerControlPreview::DrawDualZTriggersTopView(
2100 bool left_pressed, bool right_pressed) { 2126 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed,
2127 const Common::Input::ButtonStatus& right_pressed) {
2101 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; 2128 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger;
2102 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; 2129 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger;
2103 constexpr float size = 0.9f; 2130 constexpr float size = 0.9f;
@@ -2114,9 +2141,9 @@ void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF c
2114 } 2141 }
2115 2142
2116 p.setPen(colors.outline); 2143 p.setPen(colors.outline);
2117 p.setBrush(left_pressed ? colors.highlight : colors.button); 2144 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2118 DrawPolygon(p, qleft_trigger); 2145 DrawPolygon(p, qleft_trigger);
2119 p.setBrush(right_pressed ? colors.highlight : colors.button); 2146 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2120 DrawPolygon(p, qright_trigger); 2147 DrawPolygon(p, qright_trigger);
2121 2148
2122 // Draw ZL text 2149 // Draw ZL text
@@ -2130,7 +2157,8 @@ void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF c
2130 DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f); 2157 DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f);
2131} 2158}
2132 2159
2133void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, bool left_pressed) { 2160void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center,
2161 const Common::Input::ButtonStatus& left_pressed) {
2134 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; 2162 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
2135 constexpr float size = 1.78f; 2163 constexpr float size = 1.78f;
2136 constexpr float offset = 311.5f; 2164 constexpr float offset = 311.5f;
@@ -2138,15 +2166,16 @@ void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, b
2138 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { 2166 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) {
2139 qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset, 2167 qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset,
2140 left_joycon_trigger[point * 2 + 1] * size - 2168 left_joycon_trigger[point * 2 + 1] * size -
2141 (left_pressed ? 0.5f : 1.0f)); 2169 (left_pressed.value ? 0.5f : 1.0f));
2142 } 2170 }
2143 2171
2144 p.setPen(colors.outline); 2172 p.setPen(colors.outline);
2145 p.setBrush(left_pressed ? colors.highlight : colors.button); 2173 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2146 DrawPolygon(p, qleft_trigger); 2174 DrawPolygon(p, qleft_trigger);
2147} 2175}
2148 2176
2149void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, bool left_pressed) { 2177void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center,
2178 const Common::Input::ButtonStatus& left_pressed) {
2150 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger; 2179 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger;
2151 constexpr float size = 1.1115f; 2180 constexpr float size = 1.1115f;
2152 constexpr float offset2 = 335; 2181 constexpr float offset2 = 335;
@@ -2154,18 +2183,18 @@ void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center,
2154 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { 2183 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) {
2155 qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2, 2184 qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2,
2156 left_joycon_sideview_zl[point * 2 + 1] * size + 2185 left_joycon_sideview_zl[point * 2 + 1] * size +
2157 (left_pressed ? 1.5f : 1.0f)); 2186 (left_pressed.value ? 1.5f : 1.0f));
2158 } 2187 }
2159 2188
2160 p.setPen(colors.outline); 2189 p.setPen(colors.outline);
2161 p.setBrush(left_pressed ? colors.highlight : colors.button); 2190 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2162 DrawPolygon(p, qleft_trigger); 2191 DrawPolygon(p, qleft_trigger);
2163 p.drawArc(center.x() + 158, center.y() + (left_pressed ? -203.5f : -204.0f), 77, 77, 225 * 16, 2192 p.drawArc(center.x() + 158, center.y() + (left_pressed.value ? -203.5f : -204.0f), 77, 77,
2164 44 * 16); 2193 225 * 16, 44 * 16);
2165} 2194}
2166 2195
2167void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF center, 2196void PlayerControlPreview::DrawLeftTriggersTopView(
2168 bool left_pressed) { 2197 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) {
2169 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; 2198 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger;
2170 2199
2171 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { 2200 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) {
@@ -2174,7 +2203,7 @@ void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF ce
2174 } 2203 }
2175 2204
2176 p.setPen(colors.outline); 2205 p.setPen(colors.outline);
2177 p.setBrush(left_pressed ? colors.highlight : colors.button); 2206 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2178 DrawPolygon(p, qleft_trigger); 2207 DrawPolygon(p, qleft_trigger);
2179 2208
2180 // Draw L text 2209 // Draw L text
@@ -2183,8 +2212,8 @@ void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF ce
2183 DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f); 2212 DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f);
2184} 2213}
2185 2214
2186void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF center, 2215void PlayerControlPreview::DrawLeftZTriggersTopView(
2187 bool left_pressed) { 2216 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) {
2188 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; 2217 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger;
2189 2218
2190 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { 2219 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) {
@@ -2193,7 +2222,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF c
2193 } 2222 }
2194 2223
2195 p.setPen(colors.outline); 2224 p.setPen(colors.outline);
2196 p.setBrush(left_pressed ? colors.highlight : colors.button); 2225 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2197 DrawPolygon(p, qleft_trigger); 2226 DrawPolygon(p, qleft_trigger);
2198 2227
2199 // Draw ZL text 2228 // Draw ZL text
@@ -2203,7 +2232,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF c
2203} 2232}
2204 2233
2205void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center, 2234void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center,
2206 bool right_pressed) { 2235 const Common::Input::ButtonStatus& right_pressed) {
2207 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; 2236 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
2208 constexpr float size = 1.78f; 2237 constexpr float size = 1.78f;
2209 constexpr float offset = 311.5f; 2238 constexpr float offset = 311.5f;
@@ -2211,36 +2240,36 @@ void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center,
2211 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { 2240 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) {
2212 qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset, 2241 qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset,
2213 left_joycon_trigger[point * 2 + 1] * size - 2242 left_joycon_trigger[point * 2 + 1] * size -
2214 (right_pressed ? 0.5f : 1.0f)); 2243 (right_pressed.value ? 0.5f : 1.0f));
2215 } 2244 }
2216 2245
2217 p.setPen(colors.outline); 2246 p.setPen(colors.outline);
2218 p.setBrush(right_pressed ? colors.highlight : colors.button); 2247 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2219 DrawPolygon(p, qright_trigger); 2248 DrawPolygon(p, qright_trigger);
2220} 2249}
2221 2250
2222void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center, 2251void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center,
2223 bool right_pressed) { 2252 const Common::Input::ButtonStatus& right_pressed) {
2224 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger; 2253 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger;
2225 constexpr float size = 1.1115f; 2254 constexpr float size = 1.1115f;
2226 constexpr float offset2 = 335; 2255 constexpr float offset2 = 335;
2227 2256
2228 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { 2257 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) {
2229 qright_trigger[point] = 2258 qright_trigger[point] =
2230 center + 2259 center + QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2,
2231 QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2, 2260 left_joycon_sideview_zl[point * 2 + 1] * size +
2232 left_joycon_sideview_zl[point * 2 + 1] * size + (right_pressed ? 0.5f : 0) + 1); 2261 (right_pressed.value ? 0.5f : 0) + 1);
2233 } 2262 }
2234 2263
2235 p.setPen(colors.outline); 2264 p.setPen(colors.outline);
2236 p.setBrush(right_pressed ? colors.highlight : colors.button); 2265 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2237 DrawPolygon(p, qright_trigger); 2266 DrawPolygon(p, qright_trigger);
2238 p.drawArc(center.x() - 236, center.y() + (right_pressed ? -203.5f : -204.0f), 77, 77, 271 * 16, 2267 p.drawArc(center.x() - 236, center.y() + (right_pressed.value ? -203.5f : -204.0f), 77, 77,
2239 44 * 16); 2268 271 * 16, 44 * 16);
2240} 2269}
2241 2270
2242void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF center, 2271void PlayerControlPreview::DrawRightTriggersTopView(
2243 bool right_pressed) { 2272 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) {
2244 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; 2273 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger;
2245 2274
2246 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { 2275 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) {
@@ -2249,7 +2278,7 @@ void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF c
2249 } 2278 }
2250 2279
2251 p.setPen(colors.outline); 2280 p.setPen(colors.outline);
2252 p.setBrush(right_pressed ? colors.highlight : colors.button); 2281 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2253 DrawPolygon(p, qright_trigger); 2282 DrawPolygon(p, qright_trigger);
2254 2283
2255 // Draw R text 2284 // Draw R text
@@ -2258,8 +2287,8 @@ void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF c
2258 DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f); 2287 DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f);
2259} 2288}
2260 2289
2261void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF center, 2290void PlayerControlPreview::DrawRightZTriggersTopView(
2262 bool right_pressed) { 2291 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) {
2263 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; 2292 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger;
2264 2293
2265 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { 2294 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) {
@@ -2268,7 +2297,7 @@ void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF
2268 } 2297 }
2269 2298
2270 p.setPen(colors.outline); 2299 p.setPen(colors.outline);
2271 p.setBrush(right_pressed ? colors.highlight : colors.button); 2300 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2272 DrawPolygon(p, qright_trigger); 2301 DrawPolygon(p, qright_trigger);
2273 2302
2274 // Draw ZR text 2303 // Draw ZR text
@@ -2278,13 +2307,13 @@ void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF
2278} 2307}
2279 2308
2280void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size, 2309void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size,
2281 bool pressed) { 2310 const Common::Input::ButtonStatus& pressed) {
2282 const float radius1 = 13.0f * size; 2311 const float radius1 = 13.0f * size;
2283 const float radius2 = 9.0f * size; 2312 const float radius2 = 9.0f * size;
2284 2313
2285 // Outer circle 2314 // Outer circle
2286 p.setPen(colors.outline); 2315 p.setPen(colors.outline);
2287 p.setBrush(pressed ? colors.highlight : colors.button); 2316 p.setBrush(pressed.value ? colors.highlight : colors.button);
2288 DrawCircle(p, center, radius1); 2317 DrawCircle(p, center, radius1);
2289 2318
2290 // Cross 2319 // Cross
@@ -2292,17 +2321,18 @@ void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float
2292 p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1)); 2321 p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1));
2293 2322
2294 // Inner circle 2323 // Inner circle
2295 p.setBrush(pressed ? colors.highlight2 : colors.button2); 2324 p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
2296 DrawCircle(p, center, radius2); 2325 DrawCircle(p, center, radius2);
2297} 2326}
2298 2327
2299void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle, 2328void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle,
2300 float size, bool pressed) { 2329 float size,
2330 const Common::Input::ButtonStatus& pressed) {
2301 QVector<QPointF> joystick; 2331 QVector<QPointF> joystick;
2302 joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2)); 2332 joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2));
2303 2333
2304 for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) { 2334 for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) {
2305 joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed ? 1 : 0), 2335 joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed.value ? 1 : 0),
2306 left_joystick_sideview[point * 2 + 1] * size - 1)); 2336 left_joystick_sideview[point * 2 + 1] * size - 1));
2307 } 2337 }
2308 2338
@@ -2314,14 +2344,15 @@ void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF cente
2314 2344
2315 // Draw joystick 2345 // Draw joystick
2316 p.setPen(colors.outline); 2346 p.setPen(colors.outline);
2317 p.setBrush(pressed ? colors.highlight : colors.button); 2347 p.setBrush(pressed.value ? colors.highlight : colors.button);
2318 p.drawPolygon(p2); 2348 p.drawPolygon(p2);
2319 p.drawLine(p2.at(1), p2.at(30)); 2349 p.drawLine(p2.at(1), p2.at(30));
2320 p.drawLine(p2.at(32), p2.at(71)); 2350 p.drawLine(p2.at(32), p2.at(71));
2321} 2351}
2322 2352
2323void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset, 2353void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset,
2324 float offset_scalar, bool pressed) { 2354 float offset_scalar,
2355 const Common::Input::ButtonStatus& pressed) {
2325 const float radius1 = 24.0f; 2356 const float radius1 = 24.0f;
2326 const float radius2 = 17.0f; 2357 const float radius2 = 17.0f;
2327 2358
@@ -2339,11 +2370,11 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co
2339 2370
2340 // Outer circle 2371 // Outer circle
2341 p.setPen(colors.outline); 2372 p.setPen(colors.outline);
2342 p.setBrush(pressed ? colors.highlight : colors.button); 2373 p.setBrush(pressed.value ? colors.highlight : colors.button);
2343 p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1); 2374 p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1);
2344 2375
2345 // Inner circle 2376 // Inner circle
2346 p.setBrush(pressed ? colors.highlight2 : colors.button2); 2377 p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
2347 2378
2348 const float inner_offset = 2379 const float inner_offset =
2349 (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f); 2380 (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f);
@@ -2355,14 +2386,15 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co
2355 p.restore(); 2386 p.restore();
2356} 2387}
2357 2388
2358void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, bool pressed) { 2389void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center,
2390 const Common::Input::ButtonStatus& pressed) {
2359 // Outer circle 2391 // Outer circle
2360 p.setPen(colors.outline); 2392 p.setPen(colors.outline);
2361 p.setBrush(pressed ? colors.highlight : colors.button); 2393 p.setBrush(pressed.value ? colors.highlight : colors.button);
2362 DrawCircle(p, center, 26.0f); 2394 DrawCircle(p, center, 26.0f);
2363 2395
2364 // Inner circle 2396 // Inner circle
2365 p.setBrush(pressed ? colors.highlight2 : colors.button2); 2397 p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
2366 DrawCircle(p, center, 19.0f); 2398 DrawCircle(p, center, 19.0f);
2367 p.setBrush(colors.transparent); 2399 p.setBrush(colors.transparent);
2368 DrawCircle(p, center, 13.5f); 2400 DrawCircle(p, center, 13.5f);
@@ -2371,31 +2403,29 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, boo
2371 2403
2372void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) { 2404void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) {
2373 using namespace Settings::NativeAnalog; 2405 using namespace Settings::NativeAnalog;
2374 if (controller_type != Settings::ControllerType::LeftJoycon) { 2406 if (center_right != QPointF(0, 0)) {
2375 DrawJoystickProperties(p, center_right, axis_values[RStick].properties); 2407 DrawJoystickProperties(p, center_right, stick_values[RStick].x.properties);
2376 p.setPen(colors.indicator); 2408 p.setPen(colors.indicator);
2377 p.setBrush(colors.indicator); 2409 p.setBrush(colors.indicator);
2378 DrawJoystickDot(p, center_right, axis_values[RStick].raw_value, 2410 DrawJoystickDot(p, center_right, stick_values[RStick], true);
2379 axis_values[RStick].properties);
2380 p.setPen(colors.indicator2); 2411 p.setPen(colors.indicator2);
2381 p.setBrush(colors.indicator2); 2412 p.setBrush(colors.indicator2);
2382 DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties); 2413 DrawJoystickDot(p, center_right, stick_values[RStick], false);
2383 } 2414 }
2384 2415
2385 if (controller_type != Settings::ControllerType::RightJoycon) { 2416 if (center_left != QPointF(0, 0)) {
2386 DrawJoystickProperties(p, center_left, axis_values[LStick].properties); 2417 DrawJoystickProperties(p, center_left, stick_values[LStick].x.properties);
2387 p.setPen(colors.indicator); 2418 p.setPen(colors.indicator);
2388 p.setBrush(colors.indicator); 2419 p.setBrush(colors.indicator);
2389 DrawJoystickDot(p, center_left, axis_values[LStick].raw_value, 2420 DrawJoystickDot(p, center_left, stick_values[LStick], true);
2390 axis_values[LStick].properties);
2391 p.setPen(colors.indicator2); 2421 p.setPen(colors.indicator2);
2392 p.setBrush(colors.indicator2); 2422 p.setBrush(colors.indicator2);
2393 DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties); 2423 DrawJoystickDot(p, center_left, stick_values[LStick], false);
2394 } 2424 }
2395} 2425}
2396 2426
2397void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center, 2427void PlayerControlPreview::DrawJoystickProperties(
2398 const Input::AnalogProperties& properties) { 2428 QPainter& p, const QPointF center, const Common::Input::AnalogProperties& properties) {
2399 constexpr float size = 45.0f; 2429 constexpr float size = 45.0f;
2400 const float range = size * properties.range; 2430 const float range = size * properties.range;
2401 const float deadzone = size * properties.deadzone; 2431 const float deadzone = size * properties.deadzone;
@@ -2414,19 +2444,26 @@ void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF cen
2414 DrawCircle(p, center, deadzone); 2444 DrawCircle(p, center, deadzone);
2415} 2445}
2416 2446
2417void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value, 2447void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
2418 const Input::AnalogProperties& properties) { 2448 const Common::Input::StickStatus& stick, bool raw) {
2419 constexpr float size = 45.0f; 2449 constexpr float size = 45.0f;
2420 const float range = size * properties.range; 2450 const float range = size * stick.x.properties.range;
2421 2451
2422 // Dot pointer 2452 if (raw) {
2423 DrawCircle(p, center + (value * range), 2); 2453 const QPointF value = QPointF(stick.x.raw_value, stick.y.raw_value) * size;
2454 DrawCircle(p, center + value, 2);
2455 return;
2456 }
2457
2458 const QPointF value = QPointF(stick.x.value, stick.y.value) * range;
2459 DrawCircle(p, center + value, 2);
2424} 2460}
2425 2461
2426void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, 2462void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2463 const Common::Input::ButtonStatus& pressed, float width,
2427 float height, Direction direction, float radius) { 2464 float height, Direction direction, float radius) {
2428 p.setBrush(button_color); 2465 p.setBrush(button_color);
2429 if (pressed) { 2466 if (pressed.value) {
2430 switch (direction) { 2467 switch (direction) {
2431 case Direction::Left: 2468 case Direction::Left:
2432 center.setX(center.x() - 1); 2469 center.setX(center.x() - 1);
@@ -2448,17 +2485,19 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pre
2448 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; 2485 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
2449 p.drawRoundedRect(rect, radius, radius); 2486 p.drawRoundedRect(rect, radius, radius);
2450} 2487}
2451void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, bool pressed, 2488void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
2489 const Common::Input::ButtonStatus& pressed,
2452 int button_size) { 2490 int button_size) {
2453 p.setPen(colors.outline); 2491 p.setPen(colors.outline);
2454 p.setBrush(pressed ? colors.highlight : colors.button); 2492 p.setBrush(pressed.value ? colors.highlight : colors.button);
2455 DrawRectangle(p, center, button_size, button_size / 3.0f); 2493 DrawRectangle(p, center, button_size, button_size / 3.0f);
2456} 2494}
2457void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, bool pressed, 2495void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
2496 const Common::Input::ButtonStatus& pressed,
2458 int button_size) { 2497 int button_size) {
2459 // Draw outer line 2498 // Draw outer line
2460 p.setPen(colors.outline); 2499 p.setPen(colors.outline);
2461 p.setBrush(pressed ? colors.highlight : colors.button); 2500 p.setBrush(pressed.value ? colors.highlight : colors.button);
2462 DrawRectangle(p, center, button_size, button_size / 3.0f); 2501 DrawRectangle(p, center, button_size, button_size / 3.0f);
2463 DrawRectangle(p, center, button_size / 3.0f, button_size); 2502 DrawRectangle(p, center, button_size / 3.0f, button_size);
2464 2503
@@ -2471,7 +2510,8 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, boo
2471 DrawRectangle(p, center, button_size / 3.0f, button_size); 2510 DrawRectangle(p, center, button_size / 3.0f, button_size);
2472} 2511}
2473 2512
2474void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool pressed) { 2513void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
2514 const Common::Input::ButtonStatus& pressed) {
2475 std::array<QPointF, gc_button_x.size() / 2> button_x; 2515 std::array<QPointF, gc_button_x.size() / 2> button_x;
2476 2516
2477 for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) { 2517 for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) {
@@ -2479,11 +2519,12 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool
2479 } 2519 }
2480 2520
2481 p.setPen(colors.outline); 2521 p.setPen(colors.outline);
2482 p.setBrush(pressed ? colors.highlight : colors.button); 2522 p.setBrush(pressed.value ? colors.highlight : colors.button);
2483 DrawPolygon(p, button_x); 2523 DrawPolygon(p, button_x);
2484} 2524}
2485 2525
2486void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool pressed) { 2526void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
2527 const Common::Input::ButtonStatus& pressed) {
2487 std::array<QPointF, gc_button_y.size() / 2> button_x; 2528 std::array<QPointF, gc_button_y.size() / 2> button_x;
2488 2529
2489 for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) { 2530 for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) {
@@ -2491,27 +2532,29 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool
2491 } 2532 }
2492 2533
2493 p.setPen(colors.outline); 2534 p.setPen(colors.outline);
2494 p.setBrush(pressed ? colors.highlight : colors.button); 2535 p.setBrush(pressed.value ? colors.highlight : colors.button);
2495 DrawPolygon(p, button_x); 2536 DrawPolygon(p, button_x);
2496} 2537}
2497 2538
2498void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, bool pressed) { 2539void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
2540 const Common::Input::ButtonStatus& pressed) {
2499 std::array<QPointF, gc_button_z.size() / 2> button_x; 2541 std::array<QPointF, gc_button_z.size() / 2> button_x;
2500 2542
2501 for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) { 2543 for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) {
2502 button_x[point] = center + QPointF(gc_button_z[point * 2], 2544 button_x[point] = center + QPointF(gc_button_z[point * 2],
2503 gc_button_z[point * 2 + 1] + (pressed ? 1 : 0)); 2545 gc_button_z[point * 2 + 1] + (pressed.value ? 1 : 0));
2504 } 2546 }
2505 2547
2506 p.setPen(colors.outline); 2548 p.setPen(colors.outline);
2507 p.setBrush(pressed ? colors.highlight : colors.button2); 2549 p.setBrush(pressed.value ? colors.highlight : colors.button2);
2508 DrawPolygon(p, button_x); 2550 DrawPolygon(p, button_x);
2509} 2551}
2510 2552
2511void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, bool pressed, 2553void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
2554 const Common::Input::ButtonStatus& pressed,
2512 float button_size) { 2555 float button_size) {
2513 p.setBrush(button_color); 2556 p.setBrush(button_color);
2514 if (pressed) { 2557 if (pressed.value) {
2515 p.setBrush(colors.highlight); 2558 p.setBrush(colors.highlight);
2516 } 2559 }
2517 p.drawEllipse(center, button_size, button_size); 2560 p.drawEllipse(center, button_size, button_size);
@@ -2540,7 +2583,8 @@ void PlayerControlPreview::DrawArrowButtonOutline(QPainter& p, const QPointF cen
2540} 2583}
2541 2584
2542void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, 2585void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2543 const Direction direction, bool pressed, float size) { 2586 const Direction direction,
2587 const Common::Input::ButtonStatus& pressed, float size) {
2544 std::array<QPointF, up_arrow_button.size() / 2> arrow_button; 2588 std::array<QPointF, up_arrow_button.size() / 2> arrow_button;
2545 QPoint offset; 2589 QPoint offset;
2546 2590
@@ -2552,38 +2596,39 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2552 case Direction::Up: 2596 case Direction::Up:
2553 arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size); 2597 arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size);
2554 break; 2598 break;
2555 case Direction::Left:
2556 arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size);
2557 break;
2558 case Direction::Right: 2599 case Direction::Right:
2559 arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size); 2600 arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size);
2560 break; 2601 break;
2561 case Direction::Down: 2602 case Direction::Down:
2562 arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size); 2603 arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size);
2563 break; 2604 break;
2605 case Direction::Left:
2606 // Compiler doesn't optimize this correctly check why
2607 arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size);
2608 break;
2564 case Direction::None: 2609 case Direction::None:
2565 break; 2610 break;
2566 } 2611 }
2567 } 2612 }
2568 2613
2569 // Draw arrow button 2614 // Draw arrow button
2570 p.setPen(pressed ? colors.highlight : colors.button); 2615 p.setPen(pressed.value ? colors.highlight : colors.button);
2571 p.setBrush(pressed ? colors.highlight : colors.button); 2616 p.setBrush(pressed.value ? colors.highlight : colors.button);
2572 DrawPolygon(p, arrow_button); 2617 DrawPolygon(p, arrow_button);
2573 2618
2574 switch (direction) { 2619 switch (direction) {
2575 case Direction::Up: 2620 case Direction::Up:
2576 offset = QPoint(0, -20 * size); 2621 offset = QPoint(0, -20 * size);
2577 break; 2622 break;
2578 case Direction::Left:
2579 offset = QPoint(-20 * size, 0);
2580 break;
2581 case Direction::Right: 2623 case Direction::Right:
2582 offset = QPoint(20 * size, 0); 2624 offset = QPoint(20 * size, 0);
2583 break; 2625 break;
2584 case Direction::Down: 2626 case Direction::Down:
2585 offset = QPoint(0, 20 * size); 2627 offset = QPoint(0, 20 * size);
2586 break; 2628 break;
2629 case Direction::Left:
2630 offset = QPoint(-20 * size, 0);
2631 break;
2587 case Direction::None: 2632 case Direction::None:
2588 offset = QPoint(0, 0); 2633 offset = QPoint(0, 0);
2589 break; 2634 break;
@@ -2596,7 +2641,8 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2596} 2641}
2597 2642
2598void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, 2643void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2599 const Direction direction, bool pressed) { 2644 const Direction direction,
2645 const Common::Input::ButtonStatus& pressed) {
2600 std::array<QPointF, trigger_button.size() / 2> qtrigger_button; 2646 std::array<QPointF, trigger_button.size() / 2> qtrigger_button;
2601 2647
2602 for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) { 2648 for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) {
@@ -2619,10 +2665,51 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2619 2665
2620 // Draw arrow button 2666 // Draw arrow button
2621 p.setPen(colors.outline); 2667 p.setPen(colors.outline);
2622 p.setBrush(pressed ? colors.highlight : colors.button); 2668 p.setBrush(pressed.value ? colors.highlight : colors.button);
2623 DrawPolygon(p, qtrigger_button); 2669 DrawPolygon(p, qtrigger_button);
2624} 2670}
2625 2671
2672void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2673 Common::Input::BatteryLevel battery) {
2674 if (battery == Common::Input::BatteryLevel::None) {
2675 return;
2676 }
2677 p.setPen(colors.outline);
2678 p.setBrush(colors.transparent);
2679 p.drawRect(center.x(), center.y(), 56, 20);
2680 p.drawRect(center.x() + 56, center.y() + 6, 3, 8);
2681 p.setBrush(colors.deadzone);
2682 switch (battery) {
2683 case Common::Input::BatteryLevel::Charging:
2684 p.setBrush(colors.indicator2);
2685 p.drawText(center + QPoint(2, 14), tr("Charging"));
2686 break;
2687 case Common::Input::BatteryLevel::Full:
2688 p.drawRect(center.x() + 42, center.y(), 14, 20);
2689 p.drawRect(center.x() + 28, center.y(), 14, 20);
2690 p.drawRect(center.x() + 14, center.y(), 14, 20);
2691 p.drawRect(center.x(), center.y(), 14, 20);
2692 break;
2693 case Common::Input::BatteryLevel::Medium:
2694 p.drawRect(center.x() + 28, center.y(), 14, 20);
2695 p.drawRect(center.x() + 14, center.y(), 14, 20);
2696 p.drawRect(center.x(), center.y(), 14, 20);
2697 break;
2698 case Common::Input::BatteryLevel::Low:
2699 p.drawRect(center.x() + 14, center.y(), 14, 20);
2700 p.drawRect(center.x(), center.y(), 14, 20);
2701 break;
2702 case Common::Input::BatteryLevel::Critical:
2703 p.drawRect(center.x(), center.y(), 14, 20);
2704 break;
2705 case Common::Input::BatteryLevel::Empty:
2706 p.drawRect(center.x(), center.y(), 5, 20);
2707 break;
2708 default:
2709 break;
2710 }
2711}
2712
2626void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol, 2713void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol,
2627 float icon_size) { 2714 float icon_size) {
2628 std::array<QPointF, house.size() / 2> house_icon; 2715 std::array<QPointF, house.size() / 2> house_icon;
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index f4bbfa528..4cd5c3be0 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -7,9 +7,11 @@
7#include <array> 7#include <array>
8#include <QFrame> 8#include <QFrame>
9#include <QPointer> 9#include <QPointer>
10#include "common/settings.h" 10
11#include "core/frontend/input.h" 11#include "common/input.h"
12#include "yuzu/debugger/controller.h" 12#include "common/settings_input.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_types.h"
13 15
14class QLabel; 16class QLabel;
15 17
@@ -24,17 +26,26 @@ public:
24 explicit PlayerControlPreview(QWidget* parent); 26 explicit PlayerControlPreview(QWidget* parent);
25 ~PlayerControlPreview() override; 27 ~PlayerControlPreview() override;
26 28
27 void SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, 29 // Sets the emulated controller to be displayed
28 const AnalogParam& analogs_param); 30 void SetController(Core::HID::EmulatedController* controller);
29 void SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw& buttons_, 31
30 Settings::AnalogsRaw analogs_); 32 // Disables events from the emulated controller
31 void SetConnectedStatus(bool checked); 33 void UnloadController();
32 void SetControllerType(Settings::ControllerType type); 34
35 // Starts blinking animation at the button specified
33 void BeginMappingButton(std::size_t button_id); 36 void BeginMappingButton(std::size_t button_id);
34 void BeginMappingAnalog(std::size_t button_id); 37
38 // Starts moving animation at the stick specified
39 void BeginMappingAnalog(std::size_t stick_id);
40
41 // Stops any ongoing animation
35 void EndMapping(); 42 void EndMapping();
43
44 // Handles emulated controller events
45 void ControllerUpdate(Core::HID::ControllerTriggerType type);
46
47 // Updates input on sheduled interval
36 void UpdateInput(); 48 void UpdateInput();
37 void SetCallBack(ControllerCallback callback_);
38 49
39protected: 50protected:
40 void paintEvent(QPaintEvent* event) override; 51 void paintEvent(QPaintEvent* event) override;
@@ -63,22 +74,6 @@ private:
63 SR, 74 SR,
64 }; 75 };
65 76
66 struct AxisValue {
67 QPointF value{};
68 QPointF raw_value{};
69 Input::AnalogProperties properties{};
70 int size{};
71 QPoint offset{};
72 bool active{};
73 };
74
75 struct LedPattern {
76 bool position1;
77 bool position2;
78 bool position3;
79 bool position4;
80 };
81
82 struct ColorMapping { 77 struct ColorMapping {
83 QColor outline{}; 78 QColor outline{};
84 QColor primary{}; 79 QColor primary{};
@@ -101,7 +96,6 @@ private:
101 QColor deadzone{}; 96 QColor deadzone{};
102 }; 97 };
103 98
104 static LedPattern GetColorPattern(std::size_t index, bool player_on);
105 void UpdateColors(); 99 void UpdateColors();
106 void ResetInputs(); 100 void ResetInputs();
107 101
@@ -122,47 +116,75 @@ private:
122 void DrawGCBody(QPainter& p, QPointF center); 116 void DrawGCBody(QPainter& p, QPointF center);
123 117
124 // Draw triggers functions 118 // Draw triggers functions
125 void DrawProTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 119 void DrawProTriggers(QPainter& p, QPointF center,
126 void DrawGCTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 120 const Common::Input::ButtonStatus& left_pressed,
127 void DrawHandheldTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 121 const Common::Input::ButtonStatus& right_pressed);
128 void DrawDualTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 122 void DrawGCTriggers(QPainter& p, QPointF center, Common::Input::TriggerStatus left_trigger,
129 void DrawDualTriggersTopView(QPainter& p, QPointF center, bool left_pressed, 123 Common::Input::TriggerStatus right_trigger);
130 bool right_pressed); 124 void DrawHandheldTriggers(QPainter& p, QPointF center,
131 void DrawDualZTriggersTopView(QPainter& p, QPointF center, bool left_pressed, 125 const Common::Input::ButtonStatus& left_pressed,
132 bool right_pressed); 126 const Common::Input::ButtonStatus& right_pressed);
133 void DrawLeftTriggers(QPainter& p, QPointF center, bool left_pressed); 127 void DrawDualTriggers(QPainter& p, QPointF center,
134 void DrawLeftZTriggers(QPainter& p, QPointF center, bool left_pressed); 128 const Common::Input::ButtonStatus& left_pressed,
135 void DrawLeftTriggersTopView(QPainter& p, QPointF center, bool left_pressed); 129 const Common::Input::ButtonStatus& right_pressed);
136 void DrawLeftZTriggersTopView(QPainter& p, QPointF center, bool left_pressed); 130 void DrawDualTriggersTopView(QPainter& p, QPointF center,
137 void DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed); 131 const Common::Input::ButtonStatus& left_pressed,
138 void DrawRightZTriggers(QPainter& p, QPointF center, bool right_pressed); 132 const Common::Input::ButtonStatus& right_pressed);
139 void DrawRightTriggersTopView(QPainter& p, QPointF center, bool right_pressed); 133 void DrawDualZTriggersTopView(QPainter& p, QPointF center,
140 void DrawRightZTriggersTopView(QPainter& p, QPointF center, bool right_pressed); 134 const Common::Input::ButtonStatus& left_pressed,
135 const Common::Input::ButtonStatus& right_pressed);
136 void DrawLeftTriggers(QPainter& p, QPointF center,
137 const Common::Input::ButtonStatus& left_pressed);
138 void DrawLeftZTriggers(QPainter& p, QPointF center,
139 const Common::Input::ButtonStatus& left_pressed);
140 void DrawLeftTriggersTopView(QPainter& p, QPointF center,
141 const Common::Input::ButtonStatus& left_pressed);
142 void DrawLeftZTriggersTopView(QPainter& p, QPointF center,
143 const Common::Input::ButtonStatus& left_pressed);
144 void DrawRightTriggers(QPainter& p, QPointF center,
145 const Common::Input::ButtonStatus& right_pressed);
146 void DrawRightZTriggers(QPainter& p, QPointF center,
147 const Common::Input::ButtonStatus& right_pressed);
148 void DrawRightTriggersTopView(QPainter& p, QPointF center,
149 const Common::Input::ButtonStatus& right_pressed);
150 void DrawRightZTriggersTopView(QPainter& p, QPointF center,
151 const Common::Input::ButtonStatus& right_pressed);
141 152
142 // Draw joystick functions 153 // Draw joystick functions
143 void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed); 154 void DrawJoystick(QPainter& p, QPointF center, float size,
144 void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed); 155 const Common::Input::ButtonStatus& pressed);
156 void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size,
157 const Common::Input::ButtonStatus& pressed);
145 void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right); 158 void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right);
146 void DrawJoystickProperties(QPainter& p, QPointF center, 159 void DrawJoystickProperties(QPainter& p, QPointF center,
147 const Input::AnalogProperties& properties); 160 const Common::Input::AnalogProperties& properties);
148 void DrawJoystickDot(QPainter& p, QPointF center, QPointF value, 161 void DrawJoystickDot(QPainter& p, QPointF center, const Common::Input::StickStatus& stick,
149 const Input::AnalogProperties& properties); 162 bool raw);
150 void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed); 163 void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar,
151 void DrawGCJoystick(QPainter& p, QPointF center, bool pressed); 164 const Common::Input::ButtonStatus& pressed);
165 void DrawGCJoystick(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
152 166
153 // Draw button functions 167 // Draw button functions
154 void DrawCircleButton(QPainter& p, QPointF center, bool pressed, float button_size); 168 void DrawCircleButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
155 void DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, float height, 169 float button_size);
156 Direction direction = Direction::None, float radius = 2); 170 void DrawRoundButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
157 void DrawMinusButton(QPainter& p, QPointF center, bool pressed, int button_size); 171 float width, float height, Direction direction = Direction::None,
158 void DrawPlusButton(QPainter& p, QPointF center, bool pressed, int button_size); 172 float radius = 2);
159 void DrawGCButtonX(QPainter& p, QPointF center, bool pressed); 173 void DrawMinusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
160 void DrawGCButtonY(QPainter& p, QPointF center, bool pressed); 174 int button_size);
161 void DrawGCButtonZ(QPainter& p, QPointF center, bool pressed); 175 void DrawPlusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
176 int button_size);
177 void DrawGCButtonX(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
178 void DrawGCButtonY(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
179 void DrawGCButtonZ(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
162 void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f); 180 void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f);
163 void DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed, 181 void DrawArrowButton(QPainter& p, QPointF center, Direction direction,
164 float size = 1.0f); 182 const Common::Input::ButtonStatus& pressed, float size = 1.0f);
165 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, bool pressed); 183 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
184 const Common::Input::ButtonStatus& pressed);
185
186 // Draw battery functions
187 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery);
166 188
167 // Draw icon functions 189 // Draw icon functions
168 void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size); 190 void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size);
@@ -178,24 +200,23 @@ private:
178 void SetTextFont(QPainter& p, float text_size, 200 void SetTextFont(QPainter& p, float text_size,
179 const QString& font_family = QStringLiteral("sans-serif")); 201 const QString& font_family = QStringLiteral("sans-serif"));
180 202
181 using ButtonArray = 203 bool is_controller_set{};
182 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::BUTTON_NS_END>; 204 bool is_connected{};
183 using StickArray = 205 bool needs_redraw{};
184 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>; 206 Core::HID::NpadStyleIndex controller_type;
185 207
186 ControllerCallback controller_callback;
187 bool is_enabled{};
188 bool mapping_active{}; 208 bool mapping_active{};
189 int blink_counter{}; 209 int blink_counter{};
210 int callback_key;
190 QColor button_color{}; 211 QColor button_color{};
191 ColorMapping colors{}; 212 ColorMapping colors{};
192 std::array<QColor, 4> led_color{}; 213 Core::HID::LedPattern led_pattern{0, 0, 0, 0};
193 ButtonArray buttons{};
194 StickArray sticks{};
195 std::size_t player_index{}; 214 std::size_t player_index{};
196 std::size_t button_mapping_index{Settings::NativeButton::BUTTON_NS_END}; 215 Core::HID::EmulatedController* controller;
197 std::size_t analog_mapping_index{Settings::NativeAnalog::NUM_STICKS_HID}; 216 std::size_t button_mapping_index{Settings::NativeButton::NumButtons};
198 std::array<AxisValue, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; 217 std::size_t analog_mapping_index{Settings::NativeAnalog::NumAnalogs};
199 std::array<bool, Settings::NativeButton::NumButtons> button_values{}; 218 Core::HID::ButtonValues button_values{};
200 Settings::ControllerType controller_type{Settings::ControllerType::ProController}; 219 Core::HID::SticksValues stick_values{};
220 Core::HID::TriggerValues trigger_values{};
221 Core::HID::BatteryValues battery_values{};
201}; 222};
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp
index cd5a88cea..17bbe6b61 100644
--- a/src/yuzu/configuration/configure_input_profile_dialog.cpp
+++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp
@@ -11,8 +11,8 @@ ConfigureInputProfileDialog::ConfigureInputProfileDialog(
11 QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles, 11 QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles,
12 Core::System& system) 12 Core::System& system)
13 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()), 13 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
14 profile_widget( 14 profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles,
15 new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, false)) { 15 system.HIDCore(), system.IsPoweredOn(), false)) {
16 ui->setupUi(this); 16 ui->setupUi(this);
17 17
18 ui->controllerLayout->addWidget(profile_widget); 18 ui->controllerLayout->addWidget(profile_widget);
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index f8e08c422..8539a5c8b 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -15,9 +15,9 @@
15 15
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/settings.h" 17#include "common/settings.h"
18#include "input_common/drivers/udp_client.h"
19#include "input_common/helpers/udp_protocol.h"
18#include "input_common/main.h" 20#include "input_common/main.h"
19#include "input_common/udp/client.h"
20#include "input_common/udp/udp.h"
21#include "ui_configure_motion_touch.h" 21#include "ui_configure_motion_touch.h"
22#include "yuzu/configuration/configure_motion_touch.h" 22#include "yuzu/configuration/configure_motion_touch.h"
23#include "yuzu/configuration/configure_touch_from_button.h" 23#include "yuzu/configuration/configure_touch_from_button.h"
@@ -93,6 +93,7 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
93 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " 93 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
94 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); 94 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
95 95
96 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
96 SetConfiguration(); 97 SetConfiguration();
97 UpdateUiDisplay(); 98 UpdateUiDisplay();
98 ConnectEvents(); 99 ConnectEvents();
@@ -101,17 +102,14 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
101ConfigureMotionTouch::~ConfigureMotionTouch() = default; 102ConfigureMotionTouch::~ConfigureMotionTouch() = default;
102 103
103void ConfigureMotionTouch::SetConfiguration() { 104void ConfigureMotionTouch::SetConfiguration() {
104 const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue());
105 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); 105 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
106 106
107 ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue());
108 touch_from_button_maps = Settings::values.touch_from_button_maps; 107 touch_from_button_maps = Settings::values.touch_from_button_maps;
109 for (const auto& touch_map : touch_from_button_maps) { 108 for (const auto& touch_map : touch_from_button_maps) {
110 ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); 109 ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
111 } 110 }
112 ui->touch_from_button_map->setCurrentIndex( 111 ui->touch_from_button_map->setCurrentIndex(
113 Settings::values.touch_from_button_map_index.GetValue()); 112 Settings::values.touch_from_button_map_index.GetValue());
114 ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
115 113
116 min_x = touch_param.Get("min_x", 100); 114 min_x = touch_param.Get("min_x", 100);
117 min_y = touch_param.Get("min_y", 50); 115 min_y = touch_param.Get("min_y", 50);
@@ -139,9 +137,6 @@ void ConfigureMotionTouch::SetConfiguration() {
139void ConfigureMotionTouch::UpdateUiDisplay() { 137void ConfigureMotionTouch::UpdateUiDisplay() {
140 const QString cemuhook_udp = QStringLiteral("cemuhookudp"); 138 const QString cemuhook_udp = QStringLiteral("cemuhookudp");
141 139
142 ui->motion_sensitivity_label->setVisible(true);
143 ui->motion_sensitivity->setVisible(true);
144
145 ui->touch_calibration->setVisible(true); 140 ui->touch_calibration->setVisible(true);
146 ui->touch_calibration_config->setVisible(true); 141 ui->touch_calibration_config->setVisible(true);
147 ui->touch_calibration_label->setVisible(true); 142 ui->touch_calibration_label->setVisible(true);
@@ -312,7 +307,6 @@ void ConfigureMotionTouch::ApplyConfiguration() {
312 touch_param.Set("max_y", max_y); 307 touch_param.Set("max_y", max_y);
313 308
314 Settings::values.touch_device = touch_param.Serialize(); 309 Settings::values.touch_device = touch_param.Serialize();
315 Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();
316 Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex(); 310 Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex();
317 Settings::values.touch_from_button_maps = touch_from_button_maps; 311 Settings::values.touch_from_button_maps = touch_from_button_maps;
318 Settings::values.udp_input_servers = GetUDPServerString(); 312 Settings::values.udp_input_servers = GetUDPServerString();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index 1e35ea946..c75a84ae4 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -2,14 +2,6 @@
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureMotionTouch</class> 3 <class>ConfigureMotionTouch</class>
4 <widget class="QDialog" name="ConfigureMotionTouch"> 4 <widget class="QDialog" name="ConfigureMotionTouch">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>500</width>
10 <height>482</height>
11 </rect>
12 </property>
13 <property name="windowTitle"> 5 <property name="windowTitle">
14 <string>Configure Motion / Touch</string> 6 <string>Configure Motion / Touch</string>
15 </property> 7 </property>
@@ -18,48 +10,6 @@
18 </property> 10 </property>
19 <layout class="QVBoxLayout"> 11 <layout class="QVBoxLayout">
20 <item> 12 <item>
21 <widget class="QGroupBox" name="motion_group_box">
22 <property name="title">
23 <string>Mouse Motion</string>
24 </property>
25 <layout class="QVBoxLayout">
26 <item>
27 <layout class="QHBoxLayout">
28 <item>
29 <widget class="QLabel" name="motion_sensitivity_label">
30 <property name="text">
31 <string>Sensitivity:</string>
32 </property>
33 </widget>
34 </item>
35 <item>
36 <widget class="QDoubleSpinBox" name="motion_sensitivity">
37 <property name="alignment">
38 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
39 </property>
40 <property name="decimals">
41 <number>4</number>
42 </property>
43 <property name="minimum">
44 <double>0.010000000000000</double>
45 </property>
46 <property name="maximum">
47 <double>10.000000000000000</double>
48 </property>
49 <property name="singleStep">
50 <double>0.001000000000000</double>
51 </property>
52 <property name="value">
53 <double>0.010000000000000</double>
54 </property>
55 </widget>
56 </item>
57 </layout>
58 </item>
59 </layout>
60 </widget>
61 </item>
62 <item>
63 <widget class="QGroupBox" name="touch_group_box"> 13 <widget class="QGroupBox" name="touch_group_box">
64 <property name="title"> 14 <property name="title">
65 <string>Touch</string> 15 <string>Touch</string>
@@ -101,19 +51,13 @@
101 </item> 51 </item>
102 <item> 52 <item>
103 <layout class="QHBoxLayout"> 53 <layout class="QHBoxLayout">
104 <item> 54 <item>
105 <widget class="QCheckBox" name="touch_from_button_checkbox"> 55 <widget class="QLabel" name="touch_from_button_label">
106 <property name="sizePolicy"> 56 <property name="text">
107 <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> 57 <string>Touch from button profile:</string>
108 <horstretch>0</horstretch> 58 </property>
109 <verstretch>0</verstretch> 59 </widget>
110 </sizepolicy> 60 </item>
111 </property>
112 <property name="text">
113 <string>Use button mapping:</string>
114 </property>
115 </widget>
116 </item>
117 <item> 61 <item>
118 <widget class="QComboBox" name="touch_from_button_map"/> 62 <widget class="QComboBox" name="touch_from_button_map"/>
119 </item> 63 </item>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
deleted file mode 100644
index 2af3afda8..000000000
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7
8#include <QKeyEvent>
9#include <QMenu>
10#include <QTimer>
11
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_mouse_advanced.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_mouse_advanced.h"
18
19static QString GetKeyName(int key_code) {
20 switch (key_code) {
21 case Qt::LeftButton:
22 return QObject::tr("Click 0");
23 case Qt::RightButton:
24 return QObject::tr("Click 1");
25 case Qt::MiddleButton:
26 return QObject::tr("Click 2");
27 case Qt::BackButton:
28 return QObject::tr("Click 3");
29 case Qt::ForwardButton:
30 return QObject::tr("Click 4");
31 case Qt::Key_Shift:
32 return QObject::tr("Shift");
33 case Qt::Key_Control:
34 return QObject::tr("Ctrl");
35 case Qt::Key_Alt:
36 return QObject::tr("Alt");
37 case Qt::Key_Meta:
38 return {};
39 default:
40 return QKeySequence(key_code).toString();
41 }
42}
43
44static QString ButtonToText(const Common::ParamPackage& param) {
45 if (!param.Has("engine")) {
46 return QObject::tr("[not set]");
47 }
48
49 if (param.Get("engine", "") == "keyboard") {
50 return GetKeyName(param.Get("code", 0));
51 }
52
53 if (param.Get("engine", "") == "sdl") {
54 if (param.Has("hat")) {
55 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
56 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
57
58 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
59 }
60
61 if (param.Has("axis")) {
62 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
63 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
64
65 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
66 }
67
68 if (param.Has("button")) {
69 const QString button_str = QString::fromStdString(param.Get("button", ""));
70
71 return QObject::tr("Button %1").arg(button_str);
72 }
73 return {};
74 }
75
76 return QObject::tr("[unknown]");
77}
78
79ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
80 InputCommon::InputSubsystem* input_subsystem_)
81 : QDialog(parent),
82 ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
83 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
84 ui->setupUi(this);
85 setFocusPolicy(Qt::ClickFocus);
86
87 button_map = {
88 ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
89 };
90
91 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
92 auto* const button = button_map[button_id];
93 if (button == nullptr) {
94 continue;
95 }
96
97 button->setContextMenuPolicy(Qt::CustomContextMenu);
98 connect(button, &QPushButton::clicked, [=, this] {
99 HandleClick(
100 button_map[button_id],
101 [=, this](const Common::ParamPackage& params) {
102 buttons_param[button_id] = params;
103 },
104 InputCommon::Polling::DeviceType::Button);
105 });
106 connect(button, &QPushButton::customContextMenuRequested,
107 [=, this](const QPoint& menu_location) {
108 QMenu context_menu;
109 context_menu.addAction(tr("Clear"), [&] {
110 buttons_param[button_id].Clear();
111 button_map[button_id]->setText(tr("[not set]"));
112 });
113 context_menu.addAction(tr("Restore Default"), [&] {
114 buttons_param[button_id] =
115 Common::ParamPackage{InputCommon::GenerateKeyboardParam(
116 Config::default_mouse_buttons[button_id])};
117 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
118 });
119 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
120 });
121 }
122
123 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
124 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
125
126 timeout_timer->setSingleShot(true);
127 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
128
129 connect(poll_timer.get(), &QTimer::timeout, [this] {
130 Common::ParamPackage params;
131 for (auto& poller : device_pollers) {
132 params = poller->GetNextInput();
133 if (params.Has("engine")) {
134 SetPollingResult(params, false);
135 return;
136 }
137 }
138 });
139
140 LoadConfiguration();
141 resize(0, 0);
142}
143
144ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
145
146void ConfigureMouseAdvanced::ApplyConfiguration() {
147 std::transform(buttons_param.begin(), buttons_param.end(),
148 Settings::values.mouse_buttons.begin(),
149 [](const Common::ParamPackage& param) { return param.Serialize(); });
150}
151
152void ConfigureMouseAdvanced::LoadConfiguration() {
153 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
154 buttons_param.begin(),
155 [](const std::string& str) { return Common::ParamPackage(str); });
156 UpdateButtonLabels();
157}
158
159void ConfigureMouseAdvanced::changeEvent(QEvent* event) {
160 if (event->type() == QEvent::LanguageChange) {
161 RetranslateUI();
162 }
163
164 QDialog::changeEvent(event);
165}
166
167void ConfigureMouseAdvanced::RetranslateUI() {
168 ui->retranslateUi(this);
169}
170
171void ConfigureMouseAdvanced::RestoreDefaults() {
172 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
173 buttons_param[button_id] = Common::ParamPackage{
174 InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
175 }
176
177 UpdateButtonLabels();
178}
179
180void ConfigureMouseAdvanced::ClearAll() {
181 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
182 const auto* const button = button_map[i];
183 if (button != nullptr && button->isEnabled()) {
184 buttons_param[i].Clear();
185 }
186 }
187
188 UpdateButtonLabels();
189}
190
191void ConfigureMouseAdvanced::UpdateButtonLabels() {
192 for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
193 button_map[button]->setText(ButtonToText(buttons_param[button]));
194 }
195}
196
197void ConfigureMouseAdvanced::HandleClick(
198 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
199 InputCommon::Polling::DeviceType type) {
200 button->setText(tr("[press key]"));
201 button->setFocus();
202
203 // Keyboard keys or mouse buttons can only be used as button devices
204 want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
205 if (want_keyboard_mouse) {
206 const auto iter = std::find(button_map.begin(), button_map.end(), button);
207 ASSERT(iter != button_map.end());
208 const auto index = std::distance(button_map.begin(), iter);
209 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
210 }
211
212 input_setter = new_input_setter;
213
214 device_pollers = input_subsystem->GetPollers(type);
215
216 for (auto& poller : device_pollers) {
217 poller->Start();
218 }
219
220 QWidget::grabMouse();
221 QWidget::grabKeyboard();
222
223 timeout_timer->start(2500); // Cancel after 2.5 seconds
224 poll_timer->start(50); // Check for new inputs every 50ms
225}
226
227void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
228 timeout_timer->stop();
229 poll_timer->stop();
230 for (auto& poller : device_pollers) {
231 poller->Stop();
232 }
233
234 QWidget::releaseMouse();
235 QWidget::releaseKeyboard();
236
237 if (!abort) {
238 (*input_setter)(params);
239 }
240
241 UpdateButtonLabels();
242 input_setter = std::nullopt;
243}
244
245void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
246 if (!input_setter || !event) {
247 return;
248 }
249
250 if (want_keyboard_mouse) {
251 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
252 false);
253 } else {
254 // We don't want any mouse buttons, so don't stop polling
255 return;
256 }
257
258 SetPollingResult({}, true);
259}
260
261void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
262 if (!input_setter || !event) {
263 return;
264 }
265
266 if (event->key() != Qt::Key_Escape) {
267 if (want_keyboard_mouse) {
268 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
269 false);
270 } else {
271 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
272 return;
273 }
274 }
275 SetPollingResult({}, true);
276}
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
deleted file mode 100644
index 65b6fca9a..000000000
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ /dev/null
@@ -1,78 +0,0 @@
1// Copyright 2016 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 <optional>
9#include <QDialog>
10
11class QCheckBox;
12class QPushButton;
13class QTimer;
14
15namespace InputCommon {
16class InputSubsystem;
17}
18
19namespace Ui {
20class ConfigureMouseAdvanced;
21}
22
23class ConfigureMouseAdvanced : public QDialog {
24 Q_OBJECT
25
26public:
27 explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
28 ~ConfigureMouseAdvanced() override;
29
30 void ApplyConfiguration();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35
36 /// Load configuration settings.
37 void LoadConfiguration();
38 /// Restore all buttons to their default values.
39 void RestoreDefaults();
40 /// Clear all input configuration
41 void ClearAll();
42
43 /// Update UI to reflect current configuration.
44 void UpdateButtonLabels();
45
46 /// Called when the button was pressed.
47 void HandleClick(QPushButton* button,
48 std::function<void(const Common::ParamPackage&)> new_input_setter,
49 InputCommon::Polling::DeviceType type);
50
51 /// Finish polling and configure input using the input_setter
52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
53
54 /// Handle mouse button press events.
55 void mousePressEvent(QMouseEvent* event) override;
56
57 /// Handle key press events.
58 void keyPressEvent(QKeyEvent* event) override;
59
60 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
61
62 InputCommon::InputSubsystem* input_subsystem;
63
64 /// This will be the the setting function when an input is awaiting configuration.
65 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
66
67 std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
68 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
69
70 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
71
72 std::unique_ptr<QTimer> timeout_timer;
73 std::unique_ptr<QTimer> poll_timer;
74
75 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
76 /// keyboard events are ignored.
77 bool want_keyboard_mouse = false;
78};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
deleted file mode 100644
index 5b99e1c37..000000000
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ /dev/null
@@ -1,335 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMouseAdvanced</class>
4 <widget class="QDialog" name="ConfigureMouseAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>310</width>
10 <height>193</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Mouse</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true">QPushButton {
18 min-width: 60px;
19}</string>
20 </property>
21 <layout class="QVBoxLayout" name="verticalLayout">
22 <item>
23 <widget class="QGroupBox" name="gridGroupBox">
24 <property name="title">
25 <string>Mouse Buttons</string>
26 </property>
27 <layout class="QGridLayout" name="gridLayout">
28 <item row="3" column="5">
29 <layout class="QVBoxLayout" name="verticalLayout_6">
30 <item>
31 <layout class="QHBoxLayout" name="horizontalLayout_5">
32 <item>
33 <widget class="QLabel" name="label_5">
34 <property name="text">
35 <string>Forward:</string>
36 </property>
37 </widget>
38 </item>
39 </layout>
40 </item>
41 <item>
42 <widget class="QPushButton" name="forward_button">
43 <property name="minimumSize">
44 <size>
45 <width>68</width>
46 <height>0</height>
47 </size>
48 </property>
49 <property name="maximumSize">
50 <size>
51 <width>68</width>
52 <height>16777215</height>
53 </size>
54 </property>
55 <property name="text">
56 <string/>
57 </property>
58 </widget>
59 </item>
60 </layout>
61 </item>
62 <item row="3" column="1">
63 <layout class="QVBoxLayout" name="verticalLayout_5">
64 <item>
65 <layout class="QHBoxLayout" name="horizontalLayout_4">
66 <item>
67 <widget class="QLabel" name="label_4">
68 <property name="minimumSize">
69 <size>
70 <width>54</width>
71 <height>0</height>
72 </size>
73 </property>
74 <property name="text">
75 <string>Back:</string>
76 </property>
77 </widget>
78 </item>
79 </layout>
80 </item>
81 <item>
82 <widget class="QPushButton" name="back_button">
83 <property name="minimumSize">
84 <size>
85 <width>68</width>
86 <height>0</height>
87 </size>
88 </property>
89 <property name="text">
90 <string/>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item row="0" column="1">
97 <layout class="QVBoxLayout" name="verticalLayout_2">
98 <item>
99 <layout class="QHBoxLayout" name="horizontalLayout">
100 <item>
101 <widget class="QLabel" name="label">
102 <property name="text">
103 <string>Left:</string>
104 </property>
105 </widget>
106 </item>
107 </layout>
108 </item>
109 <item>
110 <widget class="QPushButton" name="left_button">
111 <property name="minimumSize">
112 <size>
113 <width>68</width>
114 <height>0</height>
115 </size>
116 </property>
117 <property name="text">
118 <string/>
119 </property>
120 </widget>
121 </item>
122 </layout>
123 </item>
124 <item row="0" column="3">
125 <layout class="QVBoxLayout" name="verticalLayout_3">
126 <item>
127 <layout class="QHBoxLayout" name="horizontalLayout_2">
128 <item>
129 <widget class="QLabel" name="label_2">
130 <property name="text">
131 <string>Middle:</string>
132 </property>
133 </widget>
134 </item>
135 </layout>
136 </item>
137 <item>
138 <widget class="QPushButton" name="middle_button">
139 <property name="minimumSize">
140 <size>
141 <width>68</width>
142 <height>0</height>
143 </size>
144 </property>
145 <property name="maximumSize">
146 <size>
147 <width>68</width>
148 <height>16777215</height>
149 </size>
150 </property>
151 <property name="text">
152 <string/>
153 </property>
154 </widget>
155 </item>
156 </layout>
157 </item>
158 <item row="0" column="6">
159 <spacer name="horizontalSpacer_2">
160 <property name="orientation">
161 <enum>Qt::Horizontal</enum>
162 </property>
163 <property name="sizeType">
164 <enum>QSizePolicy::Fixed</enum>
165 </property>
166 <property name="sizeHint" stdset="0">
167 <size>
168 <width>0</width>
169 <height>20</height>
170 </size>
171 </property>
172 </spacer>
173 </item>
174 <item row="0" column="0">
175 <spacer name="horizontalSpacer">
176 <property name="orientation">
177 <enum>Qt::Horizontal</enum>
178 </property>
179 <property name="sizeType">
180 <enum>QSizePolicy::Fixed</enum>
181 </property>
182 <property name="sizeHint" stdset="0">
183 <size>
184 <width>0</width>
185 <height>20</height>
186 </size>
187 </property>
188 </spacer>
189 </item>
190 <item row="0" column="5">
191 <layout class="QVBoxLayout" name="verticalLayout_4">
192 <item>
193 <layout class="QHBoxLayout" name="horizontalLayout_3">
194 <item>
195 <widget class="QLabel" name="label_3">
196 <property name="text">
197 <string>Right:</string>
198 </property>
199 </widget>
200 </item>
201 </layout>
202 </item>
203 <item>
204 <widget class="QPushButton" name="right_button">
205 <property name="minimumSize">
206 <size>
207 <width>68</width>
208 <height>0</height>
209 </size>
210 </property>
211 <property name="maximumSize">
212 <size>
213 <width>68</width>
214 <height>16777215</height>
215 </size>
216 </property>
217 <property name="text">
218 <string/>
219 </property>
220 </widget>
221 </item>
222 </layout>
223 </item>
224 <item row="0" column="2">
225 <spacer name="horizontalSpacer_4">
226 <property name="orientation">
227 <enum>Qt::Horizontal</enum>
228 </property>
229 <property name="sizeHint" stdset="0">
230 <size>
231 <width>0</width>
232 <height>20</height>
233 </size>
234 </property>
235 </spacer>
236 </item>
237 <item row="0" column="4">
238 <spacer name="horizontalSpacer_5">
239 <property name="orientation">
240 <enum>Qt::Horizontal</enum>
241 </property>
242 <property name="sizeHint" stdset="0">
243 <size>
244 <width>0</width>
245 <height>20</height>
246 </size>
247 </property>
248 </spacer>
249 </item>
250 </layout>
251 </widget>
252 </item>
253 <item>
254 <layout class="QHBoxLayout" name="horizontalLayout_6">
255 <item>
256 <widget class="QPushButton" name="buttonClearAll">
257 <property name="minimumSize">
258 <size>
259 <width>68</width>
260 <height>0</height>
261 </size>
262 </property>
263 <property name="maximumSize">
264 <size>
265 <width>68</width>
266 <height>16777215</height>
267 </size>
268 </property>
269 <property name="text">
270 <string>Clear</string>
271 </property>
272 </widget>
273 </item>
274 <item>
275 <widget class="QPushButton" name="buttonRestoreDefaults">
276 <property name="minimumSize">
277 <size>
278 <width>68</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="maximumSize">
283 <size>
284 <width>68</width>
285 <height>16777215</height>
286 </size>
287 </property>
288 <property name="text">
289 <string>Defaults</string>
290 </property>
291 </widget>
292 </item>
293 <item>
294 <spacer name="horizontalSpacer_3">
295 <property name="orientation">
296 <enum>Qt::Horizontal</enum>
297 </property>
298 <property name="sizeHint" stdset="0">
299 <size>
300 <width>0</width>
301 <height>20</height>
302 </size>
303 </property>
304 </spacer>
305 </item>
306 <item>
307 <widget class="QDialogButtonBox" name="buttonBox">
308 <property name="styleSheet">
309 <string notr="true"/>
310 </property>
311 <property name="standardButtons">
312 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
313 </property>
314 </widget>
315 </item>
316 </layout>
317 </item>
318 </layout>
319 </widget>
320 <resources/>
321 <connections>
322 <connection>
323 <sender>buttonBox</sender>
324 <signal>accepted()</signal>
325 <receiver>ConfigureMouseAdvanced</receiver>
326 <slot>accept()</slot>
327 </connection>
328 <connection>
329 <sender>buttonBox</sender>
330 <signal>rejected()</signal>
331 <receiver>ConfigureMouseAdvanced</receiver>
332 <slot>reject()</slot>
333 </connection>
334 </connections>
335</ui>
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 8e5a4c72d..979a8db61 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -32,7 +32,6 @@ void ConfigureTasDialog::LoadConfiguration() {
32 ui->tas_path_edit->setText( 32 ui->tas_path_edit->setText(
33 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir))); 33 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir)));
34 ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue()); 34 ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue());
35 ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue());
36 ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue()); 35 ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue());
37 ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue()); 36 ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue());
38} 37}
@@ -40,7 +39,6 @@ void ConfigureTasDialog::LoadConfiguration() {
40void ConfigureTasDialog::ApplyConfiguration() { 39void ConfigureTasDialog::ApplyConfiguration() {
41 Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString()); 40 Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString());
42 Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked()); 41 Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked());
43 Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked());
44 Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked()); 42 Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked());
45 Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked()); 43 Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked());
46} 44}
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui
index 7d44895c4..cf88a5bf0 100644
--- a/src/yuzu/configuration/configure_tas.ui
+++ b/src/yuzu/configuration/configure_tas.ui
@@ -59,20 +59,13 @@
59 </widget> 59 </widget>
60 </item> 60 </item>
61 <item row="1" column="0" colspan="4"> 61 <item row="1" column="0" colspan="4">
62 <widget class="QCheckBox" name="tas_control_swap">
63 <property name="text">
64 <string>Automatic controller profile swapping</string>
65 </property>
66 </widget>
67 </item>
68 <item row="2" column="0" colspan="4">
69 <widget class="QCheckBox" name="tas_loop_script"> 62 <widget class="QCheckBox" name="tas_loop_script">
70 <property name="text"> 63 <property name="text">
71 <string>Loop script</string> 64 <string>Loop script</string>
72 </property> 65 </property>
73 </widget> 66 </widget>
74 </item> 67 </item>
75 <item row="3" column="0" colspan="4"> 68 <item row="2" column="0" colspan="4">
76 <widget class="QCheckBox" name="tas_pause_on_load"> 69 <widget class="QCheckBox" name="tas_pause_on_load">
77 <property name="enabled"> 70 <property name="enabled">
78 <bool>false</bool> 71 <bool>false</bool>
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index 40129f228..bde0a08c4 100644
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -163,13 +163,10 @@ void ConfigureTouchFromButton::ConnectEvents() {
163 connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); }); 163 connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
164 164
165 connect(poll_timer.get(), &QTimer::timeout, [this]() { 165 connect(poll_timer.get(), &QTimer::timeout, [this]() {
166 Common::ParamPackage params; 166 const auto& params = input_subsystem->GetNextInput();
167 for (auto& poller : device_pollers) { 167 if (params.Has("engine")) {
168 params = poller->GetNextInput(); 168 SetPollingResult(params, false);
169 if (params.Has("engine")) { 169 return;
170 SetPollingResult(params, false);
171 return;
172 }
173 } 170 }
174 }); 171 });
175} 172}
@@ -248,11 +245,7 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is
248 } 245 }
249 }; 246 };
250 247
251 device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button); 248 input_subsystem->BeginMapping(InputCommon::Polling::InputType::Button);
252
253 for (auto& poller : device_pollers) {
254 poller->Start();
255 }
256 249
257 grabKeyboard(); 250 grabKeyboard();
258 grabMouse(); 251 grabMouse();
@@ -365,14 +358,14 @@ void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& po
365 358
366void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, 359void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params,
367 const bool cancel) { 360 const bool cancel) {
361 timeout_timer->stop();
362 poll_timer->stop();
363 input_subsystem->StopMapping();
364
368 releaseKeyboard(); 365 releaseKeyboard();
369 releaseMouse(); 366 releaseMouse();
370 qApp->restoreOverrideCursor(); 367 qApp->restoreOverrideCursor();
371 timeout_timer->stop(); 368
372 poll_timer->stop();
373 for (auto& poller : device_pollers) {
374 poller->Stop();
375 }
376 if (input_setter) { 369 if (input_setter) {
377 (*input_setter)(params, cancel); 370 (*input_setter)(params, cancel);
378 input_setter.reset(); 371 input_setter.reset();
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h
index d9513e3bc..e1400481a 100644
--- a/src/yuzu/configuration/configure_touch_from_button.h
+++ b/src/yuzu/configuration/configure_touch_from_button.h
@@ -24,10 +24,6 @@ namespace InputCommon {
24class InputSubsystem; 24class InputSubsystem;
25} 25}
26 26
27namespace InputCommon::Polling {
28class DevicePoller;
29}
30
31namespace Settings { 27namespace Settings {
32struct TouchFromButtonMap; 28struct TouchFromButtonMap;
33} 29}
@@ -85,7 +81,6 @@ private:
85 81
86 std::unique_ptr<QTimer> timeout_timer; 82 std::unique_ptr<QTimer> timeout_timer;
87 std::unique_ptr<QTimer> poll_timer; 83 std::unique_ptr<QTimer> poll_timer;
88 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
89 std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter; 84 std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter;
90 85
91 static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2; 86 static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2;
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index 46a0f3025..adce04b27 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -59,80 +59,6 @@ void ConfigureVibration::ApplyConfiguration() {
59 ui->checkBoxAccurateVibration->isChecked()); 59 ui->checkBoxAccurateVibration->isChecked());
60} 60}
61 61
62void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
63 using namespace Settings::NativeButton;
64 static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
65 {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
66 {A, B, X, Y, R, ZR}, // Right Buttons
67 }};
68
69 auto& player = Settings::values.players.GetValue()[player_index];
70
71 for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
72 std::unordered_map<std::string, int> params_count;
73
74 for (const auto button_index : buttons[device_idx]) {
75 const auto& player_button = player.buttons[button_index];
76
77 if (params_count.find(player_button) != params_count.end()) {
78 ++params_count[player_button];
79 continue;
80 }
81
82 params_count.insert_or_assign(player_button, 1);
83 }
84
85 const auto it = std::max_element(
86 params_count.begin(), params_count.end(),
87 [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
88
89 auto& vibration_param_str = player.vibrations[device_idx];
90 vibration_param_str.clear();
91
92 if (it->first.empty()) {
93 continue;
94 }
95
96 const auto param = Common::ParamPackage(it->first);
97
98 const auto engine = param.Get("engine", "");
99 const auto guid = param.Get("guid", "");
100 const auto port = param.Get("port", "");
101
102 if (engine.empty() || engine == "keyboard" || engine == "mouse" || engine == "tas") {
103 continue;
104 }
105
106 vibration_param_str += fmt::format("engine:{}", engine);
107
108 if (!port.empty()) {
109 vibration_param_str += fmt::format(",port:{}", port);
110 }
111 if (!guid.empty()) {
112 vibration_param_str += fmt::format(",guid:{}", guid);
113 }
114 }
115
116 if (player.vibrations[0] != player.vibrations[1]) {
117 return;
118 }
119
120 if (!player.vibrations[0].empty() &&
121 player.controller_type != Settings::ControllerType::RightJoycon) {
122 player.vibrations[1].clear();
123 } else if (!player.vibrations[1].empty() &&
124 player.controller_type == Settings::ControllerType::RightJoycon) {
125 player.vibrations[0].clear();
126 }
127}
128
129void ConfigureVibration::SetAllVibrationDevices() {
130 // Set vibration devices for all player indices including handheld
131 for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
132 SetVibrationDevices(player_idx);
133 }
134}
135
136void ConfigureVibration::changeEvent(QEvent* event) { 62void ConfigureVibration::changeEvent(QEvent* event) {
137 if (event->type() == QEvent::LanguageChange) { 63 if (event->type() == QEvent::LanguageChange) {
138 RetranslateUI(); 64 RetranslateUI();
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
index 07411a86f..37bbc2653 100644
--- a/src/yuzu/configuration/configure_vibration.h
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -24,9 +24,6 @@ public:
24 24
25 void ApplyConfiguration(); 25 void ApplyConfiguration();
26 26
27 static void SetVibrationDevices(std::size_t player_index);
28 static void SetAllVibrationDevices();
29
30private: 27private:
31 void changeEvent(QEvent* event) override; 28 void changeEvent(QEvent* event) override;
32 void RetranslateUI(); 29 void RetranslateUI();
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 5a844409b..6b834c42e 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -6,13 +6,17 @@
6#include <QLayout> 6#include <QLayout>
7#include <QString> 7#include <QString>
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "input_common/drivers/tas_input.h"
9#include "input_common/main.h" 12#include "input_common/main.h"
10#include "input_common/tas/tas_input.h"
11#include "yuzu/configuration/configure_input_player_widget.h" 13#include "yuzu/configuration/configure_input_player_widget.h"
12#include "yuzu/debugger/controller.h" 14#include "yuzu/debugger/controller.h"
13 15
14ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) 16ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
15 : QWidget(parent, Qt::Dialog), input_subsystem{input_subsystem_} { 17 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
18 QWidget* parent)
19 : QWidget(parent, Qt::Dialog), hid_core{hid_core_}, input_subsystem{input_subsystem_} {
16 setObjectName(QStringLiteral("Controller")); 20 setObjectName(QStringLiteral("Controller"));
17 setWindowTitle(tr("Controller P1")); 21 setWindowTitle(tr("Controller P1"));
18 resize(500, 350); 22 resize(500, 350);
@@ -31,20 +35,24 @@ ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem*
31 // Configure focus so that widget is focusable and the dialog automatically forwards focus to 35 // Configure focus so that widget is focusable and the dialog automatically forwards focus to
32 // it. 36 // it.
33 setFocusProxy(widget); 37 setFocusProxy(widget);
34 widget->SetConnectedStatus(false);
35 widget->setFocusPolicy(Qt::StrongFocus); 38 widget->setFocusPolicy(Qt::StrongFocus);
36 widget->setFocus(); 39 widget->setFocus();
37} 40}
38 41
39void ControllerDialog::refreshConfiguration() { 42void ControllerDialog::refreshConfiguration() {
40 const auto& players = Settings::values.players.GetValue(); 43 UnloadController();
41 constexpr std::size_t player = 0; 44 auto* player_1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
42 widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); 45 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
43 widget->SetControllerType(players[player].controller_type); 46 // Display the correct controller
44 ControllerCallback callback{[this](ControllerInput input) { InputController(input); }}; 47 controller = handheld->IsConnected() ? handheld : player_1;
45 widget->SetCallBack(callback); 48
46 widget->repaint(); 49 Core::HID::ControllerUpdateCallback engine_callback{
47 widget->SetConnectedStatus(players[player].connected); 50 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
51 .is_npad_service = true,
52 };
53 callback_key = controller->SetCallback(engine_callback);
54 widget->SetController(controller);
55 is_controller_set = true;
48} 56}
49 57
50QAction* ControllerDialog::toggleViewAction() { 58QAction* ControllerDialog::toggleViewAction() {
@@ -58,11 +66,18 @@ QAction* ControllerDialog::toggleViewAction() {
58 return toggle_view_action; 66 return toggle_view_action;
59} 67}
60 68
69void ControllerDialog::UnloadController() {
70 widget->UnloadController();
71 if (is_controller_set) {
72 controller->DeleteCallback(callback_key);
73 is_controller_set = false;
74 }
75}
76
61void ControllerDialog::showEvent(QShowEvent* ev) { 77void ControllerDialog::showEvent(QShowEvent* ev) {
62 if (toggle_view_action) { 78 if (toggle_view_action) {
63 toggle_view_action->setChecked(isVisible()); 79 toggle_view_action->setChecked(isVisible());
64 } 80 }
65 refreshConfiguration();
66 QWidget::showEvent(ev); 81 QWidget::showEvent(ev);
67} 82}
68 83
@@ -70,16 +85,34 @@ void ControllerDialog::hideEvent(QHideEvent* ev) {
70 if (toggle_view_action) { 85 if (toggle_view_action) {
71 toggle_view_action->setChecked(isVisible()); 86 toggle_view_action->setChecked(isVisible());
72 } 87 }
73 widget->SetConnectedStatus(false);
74 QWidget::hideEvent(ev); 88 QWidget::hideEvent(ev);
75} 89}
76 90
77void ControllerDialog::InputController(ControllerInput input) { 91void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
78 u32 buttons = 0; 92 // TODO(german77): Remove TAS from here
79 int index = 0; 93 switch (type) {
80 for (bool btn : input.button_values) { 94 case Core::HID::ControllerTriggerType::Button:
81 buttons |= (btn ? 1U : 0U) << index; 95 case Core::HID::ControllerTriggerType::Stick: {
82 index++; 96 const auto buttons_values = controller->GetButtonsValues();
97 const auto stick_values = controller->GetSticksValues();
98 u64 buttons = 0;
99 std::size_t index = 0;
100 for (const auto& button : buttons_values) {
101 buttons |= button.value ? 1LLU << index : 0;
102 index++;
103 }
104 const InputCommon::TasInput::TasAnalog left_axis = {
105 .x = stick_values[Settings::NativeAnalog::LStick].x.value,
106 .y = stick_values[Settings::NativeAnalog::LStick].y.value,
107 };
108 const InputCommon::TasInput::TasAnalog right_axis = {
109 .x = stick_values[Settings::NativeAnalog::RStick].x.value,
110 .y = stick_values[Settings::NativeAnalog::RStick].y.value,
111 };
112 input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis);
113 break;
114 }
115 default:
116 break;
83 } 117 }
84 input_subsystem->GetTas()->RecordInput(buttons, input.axis_values);
85} 118}
diff --git a/src/yuzu/debugger/controller.h b/src/yuzu/debugger/controller.h
index 7742db58b..52cea3326 100644
--- a/src/yuzu/debugger/controller.h
+++ b/src/yuzu/debugger/controller.h
@@ -4,9 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <QFileSystemWatcher>
8#include <QWidget> 7#include <QWidget>
9#include "common/settings.h"
10 8
11class QAction; 9class QAction;
12class QHideEvent; 10class QHideEvent;
@@ -17,35 +15,43 @@ namespace InputCommon {
17class InputSubsystem; 15class InputSubsystem;
18} 16}
19 17
20struct ControllerInput { 18namespace Core::HID {
21 std::array<std::pair<float, float>, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; 19class HIDCore;
22 std::array<bool, Settings::NativeButton::NumButtons> button_values{}; 20class EmulatedController;
23 bool changed{}; 21enum class ControllerTriggerType;
24}; 22} // namespace Core::HID
25
26struct ControllerCallback {
27 std::function<void(ControllerInput)> input;
28};
29 23
30class ControllerDialog : public QWidget { 24class ControllerDialog : public QWidget {
31 Q_OBJECT 25 Q_OBJECT
32 26
33public: 27public:
34 explicit ControllerDialog(QWidget* parent = nullptr, 28 explicit ControllerDialog(Core::HID::HIDCore& hid_core_,
35 InputCommon::InputSubsystem* input_subsystem_ = nullptr); 29 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
30 QWidget* parent = nullptr);
36 31
37 /// Returns a QAction that can be used to toggle visibility of this dialog. 32 /// Returns a QAction that can be used to toggle visibility of this dialog.
38 QAction* toggleViewAction(); 33 QAction* toggleViewAction();
34
35 /// Reloads the widget to apply any changes in the configuration
39 void refreshConfiguration(); 36 void refreshConfiguration();
40 37
38 /// Disables events from the emulated controller
39 void UnloadController();
40
41protected: 41protected:
42 void showEvent(QShowEvent* ev) override; 42 void showEvent(QShowEvent* ev) override;
43 void hideEvent(QHideEvent* ev) override; 43 void hideEvent(QHideEvent* ev) override;
44 44
45private: 45private:
46 void InputController(ControllerInput input); 46 /// Redirects input from the widget to the TAS driver
47 void ControllerUpdate(Core::HID::ControllerTriggerType type);
48
49 int callback_key;
50 bool is_controller_set{};
51 Core::HID::EmulatedController* controller;
52
47 QAction* toggle_view_action = nullptr; 53 QAction* toggle_view_action = nullptr;
48 QFileSystemWatcher* watcher = nullptr;
49 PlayerControlPreview* widget; 54 PlayerControlPreview* widget;
50 InputCommon::InputSubsystem* input_subsystem; 55 Core::HID::HIDCore& hid_core;
56 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
51}; 57};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c4c76b094..9bd0db10a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -26,6 +26,8 @@
26#include "core/frontend/applets/controller.h" 26#include "core/frontend/applets/controller.h"
27#include "core/frontend/applets/general_frontend.h" 27#include "core/frontend/applets/general_frontend.h"
28#include "core/frontend/applets/software_keyboard.h" 28#include "core/frontend/applets/software_keyboard.h"
29#include "core/hid/emulated_controller.h"
30#include "core/hid/hid_core.h"
29#include "core/hle/service/acc/profile_manager.h" 31#include "core/hle/service/acc/profile_manager.h"
30#include "core/hle/service/am/applet_ae.h" 32#include "core/hle/service/am/applet_ae.h"
31#include "core/hle/service/am/applet_oe.h" 33#include "core/hle/service/am/applet_oe.h"
@@ -106,8 +108,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
106#include "core/loader/loader.h" 108#include "core/loader/loader.h"
107#include "core/perf_stats.h" 109#include "core/perf_stats.h"
108#include "core/telemetry_session.h" 110#include "core/telemetry_session.h"
111#include "input_common/drivers/tas_input.h"
109#include "input_common/main.h" 112#include "input_common/main.h"
110#include "input_common/tas/tas_input.h"
111#include "ui_main.h" 113#include "ui_main.h"
112#include "util/overlay_dialog.h" 114#include "util/overlay_dialog.h"
113#include "video_core/gpu.h" 115#include "video_core/gpu.h"
@@ -227,6 +229,9 @@ GMainWindow::GMainWindow()
227 ConnectMenuEvents(); 229 ConnectMenuEvents();
228 ConnectWidgetEvents(); 230 ConnectWidgetEvents();
229 231
232 system->HIDCore().ReloadInputDevices();
233 controller_dialog->refreshConfiguration();
234
230 const auto branch_name = std::string(Common::g_scm_branch); 235 const auto branch_name = std::string(Common::g_scm_branch);
231 const auto description = std::string(Common::g_scm_desc); 236 const auto description = std::string(Common::g_scm_desc);
232 const auto build_id = std::string(Common::g_build_id); 237 const auto build_id = std::string(Common::g_build_id);
@@ -829,15 +834,16 @@ void GMainWindow::InitializeWidgets() {
829 dock_status_button->setFocusPolicy(Qt::NoFocus); 834 dock_status_button->setFocusPolicy(Qt::NoFocus);
830 connect(dock_status_button, &QPushButton::clicked, [&] { 835 connect(dock_status_button, &QPushButton::clicked, [&] {
831 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 836 const bool is_docked = Settings::values.use_docked_mode.GetValue();
832 auto& controller_type = Settings::values.players.GetValue()[0].controller_type; 837 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
838 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
833 839
834 if (!is_docked && controller_type == Settings::ControllerType::Handheld) { 840 if (!is_docked && handheld->IsConnected()) {
835 QMessageBox::warning(this, tr("Invalid config detected"), 841 QMessageBox::warning(this, tr("Invalid config detected"),
836 tr("Handheld controller can't be used on docked mode. Pro " 842 tr("Handheld controller can't be used on docked mode. Pro "
837 "controller will be selected.")); 843 "controller will be selected."));
838 controller_type = Settings::ControllerType::ProController; 844 handheld->Disconnect();
839 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system); 845 player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
840 configure_dialog.ApplyConfiguration(); 846 player_1->Connect();
841 controller_dialog->refreshConfiguration(); 847 controller_dialog->refreshConfiguration();
842 } 848 }
843 849
@@ -922,7 +928,7 @@ void GMainWindow::InitializeDebugWidgets() {
922 waitTreeWidget->hide(); 928 waitTreeWidget->hide();
923 debug_menu->addAction(waitTreeWidget->toggleViewAction()); 929 debug_menu->addAction(waitTreeWidget->toggleViewAction());
924 930
925 controller_dialog = new ControllerDialog(this, input_subsystem.get()); 931 controller_dialog = new ControllerDialog(system->HIDCore(), input_subsystem, this);
926 controller_dialog->hide(); 932 controller_dialog->hide();
927 debug_menu->addAction(controller_dialog->toggleViewAction()); 933 debug_menu->addAction(controller_dialog->toggleViewAction());
928 934
@@ -952,171 +958,80 @@ void GMainWindow::InitializeRecentFileMenuActions() {
952 UpdateRecentFiles(); 958 UpdateRecentFiles();
953} 959}
954 960
961void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name) {
962 static const QString main_window = QStringLiteral("Main Window");
963 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name));
964 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name));
965
966 this->addAction(action);
967}
968
955void GMainWindow::InitializeHotkeys() { 969void GMainWindow::InitializeHotkeys() {
956 hotkey_registry.LoadHotkeys(); 970 hotkey_registry.LoadHotkeys();
957 971
958 const QString main_window = QStringLiteral("Main Window"); 972 LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
959 const QString load_file = QStringLiteral("Load File"); 973 LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo"));
960 const QString load_amiibo = QStringLiteral("Load Amiibo"); 974 LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu"));
961 const QString exit_yuzu = QStringLiteral("Exit yuzu"); 975 LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));
962 const QString restart_emulation = QStringLiteral("Restart Emulation"); 976 LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation"));
963 const QString stop_emulation = QStringLiteral("Stop Emulation"); 977 LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation"));
964 const QString toggle_filter_bar = QStringLiteral("Toggle Filter Bar"); 978 LinkActionShortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar"));
965 const QString toggle_status_bar = QStringLiteral("Toggle Status Bar"); 979 LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
966 const QString fullscreen = QStringLiteral("Fullscreen"); 980 LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
967 const QString capture_screenshot = QStringLiteral("Capture Screenshot"); 981 LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
968 982 LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"));
969 ui->action_Load_File->setShortcut(hotkey_registry.GetKeySequence(main_window, load_file)); 983 LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"));
970 ui->action_Load_File->setShortcutContext( 984 LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"));
971 hotkey_registry.GetShortcutContext(main_window, load_file)); 985
972 986 static const QString main_window = QStringLiteral("Main Window");
973 ui->action_Load_Amiibo->setShortcut(hotkey_registry.GetKeySequence(main_window, load_amiibo)); 987 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
974 ui->action_Load_Amiibo->setShortcutContext( 988 const QShortcut* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this);
975 hotkey_registry.GetShortcutContext(main_window, load_amiibo)); 989 connect(hotkey, &QShortcut::activated, this, function);
976 990 };
977 ui->action_Exit->setShortcut(hotkey_registry.GetKeySequence(main_window, exit_yuzu)); 991
978 ui->action_Exit->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, exit_yuzu)); 992 connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] {
979 993 if (emulation_running && ui->action_Fullscreen->isChecked()) {
980 ui->action_Restart->setShortcut(hotkey_registry.GetKeySequence(main_window, restart_emulation)); 994 ui->action_Fullscreen->setChecked(false);
981 ui->action_Restart->setShortcutContext( 995 ToggleFullscreen();
982 hotkey_registry.GetShortcutContext(main_window, restart_emulation)); 996 }
983 997 });
984 ui->action_Stop->setShortcut(hotkey_registry.GetKeySequence(main_window, stop_emulation)); 998 connect_shortcut(QStringLiteral("Toggle Speed Limit"), [&] {
985 ui->action_Stop->setShortcutContext( 999 Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
986 hotkey_registry.GetShortcutContext(main_window, stop_emulation)); 1000 UpdateStatusBar();
987 1001 });
988 ui->action_Show_Filter_Bar->setShortcut(
989 hotkey_registry.GetKeySequence(main_window, toggle_filter_bar));
990 ui->action_Show_Filter_Bar->setShortcutContext(
991 hotkey_registry.GetShortcutContext(main_window, toggle_filter_bar));
992
993 ui->action_Show_Status_Bar->setShortcut(
994 hotkey_registry.GetKeySequence(main_window, toggle_status_bar));
995 ui->action_Show_Status_Bar->setShortcutContext(
996 hotkey_registry.GetShortcutContext(main_window, toggle_status_bar));
997
998 ui->action_Capture_Screenshot->setShortcut(
999 hotkey_registry.GetKeySequence(main_window, capture_screenshot));
1000 ui->action_Capture_Screenshot->setShortcutContext(
1001 hotkey_registry.GetShortcutContext(main_window, capture_screenshot));
1002
1003 ui->action_Fullscreen->setShortcut(
1004 hotkey_registry.GetHotkey(main_window, fullscreen, this)->key());
1005 ui->action_Fullscreen->setShortcutContext(
1006 hotkey_registry.GetShortcutContext(main_window, fullscreen));
1007
1008 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
1009 &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
1010 connect(
1011 hotkey_registry.GetHotkey(main_window, QStringLiteral("Continue/Pause Emulation"), this),
1012 &QShortcut::activated, this, [&] {
1013 if (emulation_running) {
1014 if (emu_thread->IsRunning()) {
1015 OnPauseGame();
1016 } else {
1017 OnStartGame();
1018 }
1019 }
1020 });
1021 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Restart Emulation"), this),
1022 &QShortcut::activated, this, [this] {
1023 if (!system->IsPoweredOn()) {
1024 return;
1025 }
1026 BootGame(game_path);
1027 });
1028 connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window),
1029 &QShortcut::activated, ui->action_Fullscreen, &QAction::trigger);
1030 connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window),
1031 &QShortcut::activatedAmbiguously, ui->action_Fullscreen, &QAction::trigger);
1032 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Exit Fullscreen"), this),
1033 &QShortcut::activated, this, [&] {
1034 if (emulation_running && ui->action_Fullscreen->isChecked()) {
1035 ui->action_Fullscreen->setChecked(false);
1036 ToggleFullscreen();
1037 }
1038 });
1039 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
1040 &QShortcut::activated, this, [&] {
1041 Settings::values.use_speed_limit.SetValue(
1042 !Settings::values.use_speed_limit.GetValue());
1043 UpdateStatusBar();
1044 });
1045 constexpr u16 SPEED_LIMIT_STEP = 5; 1002 constexpr u16 SPEED_LIMIT_STEP = 5;
1046 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), 1003 connect_shortcut(QStringLiteral("Increase Speed Limit"), [&] {
1047 &QShortcut::activated, this, [&] { 1004 if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
1048 if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { 1005 Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP +
1049 Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP + 1006 Settings::values.speed_limit.GetValue());
1050 Settings::values.speed_limit.GetValue()); 1007 UpdateStatusBar();
1051 UpdateStatusBar(); 1008 }
1052 } 1009 });
1053 }); 1010 connect_shortcut(QStringLiteral("Decrease Speed Limit"), [&] {
1054 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), 1011 if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) {
1055 &QShortcut::activated, this, [&] { 1012 Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() -
1056 if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) { 1013 SPEED_LIMIT_STEP);
1057 Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() - 1014 UpdateStatusBar();
1058 SPEED_LIMIT_STEP); 1015 }
1059 UpdateStatusBar(); 1016 });
1060 } 1017 connect_shortcut(QStringLiteral("Change Docked Mode"), [&] {
1061 }); 1018 Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue());
1062 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load Amiibo"), this), 1019 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
1063 &QShortcut::activated, this, [&] { 1020 Settings::values.use_docked_mode.GetValue(), *system);
1064 if (ui->action_Load_Amiibo->isEnabled()) { 1021 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
1065 OnLoadAmiibo(); 1022 });
1066 } 1023 connect_shortcut(QStringLiteral("Mute Audio"),
1067 }); 1024 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1068 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), 1025 connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
1069 &QShortcut::activated, this, [&] { 1026 Settings::values.disable_fps_limit.SetValue(!Settings::values.disable_fps_limit.GetValue());
1070 if (emu_thread != nullptr && emu_thread->IsRunning()) { 1027 });
1071 OnCaptureScreenshot(); 1028 connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
1072 } 1029 Settings::values.mouse_panning = !Settings::values.mouse_panning;
1073 }); 1030 if (Settings::values.mouse_panning) {
1074 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), 1031 render_window->installEventFilter(render_window);
1075 &QShortcut::activated, this, [&] { 1032 render_window->setAttribute(Qt::WA_Hover, true);
1076 Settings::values.use_docked_mode.SetValue( 1033 }
1077 !Settings::values.use_docked_mode.GetValue()); 1034 });
1078 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
1079 Settings::values.use_docked_mode.GetValue(), *system);
1080 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
1081 });
1082 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
1083 &QShortcut::activated, this,
1084 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1085 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Framerate Limit"), this),
1086 &QShortcut::activated, this, [] {
1087 Settings::values.disable_fps_limit.SetValue(
1088 !Settings::values.disable_fps_limit.GetValue());
1089 });
1090 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
1091 &QShortcut::activated, this, [&] {
1092 Settings::values.mouse_panning = !Settings::values.mouse_panning;
1093 if (Settings::values.mouse_panning) {
1094 render_window->installEventFilter(render_window);
1095 render_window->setAttribute(Qt::WA_Hover, true);
1096 }
1097 });
1098 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this),
1099 &QShortcut::activated, this, [&] {
1100 if (!emulation_running) {
1101 return;
1102 }
1103 input_subsystem->GetTas()->StartStop();
1104 });
1105 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this),
1106 &QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); });
1107 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this),
1108 &QShortcut::activated, this, [&] {
1109 if (!emulation_running) {
1110 return;
1111 }
1112 bool is_recording = input_subsystem->GetTas()->Record();
1113 if (!is_recording) {
1114 const auto res = QMessageBox::question(this, tr("TAS Recording"),
1115 tr("Overwrite file of player 1?"),
1116 QMessageBox::Yes | QMessageBox::No);
1117 input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
1118 }
1119 });
1120} 1035}
1121 1036
1122void GMainWindow::SetDefaultUIGeometry() { 1037void GMainWindow::SetDefaultUIGeometry() {
@@ -1171,7 +1086,8 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
1171 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { 1086 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
1172 auto_paused = true; 1087 auto_paused = true;
1173 OnPauseGame(); 1088 OnPauseGame();
1174 } else if (ui->action_Start->isEnabled() && auto_paused && state == Qt::ApplicationActive) { 1089 } else if (emulation_running && !emu_thread->IsRunning() && auto_paused &&
1090 state == Qt::ApplicationActive) {
1175 auto_paused = false; 1091 auto_paused = false;
1176 OnStartGame(); 1092 OnStartGame();
1177 } 1093 }
@@ -1215,61 +1131,86 @@ void GMainWindow::ConnectWidgetEvents() {
1215} 1131}
1216 1132
1217void GMainWindow::ConnectMenuEvents() { 1133void GMainWindow::ConnectMenuEvents() {
1134 const auto connect_menu = [&]<typename Fn>(QAction* action, const Fn& event_fn) {
1135 connect(action, &QAction::triggered, this, event_fn);
1136 // Add actions to this window so that hiding menus in fullscreen won't disable them
1137 addAction(action);
1138 // Add actions to the render window so that they work outside of single window mode
1139 render_window->addAction(action);
1140 };
1141
1218 // File 1142 // File
1219 connect(ui->action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); 1143 connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
1220 connect(ui->action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); 1144 connect_menu(ui->action_Load_Folder, &GMainWindow::OnMenuLoadFolder);
1221 connect(ui->action_Install_File_NAND, &QAction::triggered, this, 1145 connect_menu(ui->action_Install_File_NAND, &GMainWindow::OnMenuInstallToNAND);
1222 &GMainWindow::OnMenuInstallToNAND); 1146 connect_menu(ui->action_Exit, &QMainWindow::close);
1223 connect(ui->action_Exit, &QAction::triggered, this, &QMainWindow::close); 1147 connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo);
1224 connect(ui->action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
1225 1148
1226 // Emulation 1149 // Emulation
1227 connect(ui->action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 1150 connect_menu(ui->action_Pause, &GMainWindow::OnPauseContinueGame);
1228 connect(ui->action_Pause, &QAction::triggered, this, &GMainWindow::OnPauseGame); 1151 connect_menu(ui->action_Stop, &GMainWindow::OnStopGame);
1229 connect(ui->action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); 1152 connect_menu(ui->action_Report_Compatibility, &GMainWindow::OnMenuReportCompatibility);
1230 connect(ui->action_Report_Compatibility, &QAction::triggered, this, 1153 connect_menu(ui->action_Open_Mods_Page, &GMainWindow::OnOpenModsPage);
1231 &GMainWindow::OnMenuReportCompatibility); 1154 connect_menu(ui->action_Open_Quickstart_Guide, &GMainWindow::OnOpenQuickstartGuide);
1232 connect(ui->action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); 1155 connect_menu(ui->action_Open_FAQ, &GMainWindow::OnOpenFAQ);
1233 connect(ui->action_Open_Quickstart_Guide, &QAction::triggered, this, 1156 connect_menu(ui->action_Restart, &GMainWindow::OnRestartGame);
1234 &GMainWindow::OnOpenQuickstartGuide); 1157 connect_menu(ui->action_Configure, &GMainWindow::OnConfigure);
1235 connect(ui->action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); 1158 connect_menu(ui->action_Configure_Current_Game, &GMainWindow::OnConfigurePerGame);
1236 connect(ui->action_Restart, &QAction::triggered, this,
1237 [this] { BootGame(QString(game_path)); });
1238 connect(ui->action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
1239 connect(ui->action_Configure_Tas, &QAction::triggered, this, &GMainWindow::OnConfigureTas);
1240 connect(ui->action_Configure_Current_Game, &QAction::triggered, this,
1241 &GMainWindow::OnConfigurePerGame);
1242 1159
1243 // View 1160 // View
1244 connect(ui->action_Single_Window_Mode, &QAction::triggered, this, 1161 connect_menu(ui->action_Fullscreen, &GMainWindow::ToggleFullscreen);
1245 &GMainWindow::ToggleWindowMode); 1162 connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode);
1246 connect(ui->action_Display_Dock_Widget_Headers, &QAction::triggered, this, 1163 connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars);
1247 &GMainWindow::OnDisplayTitleBars); 1164 connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
1248 connect(ui->action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); 1165
1249 connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 1166 connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
1250 1167
1251 connect(ui->action_Reset_Window_Size_720, &QAction::triggered, this, 1168 connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720);
1252 &GMainWindow::ResetWindowSize720); 1169 connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900);
1253 connect(ui->action_Reset_Window_Size_900, &QAction::triggered, this, 1170 connect_menu(ui->action_Reset_Window_Size_1080, &GMainWindow::ResetWindowSize1080);
1254 &GMainWindow::ResetWindowSize900); 1171 ui->menu_Reset_Window_Size->addActions({ui->action_Reset_Window_Size_720,
1255 connect(ui->action_Reset_Window_Size_1080, &QAction::triggered, this, 1172 ui->action_Reset_Window_Size_900,
1256 &GMainWindow::ResetWindowSize1080); 1173 ui->action_Reset_Window_Size_1080});
1257 ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_720);
1258 ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_900);
1259 ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_1080);
1260 1174
1261 // Fullscreen 1175 // Tools
1262 connect(ui->action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 1176 connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
1177 ReinitializeKeyBehavior::Warning));
1178 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
1263 1179
1264 // Movie 1180 // TAS
1265 connect(ui->action_Capture_Screenshot, &QAction::triggered, this, 1181 connect_menu(ui->action_TAS_Start, &GMainWindow::OnTasStartStop);
1266 &GMainWindow::OnCaptureScreenshot); 1182 connect_menu(ui->action_TAS_Record, &GMainWindow::OnTasRecord);
1183 connect_menu(ui->action_TAS_Reset, &GMainWindow::OnTasReset);
1184 connect_menu(ui->action_Configure_Tas, &GMainWindow::OnConfigureTas);
1267 1185
1268 // Help 1186 // Help
1269 connect(ui->action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); 1187 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
1270 connect(ui->action_Rederive, &QAction::triggered, this, 1188 connect_menu(ui->action_About, &GMainWindow::OnAbout);
1271 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); 1189}
1272 connect(ui->action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 1190
1191void GMainWindow::UpdateMenuState() {
1192 const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning();
1193
1194 const std::array running_actions{
1195 ui->action_Stop,
1196 ui->action_Restart,
1197 ui->action_Configure_Current_Game,
1198 ui->action_Report_Compatibility,
1199 ui->action_Load_Amiibo,
1200 ui->action_Pause,
1201 };
1202
1203 for (QAction* action : running_actions) {
1204 action->setEnabled(emulation_running);
1205 }
1206
1207 ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused);
1208
1209 if (emulation_running && is_paused) {
1210 ui->action_Pause->setText(tr("&Continue"));
1211 } else {
1212 ui->action_Pause->setText(tr("&Pause"));
1213 }
1273} 1214}
1274 1215
1275void GMainWindow::OnDisplayTitleBars(bool show) { 1216void GMainWindow::OnDisplayTitleBars(bool show) {
@@ -1439,8 +1380,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1439 Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig); 1380 Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig);
1440 } 1381 }
1441 1382
1442 ConfigureVibration::SetAllVibrationDevices();
1443
1444 // Disable fps limit toggle when booting a new title 1383 // Disable fps limit toggle when booting a new title
1445 Settings::values.disable_fps_limit.SetValue(false); 1384 Settings::values.disable_fps_limit.SetValue(false);
1446 1385
@@ -1562,15 +1501,8 @@ void GMainWindow::ShutdownGame() {
1562 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1501 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1563 1502
1564 // Update the GUI 1503 // Update the GUI
1565 ui->action_Start->setEnabled(false); 1504 UpdateMenuState();
1566 ui->action_Start->setText(tr("Start")); 1505
1567 ui->action_Pause->setEnabled(false);
1568 ui->action_Stop->setEnabled(false);
1569 ui->action_Restart->setEnabled(false);
1570 ui->action_Configure_Current_Game->setEnabled(false);
1571 ui->action_Report_Compatibility->setEnabled(false);
1572 ui->action_Load_Amiibo->setEnabled(false);
1573 ui->action_Capture_Screenshot->setEnabled(false);
1574 render_window->hide(); 1506 render_window->hide();
1575 loading_screen->hide(); 1507 loading_screen->hide();
1576 loading_screen->Clear(); 1508 loading_screen->Clear();
@@ -1582,6 +1514,7 @@ void GMainWindow::ShutdownGame() {
1582 game_list->SetFilterFocus(); 1514 game_list->SetFilterFocus();
1583 tas_label->clear(); 1515 tas_label->clear();
1584 input_subsystem->GetTas()->Stop(); 1516 input_subsystem->GetTas()->Stop();
1517 OnTasStateChanged();
1585 1518
1586 render_window->removeEventFilter(render_window); 1519 render_window->removeEventFilter(render_window);
1587 render_window->setAttribute(Qt::WA_Hover, false); 1520 render_window->setAttribute(Qt::WA_Hover, false);
@@ -2501,31 +2434,36 @@ void GMainWindow::OnStartGame() {
2501 2434
2502 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); 2435 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
2503 2436
2504 ui->action_Start->setEnabled(false); 2437 UpdateMenuState();
2505 ui->action_Start->setText(tr("&Continue")); 2438 OnTasStateChanged();
2506
2507 ui->action_Pause->setEnabled(true);
2508 ui->action_Stop->setEnabled(true);
2509 ui->action_Restart->setEnabled(true);
2510 ui->action_Configure_Current_Game->setEnabled(true);
2511 ui->action_Report_Compatibility->setEnabled(true);
2512 2439
2513 discord_rpc->Update(); 2440 discord_rpc->Update();
2514 ui->action_Load_Amiibo->setEnabled(true); 2441}
2515 ui->action_Capture_Screenshot->setEnabled(true); 2442
2443void GMainWindow::OnRestartGame() {
2444 if (!system->IsPoweredOn()) {
2445 return;
2446 }
2447 // Make a copy since BootGame edits game_path
2448 BootGame(QString(game_path));
2516} 2449}
2517 2450
2518void GMainWindow::OnPauseGame() { 2451void GMainWindow::OnPauseGame() {
2519 emu_thread->SetRunning(false); 2452 emu_thread->SetRunning(false);
2520 2453 UpdateMenuState();
2521 ui->action_Start->setEnabled(true);
2522 ui->action_Pause->setEnabled(false);
2523 ui->action_Stop->setEnabled(true);
2524 ui->action_Capture_Screenshot->setEnabled(false);
2525
2526 AllowOSSleep(); 2454 AllowOSSleep();
2527} 2455}
2528 2456
2457void GMainWindow::OnPauseContinueGame() {
2458 if (emulation_running) {
2459 if (emu_thread->IsRunning()) {
2460 OnPauseGame();
2461 } else {
2462 OnStartGame();
2463 }
2464 }
2465}
2466
2529void GMainWindow::OnStopGame() { 2467void GMainWindow::OnStopGame() {
2530 if (system->GetExitLock() && !ConfirmForceLockedExit()) { 2468 if (system->GetExitLock() && !ConfirmForceLockedExit()) {
2531 return; 2469 return;
@@ -2774,7 +2712,6 @@ void GMainWindow::OnConfigure() {
2774 2712
2775 ShowTelemetryCallout(); 2713 ShowTelemetryCallout();
2776 } 2714 }
2777 controller_dialog->refreshConfiguration();
2778 InitializeHotkeys(); 2715 InitializeHotkeys();
2779 2716
2780 if (UISettings::values.theme != old_theme) { 2717 if (UISettings::values.theme != old_theme) {
@@ -2807,6 +2744,7 @@ void GMainWindow::OnConfigure() {
2807 } 2744 }
2808 2745
2809 UpdateStatusButtons(); 2746 UpdateStatusButtons();
2747 controller_dialog->refreshConfiguration();
2810} 2748}
2811 2749
2812void GMainWindow::OnConfigureTas() { 2750void GMainWindow::OnConfigureTas() {
@@ -2821,6 +2759,32 @@ void GMainWindow::OnConfigureTas() {
2821 } 2759 }
2822} 2760}
2823 2761
2762void GMainWindow::OnTasStartStop() {
2763 if (!emulation_running) {
2764 return;
2765 }
2766 input_subsystem->GetTas()->StartStop();
2767 OnTasStateChanged();
2768}
2769
2770void GMainWindow::OnTasRecord() {
2771 if (!emulation_running) {
2772 return;
2773 }
2774 const bool is_recording = input_subsystem->GetTas()->Record();
2775 if (!is_recording) {
2776 const auto res =
2777 QMessageBox::question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
2778 QMessageBox::Yes | QMessageBox::No);
2779 input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
2780 }
2781 OnTasStateChanged();
2782}
2783
2784void GMainWindow::OnTasReset() {
2785 input_subsystem->GetTas()->Reset();
2786}
2787
2824void GMainWindow::OnConfigurePerGame() { 2788void GMainWindow::OnConfigurePerGame() {
2825 const u64 title_id = system->GetCurrentProcessProgramID(); 2789 const u64 title_id = system->GetCurrentProcessProgramID();
2826 OpenPerGameConfiguration(title_id, game_path.toStdString()); 2790 OpenPerGameConfiguration(title_id, game_path.toStdString());
@@ -2858,6 +2822,10 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
2858} 2822}
2859 2823
2860void GMainWindow::OnLoadAmiibo() { 2824void GMainWindow::OnLoadAmiibo() {
2825 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
2826 return;
2827 }
2828
2861 const QString extensions{QStringLiteral("*.bin")}; 2829 const QString extensions{QStringLiteral("*.bin")};
2862 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); 2830 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
2863 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); 2831 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter);
@@ -2921,6 +2889,10 @@ void GMainWindow::OnToggleFilterBar() {
2921} 2889}
2922 2890
2923void GMainWindow::OnCaptureScreenshot() { 2891void GMainWindow::OnCaptureScreenshot() {
2892 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
2893 return;
2894 }
2895
2924 const u64 title_id = system->GetCurrentProcessProgramID(); 2896 const u64 title_id = system->GetCurrentProcessProgramID();
2925 const auto screenshot_path = 2897 const auto screenshot_path =
2926 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); 2898 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
@@ -3003,17 +2975,35 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
3003QString GMainWindow::GetTasStateDescription() const { 2975QString GMainWindow::GetTasStateDescription() const {
3004 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus(); 2976 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
3005 switch (tas_status) { 2977 switch (tas_status) {
3006 case TasInput::TasState::Running: 2978 case InputCommon::TasInput::TasState::Running:
3007 return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames); 2979 return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames);
3008 case TasInput::TasState::Recording: 2980 case InputCommon::TasInput::TasState::Recording:
3009 return tr("TAS state: Recording %1").arg(total_tas_frames); 2981 return tr("TAS state: Recording %1").arg(total_tas_frames);
3010 case TasInput::TasState::Stopped: 2982 case InputCommon::TasInput::TasState::Stopped:
3011 return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames); 2983 return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames);
3012 default: 2984 default:
3013 return tr("TAS State: Invalid"); 2985 return tr("TAS State: Invalid");
3014 } 2986 }
3015} 2987}
3016 2988
2989void GMainWindow::OnTasStateChanged() {
2990 bool is_running = false;
2991 bool is_recording = false;
2992 if (emulation_running) {
2993 const InputCommon::TasInput::TasState tas_status =
2994 std::get<0>(input_subsystem->GetTas()->GetStatus());
2995 is_running = tas_status == InputCommon::TasInput::TasState::Running;
2996 is_recording = tas_status == InputCommon::TasInput::TasState::Recording;
2997 }
2998
2999 ui->action_TAS_Start->setText(is_running ? tr("&Stop Running") : tr("&Start"));
3000 ui->action_TAS_Record->setText(is_recording ? tr("Stop R&ecording") : tr("R&ecord"));
3001
3002 ui->action_TAS_Start->setEnabled(emulation_running);
3003 ui->action_TAS_Record->setEnabled(emulation_running);
3004 ui->action_TAS_Reset->setEnabled(emulation_running);
3005}
3006
3017void GMainWindow::UpdateStatusBar() { 3007void GMainWindow::UpdateStatusBar() {
3018 if (emu_thread == nullptr) { 3008 if (emu_thread == nullptr) {
3019 status_bar_update_timer.stop(); 3009 status_bar_update_timer.stop();
@@ -3386,6 +3376,8 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
3386 UpdateUISettings(); 3376 UpdateUISettings();
3387 game_list->SaveInterfaceLayout(); 3377 game_list->SaveInterfaceLayout();
3388 hotkey_registry.SaveHotkeys(); 3378 hotkey_registry.SaveHotkeys();
3379 controller_dialog->UnloadController();
3380 system->HIDCore().UnloadInputDevices();
3389 3381
3390 // Shutdown session if the emu thread is active... 3382 // Shutdown session if the emu thread is active...
3391 if (emu_thread != nullptr) { 3383 if (emu_thread != nullptr) {
@@ -3487,36 +3479,38 @@ void GMainWindow::filterBarSetChecked(bool state) {
3487} 3479}
3488 3480
3489void GMainWindow::UpdateUITheme() { 3481void GMainWindow::UpdateUITheme() {
3490 const QString default_icons = QStringLiteral("default"); 3482 const QString default_theme = QStringLiteral("default");
3491 const QString& current_theme = UISettings::values.theme; 3483 QString current_theme = UISettings::values.theme;
3492 const bool is_default_theme = current_theme == QString::fromUtf8(UISettings::themes[0].second);
3493 QStringList theme_paths(default_theme_paths); 3484 QStringList theme_paths(default_theme_paths);
3494 3485
3495 if (is_default_theme || current_theme.isEmpty()) { 3486 if (current_theme.isEmpty()) {
3496 const QString theme_uri(QStringLiteral(":default/style.qss")); 3487 current_theme = default_theme;
3488 }
3489
3490 if (current_theme != default_theme) {
3491 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
3497 QFile f(theme_uri); 3492 QFile f(theme_uri);
3498 if (f.open(QFile::ReadOnly | QFile::Text)) { 3493 if (!f.open(QFile::ReadOnly | QFile::Text)) {
3499 QTextStream ts(&f); 3494 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
3500 qApp->setStyleSheet(ts.readAll()); 3495 UISettings::values.theme.toStdString());
3501 setStyleSheet(ts.readAll()); 3496 current_theme = default_theme;
3502 } else {
3503 qApp->setStyleSheet({});
3504 setStyleSheet({});
3505 } 3497 }
3506 QIcon::setThemeName(default_icons); 3498 }
3499
3500 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
3501 QFile f(theme_uri);
3502 if (f.open(QFile::ReadOnly | QFile::Text)) {
3503 QTextStream ts(&f);
3504 qApp->setStyleSheet(ts.readAll());
3505 setStyleSheet(ts.readAll());
3507 } else { 3506 } else {
3508 const QString theme_uri(QLatin1Char{':'} + current_theme + QStringLiteral("/style.qss")); 3507 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
3509 QFile f(theme_uri); 3508 UISettings::values.theme.toStdString());
3510 if (f.open(QFile::ReadOnly | QFile::Text)) { 3509 qApp->setStyleSheet({});
3511 QTextStream ts(&f); 3510 setStyleSheet({});
3512 qApp->setStyleSheet(ts.readAll());
3513 setStyleSheet(ts.readAll());
3514 } else {
3515 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
3516 }
3517 QIcon::setThemeName(current_theme);
3518 } 3511 }
3519 3512
3513 QIcon::setThemeName(current_theme);
3520 QIcon::setThemeSearchPaths(theme_paths); 3514 QIcon::setThemeSearchPaths(theme_paths);
3521} 3515}
3522 3516
@@ -3552,9 +3546,6 @@ void GMainWindow::OnLanguageChanged(const QString& locale) {
3552 LoadTranslation(); 3546 LoadTranslation();
3553 ui->retranslateUi(this); 3547 ui->retranslateUi(this);
3554 UpdateWindowTitle(); 3548 UpdateWindowTitle();
3555
3556 if (emulation_running)
3557 ui->action_Start->setText(tr("&Continue"));
3558} 3549}
3559 3550
3560void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { 3551void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 24633ff2d..0fd41ed4f 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -177,6 +177,7 @@ public slots:
177 void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, 177 void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args,
178 bool is_local); 178 bool is_local);
179 void OnAppFocusStateChanged(Qt::ApplicationState state); 179 void OnAppFocusStateChanged(Qt::ApplicationState state);
180 void OnTasStateChanged();
180 181
181private: 182private:
182 void RegisterMetaTypes(); 183 void RegisterMetaTypes();
@@ -190,6 +191,7 @@ private:
190 191
191 void ConnectWidgetEvents(); 192 void ConnectWidgetEvents();
192 void ConnectMenuEvents(); 193 void ConnectMenuEvents();
194 void UpdateMenuState();
193 195
194 void PreventOSSleep(); 196 void PreventOSSleep();
195 void AllowOSSleep(); 197 void AllowOSSleep();
@@ -239,7 +241,9 @@ private:
239 241
240private slots: 242private slots:
241 void OnStartGame(); 243 void OnStartGame();
244 void OnRestartGame();
242 void OnPauseGame(); 245 void OnPauseGame();
246 void OnPauseContinueGame();
243 void OnStopGame(); 247 void OnStopGame();
244 void OnMenuReportCompatibility(); 248 void OnMenuReportCompatibility();
245 void OnOpenModsPage(); 249 void OnOpenModsPage();
@@ -268,6 +272,9 @@ private slots:
268 void OnMenuRecentFile(); 272 void OnMenuRecentFile();
269 void OnConfigure(); 273 void OnConfigure();
270 void OnConfigureTas(); 274 void OnConfigureTas();
275 void OnTasStartStop();
276 void OnTasRecord();
277 void OnTasReset();
271 void OnConfigurePerGame(); 278 void OnConfigurePerGame();
272 void OnLoadAmiibo(); 279 void OnLoadAmiibo();
273 void OnOpenYuzuFolder(); 280 void OnOpenYuzuFolder();
@@ -290,6 +297,9 @@ private slots:
290 void OnMouseActivity(); 297 void OnMouseActivity();
291 298
292private: 299private:
300 /// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry.
301 void LinkActionShortcut(QAction* action, const QString& action_name);
302
293 void RemoveBaseContent(u64 program_id, const QString& entry_type); 303 void RemoveBaseContent(u64 program_id, const QString& entry_type);
294 void RemoveUpdateContent(u64 program_id, const QString& entry_type); 304 void RemoveUpdateContent(u64 program_id, const QString& entry_type);
295 void RemoveAddOnContent(u64 program_id, const QString& entry_type); 305 void RemoveAddOnContent(u64 program_id, const QString& entry_type);
@@ -313,6 +323,7 @@ private:
313 void OpenURL(const QUrl& url); 323 void OpenURL(const QUrl& url);
314 void LoadTranslation(); 324 void LoadTranslation();
315 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 325 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
326
316 QString GetTasStateDescription() const; 327 QString GetTasStateDescription() const;
317 328
318 std::unique_ptr<Ui::MainWindow> ui; 329 std::unique_ptr<Ui::MainWindow> ui;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index a62e39a06..5719b2ee4 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -66,7 +66,6 @@
66 <property name="title"> 66 <property name="title">
67 <string>&amp;Emulation</string> 67 <string>&amp;Emulation</string>
68 </property> 68 </property>
69 <addaction name="action_Start"/>
70 <addaction name="action_Pause"/> 69 <addaction name="action_Pause"/>
71 <addaction name="action_Stop"/> 70 <addaction name="action_Stop"/>
72 <addaction name="action_Restart"/> 71 <addaction name="action_Restart"/>
@@ -79,39 +78,39 @@
79 <string>&amp;View</string> 78 <string>&amp;View</string>
80 </property> 79 </property>
81 <widget class="QMenu" name="menu_Reset_Window_Size"> 80 <widget class="QMenu" name="menu_Reset_Window_Size">
82 <property name="title"> 81 <property name="title">
83 <string>&amp;Reset Window Size</string> 82 <string>&amp;Reset Window Size</string>
84 </property> 83 </property>
84 </widget>
85 <widget class="QMenu" name="menu_View_Debugging">
86 <property name="title">
87 <string>&amp;Debugging</string>
88 </property>
85 </widget> 89 </widget>
86 <action name="action_Reset_Window_Size_720"> 90 <action name="action_Reset_Window_Size_720">
87 <property name="text"> 91 <property name="text">
88 <string>Reset Window Size to &amp;720p</string> 92 <string>Reset Window Size to &amp;720p</string>
89 </property> 93 </property>
90 <property name="iconText"> 94 <property name="iconText">
91 <string>Reset Window Size to 720p</string> 95 <string>Reset Window Size to 720p</string>
92 </property> 96 </property>
93 </action> 97 </action>
94 <action name="action_Reset_Window_Size_900"> 98 <action name="action_Reset_Window_Size_900">
95 <property name="text"> 99 <property name="text">
96 <string>Reset Window Size to &amp;900p</string> 100 <string>Reset Window Size to &amp;900p</string>
97 </property> 101 </property>
98 <property name="iconText"> 102 <property name="iconText">
99 <string>Reset Window Size to 900p</string> 103 <string>Reset Window Size to 900p</string>
100 </property> 104 </property>
101 </action> 105 </action>
102 <action name="action_Reset_Window_Size_1080"> 106 <action name="action_Reset_Window_Size_1080">
103 <property name="text"> 107 <property name="text">
104 <string>Reset Window Size to &amp;1080p</string> 108 <string>Reset Window Size to &amp;1080p</string>
105 </property>
106 <property name="iconText">
107 <string>Reset Window Size to 1080p</string>
108 </property>
109 </action>
110 <widget class="QMenu" name="menu_View_Debugging">
111 <property name="title">
112 <string>&amp;Debugging</string>
113 </property> 109 </property>
114 </widget> 110 <property name="iconText">
111 <string>Reset Window Size to 1080p</string>
112 </property>
113 </action>
115 <addaction name="action_Fullscreen"/> 114 <addaction name="action_Fullscreen"/>
116 <addaction name="action_Single_Window_Mode"/> 115 <addaction name="action_Single_Window_Mode"/>
117 <addaction name="action_Display_Dock_Widget_Headers"/> 116 <addaction name="action_Display_Dock_Widget_Headers"/>
@@ -125,10 +124,20 @@
125 <property name="title"> 124 <property name="title">
126 <string>&amp;Tools</string> 125 <string>&amp;Tools</string>
127 </property> 126 </property>
127 <widget class="QMenu" name="menuTAS">
128 <property name="title">
129 <string>&amp;TAS</string>
130 </property>
131 <addaction name="action_TAS_Start"/>
132 <addaction name="action_TAS_Record"/>
133 <addaction name="action_TAS_Reset"/>
134 <addaction name="separator"/>
135 <addaction name="action_Configure_Tas"/>
136 </widget>
128 <addaction name="action_Rederive"/> 137 <addaction name="action_Rederive"/>
129 <addaction name="separator"/> 138 <addaction name="separator"/>
130 <addaction name="action_Capture_Screenshot"/> 139 <addaction name="action_Capture_Screenshot"/>
131 <addaction name="action_Configure_Tas"/> 140 <addaction name="menuTAS"/>
132 </widget> 141 </widget>
133 <widget class="QMenu" name="menu_Help"> 142 <widget class="QMenu" name="menu_Help">
134 <property name="title"> 143 <property name="title">
@@ -170,14 +179,6 @@
170 <string>E&amp;xit</string> 179 <string>E&amp;xit</string>
171 </property> 180 </property>
172 </action> 181 </action>
173 <action name="action_Start">
174 <property name="enabled">
175 <bool>false</bool>
176 </property>
177 <property name="text">
178 <string>&amp;Start</string>
179 </property>
180 </action>
181 <action name="action_Pause"> 182 <action name="action_Pause">
182 <property name="enabled"> 183 <property name="enabled">
183 <bool>false</bool> 184 <bool>false</bool>
@@ -309,7 +310,7 @@
309 </action> 310 </action>
310 <action name="action_Configure_Tas"> 311 <action name="action_Configure_Tas">
311 <property name="text"> 312 <property name="text">
312 <string>Configure &amp;TAS...</string> 313 <string>&amp;Configure TAS...</string>
313 </property> 314 </property>
314 </action> 315 </action>
315 <action name="action_Configure_Current_Game"> 316 <action name="action_Configure_Current_Game">
@@ -320,6 +321,30 @@
320 <string>Configure C&amp;urrent Game...</string> 321 <string>Configure C&amp;urrent Game...</string>
321 </property> 322 </property>
322 </action> 323 </action>
324 <action name="action_TAS_Start">
325 <property name="enabled">
326 <bool>false</bool>
327 </property>
328 <property name="text">
329 <string>&amp;Start</string>
330 </property>
331 </action>
332 <action name="action_TAS_Reset">
333 <property name="enabled">
334 <bool>false</bool>
335 </property>
336 <property name="text">
337 <string>&amp;Reset</string>
338 </property>
339 </action>
340 <action name="action_TAS_Record">
341 <property name="enabled">
342 <bool>false</bool>
343 </property>
344 <property name="text">
345 <string>R&amp;ecord</string>
346 </property>
347 </action>
323 </widget> 348 </widget>
324 <resources> 349 <resources>
325 <include location="yuzu.qrc"/> 350 <include location="yuzu.qrc"/>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 37499fc85..21683576c 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -7,8 +7,8 @@
7namespace UISettings { 7namespace UISettings {
8 8
9const Themes themes{{ 9const Themes themes{{
10 {"Light", "default"}, 10 {"Default", "default"},
11 {"Light Colorful", "colorful"}, 11 {"Default Colorful", "colorful"},
12 {"Dark", "qdarkstyle"}, 12 {"Dark", "qdarkstyle"},
13 {"Dark Colorful", "colorful_dark"}, 13 {"Dark Colorful", "colorful_dark"},
14 {"Midnight Blue", "qdarkstyle_midnight_blue"}, 14 {"Midnight Blue", "qdarkstyle_midnight_blue"},
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index 95b148545..c66dfbdff 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -6,7 +6,8 @@
6#include <QScreen> 6#include <QScreen>
7 7
8#include "core/core.h" 8#include "core/core.h"
9#include "core/frontend/input_interpreter.h" 9#include "core/hid/hid_types.h"
10#include "core/hid/input_interpreter.h"
10#include "ui_overlay_dialog.h" 11#include "ui_overlay_dialog.h"
11#include "yuzu/util/overlay_dialog.h" 12#include "yuzu/util/overlay_dialog.h"
12 13
@@ -179,9 +180,9 @@ void OverlayDialog::MoveAndResizeWindow() {
179 QDialog::resize(width, height); 180 QDialog::resize(width, height);
180} 181}
181 182
182template <HIDButton... T> 183template <Core::HID::NpadButton... T>
183void OverlayDialog::HandleButtonPressedOnce() { 184void OverlayDialog::HandleButtonPressedOnce() {
184 const auto f = [this](HIDButton button) { 185 const auto f = [this](Core::HID::NpadButton button) {
185 if (input_interpreter->IsButtonPressedOnce(button)) { 186 if (input_interpreter->IsButtonPressedOnce(button)) {
186 TranslateButtonPress(button); 187 TranslateButtonPress(button);
187 } 188 }
@@ -190,7 +191,7 @@ void OverlayDialog::HandleButtonPressedOnce() {
190 (f(T), ...); 191 (f(T), ...);
191} 192}
192 193
193void OverlayDialog::TranslateButtonPress(HIDButton button) { 194void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) {
194 QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel; 195 QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel;
195 QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label; 196 QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label;
196 197
@@ -198,20 +199,20 @@ void OverlayDialog::TranslateButtonPress(HIDButton button) {
198 // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it 199 // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it
199 200
200 switch (button) { 201 switch (button) {
201 case HIDButton::A: 202 case Core::HID::NpadButton::A:
202 case HIDButton::B: 203 case Core::HID::NpadButton::B:
203 if (left_button->hasFocus()) { 204 if (left_button->hasFocus()) {
204 left_button->click(); 205 left_button->click();
205 } else if (right_button->hasFocus()) { 206 } else if (right_button->hasFocus()) {
206 right_button->click(); 207 right_button->click();
207 } 208 }
208 break; 209 break;
209 case HIDButton::DLeft: 210 case Core::HID::NpadButton::Left:
210 case HIDButton::LStickLeft: 211 case Core::HID::NpadButton::StickLLeft:
211 focusPreviousChild(); 212 focusPreviousChild();
212 break; 213 break;
213 case HIDButton::DRight: 214 case Core::HID::NpadButton::Right:
214 case HIDButton::LStickRight: 215 case Core::HID::NpadButton::StickLRight:
215 focusNextChild(); 216 focusNextChild();
216 break; 217 break;
217 default: 218 default:
@@ -241,8 +242,10 @@ void OverlayDialog::InputThread() {
241 while (input_thread_running) { 242 while (input_thread_running) {
242 input_interpreter->PollInput(); 243 input_interpreter->PollInput();
243 244
244 HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight, 245 HandleButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
245 HIDButton::LStickLeft, HIDButton::LStickRight>(); 246 Core::HID::NpadButton::Left, Core::HID::NpadButton::Right,
247 Core::HID::NpadButton::StickLLeft,
248 Core::HID::NpadButton::StickLRight>();
246 249
247 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 250 std::this_thread::sleep_for(std::chrono::milliseconds(50));
248 } 251 }
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index e8c388bd0..d8a140ff3 100644
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -13,14 +13,16 @@
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15 15
16enum class HIDButton : u8;
17
18class InputInterpreter; 16class InputInterpreter;
19 17
20namespace Core { 18namespace Core {
21class System; 19class System;
22} 20}
23 21
22namespace Core::HID {
23enum class NpadButton : u64;
24}
25
24namespace Ui { 26namespace Ui {
25class OverlayDialog; 27class OverlayDialog;
26} 28}
@@ -79,7 +81,7 @@ private:
79 * 81 *
80 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 82 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
81 */ 83 */
82 template <HIDButton... T> 84 template <Core::HID::NpadButton... T>
83 void HandleButtonPressedOnce(); 85 void HandleButtonPressedOnce();
84 86
85 /** 87 /**
@@ -87,7 +89,7 @@ private:
87 * 89 *
88 * @param button The button press to process. 90 * @param button The button press to process.
89 */ 91 */
90 void TranslateButtonPress(HIDButton button); 92 void TranslateButtonPress(Core::HID::NpadButton button);
91 93
92 void StartInputThread(); 94 void StartInputThread();
93 void StopInputThread(); 95 void StopInputThread();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 33241ea98..8e9c7d211 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -24,7 +24,6 @@
24#include "common/settings.h" 24#include "common/settings.h"
25#include "core/hle/service/acc/profile_manager.h" 25#include "core/hle/service/acc/profile_manager.h"
26#include "input_common/main.h" 26#include "input_common/main.h"
27#include "input_common/udp/client.h"
28#include "yuzu_cmd/config.h" 27#include "yuzu_cmd/config.h"
29#include "yuzu_cmd/default_ini.h" 28#include "yuzu_cmd/default_ini.h"
30 29
@@ -84,163 +83,6 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
84 }, 83 },
85}}; 84}};
86 85
87static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
88 SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
89 SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
90};
91
92static const std::array<int, 0x8A> keyboard_keys = {
93 0,
94 0,
95 0,
96 0,
97 SDL_SCANCODE_A,
98 SDL_SCANCODE_B,
99 SDL_SCANCODE_C,
100 SDL_SCANCODE_D,
101 SDL_SCANCODE_E,
102 SDL_SCANCODE_F,
103 SDL_SCANCODE_G,
104 SDL_SCANCODE_H,
105 SDL_SCANCODE_I,
106 SDL_SCANCODE_J,
107 SDL_SCANCODE_K,
108 SDL_SCANCODE_L,
109 SDL_SCANCODE_M,
110 SDL_SCANCODE_N,
111 SDL_SCANCODE_O,
112 SDL_SCANCODE_P,
113 SDL_SCANCODE_Q,
114 SDL_SCANCODE_R,
115 SDL_SCANCODE_S,
116 SDL_SCANCODE_T,
117 SDL_SCANCODE_U,
118 SDL_SCANCODE_V,
119 SDL_SCANCODE_W,
120 SDL_SCANCODE_X,
121 SDL_SCANCODE_Y,
122 SDL_SCANCODE_Z,
123 SDL_SCANCODE_1,
124 SDL_SCANCODE_2,
125 SDL_SCANCODE_3,
126 SDL_SCANCODE_4,
127 SDL_SCANCODE_5,
128 SDL_SCANCODE_6,
129 SDL_SCANCODE_7,
130 SDL_SCANCODE_8,
131 SDL_SCANCODE_9,
132 SDL_SCANCODE_0,
133 SDL_SCANCODE_RETURN,
134 SDL_SCANCODE_ESCAPE,
135 SDL_SCANCODE_BACKSPACE,
136 SDL_SCANCODE_TAB,
137 SDL_SCANCODE_SPACE,
138 SDL_SCANCODE_MINUS,
139 SDL_SCANCODE_EQUALS,
140 SDL_SCANCODE_LEFTBRACKET,
141 SDL_SCANCODE_RIGHTBRACKET,
142 SDL_SCANCODE_BACKSLASH,
143 0,
144 SDL_SCANCODE_SEMICOLON,
145 SDL_SCANCODE_APOSTROPHE,
146 SDL_SCANCODE_GRAVE,
147 SDL_SCANCODE_COMMA,
148 SDL_SCANCODE_PERIOD,
149 SDL_SCANCODE_SLASH,
150 SDL_SCANCODE_CAPSLOCK,
151
152 SDL_SCANCODE_F1,
153 SDL_SCANCODE_F2,
154 SDL_SCANCODE_F3,
155 SDL_SCANCODE_F4,
156 SDL_SCANCODE_F5,
157 SDL_SCANCODE_F6,
158 SDL_SCANCODE_F7,
159 SDL_SCANCODE_F8,
160 SDL_SCANCODE_F9,
161 SDL_SCANCODE_F10,
162 SDL_SCANCODE_F11,
163 SDL_SCANCODE_F12,
164
165 0,
166 SDL_SCANCODE_SCROLLLOCK,
167 SDL_SCANCODE_PAUSE,
168 SDL_SCANCODE_INSERT,
169 SDL_SCANCODE_HOME,
170 SDL_SCANCODE_PAGEUP,
171 SDL_SCANCODE_DELETE,
172 SDL_SCANCODE_END,
173 SDL_SCANCODE_PAGEDOWN,
174 SDL_SCANCODE_RIGHT,
175 SDL_SCANCODE_LEFT,
176 SDL_SCANCODE_DOWN,
177 SDL_SCANCODE_UP,
178
179 SDL_SCANCODE_NUMLOCKCLEAR,
180 SDL_SCANCODE_KP_DIVIDE,
181 SDL_SCANCODE_KP_MULTIPLY,
182 SDL_SCANCODE_KP_MINUS,
183 SDL_SCANCODE_KP_PLUS,
184 SDL_SCANCODE_KP_ENTER,
185 SDL_SCANCODE_KP_1,
186 SDL_SCANCODE_KP_2,
187 SDL_SCANCODE_KP_3,
188 SDL_SCANCODE_KP_4,
189 SDL_SCANCODE_KP_5,
190 SDL_SCANCODE_KP_6,
191 SDL_SCANCODE_KP_7,
192 SDL_SCANCODE_KP_8,
193 SDL_SCANCODE_KP_9,
194 SDL_SCANCODE_KP_0,
195 SDL_SCANCODE_KP_PERIOD,
196
197 0,
198 0,
199 SDL_SCANCODE_POWER,
200 SDL_SCANCODE_KP_EQUALS,
201
202 SDL_SCANCODE_F13,
203 SDL_SCANCODE_F14,
204 SDL_SCANCODE_F15,
205 SDL_SCANCODE_F16,
206 SDL_SCANCODE_F17,
207 SDL_SCANCODE_F18,
208 SDL_SCANCODE_F19,
209 SDL_SCANCODE_F20,
210 SDL_SCANCODE_F21,
211 SDL_SCANCODE_F22,
212 SDL_SCANCODE_F23,
213 SDL_SCANCODE_F24,
214
215 0,
216 SDL_SCANCODE_HELP,
217 SDL_SCANCODE_MENU,
218 0,
219 0,
220 0,
221 0,
222 0,
223 0,
224 0,
225 0,
226 0,
227 0,
228 0,
229 0,
230 SDL_SCANCODE_KP_COMMA,
231 SDL_SCANCODE_KP_LEFTPAREN,
232 SDL_SCANCODE_KP_RIGHTPAREN,
233 0,
234 0,
235 0,
236 0,
237};
238
239static const std::array<int, 8> keyboard_mods{
240 SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
241 SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
242};
243
244template <> 86template <>
245void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { 87void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) {
246 setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); 88 setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
@@ -284,16 +126,6 @@ void Config::ReadValues() {
284 } 126 }
285 127
286 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); 128 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
287 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
288 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
289 Settings::values.mouse_buttons[i] = sdl2_config->Get(
290 "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
291 default_param);
292 if (Settings::values.mouse_buttons[i].empty())
293 Settings::values.mouse_buttons[i] = default_param;
294 }
295
296 ReadSetting("ControlsGeneral", Settings::values.motion_device);
297 129
298 ReadSetting("ControlsGeneral", Settings::values.touch_device); 130 ReadSetting("ControlsGeneral", Settings::values.touch_device);
299 131
@@ -363,21 +195,11 @@ void Config::ReadValues() {
363 Settings::TouchFromButtonMap{"default", {}}); 195 Settings::TouchFromButtonMap{"default", {}});
364 num_touch_from_button_maps = 1; 196 num_touch_from_button_maps = 1;
365 } 197 }
366 ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button);
367 Settings::values.touch_from_button_map_index = std::clamp( 198 Settings::values.touch_from_button_map_index = std::clamp(
368 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); 199 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
369 200
370 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); 201 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
371 202
372 std::transform(keyboard_keys.begin(), keyboard_keys.end(),
373 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
374 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
375 Settings::values.keyboard_keys.begin() +
376 Settings::NativeKeyboard::LeftControlKey,
377 InputCommon::GenerateKeyboardParam);
378 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
379 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
380
381 // Data Storage 203 // Data Storage
382 ReadSetting("Data Storage", Settings::values.use_virtual_sd); 204 ReadSetting("Data Storage", Settings::values.use_virtual_sd);
383 FS::SetYuzuPath(FS::YuzuPath::NANDDir, 205 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index ecdc271a8..6d613bf7a 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -84,23 +84,10 @@ enable_accurate_vibrations=
84# 0: Disabled, 1 (default): Enabled 84# 0: Disabled, 1 (default): Enabled
85motion_enabled = 85motion_enabled =
86 86
87# for motion input, the following devices are available: 87# Defines the udp device's touch screen coordinate system for cemuhookudp devices
88# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: 88# - "min_x", "min_y", "max_x", "max_y"
89# - "update_period": update period in milliseconds (default to 100)
90# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
91# - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol
92motion_device=
93
94# for touch input, the following devices are available:
95# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required
96# - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol
97# - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system
98touch_device= 89touch_device=
99 90
100# Whether to enable or disable touch input from button
101# 0 (default): Disabled, 1: Enabled
102use_touch_from_button=
103
104# for mapping buttons to touch inputs. 91# for mapping buttons to touch inputs.
105#touch_from_button_map=1 92#touch_from_button_map=1
106#touch_from_button_maps_0_name=default 93#touch_from_button_maps_0_name=default
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 87fce0c23..57f807826 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -9,10 +9,10 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/perf_stats.h" 11#include "core/perf_stats.h"
12#include "input_common/keyboard.h" 12#include "input_common/drivers/keyboard.h"
13#include "input_common/drivers/mouse.h"
14#include "input_common/drivers/touch_screen.h"
13#include "input_common/main.h" 15#include "input_common/main.h"
14#include "input_common/mouse/mouse_input.h"
15#include "input_common/sdl/sdl.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
17#include "yuzu_cmd/yuzu_icon.h" 17#include "yuzu_cmd/yuzu_icon.h"
18 18
@@ -32,42 +32,32 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
32} 32}
33 33
34void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 34void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
35 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); 35 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
36
37 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
38} 36}
39 37
40MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { 38InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
41 switch (button) { 39 switch (button) {
42 case SDL_BUTTON_LEFT: 40 case SDL_BUTTON_LEFT:
43 return MouseInput::MouseButton::Left; 41 return InputCommon::MouseButton::Left;
44 case SDL_BUTTON_RIGHT: 42 case SDL_BUTTON_RIGHT:
45 return MouseInput::MouseButton::Right; 43 return InputCommon::MouseButton::Right;
46 case SDL_BUTTON_MIDDLE: 44 case SDL_BUTTON_MIDDLE:
47 return MouseInput::MouseButton::Wheel; 45 return InputCommon::MouseButton::Wheel;
48 case SDL_BUTTON_X1: 46 case SDL_BUTTON_X1:
49 return MouseInput::MouseButton::Backward; 47 return InputCommon::MouseButton::Backward;
50 case SDL_BUTTON_X2: 48 case SDL_BUTTON_X2:
51 return MouseInput::MouseButton::Forward; 49 return InputCommon::MouseButton::Forward;
52 default: 50 default:
53 return MouseInput::MouseButton::Undefined; 51 return InputCommon::MouseButton::Undefined;
54 } 52 }
55} 53}
56 54
57void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 55void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
58 const auto mouse_button = SDLButtonToMouseButton(button); 56 const auto mouse_button = SDLButtonToMouseButton(button);
59 if (button == SDL_BUTTON_LEFT) { 57 if (state == SDL_PRESSED) {
60 if (state == SDL_PRESSED) { 58 input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button);
61 TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
62 } else {
63 TouchReleased(0);
64 }
65 } else { 59 } else {
66 if (state == SDL_PRESSED) { 60 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
67 input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
68 } else {
69 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
70 }
71 } 61 }
72} 62}
73 63
@@ -82,29 +72,35 @@ std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, flo
82 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; 72 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
83} 73}
84 74
85void EmuWindow_SDL2::OnFingerDown(float x, float y) { 75void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
86 // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind 76 int width, height;
87 // This isn't critical because the best we can do when we have that is to average them, like the 77 SDL_GetWindowSize(render_window, &width, &height);
88 // 3DS does
89
90 const auto [px, py] = TouchToPixelPos(x, y); 78 const auto [px, py] = TouchToPixelPos(x, y);
91 TouchPressed(px, py, 0); 79 const float fx = px * 1.0f / width;
80 const float fy = py * 1.0f / height;
81
82 input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
92} 83}
93 84
94void EmuWindow_SDL2::OnFingerMotion(float x, float y) { 85void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
86 int width, height;
87 SDL_GetWindowSize(render_window, &width, &height);
95 const auto [px, py] = TouchToPixelPos(x, y); 88 const auto [px, py] = TouchToPixelPos(x, y);
96 TouchMoved(px, py, 0); 89 const float fx = px * 1.0f / width;
90 const float fy = py * 1.0f / height;
91
92 input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
97} 93}
98 94
99void EmuWindow_SDL2::OnFingerUp() { 95void EmuWindow_SDL2::OnFingerUp() {
100 TouchReleased(0); 96 input_subsystem->GetTouchScreen()->TouchReleased(0);
101} 97}
102 98
103void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 99void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
104 if (state == SDL_PRESSED) { 100 if (state == SDL_PRESSED) {
105 input_subsystem->GetKeyboard()->PressKey(key); 101 input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
106 } else if (state == SDL_RELEASED) { 102 } else if (state == SDL_RELEASED) {
107 input_subsystem->GetKeyboard()->ReleaseKey(key); 103 input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key));
108 } 104 }
109} 105}
110 106
@@ -205,10 +201,12 @@ void EmuWindow_SDL2::WaitEvent() {
205 } 201 }
206 break; 202 break;
207 case SDL_FINGERDOWN: 203 case SDL_FINGERDOWN:
208 OnFingerDown(event.tfinger.x, event.tfinger.y); 204 OnFingerDown(event.tfinger.x, event.tfinger.y,
205 static_cast<std::size_t>(event.tfinger.touchId));
209 break; 206 break;
210 case SDL_FINGERMOTION: 207 case SDL_FINGERMOTION:
211 OnFingerMotion(event.tfinger.x, event.tfinger.y); 208 OnFingerMotion(event.tfinger.x, event.tfinger.y,
209 static_cast<std::size_t>(event.tfinger.touchId));
212 break; 210 break;
213 case SDL_FINGERUP: 211 case SDL_FINGERUP:
214 OnFingerUp(); 212 OnFingerUp();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 4810f8775..0af002693 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -16,11 +16,8 @@ class System;
16 16
17namespace InputCommon { 17namespace InputCommon {
18class InputSubsystem; 18class InputSubsystem;
19}
20
21namespace MouseInput {
22enum class MouseButton; 19enum class MouseButton;
23} 20} // namespace InputCommon
24 21
25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 22class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
26public: 23public:
@@ -47,7 +44,7 @@ protected:
47 void OnMouseMotion(s32 x, s32 y); 44 void OnMouseMotion(s32 x, s32 y);
48 45
49 /// Converts a SDL mouse button into MouseInput mouse button 46 /// Converts a SDL mouse button into MouseInput mouse button
50 MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const; 47 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
51 48
52 /// Called by WaitEvent when a mouse button is pressed or released 49 /// Called by WaitEvent when a mouse button is pressed or released
53 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 50 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
@@ -56,10 +53,10 @@ protected:
56 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; 53 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
57 54
58 /// Called by WaitEvent when a finger starts touching the touchscreen 55 /// Called by WaitEvent when a finger starts touching the touchscreen
59 void OnFingerDown(float x, float y); 56 void OnFingerDown(float x, float y, std::size_t id);
60 57
61 /// Called by WaitEvent when a finger moves while touching the touchscreen 58 /// Called by WaitEvent when a finger moves while touching the touchscreen
62 void OnFingerMotion(float x, float y); 59 void OnFingerMotion(float x, float y, std::size_t id);
63 60
64 /// Called by WaitEvent when a finger stops touching the touchscreen 61 /// Called by WaitEvent when a finger stops touching the touchscreen
65 void OnFingerUp(); 62 void OnFingerUp();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index a075ad08a..70db865ec 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -17,7 +17,6 @@
17#include "common/settings.h" 17#include "common/settings.h"
18#include "common/string_util.h" 18#include "common/string_util.h"
19#include "core/core.h" 19#include "core/core.h"
20#include "input_common/keyboard.h"
21#include "input_common/main.h" 20#include "input_common/main.h"
22#include "video_core/renderer_base.h" 21#include "video_core/renderer_base.h"
23#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 22#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"