summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/param_package.h2
-rw-r--r--src/common/thread.cpp12
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp6
-rw-r--r--src/core/arm/cpu_interrupt_handler.h3
-rw-r--r--src/core/cpu_manager.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp235
-rw-r--r--src/core/hle/service/hid/controllers/npad.h11
-rw-r--r--src/core/hle/service/hid/hid.cpp14
-rw-r--r--src/core/settings.cpp50
-rw-r--r--src/core/settings.h339
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp4
-rw-r--r--src/input_common/main.cpp201
-rw-r--r--src/input_common/main.h129
-rw-r--r--src/input_common/sdl/sdl.h19
-rw-r--r--src/input_common/sdl/sdl_impl.cpp413
-rw-r--r--src/input_common/sdl/sdl_impl.h8
-rw-r--r--src/input_common/settings.cpp33
-rw-r--r--src/input_common/settings.h335
-rw-r--r--src/input_common/udp/udp.cpp14
-rw-r--r--src/input_common/udp/udp.h7
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt43
-rw-r--r--src/video_core/host_shaders/StringShaderHeader.cmake11
-rw-r--r--src/video_core/host_shaders/opengl_present.frag10
-rw-r--r--src/video_core/host_shaders/opengl_present.vert24
-rw-r--r--src/video_core/host_shaders/source_shader.h.in9
-rw-r--r--src/video_core/memory_manager.h34
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp46
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h16
-rw-r--r--src/video_core/shader/decode/memory.cpp3
-rw-r--r--src/yuzu/CMakeLists.txt9
-rw-r--r--src/yuzu/bootmanager.cpp25
-rw-r--r--src/yuzu/bootmanager.h9
-rw-r--r--src/yuzu/configuration/config.cpp68
-rw-r--r--src/yuzu/configuration/config.h4
-rw-r--r--src/yuzu/configuration/configure.ui59
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp40
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h38
-rw-r--r--src/yuzu/configuration/configure_debug_controller.ui97
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp13
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-rw-r--r--src/yuzu/configuration/configure_input.cpp272
-rw-r--r--src/yuzu/configuration/configure_input.h31
-rw-r--r--src/yuzu/configuration/configure_input.ui1039
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp169
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h45
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui2688
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp786
-rw-r--r--src/yuzu/configuration/configure_input_player.h114
-rw-r--r--src/yuzu/configuration/configure_input_player.ui4019
-rw-r--r--src/yuzu/configuration/configure_input_simple.cpp152
-rw-r--r--src/yuzu/configuration/configure_input_simple.h43
-rw-r--r--src/yuzu/configuration/configure_input_simple.ui97
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp56
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h15
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui252
-rw-r--r--src/yuzu/main.cpp11
-rw-r--r--src/yuzu/main.h17
-rw-r--r--src/yuzu/uisettings.cpp1
-rw-r--r--src/yuzu/uisettings.h4
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp18
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp9
-rw-r--r--src/yuzu_tester/config.cpp1
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp5
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h6
81 files changed, 8906 insertions, 3445 deletions
diff --git a/src/common/assert.h b/src/common/assert.h
index 5b67c5c52..06d7b5612 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -17,11 +17,12 @@
17// enough for our purposes. 17// enough for our purposes.
18template <typename Fn> 18template <typename Fn>
19#if defined(_MSC_VER) 19#if defined(_MSC_VER)
20__declspec(noinline, noreturn) 20[[msvc::noinline, noreturn]]
21#elif defined(__GNUC__) 21#elif defined(__GNUC__)
22 __attribute__((noinline, noreturn, cold)) 22[[gnu::cold, gnu::noinline, noreturn]]
23#endif 23#endif
24 static void assert_noinline_call(const Fn& fn) { 24static void
25assert_noinline_call(const Fn& fn) {
25 fn(); 26 fn();
26 Crash(); 27 Crash();
27 exit(1); // Keeps GCC's mouth shut about this actually returning 28 exit(1); // Keeps GCC's mouth shut about this actually returning
diff --git a/src/common/param_package.h b/src/common/param_package.h
index c8a70bfa9..c13e45479 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,7 +19,7 @@ public:
19 explicit ParamPackage(const std::string& serialized); 19 explicit ParamPackage(const std::string& serialized);
20 ParamPackage(std::initializer_list<DataType::value_type> list); 20 ParamPackage(std::initializer_list<DataType::value_type> list);
21 ParamPackage(const ParamPackage& other) = default; 21 ParamPackage(const ParamPackage& other) = default;
22 ParamPackage(ParamPackage&& other) = default; 22 ParamPackage(ParamPackage&& other) noexcept = default;
23 23
24 ParamPackage& operator=(const ParamPackage& other) = default; 24 ParamPackage& operator=(const ParamPackage& other) = default;
25 ParamPackage& operator=(ParamPackage&& other) = default; 25 ParamPackage& operator=(ParamPackage&& other) = default;
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 8e5935e6a..d2c1ac60d 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.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 "common/common_funcs.h"
6#include "common/logging/log.h"
5#include "common/thread.h" 7#include "common/thread.h"
6#ifdef __APPLE__ 8#ifdef __APPLE__
7#include <mach/mach.h> 9#include <mach/mach.h>
@@ -19,6 +21,8 @@
19#include <unistd.h> 21#include <unistd.h>
20#endif 22#endif
21 23
24#include <string>
25
22#ifdef __FreeBSD__ 26#ifdef __FreeBSD__
23#define cpu_set_t cpuset_t 27#define cpu_set_t cpuset_t
24#endif 28#endif
@@ -110,6 +114,14 @@ void SetCurrentThreadName(const char* name) {
110 pthread_set_name_np(pthread_self(), name); 114 pthread_set_name_np(pthread_self(), name);
111#elif defined(__NetBSD__) 115#elif defined(__NetBSD__)
112 pthread_setname_np(pthread_self(), "%s", (void*)name); 116 pthread_setname_np(pthread_self(), "%s", (void*)name);
117#elif defined(__linux__)
118 // Linux limits thread names to 15 characters and will outright reject any
119 // attempt to set a longer name with ERANGE.
120 std::string truncated(name, std::min(strlen(name), static_cast<size_t>(15)));
121 if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) {
122 errno = e;
123 LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg());
124 }
113#else 125#else
114 pthread_setname_np(pthread_self(), name); 126 pthread_setname_np(pthread_self(), name);
115#endif 127#endif
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
7 7
8namespace Core { 8namespace Core {
9 9
10CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { 10CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
11 interrupt_event = std::make_unique<Common::Event>();
12}
13 11
14CPUInterruptHandler::~CPUInterruptHandler() = default; 12CPUInterruptHandler::~CPUInterruptHandler() = default;
15 13
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
17 if (is_interrupted_) { 15 if (is_interrupted_) {
18 interrupt_event->Set(); 16 interrupt_event->Set();
19 } 17 }
20 this->is_interrupted = is_interrupted_; 18 is_interrupted = is_interrupted_;
21} 19}
22 20
23void CPUInterruptHandler::AwaitInterrupt() { 21void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..71e582f79 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8 9
9namespace Common { 10namespace Common {
@@ -32,8 +33,8 @@ public:
32 void AwaitInterrupt(); 33 void AwaitInterrupt();
33 34
34private: 35private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event; 36 std::unique_ptr<Common::Event> interrupt_event;
37 std::atomic_bool is_interrupted{false};
37}; 38};
38 39
39} // namespace Core 40} // namespace Core
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index ef0bae556..688b99eba 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -328,7 +328,7 @@ void CpuManager::RunThread(std::size_t core) {
328 system.RegisterCoreThread(core); 328 system.RegisterCoreThread(core);
329 std::string name; 329 std::string name;
330 if (is_multicore) { 330 if (is_multicore) {
331 name = "yuzu:CoreCPUThread_" + std::to_string(core); 331 name = "yuzu:CPUCore_" + std::to_string(core);
332 } else { 332 } else {
333 name = "yuzu:CPUThread"; 333 name = "yuzu:CPUThread";
334 } 334 }
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 0e7794dc7..45fde8df2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr std::size_t NPAD_OFFSET = 0x9A00; 24constexpr std::size_t NPAD_OFFSET = 0x9A00;
25constexpr u32 BATTERY_FULL = 2; 25constexpr u32 BATTERY_FULL = 2;
26constexpr u32 MAX_NPAD_ID = 7; 26constexpr u32 MAX_NPAD_ID = 7;
27constexpr std::size_t HANDHELD_INDEX = 8;
27constexpr std::array<u32, 10> npad_id_list{ 28constexpr std::array<u32, 10> npad_id_list{
28 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 29 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
29}; 30};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
33 Joystick_Right, 34 Joystick_Right,
34}; 35};
35 36
36static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { 37Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
38 Settings::ControllerType type) {
37 switch (type) { 39 switch (type) {
38 case Settings::ControllerType::ProController: 40 case Settings::ControllerType::ProController:
39 return Controller_NPad::NPadControllerType::ProController; 41 return NPadControllerType::ProController;
40 case Settings::ControllerType::DualJoycon: 42 case Settings::ControllerType::DualJoyconDetached:
41 return Controller_NPad::NPadControllerType::JoyDual; 43 return NPadControllerType::JoyDual;
42 case Settings::ControllerType::LeftJoycon: 44 case Settings::ControllerType::LeftJoycon:
43 return Controller_NPad::NPadControllerType::JoyLeft; 45 return NPadControllerType::JoyLeft;
44 case Settings::ControllerType::RightJoycon: 46 case Settings::ControllerType::RightJoycon:
45 return Controller_NPad::NPadControllerType::JoyRight; 47 return NPadControllerType::JoyRight;
48 case Settings::ControllerType::Handheld:
49 return NPadControllerType::Handheld;
46 default: 50 default:
47 UNREACHABLE(); 51 UNREACHABLE();
48 return Controller_NPad::NPadControllerType::JoyDual; 52 return NPadControllerType::ProController;
53 }
54}
55
56Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
57 Controller_NPad::NPadControllerType type) {
58 switch (type) {
59 case NPadControllerType::ProController:
60 return Settings::ControllerType::ProController;
61 case NPadControllerType::JoyDual:
62 return Settings::ControllerType::DualJoyconDetached;
63 case NPadControllerType::JoyLeft:
64 return Settings::ControllerType::LeftJoycon;
65 case NPadControllerType::JoyRight:
66 return Settings::ControllerType::RightJoycon;
67 case NPadControllerType::Handheld:
68 return Settings::ControllerType::Handheld;
69 default:
70 UNREACHABLE();
71 return Settings::ControllerType::ProController;
49 } 72 }
50} 73}
51 74
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
60 case 6: 83 case 6:
61 case 7: 84 case 7:
62 return npad_id; 85 return npad_id;
63 case 8: 86 case HANDHELD_INDEX:
64 case NPAD_HANDHELD: 87 case NPAD_HANDHELD:
65 return 8; 88 return HANDHELD_INDEX;
66 case 9: 89 case 9:
67 case NPAD_UNKNOWN: 90 case NPAD_UNKNOWN:
68 return 9; 91 return 9;
@@ -83,7 +106,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
83 case 6: 106 case 6:
84 case 7: 107 case 7:
85 return static_cast<u32>(index); 108 return static_cast<u32>(index);
86 case 8: 109 case HANDHELD_INDEX:
87 return NPAD_HANDHELD; 110 return NPAD_HANDHELD;
88 case 9: 111 case 9:
89 return NPAD_UNKNOWN; 112 return NPAD_UNKNOWN;
@@ -96,25 +119,35 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 120Controller_NPad::~Controller_NPad() = default;
98 121
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 122void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
100 const auto controller_type = connected_controllers[controller_idx].type; 123 const auto controller_type = connected_controllers[controller_idx].type;
101 auto& controller = shared_memory_entries[controller_idx]; 124 auto& controller = shared_memory_entries[controller_idx];
102 if (controller_type == NPadControllerType::None) { 125 if (controller_type == NPadControllerType::None) {
126 styleset_changed_events[controller_idx].writable->Signal();
103 return; 127 return;
104 } 128 }
105 controller.joy_styles.raw = 0; // Zero out 129 controller.joy_styles.raw = 0; // Zero out
106 controller.device_type.raw = 0; 130 controller.device_type.raw = 0;
131 controller.properties.raw = 0;
107 switch (controller_type) { 132 switch (controller_type) {
108 case NPadControllerType::None: 133 case NPadControllerType::None:
109 UNREACHABLE(); 134 UNREACHABLE();
110 break; 135 break;
136 case NPadControllerType::ProController:
137 controller.joy_styles.pro_controller.Assign(1);
138 controller.device_type.pro_controller.Assign(1);
139 controller.properties.is_vertical.Assign(1);
140 controller.properties.use_plus.Assign(1);
141 controller.properties.use_minus.Assign(1);
142 controller.pad_assignment = NPadAssignments::Single;
143 break;
111 case NPadControllerType::Handheld: 144 case NPadControllerType::Handheld:
112 controller.joy_styles.handheld.Assign(1); 145 controller.joy_styles.handheld.Assign(1);
113 controller.device_type.handheld.Assign(1); 146 controller.device_type.handheld.Assign(1);
114 controller.pad_assignment = NPadAssignments::Dual;
115 controller.properties.is_vertical.Assign(1); 147 controller.properties.is_vertical.Assign(1);
116 controller.properties.use_plus.Assign(1); 148 controller.properties.use_plus.Assign(1);
117 controller.properties.use_minus.Assign(1); 149 controller.properties.use_minus.Assign(1);
150 controller.pad_assignment = NPadAssignments::Dual;
118 break; 151 break;
119 case NPadControllerType::JoyDual: 152 case NPadControllerType::JoyDual:
120 controller.joy_styles.joycon_dual.Assign(1); 153 controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
144 controller.device_type.pokeball.Assign(1); 177 controller.device_type.pokeball.Assign(1);
145 controller.pad_assignment = NPadAssignments::Single; 178 controller.pad_assignment = NPadAssignments::Single;
146 break; 179 break;
147 case NPadControllerType::ProController:
148 controller.joy_styles.pro_controller.Assign(1);
149 controller.device_type.pro_controller.Assign(1);
150 controller.properties.is_vertical.Assign(1);
151 controller.properties.use_plus.Assign(1);
152 controller.properties.use_minus.Assign(1);
153 controller.pad_assignment = NPadAssignments::Single;
154 break;
155 } 180 }
156 181
157 controller.single_color_error = ColorReadError::ReadOk; 182 controller.single_color_error = ColorReadError::ReadOk;
@@ -192,36 +217,25 @@ void Controller_NPad::OnInit() {
192 style.pokeball.Assign(1); 217 style.pokeball.Assign(1);
193 } 218 }
194 219
195 std::transform( 220 std::transform(Settings::values.players.begin(), Settings::values.players.end(),
196 Settings::values.players.begin(), Settings::values.players.end(), 221 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
197 connected_controllers.begin(), [](const Settings::PlayerInput& player) { 222 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
198 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; 223 player.connected};
199 }); 224 });
200
201 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
202 [](const ControllerHolder& holder) { return holder.is_connected; });
203 225
204 // Account for handheld 226 // Account for handheld
205 if (connected_controllers[8].is_connected) 227 if (connected_controllers[HANDHELD_INDEX].is_connected) {
206 connected_controllers[8].type = NPadControllerType::Handheld; 228 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
229 }
207 230
208 supported_npad_id_types.resize(npad_id_list.size()); 231 supported_npad_id_types.resize(npad_id_list.size());
209 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 232 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
210 npad_id_list.size() * sizeof(u32)); 233 npad_id_list.size() * sizeof(u32));
211 234
212 // Add a default dual joycon controller if none are present.
213 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
214 [](const ControllerHolder& controller) { return controller.is_connected; })) {
215 supported_npad_id_types.resize(npad_id_list.size());
216 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
217 npad_id_list.size() * sizeof(u32));
218 AddNewController(NPadControllerType::JoyDual);
219 }
220
221 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 235 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
222 const auto& controller = connected_controllers[i]; 236 const auto& controller = connected_controllers[i];
223 if (controller.is_connected) { 237 if (controller.is_connected) {
224 AddNewControllerAt(controller.type, IndexToNPad(i)); 238 AddNewControllerAt(controller.type, i);
225 } 239 }
226 } 240 }
227} 241}
@@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
309 323
310void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 324void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
311 std::size_t data_len) { 325 std::size_t data_len) {
312 if (!IsControllerActivated()) 326 if (!IsControllerActivated()) {
313 return; 327 return;
328 }
314 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 329 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
315 auto& npad = shared_memory_entries[i]; 330 auto& npad = shared_memory_entries[i];
316 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, 331 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -360,13 +375,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 375 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
361 376
362 libnx_entry.connection_status.raw = 0; 377 libnx_entry.connection_status.raw = 0;
378 libnx_entry.connection_status.IsConnected.Assign(1);
363 379
364 switch (controller_type) { 380 switch (controller_type) {
365 case NPadControllerType::None: 381 case NPadControllerType::None:
366 UNREACHABLE(); 382 UNREACHABLE();
367 break; 383 break;
384 case NPadControllerType::ProController:
385 main_controller.connection_status.raw = 0;
386 main_controller.connection_status.IsConnected.Assign(1);
387 main_controller.connection_status.IsWired.Assign(1);
388 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
389 main_controller.pad.l_stick = pad_state.l_stick;
390 main_controller.pad.r_stick = pad_state.r_stick;
391
392 libnx_entry.connection_status.IsWired.Assign(1);
393 break;
368 case NPadControllerType::Handheld: 394 case NPadControllerType::Handheld:
369 handheld_entry.connection_status.raw = 0; 395 handheld_entry.connection_status.raw = 0;
396 handheld_entry.connection_status.IsConnected.Assign(1);
370 handheld_entry.connection_status.IsWired.Assign(1); 397 handheld_entry.connection_status.IsWired.Assign(1);
371 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); 398 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
372 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 399 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +402,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
375 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 402 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
376 handheld_entry.pad.l_stick = pad_state.l_stick; 403 handheld_entry.pad.l_stick = pad_state.l_stick;
377 handheld_entry.pad.r_stick = pad_state.r_stick; 404 handheld_entry.pad.r_stick = pad_state.r_stick;
405
406 libnx_entry.connection_status.IsWired.Assign(1);
407 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
410 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
378 break; 411 break;
379 case NPadControllerType::JoyDual: 412 case NPadControllerType::JoyDual:
380 dual_entry.connection_status.raw = 0; 413 dual_entry.connection_status.raw = 0;
381 414 dual_entry.connection_status.IsConnected.Assign(1);
382 dual_entry.connection_status.IsLeftJoyConnected.Assign(1); 415 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
383 dual_entry.connection_status.IsRightJoyConnected.Assign(1); 416 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
384 dual_entry.connection_status.IsConnected.Assign(1);
385
386 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
387 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
388 libnx_entry.connection_status.IsConnected.Assign(1);
389
390 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 417 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
391 dual_entry.pad.l_stick = pad_state.l_stick; 418 dual_entry.pad.l_stick = pad_state.l_stick;
392 dual_entry.pad.r_stick = pad_state.r_stick; 419 dual_entry.pad.r_stick = pad_state.r_stick;
420
421 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
422 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
393 break; 423 break;
394 case NPadControllerType::JoyLeft: 424 case NPadControllerType::JoyLeft:
395 left_entry.connection_status.raw = 0; 425 left_entry.connection_status.raw = 0;
396
397 left_entry.connection_status.IsConnected.Assign(1); 426 left_entry.connection_status.IsConnected.Assign(1);
427 left_entry.connection_status.IsLeftJoyConnected.Assign(1);
398 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 428 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
399 left_entry.pad.l_stick = pad_state.l_stick; 429 left_entry.pad.l_stick = pad_state.l_stick;
400 left_entry.pad.r_stick = pad_state.r_stick; 430 left_entry.pad.r_stick = pad_state.r_stick;
431
432 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
401 break; 433 break;
402 case NPadControllerType::JoyRight: 434 case NPadControllerType::JoyRight:
403 right_entry.connection_status.raw = 0; 435 right_entry.connection_status.raw = 0;
404
405 right_entry.connection_status.IsConnected.Assign(1); 436 right_entry.connection_status.IsConnected.Assign(1);
437 right_entry.connection_status.IsRightJoyConnected.Assign(1);
406 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 438 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
407 right_entry.pad.l_stick = pad_state.l_stick; 439 right_entry.pad.l_stick = pad_state.l_stick;
408 right_entry.pad.r_stick = pad_state.r_stick; 440 right_entry.pad.r_stick = pad_state.r_stick;
441
442 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 break; 443 break;
410 case NPadControllerType::Pokeball: 444 case NPadControllerType::Pokeball:
411 pokeball_entry.connection_status.raw = 0; 445 pokeball_entry.connection_status.raw = 0;
412
413 pokeball_entry.connection_status.IsConnected.Assign(1); 446 pokeball_entry.connection_status.IsConnected.Assign(1);
414 pokeball_entry.connection_status.IsWired.Assign(1);
415
416 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 447 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
417 pokeball_entry.pad.l_stick = pad_state.l_stick; 448 pokeball_entry.pad.l_stick = pad_state.l_stick;
418 pokeball_entry.pad.r_stick = pad_state.r_stick; 449 pokeball_entry.pad.r_stick = pad_state.r_stick;
419 break; 450 break;
420 case NPadControllerType::ProController:
421 main_controller.connection_status.raw = 0;
422
423 main_controller.connection_status.IsConnected.Assign(1);
424 main_controller.connection_status.IsWired.Assign(1);
425 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
426 main_controller.pad.l_stick = pad_state.l_stick;
427 main_controller.pad.r_stick = pad_state.r_stick;
428 break;
429 } 451 }
430 452
431 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 453 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -453,26 +475,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
453 supported_npad_id_types.clear(); 475 supported_npad_id_types.clear();
454 supported_npad_id_types.resize(length / sizeof(u32)); 476 supported_npad_id_types.resize(length / sizeof(u32));
455 std::memcpy(supported_npad_id_types.data(), data, length); 477 std::memcpy(supported_npad_id_types.data(), data, length);
456 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
457 auto& controller = connected_controllers[i];
458 if (!controller.is_connected) {
459 continue;
460 }
461 const auto requested_controller =
462 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
463 : NPadControllerType::Handheld;
464 if (!IsControllerSupported(requested_controller)) {
465 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
466 if (is_handheld) {
467 controller.type = NPadControllerType::None;
468 controller.is_connected = false;
469 AddNewController(requested_controller);
470 } else {
471 controller.type = requested_controller;
472 InitNewlyAddedControler(i);
473 }
474 }
475 }
476} 478}
477 479
478void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 480void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -504,7 +506,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
504 const std::vector<Vibration>& vibrations) { 506 const std::vector<Vibration>& vibrations) {
505 LOG_DEBUG(Service_HID, "(STUBBED) called"); 507 LOG_DEBUG(Service_HID, "(STUBBED) called");
506 508
507 if (!can_controllers_vibrate) { 509 if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
508 return; 510 return;
509 } 511 }
510 for (std::size_t i = 0; i < controller_ids.size(); i++) { 512 for (std::size_t i = 0; i < controller_ids.size(); i++) {
@@ -517,8 +519,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
517} 519}
518 520
519std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 521std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
520 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
521 // be signalled at least once, and signaled after a new controller is connected?
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 523 return styleset_event.readable;
524} 524}
@@ -527,43 +527,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
527 return last_processed_vibration; 527 return last_processed_vibration;
528} 528}
529 529
530void Controller_NPad::AddNewController(NPadControllerType controller) { 530void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
531 controller = DecideBestController(controller); 531 UpdateControllerAt(controller, npad_index, true);
532 if (controller == NPadControllerType::Handheld) { 532}
533 connected_controllers[8] = {controller, true}; 533
534 InitNewlyAddedControler(8); 534void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 return; 535 bool connected) {
536 } 536 if (!connected) {
537 const auto pos = 537 DisconnectNPad(IndexToNPad(npad_index));
538 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
539 [](const ControllerHolder& holder) { return !holder.is_connected; });
540 if (pos == connected_controllers.end() - 2) {
541 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
542 return; 538 return;
543 } 539 }
544 const auto controller_id = std::distance(connected_controllers.begin(), pos);
545 connected_controllers[controller_id] = {controller, true};
546 InitNewlyAddedControler(controller_id);
547}
548 540
549void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
550 controller = DecideBestController(controller);
551 if (controller == NPadControllerType::Handheld) { 541 if (controller == NPadControllerType::Handheld) {
552 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; 542 Settings::values.players[HANDHELD_INDEX].controller_type =
553 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); 543 MapNPadToSettingsType(controller);
544 Settings::values.players[HANDHELD_INDEX].connected = true;
545 connected_controllers[HANDHELD_INDEX] = {controller, true};
546 InitNewlyAddedController(HANDHELD_INDEX);
554 return; 547 return;
555 } 548 }
556 549
557 connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; 550 Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
558 InitNewlyAddedControler(NPadIdToIndex(npad_id)); 551 Settings::values.players[npad_index].connected = true;
559} 552 connected_controllers[npad_index] = {controller, true};
560 553 InitNewlyAddedController(npad_index);
561void Controller_NPad::ConnectNPad(u32 npad_id) {
562 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
563} 554}
564 555
565void Controller_NPad::DisconnectNPad(u32 npad_id) { 556void Controller_NPad::DisconnectNPad(u32 npad_id) {
566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 557 const auto npad_index = NPadIdToIndex(npad_id);
558 connected_controllers[npad_index].is_connected = false;
559 Settings::values.players[npad_index].connected = false;
560
561 auto& controller = shared_memory_entries[npad_index];
562 controller.joy_styles.raw = 0; // Zero out
563 controller.device_type.raw = 0;
564 controller.properties.raw = 0;
565
566 styleset_changed_events[npad_index].writable->Signal();
567} 567}
568 568
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -599,8 +599,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
599 599
600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
601 601
602 InitNewlyAddedControler(npad_index_1); 602 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
603 InitNewlyAddedControler(npad_index_2); 603 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
604 604
605 return true; 605 return true;
606} 606}
@@ -614,11 +614,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
614 case 0: 614 case 0:
615 return LedPattern{1, 0, 0, 0}; 615 return LedPattern{1, 0, 0, 0};
616 case 1: 616 case 1:
617 return LedPattern{0, 1, 0, 0}; 617 return LedPattern{1, 1, 0, 0};
618 case 2: 618 case 2:
619 return LedPattern{0, 0, 1, 0}; 619 return LedPattern{1, 1, 1, 0};
620 case 3: 620 case 3:
621 return LedPattern{0, 0, 0, 1}; 621 return LedPattern{1, 1, 1, 1};
622 case 4: 622 case 4:
623 return LedPattern{1, 0, 0, 1}; 623 return LedPattern{1, 0, 0, 1};
624 case 5: 624 case 5:
@@ -628,7 +628,6 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
628 case 7: 628 case 7:
629 return LedPattern{0, 1, 1, 0}; 629 return LedPattern{0, 1, 1, 0};
630 default: 630 default:
631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
632 return LedPattern{0, 0, 0, 0}; 631 return LedPattern{0, 0, 0, 0};
633 } 632 }
634} 633}
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..75ce5b731 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -118,10 +118,11 @@ public:
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; 118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 119 Vibration GetLastVibration() const;
120 120
121 void AddNewController(NPadControllerType controller); 121 // Adds a new controller at an index.
122 void AddNewControllerAt(NPadControllerType controller, u32 npad_id); 122 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
123 // Adds a new controller at an index with connection status.
124 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
123 125
124 void ConnectNPad(u32 npad_id);
125 void DisconnectNPad(u32 npad_id); 126 void DisconnectNPad(u32 npad_id);
126 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 127 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
127 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 128 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -141,6 +142,8 @@ public:
141 // Specifically for cheat engine and other features. 142 // Specifically for cheat engine and other features.
142 u32 GetAndResetPressState(); 143 u32 GetAndResetPressState();
143 144
145 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
146 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
144 static std::size_t NPadIdToIndex(u32 npad_id); 147 static std::size_t NPadIdToIndex(u32 npad_id);
145 static u32 IndexToNPad(std::size_t index); 148 static u32 IndexToNPad(std::size_t index);
146 149
@@ -309,7 +312,7 @@ private:
309 bool is_connected; 312 bool is_connected;
310 }; 313 };
311 314
312 void InitNewlyAddedControler(std::size_t controller_idx); 315 void InitNewlyAddedController(std::size_t controller_idx);
313 bool IsControllerSupported(NPadControllerType controller) const; 316 bool IsControllerSupported(NPadControllerType controller) const;
314 NPadControllerType DecideBestController(NPadControllerType priority) const; 317 NPadControllerType DecideBestController(NPadControllerType priority) const;
315 void RequestPadStateUpdate(u32 npad_id); 318 void RequestPadStateUpdate(u32 npad_id);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1e95b7580..33416b5dd 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,11 +38,9 @@
38namespace Service::HID { 38namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// HID is polled every 15ms, this value was derived from
42constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43[[maybe_unused]] constexpr auto accelerometer_update_ns = 43constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
44 std::chrono::nanoseconds{1000000000 / 100};
45[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
46constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
47 45
48IAppletResource::IAppletResource(Core::System& system) 46IAppletResource::IAppletResource(Core::System& system)
@@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
845void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { 843void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
846 IPC::RequestParser rp{ctx}; 844 IPC::RequestParser rp{ctx};
847 const auto can_vibrate{rp.Pop<bool>()}; 845 const auto can_vibrate{rp.Pop<bool>()};
848 applet_resource->GetController<Controller_NPad>(HidController::NPad) 846 Settings::values.vibration_enabled = can_vibrate;
849 .SetVibrationEnabled(can_vibrate);
850 847
851 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 848 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
852 849
@@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
859 856
860 IPC::ResponseBuilder rb{ctx, 3}; 857 IPC::ResponseBuilder rb{ctx, 3};
861 rb.Push(RESULT_SUCCESS); 858 rb.Push(RESULT_SUCCESS);
862 rb.Push( 859 rb.Push(Settings::values.vibration_enabled);
863 applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
864} 860}
865 861
866void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 862void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d328fb8b7..28d3f9099 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,56 +13,6 @@
13 13
14namespace Settings { 14namespace Settings {
15 15
16namespace NativeButton {
17const std::array<const char*, NumButtons> mapping = {{
18 "button_a",
19 "button_b",
20 "button_x",
21 "button_y",
22 "button_lstick",
23 "button_rstick",
24 "button_l",
25 "button_r",
26 "button_zl",
27 "button_zr",
28 "button_plus",
29 "button_minus",
30 "button_dleft",
31 "button_dup",
32 "button_dright",
33 "button_ddown",
34 "button_lstick_left",
35 "button_lstick_up",
36 "button_lstick_right",
37 "button_lstick_down",
38 "button_rstick_left",
39 "button_rstick_up",
40 "button_rstick_right",
41 "button_rstick_down",
42 "button_sl",
43 "button_sr",
44 "button_home",
45 "button_screenshot",
46}};
47}
48
49namespace NativeAnalog {
50const std::array<const char*, NumAnalogs> mapping = {{
51 "lstick",
52 "rstick",
53}};
54}
55
56namespace NativeMouseButton {
57const std::array<const char*, NumMouseButtons> mapping = {{
58 "left",
59 "right",
60 "middle",
61 "forward",
62 "back",
63}};
64}
65
66Values values = {}; 16Values values = {};
67bool configuring_global = true; 17bool configuring_global = true;
68 18
diff --git a/src/core/settings.h b/src/core/settings.h
index 3681b5e9d..732c6a894 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
12#include <string> 12#include <string>
13#include <vector> 13#include <vector>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/settings.h"
15 16
16namespace Settings { 17namespace Settings {
17 18
18namespace NativeButton {
19enum Values {
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 LStick_Left,
39 LStick_Up,
40 LStick_Right,
41 LStick_Down,
42
43 RStick_Left,
44 RStick_Up,
45 RStick_Right,
46 RStick_Down,
47
48 SL,
49 SR,
50
51 Home,
52 Screenshot,
53
54 NumButtons,
55};
56
57constexpr int BUTTON_HID_BEGIN = A;
58constexpr int BUTTON_NS_BEGIN = Home;
59
60constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
61constexpr int BUTTON_NS_END = NumButtons;
62
63constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
64constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
65
66extern const std::array<const char*, NumButtons> mapping;
67
68} // namespace NativeButton
69
70namespace NativeAnalog {
71enum Values {
72 LStick,
73 RStick,
74
75 NumAnalogs,
76};
77
78constexpr int STICK_HID_BEGIN = LStick;
79constexpr int STICK_HID_END = NumAnalogs;
80constexpr int NUM_STICKS_HID = NumAnalogs;
81
82extern const std::array<const char*, NumAnalogs> mapping;
83} // namespace NativeAnalog
84
85namespace NativeMouseButton {
86enum Values {
87 Left,
88 Right,
89 Middle,
90 Forward,
91 Back,
92
93 NumMouseButtons,
94};
95
96constexpr int MOUSE_HID_BEGIN = Left;
97constexpr int MOUSE_HID_END = NumMouseButtons;
98constexpr int NUM_MOUSE_HID = NumMouseButtons;
99
100extern const std::array<const char*, NumMouseButtons> mapping;
101} // namespace NativeMouseButton
102
103namespace NativeKeyboard {
104enum Keys {
105 None,
106 Error,
107
108 A = 4,
109 B,
110 C,
111 D,
112 E,
113 F,
114 G,
115 H,
116 I,
117 J,
118 K,
119 L,
120 M,
121 N,
122 O,
123 P,
124 Q,
125 R,
126 S,
127 T,
128 U,
129 V,
130 W,
131 X,
132 Y,
133 Z,
134 N1,
135 N2,
136 N3,
137 N4,
138 N5,
139 N6,
140 N7,
141 N8,
142 N9,
143 N0,
144 Enter,
145 Escape,
146 Backspace,
147 Tab,
148 Space,
149 Minus,
150 Equal,
151 LeftBrace,
152 RightBrace,
153 Backslash,
154 Tilde,
155 Semicolon,
156 Apostrophe,
157 Grave,
158 Comma,
159 Dot,
160 Slash,
161 CapsLockKey,
162
163 F1,
164 F2,
165 F3,
166 F4,
167 F5,
168 F6,
169 F7,
170 F8,
171 F9,
172 F10,
173 F11,
174 F12,
175
176 SystemRequest,
177 ScrollLockKey,
178 Pause,
179 Insert,
180 Home,
181 PageUp,
182 Delete,
183 End,
184 PageDown,
185 Right,
186 Left,
187 Down,
188 Up,
189
190 NumLockKey,
191 KPSlash,
192 KPAsterisk,
193 KPMinus,
194 KPPlus,
195 KPEnter,
196 KP1,
197 KP2,
198 KP3,
199 KP4,
200 KP5,
201 KP6,
202 KP7,
203 KP8,
204 KP9,
205 KP0,
206 KPDot,
207
208 Key102,
209 Compose,
210 Power,
211 KPEqual,
212
213 F13,
214 F14,
215 F15,
216 F16,
217 F17,
218 F18,
219 F19,
220 F20,
221 F21,
222 F22,
223 F23,
224 F24,
225
226 Open,
227 Help,
228 Properties,
229 Front,
230 Stop,
231 Repeat,
232 Undo,
233 Cut,
234 Copy,
235 Paste,
236 Find,
237 Mute,
238 VolumeUp,
239 VolumeDown,
240 CapsLockActive,
241 NumLockActive,
242 ScrollLockActive,
243 KPComma,
244
245 KPLeftParenthesis,
246 KPRightParenthesis,
247
248 LeftControlKey = 0xE0,
249 LeftShiftKey,
250 LeftAltKey,
251 LeftMetaKey,
252 RightControlKey,
253 RightShiftKey,
254 RightAltKey,
255 RightMetaKey,
256
257 MediaPlayPause,
258 MediaStopCD,
259 MediaPrevious,
260 MediaNext,
261 MediaEject,
262 MediaVolumeUp,
263 MediaVolumeDown,
264 MediaMute,
265 MediaWebsite,
266 MediaBack,
267 MediaForward,
268 MediaStop,
269 MediaFind,
270 MediaScrollUp,
271 MediaScrollDown,
272 MediaEdit,
273 MediaSleep,
274 MediaCoffee,
275 MediaRefresh,
276 MediaCalculator,
277
278 NumKeyboardKeys,
279};
280
281static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
282
283enum Modifiers {
284 LeftControl,
285 LeftShift,
286 LeftAlt,
287 LeftMeta,
288 RightControl,
289 RightShift,
290 RightAlt,
291 RightMeta,
292 CapsLock,
293 ScrollLock,
294 NumLock,
295
296 NumKeyboardMods,
297};
298
299constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
300constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
301constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
302
303constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
304constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
305constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
306
307} // namespace NativeKeyboard
308
309using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
310using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
314
315constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
316constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
317constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
318constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
319
320enum class ControllerType {
321 ProController,
322 DualJoycon,
323 RightJoycon,
324 LeftJoycon,
325};
326
327struct PlayerInput {
328 bool connected;
329 ControllerType type;
330 ButtonsRaw buttons;
331 AnalogsRaw analogs;
332
333 u32 body_color_right;
334 u32 button_color_right;
335 u32 body_color_left;
336 u32 button_color_left;
337};
338
339struct TouchscreenInput {
340 bool enabled;
341 std::string device;
342
343 u32 finger;
344 u32 diameter_x;
345 u32 diameter_y;
346 u32 rotation_angle;
347};
348
349enum class RendererBackend { 19enum class RendererBackend {
350 OpenGL = 0, 20 OpenGL = 0,
351 Vulkan = 1, 21 Vulkan = 1,
@@ -461,6 +131,8 @@ struct Values {
461 // Controls 131 // Controls
462 std::array<PlayerInput, 10> players; 132 std::array<PlayerInput, 10> players;
463 133
134 bool use_docked_mode;
135
464 bool mouse_enabled; 136 bool mouse_enabled;
465 std::string mouse_device; 137 std::string mouse_device;
466 MouseButtonsRaw mouse_buttons; 138 MouseButtonsRaw mouse_buttons;
@@ -474,14 +146,15 @@ struct Values {
474 AnalogsRaw debug_pad_analogs; 146 AnalogsRaw debug_pad_analogs;
475 147
476 std::string motion_device; 148 std::string motion_device;
149
150 bool vibration_enabled;
151
477 TouchscreenInput touchscreen; 152 TouchscreenInput touchscreen;
478 std::atomic_bool is_device_reload_pending{true}; 153 std::atomic_bool is_device_reload_pending{true};
479 std::string udp_input_address; 154 std::string udp_input_address;
480 u16 udp_input_port; 155 u16 udp_input_port;
481 u8 udp_pad_index; 156 u8 udp_pad_index;
482 157
483 bool use_docked_mode;
484
485 // Data Storage 158 // Data Storage
486 bool use_virtual_sd; 159 bool use_virtual_sd;
487 bool gamecard_inserted; 160 bool gamecard_inserted;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 317c25bad..56267c8a8 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,8 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 settings.cpp
11 settings.h
10 gcadapter/gc_adapter.cpp 12 gcadapter/gc_adapter.cpp
11 gcadapter/gc_adapter.h 13 gcadapter/gc_adapter.h
12 gcadapter/gc_poller.cpp 14 gcadapter/gc_poller.cpp
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index b346fdf8e..71cd85eeb 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -191,7 +191,7 @@ public:
191 191
192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
193 const auto [x, y] = GetStatus(); 193 const auto [x, y] = GetStatus();
194 const float directional_deadzone = 0.4f; 194 const float directional_deadzone = 0.5f;
195 switch (direction) { 195 switch (direction) {
196 case Input::AnalogDirection::RIGHT: 196 case Input::AnalogDirection::RIGHT:
197 return x > directional_deadzone; 197 return x > directional_deadzone;
@@ -232,7 +232,7 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
232 const int port = params.Get("port", 0); 232 const int port = params.Get("port", 0);
233 const int axis_x = params.Get("axis_x", 0); 233 const int axis_x = params.Get("axis_x", 0);
234 const int axis_y = params.Get("axis_y", 1); 234 const int axis_y = params.Get("axis_y", 1);
235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
237 237
238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range); 238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b9d5d0ec3..57e7a25fe 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -18,67 +18,166 @@
18 18
19namespace InputCommon { 19namespace InputCommon {
20 20
21static std::shared_ptr<Keyboard> keyboard; 21struct InputSubsystem::Impl {
22static std::shared_ptr<MotionEmu> motion_emu; 22 void Initialize() {
23 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
24 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
25 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
26 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
27 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
28
29 keyboard = std::make_shared<Keyboard>();
30 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
31 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
32 std::make_shared<AnalogFromButton>());
33 motion_emu = std::make_shared<MotionEmu>();
34 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
35
23#ifdef HAVE_SDL2 36#ifdef HAVE_SDL2
24static std::unique_ptr<SDL::State> sdl; 37 sdl = SDL::Init();
25#endif 38#endif
26static std::unique_ptr<CemuhookUDP::State> udp;
27static std::shared_ptr<GCButtonFactory> gcbuttons;
28static std::shared_ptr<GCAnalogFactory> gcanalog;
29
30void Init() {
31 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
32 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
33 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
34 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
35 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
36
37 keyboard = std::make_shared<Keyboard>();
38 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
39 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
40 std::make_shared<AnalogFromButton>());
41 motion_emu = std::make_shared<MotionEmu>();
42 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
43 39
40 udp = CemuhookUDP::Init();
41 }
42
43 void Shutdown() {
44 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
45 keyboard.reset();
46 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
47 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
48 motion_emu.reset();
44#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
45 sdl = SDL::Init(); 50 sdl.reset();
46#endif 51#endif
52 udp.reset();
53 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
54 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
55
56 gcbuttons.reset();
57 gcanalog.reset();
58 }
59
60 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
61 std::vector<Common::ParamPackage> devices = {
62 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
63 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
64 };
65#ifdef HAVE_SDL2
66 auto sdl_devices = sdl->GetInputDevices();
67 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
68#endif
69 auto udp_devices = udp->GetInputDevices();
70 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
71 return devices;
72 }
73
74 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
75 const Common::ParamPackage& params) const {
76 if (!params.Has("class") || params.Get("class", "") == "any") {
77 return {};
78 }
79 if (params.Get("class", "") == "key") {
80 // TODO consider returning the SDL key codes for the default keybindings
81 return {};
82 }
83#ifdef HAVE_SDL2
84 if (params.Get("class", "") == "sdl") {
85 return sdl->GetAnalogMappingForDevice(params);
86 }
87#endif
88 return {};
89 }
90
91 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
92 const Common::ParamPackage& params) const {
93 if (!params.Has("class") || params.Get("class", "") == "any") {
94 return {};
95 }
96 if (params.Get("class", "") == "key") {
97 // TODO consider returning the SDL key codes for the default keybindings
98 return {};
99 }
100#ifdef HAVE_SDL2
101 if (params.Get("class", "") == "sdl") {
102 return sdl->GetButtonMappingForDevice(params);
103 }
104#endif
105 return {};
106 }
47 107
48 udp = CemuhookUDP::Init(); 108 std::shared_ptr<Keyboard> keyboard;
49} 109 std::shared_ptr<MotionEmu> motion_emu;
50
51void Shutdown() {
52 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
53 keyboard.reset();
54 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
55 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
56 motion_emu.reset();
57#ifdef HAVE_SDL2 110#ifdef HAVE_SDL2
58 sdl.reset(); 111 std::unique_ptr<SDL::State> sdl;
59#endif 112#endif
60 udp.reset(); 113 std::unique_ptr<CemuhookUDP::State> udp;
61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 114 std::shared_ptr<GCButtonFactory> gcbuttons;
62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 115 std::shared_ptr<GCAnalogFactory> gcanalog;
116};
117
118InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
119
120InputSubsystem::~InputSubsystem() = default;
121
122void InputSubsystem::Initialize() {
123 impl->Initialize();
124}
125
126void InputSubsystem::Shutdown() {
127 impl->Shutdown();
128}
129
130Keyboard* InputSubsystem::GetKeyboard() {
131 return impl->keyboard.get();
132}
133
134const Keyboard* InputSubsystem::GetKeyboard() const {
135 return impl->keyboard.get();
136}
63 137
64 gcbuttons.reset(); 138MotionEmu* InputSubsystem::GetMotionEmu() {
65 gcanalog.reset(); 139 return impl->motion_emu.get();
66} 140}
67 141
68Keyboard* GetKeyboard() { 142const MotionEmu* InputSubsystem::GetMotionEmu() const {
69 return keyboard.get(); 143 return impl->motion_emu.get();
70} 144}
71 145
72MotionEmu* GetMotionEmu() { 146std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
73 return motion_emu.get(); 147 return impl->GetInputDevices();
74} 148}
75 149
76GCButtonFactory* GetGCButtons() { 150AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
77 return gcbuttons.get(); 151 return impl->GetAnalogMappingForDevice(device);
78} 152}
79 153
80GCAnalogFactory* GetGCAnalogs() { 154ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
81 return gcanalog.get(); 155 return impl->GetButtonMappingForDevice(device);
156}
157
158GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
159 return impl->gcanalog.get();
160}
161
162const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
163 return impl->gcanalog.get();
164}
165
166GCButtonFactory* InputSubsystem::GetGCButtons() {
167 return impl->gcbuttons.get();
168}
169
170const GCButtonFactory* InputSubsystem::GetGCButtons() const {
171 return impl->gcbuttons.get();
172}
173
174std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
175 Polling::DeviceType type) const {
176#ifdef HAVE_SDL2
177 return impl->sdl->GetPollers(type);
178#else
179 return {};
180#endif
82} 181}
83 182
84std::string GenerateKeyboardParam(int key_code) { 183std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +201,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
102 }; 201 };
103 return circle_pad_param.Serialize(); 202 return circle_pad_param.Serialize();
104} 203}
105
106namespace Polling {
107
108std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
109 std::vector<std::unique_ptr<DevicePoller>> pollers;
110
111#ifdef HAVE_SDL2
112 pollers = sdl->GetPollers(type);
113#endif
114
115 return pollers;
116}
117
118} // namespace Polling
119} // namespace InputCommon 204} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 0e32856f6..58e5dc250 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -6,45 +6,25 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <unordered_map>
9#include <vector> 10#include <vector>
10#include "input_common/gcadapter/gc_poller.h"
11 11
12namespace Common { 12namespace Common {
13class ParamPackage; 13class ParamPackage;
14} 14}
15 15
16namespace InputCommon { 16namespace Settings::NativeAnalog {
17 17enum Values : int;
18/// Initializes and registers all built-in input device factories. 18}
19void Init();
20
21/// Deregisters all built-in input device factories and shuts them down.
22void Shutdown();
23
24class Keyboard;
25
26/// Gets the keyboard button device factory.
27Keyboard* GetKeyboard();
28
29class MotionEmu;
30
31/// Gets the motion emulation factory.
32MotionEmu* GetMotionEmu();
33
34GCButtonFactory* GetGCButtons();
35
36GCAnalogFactory* GetGCAnalogs();
37
38/// Generates a serialized param package for creating a keyboard button device
39std::string GenerateKeyboardParam(int key_code);
40 19
41/// Generates a serialized param package for creating an analog device taking input from keyboard 20namespace Settings::NativeButton {
42std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 21enum Values : int;
43 int key_modifier, float modifier_scale); 22}
44 23
24namespace InputCommon {
45namespace Polling { 25namespace Polling {
46 26
47enum class DeviceType { Button, Analog }; 27enum class DeviceType { Button, AnalogPreferred };
48 28
49/** 29/**
50 * A class that can be used to get inputs from an input device like controllers without having to 30 * A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +34,9 @@ class DevicePoller {
54public: 34public:
55 virtual ~DevicePoller() = default; 35 virtual ~DevicePoller() = default;
56 /// Setup and start polling for inputs, should be called before GetNextInput 36 /// Setup and start polling for inputs, should be called before GetNextInput
57 virtual void Start() = 0; 37 /// If a device_id is provided, events should be filtered to only include events from this
38 /// device id
39 virtual void Start(const std::string& device_id = "") = 0;
58 /// Stop polling 40 /// Stop polling
59 virtual void Stop() = 0; 41 virtual void Stop() = 0;
60 /** 42 /**
@@ -64,8 +46,89 @@ public:
64 */ 46 */
65 virtual Common::ParamPackage GetNextInput() = 0; 47 virtual Common::ParamPackage GetNextInput() = 0;
66}; 48};
67
68// Get all DevicePoller from all backends for a specific device type
69std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
70} // namespace Polling 49} // namespace Polling
50
51class GCAnalogFactory;
52class GCButtonFactory;
53class Keyboard;
54class MotionEmu;
55
56/**
57 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
58 * mapping for the device. This is currently only implemented for the SDL backend devices.
59 */
60using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
61using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
62
63class InputSubsystem {
64public:
65 explicit InputSubsystem();
66 ~InputSubsystem();
67
68 InputSubsystem(const InputSubsystem&) = delete;
69 InputSubsystem& operator=(const InputSubsystem&) = delete;
70
71 InputSubsystem(InputSubsystem&&) = delete;
72 InputSubsystem& operator=(InputSubsystem&&) = delete;
73
74 /// Initializes and registers all built-in input device factories.
75 void Initialize();
76
77 /// Unregisters all built-in input device factories and shuts them down.
78 void Shutdown();
79
80 /// Retrieves the underlying keyboard device.
81 [[nodiscard]] Keyboard* GetKeyboard();
82
83 /// Retrieves the underlying keyboard device.
84 [[nodiscard]] const Keyboard* GetKeyboard() const;
85
86 /// Retrieves the underlying motion emulation factory.
87 [[nodiscard]] MotionEmu* GetMotionEmu();
88
89 /// Retrieves the underlying motion emulation factory.
90 [[nodiscard]] const MotionEmu* GetMotionEmu() const;
91
92 /**
93 * Returns all available input devices that this Factory can create a new device with.
94 * Each returned ParamPackage should have a `display` field used for display, a class field for
95 * backends to determine if this backend is meant to service the request and any other
96 * information needed to identify this in the backend later.
97 */
98 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
99
100 /// Retrieves the analog mappings for the given device.
101 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
102
103 /// Retrieves the button mappings for the given device.
104 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
105
106 /// Retrieves the underlying GameCube analog handler.
107 [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
108
109 /// Retrieves the underlying GameCube analog handler.
110 [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
111
112 /// Retrieves the underlying GameCube button handler.
113 [[nodiscard]] GCButtonFactory* GetGCButtons();
114
115 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117
118 /// Get all DevicePoller from all backends for a specific device type
119 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
120 Polling::DeviceType type) const;
121
122private:
123 struct Impl;
124 std::unique_ptr<Impl> impl;
125};
126
127/// Generates a serialized param package for creating a keyboard button device
128std::string GenerateKeyboardParam(int key_code);
129
130/// Generates a serialized param package for creating an analog device taking input from keyboard
131std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
132 int key_modifier, float modifier_scale);
133
71} // namespace InputCommon 134} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 5306daa70..f3554be9a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/param_package.h"
9#include "input_common/main.h" 10#include "input_common/main.h"
10 11
11namespace InputCommon::Polling { 12namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
22 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
23 virtual ~State() = default; 24 virtual ~State() = default;
24 25
25 virtual Pollers GetPollers(Polling::DeviceType type) = 0; 26 virtual Pollers GetPollers(Polling::DeviceType type) {
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 }
26}; 40};
27 41
28class NullState : public State { 42class NullState : public State {
29public: 43public:
30 Pollers GetPollers(Polling::DeviceType type) override {
31 return {};
32 }
33}; 44};
34 45
35std::unique_ptr<State> Init(); 46std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d76c279d3..a9e676f4b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,10 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <atomic> 7#include <atomic>
7#include <cmath> 8#include <cmath>
8#include <functional> 9#include <functional>
9#include <mutex> 10#include <mutex>
11#include <optional>
12#include <sstream>
10#include <string> 13#include <string>
11#include <thread> 14#include <thread>
12#include <tuple> 15#include <tuple>
@@ -15,15 +18,16 @@
15#include <vector> 18#include <vector>
16#include <SDL.h> 19#include <SDL.h>
17#include "common/logging/log.h" 20#include "common/logging/log.h"
18#include "common/math_util.h"
19#include "common/param_package.h" 21#include "common/param_package.h"
20#include "common/threadsafe_queue.h" 22#include "common/threadsafe_queue.h"
21#include "core/frontend/input.h" 23#include "core/frontend/input.h"
22#include "input_common/sdl/sdl_impl.h" 24#include "input_common/sdl/sdl_impl.h"
25#include "input_common/settings.h"
23 26
24namespace InputCommon::SDL { 27namespace InputCommon::SDL {
25 28
26static std::string GetGUID(SDL_Joystick* joystick) { 29namespace {
30std::string GetGUID(SDL_Joystick* joystick) {
27 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 31 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
28 char guid_str[33]; 32 char guid_str[33];
29 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); 33 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +35,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
31} 35}
32 36
33/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 37/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
34static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); 38Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
39} // Anonymous namespace
35 40
36static int SDLEventWatcher(void* user_data, SDL_Event* event) { 41static int SDLEventWatcher(void* user_data, SDL_Event* event) {
37 auto* const sdl_state = static_cast<SDLState*>(user_data); 42 auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +53,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
48 53
49class SDLJoystick { 54class SDLJoystick {
50public: 55public:
51 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) 56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
52 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} 57 SDL_GameController* gamecontroller)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
59 sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
53 60
54 void SetButton(int button, bool value) { 61 void SetButton(int button, bool value) {
55 std::lock_guard lock{mutex}; 62 std::lock_guard lock{mutex};
@@ -115,10 +122,15 @@ public:
115 return sdl_joystick.get(); 122 return sdl_joystick.get();
116 } 123 }
117 124
118 void SetSDLJoystick(SDL_Joystick* joystick) { 125 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
126 sdl_controller.reset(controller);
119 sdl_joystick.reset(joystick); 127 sdl_joystick.reset(joystick);
120 } 128 }
121 129
130 SDL_GameController* GetSDLGameController() const {
131 return sdl_controller.get();
132 }
133
122private: 134private:
123 struct State { 135 struct State {
124 std::unordered_map<int, bool> buttons; 136 std::unordered_map<int, bool> buttons;
@@ -128,6 +140,7 @@ private:
128 std::string guid; 140 std::string guid;
129 int port; 141 int port;
130 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 142 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
143 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
131 mutable std::mutex mutex; 144 mutable std::mutex mutex;
132}; 145};
133 146
@@ -136,18 +149,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
136 const auto it = joystick_map.find(guid); 149 const auto it = joystick_map.find(guid);
137 if (it != joystick_map.end()) { 150 if (it != joystick_map.end()) {
138 while (it->second.size() <= static_cast<std::size_t>(port)) { 151 while (it->second.size() <= static_cast<std::size_t>(port)) {
139 auto joystick = 152 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
140 std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr); 153 nullptr, nullptr);
141 it->second.emplace_back(std::move(joystick)); 154 it->second.emplace_back(std::move(joystick));
142 } 155 }
143 return it->second[port]; 156 return it->second[port];
144 } 157 }
145 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); 158 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
146 return joystick_map[guid].emplace_back(std::move(joystick)); 159 return joystick_map[guid].emplace_back(std::move(joystick));
147} 160}
148 161
149std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 162std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
150 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
164 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
151 const std::string guid = GetGUID(sdl_joystick); 165 const std::string guid = GetGUID(sdl_joystick);
152 166
153 std::lock_guard lock{joystick_map_mutex}; 167 std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +185,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
171 }); 185 });
172 if (nullptr_it != map_it->second.end()) { 186 if (nullptr_it != map_it->second.end()) {
173 // ... and map it 187 // ... and map it
174 (*nullptr_it)->SetSDLJoystick(sdl_joystick); 188 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
175 return *nullptr_it; 189 return *nullptr_it;
176 } 190 }
177 191
178 // There is no SDLJoystick without a mapped SDL_Joystick 192 // There is no SDLJoystick without a mapped SDL_Joystick
179 // Create a new SDLJoystick 193 // Create a new SDLJoystick
180 const int port = static_cast<int>(map_it->second.size()); 194 const int port = static_cast<int>(map_it->second.size());
181 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 195 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
182 return map_it->second.emplace_back(std::move(joystick)); 196 return map_it->second.emplace_back(std::move(joystick));
183 } 197 }
184 198
185 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
186 return joystick_map[guid].emplace_back(std::move(joystick)); 200 return joystick_map[guid].emplace_back(std::move(joystick));
187} 201}
188 202
189void SDLState::InitJoystick(int joystick_index) { 203void SDLState::InitJoystick(int joystick_index) {
190 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 204 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
205 SDL_GameController* sdl_gamecontroller = nullptr;
206 if (SDL_IsGameController(joystick_index)) {
207 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
208 }
191 if (!sdl_joystick) { 209 if (!sdl_joystick) {
192 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 210 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
193 return; 211 return;
@@ -196,7 +214,7 @@ void SDLState::InitJoystick(int joystick_index) {
196 214
197 std::lock_guard lock{joystick_map_mutex}; 215 std::lock_guard lock{joystick_map_mutex};
198 if (joystick_map.find(guid) == joystick_map.end()) { 216 if (joystick_map.find(guid) == joystick_map.end()) {
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 217 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
200 joystick_map[guid].emplace_back(std::move(joystick)); 218 joystick_map[guid].emplace_back(std::move(joystick));
201 return; 219 return;
202 } 220 }
@@ -205,11 +223,11 @@ void SDLState::InitJoystick(int joystick_index) {
205 joystick_guid_list.begin(), joystick_guid_list.end(), 223 joystick_guid_list.begin(), joystick_guid_list.end(),
206 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 224 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
207 if (it != joystick_guid_list.end()) { 225 if (it != joystick_guid_list.end()) {
208 (*it)->SetSDLJoystick(sdl_joystick); 226 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
209 return; 227 return;
210 } 228 }
211 const int port = static_cast<int>(joystick_guid_list.size()); 229 const int port = static_cast<int>(joystick_guid_list.size());
212 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 230 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
213 joystick_guid_list.emplace_back(std::move(joystick)); 231 joystick_guid_list.emplace_back(std::move(joystick));
214} 232}
215 233
@@ -231,7 +249,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
231 249
232 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the 250 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
233 // event callback which locks the mutex again. 251 // event callback which locks the mutex again.
234 joystick->SetSDLJoystick(nullptr); 252 joystick->SetSDLJoystick(nullptr, nullptr);
235} 253}
236 254
237void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 255void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -341,12 +359,12 @@ public:
341 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
342 y / r * (r - deadzone) / (1 - deadzone)); 360 y / r * (r - deadzone) / (1 - deadzone));
343 } 361 }
344 return std::make_tuple<float, float>(0.0f, 0.0f); 362 return {};
345 } 363 }
346 364
347 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 365 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
348 const auto [x, y] = GetStatus(); 366 const auto [x, y] = GetStatus();
349 const float directional_deadzone = 0.4f; 367 const float directional_deadzone = 0.5f;
350 switch (direction) { 368 switch (direction) {
351 case Input::AnalogDirection::RIGHT: 369 case Input::AnalogDirection::RIGHT:
352 return x > directional_deadzone; 370 return x > directional_deadzone;
@@ -460,7 +478,7 @@ public:
460 const int port = params.Get("port", 0); 478 const int port = params.Get("port", 0);
461 const int axis_x = params.Get("axis_x", 0); 479 const int axis_x = params.Get("axis_x", 0);
462 const int axis_y = params.Get("axis_y", 1); 480 const int axis_y = params.Get("axis_y", 1);
463 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 481 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
464 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
465 auto joystick = state.GetSDLJoystickByGUID(guid, port); 483 auto joystick = state.GetSDLJoystickByGUID(guid, port);
466 484
@@ -476,8 +494,10 @@ private:
476 494
477SDLState::SDLState() { 495SDLState::SDLState() {
478 using namespace Input; 496 using namespace Input;
479 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); 497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
480 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this)); 498 button_factory = std::make_shared<SDLButtonFactory>(*this);
499 RegisterFactory<AnalogDevice>("sdl", analog_factory);
500 RegisterFactory<ButtonDevice>("sdl", button_factory);
481 501
482 // If the frontend is going to manage the event loop, then we dont start one here 502 // If the frontend is going to manage the event loop, then we dont start one here
483 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -485,6 +505,7 @@ SDLState::SDLState() {
485 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 505 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
486 return; 506 return;
487 } 507 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
488 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 509 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
489 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 510 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
490 } 511 }
@@ -497,7 +518,7 @@ SDLState::SDLState() {
497 using namespace std::chrono_literals; 518 using namespace std::chrono_literals;
498 while (initialized) { 519 while (initialized) {
499 SDL_PumpEvents(); 520 SDL_PumpEvents();
500 std::this_thread::sleep_for(10ms); 521 std::this_thread::sleep_for(5ms);
501 } 522 }
502 }); 523 });
503 } 524 }
@@ -523,65 +544,230 @@ SDLState::~SDLState() {
523 } 544 }
524} 545}
525 546
526static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 547std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
548 std::scoped_lock lock(joystick_map_mutex);
549 std::vector<Common::ParamPackage> devices;
550 for (const auto& [key, value] : joystick_map) {
551 for (const auto& joystick : value) {
552 auto joy = joystick->GetSDLJoystick();
553 if (auto controller = joystick->GetSDLGameController()) {
554 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{
557 {"class", "sdl"},
558 {"display", std::move(name)},
559 {"guid", joystick->GetGUID()},
560 {"port", std::to_string(joystick->GetPort())},
561 });
562 } else if (joy) {
563 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
564 devices.emplace_back(Common::ParamPackage{
565 {"class", "sdl"},
566 {"display", std::move(name)},
567 {"guid", joystick->GetGUID()},
568 {"port", std::to_string(joystick->GetPort())},
569 });
570 }
571 }
572 }
573 return devices;
574}
575
576namespace {
577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
578 float value = 0.1f) {
527 Common::ParamPackage params({{"engine", "sdl"}}); 579 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port);
581 params.Set("guid", std::move(guid));
582 params.Set("axis", axis);
583 if (value > 0) {
584 params.Set("direction", "+");
585 params.Set("threshold", "0.5");
586 } else {
587 params.Set("direction", "-");
588 params.Set("threshold", "-0.5");
589 }
590 return params;
591}
528 592
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
594 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port);
596 params.Set("guid", std::move(guid));
597 params.Set("button", button);
598 return params;
599}
600
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
602 Common::ParamPackage params({{"engine", "sdl"}});
603
604 params.Set("port", port);
605 params.Set("guid", std::move(guid));
606 params.Set("hat", hat);
607 switch (value) {
608 case SDL_HAT_UP:
609 params.Set("direction", "up");
610 break;
611 case SDL_HAT_DOWN:
612 params.Set("direction", "down");
613 break;
614 case SDL_HAT_LEFT:
615 params.Set("direction", "left");
616 break;
617 case SDL_HAT_RIGHT:
618 params.Set("direction", "right");
619 break;
620 default:
621 return {};
622 }
623 return params;
624}
625
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
529 switch (event.type) { 627 switch (event.type) {
530 case SDL_JOYAXISMOTION: { 628 case SDL_JOYAXISMOTION: {
531 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
532 params.Set("port", joystick->GetPort()); 630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
533 params.Set("guid", joystick->GetGUID()); 631 event.jaxis.axis, event.jaxis.value);
534 params.Set("axis", event.jaxis.axis);
535 if (event.jaxis.value > 0) {
536 params.Set("direction", "+");
537 params.Set("threshold", "0.5");
538 } else {
539 params.Set("direction", "-");
540 params.Set("threshold", "-0.5");
541 }
542 break;
543 } 632 }
544 case SDL_JOYBUTTONUP: { 633 case SDL_JOYBUTTONUP: {
545 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
546 params.Set("port", joystick->GetPort()); 635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
547 params.Set("guid", joystick->GetGUID()); 636 event.jbutton.button);
548 params.Set("button", event.jbutton.button);
549 break;
550 } 637 }
551 case SDL_JOYHATMOTION: { 638 case SDL_JOYHATMOTION: {
552 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
553 params.Set("port", joystick->GetPort()); 640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
554 params.Set("guid", joystick->GetGUID()); 641 event.jhat.hat, event.jhat.value);
555 params.Set("hat", event.jhat.hat);
556 switch (event.jhat.value) {
557 case SDL_HAT_UP:
558 params.Set("direction", "up");
559 break;
560 case SDL_HAT_DOWN:
561 params.Set("direction", "down");
562 break;
563 case SDL_HAT_LEFT:
564 params.Set("direction", "left");
565 break;
566 case SDL_HAT_RIGHT:
567 params.Set("direction", "right");
568 break;
569 default:
570 return {};
571 }
572 break;
573 } 642 }
574 } 643 }
644 return {};
645}
646
647Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
648 const SDL_GameControllerButtonBind& binding) {
649 switch (binding.bindType) {
650 case SDL_CONTROLLER_BINDTYPE_AXIS:
651 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
652 case SDL_CONTROLLER_BINDTYPE_BUTTON:
653 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
654 case SDL_CONTROLLER_BINDTYPE_HAT:
655 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
656 binding.value.hat.hat_mask);
657 }
658 return {};
659}
660
661Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
662 int axis_y) {
663 Common::ParamPackage params;
664 params.Set("engine", "sdl");
665 params.Set("port", port);
666 params.Set("guid", guid);
667 params.Set("axis_x", axis_x);
668 params.Set("axis_y", axis_y);
575 return params; 669 return params;
576} 670}
671} // Anonymous namespace
577 672
578namespace Polling { 673ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
674 if (!params.Has("guid") || !params.Has("port")) {
675 return {};
676 }
677 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
678 auto* controller = joystick->GetSDLGameController();
679 if (controller == nullptr) {
680 return {};
681 }
682
683 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
684 // We will add those afterwards
685 // This list also excludes Screenshot since theres not really a mapping for that
686 using ButtonBindings =
687 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
688 static constexpr ButtonBindings switch_to_sdl_button{{
689 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
690 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
691 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
692 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
693 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
694 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
695 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
696 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
697 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
698 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
699 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
700 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
701 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
702 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
703 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
704 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
705 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
706 }};
707
708 // Add the missing bindings for ZL/ZR
709 using ZBindings =
710 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
711 static constexpr ZBindings switch_to_sdl_axis{{
712 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
713 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
714 }};
715
716 ButtonMapping mapping;
717 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
718
719 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
720 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
721 mapping.insert_or_assign(
722 switch_button,
723 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
724 }
725 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
726 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
727 mapping.insert_or_assign(
728 switch_button,
729 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
730 }
731
732 return mapping;
733}
579 734
735AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
736 if (!params.Has("guid") || !params.Has("port")) {
737 return {};
738 }
739 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
740 auto* controller = joystick->GetSDLGameController();
741 if (controller == nullptr) {
742 return {};
743 }
744
745 AnalogMapping mapping = {};
746 const auto& binding_left_x =
747 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
748 const auto& binding_left_y =
749 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
750 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
751 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
752 binding_left_x.value.axis,
753 binding_left_y.value.axis));
754 const auto& binding_right_x =
755 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
756 const auto& binding_right_y =
757 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
758 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
759 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
760 binding_right_x.value.axis,
761 binding_right_y.value.axis));
762 return mapping;
763}
764
765namespace Polling {
580class SDLPoller : public InputCommon::Polling::DevicePoller { 766class SDLPoller : public InputCommon::Polling::DevicePoller {
581public: 767public:
582 explicit SDLPoller(SDLState& state_) : state(state_) {} 768 explicit SDLPoller(SDLState& state_) : state(state_) {}
583 769
584 void Start() override { 770 void Start(const std::string& device_id) override {
585 state.event_queue.Clear(); 771 state.event_queue.Clear();
586 state.polling = true; 772 state.polling = true;
587 } 773 }
@@ -601,71 +787,106 @@ public:
601 Common::ParamPackage GetNextInput() override { 787 Common::ParamPackage GetNextInput() override {
602 SDL_Event event; 788 SDL_Event event;
603 while (state.event_queue.Pop(event)) { 789 while (state.event_queue.Pop(event)) {
604 switch (event.type) { 790 const auto package = FromEvent(event);
605 case SDL_JOYAXISMOTION: 791 if (package) {
606 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 792 return *package;
607 break;
608 }
609 [[fallthrough]];
610 case SDL_JOYBUTTONUP:
611 case SDL_JOYHATMOTION:
612 return SDLEventToButtonParamPackage(state, event);
613 } 793 }
614 } 794 }
615 return {}; 795 return {};
616 } 796 }
797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
798 switch (event.type) {
799 case SDL_JOYAXISMOTION:
800 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
801 break;
802 }
803 [[fallthrough]];
804 case SDL_JOYBUTTONUP:
805 case SDL_JOYHATMOTION:
806 return {SDLEventToButtonParamPackage(state, event)};
807 }
808 return std::nullopt;
809 }
617}; 810};
618 811
619class SDLAnalogPoller final : public SDLPoller { 812/**
813 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
814 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
815 * instead
816 */
817class SDLAnalogPreferredPoller final : public SDLPoller {
620public: 818public:
621 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 819 explicit SDLAnalogPreferredPoller(SDLState& state_)
622 820 : SDLPoller(state_), button_poller(state_) {}
623 void Start() override {
624 SDLPoller::Start();
625 821
822 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id);
824 // Load the game controller
626 // Reset stored axes 825 // Reset stored axes
627 analog_x_axis = -1; 826 analog_x_axis = -1;
628 analog_y_axis = -1; 827 analog_y_axis = -1;
629 analog_axes_joystick = -1;
630 } 828 }
631 829
632 Common::ParamPackage GetNextInput() override { 830 Common::ParamPackage GetNextInput() override {
633 SDL_Event event; 831 SDL_Event event;
634 while (state.event_queue.Pop(event)) { 832 while (state.event_queue.Pop(event)) {
635 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 833 // Filter out axis events that are below a threshold
834 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
636 continue; 835 continue;
637 } 836 }
638 // An analog device needs two axes, so we need to store the axis for later and wait for 837 // Simplify controller config by testing if game controller support is enabled.
639 // a second SDL event. The axes also must be from the same joystick. 838 if (event.type == SDL_JOYAXISMOTION) {
640 const int axis = event.jaxis.axis; 839 const auto axis = event.jaxis.axis;
641 if (analog_x_axis == -1) { 840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
642 analog_x_axis = axis; 841 const auto controller = joystick->GetSDLGameController();
643 analog_axes_joystick = event.jaxis.which; 842 if (controller) {
644 } else if (analog_y_axis == -1 && analog_x_axis != axis && 843 const auto axis_left_x =
645 analog_axes_joystick == event.jaxis.which) { 844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
646 analog_y_axis = axis; 845 .value.axis;
846 const auto axis_left_y =
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
848 .value.axis;
849 const auto axis_right_x =
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
851 .value.axis;
852 const auto axis_right_y =
853 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
854 .value.axis;
855
856 if (axis == axis_left_x || axis == axis_left_y) {
857 analog_x_axis = axis_left_x;
858 analog_y_axis = axis_left_y;
859 break;
860 } else if (axis == axis_right_x || axis == axis_right_y) {
861 analog_x_axis = axis_right_x;
862 analog_y_axis = axis_right_y;
863 break;
864 }
865 }
866 }
867
868 // If the press wasn't accepted as a joy axis, check for a button press
869 auto button_press = button_poller.FromEvent(event);
870 if (button_press) {
871 return *button_press;
647 } 872 }
648 } 873 }
649 Common::ParamPackage params; 874
650 if (analog_x_axis != -1 && analog_y_axis != -1) { 875 if (analog_x_axis != -1 && analog_y_axis != -1) {
651 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
652 params.Set("engine", "sdl"); 877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
653 params.Set("port", joystick->GetPort()); 878 analog_x_axis, analog_y_axis);
654 params.Set("guid", joystick->GetGUID());
655 params.Set("axis_x", analog_x_axis);
656 params.Set("axis_y", analog_y_axis);
657 analog_x_axis = -1; 879 analog_x_axis = -1;
658 analog_y_axis = -1; 880 analog_y_axis = -1;
659 analog_axes_joystick = -1;
660 return params; 881 return params;
661 } 882 }
662 return params; 883 return {};
663 } 884 }
664 885
665private: 886private:
666 int analog_x_axis = -1; 887 int analog_x_axis = -1;
667 int analog_y_axis = -1; 888 int analog_y_axis = -1;
668 SDL_JoystickID analog_axes_joystick = -1; 889 SDLButtonPoller button_poller;
669}; 890};
670} // namespace Polling 891} // namespace Polling
671 892
@@ -673,8 +894,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
673 Pollers pollers; 894 Pollers pollers;
674 895
675 switch (type) { 896 switch (type) {
676 case InputCommon::Polling::DeviceType::Analog: 897 case InputCommon::Polling::DeviceType::AnalogPreferred:
677 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 898 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
678 break; 899 break;
679 case InputCommon::Polling::DeviceType::Button: 900 case InputCommon::Polling::DeviceType::Button:
680 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 901 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 606a32c5b..bd19ba61d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -50,6 +50,11 @@ public:
50 std::atomic<bool> polling = false; 50 std::atomic<bool> polling = false;
51 Common::SPSCQueue<SDL_Event> event_queue; 51 Common::SPSCQueue<SDL_Event> event_queue;
52 52
53 std::vector<Common::ParamPackage> GetInputDevices() override;
54
55 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
56 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
57
53private: 58private:
54 void InitJoystick(int joystick_index); 59 void InitJoystick(int joystick_index);
55 void CloseJoystick(SDL_Joystick* sdl_joystick); 60 void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,6 +62,9 @@ private:
57 /// Needs to be called before SDL_QuitSubSystem. 62 /// Needs to be called before SDL_QuitSubSystem.
58 void CloseJoysticks(); 63 void CloseJoysticks();
59 64
65 // Set to true if SDL supports game controller subsystem
66 bool has_gamecontroller = false;
67
60 /// Map of GUID of a list of corresponding virtual Joysticks 68 /// Map of GUID of a list of corresponding virtual Joysticks
61 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 69 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
62 std::mutex joystick_map_mutex; 70 std::mutex joystick_map_mutex;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
new file mode 100644
index 000000000..80c719cf4
--- /dev/null
+++ b/src/input_common/settings.cpp
@@ -0,0 +1,33 @@
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/settings.h"
6
7namespace Settings {
8namespace NativeButton {
9const std::array<const char*, NumButtons> mapping = {{
10 "button_a", "button_b", "button_x", "button_y", "button_lstick",
11 "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
12 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
13 "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
14}};
15}
16
17namespace NativeAnalog {
18const std::array<const char*, NumAnalogs> mapping = {{
19 "lstick",
20 "rstick",
21}};
22}
23
24namespace NativeMouseButton {
25const std::array<const char*, NumMouseButtons> mapping = {{
26 "left",
27 "right",
28 "middle",
29 "forward",
30 "back",
31}};
32}
33} // namespace Settings
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
new file mode 100644
index 000000000..2d258960b
--- /dev/null
+++ b/src/input_common/settings.h
@@ -0,0 +1,335 @@
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 <string>
9#include "common/common_types.h"
10
11namespace Settings {
12namespace NativeButton {
13enum Values : int {
14 A,
15 B,
16 X,
17 Y,
18 LStick,
19 RStick,
20 L,
21 R,
22 ZL,
23 ZR,
24 Plus,
25 Minus,
26
27 DLeft,
28 DUp,
29 DRight,
30 DDown,
31
32 SL,
33 SR,
34
35 Home,
36 Screenshot,
37
38 NumButtons,
39};
40
41constexpr int BUTTON_HID_BEGIN = A;
42constexpr int BUTTON_NS_BEGIN = Home;
43
44constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
45constexpr int BUTTON_NS_END = NumButtons;
46
47constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
48constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
49
50extern const std::array<const char*, NumButtons> mapping;
51
52} // namespace NativeButton
53
54namespace NativeAnalog {
55enum Values : int {
56 LStick,
57 RStick,
58
59 NumAnalogs,
60};
61
62constexpr int STICK_HID_BEGIN = LStick;
63constexpr int STICK_HID_END = NumAnalogs;
64constexpr int NUM_STICKS_HID = NumAnalogs;
65
66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog
68
69namespace NativeMouseButton {
70enum Values {
71 Left,
72 Right,
73 Middle,
74 Forward,
75 Back,
76
77 NumMouseButtons,
78};
79
80constexpr int MOUSE_HID_BEGIN = Left;
81constexpr int MOUSE_HID_END = NumMouseButtons;
82constexpr int NUM_MOUSE_HID = NumMouseButtons;
83
84extern const std::array<const char*, NumMouseButtons> mapping;
85} // namespace NativeMouseButton
86
87namespace NativeKeyboard {
88enum Keys {
89 None,
90 Error,
91
92 A = 4,
93 B,
94 C,
95 D,
96 E,
97 F,
98 G,
99 H,
100 I,
101 J,
102 K,
103 L,
104 M,
105 N,
106 O,
107 P,
108 Q,
109 R,
110 S,
111 T,
112 U,
113 V,
114 W,
115 X,
116 Y,
117 Z,
118 N1,
119 N2,
120 N3,
121 N4,
122 N5,
123 N6,
124 N7,
125 N8,
126 N9,
127 N0,
128 Enter,
129 Escape,
130 Backspace,
131 Tab,
132 Space,
133 Minus,
134 Equal,
135 LeftBrace,
136 RightBrace,
137 Backslash,
138 Tilde,
139 Semicolon,
140 Apostrophe,
141 Grave,
142 Comma,
143 Dot,
144 Slash,
145 CapsLockKey,
146
147 F1,
148 F2,
149 F3,
150 F4,
151 F5,
152 F6,
153 F7,
154 F8,
155 F9,
156 F10,
157 F11,
158 F12,
159
160 SystemRequest,
161 ScrollLockKey,
162 Pause,
163 Insert,
164 Home,
165 PageUp,
166 Delete,
167 End,
168 PageDown,
169 Right,
170 Left,
171 Down,
172 Up,
173
174 NumLockKey,
175 KPSlash,
176 KPAsterisk,
177 KPMinus,
178 KPPlus,
179 KPEnter,
180 KP1,
181 KP2,
182 KP3,
183 KP4,
184 KP5,
185 KP6,
186 KP7,
187 KP8,
188 KP9,
189 KP0,
190 KPDot,
191
192 Key102,
193 Compose,
194 Power,
195 KPEqual,
196
197 F13,
198 F14,
199 F15,
200 F16,
201 F17,
202 F18,
203 F19,
204 F20,
205 F21,
206 F22,
207 F23,
208 F24,
209
210 Open,
211 Help,
212 Properties,
213 Front,
214 Stop,
215 Repeat,
216 Undo,
217 Cut,
218 Copy,
219 Paste,
220 Find,
221 Mute,
222 VolumeUp,
223 VolumeDown,
224 CapsLockActive,
225 NumLockActive,
226 ScrollLockActive,
227 KPComma,
228
229 KPLeftParenthesis,
230 KPRightParenthesis,
231
232 LeftControlKey = 0xE0,
233 LeftShiftKey,
234 LeftAltKey,
235 LeftMetaKey,
236 RightControlKey,
237 RightShiftKey,
238 RightAltKey,
239 RightMetaKey,
240
241 MediaPlayPause,
242 MediaStopCD,
243 MediaPrevious,
244 MediaNext,
245 MediaEject,
246 MediaVolumeUp,
247 MediaVolumeDown,
248 MediaMute,
249 MediaWebsite,
250 MediaBack,
251 MediaForward,
252 MediaStop,
253 MediaFind,
254 MediaScrollUp,
255 MediaScrollDown,
256 MediaEdit,
257 MediaSleep,
258 MediaCoffee,
259 MediaRefresh,
260 MediaCalculator,
261
262 NumKeyboardKeys,
263};
264
265static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
266
267enum Modifiers {
268 LeftControl,
269 LeftShift,
270 LeftAlt,
271 LeftMeta,
272 RightControl,
273 RightShift,
274 RightAlt,
275 RightMeta,
276 CapsLock,
277 ScrollLock,
278 NumLock,
279
280 NumKeyboardMods,
281};
282
283constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
284constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
285constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
286
287constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
288constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
289constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
290
291} // namespace NativeKeyboard
292
293using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
294using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
295using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
296using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
297using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
298
299constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
300constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
301constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
302constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
303
304enum class ControllerType {
305 ProController,
306 DualJoyconDetached,
307 LeftJoycon,
308 RightJoycon,
309 Handheld,
310};
311
312struct PlayerInput {
313 bool connected;
314 ControllerType controller_type;
315 ButtonsRaw buttons;
316 AnalogsRaw analogs;
317 std::string lstick_mod;
318 std::string rstick_mod;
319
320 u32 body_color_left;
321 u32 body_color_right;
322 u32 button_color_left;
323 u32 button_color_right;
324};
325
326struct TouchscreenInput {
327 bool enabled;
328 std::string device;
329
330 u32 finger;
331 u32 diameter_x;
332 u32 diameter_y;
333 u32 rotation_angle;
334};
335} // namespace Settings
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8c6ef1394..4b347e47e 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -77,10 +77,11 @@ State::State() {
77 std::make_unique<Client>(status, Settings::values.udp_input_address, 77 std::make_unique<Client>(status, Settings::values.udp_input_address,
78 Settings::values.udp_input_port, Settings::values.udp_pad_index); 78 Settings::values.udp_input_port, Settings::values.udp_pad_index);
79 79
80 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", 80 motion_factory = std::make_shared<UDPMotionFactory>(status);
81 std::make_shared<UDPTouchFactory>(status)); 81 touch_factory = std::make_shared<UDPTouchFactory>(status);
82 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", 82
83 std::make_shared<UDPMotionFactory>(status)); 83 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
84 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
84} 85}
85 86
86State::~State() { 87State::~State() {
@@ -88,6 +89,11 @@ State::~State() {
88 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 89 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
89} 90}
90 91
92std::vector<Common::ParamPackage> State::GetInputDevices() const {
93 // TODO support binding udp devices
94 return {};
95}
96
91void State::ReloadUDPClient() { 97void State::ReloadUDPClient() {
92 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, 98 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
93 Settings::values.udp_pad_index); 99 Settings::values.udp_pad_index);
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 4f83f0441..672a5c812 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -5,19 +5,26 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
9#include "common/param_package.h"
8 10
9namespace InputCommon::CemuhookUDP { 11namespace InputCommon::CemuhookUDP {
10 12
11class Client; 13class Client;
14class UDPMotionFactory;
15class UDPTouchFactory;
12 16
13class State { 17class State {
14public: 18public:
15 State(); 19 State();
16 ~State(); 20 ~State();
17 void ReloadUDPClient(); 21 void ReloadUDPClient();
22 std::vector<Common::ParamPackage> GetInputDevices() const;
18 23
19private: 24private:
20 std::unique_ptr<Client> client; 25 std::unique_ptr<Client> client;
26 std::shared_ptr<UDPMotionFactory> motion_factory;
27 std::shared_ptr<UDPTouchFactory> touch_factory;
21}; 28};
22 29
23std::unique_ptr<State> Init(); 30std::unique_ptr<State> Init();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3cd896a0f..d85f1e9d1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,3 +1,5 @@
1add_subdirectory(host_shaders)
2
1add_library(video_core STATIC 3add_library(video_core STATIC
2 buffer_cache/buffer_block.h 4 buffer_cache/buffer_block.h
3 buffer_cache/buffer_cache.h 5 buffer_cache/buffer_cache.h
@@ -244,6 +246,9 @@ create_target_directory_groups(video_core)
244target_link_libraries(video_core PUBLIC common core) 246target_link_libraries(video_core PUBLIC common core)
245target_link_libraries(video_core PRIVATE glad xbyak) 247target_link_libraries(video_core PRIVATE glad xbyak)
246 248
249add_dependencies(video_core host_shaders)
250target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
251
247if (ENABLE_VULKAN) 252if (ENABLE_VULKAN)
248 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 253 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
249 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 254 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
new file mode 100644
index 000000000..aa62363a7
--- /dev/null
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -0,0 +1,43 @@
1set(SHADER_FILES
2 opengl_present.frag
3 opengl_present.vert
4)
5
6set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
7set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
8
9set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
10add_custom_command(
11 OUTPUT
12 ${SHADER_DIR}
13 COMMAND
14 ${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
15)
16
17set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
18set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
19
20foreach(FILENAME IN ITEMS ${SHADER_FILES})
21 string(REPLACE "." "_" SHADER_NAME ${FILENAME})
22 set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
23 set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
24 add_custom_command(
25 OUTPUT
26 ${HEADER_FILE}
27 COMMAND
28 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
29 MAIN_DEPENDENCY
30 ${SOURCE_FILE}
31 DEPENDS
32 ${HEADER_GENERATOR}
33 ${INPUT_FILE}
34 )
35 set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
36endforeach()
37
38add_custom_target(host_shaders
39 DEPENDS
40 ${SHADER_HEADERS}
41 SOURCES
42 ${SHADER_FILES}
43)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
new file mode 100644
index 000000000..368bce0ed
--- /dev/null
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -0,0 +1,11 @@
1set(SOURCE_FILE ${CMAKE_ARGV3})
2set(HEADER_FILE ${CMAKE_ARGV4})
3set(INPUT_FILE ${CMAKE_ARGV5})
4
5get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
6string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
7string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
8
9file(READ ${SOURCE_FILE} CONTENTS)
10
11configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
new file mode 100644
index 000000000..8a4cb024b
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -0,0 +1,10 @@
1#version 430 core
2
3layout (location = 0) in vec2 frag_tex_coord;
4layout (location = 0) out vec4 color;
5
6layout (binding = 0) uniform sampler2D color_texture;
7
8void main() {
9 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
10}
diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert
new file mode 100644
index 000000000..2235d31a4
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.vert
@@ -0,0 +1,24 @@
1#version 430 core
2
3out gl_PerVertex {
4 vec4 gl_Position;
5};
6
7layout (location = 0) in vec2 vert_position;
8layout (location = 1) in vec2 vert_tex_coord;
9layout (location = 0) out vec2 frag_tex_coord;
10
11// This is a truncated 3x3 matrix for 2D transformations:
12// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
13// The third column performs translation.
14// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
15// implicitly be [0, 0, 1]
16layout (location = 0) uniform mat3x2 modelview_matrix;
17
18void main() {
19 // Multiply input position by the rotscale part of the matrix and then manually translate by
20 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
21 // to `vec3(vert_position.xy, 1.0)`
22 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
23 frag_tex_coord = vert_tex_coord;
24}
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
new file mode 100644
index 000000000..ccdb0d2a9
--- /dev/null
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -0,0 +1,9 @@
1#pragma once
2
3#include <string_view>
4
5namespace HostShaders {
6
7constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
8
9} // namespace HostShaders
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 533b415e9..53c8d122a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -31,19 +31,19 @@ public:
31 constexpr PageEntry(State state) : state{state} {} 31 constexpr PageEntry(State state) : state{state} {}
32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {} 32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
33 33
34 constexpr bool IsUnmapped() const { 34 [[nodiscard]] constexpr bool IsUnmapped() const {
35 return state == State::Unmapped; 35 return state == State::Unmapped;
36 } 36 }
37 37
38 constexpr bool IsAllocated() const { 38 [[nodiscard]] constexpr bool IsAllocated() const {
39 return state == State::Allocated; 39 return state == State::Allocated;
40 } 40 }
41 41
42 constexpr bool IsValid() const { 42 [[nodiscard]] constexpr bool IsValid() const {
43 return !IsUnmapped() && !IsAllocated(); 43 return !IsUnmapped() && !IsAllocated();
44 } 44 }
45 45
46 constexpr VAddr ToAddress() const { 46 [[nodiscard]] constexpr VAddr ToAddress() const {
47 if (!IsValid()) { 47 if (!IsValid()) {
48 return {}; 48 return {};
49 } 49 }
@@ -51,7 +51,7 @@ public:
51 return static_cast<VAddr>(state) << ShiftBits; 51 return static_cast<VAddr>(state) << ShiftBits;
52 } 52 }
53 53
54 constexpr PageEntry operator+(u64 offset) { 54 [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
55 // If this is a reserved value, offsets do not apply 55 // If this is a reserved value, offsets do not apply
56 if (!IsValid()) { 56 if (!IsValid()) {
57 return *this; 57 return *this;
@@ -74,16 +74,16 @@ public:
74 /// Binds a renderer to the memory manager. 74 /// Binds a renderer to the memory manager.
75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer); 75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
76 76
77 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; 77 [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
78 78
79 template <typename T> 79 template <typename T>
80 T Read(GPUVAddr addr) const; 80 [[nodiscard]] T Read(GPUVAddr addr) const;
81 81
82 template <typename T> 82 template <typename T>
83 void Write(GPUVAddr addr, T data); 83 void Write(GPUVAddr addr, T data);
84 84
85 u8* GetPointer(GPUVAddr addr); 85 [[nodiscard]] u8* GetPointer(GPUVAddr addr);
86 const u8* GetPointer(GPUVAddr addr) const; 86 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
87 87
88 /** 88 /**
89 * ReadBlock and WriteBlock are full read and write operations over virtual 89 * ReadBlock and WriteBlock are full read and write operations over virtual
@@ -112,24 +112,24 @@ public:
112 /** 112 /**
113 * IsGranularRange checks if a gpu region can be simply read with a pointer. 113 * IsGranularRange checks if a gpu region can be simply read with a pointer.
114 */ 114 */
115 bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const; 115 [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
116 116
117 GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); 117 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
118 GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); 118 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
119 std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size); 119 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
120 GPUVAddr Allocate(std::size_t size, std::size_t align); 120 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
121 void Unmap(GPUVAddr gpu_addr, std::size_t size); 121 void Unmap(GPUVAddr gpu_addr, std::size_t size);
122 122
123private: 123private:
124 PageEntry GetPageEntry(GPUVAddr gpu_addr) const; 124 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); 125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); 126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
127 std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const; 127 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
128 128
129 void TryLockPage(PageEntry page_entry, std::size_t size); 129 void TryLockPage(PageEntry page_entry, std::size_t size);
130 void TryUnlockPage(PageEntry page_entry, std::size_t size); 130 void TryUnlockPage(PageEntry page_entry, std::size_t size);
131 131
132 static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { 132 [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
133 return (gpu_addr >> page_bits) & page_table_mask; 133 return (gpu_addr >> page_bits) & page_table_mask;
134 } 134 }
135 135
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index a787e27d2..0ebcec427 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -2,6 +2,7 @@
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 <string_view>
5#include <utility> 6#include <utility>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/common_types.h" 8#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
82 handle = 0; 83 handle = 0;
83} 84}
84 85
85void OGLShader::Create(const char* source, GLenum type) { 86void OGLShader::Create(std::string_view source, GLenum type) {
86 if (handle != 0) 87 if (handle != 0) {
87 return; 88 return;
88 if (source == nullptr) 89 }
90 if (source.empty()) {
89 return; 91 return;
92 }
90 93
91 MICROPROFILE_SCOPE(OpenGL_ResourceCreation); 94 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
92 handle = GLShader::LoadShader(source, type); 95 handle = GLShader::LoadShader(source, type);
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b05cb641c..f48398669 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string_view>
7#include <utility> 8#include <utility>
8#include <glad/glad.h> 9#include <glad/glad.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
127 return *this; 128 return *this;
128 } 129 }
129 130
130 void Create(const char* source, GLenum type); 131 void Create(std::string_view source, GLenum type);
131 132
132 void Release(); 133 void Release();
133 134
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index eb49a36bf..a07d56ef0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
22#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
23#include "video_core/renderer_opengl/gl_arb_decompiler.h" 23#include "video_core/renderer_opengl/gl_arb_decompiler.h"
24#include "video_core/renderer_opengl/gl_rasterizer.h" 24#include "video_core/renderer_opengl/gl_rasterizer.h"
25#include "video_core/renderer_opengl/gl_resource_manager.h"
25#include "video_core/renderer_opengl/gl_shader_cache.h" 26#include "video_core/renderer_opengl/gl_shader_cache.h"
26#include "video_core/renderer_opengl/gl_shader_decompiler.h" 27#include "video_core/renderer_opengl/gl_shader_decompiler.h"
27#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 28#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 9e74eda0d..4bf0d6090 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -2,6 +2,7 @@
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 <string_view>
5#include <vector> 6#include <vector>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -11,7 +12,8 @@
11namespace OpenGL::GLShader { 12namespace OpenGL::GLShader {
12 13
13namespace { 14namespace {
14const char* GetStageDebugName(GLenum type) { 15
16std::string_view StageDebugName(GLenum type) {
15 switch (type) { 17 switch (type) {
16 case GL_VERTEX_SHADER: 18 case GL_VERTEX_SHADER:
17 return "vertex"; 19 return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
25 UNIMPLEMENTED(); 27 UNIMPLEMENTED();
26 return "unknown"; 28 return "unknown";
27} 29}
30
28} // Anonymous namespace 31} // Anonymous namespace
29 32
30GLuint LoadShader(const char* source, GLenum type) { 33GLuint LoadShader(std::string_view source, GLenum type) {
31 const char* debug_type = GetStageDebugName(type); 34 const std::string_view debug_type = StageDebugName(type);
32 const GLuint shader_id = glCreateShader(type); 35 const GLuint shader_id = glCreateShader(type);
33 glShaderSource(shader_id, 1, &source, nullptr); 36
37 const GLchar* source_string = source.data();
38 const GLint source_length = static_cast<GLint>(source.size());
39
40 glShaderSource(shader_id, 1, &source_string, &source_length);
34 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); 41 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
35 glCompileShader(shader_id); 42 glCompileShader(shader_id);
36 43
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 03b7548c2..1b770532e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
38 * @param source String of the GLSL shader program 38 * @param source String of the GLSL shader program
39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER) 39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
40 */ 40 */
41GLuint LoadShader(const char* source, GLenum type); 41GLuint LoadShader(std::string_view source, GLenum type);
42 42
43/** 43/**
44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) 44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index c39663db7..b759c2dba 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,6 +21,8 @@
21#include "core/perf_stats.h" 21#include "core/perf_stats.h"
22#include "core/settings.h" 22#include "core/settings.h"
23#include "core/telemetry_session.h" 23#include "core/telemetry_session.h"
24#include "video_core/host_shaders/opengl_present_frag.h"
25#include "video_core/host_shaders/opengl_present_vert.h"
24#include "video_core/morton.h" 26#include "video_core/morton.h"
25#include "video_core/renderer_opengl/gl_rasterizer.h" 27#include "video_core/renderer_opengl/gl_rasterizer.h"
26#include "video_core/renderer_opengl/gl_shader_manager.h" 28#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -44,46 +46,6 @@ struct Frame {
44 bool is_srgb{}; /// Framebuffer is sRGB or RGB 46 bool is_srgb{}; /// Framebuffer is sRGB or RGB
45}; 47};
46 48
47constexpr char VERTEX_SHADER[] = R"(
48#version 430 core
49
50out gl_PerVertex {
51 vec4 gl_Position;
52};
53
54layout (location = 0) in vec2 vert_position;
55layout (location = 1) in vec2 vert_tex_coord;
56layout (location = 0) out vec2 frag_tex_coord;
57
58// This is a truncated 3x3 matrix for 2D transformations:
59// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
60// The third column performs translation.
61// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
62// implicitly be [0, 0, 1]
63layout (location = 0) uniform mat3x2 modelview_matrix;
64
65void main() {
66 // Multiply input position by the rotscale part of the matrix and then manually translate by
67 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
68 // to `vec3(vert_position.xy, 1.0)`
69 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
70 frag_tex_coord = vert_tex_coord;
71}
72)";
73
74constexpr char FRAGMENT_SHADER[] = R"(
75#version 430 core
76
77layout (location = 0) in vec2 frag_tex_coord;
78layout (location = 0) out vec4 color;
79
80layout (binding = 0) uniform sampler2D color_texture;
81
82void main() {
83 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
84}
85)";
86
87constexpr GLint PositionLocation = 0; 49constexpr GLint PositionLocation = 0;
88constexpr GLint TexCoordLocation = 1; 50constexpr GLint TexCoordLocation = 1;
89constexpr GLint ModelViewMatrixLocation = 0; 51constexpr GLint ModelViewMatrixLocation = 0;
@@ -461,10 +423,10 @@ void RendererOpenGL::InitOpenGLObjects() {
461 423
462 // Create shader programs 424 // Create shader programs
463 OGLShader vertex_shader; 425 OGLShader vertex_shader;
464 vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER); 426 vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
465 427
466 OGLShader fragment_shader; 428 OGLShader fragment_shader;
467 fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER); 429 fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
468 430
469 vertex_program.Create(true, false, vertex_shader.handle); 431 vertex_program.Create(true, false, vertex_shader.handle);
470 fragment_program.Create(true, false, fragment_shader.handle); 432 fragment_program.Create(true, false, fragment_shader.handle);
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index ebcfaa0e3..4205bd573 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -380,6 +380,14 @@ bool VKDevice::Create() {
380 380
381 CollectTelemetryParameters(); 381 CollectTelemetryParameters();
382 382
383 if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
384 // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but the <stride> field
385 // seems to be bugged. Blacklisting it for now.
386 LOG_WARNING(Render_Vulkan,
387 "Blacklisting AMD proprietary from VK_EXT_extended_dynamic_state");
388 ext_extended_dynamic_state = false;
389 }
390
383 graphics_queue = logical.GetQueue(graphics_family); 391 graphics_queue = logical.GetQueue(graphics_family);
384 present_queue = logical.GetQueue(present_family); 392 present_queue = logical.GetQueue(present_family);
385 393
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 936f76195..ff1b52eab 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1443,10 +1443,10 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1443} 1443}
1444 1444
1445void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { 1445void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1446 if (!state_tracker.TouchPrimitiveTopology()) { 1446 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1447 if (!state_tracker.ChangePrimitiveTopology(primitive_topology)) {
1447 return; 1448 return;
1448 } 1449 }
1449 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1450 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { 1450 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1451 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); 1451 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1452 }); 1452 });
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9151d9fb1..4bd1009f9 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
42 flags[DepthWriteEnable] = true; 42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true; 43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true; 44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true; 45 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true; 46 flags[StencilTestEnable] = true;
48 return flags; 47 return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
112 table[OFF(screen_y_control)] = FrontFace; 111 table[OFF(screen_y_control)] = FrontFace;
113} 112}
114 113
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) { 114void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0]; 115 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp; 116 table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -156,13 +151,13 @@ void StateTracker::Initialize() {
156 SetupDirtyDepthWriteEnable(tables); 151 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables); 152 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables); 153 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables); 154 SetupDirtyStencilOp(tables);
161 SetupDirtyStencilTestEnable(tables); 155 SetupDirtyStencilTestEnable(tables);
162} 156}
163 157
164void StateTracker::InvalidateCommandBufferState() { 158void StateTracker::InvalidateCommandBufferState() {
165 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags; 159 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
160 current_topology = INVALID_TOPOLOGY;
166} 161}
167 162
168} // namespace Vulkan 163} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 54ca0d6c6..13a6ce786 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -32,7 +32,6 @@ enum : u8 {
32 DepthWriteEnable, 32 DepthWriteEnable,
33 DepthCompareOp, 33 DepthCompareOp,
34 FrontFace, 34 FrontFace,
35 PrimitiveTopology,
36 StencilOp, 35 StencilOp,
37 StencilTestEnable, 36 StencilTestEnable,
38 37
@@ -43,6 +42,8 @@ static_assert(Last <= std::numeric_limits<u8>::max());
43} // namespace Dirty 42} // namespace Dirty
44 43
45class StateTracker { 44class StateTracker {
45 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
46
46public: 47public:
47 explicit StateTracker(Core::System& system); 48 explicit StateTracker(Core::System& system);
48 49
@@ -102,10 +103,6 @@ public:
102 return Exchange(Dirty::FrontFace, false); 103 return Exchange(Dirty::FrontFace, false);
103 } 104 }
104 105
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() { 106 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false); 107 return Exchange(Dirty::StencilOp, false);
111 } 108 }
@@ -114,7 +111,15 @@ public:
114 return Exchange(Dirty::StencilTestEnable, false); 111 return Exchange(Dirty::StencilTestEnable, false);
115 } 112 }
116 113
114 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
115 const bool has_changed = current_topology != new_topology;
116 current_topology = new_topology;
117 return has_changed;
118 }
119
117private: 120private:
121 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
122
118 bool Exchange(std::size_t id, bool new_value) const noexcept { 123 bool Exchange(std::size_t id, bool new_value) const noexcept {
119 auto& flags = system.GPU().Maxwell3D().dirty.flags; 124 auto& flags = system.GPU().Maxwell3D().dirty.flags;
120 const bool is_dirty = flags[id]; 125 const bool is_dirty = flags[id];
@@ -124,6 +129,7 @@ private:
124 129
125 Core::System& system; 130 Core::System& system;
126 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 131 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
132 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
127}; 133};
128 134
129} // namespace Vulkan 135} // namespace Vulkan
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index e4739394d..e2bba88dd 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -386,7 +386,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
386 break; 386 break;
387 } 387 }
388 case OpCode::Id::RED: { 388 case OpCode::Id::RED: {
389 UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32); 389 UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32, "type={}",
390 static_cast<int>(instr.red.type.Value()));
390 const auto [real_address, base_address, descriptor] = 391 const auto [real_address, base_address, descriptor] =
391 TrackGlobalMemory(bb, instr, true, true); 392 TrackGlobalMemory(bb, instr, true, true);
392 if (!real_address || !base_address) { 393 if (!real_address || !base_address) {
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 656096c9f..6987e85e1 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -39,6 +39,9 @@ add_executable(yuzu
39 configuration/configure_debug.cpp 39 configuration/configure_debug.cpp
40 configuration/configure_debug.h 40 configuration/configure_debug.h
41 configuration/configure_debug.ui 41 configuration/configure_debug.ui
42 configuration/configure_debug_controller.cpp
43 configuration/configure_debug_controller.h
44 configuration/configure_debug_controller.ui
42 configuration/configure_dialog.cpp 45 configuration/configure_dialog.cpp
43 configuration/configure_dialog.h 46 configuration/configure_dialog.h
44 configuration/configure_filesystem.cpp 47 configuration/configure_filesystem.cpp
@@ -62,9 +65,9 @@ add_executable(yuzu
62 configuration/configure_input_player.cpp 65 configuration/configure_input_player.cpp
63 configuration/configure_input_player.h 66 configuration/configure_input_player.h
64 configuration/configure_input_player.ui 67 configuration/configure_input_player.ui
65 configuration/configure_input_simple.cpp 68 configuration/configure_input_advanced.cpp
66 configuration/configure_input_simple.h 69 configuration/configure_input_advanced.h
67 configuration/configure_input_simple.ui 70 configuration/configure_input_advanced.ui
68 configuration/configure_mouse_advanced.cpp 71 configuration/configure_mouse_advanced.cpp
69 configuration/configure_mouse_advanced.h 72 configuration/configure_mouse_advanced.h
70 configuration/configure_mouse_advanced.ui 73 configuration/configure_mouse_advanced.ui
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 8fc322b30..21707e451 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -304,8 +304,9 @@ static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow*
304 return wsi; 304 return wsi;
305} 305}
306 306
307GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_) 307GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
308 : QWidget(parent_), emu_thread(emu_thread_) { 308 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
309 : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)} {
309 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 310 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
310 .arg(QString::fromUtf8(Common::g_build_name), 311 .arg(QString::fromUtf8(Common::g_build_name),
311 QString::fromUtf8(Common::g_scm_branch), 312 QString::fromUtf8(Common::g_scm_branch),
@@ -314,15 +315,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
314 auto layout = new QHBoxLayout(this); 315 auto layout = new QHBoxLayout(this);
315 layout->setMargin(0); 316 layout->setMargin(0);
316 setLayout(layout); 317 setLayout(layout);
317 InputCommon::Init(); 318 input_subsystem->Initialize();
318 319
319 this->setMouseTracking(true); 320 this->setMouseTracking(true);
320 321
321 connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); 322 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
322} 323}
323 324
324GRenderWindow::~GRenderWindow() { 325GRenderWindow::~GRenderWindow() {
325 InputCommon::Shutdown(); 326 input_subsystem->Shutdown();
326} 327}
327 328
328void GRenderWindow::PollEvents() { 329void GRenderWindow::PollEvents() {
@@ -391,11 +392,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
391} 392}
392 393
393void GRenderWindow::keyPressEvent(QKeyEvent* event) { 394void GRenderWindow::keyPressEvent(QKeyEvent* event) {
394 InputCommon::GetKeyboard()->PressKey(event->key()); 395 input_subsystem->GetKeyboard()->PressKey(event->key());
395} 396}
396 397
397void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 398void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
398 InputCommon::GetKeyboard()->ReleaseKey(event->key()); 399 input_subsystem->GetKeyboard()->ReleaseKey(event->key());
399} 400}
400 401
401void GRenderWindow::mousePressEvent(QMouseEvent* event) { 402void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -409,7 +410,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
409 const auto [x, y] = ScaleTouch(pos); 410 const auto [x, y] = ScaleTouch(pos);
410 this->TouchPressed(x, y); 411 this->TouchPressed(x, y);
411 } else if (event->button() == Qt::RightButton) { 412 } else if (event->button() == Qt::RightButton) {
412 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); 413 input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
413 } 414 }
414 QWidget::mousePressEvent(event); 415 QWidget::mousePressEvent(event);
415} 416}
@@ -423,7 +424,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
423 auto pos = event->pos(); 424 auto pos = event->pos();
424 const auto [x, y] = ScaleTouch(pos); 425 const auto [x, y] = ScaleTouch(pos);
425 this->TouchMoved(x, y); 426 this->TouchMoved(x, y);
426 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 427 input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
427 QWidget::mouseMoveEvent(event); 428 QWidget::mouseMoveEvent(event);
428} 429}
429 430
@@ -436,7 +437,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
436 if (event->button() == Qt::LeftButton) { 437 if (event->button() == Qt::LeftButton) {
437 this->TouchReleased(); 438 this->TouchReleased();
438 } else if (event->button() == Qt::RightButton) { 439 } else if (event->button() == Qt::RightButton) {
439 InputCommon::GetMotionEmu()->EndTilt(); 440 input_subsystem->GetMotionEmu()->EndTilt();
440 } 441 }
441} 442}
442 443
@@ -451,7 +452,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
451 int active_points = 0; 452 int active_points = 0;
452 453
453 // average all active touch points 454 // average all active touch points
454 for (const auto tp : event->touchPoints()) { 455 for (const auto& tp : event->touchPoints()) {
455 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { 456 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
456 active_points++; 457 active_points++;
457 pos += tp.pos(); 458 pos += tp.pos();
@@ -485,7 +486,7 @@ bool GRenderWindow::event(QEvent* event) {
485 486
486void GRenderWindow::focusOutEvent(QFocusEvent* event) { 487void GRenderWindow::focusOutEvent(QFocusEvent* event) {
487 QWidget::focusOutEvent(event); 488 QWidget::focusOutEvent(event);
488 InputCommon::GetKeyboard()->ReleaseAllKeys(); 489 input_subsystem->GetKeyboard()->ReleaseAllKeys();
489} 490}
490 491
491void GRenderWindow::resizeEvent(QResizeEvent* event) { 492void GRenderWindow::resizeEvent(QResizeEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 6c59b4d5c..ca35cf831 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -6,6 +6,7 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <memory>
9#include <mutex> 10#include <mutex>
10 11
11#include <QImage> 12#include <QImage>
@@ -23,6 +24,10 @@ class QKeyEvent;
23class QTouchEvent; 24class QTouchEvent;
24class QStringList; 25class QStringList;
25 26
27namespace InputCommon {
28class InputSubsystem;
29}
30
26namespace VideoCore { 31namespace VideoCore {
27enum class LoadCallbackStage; 32enum class LoadCallbackStage;
28} 33}
@@ -121,7 +126,8 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
121 Q_OBJECT 126 Q_OBJECT
122 127
123public: 128public:
124 GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); 129 explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
130 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
125 ~GRenderWindow() override; 131 ~GRenderWindow() override;
126 132
127 // EmuWindow implementation. 133 // EmuWindow implementation.
@@ -183,6 +189,7 @@ private:
183 QStringList GetUnsupportedGLExtensions() const; 189 QStringList GetUnsupportedGLExtensions() const;
184 190
185 EmuThread* emu_thread; 191 EmuThread* emu_thread;
192 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
186 193
187 // Main context that will be shared with all other contexts that are requested. 194 // Main context that will be shared with all other contexts that are requested.
188 // If this is used in a shared context setting, then this should not be used directly, but 195 // If this is used in a shared context setting, then this should not be used directly, but
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7af974d8d..588bbd677 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -6,7 +6,6 @@
6#include <QKeySequence> 6#include <QKeySequence>
7#include <QSettings> 7#include <QSettings>
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "configure_input_simple.h"
10#include "core/hle/service/acc/profile_manager.h" 9#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/hid/controllers/npad.h" 10#include "core/hle/service/hid/controllers/npad.h"
12#include "input_common/main.h" 11#include "input_common/main.h"
@@ -32,29 +31,31 @@ Config::~Config() {
32} 31}
33 32
34const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 33const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
35 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 34 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
36 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, 35 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
37 Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J, 36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
38 Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
39}; 37};
40 38
41const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ 39const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
42 { 40 {
43 Qt::Key_Up, 41 Qt::Key_Up,
44 Qt::Key_Down, 42 Qt::Key_Down,
45 Qt::Key_Left, 43 Qt::Key_Left,
46 Qt::Key_Right, 44 Qt::Key_Right,
47 Qt::Key_E,
48 }, 45 },
49 { 46 {
50 Qt::Key_I, 47 Qt::Key_I,
51 Qt::Key_K, 48 Qt::Key_K,
52 Qt::Key_J, 49 Qt::Key_J,
53 Qt::Key_L, 50 Qt::Key_L,
54 Qt::Key_R,
55 }, 51 },
56}}; 52}};
57 53
54const std::array<int, 2> Config::default_stick_mod = {
55 Qt::Key_E,
56 Qt::Key_R,
57};
58
58const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons = 59const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
59 { 60 {
60 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal, 61 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
@@ -243,10 +244,10 @@ void Config::ReadPlayerValues() {
243 player.connected = 244 player.connected =
244 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); 245 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
245 246
246 player.type = static_cast<Settings::ControllerType>( 247 player.controller_type = static_cast<Settings::ControllerType>(
247 qt_config 248 qt_config
248 ->value(QStringLiteral("player_%1_type").arg(p), 249 ->value(QStringLiteral("player_%1_type").arg(p),
249 static_cast<u8>(Settings::ControllerType::DualJoycon)) 250 static_cast<u8>(Settings::ControllerType::ProController))
250 .toUInt()); 251 .toUInt());
251 252
252 player.body_color_left = qt_config 253 player.body_color_left = qt_config
@@ -286,7 +287,7 @@ void Config::ReadPlayerValues() {
286 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
287 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 288 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
288 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
289 default_analogs[i][3], default_analogs[i][4], 0.5f); 290 default_analogs[i][3], default_stick_mod[i], 0.5f);
290 auto& player_analogs = player.analogs[i]; 291 auto& player_analogs = player.analogs[i];
291 292
292 player_analogs = qt_config 293 player_analogs = qt_config
@@ -300,12 +301,6 @@ void Config::ReadPlayerValues() {
300 } 301 }
301 } 302 }
302 } 303 }
303
304 std::stable_partition(
305 Settings::values.players.begin(),
306 Settings::values.players.begin() +
307 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
308 [](const auto& player) { return player.connected; });
309} 304}
310 305
311void Config::ReadDebugValues() { 306void Config::ReadDebugValues() {
@@ -330,7 +325,7 @@ void Config::ReadDebugValues() {
330 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 325 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
331 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 326 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
332 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 327 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
333 default_analogs[i][3], default_analogs[i][4], 0.5f); 328 default_analogs[i][3], default_stick_mod[i], 0.5f);
334 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; 329 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
335 330
336 debug_pad_analogs = qt_config 331 debug_pad_analogs = qt_config
@@ -397,13 +392,6 @@ void Config::ReadTouchscreenValues() {
397 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); 392 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
398} 393}
399 394
400void Config::ApplyDefaultProfileIfInputInvalid() {
401 if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
402 [](const Settings::PlayerInput& in) { return in.connected; })) {
403 ApplyInputProfileConfiguration(UISettings::values.profile_index);
404 }
405}
406
407void Config::ReadAudioValues() { 395void Config::ReadAudioValues() {
408 qt_config->beginGroup(QStringLiteral("Audio")); 396 qt_config->beginGroup(QStringLiteral("Audio"));
409 397
@@ -433,6 +421,8 @@ void Config::ReadControlValues() {
433 ReadMouseValues(); 421 ReadMouseValues();
434 ReadTouchscreenValues(); 422 ReadTouchscreenValues();
435 423
424 Settings::values.vibration_enabled =
425 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
436 Settings::values.motion_device = 426 Settings::values.motion_device =
437 ReadSetting(QStringLiteral("motion_device"), 427 ReadSetting(QStringLiteral("motion_device"),
438 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) 428 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
@@ -501,7 +491,7 @@ void Config::ReadDataStorageValues() {
501 Settings::values.gamecard_current_game = 491 Settings::values.gamecard_current_game =
502 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); 492 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
503 Settings::values.gamecard_path = 493 Settings::values.gamecard_path =
504 ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); 494 ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
505 495
506 qt_config->endGroup(); 496 qt_config->endGroup();
507} 497}
@@ -515,7 +505,7 @@ void Config::ReadDebuggingValues() {
515 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); 505 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
516 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); 506 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
517 Settings::values.program_args = 507 Settings::values.program_args =
518 ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); 508 ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
519 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); 509 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
520 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); 510 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
521 Settings::values.reporting_services = 511 Settings::values.reporting_services =
@@ -548,8 +538,7 @@ void Config::ReadDisabledAddOnValues() {
548 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); 538 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
549 for (int j = 0; j < d_size; ++j) { 539 for (int j = 0; j < d_size; ++j) {
550 qt_config->setArrayIndex(j); 540 qt_config->setArrayIndex(j);
551 out.push_back( 541 out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
552 ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
553 } 542 }
554 qt_config->endArray(); 543 qt_config->endArray();
555 Settings::values.disabled_addons.insert_or_assign(title_id, out); 544 Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -788,14 +777,11 @@ void Config::ReadUIValues() {
788 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); 777 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
789 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); 778 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
790 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); 779 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
791 UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
792 UISettings::values.pause_when_in_background = 780 UISettings::values.pause_when_in_background =
793 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); 781 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
794 UISettings::values.hide_mouse = 782 UISettings::values.hide_mouse =
795 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool(); 783 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
796 784
797 ApplyDefaultProfileIfInputInvalid();
798
799 qt_config->endGroup(); 785 qt_config->endGroup();
800} 786}
801 787
@@ -869,8 +855,9 @@ void Config::SavePlayerValues() {
869 const auto& player = Settings::values.players[p]; 855 const auto& player = Settings::values.players[p];
870 856
871 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); 857 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
872 WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type), 858 WriteSetting(QStringLiteral("player_%1_type").arg(p),
873 static_cast<u8>(Settings::ControllerType::DualJoycon)); 859 static_cast<u8>(player.controller_type),
860 static_cast<u8>(Settings::ControllerType::ProController));
874 861
875 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, 862 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
876 Settings::JOYCON_BODY_NEON_BLUE); 863 Settings::JOYCON_BODY_NEON_BLUE);
@@ -892,7 +879,7 @@ void Config::SavePlayerValues() {
892 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 879 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
893 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 880 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
894 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 881 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
895 default_analogs[i][3], default_analogs[i][4], 0.5f); 882 default_analogs[i][3], default_stick_mod[i], 0.5f);
896 WriteSetting(QStringLiteral("player_%1_").arg(p) + 883 WriteSetting(QStringLiteral("player_%1_").arg(p) +
897 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 884 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
898 QString::fromStdString(player.analogs[i]), 885 QString::fromStdString(player.analogs[i]),
@@ -913,7 +900,7 @@ void Config::SaveDebugValues() {
913 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 900 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
914 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 901 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
915 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 902 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
916 default_analogs[i][3], default_analogs[i][4], 0.5f); 903 default_analogs[i][3], default_stick_mod[i], 0.5f);
917 WriteSetting(QStringLiteral("debug_pad_") + 904 WriteSetting(QStringLiteral("debug_pad_") +
918 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 905 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
919 QString::fromStdString(Settings::values.debug_pad_analogs[i]), 906 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
@@ -990,6 +977,7 @@ void Config::SaveControlValues() {
990 SaveMouseValues(); 977 SaveMouseValues();
991 SaveTouchscreenValues(); 978 SaveTouchscreenValues();
992 979
980 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
993 WriteSetting(QStringLiteral("motion_device"), 981 WriteSetting(QStringLiteral("motion_device"),
994 QString::fromStdString(Settings::values.motion_device), 982 QString::fromStdString(Settings::values.motion_device),
995 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); 983 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1036,7 +1024,7 @@ void Config::SaveDataStorageValues() {
1036 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, 1024 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
1037 false); 1025 false);
1038 WriteSetting(QStringLiteral("gamecard_path"), 1026 WriteSetting(QStringLiteral("gamecard_path"),
1039 QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); 1027 QString::fromStdString(Settings::values.gamecard_path), QString{});
1040 1028
1041 qt_config->endGroup(); 1029 qt_config->endGroup();
1042} 1030}
@@ -1049,7 +1037,7 @@ void Config::SaveDebuggingValues() {
1049 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); 1037 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
1050 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); 1038 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
1051 WriteSetting(QStringLiteral("program_args"), 1039 WriteSetting(QStringLiteral("program_args"),
1052 QString::fromStdString(Settings::values.program_args), QStringLiteral("")); 1040 QString::fromStdString(Settings::values.program_args), QString{});
1053 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); 1041 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
1054 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 1042 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
1055 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); 1043 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
@@ -1076,8 +1064,7 @@ void Config::SaveDisabledAddOnValues() {
1076 qt_config->beginWriteArray(QStringLiteral("disabled")); 1064 qt_config->beginWriteArray(QStringLiteral("disabled"));
1077 for (std::size_t j = 0; j < elem.second.size(); ++j) { 1065 for (std::size_t j = 0; j < elem.second.size(); ++j) {
1078 qt_config->setArrayIndex(static_cast<int>(j)); 1066 qt_config->setArrayIndex(static_cast<int>(j));
1079 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), 1067 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
1080 QStringLiteral(""));
1081 } 1068 }
1082 qt_config->endArray(); 1069 qt_config->endArray();
1083 ++i; 1070 ++i;
@@ -1266,7 +1253,6 @@ void Config::SaveUIValues() {
1266 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); 1253 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
1267 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); 1254 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
1268 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); 1255 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
1269 WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
1270 WriteSetting(QStringLiteral("pauseWhenInBackground"), 1256 WriteSetting(QStringLiteral("pauseWhenInBackground"),
1271 UISettings::values.pause_when_in_background, false); 1257 UISettings::values.pause_when_in_background, false);
1272 WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false); 1258 WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index e5f39b040..aa929d134 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -23,7 +23,8 @@ public:
23 void Save(); 23 void Save();
24 24
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 26 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
27 static const std::array<int, 2> default_stick_mod;
27 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 28 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
28 default_mouse_buttons; 29 default_mouse_buttons;
29 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 30 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -37,7 +38,6 @@ private:
37 void ReadKeyboardValues(); 38 void ReadKeyboardValues();
38 void ReadMouseValues(); 39 void ReadMouseValues();
39 void ReadTouchscreenValues(); 40 void ReadTouchscreenValues();
40 void ApplyDefaultProfileIfInputInvalid();
41 41
42 // Read functions bases off the respective config section names. 42 // Read functions bases off the respective config section names.
43 void ReadAudioValues(); 43 void ReadAudioValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 5f5d8e571..fcf42cdcb 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>382</width> 9 <width>650</width>
10 <height>650</height> 10 <height>650</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -26,13 +26,13 @@
26 <widget class="QListWidget" name="selectorList"> 26 <widget class="QListWidget" name="selectorList">
27 <property name="minimumSize"> 27 <property name="minimumSize">
28 <size> 28 <size>
29 <width>150</width> 29 <width>120</width>
30 <height>0</height> 30 <height>0</height>
31 </size> 31 </size>
32 </property> 32 </property>
33 <property name="maximumSize"> 33 <property name="maximumSize">
34 <size> 34 <size>
35 <width>150</width> 35 <width>120</width>
36 <height>16777215</height> 36 <height>16777215</height>
37 </size> 37 </size>
38 </property> 38 </property>
@@ -44,76 +44,121 @@
44 <number>0</number> 44 <number>0</number>
45 </property> 45 </property>
46 <widget class="ConfigureGeneral" name="generalTab"> 46 <widget class="ConfigureGeneral" name="generalTab">
47 <property name="accessibleName">
48 <string>General</string>
49 </property>
47 <attribute name="title"> 50 <attribute name="title">
48 <string>General</string> 51 <string>General</string>
49 </attribute> 52 </attribute>
50 </widget> 53 </widget>
51 <widget class="ConfigureUi" name="uiTab"> 54 <widget class="ConfigureUi" name="uiTab">
55 <property name="accessibleName">
56 <string>UI</string>
57 </property>
52 <attribute name="title"> 58 <attribute name="title">
53 <string>Game List</string> 59 <string>Game List</string>
54 </attribute> 60 </attribute>
55 </widget> 61 </widget>
56 <widget class="ConfigureSystem" name="systemTab"> 62 <widget class="ConfigureSystem" name="systemTab">
63 <property name="accessibleName">
64 <string>System</string>
65 </property>
57 <attribute name="title"> 66 <attribute name="title">
58 <string>System</string> 67 <string>System</string>
59 </attribute> 68 </attribute>
60 </widget> 69 </widget>
61 <widget class="ConfigureProfileManager" name="profileManagerTab"> 70 <widget class="ConfigureProfileManager" name="profileManagerTab">
71 <property name="accessibleName">
72 <string>Profiles</string>
73 </property>
62 <attribute name="title"> 74 <attribute name="title">
63 <string>Profiles</string> 75 <string>Profiles</string>
64 </attribute> 76 </attribute>
65 </widget> 77 </widget>
66 <widget class="ConfigureFilesystem" name="filesystemTab"> 78 <widget class="ConfigureFilesystem" name="filesystemTab">
79 <property name="accessibleName">
80 <string>Filesystem</string>
81 </property>
67 <attribute name="title"> 82 <attribute name="title">
68 <string>Filesystem</string> 83 <string>Filesystem</string>
69 </attribute> 84 </attribute>
70 </widget> 85 </widget>
71 <widget class="ConfigureInputSimple" name="inputTab"> 86 <widget class="ConfigureInput" name="inputTab">
87 <property name="accessibleName">
88 <string>Controls</string>
89 </property>
72 <attribute name="title"> 90 <attribute name="title">
73 <string>Input</string> 91 <string>Controls</string>
74 </attribute> 92 </attribute>
75 </widget> 93 </widget>
76 <widget class="ConfigureHotkeys" name="hotkeysTab"> 94 <widget class="ConfigureHotkeys" name="hotkeysTab">
95 <property name="accessibleName">
96 <string>Hotkeys</string>
97 </property>
77 <attribute name="title"> 98 <attribute name="title">
78 <string>Hotkeys</string> 99 <string>Hotkeys</string>
79 </attribute> 100 </attribute>
80 </widget> 101 </widget>
81 <widget class="ConfigureCpu" name="cpuTab"> 102 <widget class="ConfigureCpu" name="cpuTab">
103 <property name="accessibleName">
104 <string>CPU</string>
105 </property>
82 <attribute name="title"> 106 <attribute name="title">
83 <string>CPU</string> 107 <string>CPU</string>
84 </attribute> 108 </attribute>
85 </widget> 109 </widget>
86 <widget class="ConfigureCpuDebug" name="cpuDebugTab"> 110 <widget class="ConfigureCpuDebug" name="cpuDebugTab">
111 <property name="accessibleName">
112 <string>Debug</string>
113 </property>
87 <attribute name="title"> 114 <attribute name="title">
88 <string>Debug</string> 115 <string>Debug</string>
89 </attribute> 116 </attribute>
90 </widget> 117 </widget>
91 <widget class="ConfigureGraphics" name="graphicsTab"> 118 <widget class="ConfigureGraphics" name="graphicsTab">
119 <property name="accessibleName">
120 <string>Graphics</string>
121 </property>
92 <attribute name="title"> 122 <attribute name="title">
93 <string>Graphics</string> 123 <string>Graphics</string>
94 </attribute> 124 </attribute>
95 </widget> 125 </widget>
96 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab"> 126 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
127 <property name="accessibleName">
128 <string>Advanced</string>
129 </property>
97 <attribute name="title"> 130 <attribute name="title">
98 <string>GraphicsAdvanced</string> 131 <string>GraphicsAdvanced</string>
99 </attribute> 132 </attribute>
100 </widget> 133 </widget>
101 <widget class="ConfigureAudio" name="audioTab"> 134 <widget class="ConfigureAudio" name="audioTab">
135 <property name="accessibleName">
136 <string>Audio</string>
137 </property>
102 <attribute name="title"> 138 <attribute name="title">
103 <string>Audio</string> 139 <string>Audio</string>
104 </attribute> 140 </attribute>
105 </widget> 141 </widget>
106 <widget class="ConfigureDebug" name="debugTab"> 142 <widget class="ConfigureDebug" name="debugTab">
143 <property name="accessibleName">
144 <string>Debug</string>
145 </property>
107 <attribute name="title"> 146 <attribute name="title">
108 <string>Debug</string> 147 <string>Debug</string>
109 </attribute> 148 </attribute>
110 </widget> 149 </widget>
111 <widget class="ConfigureWeb" name="webTab"> 150 <widget class="ConfigureWeb" name="webTab">
151 <property name="accessibleName">
152 <string>Web</string>
153 </property>
112 <attribute name="title"> 154 <attribute name="title">
113 <string>Web</string> 155 <string>Web</string>
114 </attribute> 156 </attribute>
115 </widget> 157 </widget>
116 <widget class="ConfigureService" name="serviceTab"> 158 <widget class="ConfigureService" name="serviceTab">
159 <property name="accessibleName">
160 <string>Services</string>
161 </property>
117 <attribute name="title"> 162 <attribute name="title">
118 <string>Services</string> 163 <string>Services</string>
119 </attribute> 164 </attribute>
@@ -205,9 +250,9 @@
205 <container>1</container> 250 <container>1</container>
206 </customwidget> 251 </customwidget>
207 <customwidget> 252 <customwidget>
208 <class>ConfigureInputSimple</class> 253 <class>ConfigureInput</class>
209 <extends>QWidget</extends> 254 <extends>QWidget</extends>
210 <header>configuration/configure_input_simple.h</header> 255 <header>configuration/configure_input.h</header>
211 <container>1</container> 256 <container>1</container>
212 </customwidget> 257 </customwidget>
213 <customwidget> 258 <customwidget>
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
new file mode 100644
index 000000000..0097c9a29
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -0,0 +1,40 @@
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 "ui_configure_debug_controller.h"
6#include "yuzu/configuration/configure_debug_controller.h"
7
8ConfigureDebugController::ConfigureDebugController(QWidget* parent,
9 InputCommon::InputSubsystem* input_subsystem)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
11 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
12 ui->setupUi(this);
13
14 ui->controllerLayout->addWidget(debug_controller);
15
16 connect(ui->clear_all_button, &QPushButton::clicked, this,
17 [this] { debug_controller->ClearAll(); });
18 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
19 [this] { debug_controller->RestoreDefaults(); });
20
21 RetranslateUI();
22}
23
24ConfigureDebugController::~ConfigureDebugController() = default;
25
26void ConfigureDebugController::ApplyConfiguration() {
27 debug_controller->ApplyConfiguration();
28}
29
30void ConfigureDebugController::changeEvent(QEvent* event) {
31 if (event->type() == QEvent::LanguageChange) {
32 RetranslateUI();
33 }
34
35 QDialog::changeEvent(event);
36}
37
38void ConfigureDebugController::RetranslateUI() {
39 ui->retranslateUi(this);
40}
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
new file mode 100644
index 000000000..34dcf705f
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -0,0 +1,38 @@
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 <QDialog>
9#include "yuzu/configuration/configure_input_player.h"
10
11class QPushButton;
12
13namespace InputCommon {
14class InputSubsystem;
15}
16
17namespace Ui {
18class ConfigureDebugController;
19}
20
21class ConfigureDebugController : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureDebugController(QWidget* parent,
26 InputCommon::InputSubsystem* input_subsystem);
27 ~ConfigureDebugController() override;
28
29 void ApplyConfiguration();
30
31private:
32 void changeEvent(QEvent* event) override;
33 void RetranslateUI();
34
35 std::unique_ptr<Ui::ConfigureDebugController> ui;
36
37 ConfigureInputPlayer* debug_controller;
38};
diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui
new file mode 100644
index 000000000..a95ed50ff
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.ui
@@ -0,0 +1,97 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureDebugController</class>
4 <widget class="QDialog" name="ConfigureDebugController">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>780</width>
10 <height>500</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Debug Controller</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>9</number>
22 </property>
23 <property name="topMargin">
24 <number>9</number>
25 </property>
26 <property name="rightMargin">
27 <number>9</number>
28 </property>
29 <property name="bottomMargin">
30 <number>9</number>
31 </property>
32 <item>
33 <layout class="QHBoxLayout" name="controllerLayout"/>
34 </item>
35 <item>
36 <layout class="QHBoxLayout" name="horizontalLayout">
37 <item>
38 <widget class="QPushButton" name="clear_all_button">
39 <property name="text">
40 <string>Clear</string>
41 </property>
42 </widget>
43 </item>
44 <item>
45 <widget class="QPushButton" name="restore_defaults_button">
46 <property name="text">
47 <string>Defaults</string>
48 </property>
49 </widget>
50 </item>
51 <item>
52 <widget class="QDialogButtonBox" name="buttonBox">
53 <property name="standardButtons">
54 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
55 </property>
56 </widget>
57 </item>
58 </layout>
59 </item>
60 </layout>
61 </widget>
62 <resources/>
63 <connections>
64 <connection>
65 <sender>buttonBox</sender>
66 <signal>accepted()</signal>
67 <receiver>ConfigureDebugController</receiver>
68 <slot>accept()</slot>
69 <hints>
70 <hint type="sourcelabel">
71 <x>140</x>
72 <y>318</y>
73 </hint>
74 <hint type="destinationlabel">
75 <x>140</x>
76 <y>169</y>
77 </hint>
78 </hints>
79 </connection>
80 <connection>
81 <sender>buttonBox</sender>
82 <signal>rejected()</signal>
83 <receiver>ConfigureDebugController</receiver>
84 <slot>reject()</slot>
85 <hints>
86 <hint type="sourcelabel">
87 <x>140</x>
88 <y>318</y>
89 </hint>
90 <hint type="destinationlabel">
91 <x>140</x>
92 <y>169</y>
93 </hint>
94 </hints>
95 </connection>
96 </connections>
97</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4e30dc51e..8186929a6 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -12,7 +12,8 @@
12#include "yuzu/configuration/configure_input_player.h" 12#include "yuzu/configuration/configure_input_player.h"
13#include "yuzu/hotkeys.h" 13#include "yuzu/hotkeys.h"
14 14
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
16 InputCommon::InputSubsystem* input_subsystem)
16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
17 Settings::configuring_global = true; 18 Settings::configuring_global = true;
18 19
@@ -20,6 +21,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
20 ui->hotkeysTab->Populate(registry); 21 ui->hotkeysTab->Populate(registry);
21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 22 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
22 23
24 ui->inputTab->Initialize(input_subsystem);
25
23 SetConfiguration(); 26 SetConfiguration();
24 PopulateSelectionList(); 27 PopulateSelectionList();
25 28
@@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
80 83
81void ConfigureDialog::PopulateSelectionList() { 84void ConfigureDialog::PopulateSelectionList() {
82 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 85 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
83 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, 86 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
84 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 87 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
85 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, 88 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
86 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 89 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
87 {tr("Audio"), {ui->audioTab}}, 90 {tr("Audio"), {ui->audioTab}},
88 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 91 {tr("Controls"), ui->inputTab->GetSubTabs()}},
89 }; 92 };
90 93
91 [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList); 94 [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
@@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
117 {ui->generalTab, tr("General")}, 120 {ui->generalTab, tr("General")},
118 {ui->systemTab, tr("System")}, 121 {ui->systemTab, tr("System")},
119 {ui->profileManagerTab, tr("Profiles")}, 122 {ui->profileManagerTab, tr("Profiles")},
120 {ui->inputTab, tr("Input")}, 123 {ui->inputTab, tr("Controls")},
121 {ui->hotkeysTab, tr("Hotkeys")}, 124 {ui->hotkeysTab, tr("Hotkeys")},
122 {ui->cpuTab, tr("CPU")}, 125 {ui->cpuTab, tr("CPU")},
123 {ui->cpuDebugTab, tr("Debug")}, 126 {ui->cpuDebugTab, tr("Debug")},
@@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() {
138 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); 141 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
139 142
140 for (const auto tab : tabs) { 143 for (const auto tab : tabs) {
141 ui->tabWidget->addTab(tab, widgets.at(tab)); 144 ui->tabWidget->addTab(tab, tab->accessibleName());
142 } 145 }
143} 146}
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 4289bc225..570c3b941 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -9,6 +9,10 @@
9 9
10class HotkeyRegistry; 10class HotkeyRegistry;
11 11
12namespace InputCommon {
13class InputSubsystem;
14}
15
12namespace Ui { 16namespace Ui {
13class ConfigureDialog; 17class ConfigureDialog;
14} 18}
@@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog {
17 Q_OBJECT 21 Q_OBJECT
18 22
19public: 23public:
20 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); 24 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
25 InputCommon::InputSubsystem* input_subsystem);
21 ~ConfigureDialog() override; 26 ~ConfigureDialog() override;
22 27
23 void ApplyConfiguration(); 28 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index f2977719c..5223eed1d 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -8,18 +8,32 @@
8#include <QSignalBlocker> 8#include <QSignalBlocker>
9#include <QTimer> 9#include <QTimer>
10 10
11#include "configuration/configure_touchscreen_advanced.h"
12#include "core/core.h" 11#include "core/core.h"
13#include "core/hle/service/am/am.h" 12#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applet_ae.h" 13#include "core/hle/service/am/applet_ae.h"
15#include "core/hle/service/am/applet_oe.h" 14#include "core/hle/service/am/applet_oe.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/sm/sm.h" 15#include "core/hle/service/sm/sm.h"
18#include "ui_configure_input.h" 16#include "ui_configure_input.h"
17#include "ui_configure_input_advanced.h"
19#include "ui_configure_input_player.h" 18#include "ui_configure_input_player.h"
19#include "yuzu/configuration/configure_debug_controller.h"
20#include "yuzu/configuration/configure_input.h" 20#include "yuzu/configuration/configure_input.h"
21#include "yuzu/configuration/configure_input_advanced.h"
21#include "yuzu/configuration/configure_input_player.h" 22#include "yuzu/configuration/configure_input_player.h"
22#include "yuzu/configuration/configure_mouse_advanced.h" 23#include "yuzu/configuration/configure_mouse_advanced.h"
24#include "yuzu/configuration/configure_touchscreen_advanced.h"
25
26namespace {
27template <typename Dialog, typename... Args>
28void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
29 Dialog dialog(&parent, std::forward<Args>(args)...);
30
31 const auto res = dialog.exec();
32 if (res == QDialog::Accepted) {
33 dialog.ApplyConfiguration();
34 }
35}
36} // Anonymous namespace
23 37
24void OnDockedModeChanged(bool last_state, bool new_state) { 38void OnDockedModeChanged(bool last_state, bool new_state) {
25 if (last_state == new_state) { 39 if (last_state == new_state) {
@@ -48,97 +62,98 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
48 } 62 }
49} 63}
50 64
51namespace {
52template <typename Dialog, typename... Args>
53void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
54 parent.ApplyConfiguration();
55 Dialog dialog(&parent, std::forward<Args>(args)...);
56
57 const auto res = dialog.exec();
58 if (res == QDialog::Accepted) {
59 dialog.ApplyConfiguration();
60 }
61}
62} // Anonymous namespace
63
64ConfigureInput::ConfigureInput(QWidget* parent) 65ConfigureInput::ConfigureInput(QWidget* parent)
65 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) { 66 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
66 ui->setupUi(this); 67 ui->setupUi(this);
68}
67 69
68 players_controller = { 70ConfigureInput::~ConfigureInput() = default;
69 ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
70 ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
71 };
72 71
73 players_configure = { 72void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
74 ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure, 73 player_controllers = {
75 ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure, 74 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
75 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
76 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
77 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
78 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
79 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
80 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
81 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
76 }; 82 };
77 83
78 RetranslateUI(); 84 player_tabs = {
79 LoadConfiguration(); 85 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
80 UpdateUIEnabled(); 86 ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
87 };
81 88
82 connect(ui->restore_defaults_button, &QPushButton::clicked, this, 89 player_connected = {
83 &ConfigureInput::RestoreDefaults); 90 ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
91 ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
92 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
93 };
84 94
85 for (auto* enabled : players_controller) { 95 for (std::size_t i = 0; i < player_tabs.size(); ++i) {
86 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 96 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
87 &ConfigureInput::UpdateUIEnabled); 97 player_tabs[i]->layout()->addWidget(player_controllers[i]);
88 } 98 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
89 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 99 if (is_connected) {
90 connect(ui->handheld_connected, &QCheckBox::stateChanged, this, 100 for (std::size_t index = 0; index <= i; ++index) {
91 &ConfigureInput::UpdateUIEnabled); 101 player_connected[index]->setChecked(is_connected);
92 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 102 }
93 connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 103 } else {
94 connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 104 for (std::size_t index = i; index < player_tabs.size(); ++index) {
95 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, 105 player_connected[index]->setChecked(is_connected);
96 &ConfigureInput::UpdateUIEnabled); 106 }
97 107 }
98 for (std::size_t i = 0; i < players_configure.size(); ++i) { 108 });
99 connect(players_configure[i], &QPushButton::clicked, this, 109 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
100 [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); }); 110 [this] { UpdateAllInputDevices(); });
111 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
112 player_controllers[i]->ConnectPlayer(state == Qt::Checked);
113 });
101 } 114 }
115 // Only the first player can choose handheld mode so connect the signal just to player 1
116 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
117 [this](bool is_handheld) { UpdateDockedState(is_handheld); });
118
119 advanced = new ConfigureInputAdvanced(this);
120 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
121 ui->tabAdvanced->layout()->addWidget(advanced);
122 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
123 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
124 });
125 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
126 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
127 });
128 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
129 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
102 130
103 connect(ui->handheld_configure, &QPushButton::clicked, this, 131 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
104 [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); }); 132 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
105
106 connect(ui->debug_configure, &QPushButton::clicked, this,
107 [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
108
109 connect(ui->mouse_advanced, &QPushButton::clicked, this,
110 [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
111 133
112 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 134 RetranslateUI();
113 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); 135 LoadConfiguration();
114} 136}
115 137
116ConfigureInput::~ConfigureInput() = default; 138QList<QWidget*> ConfigureInput::GetSubTabs() const {
139 return {
140 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
141 ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
142 };
143}
117 144
118void ConfigureInput::ApplyConfiguration() { 145void ConfigureInput::ApplyConfiguration() {
119 for (std::size_t i = 0; i < players_controller.size(); ++i) { 146 for (auto controller : player_controllers) {
120 const auto controller_type_index = players_controller[i]->currentIndex(); 147 controller->ApplyConfiguration();
121
122 Settings::values.players[i].connected = controller_type_index != 0;
123
124 if (controller_type_index > 0) {
125 Settings::values.players[i].type =
126 static_cast<Settings::ControllerType>(controller_type_index - 1);
127 } else {
128 Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
129 }
130 } 148 }
131 149
150 advanced->ApplyConfiguration();
151
132 const bool pre_docked_mode = Settings::values.use_docked_mode; 152 const bool pre_docked_mode = Settings::values.use_docked_mode;
133 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 153 Settings::values.use_docked_mode = ui->radioDocked->isChecked();
134 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 154 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
135 Settings::values 155
136 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] 156 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
137 .connected = ui->handheld_connected->isChecked();
138 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
139 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
140 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
141 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
142} 157}
143 158
144void ConfigureInput::changeEvent(QEvent* event) { 159void ConfigureInput::changeEvent(QEvent* event) {
@@ -146,94 +161,63 @@ void ConfigureInput::changeEvent(QEvent* event) {
146 RetranslateUI(); 161 RetranslateUI();
147 } 162 }
148 163
149 QDialog::changeEvent(event); 164 QWidget::changeEvent(event);
150} 165}
151 166
152void ConfigureInput::RetranslateUI() { 167void ConfigureInput::RetranslateUI() {
153 ui->retranslateUi(this); 168 ui->retranslateUi(this);
154 RetranslateControllerComboBoxes();
155} 169}
156 170
157void ConfigureInput::RetranslateControllerComboBoxes() { 171void ConfigureInput::LoadConfiguration() {
158 for (auto* controller_box : players_controller) {
159 [[maybe_unused]] const QSignalBlocker blocker(controller_box);
160
161 controller_box->clear();
162 controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
163 tr("Single Right Joycon"), tr("Single Left Joycon")});
164 }
165
166 LoadPlayerControllerIndices(); 172 LoadPlayerControllerIndices();
167} 173 UpdateDockedState(Settings::values.players[0].controller_type ==
174 Settings::ControllerType::Handheld);
168 175
169void ConfigureInput::UpdateUIEnabled() { 176 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
170 bool hit_disabled = false; 177}
171 for (auto* player : players_controller) {
172 player->setDisabled(hit_disabled);
173 if (hit_disabled) {
174 player->setCurrentIndex(0);
175 }
176 if (!hit_disabled && player->currentIndex() == 0) {
177 hit_disabled = true;
178 }
179 }
180 178
181 for (std::size_t i = 0; i < players_controller.size(); ++i) { 179void ConfigureInput::LoadPlayerControllerIndices() {
182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); 180 for (std::size_t i = 0; i < player_connected.size(); ++i) {
181 const auto connected = Settings::values.players[i].connected ||
182 (i == 0 && Settings::values.players[8].connected);
183 player_connected[i]->setChecked(connected);
183 } 184 }
185}
184 186
185 ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() && 187void ConfigureInput::ClearAll() {
186 !ui->use_docked_mode->isChecked()); 188 // We don't have a good way to know what tab is active, but we can find out by getting the
187 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); 189 // parent of the consoleInputSettings
188 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && 190 auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
189 !ui->use_docked_mode->isChecked()); 191 player_tab->ClearAll();
190 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
191 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
192 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
193} 192}
194 193
195void ConfigureInput::LoadConfiguration() { 194void ConfigureInput::RestoreDefaults() {
196 std::stable_partition( 195 // We don't have a good way to know what tab is active, but we can find out by getting the
197 Settings::values.players.begin(), 196 // parent of the consoleInputSettings
198 Settings::values.players.begin() + 197 auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
199 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), 198 player_tab->RestoreDefaults();
200 [](const auto& player) { return player.connected; }); 199
200 ui->radioDocked->setChecked(true);
201 ui->radioUndocked->setChecked(false);
202 ui->vibrationGroup->setChecked(true);
203}
201 204
202 LoadPlayerControllerIndices(); 205void ConfigureInput::UpdateDockedState(bool is_handheld) {
206 // If the controller type is handheld only, disallow changing docked mode
207 ui->radioDocked->setEnabled(!is_handheld);
208 ui->radioUndocked->setEnabled(!is_handheld);
203 209
204 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 210 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
205 ui->handheld_connected->setChecked( 211 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
206 Settings::values
207 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
208 .connected);
209 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
210 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
211 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
212 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
213
214 UpdateUIEnabled();
215}
216 212
217void ConfigureInput::LoadPlayerControllerIndices() { 213 // If its handheld only, force docked mode off (since you can't play handheld in a dock)
218 for (std::size_t i = 0; i < players_controller.size(); ++i) { 214 if (is_handheld) {
219 const auto connected = Settings::values.players[i].connected; 215 ui->radioUndocked->setChecked(true);
220 players_controller[i]->setCurrentIndex(
221 connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
222 } 216 }
223} 217}
224 218
225void ConfigureInput::RestoreDefaults() { 219void ConfigureInput::UpdateAllInputDevices() {
226 players_controller[0]->setCurrentIndex(2); 220 for (const auto& player : player_controllers) {
227 221 player->UpdateInputDevices();
228 for (std::size_t i = 1; i < players_controller.size(); ++i) {
229 players_controller[i]->setCurrentIndex(0);
230 } 222 }
231
232 ui->use_docked_mode->setCheckState(Qt::Unchecked);
233 ui->handheld_connected->setCheckState(Qt::Unchecked);
234 ui->mouse_enabled->setCheckState(Qt::Unchecked);
235 ui->keyboard_enabled->setCheckState(Qt::Unchecked);
236 ui->debug_enabled->setCheckState(Qt::Unchecked);
237 ui->touchscreen_enabled->setCheckState(Qt::Checked);
238 UpdateUIEnabled();
239} 223}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 2f70cb3ca..d08a24f96 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,37 +7,50 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9 9
10#include <QDialog>
11#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QWidget>
12
13#include "yuzu/configuration/configure_input_advanced.h"
14#include "yuzu/configuration/configure_input_player.h"
12 15
13#include "ui_configure_input.h" 16#include "ui_configure_input.h"
14 17
15class QPushButton; 18class QCheckBox;
16class QString; 19class QString;
17class QTimer; 20class QTimer;
18 21
22namespace InputCommon {
23class InputSubsystem;
24}
25
19namespace Ui { 26namespace Ui {
20class ConfigureInput; 27class ConfigureInput;
21} 28}
22 29
23void OnDockedModeChanged(bool last_state, bool new_state); 30void OnDockedModeChanged(bool last_state, bool new_state);
24 31
25class ConfigureInput : public QDialog { 32class ConfigureInput : public QWidget {
26 Q_OBJECT 33 Q_OBJECT
27 34
28public: 35public:
29 explicit ConfigureInput(QWidget* parent = nullptr); 36 explicit ConfigureInput(QWidget* parent = nullptr);
30 ~ConfigureInput() override; 37 ~ConfigureInput() override;
31 38
32 /// Save all button configurations to settings file 39 /// Initializes the input dialog with the given input subsystem.
40 void Initialize(InputCommon::InputSubsystem* input_subsystem_);
41
42 /// Save all button configurations to settings file.
33 void ApplyConfiguration(); 43 void ApplyConfiguration();
34 44
45 QList<QWidget*> GetSubTabs() const;
46
35private: 47private:
36 void changeEvent(QEvent* event) override; 48 void changeEvent(QEvent* event) override;
37 void RetranslateUI(); 49 void RetranslateUI();
38 void RetranslateControllerComboBoxes(); 50 void ClearAll();
39 51
40 void UpdateUIEnabled(); 52 void UpdateDockedState(bool is_handheld);
53 void UpdateAllInputDevices();
41 54
42 /// Load configuration settings. 55 /// Load configuration settings.
43 void LoadConfiguration(); 56 void LoadConfiguration();
@@ -48,6 +61,8 @@ private:
48 61
49 std::unique_ptr<Ui::ConfigureInput> ui; 62 std::unique_ptr<Ui::ConfigureInput> ui;
50 63
51 std::array<QComboBox*, 8> players_controller; 64 std::array<ConfigureInputPlayer*, 8> player_controllers;
52 std::array<QPushButton*, 8> players_configure; 65 std::array<QWidget*, 8> player_tabs;
66 std::array<QCheckBox*, 8> player_connected;
67 ConfigureInputAdvanced* advanced;
53}; 68};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index efffd8487..136955224 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -1,529 +1,554 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInput</class> 3 <class>ConfigureInput</class>
4 <widget class="QDialog" name="ConfigureInput"> 4 <widget class="QWidget" name="ConfigureInput">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>384</width> 9 <width>700</width>
10 <height>576</height> 10 <height>540</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Custom Input Settings</string> 14 <string>ConfigureInput</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>3</number>
22 </property>
23 <property name="topMargin">
24 <number>3</number>
25 </property>
26 <property name="rightMargin">
27 <number>3</number>
28 </property>
29 <property name="bottomMargin">
30 <number>3</number>
31 </property>
17 <item> 32 <item>
18 <layout class="QVBoxLayout" name="verticalLayout"> 33 <widget class="QTabWidget" name="tabWidget">
19 <item> 34 <property name="currentIndex">
20 <widget class="QGroupBox" name="gridGroupBox_1"> 35 <number>0</number>
21 <property name="title"> 36 </property>
22 <string>Players</string> 37 <widget class="QWidget" name="tabPlayer1">
23 </property> 38 <property name="accessibleName">
24 <layout class="QGridLayout" name="gridLayout"> 39 <string>Player 1</string>
25 <item row="1" column="2"> 40 </property>
26 <widget class="QComboBox" name="player1_combobox"> 41 <attribute name="title">
27 <property name="minimumSize"> 42 <string>Player 1</string>
28 <size> 43 </attribute>
29 <width>110</width> 44 </widget>
30 <height>0</height> 45 <widget class="QWidget" name="tabPlayer2">
31 </size> 46 <property name="accessibleName">
32 </property> 47 <string>Player 2</string>
33 </widget> 48 </property>
34 </item> 49 <attribute name="title">
35 <item row="1" column="3"> 50 <string>Player 2</string>
36 <widget class="QPushButton" name="player1_configure"> 51 </attribute>
37 <property name="text"> 52 </widget>
38 <string>Configure</string> 53 <widget class="QWidget" name="tabPlayer3">
39 </property> 54 <property name="accessibleName">
40 </widget> 55 <string>Player 3</string>
41 </item> 56 </property>
42 <item row="0" column="2"> 57 <attribute name="title">
43 <widget class="QLabel" name="label"> 58 <string>Player 3</string>
44 <property name="text"> 59 </attribute>
45 <string>Controller Type</string> 60 </widget>
46 </property> 61 <widget class="QWidget" name="tabPlayer4">
47 <property name="alignment"> 62 <property name="accessibleName">
48 <set>Qt::AlignCenter</set> 63 <string>Player 4</string>
49 </property> 64 </property>
50 </widget> 65 <attribute name="title">
51 </item> 66 <string>Player 4</string>
52 <item row="2" column="2"> 67 </attribute>
53 <widget class="QComboBox" name="player2_combobox"> 68 </widget>
54 <property name="minimumSize"> 69 <widget class="QWidget" name="tabPlayer5">
55 <size> 70 <property name="accessibleName">
56 <width>110</width> 71 <string>Player 5</string>
57 <height>0</height> 72 </property>
58 </size> 73 <attribute name="title">
59 </property> 74 <string>Player 5</string>
60 </widget> 75 </attribute>
61 </item> 76 </widget>
62 <item row="3" column="2"> 77 <widget class="QWidget" name="tabPlayer6">
63 <widget class="QComboBox" name="player3_combobox"> 78 <property name="accessibleName">
64 <property name="minimumSize"> 79 <string>Player 6</string>
65 <size> 80 </property>
66 <width>110</width> 81 <attribute name="title">
67 <height>0</height> 82 <string>Player 6</string>
68 </size> 83 </attribute>
69 </property> 84 </widget>
70 </widget> 85 <widget class="QWidget" name="tabPlayer7">
71 </item> 86 <property name="accessibleName">
72 <item row="4" column="2"> 87 <string>Player 7</string>
73 <widget class="QComboBox" name="player4_combobox"> 88 </property>
74 <property name="minimumSize"> 89 <attribute name="title">
75 <size> 90 <string>Player 7</string>
76 <width>110</width> 91 </attribute>
77 <height>0</height> 92 </widget>
78 </size> 93 <widget class="QWidget" name="tabPlayer8">
79 </property> 94 <property name="accessibleName">
80 </widget> 95 <string>Player 8</string>
81 </item> 96 </property>
82 <item row="5" column="2"> 97 <attribute name="title">
83 <widget class="QComboBox" name="player5_combobox"> 98 <string>Player 8</string>
84 <property name="minimumSize"> 99 </attribute>
85 <size> 100 </widget>
86 <width>110</width> 101 <widget class="QWidget" name="tabAdvanced">
87 <height>0</height> 102 <property name="accessibleName">
88 </size> 103 <string>Advanced</string>
89 </property> 104 </property>
90 </widget> 105 <attribute name="title">
91 </item> 106 <string>Advanced</string>
92 <item row="6" column="2"> 107 </attribute>
93 <widget class="QComboBox" name="player6_combobox"> 108 </widget>
94 <property name="minimumSize"> 109 </widget>
95 <size> 110 </item>
96 <width>110</width> 111 <item alignment="Qt::AlignVCenter">
97 <height>0</height> 112 <widget class="QWidget" name="consoleInputSettings" native="true">
98 </size> 113 <layout class="QHBoxLayout" name="buttonsBottomRightHorizontalLayout">
99 </property> 114 <property name="spacing">
100 </widget> 115 <number>3</number>
101 </item> 116 </property>
102 <item row="7" column="2"> 117 <property name="leftMargin">
103 <widget class="QComboBox" name="player7_combobox"> 118 <number>0</number>
104 <property name="minimumSize"> 119 </property>
105 <size> 120 <property name="topMargin">
106 <width>110</width> 121 <number>3</number>
107 <height>0</height> 122 </property>
108 </size> 123 <property name="rightMargin">
109 </property> 124 <number>0</number>
110 </widget> 125 </property>
111 </item> 126 <property name="bottomMargin">
112 <item row="8" column="2"> 127 <number>0</number>
113 <widget class="QComboBox" name="player8_combobox"> 128 </property>
114 <property name="minimumSize"> 129 <item alignment="Qt::AlignVCenter">
115 <size> 130 <widget class="QGroupBox" name="handheldGroup">
116 <width>110</width> 131 <property name="maximumSize">
117 <height>0</height> 132 <size>
118 </size> 133 <width>16777215</width>
119 </property> 134 <height>16777215</height>
120 </widget> 135 </size>
121 </item> 136 </property>
122 <item row="2" column="3"> 137 <property name="title">
123 <widget class="QPushButton" name="player2_configure"> 138 <string>Console Mode</string>
124 <property name="text"> 139 </property>
125 <string>Configure</string> 140 <layout class="QHBoxLayout" name="horizontalLayout">
126 </property> 141 <property name="spacing">
127 </widget> 142 <number>6</number>
128 </item> 143 </property>
129 <item row="3" column="3"> 144 <property name="leftMargin">
130 <widget class="QPushButton" name="player3_configure"> 145 <number>3</number>
131 <property name="text"> 146 </property>
132 <string>Configure</string> 147 <property name="topMargin">
133 </property> 148 <number>6</number>
134 </widget> 149 </property>
135 </item> 150 <property name="rightMargin">
136 <item row="4" column="3"> 151 <number>3</number>
137 <widget class="QPushButton" name="player4_configure"> 152 </property>
138 <property name="text"> 153 <property name="bottomMargin">
139 <string>Configure</string> 154 <number>6</number>
140 </property> 155 </property>
141 </widget> 156 <item>
142 </item> 157 <widget class="QRadioButton" name="radioDocked">
143 <item row="5" column="3"> 158 <property name="text">
144 <widget class="QPushButton" name="player5_configure"> 159 <string>Docked</string>
145 <property name="text"> 160 </property>
146 <string>Configure</string> 161 <property name="checked">
147 </property> 162 <bool>true</bool>
148 </widget> 163 </property>
149 </item> 164 </widget>
150 <item row="6" column="3"> 165 </item>
151 <widget class="QPushButton" name="player6_configure"> 166 <item>
152 <property name="text"> 167 <widget class="QRadioButton" name="radioUndocked">
153 <string>Configure</string> 168 <property name="text">
154 </property> 169 <string>Undocked</string>
155 </widget> 170 </property>
156 </item> 171 </widget>
157 <item row="7" column="3"> 172 </item>
158 <widget class="QPushButton" name="player7_configure"> 173 </layout>
159 <property name="text"> 174 </widget>
160 <string>Configure</string> 175 </item>
161 </property> 176 <item>
162 </widget> 177 <widget class="QGroupBox" name="vibrationGroup">
163 </item> 178 <property name="title">
164 <item row="8" column="3"> 179 <string>Vibration</string>
165 <widget class="QPushButton" name="player8_configure"> 180 </property>
166 <property name="text"> 181 <property name="checkable">
167 <string>Configure</string> 182 <bool>true</bool>
168 </property> 183 </property>
169 </widget> 184 <layout class="QHBoxLayout" name="horizontalLayout_2">
170 </item> 185 <property name="leftMargin">
171 <item row="0" column="0"> 186 <number>3</number>
172 <spacer name="horizontalSpacer"> 187 </property>
173 <property name="orientation"> 188 <property name="topMargin">
174 <enum>Qt::Horizontal</enum> 189 <number>3</number>
175 </property> 190 </property>
176 <property name="sizeHint" stdset="0"> 191 <property name="rightMargin">
177 <size> 192 <number>3</number>
178 <width>40</width> 193 </property>
179 <height>20</height> 194 <property name="bottomMargin">
180 </size> 195 <number>3</number>
181 </property> 196 </property>
182 </spacer> 197 <item>
183 </item> 198 <widget class="QSpinBox" name="vibrationSpin">
184 <item row="0" column="4"> 199 <property name="minimumSize">
185 <spacer name="horizontalSpacer_2"> 200 <size>
186 <property name="orientation"> 201 <width>65</width>
187 <enum>Qt::Horizontal</enum> 202 <height>21</height>
188 </property> 203 </size>
189 <property name="sizeHint" stdset="0"> 204 </property>
190 <size> 205 <property name="maximumSize">
191 <width>40</width> 206 <size>
192 <height>20</height> 207 <width>65</width>
193 </size> 208 <height>16777215</height>
194 </property> 209 </size>
195 </spacer> 210 </property>
196 </item> 211 <property name="suffix">
197 <item row="1" column="1"> 212 <string>%</string>
198 <widget class="QLabel" name="label_3"> 213 </property>
199 <property name="minimumSize"> 214 <property name="minimum">
200 <size> 215 <number>1</number>
201 <width>55</width> 216 </property>
202 <height>0</height> 217 <property name="maximum">
203 </size> 218 <number>200</number>
204 </property> 219 </property>
205 <property name="text"> 220 <property name="value">
206 <string>Player 1</string> 221 <number>100</number>
207 </property> 222 </property>
208 </widget> 223 </widget>
209 </item> 224 </item>
210 <item row="2" column="1"> 225 </layout>
211 <widget class="QLabel" name="label_4"> 226 </widget>
212 <property name="text"> 227 </item>
213 <string>Player 2</string> 228 <item>
214 </property> 229 <widget class="QGroupBox" name="motionGroup">
215 </widget> 230 <property name="title">
216 </item> 231 <string>Motion</string>
217 <item row="3" column="1"> 232 </property>
218 <widget class="QLabel" name="label_5"> 233 <property name="checkable">
219 <property name="text"> 234 <bool>true</bool>
220 <string>Player 3</string> 235 </property>
221 </property> 236 <layout class="QHBoxLayout" name="horizontalLayout_3">
222 </widget> 237 <property name="leftMargin">
223 </item> 238 <number>3</number>
224 <item row="4" column="1"> 239 </property>
225 <widget class="QLabel" name="label_6"> 240 <property name="topMargin">
226 <property name="text"> 241 <number>3</number>
227 <string>Player 4</string> 242 </property>
228 </property> 243 <property name="rightMargin">
229 </widget> 244 <number>3</number>
230 </item> 245 </property>
231 <item row="5" column="1"> 246 <property name="bottomMargin">
232 <widget class="QLabel" name="label_7"> 247 <number>3</number>
233 <property name="text"> 248 </property>
234 <string>Player 5</string> 249 <item>
235 </property> 250 <widget class="QPushButton" name="motionButton">
236 </widget> 251 <property name="minimumSize">
237 </item> 252 <size>
238 <item row="6" column="1"> 253 <width>57</width>
239 <widget class="QLabel" name="label_8"> 254 <height>0</height>
240 <property name="text"> 255 </size>
241 <string>Player 6</string> 256 </property>
242 </property> 257 <property name="maximumSize">
243 </widget> 258 <size>
244 </item> 259 <width>55</width>
245 <item row="7" column="1"> 260 <height>16777215</height>
246 <widget class="QLabel" name="label_9"> 261 </size>
247 <property name="text"> 262 </property>
248 <string>Player 7</string> 263 <property name="styleSheet">
249 </property> 264 <string notr="true">min-width: 55px;</string>
250 </widget> 265 </property>
251 </item> 266 <property name="text">
252 <item row="8" column="1"> 267 <string>Configure</string>
253 <widget class="QLabel" name="label_10"> 268 </property>
254 <property name="text"> 269 </widget>
255 <string>Player 8</string> 270 </item>
256 </property> 271 </layout>
257 </widget> 272 </widget>
258 </item> 273 </item>
259 </layout> 274 <item alignment="Qt::AlignVCenter">
260 </widget> 275 <widget class="QWidget" name="widget" native="true">
261 </item> 276 <layout class="QGridLayout" name="gridLayout_2">
262 <item> 277 <property name="leftMargin">
263 <widget class="QGroupBox" name="gridGroupBox_2"> 278 <number>5</number>
264 <property name="title"> 279 </property>
265 <string>Handheld</string> 280 <property name="topMargin">
266 </property> 281 <number>0</number>
267 <layout class="QGridLayout" name="gridLayout_2">
268 <item row="1" column="2">
269 <spacer name="horizontalSpacer_5">
270 <property name="orientation">
271 <enum>Qt::Horizontal</enum>
272 </property>
273 <property name="sizeType">
274 <enum>QSizePolicy::Fixed</enum>
275 </property>
276 <property name="sizeHint" stdset="0">
277 <size>
278 <width>72</width>
279 <height>20</height>
280 </size>
281 </property>
282 </spacer>
283 </item>
284 <item row="1" column="4">
285 <spacer name="horizontalSpacer_4">
286 <property name="orientation">
287 <enum>Qt::Horizontal</enum>
288 </property>
289 <property name="sizeHint" stdset="0">
290 <size>
291 <width>40</width>
292 <height>20</height>
293 </size>
294 </property>
295 </spacer>
296 </item>
297 <item row="1" column="3">
298 <widget class="QPushButton" name="handheld_configure">
299 <property name="text">
300 <string>Configure</string>
301 </property>
302 </widget>
303 </item>
304 <item row="1" column="0">
305 <spacer name="horizontalSpacer_3">
306 <property name="orientation">
307 <enum>Qt::Horizontal</enum>
308 </property>
309 <property name="sizeHint" stdset="0">
310 <size>
311 <width>40</width>
312 <height>20</height>
313 </size>
314 </property>
315 </spacer>
316 </item>
317 <item row="1" column="1">
318 <widget class="QCheckBox" name="handheld_connected">
319 <property name="text">
320 <string>Joycons Docked</string>
321 </property>
322 </widget>
323 </item>
324 <item row="0" column="1">
325 <widget class="QCheckBox" name="use_docked_mode">
326 <property name="text">
327 <string>Use Docked Mode</string>
328 </property>
329 </widget>
330 </item>
331 </layout>
332 </widget>
333 </item>
334 <item>
335 <widget class="QGroupBox" name="gridGroupBox_3">
336 <property name="title">
337 <string>Other</string>
338 </property>
339 <layout class="QGridLayout" name="gridLayout_3">
340 <item row="1" column="1">
341 <widget class="QCheckBox" name="keyboard_enabled">
342 <property name="minimumSize">
343 <size>
344 <width>0</width>
345 <height>23</height>
346 </size>
347 </property>
348 <property name="text">
349 <string>Keyboard</string>
350 </property>
351 </widget>
352 </item>
353 <item row="2" column="1">
354 <widget class="QCheckBox" name="debug_enabled">
355 <property name="text">
356 <string>Debug Controller</string>
357 </property>
358 </widget>
359 </item>
360 <item row="3" column="1">
361 <widget class="QCheckBox" name="touchscreen_enabled">
362 <property name="text">
363 <string>Touchscreen</string>
364 </property>
365 </widget>
366 </item>
367 <item row="0" column="1">
368 <widget class="QCheckBox" name="mouse_enabled">
369 <property name="minimumSize">
370 <size>
371 <width>0</width>
372 <height>23</height>
373 </size>
374 </property>
375 <property name="text">
376 <string>Mouse</string>
377 </property>
378 </widget>
379 </item>
380 <item row="0" column="4">
381 <spacer name="horizontalSpacer_7">
382 <property name="orientation">
383 <enum>Qt::Horizontal</enum>
384 </property>
385 <property name="sizeHint" stdset="0">
386 <size>
387 <width>40</width>
388 <height>20</height>
389 </size>
390 </property>
391 </spacer>
392 </item>
393 <item row="0" column="2">
394 <spacer name="horizontalSpacer_8">
395 <property name="orientation">
396 <enum>Qt::Horizontal</enum>
397 </property>
398 <property name="sizeType">
399 <enum>QSizePolicy::Fixed</enum>
400 </property>
401 <property name="sizeHint" stdset="0">
402 <size>
403 <width>76</width>
404 <height>20</height>
405 </size>
406 </property>
407 </spacer>
408 </item>
409 <item row="0" column="0">
410 <spacer name="horizontalSpacer_6">
411 <property name="orientation">
412 <enum>Qt::Horizontal</enum>
413 </property>
414 <property name="sizeHint" stdset="0">
415 <size>
416 <width>40</width>
417 <height>20</height>
418 </size>
419 </property>
420 </spacer>
421 </item>
422 <item row="3" column="3">
423 <widget class="QPushButton" name="touchscreen_advanced">
424 <property name="text">
425 <string>Advanced</string>
426 </property>
427 </widget>
428 </item>
429 <item row="2" column="3">
430 <widget class="QPushButton" name="debug_configure">
431 <property name="text">
432 <string>Configure</string>
433 </property>
434 </widget>
435 </item>
436 <item row="0" column="3">
437 <widget class="QPushButton" name="mouse_advanced">
438 <property name="text">
439 <string>Advanced</string>
440 </property>
441 </widget>
442 </item>
443 </layout>
444 </widget>
445 </item>
446 <item>
447 <spacer name="verticalSpacer">
448 <property name="orientation">
449 <enum>Qt::Vertical</enum>
450 </property>
451 <property name="sizeHint" stdset="0">
452 <size>
453 <width>20</width>
454 <height>40</height>
455 </size>
456 </property>
457 </spacer>
458 </item>
459 <item>
460 <layout class="QHBoxLayout" name="horizontalLayout">
461 <item>
462 <widget class="QPushButton" name="restore_defaults_button">
463 <property name="text">
464 <string>Restore Defaults</string>
465 </property> 282 </property>
466 </widget> 283 <property name="rightMargin">
467 </item> 284 <number>0</number>
468 <item>
469 <spacer name="horizontalSpacer_9">
470 <property name="orientation">
471 <enum>Qt::Horizontal</enum>
472 </property> 285 </property>
473 <property name="sizeHint" stdset="0"> 286 <property name="bottomMargin">
474 <size> 287 <number>0</number>
475 <width>40</width>
476 <height>20</height>
477 </size>
478 </property> 288 </property>
479 </spacer> 289 <property name="spacing">
480 </item> 290 <number>3</number>
481 <item>
482 <widget class="QDialogButtonBox" name="buttonBox">
483 <property name="standardButtons">
484 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
485 </property> 291 </property>
486 </widget> 292 <item row="1" column="2">
487 </item> 293 <widget class="QCheckBox" name="checkboxPlayer2Connected">
488 </layout> 294 <property name="text">
489 </item> 295 <string/>
490 </layout> 296 </property>
297 </widget>
298 </item>
299 <item row="1" column="0">
300 <widget class="QLabel" name="label_2">
301 <property name="text">
302 <string>Controllers</string>
303 </property>
304 </widget>
305 </item>
306 <item row="1" column="4">
307 <widget class="QCheckBox" name="checkboxPlayer4Connected">
308 <property name="text">
309 <string/>
310 </property>
311 </widget>
312 </item>
313 <item row="1" column="3">
314 <widget class="QCheckBox" name="checkboxPlayer3Connected">
315 <property name="text">
316 <string/>
317 </property>
318 </widget>
319 </item>
320 <item row="1" column="5">
321 <widget class="QCheckBox" name="checkboxPlayer5Connected">
322 <property name="text">
323 <string/>
324 </property>
325 </widget>
326 </item>
327 <item row="0" column="1">
328 <widget class="QLabel" name="label">
329 <property name="text">
330 <string>1</string>
331 </property>
332 <property name="alignment">
333 <set>Qt::AlignCenter</set>
334 </property>
335 </widget>
336 </item>
337 <item row="1" column="7">
338 <widget class="QCheckBox" name="checkboxPlayer7Connected">
339 <property name="text">
340 <string/>
341 </property>
342 </widget>
343 </item>
344 <item row="1" column="6">
345 <widget class="QCheckBox" name="checkboxPlayer6Connected">
346 <property name="text">
347 <string/>
348 </property>
349 </widget>
350 </item>
351 <item row="1" column="1">
352 <widget class="QCheckBox" name="checkboxPlayer1Connected">
353 <property name="layoutDirection">
354 <enum>Qt::LeftToRight</enum>
355 </property>
356 <property name="checked">
357 <bool>true</bool>
358 </property>
359 </widget>
360 </item>
361 <item row="1" column="8">
362 <widget class="QCheckBox" name="checkboxPlayer8Connected">
363 <property name="text">
364 <string/>
365 </property>
366 </widget>
367 </item>
368 <item row="0" column="2">
369 <widget class="QLabel" name="label_3">
370 <property name="text">
371 <string>2</string>
372 </property>
373 <property name="alignment">
374 <set>Qt::AlignCenter</set>
375 </property>
376 </widget>
377 </item>
378 <item row="0" column="3">
379 <widget class="QLabel" name="label_4">
380 <property name="text">
381 <string>3</string>
382 </property>
383 <property name="alignment">
384 <set>Qt::AlignCenter</set>
385 </property>
386 </widget>
387 </item>
388 <item row="0" column="4">
389 <widget class="QLabel" name="label_5">
390 <property name="text">
391 <string>4</string>
392 </property>
393 <property name="alignment">
394 <set>Qt::AlignCenter</set>
395 </property>
396 </widget>
397 </item>
398 <item row="0" column="5">
399 <widget class="QLabel" name="label_6">
400 <property name="text">
401 <string>5</string>
402 </property>
403 <property name="alignment">
404 <set>Qt::AlignCenter</set>
405 </property>
406 </widget>
407 </item>
408 <item row="0" column="6">
409 <widget class="QLabel" name="label_7">
410 <property name="text">
411 <string>6</string>
412 </property>
413 <property name="alignment">
414 <set>Qt::AlignCenter</set>
415 </property>
416 </widget>
417 </item>
418 <item row="0" column="7">
419 <widget class="QLabel" name="label_8">
420 <property name="text">
421 <string>7</string>
422 </property>
423 <property name="alignment">
424 <set>Qt::AlignCenter</set>
425 </property>
426 </widget>
427 </item>
428 <item row="0" column="8">
429 <widget class="QLabel" name="label_9">
430 <property name="text">
431 <string>8</string>
432 </property>
433 <property name="alignment">
434 <set>Qt::AlignCenter</set>
435 </property>
436 </widget>
437 </item>
438 <item row="0" column="0">
439 <widget class="QLabel" name="label_10">
440 <property name="text">
441 <string>Connected</string>
442 </property>
443 </widget>
444 </item>
445 </layout>
446 </widget>
447 </item>
448 <item>
449 <spacer name="horizontalSpacer">
450 <property name="orientation">
451 <enum>Qt::Horizontal</enum>
452 </property>
453 <property name="sizeHint" stdset="0">
454 <size>
455 <width>40</width>
456 <height>20</height>
457 </size>
458 </property>
459 </spacer>
460 </item>
461 <item alignment="Qt::AlignBottom">
462 <widget class="QPushButton" name="buttonRestoreDefaults">
463 <property name="sizePolicy">
464 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
465 <horstretch>0</horstretch>
466 <verstretch>0</verstretch>
467 </sizepolicy>
468 </property>
469 <property name="minimumSize">
470 <size>
471 <width>57</width>
472 <height>0</height>
473 </size>
474 </property>
475 <property name="maximumSize">
476 <size>
477 <width>55</width>
478 <height>16777215</height>
479 </size>
480 </property>
481 <property name="sizeIncrement">
482 <size>
483 <width>0</width>
484 <height>0</height>
485 </size>
486 </property>
487 <property name="baseSize">
488 <size>
489 <width>0</width>
490 <height>0</height>
491 </size>
492 </property>
493 <property name="layoutDirection">
494 <enum>Qt::LeftToRight</enum>
495 </property>
496 <property name="styleSheet">
497 <string notr="true">min-width: 55px;</string>
498 </property>
499 <property name="text">
500 <string>Defaults</string>
501 </property>
502 </widget>
503 </item>
504 <item alignment="Qt::AlignBottom">
505 <widget class="QPushButton" name="buttonClearAll">
506 <property name="sizePolicy">
507 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
508 <horstretch>0</horstretch>
509 <verstretch>0</verstretch>
510 </sizepolicy>
511 </property>
512 <property name="minimumSize">
513 <size>
514 <width>57</width>
515 <height>0</height>
516 </size>
517 </property>
518 <property name="maximumSize">
519 <size>
520 <width>55</width>
521 <height>16777215</height>
522 </size>
523 </property>
524 <property name="sizeIncrement">
525 <size>
526 <width>0</width>
527 <height>0</height>
528 </size>
529 </property>
530 <property name="baseSize">
531 <size>
532 <width>0</width>
533 <height>0</height>
534 </size>
535 </property>
536 <property name="layoutDirection">
537 <enum>Qt::LeftToRight</enum>
538 </property>
539 <property name="styleSheet">
540 <string notr="true">min-width: 55px;</string>
541 </property>
542 <property name="text">
543 <string>Clear</string>
544 </property>
545 </widget>
546 </item>
547 </layout>
548 </widget>
491 </item> 549 </item>
492 </layout> 550 </layout>
493 </widget> 551 </widget>
494 <resources/> 552 <resources/>
495 <connections> 553 <connections/>
496 <connection>
497 <sender>buttonBox</sender>
498 <signal>accepted()</signal>
499 <receiver>ConfigureInput</receiver>
500 <slot>accept()</slot>
501 <hints>
502 <hint type="sourcelabel">
503 <x>294</x>
504 <y>553</y>
505 </hint>
506 <hint type="destinationlabel">
507 <x>191</x>
508 <y>287</y>
509 </hint>
510 </hints>
511 </connection>
512 <connection>
513 <sender>buttonBox</sender>
514 <signal>rejected()</signal>
515 <receiver>ConfigureInput</receiver>
516 <slot>reject()</slot>
517 <hints>
518 <hint type="sourcelabel">
519 <x>294</x>
520 <y>553</y>
521 </hint>
522 <hint type="destinationlabel">
523 <x>191</x>
524 <y>287</y>
525 </hint>
526 </hints>
527 </connection>
528 </connections>
529</ui> 554</ui>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
new file mode 100644
index 000000000..db42b826b
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -0,0 +1,169 @@
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 <QColorDialog>
6#include "core/core.h"
7#include "core/settings.h"
8#include "ui_configure_input_advanced.h"
9#include "yuzu/configuration/configure_input_advanced.h"
10
11ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
12 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
13 ui->setupUi(this);
14
15 controllers_color_buttons = {{
16 {
17 ui->player1_left_body_button,
18 ui->player1_left_buttons_button,
19 ui->player1_right_body_button,
20 ui->player1_right_buttons_button,
21 },
22 {
23 ui->player2_left_body_button,
24 ui->player2_left_buttons_button,
25 ui->player2_right_body_button,
26 ui->player2_right_buttons_button,
27 },
28 {
29 ui->player3_left_body_button,
30 ui->player3_left_buttons_button,
31 ui->player3_right_body_button,
32 ui->player3_right_buttons_button,
33 },
34 {
35 ui->player4_left_body_button,
36 ui->player4_left_buttons_button,
37 ui->player4_right_body_button,
38 ui->player4_right_buttons_button,
39 },
40 {
41 ui->player5_left_body_button,
42 ui->player5_left_buttons_button,
43 ui->player5_right_body_button,
44 ui->player5_right_buttons_button,
45 },
46 {
47 ui->player6_left_body_button,
48 ui->player6_left_buttons_button,
49 ui->player6_right_body_button,
50 ui->player6_right_buttons_button,
51 },
52 {
53 ui->player7_left_body_button,
54 ui->player7_left_buttons_button,
55 ui->player7_right_body_button,
56 ui->player7_right_buttons_button,
57 },
58 {
59 ui->player8_left_body_button,
60 ui->player8_left_buttons_button,
61 ui->player8_right_body_button,
62 ui->player8_right_buttons_button,
63 },
64 }};
65
66 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
67 auto& color_buttons = controllers_color_buttons[player_idx];
68 for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
69 connect(color_buttons[button_idx], &QPushButton::clicked, this,
70 [this, player_idx, button_idx] {
71 OnControllerButtonClick(static_cast<int>(player_idx),
72 static_cast<int>(button_idx));
73 });
74 }
75 }
76
77 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this,
78 &ConfigureInputAdvanced::UpdateUIEnabled);
79 connect(ui->debug_enabled, &QCheckBox::stateChanged, this,
80 &ConfigureInputAdvanced::UpdateUIEnabled);
81 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
82 &ConfigureInputAdvanced::UpdateUIEnabled);
83
84 connect(ui->debug_configure, &QPushButton::clicked, this,
85 [this] { CallDebugControllerDialog(); });
86 connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
87 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
88 [this] { CallTouchscreenConfigDialog(); });
89
90 LoadConfiguration();
91}
92
93ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
94
95void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
96 const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
97 if (!new_bg_color.isValid()) {
98 return;
99 }
100 controllers_colors[player_idx][button_idx] = new_bg_color;
101 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
102 QStringLiteral("background-color: %1; min-width: 55px;")
103 .arg(controllers_colors[player_idx][button_idx].name()));
104}
105
106void ConfigureInputAdvanced::ApplyConfiguration() {
107 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
108 auto& player = Settings::values.players[player_idx];
109 std::array<u32, 4> colors{};
110 std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
111 colors.begin(), [](QColor color) { return color.rgb(); });
112
113 player.body_color_left = colors[0];
114 player.button_color_left = colors[1];
115 player.body_color_right = colors[2];
116 player.button_color_right = colors[3];
117 }
118
119 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
120 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
121 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
122 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
123}
124
125void ConfigureInputAdvanced::LoadConfiguration() {
126 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
127 auto& player = Settings::values.players[player_idx];
128 std::array<u32, 4> colors = {
129 player.body_color_left,
130 player.button_color_left,
131 player.body_color_right,
132 player.button_color_right,
133 };
134
135 std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(),
136 [](u32 rgb) { return QColor::fromRgb(rgb); });
137
138 for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
139 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
140 QStringLiteral("background-color: %1; min-width: 55px;")
141 .arg(controllers_colors[player_idx][button_idx].name()));
142 }
143 }
144
145 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
146 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
147 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
148 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
149
150 UpdateUIEnabled();
151}
152
153void ConfigureInputAdvanced::changeEvent(QEvent* event) {
154 if (event->type() == QEvent::LanguageChange) {
155 RetranslateUI();
156 }
157
158 QWidget::changeEvent(event);
159}
160
161void ConfigureInputAdvanced::RetranslateUI() {
162 ui->retranslateUi(this);
163}
164
165void ConfigureInputAdvanced::UpdateUIEnabled() {
166 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
167 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
168 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
169}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
new file mode 100644
index 000000000..d8fcec52d
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -0,0 +1,45 @@
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 <memory>
9#include <QWidget>
10
11class QColor;
12class QPushButton;
13
14namespace Ui {
15class ConfigureInputAdvanced;
16}
17
18class ConfigureInputAdvanced : public QWidget {
19 Q_OBJECT
20
21public:
22 explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
23 ~ConfigureInputAdvanced() override;
24
25 void ApplyConfiguration();
26
27signals:
28 void CallDebugControllerDialog();
29 void CallMouseConfigDialog();
30 void CallTouchscreenConfigDialog();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35 void UpdateUIEnabled();
36
37 void OnControllerButtonClick(int player_idx, int button_idx);
38
39 void LoadConfiguration();
40
41 std::unique_ptr<Ui::ConfigureInputAdvanced> ui;
42
43 std::array<std::array<QColor, 4>, 8> controllers_colors;
44 std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
45};
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
new file mode 100644
index 000000000..5958435fc
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -0,0 +1,2688 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputAdvanced</class>
4 <widget class="QWidget" name="ConfigureInputAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>710</width>
10 <height>580</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QHBoxLayout" name="horizontalLayout">
20 <property name="spacing">
21 <number>0</number>
22 </property>
23 <property name="leftMargin">
24 <number>0</number>
25 </property>
26 <property name="topMargin">
27 <number>0</number>
28 </property>
29 <property name="rightMargin">
30 <number>0</number>
31 </property>
32 <property name="bottomMargin">
33 <number>0</number>
34 </property>
35 <item>
36 <widget class="QWidget" name="mainInputAdvanced" native="true">
37 <layout class="QHBoxLayout" name="main" stretch="1,1">
38 <property name="spacing">
39 <number>9</number>
40 </property>
41 <property name="leftMargin">
42 <number>0</number>
43 </property>
44 <property name="topMargin">
45 <number>0</number>
46 </property>
47 <property name="rightMargin">
48 <number>0</number>
49 </property>
50 <property name="bottomMargin">
51 <number>0</number>
52 </property>
53 <item>
54 <widget class="QWidget" name="leftInputAdvanced" native="true">
55 <layout class="QVBoxLayout" name="leftLayout" stretch="0">
56 <property name="spacing">
57 <number>3</number>
58 </property>
59 <property name="leftMargin">
60 <number>0</number>
61 </property>
62 <property name="topMargin">
63 <number>0</number>
64 </property>
65 <property name="rightMargin">
66 <number>0</number>
67 </property>
68 <property name="bottomMargin">
69 <number>0</number>
70 </property>
71 <item>
72 <widget class="QGroupBox" name="joyconColorsGroup">
73 <property name="title">
74 <string>Joycon Colors</string>
75 </property>
76 <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,1">
77 <property name="leftMargin">
78 <number>9</number>
79 </property>
80 <property name="topMargin">
81 <number>9</number>
82 </property>
83 <property name="rightMargin">
84 <number>9</number>
85 </property>
86 <property name="bottomMargin">
87 <number>9</number>
88 </property>
89 <item>
90 <widget class="QWidget" name="topLeftInputAdvanced" native="true">
91 <layout class="QVBoxLayout" name="verticalLayout_4">
92 <property name="spacing">
93 <number>6</number>
94 </property>
95 <property name="leftMargin">
96 <number>0</number>
97 </property>
98 <property name="topMargin">
99 <number>0</number>
100 </property>
101 <property name="rightMargin">
102 <number>0</number>
103 </property>
104 <property name="bottomMargin">
105 <number>0</number>
106 </property>
107 <item>
108 <widget class="QWidget" name="player12Widget" native="true">
109 <layout class="QHBoxLayout" name="horizontalLayout_4">
110 <property name="leftMargin">
111 <number>0</number>
112 </property>
113 <property name="topMargin">
114 <number>0</number>
115 </property>
116 <property name="rightMargin">
117 <number>0</number>
118 </property>
119 <property name="bottomMargin">
120 <number>0</number>
121 </property>
122 <item>
123 <widget class="QGroupBox" name="player1Group">
124 <property name="title">
125 <string>Player 1</string>
126 </property>
127 <layout class="QHBoxLayout" name="horizontalLayout_2">
128 <property name="spacing">
129 <number>6</number>
130 </property>
131 <property name="leftMargin">
132 <number>6</number>
133 </property>
134 <property name="topMargin">
135 <number>0</number>
136 </property>
137 <property name="rightMargin">
138 <number>6</number>
139 </property>
140 <property name="bottomMargin">
141 <number>6</number>
142 </property>
143 <item>
144 <widget class="QWidget" name="player1LeftJoycon" native="true">
145 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_14">
146 <property name="spacing">
147 <number>0</number>
148 </property>
149 <property name="leftMargin">
150 <number>0</number>
151 </property>
152 <property name="topMargin">
153 <number>0</number>
154 </property>
155 <property name="rightMargin">
156 <number>0</number>
157 </property>
158 <property name="bottomMargin">
159 <number>0</number>
160 </property>
161 <item alignment="Qt::AlignHCenter">
162 <widget class="QGroupBox" name="player1LeftBodyGroup">
163 <property name="title">
164 <string>L Body</string>
165 </property>
166 <property name="alignment">
167 <set>Qt::AlignCenter</set>
168 </property>
169 <layout class="QVBoxLayout" name="verticalLayout_66">
170 <property name="spacing">
171 <number>3</number>
172 </property>
173 <property name="leftMargin">
174 <number>3</number>
175 </property>
176 <property name="topMargin">
177 <number>3</number>
178 </property>
179 <property name="rightMargin">
180 <number>3</number>
181 </property>
182 <property name="bottomMargin">
183 <number>3</number>
184 </property>
185 <item>
186 <widget class="QPushButton" name="player1_left_body_button">
187 <property name="sizePolicy">
188 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
189 <horstretch>0</horstretch>
190 <verstretch>0</verstretch>
191 </sizepolicy>
192 </property>
193 <property name="minimumSize">
194 <size>
195 <width>57</width>
196 <height>0</height>
197 </size>
198 </property>
199 <property name="maximumSize">
200 <size>
201 <width>55</width>
202 <height>16777215</height>
203 </size>
204 </property>
205 <property name="styleSheet">
206 <string notr="true">min-width: 55px;</string>
207 </property>
208 <property name="text">
209 <string/>
210 </property>
211 </widget>
212 </item>
213 </layout>
214 </widget>
215 </item>
216 <item alignment="Qt::AlignHCenter">
217 <widget class="QGroupBox" name="player1LeftButtonsGroup">
218 <property name="title">
219 <string>L Button</string>
220 </property>
221 <property name="alignment">
222 <set>Qt::AlignCenter</set>
223 </property>
224 <layout class="QVBoxLayout" name="verticalLayout_67">
225 <property name="spacing">
226 <number>3</number>
227 </property>
228 <property name="leftMargin">
229 <number>3</number>
230 </property>
231 <property name="topMargin">
232 <number>3</number>
233 </property>
234 <property name="rightMargin">
235 <number>3</number>
236 </property>
237 <property name="bottomMargin">
238 <number>3</number>
239 </property>
240 <item>
241 <widget class="QPushButton" name="player1_left_buttons_button">
242 <property name="sizePolicy">
243 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
244 <horstretch>0</horstretch>
245 <verstretch>0</verstretch>
246 </sizepolicy>
247 </property>
248 <property name="minimumSize">
249 <size>
250 <width>57</width>
251 <height>0</height>
252 </size>
253 </property>
254 <property name="maximumSize">
255 <size>
256 <width>55</width>
257 <height>16777215</height>
258 </size>
259 </property>
260 <property name="styleSheet">
261 <string notr="true">min-width: 55px;</string>
262 </property>
263 <property name="text">
264 <string/>
265 </property>
266 </widget>
267 </item>
268 </layout>
269 </widget>
270 </item>
271 </layout>
272 </widget>
273 </item>
274 <item>
275 <widget class="QWidget" name="player1RightJoycon" native="true">
276 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_14">
277 <property name="spacing">
278 <number>0</number>
279 </property>
280 <property name="leftMargin">
281 <number>0</number>
282 </property>
283 <property name="topMargin">
284 <number>0</number>
285 </property>
286 <property name="rightMargin">
287 <number>0</number>
288 </property>
289 <property name="bottomMargin">
290 <number>0</number>
291 </property>
292 <item alignment="Qt::AlignHCenter">
293 <widget class="QGroupBox" name="player1RightBodyGroup">
294 <property name="title">
295 <string>R Body</string>
296 </property>
297 <property name="alignment">
298 <set>Qt::AlignCenter</set>
299 </property>
300 <layout class="QVBoxLayout" name="verticalLayout_64">
301 <property name="spacing">
302 <number>3</number>
303 </property>
304 <property name="leftMargin">
305 <number>3</number>
306 </property>
307 <property name="topMargin">
308 <number>3</number>
309 </property>
310 <property name="rightMargin">
311 <number>3</number>
312 </property>
313 <property name="bottomMargin">
314 <number>3</number>
315 </property>
316 <item>
317 <widget class="QPushButton" name="player1_right_body_button">
318 <property name="sizePolicy">
319 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
320 <horstretch>0</horstretch>
321 <verstretch>0</verstretch>
322 </sizepolicy>
323 </property>
324 <property name="minimumSize">
325 <size>
326 <width>57</width>
327 <height>0</height>
328 </size>
329 </property>
330 <property name="maximumSize">
331 <size>
332 <width>55</width>
333 <height>16777215</height>
334 </size>
335 </property>
336 <property name="styleSheet">
337 <string notr="true">min-width: 55px;</string>
338 </property>
339 <property name="text">
340 <string/>
341 </property>
342 </widget>
343 </item>
344 </layout>
345 </widget>
346 </item>
347 <item alignment="Qt::AlignHCenter">
348 <widget class="QGroupBox" name="player1RightButtonsGroup">
349 <property name="title">
350 <string>R Button</string>
351 </property>
352 <property name="alignment">
353 <set>Qt::AlignCenter</set>
354 </property>
355 <layout class="QVBoxLayout" name="verticalLayout_65">
356 <property name="spacing">
357 <number>3</number>
358 </property>
359 <property name="leftMargin">
360 <number>3</number>
361 </property>
362 <property name="topMargin">
363 <number>3</number>
364 </property>
365 <property name="rightMargin">
366 <number>3</number>
367 </property>
368 <property name="bottomMargin">
369 <number>3</number>
370 </property>
371 <item>
372 <widget class="QPushButton" name="player1_right_buttons_button">
373 <property name="sizePolicy">
374 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
375 <horstretch>0</horstretch>
376 <verstretch>0</verstretch>
377 </sizepolicy>
378 </property>
379 <property name="minimumSize">
380 <size>
381 <width>57</width>
382 <height>0</height>
383 </size>
384 </property>
385 <property name="maximumSize">
386 <size>
387 <width>55</width>
388 <height>16777215</height>
389 </size>
390 </property>
391 <property name="styleSheet">
392 <string notr="true">min-width: 55px;</string>
393 </property>
394 <property name="text">
395 <string/>
396 </property>
397 </widget>
398 </item>
399 </layout>
400 </widget>
401 </item>
402 </layout>
403 </widget>
404 </item>
405 </layout>
406 </widget>
407 </item>
408 <item>
409 <widget class="QGroupBox" name="player2Group">
410 <property name="title">
411 <string>Player 2</string>
412 </property>
413 <layout class="QHBoxLayout" name="horizontalLayout_14">
414 <property name="spacing">
415 <number>6</number>
416 </property>
417 <property name="leftMargin">
418 <number>6</number>
419 </property>
420 <property name="topMargin">
421 <number>0</number>
422 </property>
423 <property name="rightMargin">
424 <number>6</number>
425 </property>
426 <property name="bottomMargin">
427 <number>6</number>
428 </property>
429 <item>
430 <widget class="QWidget" name="player2LeftJoycon" native="true">
431 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_15">
432 <property name="spacing">
433 <number>0</number>
434 </property>
435 <property name="leftMargin">
436 <number>0</number>
437 </property>
438 <property name="topMargin">
439 <number>0</number>
440 </property>
441 <property name="rightMargin">
442 <number>0</number>
443 </property>
444 <property name="bottomMargin">
445 <number>0</number>
446 </property>
447 <item alignment="Qt::AlignHCenter">
448 <widget class="QGroupBox" name="player2LeftBodyGroup">
449 <property name="title">
450 <string>L Body</string>
451 </property>
452 <property name="alignment">
453 <set>Qt::AlignCenter</set>
454 </property>
455 <layout class="QVBoxLayout" name="verticalLayout_70">
456 <property name="spacing">
457 <number>3</number>
458 </property>
459 <property name="leftMargin">
460 <number>3</number>
461 </property>
462 <property name="topMargin">
463 <number>3</number>
464 </property>
465 <property name="rightMargin">
466 <number>3</number>
467 </property>
468 <property name="bottomMargin">
469 <number>3</number>
470 </property>
471 <item>
472 <widget class="QPushButton" name="player2_left_body_button">
473 <property name="sizePolicy">
474 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
475 <horstretch>0</horstretch>
476 <verstretch>0</verstretch>
477 </sizepolicy>
478 </property>
479 <property name="minimumSize">
480 <size>
481 <width>57</width>
482 <height>0</height>
483 </size>
484 </property>
485 <property name="maximumSize">
486 <size>
487 <width>55</width>
488 <height>16777215</height>
489 </size>
490 </property>
491 <property name="styleSheet">
492 <string notr="true">min-width: 55px;</string>
493 </property>
494 <property name="text">
495 <string/>
496 </property>
497 </widget>
498 </item>
499 </layout>
500 </widget>
501 </item>
502 <item alignment="Qt::AlignHCenter">
503 <widget class="QGroupBox" name="player2LeftButtonsGroup">
504 <property name="title">
505 <string>L Button</string>
506 </property>
507 <property name="alignment">
508 <set>Qt::AlignCenter</set>
509 </property>
510 <layout class="QVBoxLayout" name="verticalLayout_71">
511 <property name="spacing">
512 <number>3</number>
513 </property>
514 <property name="leftMargin">
515 <number>3</number>
516 </property>
517 <property name="topMargin">
518 <number>3</number>
519 </property>
520 <property name="rightMargin">
521 <number>3</number>
522 </property>
523 <property name="bottomMargin">
524 <number>3</number>
525 </property>
526 <item>
527 <widget class="QPushButton" name="player2_left_buttons_button">
528 <property name="sizePolicy">
529 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
530 <horstretch>0</horstretch>
531 <verstretch>0</verstretch>
532 </sizepolicy>
533 </property>
534 <property name="minimumSize">
535 <size>
536 <width>57</width>
537 <height>0</height>
538 </size>
539 </property>
540 <property name="maximumSize">
541 <size>
542 <width>55</width>
543 <height>16777215</height>
544 </size>
545 </property>
546 <property name="styleSheet">
547 <string notr="true">min-width: 55px;</string>
548 </property>
549 <property name="text">
550 <string/>
551 </property>
552 </widget>
553 </item>
554 </layout>
555 </widget>
556 </item>
557 </layout>
558 </widget>
559 </item>
560 <item>
561 <widget class="QWidget" name="player2RightJoycon" native="true">
562 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_15">
563 <property name="spacing">
564 <number>0</number>
565 </property>
566 <property name="leftMargin">
567 <number>0</number>
568 </property>
569 <property name="topMargin">
570 <number>0</number>
571 </property>
572 <property name="rightMargin">
573 <number>0</number>
574 </property>
575 <property name="bottomMargin">
576 <number>0</number>
577 </property>
578 <item alignment="Qt::AlignHCenter">
579 <widget class="QGroupBox" name="player2RightBodyGroup">
580 <property name="title">
581 <string>R Body</string>
582 </property>
583 <property name="alignment">
584 <set>Qt::AlignCenter</set>
585 </property>
586 <layout class="QVBoxLayout" name="verticalLayout_68">
587 <property name="spacing">
588 <number>3</number>
589 </property>
590 <property name="leftMargin">
591 <number>3</number>
592 </property>
593 <property name="topMargin">
594 <number>3</number>
595 </property>
596 <property name="rightMargin">
597 <number>3</number>
598 </property>
599 <property name="bottomMargin">
600 <number>3</number>
601 </property>
602 <item>
603 <widget class="QPushButton" name="player2_right_body_button">
604 <property name="sizePolicy">
605 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
606 <horstretch>0</horstretch>
607 <verstretch>0</verstretch>
608 </sizepolicy>
609 </property>
610 <property name="minimumSize">
611 <size>
612 <width>57</width>
613 <height>0</height>
614 </size>
615 </property>
616 <property name="maximumSize">
617 <size>
618 <width>55</width>
619 <height>16777215</height>
620 </size>
621 </property>
622 <property name="styleSheet">
623 <string notr="true">min-width: 55px;</string>
624 </property>
625 <property name="text">
626 <string/>
627 </property>
628 </widget>
629 </item>
630 </layout>
631 </widget>
632 </item>
633 <item alignment="Qt::AlignHCenter">
634 <widget class="QGroupBox" name="player2RightButtonsGroup">
635 <property name="title">
636 <string>R Button</string>
637 </property>
638 <property name="alignment">
639 <set>Qt::AlignCenter</set>
640 </property>
641 <layout class="QVBoxLayout" name="verticalLayout_69">
642 <property name="spacing">
643 <number>3</number>
644 </property>
645 <property name="leftMargin">
646 <number>3</number>
647 </property>
648 <property name="topMargin">
649 <number>3</number>
650 </property>
651 <property name="rightMargin">
652 <number>3</number>
653 </property>
654 <property name="bottomMargin">
655 <number>3</number>
656 </property>
657 <item>
658 <widget class="QPushButton" name="player2_right_buttons_button">
659 <property name="sizePolicy">
660 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
661 <horstretch>0</horstretch>
662 <verstretch>0</verstretch>
663 </sizepolicy>
664 </property>
665 <property name="minimumSize">
666 <size>
667 <width>57</width>
668 <height>0</height>
669 </size>
670 </property>
671 <property name="maximumSize">
672 <size>
673 <width>55</width>
674 <height>16777215</height>
675 </size>
676 </property>
677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string>
679 </property>
680 <property name="text">
681 <string/>
682 </property>
683 </widget>
684 </item>
685 </layout>
686 </widget>
687 </item>
688 </layout>
689 </widget>
690 </item>
691 </layout>
692 </widget>
693 </item>
694 </layout>
695 </widget>
696 </item>
697 <item>
698 <widget class="QWidget" name="player34Widget" native="true">
699 <layout class="QHBoxLayout" name="horizontalLayout_5">
700 <property name="leftMargin">
701 <number>0</number>
702 </property>
703 <property name="topMargin">
704 <number>0</number>
705 </property>
706 <property name="rightMargin">
707 <number>0</number>
708 </property>
709 <property name="bottomMargin">
710 <number>0</number>
711 </property>
712 <item>
713 <widget class="QGroupBox" name="player3Group">
714 <property name="title">
715 <string>Player 3</string>
716 </property>
717 <layout class="QHBoxLayout" name="horizontalLayout_15">
718 <property name="spacing">
719 <number>6</number>
720 </property>
721 <property name="leftMargin">
722 <number>6</number>
723 </property>
724 <property name="topMargin">
725 <number>0</number>
726 </property>
727 <property name="rightMargin">
728 <number>6</number>
729 </property>
730 <property name="bottomMargin">
731 <number>6</number>
732 </property>
733 <item>
734 <widget class="QWidget" name="player3LeftJoycon" native="true">
735 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_16">
736 <property name="spacing">
737 <number>0</number>
738 </property>
739 <property name="leftMargin">
740 <number>0</number>
741 </property>
742 <property name="topMargin">
743 <number>0</number>
744 </property>
745 <property name="rightMargin">
746 <number>0</number>
747 </property>
748 <property name="bottomMargin">
749 <number>0</number>
750 </property>
751 <item alignment="Qt::AlignHCenter">
752 <widget class="QGroupBox" name="player3LeftBodyGroup">
753 <property name="title">
754 <string>L Body</string>
755 </property>
756 <property name="alignment">
757 <set>Qt::AlignCenter</set>
758 </property>
759 <layout class="QVBoxLayout" name="verticalLayout_74">
760 <property name="spacing">
761 <number>3</number>
762 </property>
763 <property name="leftMargin">
764 <number>3</number>
765 </property>
766 <property name="topMargin">
767 <number>3</number>
768 </property>
769 <property name="rightMargin">
770 <number>3</number>
771 </property>
772 <property name="bottomMargin">
773 <number>3</number>
774 </property>
775 <item>
776 <widget class="QPushButton" name="player3_left_body_button">
777 <property name="sizePolicy">
778 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
779 <horstretch>0</horstretch>
780 <verstretch>0</verstretch>
781 </sizepolicy>
782 </property>
783 <property name="minimumSize">
784 <size>
785 <width>57</width>
786 <height>0</height>
787 </size>
788 </property>
789 <property name="maximumSize">
790 <size>
791 <width>55</width>
792 <height>16777215</height>
793 </size>
794 </property>
795 <property name="styleSheet">
796 <string notr="true">min-width: 55px;</string>
797 </property>
798 <property name="text">
799 <string/>
800 </property>
801 </widget>
802 </item>
803 </layout>
804 </widget>
805 </item>
806 <item alignment="Qt::AlignHCenter">
807 <widget class="QGroupBox" name="player3LeftButtonsGroup">
808 <property name="title">
809 <string>L Button</string>
810 </property>
811 <property name="alignment">
812 <set>Qt::AlignCenter</set>
813 </property>
814 <layout class="QVBoxLayout" name="verticalLayout_75">
815 <property name="spacing">
816 <number>3</number>
817 </property>
818 <property name="leftMargin">
819 <number>3</number>
820 </property>
821 <property name="topMargin">
822 <number>3</number>
823 </property>
824 <property name="rightMargin">
825 <number>3</number>
826 </property>
827 <property name="bottomMargin">
828 <number>3</number>
829 </property>
830 <item>
831 <widget class="QPushButton" name="player3_left_buttons_button">
832 <property name="sizePolicy">
833 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
834 <horstretch>0</horstretch>
835 <verstretch>0</verstretch>
836 </sizepolicy>
837 </property>
838 <property name="minimumSize">
839 <size>
840 <width>57</width>
841 <height>0</height>
842 </size>
843 </property>
844 <property name="maximumSize">
845 <size>
846 <width>55</width>
847 <height>16777215</height>
848 </size>
849 </property>
850 <property name="styleSheet">
851 <string notr="true">min-width: 55px;</string>
852 </property>
853 <property name="text">
854 <string/>
855 </property>
856 </widget>
857 </item>
858 </layout>
859 </widget>
860 </item>
861 </layout>
862 </widget>
863 </item>
864 <item>
865 <widget class="QWidget" name="player3RightJoycon" native="true">
866 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_16">
867 <property name="spacing">
868 <number>0</number>
869 </property>
870 <property name="leftMargin">
871 <number>0</number>
872 </property>
873 <property name="topMargin">
874 <number>0</number>
875 </property>
876 <property name="rightMargin">
877 <number>0</number>
878 </property>
879 <property name="bottomMargin">
880 <number>0</number>
881 </property>
882 <item alignment="Qt::AlignHCenter">
883 <widget class="QGroupBox" name="player3RightBodyGroup">
884 <property name="title">
885 <string>R Body</string>
886 </property>
887 <property name="alignment">
888 <set>Qt::AlignCenter</set>
889 </property>
890 <layout class="QVBoxLayout" name="verticalLayout_72">
891 <property name="spacing">
892 <number>3</number>
893 </property>
894 <property name="leftMargin">
895 <number>3</number>
896 </property>
897 <property name="topMargin">
898 <number>3</number>
899 </property>
900 <property name="rightMargin">
901 <number>3</number>
902 </property>
903 <property name="bottomMargin">
904 <number>3</number>
905 </property>
906 <item>
907 <widget class="QPushButton" name="player3_right_body_button">
908 <property name="sizePolicy">
909 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
910 <horstretch>0</horstretch>
911 <verstretch>0</verstretch>
912 </sizepolicy>
913 </property>
914 <property name="minimumSize">
915 <size>
916 <width>57</width>
917 <height>0</height>
918 </size>
919 </property>
920 <property name="maximumSize">
921 <size>
922 <width>55</width>
923 <height>16777215</height>
924 </size>
925 </property>
926 <property name="styleSheet">
927 <string notr="true">min-width: 55px;</string>
928 </property>
929 <property name="text">
930 <string/>
931 </property>
932 </widget>
933 </item>
934 </layout>
935 </widget>
936 </item>
937 <item alignment="Qt::AlignHCenter">
938 <widget class="QGroupBox" name="player3RightButtonsGroup">
939 <property name="title">
940 <string>R Button</string>
941 </property>
942 <property name="alignment">
943 <set>Qt::AlignCenter</set>
944 </property>
945 <layout class="QVBoxLayout" name="verticalLayout_73">
946 <property name="spacing">
947 <number>3</number>
948 </property>
949 <property name="leftMargin">
950 <number>3</number>
951 </property>
952 <property name="topMargin">
953 <number>3</number>
954 </property>
955 <property name="rightMargin">
956 <number>3</number>
957 </property>
958 <property name="bottomMargin">
959 <number>3</number>
960 </property>
961 <item>
962 <widget class="QPushButton" name="player3_right_buttons_button">
963 <property name="sizePolicy">
964 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
965 <horstretch>0</horstretch>
966 <verstretch>0</verstretch>
967 </sizepolicy>
968 </property>
969 <property name="minimumSize">
970 <size>
971 <width>57</width>
972 <height>0</height>
973 </size>
974 </property>
975 <property name="maximumSize">
976 <size>
977 <width>55</width>
978 <height>16777215</height>
979 </size>
980 </property>
981 <property name="styleSheet">
982 <string notr="true">min-width: 55px;</string>
983 </property>
984 <property name="text">
985 <string/>
986 </property>
987 </widget>
988 </item>
989 </layout>
990 </widget>
991 </item>
992 </layout>
993 </widget>
994 </item>
995 </layout>
996 </widget>
997 </item>
998 <item>
999 <widget class="QGroupBox" name="player4Group">
1000 <property name="title">
1001 <string>Player 4</string>
1002 </property>
1003 <layout class="QHBoxLayout" name="horizontalLayout_16">
1004 <property name="spacing">
1005 <number>6</number>
1006 </property>
1007 <property name="leftMargin">
1008 <number>6</number>
1009 </property>
1010 <property name="topMargin">
1011 <number>0</number>
1012 </property>
1013 <property name="rightMargin">
1014 <number>6</number>
1015 </property>
1016 <property name="bottomMargin">
1017 <number>6</number>
1018 </property>
1019 <item>
1020 <widget class="QWidget" name="player4LeftJoycon" native="true">
1021 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_17">
1022 <property name="spacing">
1023 <number>0</number>
1024 </property>
1025 <property name="leftMargin">
1026 <number>0</number>
1027 </property>
1028 <property name="topMargin">
1029 <number>0</number>
1030 </property>
1031 <property name="rightMargin">
1032 <number>0</number>
1033 </property>
1034 <property name="bottomMargin">
1035 <number>0</number>
1036 </property>
1037 <item alignment="Qt::AlignHCenter">
1038 <widget class="QGroupBox" name="player4LeftBodyGroup">
1039 <property name="title">
1040 <string>L Body</string>
1041 </property>
1042 <property name="alignment">
1043 <set>Qt::AlignCenter</set>
1044 </property>
1045 <layout class="QVBoxLayout" name="verticalLayout_78">
1046 <property name="spacing">
1047 <number>3</number>
1048 </property>
1049 <property name="leftMargin">
1050 <number>3</number>
1051 </property>
1052 <property name="topMargin">
1053 <number>3</number>
1054 </property>
1055 <property name="rightMargin">
1056 <number>3</number>
1057 </property>
1058 <property name="bottomMargin">
1059 <number>3</number>
1060 </property>
1061 <item>
1062 <widget class="QPushButton" name="player4_left_body_button">
1063 <property name="sizePolicy">
1064 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1065 <horstretch>0</horstretch>
1066 <verstretch>0</verstretch>
1067 </sizepolicy>
1068 </property>
1069 <property name="minimumSize">
1070 <size>
1071 <width>57</width>
1072 <height>0</height>
1073 </size>
1074 </property>
1075 <property name="maximumSize">
1076 <size>
1077 <width>55</width>
1078 <height>16777215</height>
1079 </size>
1080 </property>
1081 <property name="styleSheet">
1082 <string notr="true">min-width: 55px;</string>
1083 </property>
1084 <property name="text">
1085 <string/>
1086 </property>
1087 </widget>
1088 </item>
1089 </layout>
1090 </widget>
1091 </item>
1092 <item alignment="Qt::AlignHCenter">
1093 <widget class="QGroupBox" name="player4LeftButtonsGroup">
1094 <property name="title">
1095 <string>L Button</string>
1096 </property>
1097 <property name="alignment">
1098 <set>Qt::AlignCenter</set>
1099 </property>
1100 <layout class="QVBoxLayout" name="verticalLayout_79">
1101 <property name="spacing">
1102 <number>3</number>
1103 </property>
1104 <property name="leftMargin">
1105 <number>3</number>
1106 </property>
1107 <property name="topMargin">
1108 <number>3</number>
1109 </property>
1110 <property name="rightMargin">
1111 <number>3</number>
1112 </property>
1113 <property name="bottomMargin">
1114 <number>3</number>
1115 </property>
1116 <item>
1117 <widget class="QPushButton" name="player4_left_buttons_button">
1118 <property name="sizePolicy">
1119 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1120 <horstretch>0</horstretch>
1121 <verstretch>0</verstretch>
1122 </sizepolicy>
1123 </property>
1124 <property name="minimumSize">
1125 <size>
1126 <width>57</width>
1127 <height>0</height>
1128 </size>
1129 </property>
1130 <property name="maximumSize">
1131 <size>
1132 <width>55</width>
1133 <height>16777215</height>
1134 </size>
1135 </property>
1136 <property name="styleSheet">
1137 <string notr="true">min-width: 55px;</string>
1138 </property>
1139 <property name="text">
1140 <string/>
1141 </property>
1142 </widget>
1143 </item>
1144 </layout>
1145 </widget>
1146 </item>
1147 </layout>
1148 </widget>
1149 </item>
1150 <item>
1151 <widget class="QWidget" name="player4RightJoycon" native="true">
1152 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_17">
1153 <property name="spacing">
1154 <number>0</number>
1155 </property>
1156 <property name="leftMargin">
1157 <number>0</number>
1158 </property>
1159 <property name="topMargin">
1160 <number>0</number>
1161 </property>
1162 <property name="rightMargin">
1163 <number>0</number>
1164 </property>
1165 <property name="bottomMargin">
1166 <number>0</number>
1167 </property>
1168 <item alignment="Qt::AlignHCenter">
1169 <widget class="QGroupBox" name="player4RightBodyGroup">
1170 <property name="title">
1171 <string>R Body</string>
1172 </property>
1173 <property name="alignment">
1174 <set>Qt::AlignCenter</set>
1175 </property>
1176 <layout class="QVBoxLayout" name="verticalLayout_76">
1177 <property name="spacing">
1178 <number>3</number>
1179 </property>
1180 <property name="leftMargin">
1181 <number>3</number>
1182 </property>
1183 <property name="topMargin">
1184 <number>3</number>
1185 </property>
1186 <property name="rightMargin">
1187 <number>3</number>
1188 </property>
1189 <property name="bottomMargin">
1190 <number>3</number>
1191 </property>
1192 <item>
1193 <widget class="QPushButton" name="player4_right_body_button">
1194 <property name="sizePolicy">
1195 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1196 <horstretch>0</horstretch>
1197 <verstretch>0</verstretch>
1198 </sizepolicy>
1199 </property>
1200 <property name="minimumSize">
1201 <size>
1202 <width>57</width>
1203 <height>0</height>
1204 </size>
1205 </property>
1206 <property name="maximumSize">
1207 <size>
1208 <width>55</width>
1209 <height>16777215</height>
1210 </size>
1211 </property>
1212 <property name="styleSheet">
1213 <string notr="true">min-width: 55px;</string>
1214 </property>
1215 <property name="text">
1216 <string/>
1217 </property>
1218 </widget>
1219 </item>
1220 </layout>
1221 </widget>
1222 </item>
1223 <item alignment="Qt::AlignHCenter">
1224 <widget class="QGroupBox" name="player4RightButtonsGroup">
1225 <property name="title">
1226 <string>R Button</string>
1227 </property>
1228 <property name="alignment">
1229 <set>Qt::AlignCenter</set>
1230 </property>
1231 <layout class="QVBoxLayout" name="verticalLayout_77">
1232 <property name="spacing">
1233 <number>3</number>
1234 </property>
1235 <property name="leftMargin">
1236 <number>3</number>
1237 </property>
1238 <property name="topMargin">
1239 <number>3</number>
1240 </property>
1241 <property name="rightMargin">
1242 <number>3</number>
1243 </property>
1244 <property name="bottomMargin">
1245 <number>3</number>
1246 </property>
1247 <item>
1248 <widget class="QPushButton" name="player4_right_buttons_button">
1249 <property name="sizePolicy">
1250 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1251 <horstretch>0</horstretch>
1252 <verstretch>0</verstretch>
1253 </sizepolicy>
1254 </property>
1255 <property name="minimumSize">
1256 <size>
1257 <width>57</width>
1258 <height>0</height>
1259 </size>
1260 </property>
1261 <property name="maximumSize">
1262 <size>
1263 <width>55</width>
1264 <height>16777215</height>
1265 </size>
1266 </property>
1267 <property name="styleSheet">
1268 <string notr="true">min-width: 55px;</string>
1269 </property>
1270 <property name="text">
1271 <string/>
1272 </property>
1273 </widget>
1274 </item>
1275 </layout>
1276 </widget>
1277 </item>
1278 </layout>
1279 </widget>
1280 </item>
1281 </layout>
1282 </widget>
1283 </item>
1284 </layout>
1285 </widget>
1286 </item>
1287 </layout>
1288 </widget>
1289 </item>
1290 <item>
1291 <widget class="QWidget" name="bottomLeftInputAdvanced" native="true">
1292 <layout class="QVBoxLayout" name="verticalLayout_5">
1293 <property name="spacing">
1294 <number>6</number>
1295 </property>
1296 <property name="leftMargin">
1297 <number>0</number>
1298 </property>
1299 <property name="topMargin">
1300 <number>0</number>
1301 </property>
1302 <property name="rightMargin">
1303 <number>0</number>
1304 </property>
1305 <property name="bottomMargin">
1306 <number>0</number>
1307 </property>
1308 <item>
1309 <widget class="QWidget" name="player56Widget" native="true">
1310 <layout class="QHBoxLayout" name="horizontalLayout_6">
1311 <property name="leftMargin">
1312 <number>0</number>
1313 </property>
1314 <property name="topMargin">
1315 <number>0</number>
1316 </property>
1317 <property name="rightMargin">
1318 <number>0</number>
1319 </property>
1320 <property name="bottomMargin">
1321 <number>0</number>
1322 </property>
1323 <item>
1324 <widget class="QGroupBox" name="player5Group">
1325 <property name="title">
1326 <string>Player 5</string>
1327 </property>
1328 <layout class="QHBoxLayout" name="horizontalLayout_10">
1329 <property name="spacing">
1330 <number>6</number>
1331 </property>
1332 <property name="leftMargin">
1333 <number>6</number>
1334 </property>
1335 <property name="topMargin">
1336 <number>0</number>
1337 </property>
1338 <property name="rightMargin">
1339 <number>6</number>
1340 </property>
1341 <property name="bottomMargin">
1342 <number>6</number>
1343 </property>
1344 <item>
1345 <widget class="QWidget" name="player5LeftJoycon" native="true">
1346 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_10">
1347 <property name="spacing">
1348 <number>0</number>
1349 </property>
1350 <property name="leftMargin">
1351 <number>0</number>
1352 </property>
1353 <property name="topMargin">
1354 <number>0</number>
1355 </property>
1356 <property name="rightMargin">
1357 <number>0</number>
1358 </property>
1359 <property name="bottomMargin">
1360 <number>0</number>
1361 </property>
1362 <item alignment="Qt::AlignHCenter">
1363 <widget class="QGroupBox" name="player5LeftBodyGroup">
1364 <property name="title">
1365 <string>L Body</string>
1366 </property>
1367 <property name="alignment">
1368 <set>Qt::AlignCenter</set>
1369 </property>
1370 <layout class="QVBoxLayout" name="verticalLayout_50">
1371 <property name="spacing">
1372 <number>3</number>
1373 </property>
1374 <property name="leftMargin">
1375 <number>3</number>
1376 </property>
1377 <property name="topMargin">
1378 <number>3</number>
1379 </property>
1380 <property name="rightMargin">
1381 <number>3</number>
1382 </property>
1383 <property name="bottomMargin">
1384 <number>3</number>
1385 </property>
1386 <item>
1387 <widget class="QPushButton" name="player5_left_body_button">
1388 <property name="sizePolicy">
1389 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1390 <horstretch>0</horstretch>
1391 <verstretch>0</verstretch>
1392 </sizepolicy>
1393 </property>
1394 <property name="minimumSize">
1395 <size>
1396 <width>57</width>
1397 <height>0</height>
1398 </size>
1399 </property>
1400 <property name="maximumSize">
1401 <size>
1402 <width>55</width>
1403 <height>16777215</height>
1404 </size>
1405 </property>
1406 <property name="styleSheet">
1407 <string notr="true">min-width: 55px;</string>
1408 </property>
1409 <property name="text">
1410 <string/>
1411 </property>
1412 </widget>
1413 </item>
1414 </layout>
1415 </widget>
1416 </item>
1417 <item alignment="Qt::AlignHCenter">
1418 <widget class="QGroupBox" name="player5LeftButtonsGroup">
1419 <property name="title">
1420 <string>L Button</string>
1421 </property>
1422 <property name="alignment">
1423 <set>Qt::AlignCenter</set>
1424 </property>
1425 <layout class="QVBoxLayout" name="verticalLayout_51">
1426 <property name="spacing">
1427 <number>3</number>
1428 </property>
1429 <property name="leftMargin">
1430 <number>3</number>
1431 </property>
1432 <property name="topMargin">
1433 <number>3</number>
1434 </property>
1435 <property name="rightMargin">
1436 <number>3</number>
1437 </property>
1438 <property name="bottomMargin">
1439 <number>3</number>
1440 </property>
1441 <item>
1442 <widget class="QPushButton" name="player5_left_buttons_button">
1443 <property name="sizePolicy">
1444 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1445 <horstretch>0</horstretch>
1446 <verstretch>0</verstretch>
1447 </sizepolicy>
1448 </property>
1449 <property name="minimumSize">
1450 <size>
1451 <width>57</width>
1452 <height>0</height>
1453 </size>
1454 </property>
1455 <property name="maximumSize">
1456 <size>
1457 <width>55</width>
1458 <height>16777215</height>
1459 </size>
1460 </property>
1461 <property name="styleSheet">
1462 <string notr="true">min-width: 55px;</string>
1463 </property>
1464 <property name="text">
1465 <string/>
1466 </property>
1467 </widget>
1468 </item>
1469 </layout>
1470 </widget>
1471 </item>
1472 </layout>
1473 </widget>
1474 </item>
1475 <item>
1476 <widget class="QWidget" name="player5RightJoycon" native="true">
1477 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_10">
1478 <property name="spacing">
1479 <number>0</number>
1480 </property>
1481 <property name="leftMargin">
1482 <number>0</number>
1483 </property>
1484 <property name="topMargin">
1485 <number>0</number>
1486 </property>
1487 <property name="rightMargin">
1488 <number>0</number>
1489 </property>
1490 <property name="bottomMargin">
1491 <number>0</number>
1492 </property>
1493 <item alignment="Qt::AlignHCenter">
1494 <widget class="QGroupBox" name="player5RightBodyGroup">
1495 <property name="title">
1496 <string>R Body</string>
1497 </property>
1498 <property name="alignment">
1499 <set>Qt::AlignCenter</set>
1500 </property>
1501 <layout class="QVBoxLayout" name="verticalLayout_48">
1502 <property name="spacing">
1503 <number>3</number>
1504 </property>
1505 <property name="leftMargin">
1506 <number>3</number>
1507 </property>
1508 <property name="topMargin">
1509 <number>3</number>
1510 </property>
1511 <property name="rightMargin">
1512 <number>3</number>
1513 </property>
1514 <property name="bottomMargin">
1515 <number>3</number>
1516 </property>
1517 <item>
1518 <widget class="QPushButton" name="player5_right_body_button">
1519 <property name="sizePolicy">
1520 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1521 <horstretch>0</horstretch>
1522 <verstretch>0</verstretch>
1523 </sizepolicy>
1524 </property>
1525 <property name="minimumSize">
1526 <size>
1527 <width>57</width>
1528 <height>0</height>
1529 </size>
1530 </property>
1531 <property name="maximumSize">
1532 <size>
1533 <width>55</width>
1534 <height>16777215</height>
1535 </size>
1536 </property>
1537 <property name="styleSheet">
1538 <string notr="true">min-width: 55px;</string>
1539 </property>
1540 <property name="text">
1541 <string/>
1542 </property>
1543 </widget>
1544 </item>
1545 </layout>
1546 </widget>
1547 </item>
1548 <item alignment="Qt::AlignHCenter">
1549 <widget class="QGroupBox" name="player5RightButtonsGroup">
1550 <property name="title">
1551 <string>R Button</string>
1552 </property>
1553 <property name="alignment">
1554 <set>Qt::AlignCenter</set>
1555 </property>
1556 <layout class="QVBoxLayout" name="verticalLayout_49">
1557 <property name="spacing">
1558 <number>3</number>
1559 </property>
1560 <property name="leftMargin">
1561 <number>3</number>
1562 </property>
1563 <property name="topMargin">
1564 <number>3</number>
1565 </property>
1566 <property name="rightMargin">
1567 <number>3</number>
1568 </property>
1569 <property name="bottomMargin">
1570 <number>3</number>
1571 </property>
1572 <item>
1573 <widget class="QPushButton" name="player5_right_buttons_button">
1574 <property name="sizePolicy">
1575 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1576 <horstretch>0</horstretch>
1577 <verstretch>0</verstretch>
1578 </sizepolicy>
1579 </property>
1580 <property name="minimumSize">
1581 <size>
1582 <width>57</width>
1583 <height>0</height>
1584 </size>
1585 </property>
1586 <property name="maximumSize">
1587 <size>
1588 <width>55</width>
1589 <height>16777215</height>
1590 </size>
1591 </property>
1592 <property name="styleSheet">
1593 <string notr="true">min-width: 55px;</string>
1594 </property>
1595 <property name="text">
1596 <string/>
1597 </property>
1598 </widget>
1599 </item>
1600 </layout>
1601 </widget>
1602 </item>
1603 </layout>
1604 </widget>
1605 </item>
1606 </layout>
1607 </widget>
1608 </item>
1609 <item>
1610 <widget class="QGroupBox" name="player6Group">
1611 <property name="title">
1612 <string>Player 6</string>
1613 </property>
1614 <layout class="QHBoxLayout" name="horizontalLayout_11">
1615 <property name="spacing">
1616 <number>6</number>
1617 </property>
1618 <property name="leftMargin">
1619 <number>6</number>
1620 </property>
1621 <property name="topMargin">
1622 <number>0</number>
1623 </property>
1624 <property name="rightMargin">
1625 <number>6</number>
1626 </property>
1627 <property name="bottomMargin">
1628 <number>6</number>
1629 </property>
1630 <item>
1631 <widget class="QWidget" name="player6LeftJoycon" native="true">
1632 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_11">
1633 <property name="spacing">
1634 <number>0</number>
1635 </property>
1636 <property name="leftMargin">
1637 <number>0</number>
1638 </property>
1639 <property name="topMargin">
1640 <number>0</number>
1641 </property>
1642 <property name="rightMargin">
1643 <number>0</number>
1644 </property>
1645 <property name="bottomMargin">
1646 <number>0</number>
1647 </property>
1648 <item alignment="Qt::AlignHCenter">
1649 <widget class="QGroupBox" name="player6LeftBodyGroup">
1650 <property name="title">
1651 <string>L Body</string>
1652 </property>
1653 <property name="alignment">
1654 <set>Qt::AlignCenter</set>
1655 </property>
1656 <layout class="QVBoxLayout" name="verticalLayout_54">
1657 <property name="spacing">
1658 <number>3</number>
1659 </property>
1660 <property name="leftMargin">
1661 <number>3</number>
1662 </property>
1663 <property name="topMargin">
1664 <number>3</number>
1665 </property>
1666 <property name="rightMargin">
1667 <number>3</number>
1668 </property>
1669 <property name="bottomMargin">
1670 <number>3</number>
1671 </property>
1672 <item>
1673 <widget class="QPushButton" name="player6_left_body_button">
1674 <property name="sizePolicy">
1675 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1676 <horstretch>0</horstretch>
1677 <verstretch>0</verstretch>
1678 </sizepolicy>
1679 </property>
1680 <property name="minimumSize">
1681 <size>
1682 <width>57</width>
1683 <height>0</height>
1684 </size>
1685 </property>
1686 <property name="maximumSize">
1687 <size>
1688 <width>55</width>
1689 <height>16777215</height>
1690 </size>
1691 </property>
1692 <property name="styleSheet">
1693 <string notr="true">min-width: 55px;</string>
1694 </property>
1695 <property name="text">
1696 <string/>
1697 </property>
1698 </widget>
1699 </item>
1700 </layout>
1701 </widget>
1702 </item>
1703 <item alignment="Qt::AlignHCenter">
1704 <widget class="QGroupBox" name="player6LeftButtonsGroup">
1705 <property name="title">
1706 <string>L Button</string>
1707 </property>
1708 <property name="alignment">
1709 <set>Qt::AlignCenter</set>
1710 </property>
1711 <layout class="QVBoxLayout" name="verticalLayout_55">
1712 <property name="spacing">
1713 <number>3</number>
1714 </property>
1715 <property name="leftMargin">
1716 <number>3</number>
1717 </property>
1718 <property name="topMargin">
1719 <number>3</number>
1720 </property>
1721 <property name="rightMargin">
1722 <number>3</number>
1723 </property>
1724 <property name="bottomMargin">
1725 <number>3</number>
1726 </property>
1727 <item>
1728 <widget class="QPushButton" name="player6_left_buttons_button">
1729 <property name="sizePolicy">
1730 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1731 <horstretch>0</horstretch>
1732 <verstretch>0</verstretch>
1733 </sizepolicy>
1734 </property>
1735 <property name="minimumSize">
1736 <size>
1737 <width>57</width>
1738 <height>0</height>
1739 </size>
1740 </property>
1741 <property name="maximumSize">
1742 <size>
1743 <width>55</width>
1744 <height>16777215</height>
1745 </size>
1746 </property>
1747 <property name="styleSheet">
1748 <string notr="true">min-width: 55px;</string>
1749 </property>
1750 <property name="text">
1751 <string/>
1752 </property>
1753 </widget>
1754 </item>
1755 </layout>
1756 </widget>
1757 </item>
1758 </layout>
1759 </widget>
1760 </item>
1761 <item>
1762 <widget class="QWidget" name="player6RightJoycon" native="true">
1763 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_11">
1764 <property name="spacing">
1765 <number>0</number>
1766 </property>
1767 <property name="leftMargin">
1768 <number>0</number>
1769 </property>
1770 <property name="topMargin">
1771 <number>0</number>
1772 </property>
1773 <property name="rightMargin">
1774 <number>0</number>
1775 </property>
1776 <property name="bottomMargin">
1777 <number>0</number>
1778 </property>
1779 <item alignment="Qt::AlignHCenter">
1780 <widget class="QGroupBox" name="player6RightBodyGroup">
1781 <property name="title">
1782 <string>R Body</string>
1783 </property>
1784 <property name="alignment">
1785 <set>Qt::AlignCenter</set>
1786 </property>
1787 <layout class="QVBoxLayout" name="verticalLayout_52">
1788 <property name="spacing">
1789 <number>3</number>
1790 </property>
1791 <property name="leftMargin">
1792 <number>3</number>
1793 </property>
1794 <property name="topMargin">
1795 <number>3</number>
1796 </property>
1797 <property name="rightMargin">
1798 <number>3</number>
1799 </property>
1800 <property name="bottomMargin">
1801 <number>3</number>
1802 </property>
1803 <item>
1804 <widget class="QPushButton" name="player6_right_body_button">
1805 <property name="sizePolicy">
1806 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1807 <horstretch>0</horstretch>
1808 <verstretch>0</verstretch>
1809 </sizepolicy>
1810 </property>
1811 <property name="minimumSize">
1812 <size>
1813 <width>57</width>
1814 <height>0</height>
1815 </size>
1816 </property>
1817 <property name="maximumSize">
1818 <size>
1819 <width>55</width>
1820 <height>16777215</height>
1821 </size>
1822 </property>
1823 <property name="styleSheet">
1824 <string notr="true">min-width: 55px;</string>
1825 </property>
1826 <property name="text">
1827 <string/>
1828 </property>
1829 </widget>
1830 </item>
1831 </layout>
1832 </widget>
1833 </item>
1834 <item alignment="Qt::AlignHCenter">
1835 <widget class="QGroupBox" name="player6RightButtonsGroup">
1836 <property name="title">
1837 <string>R Button</string>
1838 </property>
1839 <property name="alignment">
1840 <set>Qt::AlignCenter</set>
1841 </property>
1842 <layout class="QVBoxLayout" name="verticalLayout_53">
1843 <property name="spacing">
1844 <number>3</number>
1845 </property>
1846 <property name="leftMargin">
1847 <number>3</number>
1848 </property>
1849 <property name="topMargin">
1850 <number>3</number>
1851 </property>
1852 <property name="rightMargin">
1853 <number>3</number>
1854 </property>
1855 <property name="bottomMargin">
1856 <number>3</number>
1857 </property>
1858 <item>
1859 <widget class="QPushButton" name="player6_right_buttons_button">
1860 <property name="sizePolicy">
1861 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1862 <horstretch>0</horstretch>
1863 <verstretch>0</verstretch>
1864 </sizepolicy>
1865 </property>
1866 <property name="minimumSize">
1867 <size>
1868 <width>57</width>
1869 <height>0</height>
1870 </size>
1871 </property>
1872 <property name="maximumSize">
1873 <size>
1874 <width>55</width>
1875 <height>16777215</height>
1876 </size>
1877 </property>
1878 <property name="styleSheet">
1879 <string notr="true">min-width: 55px;</string>
1880 </property>
1881 <property name="text">
1882 <string/>
1883 </property>
1884 </widget>
1885 </item>
1886 </layout>
1887 </widget>
1888 </item>
1889 </layout>
1890 </widget>
1891 </item>
1892 </layout>
1893 </widget>
1894 </item>
1895 </layout>
1896 </widget>
1897 </item>
1898 <item>
1899 <widget class="QWidget" name="player78Widget" native="true">
1900 <layout class="QHBoxLayout" name="horizontalLayout_7">
1901 <property name="leftMargin">
1902 <number>0</number>
1903 </property>
1904 <property name="topMargin">
1905 <number>0</number>
1906 </property>
1907 <property name="rightMargin">
1908 <number>0</number>
1909 </property>
1910 <property name="bottomMargin">
1911 <number>0</number>
1912 </property>
1913 <item>
1914 <widget class="QGroupBox" name="player7Group">
1915 <property name="title">
1916 <string>Player 7</string>
1917 </property>
1918 <layout class="QHBoxLayout" name="horizontalLayout_12">
1919 <property name="spacing">
1920 <number>6</number>
1921 </property>
1922 <property name="leftMargin">
1923 <number>6</number>
1924 </property>
1925 <property name="topMargin">
1926 <number>0</number>
1927 </property>
1928 <property name="rightMargin">
1929 <number>6</number>
1930 </property>
1931 <property name="bottomMargin">
1932 <number>6</number>
1933 </property>
1934 <item>
1935 <widget class="QWidget" name="player7LeftJoycon" native="true">
1936 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_12">
1937 <property name="spacing">
1938 <number>0</number>
1939 </property>
1940 <property name="leftMargin">
1941 <number>0</number>
1942 </property>
1943 <property name="topMargin">
1944 <number>0</number>
1945 </property>
1946 <property name="rightMargin">
1947 <number>0</number>
1948 </property>
1949 <property name="bottomMargin">
1950 <number>0</number>
1951 </property>
1952 <item alignment="Qt::AlignHCenter">
1953 <widget class="QGroupBox" name="player7LeftBodyGroup">
1954 <property name="title">
1955 <string>L Body</string>
1956 </property>
1957 <property name="alignment">
1958 <set>Qt::AlignCenter</set>
1959 </property>
1960 <layout class="QVBoxLayout" name="verticalLayout_58">
1961 <property name="spacing">
1962 <number>3</number>
1963 </property>
1964 <property name="leftMargin">
1965 <number>3</number>
1966 </property>
1967 <property name="topMargin">
1968 <number>3</number>
1969 </property>
1970 <property name="rightMargin">
1971 <number>3</number>
1972 </property>
1973 <property name="bottomMargin">
1974 <number>3</number>
1975 </property>
1976 <item>
1977 <widget class="QPushButton" name="player7_left_body_button">
1978 <property name="sizePolicy">
1979 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1980 <horstretch>0</horstretch>
1981 <verstretch>0</verstretch>
1982 </sizepolicy>
1983 </property>
1984 <property name="minimumSize">
1985 <size>
1986 <width>57</width>
1987 <height>0</height>
1988 </size>
1989 </property>
1990 <property name="maximumSize">
1991 <size>
1992 <width>55</width>
1993 <height>16777215</height>
1994 </size>
1995 </property>
1996 <property name="styleSheet">
1997 <string notr="true">min-width: 55px;</string>
1998 </property>
1999 <property name="text">
2000 <string/>
2001 </property>
2002 </widget>
2003 </item>
2004 </layout>
2005 </widget>
2006 </item>
2007 <item alignment="Qt::AlignHCenter">
2008 <widget class="QGroupBox" name="player7LeftButtonsGroup">
2009 <property name="title">
2010 <string>L Button</string>
2011 </property>
2012 <property name="alignment">
2013 <set>Qt::AlignCenter</set>
2014 </property>
2015 <layout class="QVBoxLayout" name="verticalLayout_59">
2016 <property name="spacing">
2017 <number>3</number>
2018 </property>
2019 <property name="leftMargin">
2020 <number>3</number>
2021 </property>
2022 <property name="topMargin">
2023 <number>3</number>
2024 </property>
2025 <property name="rightMargin">
2026 <number>3</number>
2027 </property>
2028 <property name="bottomMargin">
2029 <number>3</number>
2030 </property>
2031 <item>
2032 <widget class="QPushButton" name="player7_left_buttons_button">
2033 <property name="sizePolicy">
2034 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2035 <horstretch>0</horstretch>
2036 <verstretch>0</verstretch>
2037 </sizepolicy>
2038 </property>
2039 <property name="minimumSize">
2040 <size>
2041 <width>57</width>
2042 <height>0</height>
2043 </size>
2044 </property>
2045 <property name="maximumSize">
2046 <size>
2047 <width>55</width>
2048 <height>16777215</height>
2049 </size>
2050 </property>
2051 <property name="styleSheet">
2052 <string notr="true">min-width: 55px;</string>
2053 </property>
2054 <property name="text">
2055 <string/>
2056 </property>
2057 </widget>
2058 </item>
2059 </layout>
2060 </widget>
2061 </item>
2062 </layout>
2063 </widget>
2064 </item>
2065 <item>
2066 <widget class="QWidget" name="player7RightJoycon" native="true">
2067 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_12">
2068 <property name="spacing">
2069 <number>0</number>
2070 </property>
2071 <property name="leftMargin">
2072 <number>0</number>
2073 </property>
2074 <property name="topMargin">
2075 <number>0</number>
2076 </property>
2077 <property name="rightMargin">
2078 <number>0</number>
2079 </property>
2080 <property name="bottomMargin">
2081 <number>0</number>
2082 </property>
2083 <item alignment="Qt::AlignHCenter">
2084 <widget class="QGroupBox" name="player7RightBodyGroup">
2085 <property name="title">
2086 <string>R Body</string>
2087 </property>
2088 <property name="alignment">
2089 <set>Qt::AlignCenter</set>
2090 </property>
2091 <layout class="QVBoxLayout" name="verticalLayout_56">
2092 <property name="spacing">
2093 <number>3</number>
2094 </property>
2095 <property name="leftMargin">
2096 <number>3</number>
2097 </property>
2098 <property name="topMargin">
2099 <number>3</number>
2100 </property>
2101 <property name="rightMargin">
2102 <number>3</number>
2103 </property>
2104 <property name="bottomMargin">
2105 <number>3</number>
2106 </property>
2107 <item>
2108 <widget class="QPushButton" name="player7_right_body_button">
2109 <property name="sizePolicy">
2110 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2111 <horstretch>0</horstretch>
2112 <verstretch>0</verstretch>
2113 </sizepolicy>
2114 </property>
2115 <property name="minimumSize">
2116 <size>
2117 <width>57</width>
2118 <height>0</height>
2119 </size>
2120 </property>
2121 <property name="maximumSize">
2122 <size>
2123 <width>55</width>
2124 <height>16777215</height>
2125 </size>
2126 </property>
2127 <property name="styleSheet">
2128 <string notr="true">min-width: 55px;</string>
2129 </property>
2130 <property name="text">
2131 <string/>
2132 </property>
2133 </widget>
2134 </item>
2135 </layout>
2136 </widget>
2137 </item>
2138 <item alignment="Qt::AlignHCenter">
2139 <widget class="QGroupBox" name="player7RightButtonsGroup">
2140 <property name="title">
2141 <string>R Button</string>
2142 </property>
2143 <property name="alignment">
2144 <set>Qt::AlignCenter</set>
2145 </property>
2146 <layout class="QVBoxLayout" name="verticalLayout_57">
2147 <property name="spacing">
2148 <number>3</number>
2149 </property>
2150 <property name="leftMargin">
2151 <number>3</number>
2152 </property>
2153 <property name="topMargin">
2154 <number>3</number>
2155 </property>
2156 <property name="rightMargin">
2157 <number>3</number>
2158 </property>
2159 <property name="bottomMargin">
2160 <number>3</number>
2161 </property>
2162 <item>
2163 <widget class="QPushButton" name="player7_right_buttons_button">
2164 <property name="sizePolicy">
2165 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2166 <horstretch>0</horstretch>
2167 <verstretch>0</verstretch>
2168 </sizepolicy>
2169 </property>
2170 <property name="minimumSize">
2171 <size>
2172 <width>57</width>
2173 <height>0</height>
2174 </size>
2175 </property>
2176 <property name="maximumSize">
2177 <size>
2178 <width>55</width>
2179 <height>16777215</height>
2180 </size>
2181 </property>
2182 <property name="styleSheet">
2183 <string notr="true">min-width: 55px;</string>
2184 </property>
2185 <property name="text">
2186 <string/>
2187 </property>
2188 </widget>
2189 </item>
2190 </layout>
2191 </widget>
2192 </item>
2193 </layout>
2194 </widget>
2195 </item>
2196 </layout>
2197 </widget>
2198 </item>
2199 <item>
2200 <widget class="QGroupBox" name="player8Group">
2201 <property name="title">
2202 <string>Player 8</string>
2203 </property>
2204 <layout class="QHBoxLayout" name="horizontalLayout_13">
2205 <property name="spacing">
2206 <number>6</number>
2207 </property>
2208 <property name="leftMargin">
2209 <number>6</number>
2210 </property>
2211 <property name="topMargin">
2212 <number>0</number>
2213 </property>
2214 <property name="rightMargin">
2215 <number>6</number>
2216 </property>
2217 <property name="bottomMargin">
2218 <number>6</number>
2219 </property>
2220 <item>
2221 <widget class="QWidget" name="player8LeftJoycon" native="true">
2222 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_13">
2223 <property name="spacing">
2224 <number>0</number>
2225 </property>
2226 <property name="leftMargin">
2227 <number>0</number>
2228 </property>
2229 <property name="topMargin">
2230 <number>0</number>
2231 </property>
2232 <property name="rightMargin">
2233 <number>0</number>
2234 </property>
2235 <property name="bottomMargin">
2236 <number>0</number>
2237 </property>
2238 <item alignment="Qt::AlignHCenter">
2239 <widget class="QGroupBox" name="player8LeftBodyGroup">
2240 <property name="title">
2241 <string>L Body</string>
2242 </property>
2243 <property name="alignment">
2244 <set>Qt::AlignCenter</set>
2245 </property>
2246 <layout class="QVBoxLayout" name="verticalLayout_62">
2247 <property name="spacing">
2248 <number>3</number>
2249 </property>
2250 <property name="leftMargin">
2251 <number>3</number>
2252 </property>
2253 <property name="topMargin">
2254 <number>3</number>
2255 </property>
2256 <property name="rightMargin">
2257 <number>3</number>
2258 </property>
2259 <property name="bottomMargin">
2260 <number>3</number>
2261 </property>
2262 <item>
2263 <widget class="QPushButton" name="player8_left_body_button">
2264 <property name="sizePolicy">
2265 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2266 <horstretch>0</horstretch>
2267 <verstretch>0</verstretch>
2268 </sizepolicy>
2269 </property>
2270 <property name="minimumSize">
2271 <size>
2272 <width>57</width>
2273 <height>0</height>
2274 </size>
2275 </property>
2276 <property name="maximumSize">
2277 <size>
2278 <width>55</width>
2279 <height>16777215</height>
2280 </size>
2281 </property>
2282 <property name="styleSheet">
2283 <string notr="true">min-width: 55px;</string>
2284 </property>
2285 <property name="text">
2286 <string/>
2287 </property>
2288 </widget>
2289 </item>
2290 </layout>
2291 </widget>
2292 </item>
2293 <item alignment="Qt::AlignHCenter">
2294 <widget class="QGroupBox" name="player8LeftButtonsGroup">
2295 <property name="title">
2296 <string>L Button</string>
2297 </property>
2298 <property name="alignment">
2299 <set>Qt::AlignCenter</set>
2300 </property>
2301 <layout class="QVBoxLayout" name="verticalLayout_63">
2302 <property name="spacing">
2303 <number>3</number>
2304 </property>
2305 <property name="leftMargin">
2306 <number>3</number>
2307 </property>
2308 <property name="topMargin">
2309 <number>3</number>
2310 </property>
2311 <property name="rightMargin">
2312 <number>3</number>
2313 </property>
2314 <property name="bottomMargin">
2315 <number>3</number>
2316 </property>
2317 <item>
2318 <widget class="QPushButton" name="player8_left_buttons_button">
2319 <property name="sizePolicy">
2320 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2321 <horstretch>0</horstretch>
2322 <verstretch>0</verstretch>
2323 </sizepolicy>
2324 </property>
2325 <property name="minimumSize">
2326 <size>
2327 <width>57</width>
2328 <height>0</height>
2329 </size>
2330 </property>
2331 <property name="maximumSize">
2332 <size>
2333 <width>55</width>
2334 <height>16777215</height>
2335 </size>
2336 </property>
2337 <property name="styleSheet">
2338 <string notr="true">min-width: 55px;</string>
2339 </property>
2340 <property name="text">
2341 <string/>
2342 </property>
2343 </widget>
2344 </item>
2345 </layout>
2346 </widget>
2347 </item>
2348 </layout>
2349 </widget>
2350 </item>
2351 <item>
2352 <widget class="QWidget" name="player8RightJoycon" native="true">
2353 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_13">
2354 <property name="spacing">
2355 <number>0</number>
2356 </property>
2357 <property name="leftMargin">
2358 <number>0</number>
2359 </property>
2360 <property name="topMargin">
2361 <number>0</number>
2362 </property>
2363 <property name="rightMargin">
2364 <number>0</number>
2365 </property>
2366 <property name="bottomMargin">
2367 <number>0</number>
2368 </property>
2369 <item alignment="Qt::AlignHCenter">
2370 <widget class="QGroupBox" name="player8RightBodyGroup">
2371 <property name="title">
2372 <string>R Body</string>
2373 </property>
2374 <property name="alignment">
2375 <set>Qt::AlignCenter</set>
2376 </property>
2377 <layout class="QVBoxLayout" name="verticalLayout_60">
2378 <property name="spacing">
2379 <number>3</number>
2380 </property>
2381 <property name="leftMargin">
2382 <number>3</number>
2383 </property>
2384 <property name="topMargin">
2385 <number>3</number>
2386 </property>
2387 <property name="rightMargin">
2388 <number>3</number>
2389 </property>
2390 <property name="bottomMargin">
2391 <number>3</number>
2392 </property>
2393 <item>
2394 <widget class="QPushButton" name="player8_right_body_button">
2395 <property name="sizePolicy">
2396 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2397 <horstretch>0</horstretch>
2398 <verstretch>0</verstretch>
2399 </sizepolicy>
2400 </property>
2401 <property name="minimumSize">
2402 <size>
2403 <width>57</width>
2404 <height>0</height>
2405 </size>
2406 </property>
2407 <property name="maximumSize">
2408 <size>
2409 <width>55</width>
2410 <height>16777215</height>
2411 </size>
2412 </property>
2413 <property name="styleSheet">
2414 <string notr="true">min-width: 55px;</string>
2415 </property>
2416 <property name="text">
2417 <string/>
2418 </property>
2419 </widget>
2420 </item>
2421 </layout>
2422 </widget>
2423 </item>
2424 <item alignment="Qt::AlignHCenter">
2425 <widget class="QGroupBox" name="player8RightButtonsGroup">
2426 <property name="title">
2427 <string>R Button</string>
2428 </property>
2429 <property name="alignment">
2430 <set>Qt::AlignCenter</set>
2431 </property>
2432 <layout class="QVBoxLayout" name="verticalLayout_61">
2433 <property name="spacing">
2434 <number>3</number>
2435 </property>
2436 <property name="leftMargin">
2437 <number>3</number>
2438 </property>
2439 <property name="topMargin">
2440 <number>3</number>
2441 </property>
2442 <property name="rightMargin">
2443 <number>3</number>
2444 </property>
2445 <property name="bottomMargin">
2446 <number>3</number>
2447 </property>
2448 <item>
2449 <widget class="QPushButton" name="player8_right_buttons_button">
2450 <property name="sizePolicy">
2451 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2452 <horstretch>0</horstretch>
2453 <verstretch>0</verstretch>
2454 </sizepolicy>
2455 </property>
2456 <property name="minimumSize">
2457 <size>
2458 <width>57</width>
2459 <height>0</height>
2460 </size>
2461 </property>
2462 <property name="maximumSize">
2463 <size>
2464 <width>55</width>
2465 <height>16777215</height>
2466 </size>
2467 </property>
2468 <property name="styleSheet">
2469 <string notr="true">min-width: 55px;</string>
2470 </property>
2471 <property name="text">
2472 <string/>
2473 </property>
2474 </widget>
2475 </item>
2476 </layout>
2477 </widget>
2478 </item>
2479 </layout>
2480 </widget>
2481 </item>
2482 </layout>
2483 </widget>
2484 </item>
2485 </layout>
2486 </widget>
2487 </item>
2488 </layout>
2489 </widget>
2490 </item>
2491 </layout>
2492 </widget>
2493 </item>
2494 </layout>
2495 </widget>
2496 </item>
2497 <item>
2498 <widget class="QWidget" name="rightInputAdvanced" native="true">
2499 <layout class="QVBoxLayout" name="rightLayout" stretch="1,1">
2500 <property name="spacing">
2501 <number>3</number>
2502 </property>
2503 <property name="leftMargin">
2504 <number>0</number>
2505 </property>
2506 <property name="topMargin">
2507 <number>0</number>
2508 </property>
2509 <property name="rightMargin">
2510 <number>0</number>
2511 </property>
2512 <property name="bottomMargin">
2513 <number>0</number>
2514 </property>
2515 <item>
2516 <widget class="QWidget" name="topRightInputAdvanced" native="true">
2517 <layout class="QVBoxLayout" name="verticalLayout">
2518 <property name="leftMargin">
2519 <number>0</number>
2520 </property>
2521 <property name="topMargin">
2522 <number>0</number>
2523 </property>
2524 <property name="rightMargin">
2525 <number>0</number>
2526 </property>
2527 <property name="bottomMargin">
2528 <number>0</number>
2529 </property>
2530 <item>
2531 <widget class="QGroupBox" name="gridGroupBox_3">
2532 <property name="title">
2533 <string>Other</string>
2534 </property>
2535 <layout class="QGridLayout" name="gridLayout_3">
2536 <item row="0" column="0">
2537 <widget class="QCheckBox" name="keyboard_enabled">
2538 <property name="minimumSize">
2539 <size>
2540 <width>0</width>
2541 <height>23</height>
2542 </size>
2543 </property>
2544 <property name="text">
2545 <string>Keyboard</string>
2546 </property>
2547 </widget>
2548 </item>
2549 <item row="4" column="2">
2550 <widget class="QPushButton" name="touchscreen_advanced">
2551 <property name="text">
2552 <string>Advanced</string>
2553 </property>
2554 </widget>
2555 </item>
2556 <item row="1" column="1">
2557 <spacer name="horizontalSpacer_8">
2558 <property name="orientation">
2559 <enum>Qt::Horizontal</enum>
2560 </property>
2561 <property name="sizeType">
2562 <enum>QSizePolicy::Fixed</enum>
2563 </property>
2564 <property name="sizeHint" stdset="0">
2565 <size>
2566 <width>76</width>
2567 <height>20</height>
2568 </size>
2569 </property>
2570 </spacer>
2571 </item>
2572 <item row="1" column="2">
2573 <widget class="QPushButton" name="mouse_advanced">
2574 <property name="text">
2575 <string>Advanced</string>
2576 </property>
2577 </widget>
2578 </item>
2579 <item row="4" column="0">
2580 <widget class="QCheckBox" name="touchscreen_enabled">
2581 <property name="text">
2582 <string>Touchscreen</string>
2583 </property>
2584 </widget>
2585 </item>
2586 <item row="1" column="0">
2587 <widget class="QCheckBox" name="mouse_enabled">
2588 <property name="minimumSize">
2589 <size>
2590 <width>0</width>
2591 <height>23</height>
2592 </size>
2593 </property>
2594 <property name="text">
2595 <string>Mouse</string>
2596 </property>
2597 </widget>
2598 </item>
2599 <item row="6" column="0">
2600 <widget class="QLabel" name="motion_touch">
2601 <property name="text">
2602 <string>Motion / Touch</string>
2603 </property>
2604 </widget>
2605 </item>
2606 <item row="6" column="2">
2607 <widget class="QPushButton" name="buttonMotionTouch">
2608 <property name="text">
2609 <string>Configure</string>
2610 </property>
2611 </widget>
2612 </item>
2613 <item row="5" column="0">
2614 <widget class="QCheckBox" name="debug_enabled">
2615 <property name="text">
2616 <string>Debug Controller</string>
2617 </property>
2618 </widget>
2619 </item>
2620 <item row="5" column="2">
2621 <widget class="QPushButton" name="debug_configure">
2622 <property name="text">
2623 <string>Configure</string>
2624 </property>
2625 </widget>
2626 </item>
2627 </layout>
2628 </widget>
2629 </item>
2630 <item>
2631 <spacer name="verticalSpacer">
2632 <property name="orientation">
2633 <enum>Qt::Vertical</enum>
2634 </property>
2635 <property name="sizeHint" stdset="0">
2636 <size>
2637 <width>20</width>
2638 <height>40</height>
2639 </size>
2640 </property>
2641 </spacer>
2642 </item>
2643 </layout>
2644 </widget>
2645 </item>
2646 <item>
2647 <widget class="QWidget" name="bottomRightInputAdvanced" native="true">
2648 <layout class="QVBoxLayout" name="verticalLayout_2">
2649 <property name="leftMargin">
2650 <number>0</number>
2651 </property>
2652 <property name="topMargin">
2653 <number>0</number>
2654 </property>
2655 <property name="rightMargin">
2656 <number>0</number>
2657 </property>
2658 <property name="bottomMargin">
2659 <number>0</number>
2660 </property>
2661 <item>
2662 <spacer name="mainVerticalSpacer">
2663 <property name="orientation">
2664 <enum>Qt::Vertical</enum>
2665 </property>
2666 <property name="sizeHint" stdset="0">
2667 <size>
2668 <width>20</width>
2669 <height>40</height>
2670 </size>
2671 </property>
2672 </spacer>
2673 </item>
2674 </layout>
2675 </widget>
2676 </item>
2677 </layout>
2678 </widget>
2679 </item>
2680 </layout>
2681 </widget>
2682 </item>
2683 </layout>
2684 </widget>
2685 <resources>
2686 </resources>
2687 <connections/>
2688</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 597defe8c..13ecb3dc5 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -5,39 +5,97 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QColorDialog>
9#include <QGridLayout> 8#include <QGridLayout>
9#include <QInputDialog>
10#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QMenu> 11#include <QMenu>
12#include <QMessageBox> 12#include <QMessageBox>
13#include <QTimer> 13#include <QTimer>
14#include "common/assert.h"
15#include "common/param_package.h" 14#include "common/param_package.h"
15#include "core/core.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/hid/hid.h"
18#include "core/hle/service/sm/sm.h"
19#include "input_common/gcadapter/gc_poller.h"
16#include "input_common/main.h" 20#include "input_common/main.h"
17#include "ui_configure_input_player.h" 21#include "ui_configure_input_player.h"
18#include "yuzu/configuration/config.h" 22#include "yuzu/configuration/config.h"
19#include "yuzu/configuration/configure_input_player.h" 23#include "yuzu/configuration/configure_input_player.h"
20 24
25constexpr std::size_t HANDHELD_INDEX = 8;
26
21const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 27const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
22 ConfigureInputPlayer::analog_sub_buttons{{ 28 ConfigureInputPlayer::analog_sub_buttons{{
23 "up", 29 "up",
24 "down", 30 "down",
25 "left", 31 "left",
26 "right", 32 "right",
27 "modifier",
28 }}; 33 }};
29 34
30static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) { 35namespace {
31 const int index1 = grid->indexOf(item); 36
32 const int index2 = grid->indexOf(onTopOf); 37void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
33 int row, column, rowSpan, columnSpan; 38 bool connected) {
34 grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan); 39 Core::System& system{Core::System::GetInstance()};
35 grid->takeAt(index1); 40 if (!system.IsPoweredOn()) {
36 grid->addWidget(item, row, column, rowSpan, columnSpan); 41 return;
42 }
43 Service::SM::ServiceManager& sm = system.ServiceManager();
44
45 auto& npad =
46 sm.GetService<Service::HID::Hid>("hid")
47 ->GetAppletResource()
48 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
49
50 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
51}
52
53/// Maps the controller type combobox index to Controller Type enum
54constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
55 switch (index) {
56 case 0:
57 default:
58 return Settings::ControllerType::ProController;
59 case 1:
60 return Settings::ControllerType::DualJoyconDetached;
61 case 2:
62 return Settings::ControllerType::LeftJoycon;
63 case 3:
64 return Settings::ControllerType::RightJoycon;
65 case 4:
66 return Settings::ControllerType::Handheld;
67 }
68}
69
70/// Maps the Controller Type enum to controller type combobox index
71constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
72 switch (type) {
73 case Settings::ControllerType::ProController:
74 default:
75 return 0;
76 case Settings::ControllerType::DualJoyconDetached:
77 return 1;
78 case Settings::ControllerType::LeftJoycon:
79 return 2;
80 case Settings::ControllerType::RightJoycon:
81 return 3;
82 case Settings::ControllerType::Handheld:
83 return 4;
84 }
37} 85}
38 86
39static QString GetKeyName(int key_code) { 87QString GetKeyName(int key_code) {
40 switch (key_code) { 88 switch (key_code) {
89 case Qt::LeftButton:
90 return QObject::tr("Click 0");
91 case Qt::RightButton:
92 return QObject::tr("Click 1");
93 case Qt::MiddleButton:
94 return QObject::tr("Click 2");
95 case Qt::BackButton:
96 return QObject::tr("Click 3");
97 case Qt::ForwardButton:
98 return QObject::tr("Click 4");
41 case Qt::Key_Shift: 99 case Qt::Key_Shift:
42 return QObject::tr("Shift"); 100 return QObject::tr("Shift");
43 case Qt::Key_Control: 101 case Qt::Key_Control:
@@ -51,9 +109,16 @@ static QString GetKeyName(int key_code) {
51 } 109 }
52} 110}
53 111
54static void SetAnalogButton(const Common::ParamPackage& input_param, 112void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
55 Common::ParamPackage& analog_param, const std::string& button_name) { 113 const std::string& button_name) {
56 if (analog_param.Get("engine", "") != "analog_from_button") { 114 // The poller returned a complete axis, so set all the buttons
115 if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
116 analog_param = input_param;
117 return;
118 }
119 // Check if the current configuration has either no engine or an axis binding.
120 // Clears out the old binding and adds one with analog_from_button.
121 if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
57 analog_param = { 122 analog_param = {
58 {"engine", "analog_from_button"}, 123 {"engine", "analog_from_button"},
59 }; 124 };
@@ -61,7 +126,7 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
61 analog_param.Set(button_name, input_param.Serialize()); 126 analog_param.Set(button_name, input_param.Serialize());
62} 127}
63 128
64static QString ButtonToText(const Common::ParamPackage& param) { 129QString ButtonToText(const Common::ParamPackage& param) {
65 if (!param.Has("engine")) { 130 if (!param.Has("engine")) {
66 return QObject::tr("[not set]"); 131 return QObject::tr("[not set]");
67 } 132 }
@@ -111,7 +176,7 @@ static QString ButtonToText(const Common::ParamPackage& param) {
111 return QObject::tr("[unknown]"); 176 return QObject::tr("[unknown]");
112} 177}
113 178
114static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { 179QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
115 if (!param.Has("engine")) { 180 if (!param.Has("engine")) {
116 return QObject::tr("[not set]"); 181 return QObject::tr("[not set]");
117 } 182 }
@@ -161,22 +226,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
161 } 226 }
162 return QObject::tr("[unknown]"); 227 return QObject::tr("[unknown]");
163} 228}
164 229} // namespace
165ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) 230
166 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 231ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
167 debug(debug), timeout_timer(std::make_unique<QTimer>()), 232 QWidget* bottom_row,
168 poll_timer(std::make_unique<QTimer>()) { 233 InputCommon::InputSubsystem* input_subsystem_,
234 bool debug)
235 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
236 debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
237 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
169 ui->setupUi(this); 238 ui->setupUi(this);
239
170 setFocusPolicy(Qt::ClickFocus); 240 setFocusPolicy(Qt::ClickFocus);
171 241
172 button_map = { 242 button_map = {
173 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, 243 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
174 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, 244 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
175 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, 245 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
176 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, 246 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
177 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown, 247 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
178 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
179 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
180 }; 248 };
181 249
182 analog_map_buttons = {{ 250 analog_map_buttons = {{
@@ -185,223 +253,165 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
185 ui->buttonLStickDown, 253 ui->buttonLStickDown,
186 ui->buttonLStickLeft, 254 ui->buttonLStickLeft,
187 ui->buttonLStickRight, 255 ui->buttonLStickRight,
188 ui->buttonLStickMod,
189 }, 256 },
190 { 257 {
191 ui->buttonRStickUp, 258 ui->buttonRStickUp,
192 ui->buttonRStickDown, 259 ui->buttonRStickDown,
193 ui->buttonRStickLeft, 260 ui->buttonRStickLeft,
194 ui->buttonRStickRight, 261 ui->buttonRStickRight,
195 ui->buttonRStickMod,
196 }, 262 },
197 }}; 263 }};
198 264
199 debug_hidden = { 265 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
200 ui->buttonSL, ui->labelSL, 266 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
201 ui->buttonSR, ui->labelSR, 267 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
202 ui->buttonLStick, ui->labelLStickPressed, 268 analog_map_modifier_label = {ui->labelLStickModifierRange, ui->labelRStickModifierRange};
203 ui->buttonRStick, ui->labelRStickPressed, 269 analog_map_modifier_slider = {ui->sliderLStickModifierRange, ui->sliderRStickModifierRange};
204 ui->buttonHome, ui->labelHome, 270 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
205 ui->buttonScreenshot, ui->labelScreenshot, 271 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
206 };
207
208 auto layout = Settings::values.players[player_index].type;
209 if (debug)
210 layout = Settings::ControllerType::DualJoycon;
211
212 switch (layout) {
213 case Settings::ControllerType::ProController:
214 case Settings::ControllerType::DualJoycon:
215 layout_hidden = {
216 ui->buttonSL,
217 ui->labelSL,
218 ui->buttonSR,
219 ui->labelSR,
220 };
221 break;
222 case Settings::ControllerType::LeftJoycon:
223 layout_hidden = {
224 ui->right_body_button,
225 ui->right_buttons_button,
226 ui->right_body_label,
227 ui->right_buttons_label,
228 ui->buttonR,
229 ui->labelR,
230 ui->buttonZR,
231 ui->labelZR,
232 ui->labelHome,
233 ui->buttonHome,
234 ui->buttonPlus,
235 ui->labelPlus,
236 ui->RStick,
237 ui->faceButtons,
238 };
239 break;
240 case Settings::ControllerType::RightJoycon:
241 layout_hidden = {
242 ui->left_body_button, ui->left_buttons_button,
243 ui->left_body_label, ui->left_buttons_label,
244 ui->buttonL, ui->labelL,
245 ui->buttonZL, ui->labelZL,
246 ui->labelScreenshot, ui->buttonScreenshot,
247 ui->buttonMinus, ui->labelMinus,
248 ui->LStick, ui->Dpad,
249 };
250 break;
251 }
252
253 if (debug || layout == Settings::ControllerType::ProController) {
254 ui->controller_color->hide();
255 } else {
256 if (layout == Settings::ControllerType::LeftJoycon ||
257 layout == Settings::ControllerType::RightJoycon) {
258 ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
259
260 LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
261 LayerGridElements(ui->buttons, ui->misc, ui->RStick);
262 LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
263 LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
264 }
265 }
266
267 for (auto* widget : layout_hidden)
268 widget->setVisible(false);
269
270 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
271 analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier,
272 ui->sliderRStickDeadzoneAndModifier};
273 analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier,
274 ui->labelRStickDeadzoneAndModifier};
275 272
276 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 273 const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
277 auto* const button = button_map[button_id]; 274 int default_val) {
278 if (button == nullptr) {
279 continue;
280 }
281
282 button->setContextMenuPolicy(Qt::CustomContextMenu);
283 connect(button, &QPushButton::clicked, [=, this] { 275 connect(button, &QPushButton::clicked, [=, this] {
284 HandleClick( 276 HandleClick(
285 button_map[button_id], 277 button,
286 [=, this](Common::ParamPackage params) { 278 [=, this](Common::ParamPackage params) {
287 // Workaround for ZL & ZR for analog triggers like on XBOX controllors. 279 // Workaround for ZL & ZR for analog triggers like on XBOX
288 // Analog triggers (from controllers like the XBOX controller) would not 280 // controllers. Analog triggers (from controllers like the XBOX
289 // work due to a different range of their signals (from 0 to 255 on 281 // controller) would not work due to a different range of their
290 // analog triggers instead of -32768 to 32768 on analog joysticks). The 282 // signals (from 0 to 255 on analog triggers instead of -32768 to
291 // SDL driver misinterprets analog triggers as analog joysticks. 283 // 32768 on analog joysticks). The SDL driver misinterprets analog
284 // triggers as analog joysticks.
292 // TODO: reinterpret the signal range for analog triggers to map the 285 // TODO: reinterpret the signal range for analog triggers to map the
293 // values correctly. This is required for the correct emulation of the 286 // values correctly. This is required for the correct emulation of
294 // analog triggers of the GameCube controller. 287 // the analog triggers of the GameCube controller.
295 if (button_id == Settings::NativeButton::ZL || 288 if (button == ui->buttonZL || button == ui->buttonZR) {
296 button_id == Settings::NativeButton::ZR) {
297 params.Set("direction", "+"); 289 params.Set("direction", "+");
298 params.Set("threshold", "0.5"); 290 params.Set("threshold", "0.5");
299 } 291 }
300 buttons_param[button_id] = std::move(params); 292 *param = std::move(params);
301 }, 293 },
302 InputCommon::Polling::DeviceType::Button); 294 InputCommon::Polling::DeviceType::Button);
303 }); 295 });
304 connect(button, &QPushButton::customContextMenuRequested, 296 };
305 [=, this](const QPoint& menu_location) { 297
306 QMenu context_menu; 298 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
307 context_menu.addAction(tr("Clear"), [&] { 299 auto* const button = button_map[button_id];
308 buttons_param[button_id].Clear(); 300 if (button == nullptr) {
309 button_map[button_id]->setText(tr("[not set]")); 301 continue;
310 }); 302 }
311 context_menu.addAction(tr("Restore Default"), [&] { 303 ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
312 buttons_param[button_id] = Common::ParamPackage{ 304 Config::default_buttons[button_id]);
313 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
314 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
315 });
316 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
317 });
318 } 305 }
319 306
320 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 307 // Handle clicks for the modifier buttons as well.
321 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 308 ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
309 ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
310
311 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
312 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
322 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 313 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
314
323 if (analog_button == nullptr) { 315 if (analog_button == nullptr) {
324 continue; 316 continue;
325 } 317 }
326 318
327 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
328 connect(analog_button, &QPushButton::clicked, [=, this] { 319 connect(analog_button, &QPushButton::clicked, [=, this] {
329 HandleClick( 320 HandleClick(
330 analog_map_buttons[analog_id][sub_button_id], 321 analog_map_buttons[analog_id][sub_button_id],
331 [=, this](const Common::ParamPackage& params) { 322 [=, this](const Common::ParamPackage& params) {
332 SetAnalogButton(params, analogs_param[analog_id], 323 SetAnalogParam(params, analogs_param[analog_id],
333 analog_sub_buttons[sub_button_id]); 324 analog_sub_buttons[sub_button_id]);
334 }, 325 },
335 InputCommon::Polling::DeviceType::Button); 326 InputCommon::Polling::DeviceType::AnalogPreferred);
336 }); 327 });
337 connect(analog_button, &QPushButton::customContextMenuRequested,
338 [=, this](const QPoint& menu_location) {
339 QMenu context_menu;
340 context_menu.addAction(tr("Clear"), [&] {
341 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
342 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
343 });
344 context_menu.addAction(tr("Restore Default"), [&] {
345 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
346 Config::default_analogs[analog_id][sub_button_id])};
347 SetAnalogButton(params, analogs_param[analog_id],
348 analog_sub_buttons[sub_button_id]);
349 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
350 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
351 });
352 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
353 menu_location));
354 });
355 } 328 }
356 connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {
357 if (QMessageBox::information(
358 this, tr("Information"),
359 tr("After pressing OK, first move your joystick horizontally, "
360 "and then vertically."),
361 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
362 HandleClick(
363 analog_map_stick[analog_id],
364 [=, this](const Common::ParamPackage& params) {
365 analogs_param[analog_id] = params;
366 },
367 InputCommon::Polling::DeviceType::Analog);
368 }
369 });
370 329
371 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, 330 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
372 [=, this] { 331 [=, this] {
373 const float slider_value = 332 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
374 analog_map_deadzone_and_modifier_slider[analog_id]->value(); 333 analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
375 if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
376 analogs_param[analog_id].Get("engine", "") == "gcpad") {
377 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
378 tr("Deadzone: %1%").arg(slider_value));
379 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
380 } else {
381 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
382 tr("Modifier Scale: %1%").arg(slider_value));
383 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
384 }
385 }); 334 });
335
336 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
337 const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
338 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
339 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
340 });
341
342 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
343 const auto slider_value = analog_map_modifier_slider[analog_id]->value();
344 analog_map_modifier_label[analog_id]->setText(
345 tr("Modifier Range: %1%").arg(slider_value));
346 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
347 });
386 } 348 }
387 349
388 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); 350 // Player Connected checkbox
389 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); 351 connect(ui->groupConnectedController, &QGroupBox::toggled,
352 [this](bool checked) { emit Connected(checked); });
353
354 // Set up controller type. Only Player 1 can choose Handheld.
355 ui->comboControllerType->clear();
356
357 QStringList controller_types = {
358 tr("Pro Controller"),
359 tr("Dual Joycons"),
360 tr("Left Joycon"),
361 tr("Right Joycon"),
362 };
363
364 if (player_index == 0) {
365 controller_types.append(tr("Handheld"));
366 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
367 [this](int index) {
368 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
369 Settings::ControllerType::Handheld);
370 });
371 }
372
373 // The Debug Controller can only choose the Pro Controller.
374 if (debug) {
375 ui->buttonScreenshot->setEnabled(false);
376 ui->buttonHome->setEnabled(false);
377 ui->groupConnectedController->setCheckable(false);
378 QStringList debug_controller_types = {
379 tr("Pro Controller"),
380 };
381 ui->comboControllerType->addItems(debug_controller_types);
382 } else {
383 ui->comboControllerType->addItems(controller_types);
384 }
385
386 UpdateControllerIcon();
387 UpdateControllerAvailableButtons();
388 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
389 UpdateControllerIcon();
390 UpdateControllerAvailableButtons();
391 });
392
393 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
394 &ConfigureInputPlayer::UpdateMappingWithDefaults);
395
396 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
397 UpdateInputDevices();
398 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
399 [this] { emit RefreshInputDevices(); });
390 400
391 timeout_timer->setSingleShot(true); 401 timeout_timer->setSingleShot(true);
392 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 402 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
393 403
394 connect(poll_timer.get(), &QTimer::timeout, [this] { 404 connect(poll_timer.get(), &QTimer::timeout, [this] {
395 Common::ParamPackage params; 405 Common::ParamPackage params;
396 if (InputCommon::GetGCButtons()->IsPolling()) { 406 if (input_subsystem->GetGCButtons()->IsPolling()) {
397 params = InputCommon::GetGCButtons()->GetNextInput(); 407 params = input_subsystem->GetGCButtons()->GetNextInput();
398 if (params.Has("engine")) { 408 if (params.Has("engine")) {
399 SetPollingResult(params, false); 409 SetPollingResult(params, false);
400 return; 410 return;
401 } 411 }
402 } 412 }
403 if (InputCommon::GetGCAnalogs()->IsPolling()) { 413 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
404 params = InputCommon::GetGCAnalogs()->GetNextInput(); 414 params = input_subsystem->GetGCAnalogs()->GetNextInput();
405 if (params.Has("engine")) { 415 if (params.Has("engine")) {
406 SetPollingResult(params, false); 416 SetPollingResult(params, false);
407 return; 417 return;
@@ -416,20 +426,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
416 } 426 }
417 }); 427 });
418 428
419 controller_color_buttons = {
420 ui->left_body_button,
421 ui->left_buttons_button,
422 ui->right_body_button,
423 ui->right_buttons_button,
424 };
425
426 for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
427 connect(controller_color_buttons[i], &QPushButton::clicked, this,
428 [this, i] { OnControllerButtonClick(static_cast<int>(i)); });
429 }
430
431 LoadConfiguration(); 429 LoadConfiguration();
432 resize(0, 0);
433 430
434 // TODO(wwylele): enable this when we actually emulate it 431 // TODO(wwylele): enable this when we actually emulate it
435 ui->buttonHome->setEnabled(false); 432 ui->buttonHome->setEnabled(false);
@@ -438,27 +435,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
438ConfigureInputPlayer::~ConfigureInputPlayer() = default; 435ConfigureInputPlayer::~ConfigureInputPlayer() = default;
439 436
440void ConfigureInputPlayer::ApplyConfiguration() { 437void ConfigureInputPlayer::ApplyConfiguration() {
441 auto& buttons = 438 auto& player = Settings::values.players[player_index];
442 debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons; 439 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
443 auto& analogs = 440 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
444 debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
445 441
446 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(), 442 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
447 [](const Common::ParamPackage& param) { return param.Serialize(); }); 443 [](const Common::ParamPackage& param) { return param.Serialize(); });
448 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), 444 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
449 [](const Common::ParamPackage& param) { return param.Serialize(); }); 445 [](const Common::ParamPackage& param) { return param.Serialize(); });
450 446
451 if (debug) 447 if (debug) {
452 return; 448 return;
449 }
450
451 player.controller_type =
452 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
453 player.connected = ui->groupConnectedController->isChecked();
453 454
454 std::array<u32, 4> colors{}; 455 // Player 2-8
455 std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(), 456 if (player_index != 0) {
456 [](QColor color) { return color.rgb(); }); 457 UpdateController(player.controller_type, player_index, player.connected);
458 return;
459 }
457 460
458 Settings::values.players[player_index].body_color_left = colors[0]; 461 // Player 1 and Handheld
459 Settings::values.players[player_index].button_color_left = colors[1]; 462 auto& handheld = Settings::values.players[HANDHELD_INDEX];
460 Settings::values.players[player_index].body_color_right = colors[2]; 463 // If Handheld is selected, copy all the settings from Player 1 to Handheld.
461 Settings::values.players[player_index].button_color_right = colors[3]; 464 if (player.controller_type == Settings::ControllerType::Handheld) {
465 handheld = player;
466 handheld.connected = ui->groupConnectedController->isChecked();
467 player.connected = false; // Disconnect Player 1
468 } else {
469 player.connected = ui->groupConnectedController->isChecked();
470 handheld.connected = false; // Disconnect Handheld
471 }
472
473 UpdateController(player.controller_type, player_index, player.connected);
474 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
462} 475}
463 476
464void ConfigureInputPlayer::changeEvent(QEvent* event) { 477void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -466,24 +479,16 @@ void ConfigureInputPlayer::changeEvent(QEvent* event) {
466 RetranslateUI(); 479 RetranslateUI();
467 } 480 }
468 481
469 QDialog::changeEvent(event); 482 QWidget::changeEvent(event);
470} 483}
471 484
472void ConfigureInputPlayer::RetranslateUI() { 485void ConfigureInputPlayer::RetranslateUI() {
473 ui->retranslateUi(this); 486 ui->retranslateUi(this);
474 UpdateButtonLabels(); 487 UpdateUI();
475}
476
477void ConfigureInputPlayer::OnControllerButtonClick(int i) {
478 const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
479 if (!new_bg_color.isValid())
480 return;
481 controller_colors[i] = new_bg_color;
482 controller_color_buttons[i]->setStyleSheet(
483 QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
484} 488}
485 489
486void ConfigureInputPlayer::LoadConfiguration() { 490void ConfigureInputPlayer::LoadConfiguration() {
491 auto& player = Settings::values.players[player_index];
487 if (debug) { 492 if (debug) {
488 std::transform(Settings::values.debug_pad_buttons.begin(), 493 std::transform(Settings::values.debug_pad_buttons.begin(),
489 Settings::values.debug_pad_buttons.end(), buttons_param.begin(), 494 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -492,56 +497,60 @@ void ConfigureInputPlayer::LoadConfiguration() {
492 Settings::values.debug_pad_analogs.end(), analogs_param.begin(), 497 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
493 [](const std::string& str) { return Common::ParamPackage(str); }); 498 [](const std::string& str) { return Common::ParamPackage(str); });
494 } else { 499 } else {
495 std::transform(Settings::values.players[player_index].buttons.begin(), 500 std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
496 Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
497 [](const std::string& str) { return Common::ParamPackage(str); }); 501 [](const std::string& str) { return Common::ParamPackage(str); });
498 std::transform(Settings::values.players[player_index].analogs.begin(), 502 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
499 Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
500 [](const std::string& str) { return Common::ParamPackage(str); }); 503 [](const std::string& str) { return Common::ParamPackage(str); });
501 } 504 }
502 505
503 UpdateButtonLabels(); 506 UpdateUI();
504 507
505 if (debug) 508 if (debug) {
506 return; 509 return;
510 }
507 511
508 std::array<u32, 4> colors = { 512 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
509 Settings::values.players[player_index].body_color_left, 513 ui->groupConnectedController->setChecked(
510 Settings::values.players[player_index].button_color_left, 514 player.connected ||
511 Settings::values.players[player_index].body_color_right, 515 (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
512 Settings::values.players[player_index].button_color_right, 516}
513 };
514
515 std::transform(colors.begin(), colors.end(), controller_colors.begin(),
516 [](u32 rgb) { return QColor::fromRgb(rgb); });
517 517
518 for (std::size_t i = 0; i < colors.size(); ++i) { 518void ConfigureInputPlayer::UpdateInputDevices() {
519 controller_color_buttons[i]->setStyleSheet( 519 input_devices = input_subsystem->GetInputDevices();
520 QStringLiteral("QPushButton { background-color: %1 }") 520 ui->comboDevices->clear();
521 .arg(controller_colors[i].name())); 521 for (auto device : input_devices) {
522 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
522 } 523 }
523} 524}
524 525
525void ConfigureInputPlayer::RestoreDefaults() { 526void ConfigureInputPlayer::RestoreDefaults() {
526 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 527 // Reset Buttons
528 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
527 buttons_param[button_id] = Common::ParamPackage{ 529 buttons_param[button_id] = Common::ParamPackage{
528 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 530 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
529 } 531 }
530 532
531 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 533 // Reset Modifier Buttons
532 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 534 lstick_mod =
535 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[0]));
536 rstick_mod =
537 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[1]));
538
539 // Reset Analogs
540 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
541 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
533 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 542 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
534 Config::default_analogs[analog_id][sub_button_id])}; 543 Config::default_analogs[analog_id][sub_button_id])};
535 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 544 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
536 } 545 }
537 } 546 }
538 547 UpdateUI();
539 UpdateButtonLabels(); 548 UpdateInputDevices();
540 ApplyConfiguration(); 549 ui->comboControllerType->setCurrentIndex(0);
541} 550}
542 551
543void ConfigureInputPlayer::ClearAll() { 552void ConfigureInputPlayer::ClearAll() {
544 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 553 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
545 const auto* const button = button_map[button_id]; 554 const auto* const button = button_map[button_id];
546 if (button == nullptr || !button->isEnabled()) { 555 if (button == nullptr || !button->isEnabled()) {
547 continue; 556 continue;
@@ -550,8 +559,11 @@ void ConfigureInputPlayer::ClearAll() {
550 buttons_param[button_id].Clear(); 559 buttons_param[button_id].Clear();
551 } 560 }
552 561
553 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 562 lstick_mod.Clear();
554 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 563 rstick_mod.Clear();
564
565 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
566 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
555 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 567 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
556 if (analog_button == nullptr || !analog_button->isEnabled()) { 568 if (analog_button == nullptr || !analog_button->isEnabled()) {
557 continue; 569 continue;
@@ -561,17 +573,20 @@ void ConfigureInputPlayer::ClearAll() {
561 } 573 }
562 } 574 }
563 575
564 UpdateButtonLabels(); 576 UpdateUI();
565 ApplyConfiguration(); 577 UpdateInputDevices();
566} 578}
567 579
568void ConfigureInputPlayer::UpdateButtonLabels() { 580void ConfigureInputPlayer::UpdateUI() {
569 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { 581 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
570 button_map[button]->setText(ButtonToText(buttons_param[button])); 582 button_map[button]->setText(ButtonToText(buttons_param[button]));
571 } 583 }
572 584
573 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 585 ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
574 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 586 ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
587
588 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
589 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
575 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 590 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
576 591
577 if (analog_button == nullptr) { 592 if (analog_button == nullptr) {
@@ -581,99 +596,141 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
581 analog_button->setText( 596 analog_button->setText(
582 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 597 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
583 } 598 }
584 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
585 599
600 const auto deadzone_label = analog_map_deadzone_label[analog_id];
601 const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
602 const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id];
603 const auto modifier_label = analog_map_modifier_label[analog_id];
604 const auto modifier_slider = analog_map_modifier_slider[analog_id];
605 const auto range_groupbox = analog_map_range_groupbox[analog_id];
606 const auto range_spinbox = analog_map_range_spinbox[analog_id];
607
608 int slider_value;
586 auto& param = analogs_param[analog_id]; 609 auto& param = analogs_param[analog_id];
587 auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id]; 610 const bool is_controller =
588 auto* const analog_stick_slider_label = 611 param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad";
589 analog_map_deadzone_and_modifier_slider_label[analog_id]; 612
590 613 if (is_controller) {
591 if (param.Has("engine")) { 614 if (!param.Has("deadzone")) {
592 if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") { 615 param.Set("deadzone", 0.1f);
593 if (!param.Has("deadzone")) {
594 param.Set("deadzone", 0.1f);
595 }
596
597 analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
598 if (analog_stick_slider->value() == 0) {
599 analog_stick_slider_label->setText(tr("Deadzone: 0%"));
600 }
601 } else {
602 if (!param.Has("modifier_scale")) {
603 param.Set("modifier_scale", 0.5f);
604 }
605
606 analog_stick_slider->setValue(
607 static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
608 if (analog_stick_slider->value() == 0) {
609 analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
610 }
611 } 616 }
617 slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
618 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
619 deadzone_slider->setValue(slider_value);
620 if (!param.Has("range")) {
621 param.Set("range", 1.0f);
622 }
623 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
624 } else {
625 if (!param.Has("modifier_scale")) {
626 param.Set("modifier_scale", 0.5f);
627 }
628 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
629 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
630 modifier_slider->setValue(slider_value);
612 } 631 }
632
633 deadzone_label->setVisible(is_controller);
634 deadzone_slider->setVisible(is_controller);
635 modifier_groupbox->setVisible(!is_controller);
636 modifier_label->setVisible(!is_controller);
637 modifier_slider->setVisible(!is_controller);
638 range_groupbox->setVisible(is_controller);
613 } 639 }
614} 640}
615 641
642void ConfigureInputPlayer::UpdateMappingWithDefaults() {
643 if (ui->comboDevices->currentIndex() < 2) {
644 return;
645 }
646 const auto& device = input_devices[ui->comboDevices->currentIndex()];
647 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
648 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
649 for (int i = 0; i < buttons_param.size(); ++i) {
650 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
651 }
652 for (int i = 0; i < analogs_param.size(); ++i) {
653 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
654 }
655
656 UpdateUI();
657}
658
616void ConfigureInputPlayer::HandleClick( 659void ConfigureInputPlayer::HandleClick(
617 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 660 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
618 InputCommon::Polling::DeviceType type) { 661 InputCommon::Polling::DeviceType type) {
619 button->setText(tr("[press key]")); 662 button->setText(tr("[waiting]"));
620 button->setFocus(); 663 button->setFocus();
621 664
622 // Keyboard keys can only be used as button devices 665 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
623 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; 666 // controller, then they don't want keyboard/mouse input
624 if (want_keyboard_keys) { 667 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
625 const auto iter = std::find(button_map.begin(), button_map.end(), button);
626 ASSERT(iter != button_map.end());
627 const auto index = std::distance(button_map.begin(), iter);
628 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
629 }
630 668
631 input_setter = new_input_setter; 669 input_setter = new_input_setter;
632 670
633 device_pollers = InputCommon::Polling::GetPollers(type); 671 device_pollers = input_subsystem->GetPollers(type);
634 672
635 for (auto& poller : device_pollers) { 673 for (auto& poller : device_pollers) {
636 poller->Start(); 674 poller->Start();
637 } 675 }
638 676
639 grabKeyboard(); 677 QWidget::grabMouse();
640 grabMouse(); 678 QWidget::grabKeyboard();
679
641 if (type == InputCommon::Polling::DeviceType::Button) { 680 if (type == InputCommon::Polling::DeviceType::Button) {
642 InputCommon::GetGCButtons()->BeginConfiguration(); 681 input_subsystem->GetGCButtons()->BeginConfiguration();
643 } else { 682 } else {
644 InputCommon::GetGCAnalogs()->BeginConfiguration(); 683 input_subsystem->GetGCAnalogs()->BeginConfiguration();
645 } 684 }
646 timeout_timer->start(5000); // Cancel after 5 seconds 685
647 poll_timer->start(200); // Check for new inputs every 200ms 686 timeout_timer->start(2500); // Cancel after 2.5 seconds
687 poll_timer->start(50); // Check for new inputs every 50ms
648} 688}
649 689
650void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { 690void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
651 releaseKeyboard();
652 releaseMouse();
653 timeout_timer->stop(); 691 timeout_timer->stop();
654 poll_timer->stop(); 692 poll_timer->stop();
655 for (auto& poller : device_pollers) { 693 for (auto& poller : device_pollers) {
656 poller->Stop(); 694 poller->Stop();
657 } 695 }
658 696
659 InputCommon::GetGCButtons()->EndConfiguration(); 697 QWidget::releaseMouse();
660 InputCommon::GetGCAnalogs()->EndConfiguration(); 698 QWidget::releaseKeyboard();
699
700 input_subsystem->GetGCButtons()->EndConfiguration();
701 input_subsystem->GetGCAnalogs()->EndConfiguration();
661 702
662 if (!abort) { 703 if (!abort) {
663 (*input_setter)(params); 704 (*input_setter)(params);
664 } 705 }
665 706
666 UpdateButtonLabels(); 707 UpdateUI();
667 input_setter = std::nullopt; 708 input_setter = std::nullopt;
668} 709}
669 710
711void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
712 if (!input_setter || !event) {
713 return;
714 }
715
716 if (want_keyboard_mouse) {
717 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
718 false);
719 } else {
720 // We don't want any mouse buttons, so don't stop polling
721 return;
722 }
723
724 SetPollingResult({}, true);
725}
726
670void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 727void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
671 if (!input_setter || !event) { 728 if (!input_setter || !event) {
672 return; 729 return;
673 } 730 }
674 731
675 if (event->key() != Qt::Key_Escape) { 732 if (event->key() != Qt::Key_Escape) {
676 if (want_keyboard_keys) { 733 if (want_keyboard_mouse) {
677 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 734 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
678 false); 735 false);
679 } else { 736 } else {
@@ -681,5 +738,108 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
681 return; 738 return;
682 } 739 }
683 } 740 }
741
684 SetPollingResult({}, true); 742 SetPollingResult({}, true);
685} 743}
744
745void ConfigureInputPlayer::UpdateControllerIcon() {
746 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
747 // "nonstandard" to use an image through the icon support)
748 const QString stylesheet = [this] {
749 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
750 case Settings::ControllerType::ProController:
751 return QStringLiteral("image: url(:/controller/pro_controller%0)");
752 case Settings::ControllerType::DualJoyconDetached:
753 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
754 case Settings::ControllerType::LeftJoycon:
755 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
756 case Settings::ControllerType::RightJoycon:
757 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
758 case Settings::ControllerType::Handheld:
759 return QStringLiteral("image: url(:/controller/handheld%0)");
760 default:
761 return QString{};
762 }
763 }();
764
765 const QString theme = [this] {
766 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
767 return QStringLiteral("_dark");
768 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
769 return QStringLiteral("_midnight");
770 } else {
771 return QString{};
772 }
773 }();
774
775 ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
776}
777
778void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
779 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
780 if (debug) {
781 layout = Settings::ControllerType::ProController;
782 }
783
784 // List of all the widgets that will be hidden by any of the following layouts that need
785 // "unhidden" after the controller type changes
786 const std::array<QWidget*, 9> layout_show = {
787 ui->buttonShoulderButtonsSLSR,
788 ui->horizontalSpacerShoulderButtonsWidget,
789 ui->horizontalSpacerShoulderButtonsWidget2,
790 ui->buttonShoulderButtonsLeft,
791 ui->buttonMiscButtonsMinusScreenshot,
792 ui->bottomLeft,
793 ui->buttonShoulderButtonsRight,
794 ui->buttonMiscButtonsPlusHome,
795 ui->bottomRight,
796 };
797
798 for (auto* widget : layout_show) {
799 widget->show();
800 }
801
802 std::vector<QWidget*> layout_hidden;
803 switch (layout) {
804 case Settings::ControllerType::ProController:
805 case Settings::ControllerType::DualJoyconDetached:
806 case Settings::ControllerType::Handheld:
807 layout_hidden = {
808 ui->buttonShoulderButtonsSLSR,
809 ui->horizontalSpacerShoulderButtonsWidget2,
810 };
811 break;
812 case Settings::ControllerType::LeftJoycon:
813 layout_hidden = {
814 ui->horizontalSpacerShoulderButtonsWidget2,
815 ui->buttonShoulderButtonsRight,
816 ui->buttonMiscButtonsPlusHome,
817 ui->bottomRight,
818 };
819 break;
820 case Settings::ControllerType::RightJoycon:
821 layout_hidden = {
822 ui->horizontalSpacerShoulderButtonsWidget,
823 ui->buttonShoulderButtonsLeft,
824 ui->buttonMiscButtonsMinusScreenshot,
825 ui->bottomLeft,
826 };
827 break;
828 }
829
830 for (auto* widget : layout_hidden) {
831 widget->hide();
832 }
833}
834
835void ConfigureInputPlayer::showEvent(QShowEvent* event) {
836 if (bottom_row == nullptr) {
837 return;
838 }
839 QWidget::showEvent(event);
840 ui->main->addWidget(bottom_row);
841}
842
843void ConfigureInputPlayer::ConnectPlayer(bool connected) {
844 ui->groupConnectedController->setChecked(connected);
845}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 95afa5375..a25bc3bd9 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -10,16 +10,25 @@
10#include <optional> 10#include <optional>
11#include <string> 11#include <string>
12 12
13#include <QDialog> 13#include <QWidget>
14 14
15#include "common/param_package.h" 15#include "common/param_package.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "ui_configure_input.h" 17#include "ui_configure_input.h"
18 18
19class QCheckBox;
19class QKeyEvent; 20class QKeyEvent;
21class QLabel;
20class QPushButton; 22class QPushButton;
23class QSlider;
24class QSpinBox;
21class QString; 25class QString;
22class QTimer; 26class QTimer;
27class QWidget;
28
29namespace InputCommon {
30class InputSubsystem;
31}
23 32
24namespace InputCommon::Polling { 33namespace InputCommon::Polling {
25class DevicePoller; 34class DevicePoller;
@@ -30,77 +39,116 @@ namespace Ui {
30class ConfigureInputPlayer; 39class ConfigureInputPlayer;
31} 40}
32 41
33class ConfigureInputPlayer : public QDialog { 42class ConfigureInputPlayer : public QWidget {
34 Q_OBJECT 43 Q_OBJECT
35 44
36public: 45public:
37 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false); 46 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
47 InputCommon::InputSubsystem* input_subsystem_,
48 bool debug = false);
38 ~ConfigureInputPlayer() override; 49 ~ConfigureInputPlayer() override;
39 50
40 /// Save all button configurations to settings file 51 /// Save all button configurations to settings file.
41 void ApplyConfiguration(); 52 void ApplyConfiguration();
42 53
54 /// Update the input devices combobox.
55 void UpdateInputDevices();
56
57 /// Restore all buttons to their default values.
58 void RestoreDefaults();
59
60 /// Clear all input configuration.
61 void ClearAll();
62
63 /// Set the connection state checkbox (used to sync state).
64 void ConnectPlayer(bool connected);
65
66signals:
67 /// Emitted when this controller is connected by the user.
68 void Connected(bool connected);
69 /// Emitted when the Handheld mode is selected (undocked with dual joycons attached).
70 void HandheldStateChanged(bool is_handheld);
71 /// Emitted when the input devices combobox is being refreshed.
72 void RefreshInputDevices();
73
74protected:
75 void showEvent(QShowEvent* event) override;
76
43private: 77private:
44 void changeEvent(QEvent* event) override; 78 void changeEvent(QEvent* event) override;
45 void RetranslateUI(); 79 void RetranslateUI();
46 80
47 void OnControllerButtonClick(int i);
48
49 /// Load configuration settings. 81 /// Load configuration settings.
50 void LoadConfiguration(); 82 void LoadConfiguration();
51 /// Restore all buttons to their default values.
52 void RestoreDefaults();
53 /// Clear all input configuration
54 void ClearAll();
55
56 /// Update UI to reflect current configuration.
57 void UpdateButtonLabels();
58 83
59 /// Called when the button was pressed. 84 /// Called when the button was pressed.
60 void HandleClick(QPushButton* button, 85 void HandleClick(QPushButton* button,
61 std::function<void(const Common::ParamPackage&)> new_input_setter, 86 std::function<void(const Common::ParamPackage&)> new_input_setter,
62 InputCommon::Polling::DeviceType type); 87 InputCommon::Polling::DeviceType type);
63 88
64 /// Finish polling and configure input using the input_setter 89 /// Finish polling and configure input using the input_setter.
65 void SetPollingResult(const Common::ParamPackage& params, bool abort); 90 void SetPollingResult(const Common::ParamPackage& params, bool abort);
66 91
92 /// Handle mouse button press events.
93 void mousePressEvent(QMouseEvent* event) override;
94
67 /// Handle key press events. 95 /// Handle key press events.
68 void keyPressEvent(QKeyEvent* event) override; 96 void keyPressEvent(QKeyEvent* event) override;
69 97
98 /// Update UI to reflect current configuration.
99 void UpdateUI();
100
101 /// Update the controller selection combobox
102 void UpdateControllerCombobox();
103
104 /// Update the current controller icon.
105 void UpdateControllerIcon();
106
107 /// Hides and disables controller settings based on the current controller type.
108 void UpdateControllerAvailableButtons();
109
110 /// Gets the default controller mapping for this device and auto configures the input to match.
111 void UpdateMappingWithDefaults();
112
70 std::unique_ptr<Ui::ConfigureInputPlayer> ui; 113 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
71 114
72 std::size_t player_index; 115 std::size_t player_index;
73 bool debug; 116 bool debug;
74 117
118 InputCommon::InputSubsystem* input_subsystem;
119
75 std::unique_ptr<QTimer> timeout_timer; 120 std::unique_ptr<QTimer> timeout_timer;
76 std::unique_ptr<QTimer> poll_timer; 121 std::unique_ptr<QTimer> poll_timer;
77 122
123 static constexpr int PLAYER_COUNT = 8;
124 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
125
78 /// This will be the the setting function when an input is awaiting configuration. 126 /// This will be the the setting function when an input is awaiting configuration.
79 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 127 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
80 128
81 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 129 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
82 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 130 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
83 131
84 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; 132 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
85 133
86 /// Each button input is represented by a QPushButton. 134 /// Each button input is represented by a QPushButton.
87 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; 135 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
136 /// Extra buttons for the modifiers.
137 Common::ParamPackage lstick_mod;
138 Common::ParamPackage rstick_mod;
88 139
89 std::vector<QWidget*> debug_hidden; 140 /// A group of four QPushButtons represent one analog input. The buttons each represent up,
90 std::vector<QWidget*> layout_hidden; 141 /// down, left, right, respectively.
91
92 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
93 /// down, left, right, and modifier, respectively.
94 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> 142 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
95 analog_map_buttons; 143 analog_map_buttons;
96 144
97 /// Analog inputs are also represented each with a single button, used to configure with an 145 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
98 /// actual analog stick 146 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; 147 std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox;
100 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> 148 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label;
101 analog_map_deadzone_and_modifier_slider; 149 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider;
102 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> 150 std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
103 analog_map_deadzone_and_modifier_slider_label; 151 std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
104 152
105 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 153 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
106 154
@@ -108,8 +156,14 @@ private:
108 156
109 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 157 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
110 /// keyboard events are ignored. 158 /// keyboard events are ignored.
111 bool want_keyboard_keys = false; 159 bool want_keyboard_mouse = false;
160
161 /// List of physical devices users can map with. If a SDL backed device is selected, then you
162 /// can usue this device to get a default mapping.
163 std::vector<Common::ParamPackage> input_devices;
112 164
113 std::array<QPushButton*, 4> controller_color_buttons; 165 /// Bottom row is where console wide settings are held, and its "owned" by the parent
114 std::array<QColor, 4> controller_colors; 166 /// ConfigureInput widget. On show, add this widget to the main layout. This will change the
167 /// parent of the widget to this widget (but thats fine).
168 QWidget* bottom_row;
115}; 169};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index f27a77180..9bc681894 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1,1243 +1,2974 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInputPlayer</class> 3 <class>ConfigureInputPlayer</class>
4 <widget class="QDialog" name="ConfigureInputPlayer"> 4 <widget class="QWidget" name="ConfigureInputPlayer">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>408</width> 9 <width>780</width>
10 <height>731</height> 10 <height>487</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Input</string> 14 <string>Configure Input</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QHBoxLayout" name="horizontalLayout_2">
17 <property name="spacing">
18 <number>0</number>
19 </property>
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
17 <item> 32 <item>
18 <layout class="QGridLayout" name="buttons"> 33 <layout class="QVBoxLayout" name="main">
19 <item row="1" column="1"> 34 <property name="spacing">
20 <widget class="QGroupBox" name="RStick"> 35 <number>0</number>
21 <property name="title"> 36 </property>
22 <string>Right Stick</string> 37 <property name="leftMargin">
23 </property> 38 <number>0</number>
24 <property name="alignment"> 39 </property>
25 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> 40 <property name="topMargin">
26 </property> 41 <number>0</number>
27 <property name="flat"> 42 </property>
28 <bool>false</bool> 43 <property name="rightMargin">
44 <number>0</number>
45 </property>
46 <property name="bottomMargin">
47 <number>0</number>
48 </property>
49 <item>
50 <layout class="QHBoxLayout" name="top" stretch="0,1,2">
51 <property name="spacing">
52 <number>3</number>
29 </property> 53 </property>
30 <property name="checkable"> 54 <property name="topMargin">
31 <bool>false</bool> 55 <number>0</number>
32 </property> 56 </property>
33 <layout class="QGridLayout" name="gridLayout_5"> 57 <item>
34 <item row="1" column="1"> 58 <widget class="QGroupBox" name="groupConnectedController">
35 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> 59 <property name="layoutDirection">
60 <enum>Qt::LeftToRight</enum>
61 </property>
62 <property name="title">
63 <string>Connect Controller</string>
64 </property>
65 <property name="flat">
66 <bool>false</bool>
67 </property>
68 <property name="checkable">
69 <bool>true</bool>
70 </property>
71 <layout class="QHBoxLayout" name="horizontalLayout_3">
72 <property name="leftMargin">
73 <number>5</number>
74 </property>
75 <property name="topMargin">
76 <number>5</number>
77 </property>
78 <property name="rightMargin">
79 <number>5</number>
80 </property>
81 <property name="bottomMargin">
82 <number>5</number>
83 </property>
36 <item> 84 <item>
37 <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout"> 85 <widget class="QComboBox" name="comboControllerType">
38 <item> 86 <item>
39 <widget class="QLabel" name="labelRStickDown"> 87 <property name="text">
40 <property name="text"> 88 <string>Pro Controller</string>
41 <string>Down:</string> 89 </property>
42 </property>
43 </widget>
44 </item> 90 </item>
45 </layout>
46 </item>
47 <item>
48 <widget class="QPushButton" name="buttonRStickDown">
49 <property name="text">
50 <string/>
51 </property>
52 </widget>
53 </item>
54 </layout>
55 </item>
56 <item row="0" column="1">
57 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
58 <item>
59 <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
60 <item> 91 <item>
61 <widget class="QLabel" name="labelRStickRight"> 92 <property name="text">
62 <property name="text"> 93 <string>Dual Joycons</string>
63 <string>Right:</string> 94 </property>
64 </property>
65 </widget>
66 </item> 95 </item>
67 </layout>
68 </item>
69 <item>
70 <widget class="QPushButton" name="buttonRStickRight">
71 <property name="text">
72 <string/>
73 </property>
74 </widget>
75 </item>
76 </layout>
77 </item>
78 <item row="3" column="0" colspan="2">
79 <widget class="QPushButton" name="buttonRStickAnalog">
80 <property name="text">
81 <string>Set Analog Stick</string>
82 </property>
83 </widget>
84 </item>
85 <item row="0" column="0">
86 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
87 <item>
88 <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
89 <item> 96 <item>
90 <widget class="QLabel" name="labelRStickLeft"> 97 <property name="text">
91 <property name="text"> 98 <string>Left Joycon</string>
92 <string>Left:</string> 99 </property>
93 </property>
94 </widget>
95 </item> 100 </item>
96 </layout>
97 </item>
98 <item>
99 <widget class="QPushButton" name="buttonRStickLeft">
100 <property name="text">
101 <string/>
102 </property>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item row="1" column="0">
108 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
109 <item>
110 <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
111 <item> 101 <item>
112 <widget class="QLabel" name="labelRStickUp"> 102 <property name="text">
113 <property name="text"> 103 <string>Right Joycon</string>
114 <string>Up:</string> 104 </property>
115 </property>
116 </widget>
117 </item> 105 </item>
118 </layout>
119 </item>
120 <item>
121 <widget class="QPushButton" name="buttonRStickUp">
122 <property name="text">
123 <string/>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </item>
129 <item row="2" column="0">
130 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
131 <item>
132 <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
133 <item> 106 <item>
134 <widget class="QLabel" name="labelRStickPressed"> 107 <property name="text">
135 <property name="text"> 108 <string>Handheld</string>
136 <string>Pressed:</string> 109 </property>
137 </property>
138 </widget>
139 </item> 110 </item>
140 </layout>
141 </item>
142 <item>
143 <widget class="QPushButton" name="buttonRStick">
144 <property name="text">
145 <string/>
146 </property>
147 </widget> 111 </widget>
148 </item> 112 </item>
149 </layout> 113 </layout>
150 </item> 114 </widget>
151 <item row="2" column="1"> 115 </item>
152 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> 116 <item>
117 <widget class="QGroupBox" name="devicesGroup">
118 <property name="title">
119 <string>Input Device</string>
120 </property>
121 <layout class="QHBoxLayout" name="horizontalLayout_5">
122 <property name="spacing">
123 <number>3</number>
124 </property>
125 <property name="leftMargin">
126 <number>5</number>
127 </property>
128 <property name="topMargin">
129 <number>5</number>
130 </property>
131 <property name="rightMargin">
132 <number>5</number>
133 </property>
134 <property name="bottomMargin">
135 <number>5</number>
136 </property>
153 <item> 137 <item>
154 <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout"> 138 <widget class="QComboBox" name="comboDevices">
155 <item> 139 <item>
156 <widget class="QLabel" name="labelRStickMod"> 140 <property name="text">
157 <property name="text"> 141 <string>Any</string>
158 <string>Modifier:</string> 142 </property>
159 </property>
160 </widget>
161 </item> 143 </item>
162 </layout>
163 </item>
164 <item>
165 <widget class="QPushButton" name="buttonRStickMod">
166 <property name="text">
167 <string/>
168 </property>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 <item row="4" column="0" colspan="2">
174 <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout">
175 <item>
176 <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout">
177 <item> 144 <item>
178 <widget class="QLabel" name="labelRStickDeadzoneAndModifier"> 145 <property name="text">
179 <property name="text"> 146 <string>Keyboard/Mouse</string>
180 <string>Deadzone: 0</string> 147 </property>
181 </property>
182 <property name="alignment">
183 <enum>Qt::AlignHCenter</enum>
184 </property>
185 </widget>
186 </item> 148 </item>
187 </layout> 149 </widget>
188 </item> 150 </item>
189 <item> 151 <item>
190 <widget class="QSlider" name="sliderRStickDeadzoneAndModifier"> 152 <widget class="QPushButton" name="buttonRefreshDevices">
191 <property name="orientation"> 153 <property name="minimumSize">
192 <enum>Qt::Horizontal</enum> 154 <size>
155 <width>24</width>
156 <height>22</height>
157 </size>
158 </property>
159 <property name="maximumSize">
160 <size>
161 <width>24</width>
162 <height>22</height>
163 </size>
164 </property>
165 <property name="styleSheet">
166 <string notr="true"/>
193 </property> 167 </property>
194 </widget> 168 </widget>
195 </item> 169 </item>
196 </layout> 170 </layout>
197 </item> 171 </widget>
198 <item row="5" column="0"> 172 </item>
199 <spacer name="RStick_verticalSpacer"> 173 <item>
200 <property name="orientation"> 174 <widget class="QGroupBox" name="profilesGroup">
201 <enum>Qt::Vertical</enum> 175 <property name="minimumSize">
176 <size>
177 <width>0</width>
178 <height>0</height>
179 </size>
180 </property>
181 <property name="title">
182 <string>Profile</string>
183 </property>
184 <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,0,0,0">
185 <property name="spacing">
186 <number>3</number>
202 </property> 187 </property>
203 <property name="sizeHint" stdset="0"> 188 <property name="leftMargin">
204 <size> 189 <number>5</number>
205 <width>0</width> 190 </property>
206 <height>0</height> 191 <property name="topMargin">
207 </size> 192 <number>5</number>
193 </property>
194 <property name="rightMargin">
195 <number>5</number>
196 </property>
197 <property name="bottomMargin">
198 <number>5</number>
208 </property> 199 </property>
209 </spacer>
210 </item>
211 </layout>
212 </widget>
213 </item>
214 <item row="0" column="1">
215 <widget class="QGroupBox" name="Dpad">
216 <property name="title">
217 <string>Directional Pad</string>
218 </property>
219 <property name="flat">
220 <bool>false</bool>
221 </property>
222 <property name="checkable">
223 <bool>false</bool>
224 </property>
225 <layout class="QGridLayout" name="gridLayout_2">
226 <item row="1" column="0">
227 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
228 <item> 200 <item>
229 <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout"> 201 <widget class="QComboBox" name="comboProfiles"/>
230 <item>
231 <widget class="QLabel" name="labelDpadUp">
232 <property name="text">
233 <string>Up:</string>
234 </property>
235 </widget>
236 </item>
237 </layout>
238 </item> 202 </item>
239 <item> 203 <item>
240 <widget class="QPushButton" name="buttonDpadUp"> 204 <widget class="QPushButton" name="buttonProfilesSave">
241 <property name="text"> 205 <property name="maximumSize">
242 <string/> 206 <size>
207 <width>55</width>
208 <height>16777215</height>
209 </size>
210 </property>
211 <property name="styleSheet">
212 <string notr="true">min-width: 55px;</string>
243 </property> 213 </property>
244 </widget>
245 </item>
246 </layout>
247 </item>
248 <item row="1" column="1">
249 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
250 <item>
251 <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
252 <item>
253 <widget class="QLabel" name="labelDpadDown">
254 <property name="text">
255 <string>Down:</string>
256 </property>
257 </widget>
258 </item>
259 </layout>
260 </item>
261 <item>
262 <widget class="QPushButton" name="buttonDpadDown">
263 <property name="text"> 214 <property name="text">
264 <string/> 215 <string>Save</string>
265 </property> 216 </property>
266 </widget> 217 </widget>
267 </item> 218 </item>
268 </layout>
269 </item>
270 <item row="0" column="0">
271 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
272 <item>
273 <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
274 <item>
275 <widget class="QLabel" name="labelDpadLeft">
276 <property name="minimumSize">
277 <size>
278 <width>80</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="text">
283 <string>Left:</string>
284 </property>
285 </widget>
286 </item>
287 </layout>
288 </item>
289 <item> 219 <item>
290 <widget class="QPushButton" name="buttonDpadLeft"> 220 <widget class="QPushButton" name="buttonProfilesNew">
221 <property name="maximumSize">
222 <size>
223 <width>55</width>
224 <height>16777215</height>
225 </size>
226 </property>
227 <property name="styleSheet">
228 <string notr="true">min-width: 55px;</string>
229 </property>
291 <property name="text"> 230 <property name="text">
292 <string/> 231 <string>New</string>
293 </property> 232 </property>
294 </widget> 233 </widget>
295 </item> 234 </item>
296 </layout>
297 </item>
298 <item row="0" column="1">
299 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
300 <item>
301 <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
302 <item>
303 <widget class="QLabel" name="labelDpadRight">
304 <property name="minimumSize">
305 <size>
306 <width>80</width>
307 <height>0</height>
308 </size>
309 </property>
310 <property name="text">
311 <string>Right:</string>
312 </property>
313 </widget>
314 </item>
315 </layout>
316 </item>
317 <item> 235 <item>
318 <widget class="QPushButton" name="buttonDpadRight"> 236 <widget class="QPushButton" name="buttonProfilesDelete">
237 <property name="maximumSize">
238 <size>
239 <width>55</width>
240 <height>16777215</height>
241 </size>
242 </property>
243 <property name="styleSheet">
244 <string notr="true">min-width: 55px;</string>
245 </property>
319 <property name="text"> 246 <property name="text">
320 <string/> 247 <string>Delete</string>
321 </property> 248 </property>
322 </widget> 249 </widget>
323 </item> 250 </item>
324 </layout> 251 </layout>
325 </item> 252 </widget>
326 </layout> 253 </item>
327 </widget> 254 </layout>
328 </item> 255 </item>
329 <item row="0" column="0"> 256 <item>
330 <widget class="QGroupBox" name="faceButtons"> 257 <widget class="QFrame" name="bottom">
331 <property name="title"> 258 <property name="sizePolicy">
332 <string>Face Buttons</string> 259 <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
333 </property> 260 <horstretch>0</horstretch>
334 <property name="flat"> 261 <verstretch>0</verstretch>
335 <bool>false</bool> 262 </sizepolicy>
336 </property>
337 <property name="checkable">
338 <bool>false</bool>
339 </property> 263 </property>
340 <layout class="QGridLayout" name="gridLayout"> 264 <layout class="QHBoxLayout" name="_2">
341 <item row="0" column="0"> 265 <property name="sizeConstraint">
342 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> 266 <enum>QLayout::SetMinimumSize</enum>
343 <item> 267 </property>
344 <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout"> 268 <property name="leftMargin">
345 <item> 269 <number>0</number>
346 <widget class="QLabel" name="labelA"> 270 </property>
347 <property name="minimumSize"> 271 <property name="topMargin">
348 <size> 272 <number>0</number>
349 <width>80</width> 273 </property>
350 <height>0</height> 274 <property name="rightMargin">
351 </size> 275 <number>0</number>
276 </property>
277 <property name="bottomMargin">
278 <number>0</number>
279 </property>
280 <item>
281 <widget class="QWidget" name="bottomLeft" native="true">
282 <layout class="QVBoxLayout" name="bottomLeftLayout" stretch="0,0,0,0">
283 <property name="spacing">
284 <number>0</number>
285 </property>
286 <property name="sizeConstraint">
287 <enum>QLayout::SetDefaultConstraint</enum>
288 </property>
289 <property name="leftMargin">
290 <number>0</number>
291 </property>
292 <property name="topMargin">
293 <number>0</number>
294 </property>
295 <property name="rightMargin">
296 <number>0</number>
297 </property>
298 <property name="bottomMargin">
299 <number>0</number>
300 </property>
301 <item>
302 <widget class="QGroupBox" name="LStick">
303 <property name="sizePolicy">
304 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
305 <horstretch>0</horstretch>
306 <verstretch>0</verstretch>
307 </sizepolicy>
308 </property>
309 <property name="title">
310 <string>Left Stick</string>
311 </property>
312 <property name="alignment">
313 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
314 </property>
315 <layout class="QVBoxLayout" name="verticalLayout_3">
316 <property name="spacing">
317 <number>0</number>
352 </property> 318 </property>
353 <property name="text"> 319 <property name="sizeConstraint">
354 <string>A:</string> 320 <enum>QLayout::SetDefaultConstraint</enum>
355 </property> 321 </property>
356 </widget> 322 <property name="leftMargin">
357 </item> 323 <number>3</number>
358 </layout>
359 </item>
360 <item>
361 <widget class="QPushButton" name="buttonA">
362 <property name="text">
363 <string/>
364 </property>
365 </widget>
366 </item>
367 </layout>
368 </item>
369 <item row="0" column="1">
370 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
371 <item>
372 <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
373 <item>
374 <widget class="QLabel" name="labelB">
375 <property name="minimumSize">
376 <size>
377 <width>80</width>
378 <height>0</height>
379 </size>
380 </property> 324 </property>
381 <property name="text"> 325 <property name="topMargin">
382 <string>B:</string> 326 <number>0</number>
383 </property> 327 </property>
384 </widget> 328 <property name="rightMargin">
385 </item> 329 <number>3</number>
386 </layout>
387 </item>
388 <item>
389 <widget class="QPushButton" name="buttonB">
390 <property name="text">
391 <string/>
392 </property>
393 </widget>
394 </item>
395 </layout>
396 </item>
397 <item row="1" column="0">
398 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
399 <item>
400 <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
401 <item>
402 <widget class="QLabel" name="labelX">
403 <property name="text">
404 <string>X:</string>
405 </property> 330 </property>
406 </widget> 331 <property name="bottomMargin">
407 </item> 332 <number>0</number>
408 </layout>
409 </item>
410 <item>
411 <widget class="QPushButton" name="buttonX">
412 <property name="text">
413 <string/>
414 </property>
415 </widget>
416 </item>
417 </layout>
418 </item>
419 <item row="1" column="1">
420 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
421 <item>
422 <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
423 <item>
424 <widget class="QLabel" name="labelY">
425 <property name="text">
426 <string>Y:</string>
427 </property> 333 </property>
428 </widget> 334 <item>
429 </item> 335 <widget class="QWidget" name="buttonLStickUpWidget" native="true">
430 </layout> 336 <layout class="QHBoxLayout" name="horizontalLayout_20">
431 </item> 337 <property name="spacing">
432 <item> 338 <number>0</number>
433 <widget class="QPushButton" name="buttonY"> 339 </property>
434 <property name="text"> 340 <property name="leftMargin">
435 <string/> 341 <number>0</number>
436 </property> 342 </property>
437 </widget> 343 <property name="topMargin">
438 </item> 344 <number>0</number>
439 </layout> 345 </property>
440 </item> 346 <property name="rightMargin">
441 </layout> 347 <number>0</number>
442 </widget> 348 </property>
443 </item> 349 <property name="bottomMargin">
444 <item row="5" column="0" colspan="2"> 350 <number>0</number>
445 <widget class="QGroupBox" name="controller_color"> 351 </property>
446 <property name="title"> 352 <item>
447 <string>Controller Color</string> 353 <spacer name="horizontalSpacerLStickUpLeft">
448 </property> 354 <property name="orientation">
449 <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0"> 355 <enum>Qt::Horizontal</enum>
450 <item row="0" column="0"> 356 </property>
451 <spacer name="horizontalSpacer_2"> 357 <property name="sizeHint" stdset="0">
452 <property name="orientation"> 358 <size>
453 <enum>Qt::Horizontal</enum> 359 <width>20</width>
454 </property> 360 <height>20</height>
455 <property name="sizeHint" stdset="0"> 361 </size>
456 <size> 362 </property>
457 <width>40</width> 363 </spacer>
458 <height>20</height> 364 </item>
459 </size> 365 <item alignment="Qt::AlignHCenter">
460 </property> 366 <widget class="QGroupBox" name="buttonLStickUpGroup">
461 </spacer> 367 <property name="title">
462 </item> 368 <string>Up</string>
463 <item row="0" column="1"> 369 </property>
464 <widget class="QLabel" name="left_body_label"> 370 <property name="alignment">
465 <property name="text"> 371 <set>Qt::AlignCenter</set>
466 <string>Left Body</string> 372 </property>
467 </property> 373 <property name="flat">
468 </widget> 374 <bool>false</bool>
469 </item> 375 </property>
470 <item row="0" column="6"> 376 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
471 <spacer name="horizontalSpacer_3"> 377 <property name="spacing">
472 <property name="orientation"> 378 <number>3</number>
473 <enum>Qt::Horizontal</enum> 379 </property>
474 </property> 380 <property name="leftMargin">
475 <property name="sizeHint" stdset="0"> 381 <number>3</number>
476 <size> 382 </property>
477 <width>40</width> 383 <property name="topMargin">
478 <height>20</height> 384 <number>3</number>
479 </size> 385 </property>
480 </property> 386 <property name="rightMargin">
481 </spacer> 387 <number>3</number>
482 </item> 388 </property>
483 <item row="1" column="1"> 389 <property name="bottomMargin">
484 <widget class="QLabel" name="left_buttons_label"> 390 <number>3</number>
485 <property name="minimumSize"> 391 </property>
486 <size> 392 <item>
487 <width>90</width> 393 <widget class="QPushButton" name="buttonLStickUp">
488 <height>0</height> 394 <property name="minimumSize">
489 </size> 395 <size>
490 </property> 396 <width>57</width>
491 <property name="text"> 397 <height>0</height>
492 <string>Left Buttons</string> 398 </size>
493 </property> 399 </property>
494 </widget> 400 <property name="maximumSize">
495 </item> 401 <size>
496 <item row="1" column="5"> 402 <width>55</width>
497 <widget class="QPushButton" name="right_buttons_button"> 403 <height>16777215</height>
498 <property name="sizePolicy"> 404 </size>
499 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 405 </property>
500 <horstretch>0</horstretch> 406 <property name="styleSheet">
501 <verstretch>0</verstretch> 407 <string notr="true">min-width: 55px;</string>
502 </sizepolicy> 408 </property>
503 </property> 409 <property name="text">
504 <property name="minimumSize"> 410 <string>Up</string>
505 <size> 411 </property>
506 <width>32</width> 412 </widget>
507 <height>0</height> 413 </item>
508 </size> 414 </layout>
509 </property> 415 </widget>
510 <property name="maximumSize"> 416 </item>
511 <size> 417 <item>
512 <width>40</width> 418 <spacer name="horizontalSpacerLStickUpRight">
513 <height>16777215</height> 419 <property name="orientation">
514 </size> 420 <enum>Qt::Horizontal</enum>
515 </property> 421 </property>
516 <property name="text"> 422 <property name="sizeHint" stdset="0">
517 <string/> 423 <size>
518 </property> 424 <width>20</width>
519 </widget> 425 <height>20</height>
520 </item> 426 </size>
521 <item row="0" column="4"> 427 </property>
522 <widget class="QLabel" name="right_body_label"> 428 </spacer>
523 <property name="text"> 429 </item>
524 <string>Right Body</string> 430 </layout>
525 </property> 431 </widget>
526 </widget> 432 </item>
527 </item> 433 <item>
528 <item row="1" column="4"> 434 <layout class="QHBoxLayout" name="buttonLStickLeftRightHorizontaLayout">
529 <widget class="QLabel" name="right_buttons_label"> 435 <property name="spacing">
530 <property name="minimumSize"> 436 <number>3</number>
531 <size> 437 </property>
532 <width>90</width> 438 <item alignment="Qt::AlignHCenter">
533 <height>0</height> 439 <widget class="QGroupBox" name="buttonLStickLeftGroup">
534 </size> 440 <property name="title">
535 </property> 441 <string>Left</string>
536 <property name="text"> 442 </property>
537 <string>Right Buttons</string> 443 <property name="alignment">
538 </property> 444 <set>Qt::AlignCenter</set>
539 </widget> 445 </property>
540 </item> 446 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
541 <item row="1" column="2"> 447 <property name="spacing">
542 <widget class="QPushButton" name="left_buttons_button"> 448 <number>3</number>
543 <property name="sizePolicy"> 449 </property>
544 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 450 <property name="leftMargin">
545 <horstretch>0</horstretch> 451 <number>3</number>
546 <verstretch>0</verstretch> 452 </property>
547 </sizepolicy> 453 <property name="topMargin">
548 </property> 454 <number>3</number>
549 <property name="minimumSize"> 455 </property>
550 <size> 456 <property name="rightMargin">
551 <width>32</width> 457 <number>3</number>
552 <height>0</height> 458 </property>
553 </size> 459 <property name="bottomMargin">
554 </property> 460 <number>3</number>
555 <property name="maximumSize"> 461 </property>
556 <size> 462 <item>
557 <width>40</width> 463 <widget class="QPushButton" name="buttonLStickLeft">
558 <height>16777215</height> 464 <property name="minimumSize">
559 </size> 465 <size>
560 </property> 466 <width>57</width>
561 <property name="text"> 467 <height>0</height>
562 <string/> 468 </size>
563 </property> 469 </property>
564 </widget> 470 <property name="maximumSize">
565 </item> 471 <size>
566 <item row="0" column="2"> 472 <width>55</width>
567 <widget class="QPushButton" name="left_body_button"> 473 <height>16777215</height>
568 <property name="sizePolicy"> 474 </size>
569 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 475 </property>
570 <horstretch>0</horstretch> 476 <property name="styleSheet">
571 <verstretch>0</verstretch> 477 <string notr="true">min-width: 55px;</string>
572 </sizepolicy> 478 </property>
573 </property> 479 <property name="text">
574 <property name="minimumSize"> 480 <string>Left</string>
575 <size> 481 </property>
576 <width>32</width> 482 </widget>
577 <height>0</height> 483 </item>
578 </size> 484 </layout>
579 </property> 485 </widget>
580 <property name="maximumSize"> 486 </item>
581 <size> 487 <item alignment="Qt::AlignHCenter">
582 <width>40</width> 488 <widget class="QGroupBox" name="buttonLStickRightGroup">
583 <height>16777215</height> 489 <property name="title">
584 </size> 490 <string>Right</string>
585 </property> 491 </property>
586 <property name="text"> 492 <property name="alignment">
587 <string/> 493 <set>Qt::AlignCenter</set>
588 </property> 494 </property>
589 </widget> 495 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
590 </item> 496 <property name="spacing">
591 <item row="0" column="5"> 497 <number>3</number>
592 <widget class="QPushButton" name="right_body_button"> 498 </property>
593 <property name="sizePolicy"> 499 <property name="leftMargin">
594 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 500 <number>3</number>
595 <horstretch>0</horstretch> 501 </property>
596 <verstretch>0</verstretch> 502 <property name="topMargin">
597 </sizepolicy> 503 <number>3</number>
598 </property> 504 </property>
599 <property name="minimumSize"> 505 <property name="rightMargin">
600 <size> 506 <number>3</number>
601 <width>32</width> 507 </property>
602 <height>0</height> 508 <property name="bottomMargin">
603 </size> 509 <number>3</number>
604 </property> 510 </property>
605 <property name="maximumSize"> 511 <item>
606 <size> 512 <widget class="QPushButton" name="buttonLStickRight">
607 <width>40</width> 513 <property name="minimumSize">
608 <height>16777215</height> 514 <size>
609 </size> 515 <width>57</width>
610 </property> 516 <height>0</height>
611 <property name="text"> 517 </size>
612 <string/> 518 </property>
613 </property> 519 <property name="maximumSize">
614 </widget> 520 <size>
615 </item> 521 <width>55</width>
616 <item row="0" column="3"> 522 <height>16777215</height>
617 <spacer name="horizontalSpacer_4"> 523 </size>
618 <property name="orientation"> 524 </property>
619 <enum>Qt::Horizontal</enum> 525 <property name="styleSheet">
620 </property> 526 <string notr="true">min-width: 55px;</string>
621 <property name="sizeType"> 527 </property>
622 <enum>QSizePolicy::Fixed</enum> 528 <property name="text">
623 </property> 529 <string>Right</string>
624 <property name="sizeHint" stdset="0"> 530 </property>
625 <size> 531 </widget>
626 <width>20</width> 532 </item>
627 <height>20</height> 533 </layout>
628 </size> 534 </widget>
629 </property> 535 </item>
630 </spacer> 536 </layout>
631 </item> 537 </item>
632 </layout> 538 <item>
633 </widget> 539 <widget class="QWidget" name="buttonLStickDownWidget" native="true">
634 </item> 540 <layout class="QHBoxLayout" name="horizontalLayout_22">
635 <item row="1" column="0"> 541 <property name="spacing">
636 <widget class="QGroupBox" name="LStick"> 542 <number>0</number>
637 <property name="title"> 543 </property>
638 <string>Left Stick</string> 544 <property name="leftMargin">
639 </property> 545 <number>0</number>
640 <property name="flat"> 546 </property>
641 <bool>false</bool> 547 <property name="topMargin">
642 </property> 548 <number>0</number>
643 <property name="checkable"> 549 </property>
644 <bool>false</bool> 550 <property name="rightMargin">
645 </property> 551 <number>0</number>
646 <layout class="QGridLayout" name="gridLayout_4"> 552 </property>
647 <item row="1" column="1"> 553 <property name="bottomMargin">
648 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> 554 <number>0</number>
649 <item> 555 </property>
650 <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout"> 556 <item>
651 <item> 557 <spacer name="horizontalSpacerLStickDownLeft">
652 <widget class="QLabel" name="labelLStickUp"> 558 <property name="orientation">
653 <property name="text"> 559 <enum>Qt::Horizontal</enum>
654 <string>Up:</string> 560 </property>
561 <property name="sizeHint" stdset="0">
562 <size>
563 <width>20</width>
564 <height>20</height>
565 </size>
566 </property>
567 </spacer>
568 </item>
569 <item alignment="Qt::AlignHCenter">
570 <widget class="QGroupBox" name="buttonLStickDownGroup">
571 <property name="title">
572 <string>Down</string>
573 </property>
574 <property name="alignment">
575 <set>Qt::AlignCenter</set>
576 </property>
577 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
578 <property name="spacing">
579 <number>3</number>
580 </property>
581 <property name="leftMargin">
582 <number>3</number>
583 </property>
584 <property name="topMargin">
585 <number>3</number>
586 </property>
587 <property name="rightMargin">
588 <number>3</number>
589 </property>
590 <property name="bottomMargin">
591 <number>3</number>
592 </property>
593 <item>
594 <widget class="QPushButton" name="buttonLStickDown">
595 <property name="minimumSize">
596 <size>
597 <width>57</width>
598 <height>0</height>
599 </size>
600 </property>
601 <property name="maximumSize">
602 <size>
603 <width>55</width>
604 <height>16777215</height>
605 </size>
606 </property>
607 <property name="styleSheet">
608 <string notr="true">min-width: 55px;</string>
609 </property>
610 <property name="text">
611 <string>Down</string>
612 </property>
613 </widget>
614 </item>
615 </layout>
616 </widget>
617 </item>
618 <item>
619 <spacer name="horizontalSpacerLStickDownRight">
620 <property name="orientation">
621 <enum>Qt::Horizontal</enum>
622 </property>
623 <property name="sizeHint" stdset="0">
624 <size>
625 <width>20</width>
626 <height>20</height>
627 </size>
628 </property>
629 </spacer>
630 </item>
631 </layout>
632 </widget>
633 </item>
634 <item>
635 <layout class="QHBoxLayout" name="buttonLStickPressedModifierHorizontalLayout">
636 <property name="spacing">
637 <number>3</number>
638 </property>
639 <item alignment="Qt::AlignHCenter">
640 <widget class="QGroupBox" name="buttonLStickPressedGroup">
641 <property name="title">
642 <string>Pressed</string>
643 </property>
644 <property name="alignment">
645 <set>Qt::AlignCenter</set>
646 </property>
647 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0">
648 <property name="spacing">
649 <number>3</number>
650 </property>
651 <property name="leftMargin">
652 <number>3</number>
653 </property>
654 <property name="topMargin">
655 <number>3</number>
656 </property>
657 <property name="rightMargin">
658 <number>3</number>
659 </property>
660 <property name="bottomMargin">
661 <number>3</number>
662 </property>
663 <item>
664 <widget class="QPushButton" name="buttonLStick">
665 <property name="minimumSize">
666 <size>
667 <width>57</width>
668 <height>0</height>
669 </size>
670 </property>
671 <property name="maximumSize">
672 <size>
673 <width>55</width>
674 <height>16777215</height>
675 </size>
676 </property>
677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string>
679 </property>
680 <property name="text">
681 <string>Pressed</string>
682 </property>
683 </widget>
684 </item>
685 </layout>
686 </widget>
687 </item>
688 <item alignment="Qt::AlignHCenter">
689 <widget class="QGroupBox" name="buttonLStickModGroup">
690 <property name="title">
691 <string>Modifier</string>
692 </property>
693 <property name="alignment">
694 <set>Qt::AlignCenter</set>
695 </property>
696 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
697 <property name="spacing">
698 <number>3</number>
699 </property>
700 <property name="leftMargin">
701 <number>3</number>
702 </property>
703 <property name="topMargin">
704 <number>3</number>
705 </property>
706 <property name="rightMargin">
707 <number>3</number>
708 </property>
709 <property name="bottomMargin">
710 <number>3</number>
711 </property>
712 <item>
713 <widget class="QPushButton" name="buttonLStickMod">
714 <property name="minimumSize">
715 <size>
716 <width>57</width>
717 <height>0</height>
718 </size>
719 </property>
720 <property name="maximumSize">
721 <size>
722 <width>55</width>
723 <height>16777215</height>
724 </size>
725 </property>
726 <property name="styleSheet">
727 <string notr="true">min-width: 55px;</string>
728 </property>
729 <property name="text">
730 <string>Modifier</string>
731 </property>
732 </widget>
733 </item>
734 </layout>
735 </widget>
736 </item>
737 <item>
738 <widget class="QGroupBox" name="buttonLStickRangeGroup">
739 <property name="title">
740 <string>Range</string>
741 </property>
742 <layout class="QHBoxLayout" name="buttonLStickRangeGroupHorizontalLayout">
743 <property name="spacing">
744 <number>3</number>
745 </property>
746 <property name="leftMargin">
747 <number>3</number>
748 </property>
749 <property name="topMargin">
750 <number>3</number>
751 </property>
752 <property name="rightMargin">
753 <number>3</number>
754 </property>
755 <property name="bottomMargin">
756 <number>3</number>
757 </property>
758 <item>
759 <widget class="QSpinBox" name="spinboxLStickRange">
760 <property name="minimumSize">
761 <size>
762 <width>55</width>
763 <height>21</height>
764 </size>
765 </property>
766 <property name="maximumSize">
767 <size>
768 <width>55</width>
769 <height>16777215</height>
770 </size>
771 </property>
772 <property name="suffix">
773 <string>%</string>
774 </property>
775 <property name="minimum">
776 <number>50</number>
777 </property>
778 <property name="maximum">
779 <number>150</number>
780 </property>
781 <property name="value">
782 <number>100</number>
783 </property>
784 </widget>
785 </item>
786 </layout>
787 </widget>
788 </item>
789 </layout>
790 </item>
791 <item>
792 <layout class="QVBoxLayout" name="sliderLStickDeadzoneModifierRangeVerticalLayout">
793 <property name="spacing">
794 <number>3</number>
795 </property>
796 <property name="sizeConstraint">
797 <enum>QLayout::SetDefaultConstraint</enum>
798 </property>
799 <property name="leftMargin">
800 <number>0</number>
801 </property>
802 <property name="topMargin">
803 <number>2</number>
804 </property>
805 <property name="rightMargin">
806 <number>0</number>
807 </property>
808 <property name="bottomMargin">
809 <number>3</number>
810 </property>
811 <item>
812 <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout">
813 <item>
814 <widget class="QLabel" name="labelLStickDeadzone">
815 <property name="text">
816 <string>Deadzone: 0%</string>
817 </property>
818 <property name="alignment">
819 <set>Qt::AlignHCenter</set>
820 </property>
821 </widget>
822 </item>
823 </layout>
824 </item>
825 <item>
826 <widget class="QSlider" name="sliderLStickDeadzone">
827 <property name="maximum">
828 <number>100</number>
829 </property>
830 <property name="orientation">
831 <enum>Qt::Horizontal</enum>
832 </property>
833 </widget>
834 </item>
835 <item>
836 <layout class="QHBoxLayout" name="sliderLStickModifierRangeHorizontalLayout">
837 <item>
838 <widget class="QLabel" name="labelLStickModifierRange">
839 <property name="text">
840 <string>Modifier Range: 0%</string>
841 </property>
842 <property name="alignment">
843 <set>Qt::AlignHCenter</set>
844 </property>
845 </widget>
846 </item>
847 </layout>
848 </item>
849 <item>
850 <widget class="QSlider" name="sliderLStickModifierRange">
851 <property name="maximum">
852 <number>100</number>
853 </property>
854 <property name="orientation">
855 <enum>Qt::Horizontal</enum>
856 </property>
857 </widget>
858 </item>
859 </layout>
860 </item>
861 </layout>
862 </widget>
863 </item>
864 <item>
865 <spacer name="verticalSpacerBottomLeft">
866 <property name="orientation">
867 <enum>Qt::Vertical</enum>
868 </property>
869 <property name="sizeHint" stdset="0">
870 <size>
871 <width>20</width>
872 <height>0</height>
873 </size>
874 </property>
875 </spacer>
876 </item>
877 <item>
878 <widget class="QGroupBox" name="Dpad">
879 <property name="sizePolicy">
880 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
881 <horstretch>0</horstretch>
882 <verstretch>0</verstretch>
883 </sizepolicy>
884 </property>
885 <property name="title">
886 <string>D-Pad</string>
887 </property>
888 <property name="flat">
889 <bool>false</bool>
890 </property>
891 <property name="checkable">
892 <bool>false</bool>
893 </property>
894 <layout class="QVBoxLayout" name="verticalLayout_5">
895 <property name="spacing">
896 <number>0</number>
655 </property> 897 </property>
656 </widget> 898 <property name="leftMargin">
657 </item> 899 <number>3</number>
658 </layout>
659 </item>
660 <item>
661 <widget class="QPushButton" name="buttonLStickUp">
662 <property name="text">
663 <string/>
664 </property>
665 </widget>
666 </item>
667 </layout>
668 </item>
669 <item row="0" column="2">
670 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
671 <item>
672 <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
673 <item>
674 <widget class="QLabel" name="labelLStickRight">
675 <property name="text">
676 <string>Right:</string>
677 </property> 900 </property>
678 </widget> 901 <property name="topMargin">
679 </item> 902 <number>0</number>
680 </layout>
681 </item>
682 <item>
683 <widget class="QPushButton" name="buttonLStickRight">
684 <property name="text">
685 <string/>
686 </property>
687 </widget>
688 </item>
689 </layout>
690 </item>
691 <item row="4" column="1" colspan="2">
692 <widget class="QPushButton" name="buttonLStickAnalog">
693 <property name="text">
694 <string>Set Analog Stick</string>
695 </property>
696 </widget>
697 </item>
698 <item row="0" column="1">
699 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
700 <item>
701 <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
702 <item>
703 <widget class="QLabel" name="labelLStickLeft">
704 <property name="text">
705 <string>Left:</string>
706 </property> 903 </property>
707 </widget> 904 <property name="rightMargin">
708 </item> 905 <number>3</number>
709 </layout>
710 </item>
711 <item>
712 <widget class="QPushButton" name="buttonLStickLeft">
713 <property name="text">
714 <string/>
715 </property>
716 </widget>
717 </item>
718 </layout>
719 </item>
720 <item row="1" column="2">
721 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
722 <item>
723 <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
724 <item>
725 <widget class="QLabel" name="labelLStickDown">
726 <property name="text">
727 <string>Down:</string>
728 </property> 906 </property>
729 </widget> 907 <property name="bottomMargin">
730 </item> 908 <number>3</number>
731 </layout>
732 </item>
733 <item>
734 <widget class="QPushButton" name="buttonLStickDown">
735 <property name="text">
736 <string/>
737 </property>
738 </widget>
739 </item>
740 </layout>
741 </item>
742 <item row="3" column="2">
743 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
744 <item>
745 <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
746 <item>
747 <widget class="QLabel" name="labelLStickMod">
748 <property name="text">
749 <string>Modifier:</string>
750 </property> 909 </property>
751 </widget> 910 <item>
752 </item> 911 <widget class="QWidget" name="buttonDpadUpWidget" native="true">
753 </layout> 912 <layout class="QHBoxLayout" name="horizontalLayout_23">
754 </item> 913 <property name="spacing">
755 <item> 914 <number>0</number>
756 <widget class="QPushButton" name="buttonLStickMod"> 915 </property>
757 <property name="text"> 916 <property name="leftMargin">
758 <string/> 917 <number>0</number>
759 </property> 918 </property>
760 </widget> 919 <property name="topMargin">
761 </item> 920 <number>0</number>
762 </layout> 921 </property>
922 <property name="rightMargin">
923 <number>0</number>
924 </property>
925 <property name="bottomMargin">
926 <number>0</number>
927 </property>
928 <item>
929 <spacer name="horizontalSpacerDpadUpLeft">
930 <property name="orientation">
931 <enum>Qt::Horizontal</enum>
932 </property>
933 <property name="sizeHint" stdset="0">
934 <size>
935 <width>20</width>
936 <height>20</height>
937 </size>
938 </property>
939 </spacer>
940 </item>
941 <item alignment="Qt::AlignHCenter">
942 <widget class="QGroupBox" name="buttonDpadUpGroup">
943 <property name="title">
944 <string>Up</string>
945 </property>
946 <property name="alignment">
947 <set>Qt::AlignCenter</set>
948 </property>
949 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
950 <property name="spacing">
951 <number>3</number>
952 </property>
953 <property name="leftMargin">
954 <number>3</number>
955 </property>
956 <property name="topMargin">
957 <number>3</number>
958 </property>
959 <property name="rightMargin">
960 <number>3</number>
961 </property>
962 <property name="bottomMargin">
963 <number>3</number>
964 </property>
965 <item>
966 <widget class="QPushButton" name="buttonDpadUp">
967 <property name="minimumSize">
968 <size>
969 <width>57</width>
970 <height>0</height>
971 </size>
972 </property>
973 <property name="maximumSize">
974 <size>
975 <width>55</width>
976 <height>16777215</height>
977 </size>
978 </property>
979 <property name="styleSheet">
980 <string notr="true">min-width: 55px;</string>
981 </property>
982 <property name="text">
983 <string>Up</string>
984 </property>
985 </widget>
986 </item>
987 </layout>
988 </widget>
989 </item>
990 <item>
991 <spacer name="horizontalSpacerDpadUpRight">
992 <property name="orientation">
993 <enum>Qt::Horizontal</enum>
994 </property>
995 <property name="sizeHint" stdset="0">
996 <size>
997 <width>20</width>
998 <height>20</height>
999 </size>
1000 </property>
1001 </spacer>
1002 </item>
1003 </layout>
1004 </widget>
1005 </item>
1006 <item>
1007 <layout class="QHBoxLayout" name="buttonDpadLeftRightHorizontalLayout">
1008 <property name="spacing">
1009 <number>3</number>
1010 </property>
1011 <item alignment="Qt::AlignHCenter">
1012 <widget class="QGroupBox" name="buttonDpadLeftGroup">
1013 <property name="title">
1014 <string>Left</string>
1015 </property>
1016 <property name="alignment">
1017 <set>Qt::AlignCenter</set>
1018 </property>
1019 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
1020 <property name="spacing">
1021 <number>3</number>
1022 </property>
1023 <property name="leftMargin">
1024 <number>3</number>
1025 </property>
1026 <property name="topMargin">
1027 <number>3</number>
1028 </property>
1029 <property name="rightMargin">
1030 <number>3</number>
1031 </property>
1032 <property name="bottomMargin">
1033 <number>3</number>
1034 </property>
1035 <item>
1036 <widget class="QPushButton" name="buttonDpadLeft">
1037 <property name="minimumSize">
1038 <size>
1039 <width>57</width>
1040 <height>0</height>
1041 </size>
1042 </property>
1043 <property name="maximumSize">
1044 <size>
1045 <width>55</width>
1046 <height>16777215</height>
1047 </size>
1048 </property>
1049 <property name="styleSheet">
1050 <string notr="true">min-width: 55px;</string>
1051 </property>
1052 <property name="text">
1053 <string>Left</string>
1054 </property>
1055 </widget>
1056 </item>
1057 </layout>
1058 </widget>
1059 </item>
1060 <item alignment="Qt::AlignHCenter">
1061 <widget class="QGroupBox" name="buttonDpadRightGroup">
1062 <property name="title">
1063 <string>Right</string>
1064 </property>
1065 <property name="alignment">
1066 <set>Qt::AlignCenter</set>
1067 </property>
1068 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
1069 <property name="spacing">
1070 <number>3</number>
1071 </property>
1072 <property name="leftMargin">
1073 <number>3</number>
1074 </property>
1075 <property name="topMargin">
1076 <number>3</number>
1077 </property>
1078 <property name="rightMargin">
1079 <number>3</number>
1080 </property>
1081 <property name="bottomMargin">
1082 <number>3</number>
1083 </property>
1084 <item>
1085 <widget class="QPushButton" name="buttonDpadRight">
1086 <property name="minimumSize">
1087 <size>
1088 <width>57</width>
1089 <height>0</height>
1090 </size>
1091 </property>
1092 <property name="maximumSize">
1093 <size>
1094 <width>55</width>
1095 <height>16777215</height>
1096 </size>
1097 </property>
1098 <property name="styleSheet">
1099 <string notr="true">min-width: 55px;</string>
1100 </property>
1101 <property name="text">
1102 <string>Right</string>
1103 </property>
1104 </widget>
1105 </item>
1106 </layout>
1107 </widget>
1108 </item>
1109 </layout>
1110 </item>
1111 <item>
1112 <widget class="QWidget" name="buttonDpadDownWidget" native="true">
1113 <layout class="QHBoxLayout" name="horizontalLayout_24">
1114 <property name="spacing">
1115 <number>0</number>
1116 </property>
1117 <property name="leftMargin">
1118 <number>0</number>
1119 </property>
1120 <property name="topMargin">
1121 <number>0</number>
1122 </property>
1123 <property name="rightMargin">
1124 <number>0</number>
1125 </property>
1126 <property name="bottomMargin">
1127 <number>0</number>
1128 </property>
1129 <item>
1130 <spacer name="horizontalSpacerDpadDownLeft">
1131 <property name="orientation">
1132 <enum>Qt::Horizontal</enum>
1133 </property>
1134 <property name="sizeHint" stdset="0">
1135 <size>
1136 <width>20</width>
1137 <height>20</height>
1138 </size>
1139 </property>
1140 </spacer>
1141 </item>
1142 <item alignment="Qt::AlignHCenter">
1143 <widget class="QGroupBox" name="buttonDpadDownGroup">
1144 <property name="title">
1145 <string>Down</string>
1146 </property>
1147 <property name="alignment">
1148 <set>Qt::AlignCenter</set>
1149 </property>
1150 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
1151 <property name="spacing">
1152 <number>3</number>
1153 </property>
1154 <property name="leftMargin">
1155 <number>3</number>
1156 </property>
1157 <property name="topMargin">
1158 <number>3</number>
1159 </property>
1160 <property name="rightMargin">
1161 <number>3</number>
1162 </property>
1163 <property name="bottomMargin">
1164 <number>3</number>
1165 </property>
1166 <item>
1167 <widget class="QPushButton" name="buttonDpadDown">
1168 <property name="minimumSize">
1169 <size>
1170 <width>57</width>
1171 <height>0</height>
1172 </size>
1173 </property>
1174 <property name="maximumSize">
1175 <size>
1176 <width>55</width>
1177 <height>16777215</height>
1178 </size>
1179 </property>
1180 <property name="styleSheet">
1181 <string notr="true">min-width: 55px;</string>
1182 </property>
1183 <property name="text">
1184 <string>Down</string>
1185 </property>
1186 </widget>
1187 </item>
1188 </layout>
1189 </widget>
1190 </item>
1191 <item>
1192 <spacer name="horizontalSpacerDpadDownRight">
1193 <property name="orientation">
1194 <enum>Qt::Horizontal</enum>
1195 </property>
1196 <property name="sizeHint" stdset="0">
1197 <size>
1198 <width>20</width>
1199 <height>20</height>
1200 </size>
1201 </property>
1202 </spacer>
1203 </item>
1204 </layout>
1205 </widget>
1206 </item>
1207 </layout>
1208 </widget>
1209 </item>
1210 <item>
1211 <spacer name="verticalSpacerBottomLeft_2">
1212 <property name="orientation">
1213 <enum>Qt::Vertical</enum>
1214 </property>
1215 <property name="sizeHint" stdset="0">
1216 <size>
1217 <width>20</width>
1218 <height>0</height>
1219 </size>
1220 </property>
1221 </spacer>
1222 </item>
1223 </layout>
1224 </widget>
763 </item> 1225 </item>
764 <item row="3" column="1"> 1226 <item>
765 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> 1227 <widget class="QWidget" name="bottomMiddle" native="true">
766 <item> 1228 <layout class="QVBoxLayout" stretch="0,0,0">
767 <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout"> 1229 <property name="spacing">
768 <item> 1230 <number>6</number>
769 <widget class="QLabel" name="labelLStickPressed"> 1231 </property>
770 <property name="text"> 1232 <property name="leftMargin">
771 <string>Pressed:</string> 1233 <number>0</number>
1234 </property>
1235 <property name="topMargin">
1236 <number>0</number>
1237 </property>
1238 <property name="rightMargin">
1239 <number>0</number>
1240 </property>
1241 <property name="bottomMargin">
1242 <number>0</number>
1243 </property>
1244 <item>
1245 <layout class="QHBoxLayout" name="shoulderButtons">
1246 <property name="spacing">
1247 <number>3</number>
1248 </property>
1249 <item>
1250 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
1251 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
1252 <property name="spacing">
1253 <number>0</number>
1254 </property>
1255 <property name="leftMargin">
1256 <number>0</number>
1257 </property>
1258 <property name="topMargin">
1259 <number>0</number>
1260 </property>
1261 <property name="rightMargin">
1262 <number>0</number>
1263 </property>
1264 <property name="bottomMargin">
1265 <number>0</number>
1266 </property>
1267 <item>
1268 <widget class="QGroupBox" name="buttonShoulderButtonsButtonLGroup">
1269 <property name="title">
1270 <string>L</string>
1271 </property>
1272 <property name="alignment">
1273 <set>Qt::AlignCenter</set>
1274 </property>
1275 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
1276 <property name="spacing">
1277 <number>3</number>
1278 </property>
1279 <property name="leftMargin">
1280 <number>3</number>
1281 </property>
1282 <property name="topMargin">
1283 <number>3</number>
1284 </property>
1285 <property name="rightMargin">
1286 <number>3</number>
1287 </property>
1288 <property name="bottomMargin">
1289 <number>3</number>
1290 </property>
1291 <item>
1292 <widget class="QPushButton" name="buttonL">
1293 <property name="minimumSize">
1294 <size>
1295 <width>57</width>
1296 <height>0</height>
1297 </size>
1298 </property>
1299 <property name="maximumSize">
1300 <size>
1301 <width>55</width>
1302 <height>16777215</height>
1303 </size>
1304 </property>
1305 <property name="styleSheet">
1306 <string notr="true">min-width: 55px;</string>
1307 </property>
1308 <property name="text">
1309 <string>L</string>
1310 </property>
1311 </widget>
1312 </item>
1313 </layout>
1314 </widget>
1315 </item>
1316 <item>
1317 <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup">
1318 <property name="title">
1319 <string>ZL</string>
1320 </property>
1321 <property name="alignment">
1322 <set>Qt::AlignCenter</set>
1323 </property>
1324 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
1325 <property name="spacing">
1326 <number>3</number>
1327 </property>
1328 <property name="leftMargin">
1329 <number>3</number>
1330 </property>
1331 <property name="topMargin">
1332 <number>3</number>
1333 </property>
1334 <property name="rightMargin">
1335 <number>3</number>
1336 </property>
1337 <property name="bottomMargin">
1338 <number>3</number>
1339 </property>
1340 <item>
1341 <widget class="QPushButton" name="buttonZL">
1342 <property name="minimumSize">
1343 <size>
1344 <width>57</width>
1345 <height>0</height>
1346 </size>
1347 </property>
1348 <property name="maximumSize">
1349 <size>
1350 <width>55</width>
1351 <height>16777215</height>
1352 </size>
1353 </property>
1354 <property name="styleSheet">
1355 <string notr="true">min-width: 55px;</string>
1356 </property>
1357 <property name="text">
1358 <string>ZL</string>
1359 </property>
1360 </widget>
1361 </item>
1362 </layout>
1363 </widget>
1364 </item>
1365 </layout>
1366 </widget>
1367 </item>
1368 <item>
1369 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget" native="true">
1370 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidgetLayout">
1371 <property name="spacing">
1372 <number>0</number>
1373 </property>
1374 <property name="leftMargin">
1375 <number>0</number>
1376 </property>
1377 <property name="topMargin">
1378 <number>0</number>
1379 </property>
1380 <property name="rightMargin">
1381 <number>0</number>
1382 </property>
1383 <property name="bottomMargin">
1384 <number>0</number>
1385 </property>
1386 <item>
1387 <spacer name="horizontalSpacerShoulderButtons1">
1388 <property name="orientation">
1389 <enum>Qt::Horizontal</enum>
1390 </property>
1391 <property name="sizeHint" stdset="0">
1392 <size>
1393 <width>0</width>
1394 <height>20</height>
1395 </size>
1396 </property>
1397 </spacer>
1398 </item>
1399 </layout>
1400 </widget>
1401 </item>
1402 <item>
1403 <widget class="QWidget" name="buttonMiscButtonsMinusScreenshot" native="true">
1404 <layout class="QVBoxLayout" name="buttonMiscButtonsMinusScreenshotVerticalLayout">
1405 <property name="spacing">
1406 <number>0</number>
1407 </property>
1408 <property name="leftMargin">
1409 <number>0</number>
1410 </property>
1411 <property name="topMargin">
1412 <number>0</number>
1413 </property>
1414 <property name="rightMargin">
1415 <number>0</number>
1416 </property>
1417 <property name="bottomMargin">
1418 <number>0</number>
1419 </property>
1420 <item alignment="Qt::AlignHCenter">
1421 <widget class="QGroupBox" name="buttonMiscButtonsMinusGroup">
1422 <property name="title">
1423 <string>Minus</string>
1424 </property>
1425 <property name="alignment">
1426 <set>Qt::AlignCenter</set>
1427 </property>
1428 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
1429 <property name="spacing">
1430 <number>3</number>
1431 </property>
1432 <property name="leftMargin">
1433 <number>3</number>
1434 </property>
1435 <property name="topMargin">
1436 <number>3</number>
1437 </property>
1438 <property name="rightMargin">
1439 <number>3</number>
1440 </property>
1441 <property name="bottomMargin">
1442 <number>3</number>
1443 </property>
1444 <item>
1445 <widget class="QPushButton" name="buttonMinus">
1446 <property name="minimumSize">
1447 <size>
1448 <width>57</width>
1449 <height>0</height>
1450 </size>
1451 </property>
1452 <property name="maximumSize">
1453 <size>
1454 <width>55</width>
1455 <height>16777215</height>
1456 </size>
1457 </property>
1458 <property name="styleSheet">
1459 <string notr="true">min-width: 55px;</string>
1460 </property>
1461 <property name="text">
1462 <string>Minus</string>
1463 </property>
1464 </widget>
1465 </item>
1466 </layout>
1467 </widget>
1468 </item>
1469 <item alignment="Qt::AlignHCenter">
1470 <widget class="QGroupBox" name="buttonMiscButtonsScreenshotGroup">
1471 <property name="title">
1472 <string>Capture</string>
1473 </property>
1474 <property name="alignment">
1475 <set>Qt::AlignCenter</set>
1476 </property>
1477 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
1478 <property name="spacing">
1479 <number>3</number>
1480 </property>
1481 <property name="leftMargin">
1482 <number>3</number>
1483 </property>
1484 <property name="topMargin">
1485 <number>3</number>
1486 </property>
1487 <property name="rightMargin">
1488 <number>3</number>
1489 </property>
1490 <property name="bottomMargin">
1491 <number>3</number>
1492 </property>
1493 <item>
1494 <widget class="QPushButton" name="buttonScreenshot">
1495 <property name="minimumSize">
1496 <size>
1497 <width>57</width>
1498 <height>0</height>
1499 </size>
1500 </property>
1501 <property name="maximumSize">
1502 <size>
1503 <width>55</width>
1504 <height>16777215</height>
1505 </size>
1506 </property>
1507 <property name="styleSheet">
1508 <string notr="true">min-width: 55px;</string>
1509 </property>
1510 <property name="text">
1511 <string>Capture</string>
1512 </property>
1513 </widget>
1514 </item>
1515 </layout>
1516 </widget>
1517 </item>
1518 </layout>
1519 </widget>
1520 </item>
1521 <item>
1522 <widget class="QWidget" name="buttonMiscButtonsPlusHome" native="true">
1523 <layout class="QVBoxLayout" name="buttonMiscButtonsPlusHomeVerticalLayout">
1524 <property name="spacing">
1525 <number>0</number>
1526 </property>
1527 <property name="leftMargin">
1528 <number>0</number>
1529 </property>
1530 <property name="topMargin">
1531 <number>0</number>
1532 </property>
1533 <property name="rightMargin">
1534 <number>0</number>
1535 </property>
1536 <property name="bottomMargin">
1537 <number>0</number>
1538 </property>
1539 <item alignment="Qt::AlignHCenter">
1540 <widget class="QGroupBox" name="buttonMiscButtonsPlusGroup">
1541 <property name="title">
1542 <string>Plus</string>
1543 </property>
1544 <property name="alignment">
1545 <set>Qt::AlignCenter</set>
1546 </property>
1547 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
1548 <property name="spacing">
1549 <number>3</number>
1550 </property>
1551 <property name="leftMargin">
1552 <number>3</number>
1553 </property>
1554 <property name="topMargin">
1555 <number>3</number>
1556 </property>
1557 <property name="rightMargin">
1558 <number>3</number>
1559 </property>
1560 <property name="bottomMargin">
1561 <number>3</number>
1562 </property>
1563 <item>
1564 <widget class="QPushButton" name="buttonPlus">
1565 <property name="minimumSize">
1566 <size>
1567 <width>57</width>
1568 <height>0</height>
1569 </size>
1570 </property>
1571 <property name="maximumSize">
1572 <size>
1573 <width>55</width>
1574 <height>16777215</height>
1575 </size>
1576 </property>
1577 <property name="styleSheet">
1578 <string notr="true">min-width: 55px;</string>
1579 </property>
1580 <property name="text">
1581 <string>Plus</string>
1582 </property>
1583 </widget>
1584 </item>
1585 </layout>
1586 </widget>
1587 </item>
1588 <item alignment="Qt::AlignHCenter">
1589 <widget class="QGroupBox" name="buttonMiscButtonsHomeGroup">
1590 <property name="title">
1591 <string>Home</string>
1592 </property>
1593 <property name="alignment">
1594 <set>Qt::AlignCenter</set>
1595 </property>
1596 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
1597 <property name="spacing">
1598 <number>3</number>
1599 </property>
1600 <property name="leftMargin">
1601 <number>3</number>
1602 </property>
1603 <property name="topMargin">
1604 <number>3</number>
1605 </property>
1606 <property name="rightMargin">
1607 <number>3</number>
1608 </property>
1609 <property name="bottomMargin">
1610 <number>3</number>
1611 </property>
1612 <item>
1613 <widget class="QPushButton" name="buttonHome">
1614 <property name="minimumSize">
1615 <size>
1616 <width>57</width>
1617 <height>0</height>
1618 </size>
1619 </property>
1620 <property name="maximumSize">
1621 <size>
1622 <width>55</width>
1623 <height>16777215</height>
1624 </size>
1625 </property>
1626 <property name="styleSheet">
1627 <string notr="true">min-width: 55px;</string>
1628 </property>
1629 <property name="text">
1630 <string>Home</string>
1631 </property>
1632 </widget>
1633 </item>
1634 </layout>
1635 </widget>
1636 </item>
1637 </layout>
1638 </widget>
1639 </item>
1640 <item>
1641 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget3" native="true">
1642 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget3Layout">
1643 <property name="spacing">
1644 <number>0</number>
1645 </property>
1646 <property name="leftMargin">
1647 <number>0</number>
1648 </property>
1649 <property name="topMargin">
1650 <number>0</number>
1651 </property>
1652 <property name="rightMargin">
1653 <number>0</number>
1654 </property>
1655 <property name="bottomMargin">
1656 <number>0</number>
1657 </property>
1658 <item>
1659 <spacer name="horizontalSpacerShoulderButtons2">
1660 <property name="orientation">
1661 <enum>Qt::Horizontal</enum>
1662 </property>
1663 <property name="sizeHint" stdset="0">
1664 <size>
1665 <width>0</width>
1666 <height>20</height>
1667 </size>
1668 </property>
1669 </spacer>
1670 </item>
1671 </layout>
1672 </widget>
1673 </item>
1674 <item>
1675 <widget class="QWidget" name="buttonShoulderButtonsRight" native="true">
1676 <layout class="QVBoxLayout" name="buttonShoulderButtonsRightVerticalLayout">
1677 <property name="spacing">
1678 <number>0</number>
1679 </property>
1680 <property name="leftMargin">
1681 <number>0</number>
1682 </property>
1683 <property name="topMargin">
1684 <number>0</number>
1685 </property>
1686 <property name="rightMargin">
1687 <number>0</number>
1688 </property>
1689 <property name="bottomMargin">
1690 <number>0</number>
1691 </property>
1692 <item>
1693 <widget class="QGroupBox" name="buttonShoulderButtonsRGroup">
1694 <property name="title">
1695 <string>R</string>
1696 </property>
1697 <property name="alignment">
1698 <set>Qt::AlignCenter</set>
1699 </property>
1700 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
1701 <property name="spacing">
1702 <number>3</number>
1703 </property>
1704 <property name="leftMargin">
1705 <number>3</number>
1706 </property>
1707 <property name="topMargin">
1708 <number>3</number>
1709 </property>
1710 <property name="rightMargin">
1711 <number>3</number>
1712 </property>
1713 <property name="bottomMargin">
1714 <number>3</number>
1715 </property>
1716 <item>
1717 <widget class="QPushButton" name="buttonR">
1718 <property name="minimumSize">
1719 <size>
1720 <width>57</width>
1721 <height>0</height>
1722 </size>
1723 </property>
1724 <property name="maximumSize">
1725 <size>
1726 <width>55</width>
1727 <height>16777215</height>
1728 </size>
1729 </property>
1730 <property name="styleSheet">
1731 <string notr="true">min-width: 55px;</string>
1732 </property>
1733 <property name="text">
1734 <string>R</string>
1735 </property>
1736 </widget>
1737 </item>
1738 </layout>
1739 </widget>
1740 </item>
1741 <item>
1742 <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup">
1743 <property name="title">
1744 <string>ZR</string>
1745 </property>
1746 <property name="alignment">
1747 <set>Qt::AlignCenter</set>
1748 </property>
1749 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
1750 <property name="spacing">
1751 <number>3</number>
1752 </property>
1753 <property name="leftMargin">
1754 <number>3</number>
1755 </property>
1756 <property name="topMargin">
1757 <number>3</number>
1758 </property>
1759 <property name="rightMargin">
1760 <number>3</number>
1761 </property>
1762 <property name="bottomMargin">
1763 <number>3</number>
1764 </property>
1765 <item>
1766 <widget class="QPushButton" name="buttonZR">
1767 <property name="minimumSize">
1768 <size>
1769 <width>57</width>
1770 <height>0</height>
1771 </size>
1772 </property>
1773 <property name="maximumSize">
1774 <size>
1775 <width>55</width>
1776 <height>16777215</height>
1777 </size>
1778 </property>
1779 <property name="styleSheet">
1780 <string notr="true">min-width: 55px;</string>
1781 </property>
1782 <property name="text">
1783 <string>ZR</string>
1784 </property>
1785 </widget>
1786 </item>
1787 </layout>
1788 </widget>
1789 </item>
1790 </layout>
1791 </widget>
1792 </item>
1793 <item>
1794 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget2" native="true">
1795 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget2Layout">
1796 <property name="spacing">
1797 <number>0</number>
1798 </property>
1799 <property name="leftMargin">
1800 <number>0</number>
1801 </property>
1802 <property name="topMargin">
1803 <number>0</number>
1804 </property>
1805 <property name="rightMargin">
1806 <number>0</number>
1807 </property>
1808 <property name="bottomMargin">
1809 <number>0</number>
1810 </property>
1811 <item>
1812 <spacer name="horizontalSpacerShoulderButtons3">
1813 <property name="orientation">
1814 <enum>Qt::Horizontal</enum>
1815 </property>
1816 <property name="sizeHint" stdset="0">
1817 <size>
1818 <width>0</width>
1819 <height>20</height>
1820 </size>
1821 </property>
1822 </spacer>
1823 </item>
1824 </layout>
1825 </widget>
1826 </item>
1827 <item>
1828 <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true">
1829 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout">
1830 <property name="spacing">
1831 <number>0</number>
1832 </property>
1833 <property name="leftMargin">
1834 <number>0</number>
1835 </property>
1836 <property name="topMargin">
1837 <number>0</number>
1838 </property>
1839 <property name="rightMargin">
1840 <number>0</number>
1841 </property>
1842 <property name="bottomMargin">
1843 <number>0</number>
1844 </property>
1845 <item alignment="Qt::AlignHCenter">
1846 <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup">
1847 <property name="title">
1848 <string>SL</string>
1849 </property>
1850 <property name="alignment">
1851 <set>Qt::AlignCenter</set>
1852 </property>
1853 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
1854 <property name="spacing">
1855 <number>3</number>
1856 </property>
1857 <property name="leftMargin">
1858 <number>3</number>
1859 </property>
1860 <property name="topMargin">
1861 <number>3</number>
1862 </property>
1863 <property name="rightMargin">
1864 <number>3</number>
1865 </property>
1866 <property name="bottomMargin">
1867 <number>3</number>
1868 </property>
1869 <item>
1870 <widget class="QPushButton" name="buttonSL">
1871 <property name="minimumSize">
1872 <size>
1873 <width>57</width>
1874 <height>0</height>
1875 </size>
1876 </property>
1877 <property name="maximumSize">
1878 <size>
1879 <width>55</width>
1880 <height>16777215</height>
1881 </size>
1882 </property>
1883 <property name="styleSheet">
1884 <string notr="true">min-width: 55px;</string>
1885 </property>
1886 <property name="text">
1887 <string>SL</string>
1888 </property>
1889 </widget>
1890 </item>
1891 </layout>
1892 </widget>
1893 </item>
1894 <item alignment="Qt::AlignHCenter">
1895 <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup">
1896 <property name="title">
1897 <string>SR</string>
1898 </property>
1899 <property name="alignment">
1900 <set>Qt::AlignCenter</set>
1901 </property>
1902 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
1903 <property name="spacing">
1904 <number>3</number>
1905 </property>
1906 <property name="leftMargin">
1907 <number>3</number>
1908 </property>
1909 <property name="topMargin">
1910 <number>3</number>
1911 </property>
1912 <property name="rightMargin">
1913 <number>3</number>
1914 </property>
1915 <property name="bottomMargin">
1916 <number>3</number>
1917 </property>
1918 <item>
1919 <widget class="QPushButton" name="buttonSR">
1920 <property name="minimumSize">
1921 <size>
1922 <width>57</width>
1923 <height>0</height>
1924 </size>
1925 </property>
1926 <property name="maximumSize">
1927 <size>
1928 <width>55</width>
1929 <height>16777215</height>
1930 </size>
1931 </property>
1932 <property name="styleSheet">
1933 <string notr="true">min-width: 55px;</string>
1934 </property>
1935 <property name="text">
1936 <string>SR</string>
1937 </property>
1938 </widget>
1939 </item>
1940 </layout>
1941 </widget>
1942 </item>
1943 </layout>
1944 </widget>
1945 </item>
1946 </layout>
1947 </item>
1948 <item>
1949 <widget class="QFrame" name="controllerFrame">
1950 <property name="sizePolicy">
1951 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1952 <horstretch>0</horstretch>
1953 <verstretch>0</verstretch>
1954 </sizepolicy>
1955 </property>
1956 <property name="font">
1957 <font>
1958 <weight>75</weight>
1959 <bold>true</bold>
1960 </font>
1961 </property>
1962 <property name="styleSheet">
1963 <string notr="true">image: url(:/controller/pro);</string>
1964 </property>
1965 <layout class="QVBoxLayout" name="verticalLayout_4">
1966 <property name="leftMargin">
1967 <number>0</number>
772 </property> 1968 </property>
773 </widget> 1969 <property name="topMargin">
774 </item> 1970 <number>0</number>
775 </layout>
776 </item>
777 <item>
778 <widget class="QPushButton" name="buttonLStick">
779 <property name="text">
780 <string/>
781 </property>
782 </widget>
783 </item>
784 </layout>
785 </item>
786 <item row="5" column="1" colspan="2">
787 <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout">
788 <property name="sizeConstraint">
789 <enum>QLayout::SetDefaultConstraint</enum>
790 </property>
791 <item>
792 <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout">
793 <item>
794 <widget class="QLabel" name="labelLStickDeadzoneAndModifier">
795 <property name="text">
796 <string>Deadzone: 0</string>
797 </property> 1971 </property>
798 <property name="alignment"> 1972 <property name="rightMargin">
799 <enum>Qt::AlignHCenter</enum> 1973 <number>0</number>
800 </property> 1974 </property>
801 </widget> 1975 <property name="bottomMargin">
802 </item> 1976 <number>0</number>
803 </layout>
804 </item>
805 <item>
806 <widget class="QSlider" name="sliderLStickDeadzoneAndModifier">
807 <property name="orientation">
808 <enum>Qt::Horizontal</enum>
809 </property>
810 </widget>
811 </item>
812 </layout>
813 </item>
814 <item row="6" column="1">
815 <spacer name="LStick_verticalSpacer">
816 <property name="orientation">
817 <enum>Qt::Vertical</enum>
818 </property>
819 <property name="sizeHint" stdset="0">
820 <size>
821 <width>0</width>
822 <height>0</height>
823 </size>
824 </property>
825 </spacer>
826 </item>
827 </layout>
828 </widget>
829 </item>
830 <item row="3" column="0">
831 <widget class="QGroupBox" name="shoulderButtons">
832 <property name="title">
833 <string>Shoulder Buttons</string>
834 </property>
835 <property name="flat">
836 <bool>false</bool>
837 </property>
838 <property name="checkable">
839 <bool>false</bool>
840 </property>
841 <layout class="QGridLayout" name="gridLayout_3">
842 <item row="0" column="0">
843 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
844 <item>
845 <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
846 <item>
847 <widget class="QLabel" name="labelL">
848 <property name="text">
849 <string>L:</string>
850 </property> 1977 </property>
851 </widget> 1978 </layout>
852 </item> 1979 </widget>
853 </layout> 1980 </item>
854 </item> 1981 <item>
855 <item> 1982 <layout class="QHBoxLayout" name="miscButtons">
856 <widget class="QPushButton" name="buttonL"> 1983 <property name="spacing">
857 <property name="text"> 1984 <number>3</number>
858 <string/> 1985 </property>
859 </property> 1986 <item>
860 </widget> 1987 <spacer name="horizontalSpacerMiscButtons1">
861 </item> 1988 <property name="orientation">
862 </layout> 1989 <enum>Qt::Horizontal</enum>
1990 </property>
1991 <property name="sizeHint" stdset="0">
1992 <size>
1993 <width>40</width>
1994 <height>0</height>
1995 </size>
1996 </property>
1997 </spacer>
1998 </item>
1999 <item>
2000 <spacer name="horizontalSpacerMiscButtons4">
2001 <property name="orientation">
2002 <enum>Qt::Horizontal</enum>
2003 </property>
2004 <property name="sizeHint" stdset="0">
2005 <size>
2006 <width>40</width>
2007 <height>0</height>
2008 </size>
2009 </property>
2010 </spacer>
2011 </item>
2012 </layout>
2013 </item>
2014 </layout>
2015 </widget>
863 </item> 2016 </item>
864 <item row="0" column="1"> 2017 <item>
865 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> 2018 <widget class="QWidget" name="bottomRight" native="true">
866 <item> 2019 <layout class="QVBoxLayout" name="bottomRightLayout">
867 <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> 2020 <property name="spacing">
868 <item> 2021 <number>0</number>
869 <widget class="QLabel" name="labelR"> 2022 </property>
870 <property name="text"> 2023 <property name="leftMargin">
871 <string>R:</string> 2024 <number>0</number>
2025 </property>
2026 <property name="topMargin">
2027 <number>0</number>
2028 </property>
2029 <property name="rightMargin">
2030 <number>0</number>
2031 </property>
2032 <property name="bottomMargin">
2033 <number>0</number>
2034 </property>
2035 <item>
2036 <widget class="QGroupBox" name="faceButtons">
2037 <property name="sizePolicy">
2038 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2039 <horstretch>0</horstretch>
2040 <verstretch>0</verstretch>
2041 </sizepolicy>
2042 </property>
2043 <property name="title">
2044 <string>Face Buttons</string>
2045 </property>
2046 <property name="flat">
2047 <bool>false</bool>
2048 </property>
2049 <property name="checkable">
2050 <bool>false</bool>
2051 </property>
2052 <layout class="QVBoxLayout" name="verticalLayout">
2053 <property name="spacing">
2054 <number>0</number>
872 </property> 2055 </property>
873 </widget> 2056 <property name="leftMargin">
874 </item> 2057 <number>3</number>
875 </layout>
876 </item>
877 <item>
878 <widget class="QPushButton" name="buttonR">
879 <property name="text">
880 <string/>
881 </property>
882 </widget>
883 </item>
884 </layout>
885 </item>
886 <item row="1" column="0">
887 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
888 <item>
889 <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
890 <item>
891 <widget class="QLabel" name="labelZL">
892 <property name="text">
893 <string>ZL:</string>
894 </property> 2058 </property>
895 </widget> 2059 <property name="topMargin">
896 </item> 2060 <number>0</number>
897 </layout>
898 </item>
899 <item>
900 <widget class="QPushButton" name="buttonZL">
901 <property name="text">
902 <string/>
903 </property>
904 </widget>
905 </item>
906 </layout>
907 </item>
908 <item row="1" column="1">
909 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
910 <item>
911 <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
912 <item>
913 <widget class="QLabel" name="labelZR">
914 <property name="text">
915 <string>ZR:</string>
916 </property> 2061 </property>
917 </widget> 2062 <property name="rightMargin">
918 </item> 2063 <number>3</number>
919 </layout>
920 </item>
921 <item>
922 <widget class="QPushButton" name="buttonZR">
923 <property name="text">
924 <string/>
925 </property>
926 </widget>
927 </item>
928 </layout>
929 </item>
930 <item row="0" column="2">
931 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
932 <item>
933 <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
934 <item>
935 <widget class="QLabel" name="labelSL">
936 <property name="text">
937 <string>SL:</string>
938 </property> 2064 </property>
939 </widget> 2065 <property name="bottomMargin">
940 </item> 2066 <number>3</number>
941 </layout>
942 </item>
943 <item>
944 <widget class="QPushButton" name="buttonSL">
945 <property name="text">
946 <string/>
947 </property>
948 </widget>
949 </item>
950 </layout>
951 </item>
952 <item row="1" column="2">
953 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
954 <item>
955 <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
956 <item>
957 <widget class="QLabel" name="labelSR">
958 <property name="text">
959 <string>SR:</string>
960 </property> 2067 </property>
961 </widget> 2068 <item>
962 </item> 2069 <widget class="QWidget" name="buttonFaceButtonsBWidget" native="true">
963 </layout> 2070 <layout class="QHBoxLayout" name="horizontalLayout_6">
964 </item> 2071 <property name="spacing">
965 <item> 2072 <number>0</number>
966 <widget class="QPushButton" name="buttonSR"> 2073 </property>
967 <property name="text"> 2074 <property name="leftMargin">
968 <string/> 2075 <number>0</number>
969 </property> 2076 </property>
970 </widget> 2077 <property name="topMargin">
971 </item> 2078 <number>0</number>
972 </layout> 2079 </property>
973 </item> 2080 <property name="rightMargin">
974 </layout> 2081 <number>0</number>
975 </widget> 2082 </property>
976 </item> 2083 <property name="bottomMargin">
977 <item row="3" column="1"> 2084 <number>0</number>
978 <widget class="QGroupBox" name="misc"> 2085 </property>
979 <property name="title"> 2086 <item>
980 <string>Misc.</string> 2087 <spacer name="horizontalSpacerBLeft">
981 </property> 2088 <property name="orientation">
982 <property name="flat"> 2089 <enum>Qt::Horizontal</enum>
983 <bool>false</bool> 2090 </property>
984 </property> 2091 <property name="sizeHint" stdset="0">
985 <property name="checkable"> 2092 <size>
986 <bool>false</bool> 2093 <width>20</width>
987 </property> 2094 <height>20</height>
988 <layout class="QGridLayout" name="gridLayout_6"> 2095 </size>
989 <item row="1" column="0"> 2096 </property>
990 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> 2097 </spacer>
991 <item> 2098 </item>
992 <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout"> 2099 <item alignment="Qt::AlignHCenter">
993 <item> 2100 <widget class="QGroupBox" name="buttonFaceButtonsXGroup">
994 <widget class="QLabel" name="labelMinus"> 2101 <property name="title">
995 <property name="text"> 2102 <string>X</string>
996 <string>Minus:</string> 2103 </property>
2104 <property name="alignment">
2105 <set>Qt::AlignCenter</set>
2106 </property>
2107 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
2108 <property name="spacing">
2109 <number>3</number>
2110 </property>
2111 <property name="leftMargin">
2112 <number>3</number>
2113 </property>
2114 <property name="topMargin">
2115 <number>3</number>
2116 </property>
2117 <property name="rightMargin">
2118 <number>3</number>
2119 </property>
2120 <property name="bottomMargin">
2121 <number>3</number>
2122 </property>
2123 <item>
2124 <widget class="QPushButton" name="buttonX">
2125 <property name="minimumSize">
2126 <size>
2127 <width>57</width>
2128 <height>0</height>
2129 </size>
2130 </property>
2131 <property name="maximumSize">
2132 <size>
2133 <width>55</width>
2134 <height>16777215</height>
2135 </size>
2136 </property>
2137 <property name="styleSheet">
2138 <string notr="true">min-width: 55px;</string>
2139 </property>
2140 <property name="text">
2141 <string>X</string>
2142 </property>
2143 </widget>
2144 </item>
2145 </layout>
2146 </widget>
2147 </item>
2148 <item>
2149 <spacer name="horizontalSpacerBRight">
2150 <property name="orientation">
2151 <enum>Qt::Horizontal</enum>
2152 </property>
2153 <property name="sizeHint" stdset="0">
2154 <size>
2155 <width>20</width>
2156 <height>20</height>
2157 </size>
2158 </property>
2159 </spacer>
2160 </item>
2161 </layout>
2162 </widget>
2163 </item>
2164 <item>
2165 <layout class="QHBoxLayout" name="buttonFaceButtonsYAHorizontalLayout">
2166 <property name="spacing">
2167 <number>3</number>
2168 </property>
2169 <item alignment="Qt::AlignHCenter">
2170 <widget class="QGroupBox" name="buttonFaceButtonsYGroup">
2171 <property name="title">
2172 <string>Y</string>
2173 </property>
2174 <property name="alignment">
2175 <set>Qt::AlignCenter</set>
2176 </property>
2177 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
2178 <property name="spacing">
2179 <number>3</number>
2180 </property>
2181 <property name="leftMargin">
2182 <number>3</number>
2183 </property>
2184 <property name="topMargin">
2185 <number>3</number>
2186 </property>
2187 <property name="rightMargin">
2188 <number>3</number>
2189 </property>
2190 <property name="bottomMargin">
2191 <number>3</number>
2192 </property>
2193 <item>
2194 <widget class="QPushButton" name="buttonY">
2195 <property name="minimumSize">
2196 <size>
2197 <width>57</width>
2198 <height>0</height>
2199 </size>
2200 </property>
2201 <property name="maximumSize">
2202 <size>
2203 <width>55</width>
2204 <height>16777215</height>
2205 </size>
2206 </property>
2207 <property name="styleSheet">
2208 <string notr="true">min-width: 55px;</string>
2209 </property>
2210 <property name="text">
2211 <string>Y</string>
2212 </property>
2213 </widget>
2214 </item>
2215 </layout>
2216 </widget>
2217 </item>
2218 <item alignment="Qt::AlignHCenter">
2219 <widget class="QGroupBox" name="buttonFaceButtonsAGroup">
2220 <property name="title">
2221 <string>A</string>
2222 </property>
2223 <property name="alignment">
2224 <set>Qt::AlignCenter</set>
2225 </property>
2226 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
2227 <property name="spacing">
2228 <number>3</number>
2229 </property>
2230 <property name="leftMargin">
2231 <number>3</number>
2232 </property>
2233 <property name="topMargin">
2234 <number>3</number>
2235 </property>
2236 <property name="rightMargin">
2237 <number>3</number>
2238 </property>
2239 <property name="bottomMargin">
2240 <number>3</number>
2241 </property>
2242 <item>
2243 <widget class="QPushButton" name="buttonA">
2244 <property name="minimumSize">
2245 <size>
2246 <width>57</width>
2247 <height>0</height>
2248 </size>
2249 </property>
2250 <property name="maximumSize">
2251 <size>
2252 <width>55</width>
2253 <height>16777215</height>
2254 </size>
2255 </property>
2256 <property name="styleSheet">
2257 <string notr="true">min-width: 55px;</string>
2258 </property>
2259 <property name="text">
2260 <string>A</string>
2261 </property>
2262 </widget>
2263 </item>
2264 </layout>
2265 </widget>
2266 </item>
2267 </layout>
2268 </item>
2269 <item>
2270 <widget class="QWidget" name="buttonFaceButtonsXWidget" native="true">
2271 <layout class="QHBoxLayout" name="horizontalLayout_10">
2272 <property name="spacing">
2273 <number>0</number>
2274 </property>
2275 <property name="leftMargin">
2276 <number>0</number>
2277 </property>
2278 <property name="topMargin">
2279 <number>0</number>
2280 </property>
2281 <property name="rightMargin">
2282 <number>0</number>
2283 </property>
2284 <property name="bottomMargin">
2285 <number>0</number>
2286 </property>
2287 <item>
2288 <spacer name="horizontalSpacerXLeft">
2289 <property name="orientation">
2290 <enum>Qt::Horizontal</enum>
2291 </property>
2292 <property name="sizeHint" stdset="0">
2293 <size>
2294 <width>20</width>
2295 <height>20</height>
2296 </size>
2297 </property>
2298 </spacer>
2299 </item>
2300 <item alignment="Qt::AlignHCenter">
2301 <widget class="QGroupBox" name="buttonFaceButtonsBWidget_2">
2302 <property name="title">
2303 <string>B</string>
2304 </property>
2305 <property name="alignment">
2306 <set>Qt::AlignCenter</set>
2307 </property>
2308 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
2309 <property name="spacing">
2310 <number>3</number>
2311 </property>
2312 <property name="leftMargin">
2313 <number>3</number>
2314 </property>
2315 <property name="topMargin">
2316 <number>3</number>
2317 </property>
2318 <property name="rightMargin">
2319 <number>3</number>
2320 </property>
2321 <property name="bottomMargin">
2322 <number>3</number>
2323 </property>
2324 <item>
2325 <widget class="QPushButton" name="buttonB">
2326 <property name="minimumSize">
2327 <size>
2328 <width>57</width>
2329 <height>0</height>
2330 </size>
2331 </property>
2332 <property name="maximumSize">
2333 <size>
2334 <width>55</width>
2335 <height>16777215</height>
2336 </size>
2337 </property>
2338 <property name="styleSheet">
2339 <string notr="true">min-width: 55px;</string>
2340 </property>
2341 <property name="text">
2342 <string>B</string>
2343 </property>
2344 </widget>
2345 </item>
2346 </layout>
2347 </widget>
2348 </item>
2349 <item>
2350 <spacer name="horizontalSpacerXRight">
2351 <property name="orientation">
2352 <enum>Qt::Horizontal</enum>
2353 </property>
2354 <property name="sizeHint" stdset="0">
2355 <size>
2356 <width>20</width>
2357 <height>20</height>
2358 </size>
2359 </property>
2360 </spacer>
2361 </item>
2362 </layout>
2363 </widget>
2364 </item>
2365 </layout>
2366 </widget>
2367 </item>
2368 <item>
2369 <spacer name="verticalSpacerBottomRight">
2370 <property name="orientation">
2371 <enum>Qt::Vertical</enum>
2372 </property>
2373 <property name="sizeHint" stdset="0">
2374 <size>
2375 <width>20</width>
2376 <height>0</height>
2377 </size>
2378 </property>
2379 </spacer>
2380 </item>
2381 <item>
2382 <widget class="QGroupBox" name="RStick">
2383 <property name="sizePolicy">
2384 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2385 <horstretch>0</horstretch>
2386 <verstretch>0</verstretch>
2387 </sizepolicy>
2388 </property>
2389 <property name="title">
2390 <string>Right Stick</string>
2391 </property>
2392 <property name="alignment">
2393 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
2394 </property>
2395 <property name="flat">
2396 <bool>false</bool>
2397 </property>
2398 <property name="checkable">
2399 <bool>false</bool>
2400 </property>
2401 <layout class="QVBoxLayout" name="verticalLayout_2">
2402 <property name="spacing">
2403 <number>0</number>
997 </property> 2404 </property>
998 </widget> 2405 <property name="leftMargin">
999 </item> 2406 <number>3</number>
1000 </layout>
1001 </item>
1002 <item>
1003 <widget class="QPushButton" name="buttonMinus">
1004 <property name="text">
1005 <string/>
1006 </property>
1007 </widget>
1008 </item>
1009 </layout>
1010 </item>
1011 <item row="3" column="1">
1012 <spacer name="verticalSpacer_2">
1013 <property name="orientation">
1014 <enum>Qt::Vertical</enum>
1015 </property>
1016 <property name="sizeHint" stdset="0">
1017 <size>
1018 <width>20</width>
1019 <height>40</height>
1020 </size>
1021 </property>
1022 </spacer>
1023 </item>
1024 <item row="0" column="0">
1025 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
1026 <item>
1027 <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
1028 <item>
1029 <widget class="QLabel" name="labelPlus">
1030 <property name="text">
1031 <string>Plus:</string>
1032 </property> 2407 </property>
1033 </widget> 2408 <property name="topMargin">
1034 </item> 2409 <number>0</number>
1035 </layout>
1036 </item>
1037 <item>
1038 <widget class="QPushButton" name="buttonPlus">
1039 <property name="text">
1040 <string/>
1041 </property>
1042 </widget>
1043 </item>
1044 </layout>
1045 </item>
1046 <item row="0" column="1">
1047 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
1048 <item>
1049 <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
1050 <item>
1051 <widget class="QLabel" name="labelHome">
1052 <property name="text">
1053 <string>Home:</string>
1054 </property> 2410 </property>
1055 </widget> 2411 <property name="rightMargin">
1056 </item> 2412 <number>3</number>
1057 </layout>
1058 </item>
1059 <item>
1060 <widget class="QPushButton" name="buttonHome">
1061 <property name="text">
1062 <string/>
1063 </property>
1064 </widget>
1065 </item>
1066 </layout>
1067 </item>
1068 <item row="1" column="1">
1069 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
1070 <item>
1071 <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
1072 <item>
1073 <widget class="QLabel" name="labelScreenshot">
1074 <property name="text">
1075 <string>Screen Capture:</string>
1076 </property> 2413 </property>
1077 <property name="wordWrap"> 2414 <property name="bottomMargin">
1078 <bool>false</bool> 2415 <number>0</number>
1079 </property> 2416 </property>
1080 </widget> 2417 <item>
1081 </item> 2418 <widget class="QWidget" name="buttonRStickUpWidget" native="true">
1082 </layout> 2419 <property name="minimumSize">
1083 </item> 2420 <size>
1084 <item> 2421 <width>0</width>
1085 <widget class="QPushButton" name="buttonScreenshot"> 2422 <height>0</height>
1086 <property name="sizePolicy"> 2423 </size>
1087 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 2424 </property>
1088 <horstretch>0</horstretch> 2425 <layout class="QHBoxLayout" name="horizontalLayout_9">
1089 <verstretch>0</verstretch> 2426 <property name="spacing">
1090 </sizepolicy> 2427 <number>0</number>
1091 </property> 2428 </property>
1092 <property name="maximumSize"> 2429 <property name="leftMargin">
1093 <size> 2430 <number>0</number>
1094 <width>80</width> 2431 </property>
1095 <height>16777215</height> 2432 <property name="topMargin">
1096 </size> 2433 <number>0</number>
1097 </property> 2434 </property>
1098 <property name="text"> 2435 <property name="rightMargin">
1099 <string/> 2436 <number>0</number>
1100 </property> 2437 </property>
1101 </widget> 2438 <property name="bottomMargin">
1102 </item> 2439 <number>0</number>
1103 </layout> 2440 </property>
2441 <item>
2442 <spacer name="horizontalSpacerRStickUpLeft">
2443 <property name="orientation">
2444 <enum>Qt::Horizontal</enum>
2445 </property>
2446 <property name="sizeHint" stdset="0">
2447 <size>
2448 <width>20</width>
2449 <height>20</height>
2450 </size>
2451 </property>
2452 </spacer>
2453 </item>
2454 <item alignment="Qt::AlignHCenter">
2455 <widget class="QGroupBox" name="buttonRStickUpGroup">
2456 <property name="title">
2457 <string>Up</string>
2458 </property>
2459 <property name="alignment">
2460 <set>Qt::AlignCenter</set>
2461 </property>
2462 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
2463 <property name="spacing">
2464 <number>3</number>
2465 </property>
2466 <property name="leftMargin">
2467 <number>3</number>
2468 </property>
2469 <property name="topMargin">
2470 <number>3</number>
2471 </property>
2472 <property name="rightMargin">
2473 <number>3</number>
2474 </property>
2475 <property name="bottomMargin">
2476 <number>3</number>
2477 </property>
2478 <item>
2479 <widget class="QPushButton" name="buttonRStickUp">
2480 <property name="minimumSize">
2481 <size>
2482 <width>57</width>
2483 <height>0</height>
2484 </size>
2485 </property>
2486 <property name="maximumSize">
2487 <size>
2488 <width>55</width>
2489 <height>16777215</height>
2490 </size>
2491 </property>
2492 <property name="styleSheet">
2493 <string notr="true">min-width: 55px;</string>
2494 </property>
2495 <property name="text">
2496 <string>Up</string>
2497 </property>
2498 </widget>
2499 </item>
2500 </layout>
2501 </widget>
2502 </item>
2503 <item>
2504 <spacer name="horizontalSpacerRStickUpRight">
2505 <property name="orientation">
2506 <enum>Qt::Horizontal</enum>
2507 </property>
2508 <property name="sizeHint" stdset="0">
2509 <size>
2510 <width>20</width>
2511 <height>20</height>
2512 </size>
2513 </property>
2514 </spacer>
2515 </item>
2516 </layout>
2517 </widget>
2518 </item>
2519 <item>
2520 <layout class="QHBoxLayout" name="buttonRStickLeftRightHorizontalLayout">
2521 <property name="spacing">
2522 <number>3</number>
2523 </property>
2524 <item alignment="Qt::AlignHCenter">
2525 <widget class="QGroupBox" name="buttonRStickLeftGroup">
2526 <property name="title">
2527 <string>Left</string>
2528 </property>
2529 <property name="alignment">
2530 <set>Qt::AlignCenter</set>
2531 </property>
2532 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
2533 <property name="spacing">
2534 <number>3</number>
2535 </property>
2536 <property name="leftMargin">
2537 <number>3</number>
2538 </property>
2539 <property name="topMargin">
2540 <number>3</number>
2541 </property>
2542 <property name="rightMargin">
2543 <number>3</number>
2544 </property>
2545 <property name="bottomMargin">
2546 <number>3</number>
2547 </property>
2548 <item>
2549 <widget class="QPushButton" name="buttonRStickLeft">
2550 <property name="minimumSize">
2551 <size>
2552 <width>57</width>
2553 <height>0</height>
2554 </size>
2555 </property>
2556 <property name="maximumSize">
2557 <size>
2558 <width>55</width>
2559 <height>16777215</height>
2560 </size>
2561 </property>
2562 <property name="styleSheet">
2563 <string notr="true">min-width: 55px;</string>
2564 </property>
2565 <property name="text">
2566 <string>Left</string>
2567 </property>
2568 </widget>
2569 </item>
2570 </layout>
2571 </widget>
2572 </item>
2573 <item alignment="Qt::AlignHCenter">
2574 <widget class="QGroupBox" name="buttonRStickRightGroup">
2575 <property name="title">
2576 <string>Right</string>
2577 </property>
2578 <property name="alignment">
2579 <set>Qt::AlignCenter</set>
2580 </property>
2581 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
2582 <property name="spacing">
2583 <number>3</number>
2584 </property>
2585 <property name="leftMargin">
2586 <number>3</number>
2587 </property>
2588 <property name="topMargin">
2589 <number>3</number>
2590 </property>
2591 <property name="rightMargin">
2592 <number>3</number>
2593 </property>
2594 <property name="bottomMargin">
2595 <number>3</number>
2596 </property>
2597 <item>
2598 <widget class="QPushButton" name="buttonRStickRight">
2599 <property name="minimumSize">
2600 <size>
2601 <width>57</width>
2602 <height>0</height>
2603 </size>
2604 </property>
2605 <property name="maximumSize">
2606 <size>
2607 <width>55</width>
2608 <height>16777215</height>
2609 </size>
2610 </property>
2611 <property name="styleSheet">
2612 <string notr="true">min-width: 55px;</string>
2613 </property>
2614 <property name="text">
2615 <string>Right</string>
2616 </property>
2617 </widget>
2618 </item>
2619 </layout>
2620 </widget>
2621 </item>
2622 </layout>
2623 </item>
2624 <item>
2625 <widget class="QWidget" name="buttonRStickDownWidget" native="true">
2626 <layout class="QHBoxLayout" name="horizontalLayout_11">
2627 <property name="spacing">
2628 <number>0</number>
2629 </property>
2630 <property name="leftMargin">
2631 <number>0</number>
2632 </property>
2633 <property name="topMargin">
2634 <number>0</number>
2635 </property>
2636 <property name="rightMargin">
2637 <number>0</number>
2638 </property>
2639 <property name="bottomMargin">
2640 <number>0</number>
2641 </property>
2642 <item>
2643 <spacer name="horizontalSpacerRStickDownLeft">
2644 <property name="orientation">
2645 <enum>Qt::Horizontal</enum>
2646 </property>
2647 <property name="sizeHint" stdset="0">
2648 <size>
2649 <width>20</width>
2650 <height>20</height>
2651 </size>
2652 </property>
2653 </spacer>
2654 </item>
2655 <item>
2656 <widget class="QGroupBox" name="buttonRStickDownGroup">
2657 <property name="title">
2658 <string>Down</string>
2659 </property>
2660 <property name="alignment">
2661 <set>Qt::AlignCenter</set>
2662 </property>
2663 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
2664 <property name="spacing">
2665 <number>3</number>
2666 </property>
2667 <property name="leftMargin">
2668 <number>3</number>
2669 </property>
2670 <property name="topMargin">
2671 <number>3</number>
2672 </property>
2673 <property name="rightMargin">
2674 <number>3</number>
2675 </property>
2676 <property name="bottomMargin">
2677 <number>3</number>
2678 </property>
2679 <item>
2680 <widget class="QPushButton" name="buttonRStickDown">
2681 <property name="minimumSize">
2682 <size>
2683 <width>57</width>
2684 <height>0</height>
2685 </size>
2686 </property>
2687 <property name="maximumSize">
2688 <size>
2689 <width>55</width>
2690 <height>16777215</height>
2691 </size>
2692 </property>
2693 <property name="styleSheet">
2694 <string notr="true">min-width: 55px;</string>
2695 </property>
2696 <property name="text">
2697 <string>Down</string>
2698 </property>
2699 </widget>
2700 </item>
2701 </layout>
2702 </widget>
2703 </item>
2704 <item>
2705 <spacer name="horizontalSpacerRStickDownRight">
2706 <property name="orientation">
2707 <enum>Qt::Horizontal</enum>
2708 </property>
2709 <property name="sizeHint" stdset="0">
2710 <size>
2711 <width>20</width>
2712 <height>20</height>
2713 </size>
2714 </property>
2715 </spacer>
2716 </item>
2717 </layout>
2718 </widget>
2719 </item>
2720 <item>
2721 <layout class="QHBoxLayout" name="buttonRStickPressedModifierHorizontalLayout">
2722 <property name="spacing">
2723 <number>3</number>
2724 </property>
2725 <item alignment="Qt::AlignHCenter">
2726 <widget class="QGroupBox" name="groupRStickPressed">
2727 <property name="title">
2728 <string>Pressed</string>
2729 </property>
2730 <property name="alignment">
2731 <set>Qt::AlignCenter</set>
2732 </property>
2733 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
2734 <property name="spacing">
2735 <number>3</number>
2736 </property>
2737 <property name="leftMargin">
2738 <number>3</number>
2739 </property>
2740 <property name="topMargin">
2741 <number>3</number>
2742 </property>
2743 <property name="rightMargin">
2744 <number>3</number>
2745 </property>
2746 <property name="bottomMargin">
2747 <number>3</number>
2748 </property>
2749 <item>
2750 <widget class="QPushButton" name="buttonRStick">
2751 <property name="minimumSize">
2752 <size>
2753 <width>57</width>
2754 <height>0</height>
2755 </size>
2756 </property>
2757 <property name="maximumSize">
2758 <size>
2759 <width>55</width>
2760 <height>16777215</height>
2761 </size>
2762 </property>
2763 <property name="styleSheet">
2764 <string notr="true">min-width: 55px;</string>
2765 </property>
2766 <property name="text">
2767 <string>Pressed</string>
2768 </property>
2769 </widget>
2770 </item>
2771 </layout>
2772 </widget>
2773 </item>
2774 <item alignment="Qt::AlignHCenter">
2775 <widget class="QGroupBox" name="buttonRStickModGroup">
2776 <property name="title">
2777 <string>Modifier</string>
2778 </property>
2779 <property name="alignment">
2780 <set>Qt::AlignCenter</set>
2781 </property>
2782 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
2783 <property name="spacing">
2784 <number>3</number>
2785 </property>
2786 <property name="leftMargin">
2787 <number>3</number>
2788 </property>
2789 <property name="topMargin">
2790 <number>3</number>
2791 </property>
2792 <property name="rightMargin">
2793 <number>3</number>
2794 </property>
2795 <property name="bottomMargin">
2796 <number>3</number>
2797 </property>
2798 <item>
2799 <widget class="QPushButton" name="buttonRStickMod">
2800 <property name="minimumSize">
2801 <size>
2802 <width>57</width>
2803 <height>0</height>
2804 </size>
2805 </property>
2806 <property name="maximumSize">
2807 <size>
2808 <width>55</width>
2809 <height>16777215</height>
2810 </size>
2811 </property>
2812 <property name="styleSheet">
2813 <string notr="true">min-width: 55px;</string>
2814 </property>
2815 <property name="text">
2816 <string>Modifier</string>
2817 </property>
2818 </widget>
2819 </item>
2820 </layout>
2821 </widget>
2822 </item>
2823 <item>
2824 <widget class="QGroupBox" name="buttonRStickRangeGroup">
2825 <property name="title">
2826 <string>Range</string>
2827 </property>
2828 <layout class="QHBoxLayout" name="buttonRStickRangeGroupHorizontalLayout">
2829 <property name="spacing">
2830 <number>3</number>
2831 </property>
2832 <property name="leftMargin">
2833 <number>3</number>
2834 </property>
2835 <property name="topMargin">
2836 <number>3</number>
2837 </property>
2838 <property name="rightMargin">
2839 <number>3</number>
2840 </property>
2841 <property name="bottomMargin">
2842 <number>3</number>
2843 </property>
2844 <item>
2845 <widget class="QSpinBox" name="spinboxRStickRange">
2846 <property name="minimumSize">
2847 <size>
2848 <width>55</width>
2849 <height>21</height>
2850 </size>
2851 </property>
2852 <property name="maximumSize">
2853 <size>
2854 <width>55</width>
2855 <height>16777215</height>
2856 </size>
2857 </property>
2858 <property name="suffix">
2859 <string>%</string>
2860 </property>
2861 <property name="minimum">
2862 <number>50</number>
2863 </property>
2864 <property name="maximum">
2865 <number>150</number>
2866 </property>
2867 <property name="value">
2868 <number>100</number>
2869 </property>
2870 </widget>
2871 </item>
2872 </layout>
2873 </widget>
2874 </item>
2875 </layout>
2876 </item>
2877 <item>
2878 <layout class="QVBoxLayout" name="sliderRStickDeadzoneModifierRangeVerticalLayout">
2879 <property name="spacing">
2880 <number>3</number>
2881 </property>
2882 <property name="leftMargin">
2883 <number>0</number>
2884 </property>
2885 <property name="topMargin">
2886 <number>2</number>
2887 </property>
2888 <property name="rightMargin">
2889 <number>0</number>
2890 </property>
2891 <property name="bottomMargin">
2892 <number>3</number>
2893 </property>
2894 <item>
2895 <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout">
2896 <item>
2897 <widget class="QLabel" name="labelRStickDeadzone">
2898 <property name="text">
2899 <string>Deadzone: 0%</string>
2900 </property>
2901 <property name="alignment">
2902 <set>Qt::AlignHCenter</set>
2903 </property>
2904 </widget>
2905 </item>
2906 </layout>
2907 </item>
2908 <item>
2909 <widget class="QSlider" name="sliderRStickDeadzone">
2910 <property name="maximum">
2911 <number>100</number>
2912 </property>
2913 <property name="orientation">
2914 <enum>Qt::Horizontal</enum>
2915 </property>
2916 </widget>
2917 </item>
2918 <item>
2919 <layout class="QHBoxLayout" name="sliderRStickModifierRangeHorizontalLayout">
2920 <item>
2921 <widget class="QLabel" name="labelRStickModifierRange">
2922 <property name="text">
2923 <string>Modifier Range: 0%</string>
2924 </property>
2925 <property name="alignment">
2926 <set>Qt::AlignHCenter</set>
2927 </property>
2928 </widget>
2929 </item>
2930 </layout>
2931 </item>
2932 <item>
2933 <widget class="QSlider" name="sliderRStickModifierRange">
2934 <property name="maximum">
2935 <number>100</number>
2936 </property>
2937 <property name="orientation">
2938 <enum>Qt::Horizontal</enum>
2939 </property>
2940 </widget>
2941 </item>
2942 </layout>
2943 </item>
2944 </layout>
2945 </widget>
2946 </item>
2947 <item>
2948 <spacer name="verticalSpacerBottomRight_2">
2949 <property name="orientation">
2950 <enum>Qt::Vertical</enum>
2951 </property>
2952 <property name="sizeHint" stdset="0">
2953 <size>
2954 <width>20</width>
2955 <height>0</height>
2956 </size>
2957 </property>
2958 </spacer>
2959 </item>
2960 </layout>
2961 </widget>
1104 </item> 2962 </item>
1105 </layout> 2963 </layout>
1106 </widget> 2964 </widget>
1107 </item> 2965 </item>
1108 </layout> 2966 </layout>
1109 </item> 2967 </item>
1110 <item>
1111 <spacer name="verticalSpacer">
1112 <property name="orientation">
1113 <enum>Qt::Vertical</enum>
1114 </property>
1115 <property name="sizeHint" stdset="0">
1116 <size>
1117 <width>20</width>
1118 <height>40</height>
1119 </size>
1120 </property>
1121 </spacer>
1122 </item>
1123 <item>
1124 <layout class="QHBoxLayout" name="horizontalLayout"/>
1125 </item>
1126 <item>
1127 <layout class="QHBoxLayout" name="horizontalLayout_2">
1128 <item>
1129 <widget class="QPushButton" name="buttonClearAll">
1130 <property name="sizePolicy">
1131 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1132 <horstretch>0</horstretch>
1133 <verstretch>0</verstretch>
1134 </sizepolicy>
1135 </property>
1136 <property name="sizeIncrement">
1137 <size>
1138 <width>0</width>
1139 <height>0</height>
1140 </size>
1141 </property>
1142 <property name="baseSize">
1143 <size>
1144 <width>0</width>
1145 <height>0</height>
1146 </size>
1147 </property>
1148 <property name="layoutDirection">
1149 <enum>Qt::LeftToRight</enum>
1150 </property>
1151 <property name="text">
1152 <string>Clear All</string>
1153 </property>
1154 </widget>
1155 </item>
1156 <item>
1157 <widget class="QPushButton" name="buttonRestoreDefaults">
1158 <property name="sizePolicy">
1159 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1160 <horstretch>0</horstretch>
1161 <verstretch>0</verstretch>
1162 </sizepolicy>
1163 </property>
1164 <property name="sizeIncrement">
1165 <size>
1166 <width>0</width>
1167 <height>0</height>
1168 </size>
1169 </property>
1170 <property name="baseSize">
1171 <size>
1172 <width>0</width>
1173 <height>0</height>
1174 </size>
1175 </property>
1176 <property name="layoutDirection">
1177 <enum>Qt::LeftToRight</enum>
1178 </property>
1179 <property name="text">
1180 <string>Restore Defaults</string>
1181 </property>
1182 </widget>
1183 </item>
1184 <item>
1185 <spacer name="horizontalSpacer">
1186 <property name="orientation">
1187 <enum>Qt::Horizontal</enum>
1188 </property>
1189 <property name="sizeHint" stdset="0">
1190 <size>
1191 <width>40</width>
1192 <height>20</height>
1193 </size>
1194 </property>
1195 </spacer>
1196 </item>
1197 <item>
1198 <widget class="QDialogButtonBox" name="buttonBox">
1199 <property name="standardButtons">
1200 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
1201 </property>
1202 </widget>
1203 </item>
1204 </layout>
1205 </item>
1206 </layout> 2968 </layout>
1207 </widget> 2969 </widget>
1208 <resources/> 2970 <resources>
1209 <connections> 2971 <include location="../../../dist/icons/controller/controller.qrc"/>
1210 <connection> 2972 </resources>
1211 <sender>buttonBox</sender> 2973 <connections/>
1212 <signal>accepted()</signal>
1213 <receiver>ConfigureInputPlayer</receiver>
1214 <slot>accept()</slot>
1215 <hints>
1216 <hint type="sourcelabel">
1217 <x>371</x>
1218 <y>730</y>
1219 </hint>
1220 <hint type="destinationlabel">
1221 <x>229</x>
1222 <y>375</y>
1223 </hint>
1224 </hints>
1225 </connection>
1226 <connection>
1227 <sender>buttonBox</sender>
1228 <signal>rejected()</signal>
1229 <receiver>ConfigureInputPlayer</receiver>
1230 <slot>reject()</slot>
1231 <hints>
1232 <hint type="sourcelabel">
1233 <x>371</x>
1234 <y>730</y>
1235 </hint>
1236 <hint type="destinationlabel">
1237 <x>229</x>
1238 <y>375</y>
1239 </hint>
1240 </hints>
1241 </connection>
1242 </connections>
1243</ui> 2974</ui>
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp
deleted file mode 100644
index 0e0e8f113..000000000
--- a/src/yuzu/configuration/configure_input_simple.cpp
+++ /dev/null
@@ -1,152 +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 <array>
6#include <tuple>
7
8#include "ui_configure_input_simple.h"
9#include "yuzu/configuration/configure_input.h"
10#include "yuzu/configuration/configure_input_player.h"
11#include "yuzu/configuration/configure_input_simple.h"
12#include "yuzu/uisettings.h"
13
14namespace {
15
16template <typename Dialog, typename... Args>
17void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
18 caller->ApplyConfiguration();
19 Dialog dialog(caller, std::forward<Args>(args)...);
20
21 const auto res = dialog.exec();
22 if (res == QDialog::Accepted) {
23 dialog.ApplyConfiguration();
24 }
25}
26
27// OnProfileSelect functions should (when applicable):
28// - Set controller types
29// - Set controller enabled
30// - Set docked mode
31// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
32//
33// OnProfileSelect function should NOT however:
34// - Reset any button mappings
35// - Open any dialogs
36// - Block in any way
37
38constexpr std::size_t PLAYER_0_INDEX = 0;
39constexpr std::size_t HANDHELD_INDEX = 8;
40
41void HandheldOnProfileSelect() {
42 Settings::values.players[HANDHELD_INDEX].connected = true;
43 Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
44
45 for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
46 Settings::values.players[player].connected = false;
47 }
48
49 Settings::values.use_docked_mode = false;
50 Settings::values.keyboard_enabled = false;
51 Settings::values.mouse_enabled = false;
52 Settings::values.debug_pad_enabled = false;
53 Settings::values.touchscreen.enabled = true;
54}
55
56void DualJoyconsDockedOnProfileSelect() {
57 Settings::values.players[PLAYER_0_INDEX].connected = true;
58 Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon;
59
60 for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
61 Settings::values.players[player].connected = false;
62 }
63
64 Settings::values.use_docked_mode = true;
65 Settings::values.keyboard_enabled = false;
66 Settings::values.mouse_enabled = false;
67 Settings::values.debug_pad_enabled = false;
68 Settings::values.touchscreen.enabled = true;
69}
70
71// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
72// is clicked)
73using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
74
75constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
76 {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
77 [](ConfigureInputSimple* caller) {
78 CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
79 }},
80 {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
81 [](ConfigureInputSimple* caller) {
82 CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false);
83 }},
84 {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
85}};
86
87} // namespace
88
89void ApplyInputProfileConfiguration(int profile_index) {
90 std::get<1>(
91 INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
92}
93
94ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
95 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
96 ui->setupUi(this);
97
98 for (const auto& profile : INPUT_PROFILES) {
99 const QString label = tr(std::get<0>(profile));
100 ui->profile_combobox->addItem(label, label);
101 }
102
103 connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
104 &ConfigureInputSimple::OnSelectProfile);
105 connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure);
106
107 LoadConfiguration();
108}
109
110ConfigureInputSimple::~ConfigureInputSimple() = default;
111
112void ConfigureInputSimple::ApplyConfiguration() {
113 auto index = ui->profile_combobox->currentIndex();
114 // Make the stored index for "Custom" very large so that if new profiles are added it
115 // doesn't change.
116 if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
117 index = std::numeric_limits<int>::max();
118 }
119
120 UISettings::values.profile_index = index;
121}
122
123void ConfigureInputSimple::changeEvent(QEvent* event) {
124 if (event->type() == QEvent::LanguageChange) {
125 RetranslateUI();
126 }
127
128 QWidget::changeEvent(event);
129}
130
131void ConfigureInputSimple::RetranslateUI() {
132 ui->retranslateUi(this);
133}
134
135void ConfigureInputSimple::LoadConfiguration() {
136 const auto index = UISettings::values.profile_index;
137 if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
138 ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
139 } else {
140 ui->profile_combobox->setCurrentIndex(index);
141 }
142}
143
144void ConfigureInputSimple::OnSelectProfile(int index) {
145 const auto old_docked = Settings::values.use_docked_mode;
146 ApplyInputProfileConfiguration(index);
147 OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
148}
149
150void ConfigureInputSimple::OnConfigure() {
151 std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
152}
diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h
deleted file mode 100644
index bb5050224..000000000
--- a/src/yuzu/configuration/configure_input_simple.h
+++ /dev/null
@@ -1,43 +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
9#include <QWidget>
10
11class QPushButton;
12class QString;
13class QTimer;
14
15namespace Ui {
16class ConfigureInputSimple;
17}
18
19// Used by configuration loader to apply a profile if the input is invalid.
20void ApplyInputProfileConfiguration(int profile_index);
21
22class ConfigureInputSimple : public QWidget {
23 Q_OBJECT
24
25public:
26 explicit ConfigureInputSimple(QWidget* parent = nullptr);
27 ~ConfigureInputSimple() override;
28
29 /// Save all button configurations to settings file
30 void ApplyConfiguration();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35
36 /// Load configuration settings.
37 void LoadConfiguration();
38
39 void OnSelectProfile(int index);
40 void OnConfigure();
41
42 std::unique_ptr<Ui::ConfigureInputSimple> ui;
43};
diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui
deleted file mode 100644
index c4889caa9..000000000
--- a/src/yuzu/configuration/configure_input_simple.ui
+++ /dev/null
@@ -1,97 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputSimple</class>
4 <widget class="QWidget" name="ConfigureInputSimple">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>473</width>
10 <height>685</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigureInputSimple</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item>
20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title">
22 <string>Profile</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="2">
26 <widget class="QPushButton" name="profile_configure">
27 <property name="text">
28 <string>Configure</string>
29 </property>
30 </widget>
31 </item>
32 <item row="1" column="0">
33 <spacer name="horizontalSpacer">
34 <property name="orientation">
35 <enum>Qt::Horizontal</enum>
36 </property>
37 <property name="sizeHint" stdset="0">
38 <size>
39 <width>40</width>
40 <height>20</height>
41 </size>
42 </property>
43 </spacer>
44 </item>
45 <item row="1" column="3">
46 <spacer name="horizontalSpacer_2">
47 <property name="orientation">
48 <enum>Qt::Horizontal</enum>
49 </property>
50 <property name="sizeHint" stdset="0">
51 <size>
52 <width>40</width>
53 <height>20</height>
54 </size>
55 </property>
56 </spacer>
57 </item>
58 <item row="1" column="1">
59 <widget class="QComboBox" name="profile_combobox">
60 <property name="minimumSize">
61 <size>
62 <width>250</width>
63 <height>0</height>
64 </size>
65 </property>
66 </widget>
67 </item>
68 <item row="0" column="1" colspan="2">
69 <widget class="QLabel" name="label">
70 <property name="text">
71 <string>Choose a controller configuration:</string>
72 </property>
73 </widget>
74 </item>
75 </layout>
76 </widget>
77 </item>
78 </layout>
79 </item>
80 <item>
81 <spacer name="verticalSpacer">
82 <property name="orientation">
83 <enum>Qt::Vertical</enum>
84 </property>
85 <property name="sizeHint" stdset="0">
86 <size>
87 <width>20</width>
88 <height>40</height>
89 </size>
90 </property>
91 </spacer>
92 </item>
93 </layout>
94 </widget>
95 <resources/>
96 <connections/>
97</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index 5bcf5ffa8..2af3afda8 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -18,6 +18,16 @@
18 18
19static QString GetKeyName(int key_code) { 19static QString GetKeyName(int key_code) {
20 switch (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");
21 case Qt::Key_Shift: 31 case Qt::Key_Shift:
22 return QObject::tr("Shift"); 32 return QObject::tr("Shift");
23 case Qt::Key_Control: 33 case Qt::Key_Control:
@@ -66,8 +76,10 @@ static QString ButtonToText(const Common::ParamPackage& param) {
66 return QObject::tr("[unknown]"); 76 return QObject::tr("[unknown]");
67} 77}
68 78
69ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) 79ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
70 : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), 80 InputCommon::InputSubsystem* input_subsystem_)
81 : QDialog(parent),
82 ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
71 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { 83 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
72 ui->setupUi(this); 84 ui->setupUi(this);
73 setFocusPolicy(Qt::ClickFocus); 85 setFocusPolicy(Qt::ClickFocus);
@@ -188,9 +200,9 @@ void ConfigureMouseAdvanced::HandleClick(
188 button->setText(tr("[press key]")); 200 button->setText(tr("[press key]"));
189 button->setFocus(); 201 button->setFocus();
190 202
191 // Keyboard keys can only be used as button devices 203 // Keyboard keys or mouse buttons can only be used as button devices
192 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; 204 want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
193 if (want_keyboard_keys) { 205 if (want_keyboard_mouse) {
194 const auto iter = std::find(button_map.begin(), button_map.end(), button); 206 const auto iter = std::find(button_map.begin(), button_map.end(), button);
195 ASSERT(iter != button_map.end()); 207 ASSERT(iter != button_map.end());
196 const auto index = std::distance(button_map.begin(), iter); 208 const auto index = std::distance(button_map.begin(), iter);
@@ -199,27 +211,29 @@ void ConfigureMouseAdvanced::HandleClick(
199 211
200 input_setter = new_input_setter; 212 input_setter = new_input_setter;
201 213
202 device_pollers = InputCommon::Polling::GetPollers(type); 214 device_pollers = input_subsystem->GetPollers(type);
203 215
204 for (auto& poller : device_pollers) { 216 for (auto& poller : device_pollers) {
205 poller->Start(); 217 poller->Start();
206 } 218 }
207 219
208 grabKeyboard(); 220 QWidget::grabMouse();
209 grabMouse(); 221 QWidget::grabKeyboard();
210 timeout_timer->start(5000); // Cancel after 5 seconds 222
211 poll_timer->start(200); // Check for new inputs every 200ms 223 timeout_timer->start(2500); // Cancel after 2.5 seconds
224 poll_timer->start(50); // Check for new inputs every 50ms
212} 225}
213 226
214void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) { 227void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
215 releaseKeyboard();
216 releaseMouse();
217 timeout_timer->stop(); 228 timeout_timer->stop();
218 poll_timer->stop(); 229 poll_timer->stop();
219 for (auto& poller : device_pollers) { 230 for (auto& poller : device_pollers) {
220 poller->Stop(); 231 poller->Stop();
221 } 232 }
222 233
234 QWidget::releaseMouse();
235 QWidget::releaseKeyboard();
236
223 if (!abort) { 237 if (!abort) {
224 (*input_setter)(params); 238 (*input_setter)(params);
225 } 239 }
@@ -228,13 +242,29 @@ void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params
228 input_setter = std::nullopt; 242 input_setter = std::nullopt;
229} 243}
230 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
231void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { 261void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
232 if (!input_setter || !event) { 262 if (!input_setter || !event) {
233 return; 263 return;
234 } 264 }
235 265
236 if (event->key() != Qt::Key_Escape) { 266 if (event->key() != Qt::Key_Escape) {
237 if (want_keyboard_keys) { 267 if (want_keyboard_mouse) {
238 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 268 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
239 false); 269 false);
240 } else { 270 } else {
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
index 342b82412..65b6fca9a 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -8,12 +8,14 @@
8#include <optional> 8#include <optional>
9#include <QDialog> 9#include <QDialog>
10 10
11#include "core/settings.h"
12
13class QCheckBox; 11class QCheckBox;
14class QPushButton; 12class QPushButton;
15class QTimer; 13class QTimer;
16 14
15namespace InputCommon {
16class InputSubsystem;
17}
18
17namespace Ui { 19namespace Ui {
18class ConfigureMouseAdvanced; 20class ConfigureMouseAdvanced;
19} 21}
@@ -22,7 +24,7 @@ class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT 24 Q_OBJECT
23 25
24public: 26public:
25 explicit ConfigureMouseAdvanced(QWidget* parent); 27 explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
26 ~ConfigureMouseAdvanced() override; 28 ~ConfigureMouseAdvanced() override;
27 29
28 void ApplyConfiguration(); 30 void ApplyConfiguration();
@@ -49,11 +51,16 @@ private:
49 /// Finish polling and configure input using the input_setter 51 /// Finish polling and configure input using the input_setter
50 void SetPollingResult(const Common::ParamPackage& params, bool abort); 52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
51 53
54 /// Handle mouse button press events.
55 void mousePressEvent(QMouseEvent* event) override;
56
52 /// Handle key press events. 57 /// Handle key press events.
53 void keyPressEvent(QKeyEvent* event) override; 58 void keyPressEvent(QKeyEvent* event) override;
54 59
55 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; 60 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
56 61
62 InputCommon::InputSubsystem* input_subsystem;
63
57 /// This will be the the setting function when an input is awaiting configuration. 64 /// This will be the the setting function when an input is awaiting configuration.
58 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 65 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
59 66
@@ -67,5 +74,5 @@ private:
67 74
68 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 75 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
69 /// keyboard events are ignored. 76 /// keyboard events are ignored.
70 bool want_keyboard_keys = false; 77 bool want_keyboard_mouse = false;
71}; 78};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
index 08245ecf0..74552fdbd 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -6,13 +6,18 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>250</width> 9 <width>310</width>
10 <height>261</height> 10 <height>193</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Mouse</string> 14 <string>Configure Mouse</string>
15 </property> 15 </property>
16 <property name="styleSheet">
17 <string notr="true">QPushButton {
18 min-width: 55px;
19}</string>
20 </property>
16 <layout class="QVBoxLayout" name="verticalLayout"> 21 <layout class="QVBoxLayout" name="verticalLayout">
17 <item> 22 <item>
18 <widget class="QGroupBox" name="gridGroupBox"> 23 <widget class="QGroupBox" name="gridGroupBox">
@@ -20,81 +25,33 @@
20 <string>Mouse Buttons</string> 25 <string>Mouse Buttons</string>
21 </property> 26 </property>
22 <layout class="QGridLayout" name="gridLayout"> 27 <layout class="QGridLayout" name="gridLayout">
23 <item row="0" column="4"> 28 <item row="3" column="5">
24 <spacer name="horizontalSpacer_2"> 29 <layout class="QVBoxLayout" name="verticalLayout_6">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeType">
29 <enum>QSizePolicy::Fixed</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>20</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item row="0" column="3">
40 <layout class="QVBoxLayout" name="verticalLayout_4">
41 <item> 30 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout_3"> 31 <layout class="QHBoxLayout" name="horizontalLayout_5">
43 <item> 32 <item>
44 <widget class="QLabel" name="label_3"> 33 <widget class="QLabel" name="label_5">
45 <property name="text"> 34 <property name="text">
46 <string>Right:</string> 35 <string>Forward:</string>
47 </property> 36 </property>
48 </widget> 37 </widget>
49 </item> 38 </item>
50 </layout> 39 </layout>
51 </item> 40 </item>
52 <item> 41 <item>
53 <widget class="QPushButton" name="right_button"> 42 <widget class="QPushButton" name="forward_button">
54 <property name="minimumSize"> 43 <property name="minimumSize">
55 <size> 44 <size>
56 <width>75</width> 45 <width>57</width>
57 <height>0</height> 46 <height>0</height>
58 </size> 47 </size>
59 </property> 48 </property>
60 <property name="text"> 49 <property name="maximumSize">
61 <string/> 50 <size>
51 <width>16777215</width>
52 <height>16777215</height>
53 </size>
62 </property> 54 </property>
63 </widget>
64 </item>
65 </layout>
66 </item>
67 <item row="0" column="0">
68 <spacer name="horizontalSpacer">
69 <property name="orientation">
70 <enum>Qt::Horizontal</enum>
71 </property>
72 <property name="sizeType">
73 <enum>QSizePolicy::Fixed</enum>
74 </property>
75 <property name="sizeHint" stdset="0">
76 <size>
77 <width>20</width>
78 <height>20</height>
79 </size>
80 </property>
81 </spacer>
82 </item>
83 <item row="2" column="1">
84 <layout class="QVBoxLayout" name="verticalLayout_3">
85 <item>
86 <layout class="QHBoxLayout" name="horizontalLayout_2">
87 <item>
88 <widget class="QLabel" name="label_2">
89 <property name="text">
90 <string>Middle:</string>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item>
97 <widget class="QPushButton" name="middle_button">
98 <property name="text"> 55 <property name="text">
99 <string/> 56 <string/>
100 </property> 57 </property>
@@ -123,6 +80,12 @@
123 </item> 80 </item>
124 <item> 81 <item>
125 <widget class="QPushButton" name="back_button"> 82 <widget class="QPushButton" name="back_button">
83 <property name="minimumSize">
84 <size>
85 <width>57</width>
86 <height>0</height>
87 </size>
88 </property>
126 <property name="text"> 89 <property name="text">
127 <string/> 90 <string/>
128 </property> 91 </property>
@@ -147,7 +110,7 @@
147 <widget class="QPushButton" name="left_button"> 110 <widget class="QPushButton" name="left_button">
148 <property name="minimumSize"> 111 <property name="minimumSize">
149 <size> 112 <size>
150 <width>75</width> 113 <width>57</width>
151 <height>0</height> 114 <height>0</height>
152 </size> 115 </size>
153 </property> 116 </property>
@@ -158,21 +121,99 @@
158 </item> 121 </item>
159 </layout> 122 </layout>
160 </item> 123 </item>
161 <item row="3" column="3"> 124 <item row="0" column="3">
162 <layout class="QVBoxLayout" name="verticalLayout_6"> 125 <layout class="QVBoxLayout" name="verticalLayout_3">
163 <item> 126 <item>
164 <layout class="QHBoxLayout" name="horizontalLayout_5"> 127 <layout class="QHBoxLayout" name="horizontalLayout_2">
165 <item> 128 <item>
166 <widget class="QLabel" name="label_5"> 129 <widget class="QLabel" name="label_2">
167 <property name="text"> 130 <property name="text">
168 <string>Forward:</string> 131 <string>Middle:</string>
169 </property> 132 </property>
170 </widget> 133 </widget>
171 </item> 134 </item>
172 </layout> 135 </layout>
173 </item> 136 </item>
174 <item> 137 <item>
175 <widget class="QPushButton" name="forward_button"> 138 <widget class="QPushButton" name="middle_button">
139 <property name="minimumSize">
140 <size>
141 <width>57</width>
142 <height>0</height>
143 </size>
144 </property>
145 <property name="maximumSize">
146 <size>
147 <width>16777215</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>57</width>
208 <height>0</height>
209 </size>
210 </property>
211 <property name="maximumSize">
212 <size>
213 <width>16777215</width>
214 <height>16777215</height>
215 </size>
216 </property>
176 <property name="text"> 217 <property name="text">
177 <string/> 218 <string/>
178 </property> 219 </property>
@@ -180,6 +221,32 @@
180 </item> 221 </item>
181 </layout> 222 </layout>
182 </item> 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>
183 </layout> 250 </layout>
184 </widget> 251 </widget>
185 </item> 252 </item>
@@ -187,15 +254,39 @@
187 <layout class="QHBoxLayout" name="horizontalLayout_6"> 254 <layout class="QHBoxLayout" name="horizontalLayout_6">
188 <item> 255 <item>
189 <widget class="QPushButton" name="buttonClearAll"> 256 <widget class="QPushButton" name="buttonClearAll">
257 <property name="minimumSize">
258 <size>
259 <width>57</width>
260 <height>0</height>
261 </size>
262 </property>
263 <property name="maximumSize">
264 <size>
265 <width>16777215</width>
266 <height>16777215</height>
267 </size>
268 </property>
190 <property name="text"> 269 <property name="text">
191 <string>Clear All</string> 270 <string>Clear</string>
192 </property> 271 </property>
193 </widget> 272 </widget>
194 </item> 273 </item>
195 <item> 274 <item>
196 <widget class="QPushButton" name="buttonRestoreDefaults"> 275 <widget class="QPushButton" name="buttonRestoreDefaults">
276 <property name="minimumSize">
277 <size>
278 <width>57</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="maximumSize">
283 <size>
284 <width>16777215</width>
285 <height>16777215</height>
286 </size>
287 </property>
197 <property name="text"> 288 <property name="text">
198 <string>Restore Defaults</string> 289 <string>Defaults</string>
199 </property> 290 </property>
200 </widget> 291 </widget>
201 </item> 292 </item>
@@ -206,21 +297,24 @@
206 </property> 297 </property>
207 <property name="sizeHint" stdset="0"> 298 <property name="sizeHint" stdset="0">
208 <size> 299 <size>
209 <width>40</width> 300 <width>0</width>
210 <height>20</height> 301 <height>20</height>
211 </size> 302 </size>
212 </property> 303 </property>
213 </spacer> 304 </spacer>
214 </item> 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>
215 </layout> 316 </layout>
216 </item> 317 </item>
217 <item>
218 <widget class="QDialogButtonBox" name="buttonBox">
219 <property name="standardButtons">
220 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
221 </property>
222 </widget>
223 </item>
224 </layout> 318 </layout>
225 </widget> 319 </widget>
226 <resources/> 320 <resources/>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index cd7e78eb4..a1b61d119 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -94,6 +94,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
94#include "core/perf_stats.h" 94#include "core/perf_stats.h"
95#include "core/settings.h" 95#include "core/settings.h"
96#include "core/telemetry_session.h" 96#include "core/telemetry_session.h"
97#include "input_common/main.h"
97#include "video_core/gpu.h" 98#include "video_core/gpu.h"
98#include "video_core/shader_notify.h" 99#include "video_core/shader_notify.h"
99#include "yuzu/about_dialog.h" 100#include "yuzu/about_dialog.h"
@@ -186,9 +187,9 @@ static void InitializeLogging() {
186} 187}
187 188
188GMainWindow::GMainWindow() 189GMainWindow::GMainWindow()
189 : config(new Config()), emu_thread(nullptr), 190 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
190 vfs(std::make_shared<FileSys::RealVfsFilesystem>()), 191 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
191 provider(std::make_unique<FileSys::ManualContentProvider>()) { 192 provider{std::make_unique<FileSys::ManualContentProvider>()} {
192 InitializeLogging(); 193 InitializeLogging();
193 194
194 LoadTranslation(); 195 LoadTranslation();
@@ -473,7 +474,7 @@ void GMainWindow::InitializeWidgets() {
473#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING 474#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
474 ui.action_Report_Compatibility->setVisible(true); 475 ui.action_Report_Compatibility->setVisible(true);
475#endif 476#endif
476 render_window = new GRenderWindow(this, emu_thread.get()); 477 render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem);
477 render_window->hide(); 478 render_window->hide();
478 479
479 game_list = new GameList(vfs, provider.get(), this); 480 game_list = new GameList(vfs, provider.get(), this);
@@ -2213,7 +2214,7 @@ void GMainWindow::OnConfigure() {
2213 const auto old_theme = UISettings::values.theme; 2214 const auto old_theme = UISettings::values.theme;
2214 const bool old_discord_presence = UISettings::values.enable_discord_presence; 2215 const bool old_discord_presence = UISettings::values.enable_discord_presence;
2215 2216
2216 ConfigureDialog configure_dialog(this, hotkey_registry); 2217 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
2217 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, 2218 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
2218 &GMainWindow::OnLanguageChanged); 2219 &GMainWindow::OnLanguageChanged);
2219 2220
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 01f9131e5..0ce66a1ca 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -40,12 +40,20 @@ namespace Core::Frontend {
40struct SoftwareKeyboardParameters; 40struct SoftwareKeyboardParameters;
41} // namespace Core::Frontend 41} // namespace Core::Frontend
42 42
43namespace DiscordRPC {
44class DiscordInterface;
45}
46
43namespace FileSys { 47namespace FileSys {
44class ContentProvider; 48class ContentProvider;
45class ManualContentProvider; 49class ManualContentProvider;
46class VfsFilesystem; 50class VfsFilesystem;
47} // namespace FileSys 51} // namespace FileSys
48 52
53namespace InputCommon {
54class InputSubsystem;
55}
56
49enum class EmulatedDirectoryTarget { 57enum class EmulatedDirectoryTarget {
50 NAND, 58 NAND,
51 SDMC, 59 SDMC,
@@ -62,10 +70,6 @@ enum class ReinitializeKeyBehavior {
62 Warning, 70 Warning,
63}; 71};
64 72
65namespace DiscordRPC {
66class DiscordInterface;
67}
68
69class GMainWindow : public QMainWindow { 73class GMainWindow : public QMainWindow {
70 Q_OBJECT 74 Q_OBJECT
71 75
@@ -86,8 +90,6 @@ public:
86 GMainWindow(); 90 GMainWindow();
87 ~GMainWindow() override; 91 ~GMainWindow() override;
88 92
89 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
90
91 bool DropAction(QDropEvent* event); 93 bool DropAction(QDropEvent* event);
92 void AcceptDropEvent(QDropEvent* event); 94 void AcceptDropEvent(QDropEvent* event);
93 95
@@ -255,6 +257,9 @@ private:
255 257
256 Ui::MainWindow ui; 258 Ui::MainWindow ui;
257 259
260 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
261 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
262
258 GRenderWindow* render_window; 263 GRenderWindow* render_window;
259 GameList* game_list; 264 GameList* game_list;
260 LoadingScreen* loading_screen; 265 LoadingScreen* loading_screen;
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index a51175f36..37499fc85 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -16,4 +16,5 @@ const Themes themes{{
16}}; 16}};
17 17
18Values values = {}; 18Values values = {};
19
19} // namespace UISettings 20} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 2d2e82f15..ce3945485 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -87,9 +87,6 @@ struct Values {
87 // logging 87 // logging
88 bool show_console; 88 bool show_console;
89 89
90 // Controllers
91 int profile_index;
92
93 // Game List 90 // Game List
94 bool show_add_ons; 91 bool show_add_ons;
95 uint32_t icon_size; 92 uint32_t icon_size;
@@ -100,6 +97,7 @@ struct Values {
100}; 97};
101 98
102extern Values values; 99extern Values values;
100
103} // namespace UISettings 101} // namespace UISettings
104 102
105Q_DECLARE_METATYPE(UISettings::GameDir*); 103Q_DECLARE_METATYPE(UISettings::GameDir*);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 8a63fd191..e9f1c6500 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -288,6 +288,8 @@ void Config::ReadValues() {
288 Settings::values.debug_pad_analogs[i] = default_param; 288 Settings::values.debug_pad_analogs[i] = default_param;
289 } 289 }
290 290
291 Settings::values.vibration_enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
291 Settings::values.touchscreen.enabled = 293 Settings::values.touchscreen.enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 294 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
293 Settings::values.touchscreen.device = 295 Settings::values.touchscreen.device =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e5e684206..a804d5185 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,23 +13,25 @@
13#include "input_common/sdl/sdl.h" 13#include "input_common/sdl/sdl.h"
14#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
15 15
16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} { 16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen,
17 InputCommon::InputSubsystem* input_subsystem_)
18 : system{system}, input_subsystem{input_subsystem_} {
17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 19 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 20 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
19 exit(1); 21 exit(1);
20 } 22 }
21 InputCommon::Init(); 23 input_subsystem->Initialize();
22 SDL_SetMainReady(); 24 SDL_SetMainReady();
23} 25}
24 26
25EmuWindow_SDL2::~EmuWindow_SDL2() { 27EmuWindow_SDL2::~EmuWindow_SDL2() {
26 InputCommon::Shutdown(); 28 input_subsystem->Shutdown();
27 SDL_Quit(); 29 SDL_Quit();
28} 30}
29 31
30void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 32void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
31 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 33 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
32 InputCommon::GetMotionEmu()->Tilt(x, y); 34 input_subsystem->GetMotionEmu()->Tilt(x, y);
33} 35}
34 36
35void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 37void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -41,9 +43,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
41 } 43 }
42 } else if (button == SDL_BUTTON_RIGHT) { 44 } else if (button == SDL_BUTTON_RIGHT) {
43 if (state == SDL_PRESSED) { 45 if (state == SDL_PRESSED) {
44 InputCommon::GetMotionEmu()->BeginTilt(x, y); 46 input_subsystem->GetMotionEmu()->BeginTilt(x, y);
45 } else { 47 } else {
46 InputCommon::GetMotionEmu()->EndTilt(); 48 input_subsystem->GetMotionEmu()->EndTilt();
47 } 49 }
48 } 50 }
49} 51}
@@ -79,9 +81,9 @@ void EmuWindow_SDL2::OnFingerUp() {
79 81
80void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 82void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
81 if (state == SDL_PRESSED) { 83 if (state == SDL_PRESSED) {
82 InputCommon::GetKeyboard()->PressKey(key); 84 input_subsystem->GetKeyboard()->PressKey(key);
83 } else if (state == SDL_RELEASED) { 85 } else if (state == SDL_RELEASED) {
84 InputCommon::GetKeyboard()->ReleaseKey(key); 86 input_subsystem->GetKeyboard()->ReleaseKey(key);
85 } 87 }
86} 88}
87 89
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index fffac4252..82750ffec 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -14,9 +14,14 @@ namespace Core {
14class System; 14class System;
15} 15}
16 16
17namespace InputCommon {
18class InputSubsystem;
19}
20
17class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
18public: 22public:
19 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen); 23 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen,
24 InputCommon::InputSubsystem* input_subsystem);
20 ~EmuWindow_SDL2(); 25 ~EmuWindow_SDL2();
21 26
22 /// Polls window events 27 /// Polls window events
@@ -76,4 +81,7 @@ protected:
76 81
77 /// Keeps track of how often to update the title bar during gameplay 82 /// Keeps track of how often to update the title bar during gameplay
78 u32 last_time = 0; 83 u32 last_time = 0;
84
85 /// Input subsystem to use with this window.
86 InputCommon::InputSubsystem* input_subsystem;
79}; 87};
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 e78025737..881b67a76 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -87,8 +87,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
87 return unsupported_ext.empty(); 87 return unsupported_ext.empty();
88} 88}
89 89
90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen) 90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
91 : EmuWindow_SDL2{system, fullscreen} { 91 InputCommon::InputSubsystem* input_subsystem)
92 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 95 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 48bb41683..732a64edd 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -8,9 +8,14 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10 10
11namespace InputCommon {
12class InputSubsystem;
13}
14
11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
12public: 16public:
13 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); 17 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
18 InputCommon::InputSubsystem* input_subsystem);
14 ~EmuWindow_SDL2_GL(); 19 ~EmuWindow_SDL2_GL();
15 20
16 void Present() override; 21 void Present() override;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index cb8e68a39..53491f86e 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -19,8 +19,9 @@
19#include <SDL.h> 19#include <SDL.h>
20#include <SDL_syswm.h> 20#include <SDL_syswm.h>
21 21
22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) 22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
23 : EmuWindow_SDL2{system, fullscreen} { 23 InputCommon::InputSubsystem* input_subsystem)
24 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
24 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 25 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
25 Common::g_scm_branch, Common::g_scm_desc); 26 Common::g_scm_branch, Common::g_scm_desc);
26 render_window = 27 render_window =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index 77a6ca72b..f99704d4c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -13,9 +13,14 @@ namespace Core {
13class System; 13class System;
14} 14}
15 15
16namespace InputCommon {
17class InputSubsystem;
18}
19
16class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
17public: 21public:
18 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); 22 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
23 InputCommon::InputSubsystem* input_subsystem);
19 ~EmuWindow_SDL2_VK(); 24 ~EmuWindow_SDL2_VK();
20 25
21 void Present() override; 26 void Present() override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 8efe49390..4f00c804d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -23,12 +23,14 @@
23#include "common/telemetry.h" 23#include "common/telemetry.h"
24#include "core/core.h" 24#include "core/core.h"
25#include "core/crypto/key_manager.h" 25#include "core/crypto/key_manager.h"
26#include "core/file_sys/registered_cache.h"
26#include "core/file_sys/vfs_real.h" 27#include "core/file_sys/vfs_real.h"
27#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
28#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
29#include "core/loader/loader.h" 30#include "core/loader/loader.h"
30#include "core/settings.h" 31#include "core/settings.h"
31#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "input_common/main.h"
32#include "video_core/renderer_base.h" 34#include "video_core/renderer_base.h"
33#include "yuzu_cmd/config.h" 35#include "yuzu_cmd/config.h"
34#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 36#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
@@ -37,8 +39,6 @@
37#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 39#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
38#endif 40#endif
39 41
40#include "core/file_sys/registered_cache.h"
41
42#ifdef _WIN32 42#ifdef _WIN32
43// windows.h needs to be included before shellapi.h 43// windows.h needs to be included before shellapi.h
44#include <windows.h> 44#include <windows.h>
@@ -179,15 +179,16 @@ int main(int argc, char** argv) {
179 Settings::Apply(); 179 Settings::Apply();
180 180
181 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
182 InputCommon::InputSubsystem input_subsystem;
182 183
183 std::unique_ptr<EmuWindow_SDL2> emu_window; 184 std::unique_ptr<EmuWindow_SDL2> emu_window;
184 switch (Settings::values.renderer_backend.GetValue()) { 185 switch (Settings::values.renderer_backend.GetValue()) {
185 case Settings::RendererBackend::OpenGL: 186 case Settings::RendererBackend::OpenGL:
186 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); 187 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem);
187 break; 188 break;
188 case Settings::RendererBackend::Vulkan: 189 case Settings::RendererBackend::Vulkan:
189#ifdef HAS_VULKAN 190#ifdef HAS_VULKAN
190 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen); 191 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem);
191 break; 192 break;
192#else 193#else
193 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 194 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index 74022af23..aaf59129a 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -75,6 +75,7 @@ void Config::ReadValues() {
75 Settings::values.debug_pad_analogs[i] = ""; 75 Settings::values.debug_pad_analogs[i] = "";
76 } 76 }
77 77
78 Settings::values.vibration_enabled = true;
78 Settings::values.touchscreen.enabled = ""; 79 Settings::values.touchscreen.enabled = "";
79 Settings::values.touchscreen.device = ""; 80 Settings::values.touchscreen.device = "";
80 Settings::values.touchscreen.finger = 0; 81 Settings::values.touchscreen.finger = 0;
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index 8584f6671..78f75fb38 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -13,7 +13,6 @@
13 13
14#include <glad/glad.h> 14#include <glad/glad.h>
15 15
16#include "common/assert.h"
17#include "common/logging/log.h" 16#include "common/logging/log.h"
18#include "common/scm_rev.h" 17#include "common/scm_rev.h"
19#include "core/settings.h" 18#include "core/settings.h"
@@ -53,7 +52,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
53 exit(1); 52 exit(1);
54 } 53 }
55 54
56 InputCommon::Init(); 55 input_subsystem->Initialize();
57 56
58 SDL_SetMainReady(); 57 SDL_SetMainReady();
59 58
@@ -105,7 +104,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
105} 104}
106 105
107EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { 106EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
108 InputCommon::Shutdown(); 107 input_subsystem->Shutdown();
109 SDL_GL_DeleteContext(gl_context); 108 SDL_GL_DeleteContext(gl_context);
110 SDL_Quit(); 109 SDL_Quit();
111} 110}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index c13a82df2..a553b4b95 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -8,6 +8,10 @@
8 8
9struct SDL_Window; 9struct SDL_Window;
10 10
11namespace InputCommon {
12class InputSubsystem;
13}
14
11class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow { 15class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
12public: 16public:
13 explicit EmuWindow_SDL2_Hide(); 17 explicit EmuWindow_SDL2_Hide();
@@ -25,6 +29,8 @@ private:
25 /// Whether the GPU and driver supports the OpenGL extension required 29 /// Whether the GPU and driver supports the OpenGL extension required
26 bool SupportsRequiredGLExtensions(); 30 bool SupportsRequiredGLExtensions();
27 31
32 std::unique_ptr<InputCommon::InputSubsystem> input_subsystem;
33
28 /// Internal SDL2 render window 34 /// Internal SDL2 render window
29 SDL_Window* render_window; 35 SDL_Window* render_window;
30 36