summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/frontend/applets/controller.cpp81
-rw-r--r--src/core/frontend/applets/controller.h48
-rw-r--r--src/core/hle/service/am/applets/applets.cpp71
-rw-r--r--src/core/hle/service/am/applets/applets.h19
-rw-r--r--src/core/hle/service/am/applets/controller.cpp210
-rw-r--r--src/core/hle/service/am/applets/controller.h123
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp116
-rw-r--r--src/core/hle/service/hid/controllers/npad.h7
-rw-r--r--src/yuzu/CMakeLists.txt12
-rw-r--r--src/yuzu/applets/controller.cpp601
-rw-r--r--src/yuzu/applets/controller.h133
-rw-r--r--src/yuzu/applets/controller.ui2672
-rw-r--r--src/yuzu/configuration/configure_input.cpp22
-rw-r--r--src/yuzu/configuration/configure_input.h2
-rw-r--r--src/yuzu/configuration/configure_input_dialog.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_dialog.h38
-rw-r--r--src/yuzu/configuration/configure_input_dialog.ui57
-rw-r--r--src/yuzu/main.cpp39
-rw-r--r--src/yuzu/main.h6
20 files changed, 4150 insertions, 148 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b96ca9374..d0c405ec7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -126,6 +126,8 @@ add_library(core STATIC
126 file_sys/vfs_vector.h 126 file_sys/vfs_vector.h
127 file_sys/xts_archive.cpp 127 file_sys/xts_archive.cpp
128 file_sys/xts_archive.h 128 file_sys/xts_archive.h
129 frontend/applets/controller.cpp
130 frontend/applets/controller.h
129 frontend/applets/error.cpp 131 frontend/applets/error.cpp
130 frontend/applets/error.h 132 frontend/applets/error.h
131 frontend/applets/general_frontend.cpp 133 frontend/applets/general_frontend.cpp
@@ -244,6 +246,8 @@ add_library(core STATIC
244 hle/service/am/applet_oe.h 246 hle/service/am/applet_oe.h
245 hle/service/am/applets/applets.cpp 247 hle/service/am/applets/applets.cpp
246 hle/service/am/applets/applets.h 248 hle/service/am/applets/applets.h
249 hle/service/am/applets/controller.cpp
250 hle/service/am/applets/controller.h
247 hle/service/am/applets/error.cpp 251 hle/service/am/applets/error.cpp
248 hle/service/am/applets/error.h 252 hle/service/am/applets/error.h
249 hle/service/am/applets/general_backend.cpp 253 hle/service/am/applets/general_backend.cpp
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
new file mode 100644
index 000000000..4505da758
--- /dev/null
+++ b/src/core/frontend/applets/controller.cpp
@@ -0,0 +1,81 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/controller.h"
9#include "core/hle/service/hid/controllers/npad.h"
10#include "core/hle/service/hid/hid.h"
11#include "core/hle/service/sm/sm.h"
12
13namespace Core::Frontend {
14
15ControllerApplet::~ControllerApplet() = default;
16
17DefaultControllerApplet::~DefaultControllerApplet() = default;
18
19void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
20 ControllerParameters parameters) const {
21 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
22
23 auto& npad =
24 Core::System::GetInstance()
25 .ServiceManager()
26 .GetService<Service::HID::Hid>("hid")
27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29
30 auto& players = Settings::values.players;
31
32 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players;
34
35 // Disconnect Handheld first.
36 npad.DisconnectNPadAtIndex(8);
37
38 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) {
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNPadAtIndex(index);
43
44 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) {
46 continue;
47 }
48
49 // Connect controllers based on the following priority list from highest to lowest priority:
50 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
51 if (parameters.allow_pro_controller) {
52 npad.AddNewControllerAt(
53 npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
54 } else if (parameters.allow_dual_joycons) {
55 npad.AddNewControllerAt(
56 npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
57 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
58 // Assign left joycons to even player indices and right joycons to odd player indices.
59 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
60 // a right Joycon for Player 2 in 2 Player Assist mode.
61 if (index % 2 == 0) {
62 npad.AddNewControllerAt(
63 npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
64 } else {
65 npad.AddNewControllerAt(
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
67 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode) {
70 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
72 index);
73 } else {
74 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
75 }
76 }
77
78 callback();
79}
80
81} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
new file mode 100644
index 000000000..a227f15cd
--- /dev/null
+++ b/src/core/frontend/applets/controller.h
@@ -0,0 +1,48 @@
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 <functional>
8
9#include "common/common_types.h"
10
11namespace Core::Frontend {
12
13using BorderColor = std::array<u8, 4>;
14using ExplainText = std::array<char, 0x81>;
15
16struct ControllerParameters {
17 s8 min_players{};
18 s8 max_players{};
19 bool keep_controllers_connected{};
20 bool enable_single_mode{};
21 bool enable_border_color{};
22 std::vector<BorderColor> border_colors{};
23 bool enable_explain_text{};
24 std::vector<ExplainText> explain_text{};
25 bool allow_pro_controller{};
26 bool allow_handheld{};
27 bool allow_dual_joycons{};
28 bool allow_left_joycon{};
29 bool allow_right_joycon{};
30};
31
32class ControllerApplet {
33public:
34 virtual ~ControllerApplet();
35
36 virtual void ReconfigureControllers(std::function<void()> callback,
37 ControllerParameters parameters) const = 0;
38};
39
40class DefaultControllerApplet final : public ControllerApplet {
41public:
42 ~DefaultControllerApplet() override;
43
44 void ReconfigureControllers(std::function<void()> callback,
45 ControllerParameters parameters) const override;
46};
47
48} // namespace Core::Frontend
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index c3261f3e6..4e0800f9a 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/controller.h"
8#include "core/frontend/applets/error.h" 9#include "core/frontend/applets/error.h"
9#include "core/frontend/applets/general_frontend.h" 10#include "core/frontend/applets/general_frontend.h"
10#include "core/frontend/applets/profile_select.h" 11#include "core/frontend/applets/profile_select.h"
@@ -15,6 +16,7 @@
15#include "core/hle/kernel/writable_event.h" 16#include "core/hle/kernel/writable_event.h"
16#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
17#include "core/hle/service/am/applets/applets.h" 18#include "core/hle/service/am/applets/applets.h"
19#include "core/hle/service/am/applets/controller.h"
18#include "core/hle/service/am/applets/error.h" 20#include "core/hle/service/am/applets/error.h"
19#include "core/hle/service/am/applets/general_backend.h" 21#include "core/hle/service/am/applets/general_backend.h"
20#include "core/hle/service/am/applets/profile_select.h" 22#include "core/hle/service/am/applets/profile_select.h"
@@ -140,14 +142,14 @@ void Applet::Initialize() {
140 142
141AppletFrontendSet::AppletFrontendSet() = default; 143AppletFrontendSet::AppletFrontendSet() = default;
142 144
143AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, 145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce,
146 ErrorApplet error, ParentalControlsApplet parental_controls,
144 PhotoViewer photo_viewer, ProfileSelect profile_select, 147 PhotoViewer photo_viewer, ProfileSelect profile_select,
145 SoftwareKeyboard software_keyboard, WebBrowser web_browser, 148 SoftwareKeyboard software_keyboard, WebBrowser web_browser)
146 ECommerceApplet e_commerce) 149 : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)},
147 : parental_controls{std::move(parental_controls)}, error{std::move(error)}, 150 parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)},
148 photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)}, 151 profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)},
149 software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)}, 152 web_browser{std::move(web_browser)} {}
150 e_commerce{std::move(e_commerce)} {}
151 153
152AppletFrontendSet::~AppletFrontendSet() = default; 154AppletFrontendSet::~AppletFrontendSet() = default;
153 155
@@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
164} 166}
165 167
166void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 168void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
167 if (set.parental_controls != nullptr) 169 if (set.controller != nullptr) {
168 frontend.parental_controls = std::move(set.parental_controls); 170 frontend.controller = std::move(set.controller);
169 if (set.error != nullptr) 171 }
172
173 if (set.e_commerce != nullptr) {
174 frontend.e_commerce = std::move(set.e_commerce);
175 }
176
177 if (set.error != nullptr) {
170 frontend.error = std::move(set.error); 178 frontend.error = std::move(set.error);
171 if (set.photo_viewer != nullptr) 179 }
180
181 if (set.parental_controls != nullptr) {
182 frontend.parental_controls = std::move(set.parental_controls);
183 }
184
185 if (set.photo_viewer != nullptr) {
172 frontend.photo_viewer = std::move(set.photo_viewer); 186 frontend.photo_viewer = std::move(set.photo_viewer);
173 if (set.profile_select != nullptr) 187 }
188
189 if (set.profile_select != nullptr) {
174 frontend.profile_select = std::move(set.profile_select); 190 frontend.profile_select = std::move(set.profile_select);
175 if (set.software_keyboard != nullptr) 191 }
192
193 if (set.software_keyboard != nullptr) {
176 frontend.software_keyboard = std::move(set.software_keyboard); 194 frontend.software_keyboard = std::move(set.software_keyboard);
177 if (set.web_browser != nullptr) 195 }
196
197 if (set.web_browser != nullptr) {
178 frontend.web_browser = std::move(set.web_browser); 198 frontend.web_browser = std::move(set.web_browser);
179 if (set.e_commerce != nullptr) 199 }
180 frontend.e_commerce = std::move(set.e_commerce);
181} 200}
182 201
183void AppletManager::SetDefaultAppletFrontendSet() { 202void AppletManager::SetDefaultAppletFrontendSet() {
@@ -186,15 +205,23 @@ void AppletManager::SetDefaultAppletFrontendSet() {
186} 205}
187 206
188void AppletManager::SetDefaultAppletsIfMissing() { 207void AppletManager::SetDefaultAppletsIfMissing() {
189 if (frontend.parental_controls == nullptr) { 208 if (frontend.controller == nullptr) {
190 frontend.parental_controls = 209 frontend.controller = std::make_unique<Core::Frontend::DefaultControllerApplet>();
191 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); 210 }
211
212 if (frontend.e_commerce == nullptr) {
213 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
192 } 214 }
193 215
194 if (frontend.error == nullptr) { 216 if (frontend.error == nullptr) {
195 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 217 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
196 } 218 }
197 219
220 if (frontend.parental_controls == nullptr) {
221 frontend.parental_controls =
222 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
223 }
224
198 if (frontend.photo_viewer == nullptr) { 225 if (frontend.photo_viewer == nullptr) {
199 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); 226 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
200 } 227 }
@@ -211,10 +238,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
211 if (frontend.web_browser == nullptr) { 238 if (frontend.web_browser == nullptr) {
212 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); 239 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
213 } 240 }
214
215 if (frontend.e_commerce == nullptr) {
216 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
217 }
218} 241}
219 242
220void AppletManager::ClearAll() { 243void AppletManager::ClearAll() {
@@ -225,6 +248,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
225 switch (id) { 248 switch (id) {
226 case AppletId::Auth: 249 case AppletId::Auth:
227 return std::make_shared<Auth>(system, *frontend.parental_controls); 250 return std::make_shared<Auth>(system, *frontend.parental_controls);
251 case AppletId::Controller:
252 return std::make_shared<Controller>(system, *frontend.controller);
228 case AppletId::Error: 253 case AppletId::Error:
229 return std::make_shared<Error>(system, *frontend.error); 254 return std::make_shared<Error>(system, *frontend.error);
230 case AppletId::ProfileSelect: 255 case AppletId::ProfileSelect:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e75be86a2..a1f4cf897 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -17,6 +17,7 @@ class System;
17} 17}
18 18
19namespace Core::Frontend { 19namespace Core::Frontend {
20class ControllerApplet;
20class ECommerceApplet; 21class ECommerceApplet;
21class ErrorApplet; 22class ErrorApplet;
22class ParentalControlsApplet; 23class ParentalControlsApplet;
@@ -155,19 +156,20 @@ protected:
155}; 156};
156 157
157struct AppletFrontendSet { 158struct AppletFrontendSet {
158 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
160 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
159 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 161 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
162 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
160 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 163 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
161 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 164 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
162 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; 165 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
163 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 166 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
164 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
165 167
166 AppletFrontendSet(); 168 AppletFrontendSet();
167 AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, 169 AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error,
168 PhotoViewer photo_viewer, ProfileSelect profile_select, 170 ParentalControlsApplet parental_controls, PhotoViewer photo_viewer,
169 SoftwareKeyboard software_keyboard, WebBrowser web_browser, 171 ProfileSelect profile_select, SoftwareKeyboard software_keyboard,
170 ECommerceApplet e_commerce); 172 WebBrowser web_browser);
171 ~AppletFrontendSet(); 173 ~AppletFrontendSet();
172 174
173 AppletFrontendSet(const AppletFrontendSet&) = delete; 175 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -176,13 +178,14 @@ struct AppletFrontendSet {
176 AppletFrontendSet(AppletFrontendSet&&) noexcept; 178 AppletFrontendSet(AppletFrontendSet&&) noexcept;
177 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 179 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
178 180
179 ParentalControlsApplet parental_controls; 181 ControllerApplet controller;
182 ECommerceApplet e_commerce;
180 ErrorApplet error; 183 ErrorApplet error;
184 ParentalControlsApplet parental_controls;
181 PhotoViewer photo_viewer; 185 PhotoViewer photo_viewer;
182 ProfileSelect profile_select; 186 ProfileSelect profile_select;
183 SoftwareKeyboard software_keyboard; 187 SoftwareKeyboard software_keyboard;
184 WebBrowser web_browser; 188 WebBrowser web_browser;
185 ECommerceApplet e_commerce;
186}; 189};
187 190
188class AppletManager { 191class AppletManager {
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
new file mode 100644
index 000000000..2151da783
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -0,0 +1,210 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/core.h"
12#include "core/frontend/applets/controller.h"
13#include "core/hle/result.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/controller.h"
16#include "core/hle/service/hid/controllers/npad.h"
17
18namespace Service::AM::Applets {
19
20// This error code (0x183ACA) is thrown when the applet fails to initialize.
21[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
22// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
23[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
24
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NPadType npad_style_set;
29 npad_style_set.raw = private_arg.style_set;
30
31 return {
32 .min_players = std::max(s8(1), header.player_count_min),
33 .max_players = header.player_count_max,
34 .keep_controllers_connected = header.enable_take_over_connection,
35 .enable_single_mode = header.enable_single_mode,
36 .enable_border_color = header.enable_identification_color,
37 .border_colors = identification_colors,
38 .enable_explain_text = enable_text,
39 .explain_text = text,
40 .allow_pro_controller = npad_style_set.pro_controller == 1,
41 .allow_handheld = npad_style_set.handheld == 1,
42 .allow_dual_joycons = npad_style_set.joycon_dual == 1,
43 .allow_left_joycon = npad_style_set.joycon_left == 1,
44 .allow_right_joycon = npad_style_set.joycon_right == 1,
45 };
46}
47
48Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
49 : Applet{system_.Kernel()}, frontend(frontend_) {}
50
51Controller::~Controller() = default;
52
53void Controller::Initialize() {
54 Applet::Initialize();
55
56 LOG_INFO(Service_HID, "Initializing Controller Applet.");
57
58 LOG_DEBUG(Service_HID,
59 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
60 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
61 common_args.arguments_version, common_args.library_version,
62 common_args.play_startup_sound, common_args.size, common_args.system_tick,
63 common_args.theme_color);
64
65 library_applet_version = LibraryAppletVersion{common_args.library_version};
66
67 const auto private_arg_storage = broker.PopNormalDataToApplet();
68 ASSERT(private_arg_storage != nullptr);
69
70 const auto& private_arg = private_arg_storage->GetData();
71 ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
72
73 std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate));
74 ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
75 "Unknown ControllerSupportArgPrivate revision={} with size={}",
76 library_applet_version, controller_private_arg.arg_private_size);
77
78 switch (controller_private_arg.mode) {
79 case ControllerSupportMode::ShowControllerSupport: {
80 const auto user_arg_storage = broker.PopNormalDataToApplet();
81 ASSERT(user_arg_storage != nullptr);
82
83 const auto& user_arg = user_arg_storage->GetData();
84 switch (library_applet_version) {
85 case LibraryAppletVersion::Version3:
86 case LibraryAppletVersion::Version4:
87 case LibraryAppletVersion::Version5:
88 ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
89 std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld));
90 break;
91 case LibraryAppletVersion::Version7:
92 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
93 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
94 break;
95 default:
96 UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
97 library_applet_version, controller_private_arg.arg_size);
98 ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
99 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
100 break;
101 }
102 break;
103 }
104 case ControllerSupportMode::ShowControllerStrapGuide:
105 case ControllerSupportMode::ShowControllerFirmwareUpdate:
106 default: {
107 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
108 break;
109 }
110 }
111}
112
113bool Controller::TransactionComplete() const {
114 return complete;
115}
116
117ResultCode Controller::GetStatus() const {
118 return status;
119}
120
121void Controller::ExecuteInteractive() {
122 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
123}
124
125void Controller::Execute() {
126 switch (controller_private_arg.mode) {
127 case ControllerSupportMode::ShowControllerSupport: {
128 const auto parameters = [this] {
129 switch (library_applet_version) {
130 case LibraryAppletVersion::Version3:
131 case LibraryAppletVersion::Version4:
132 case LibraryAppletVersion::Version5:
133 return ConvertToFrontendParameters(
134 controller_private_arg, controller_user_arg_old.header,
135 controller_user_arg_old.enable_explain_text,
136 std::vector<IdentificationColor>(
137 controller_user_arg_old.identification_colors.begin(),
138 controller_user_arg_old.identification_colors.end()),
139 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
140 controller_user_arg_old.explain_text.end()));
141 case LibraryAppletVersion::Version7:
142 default:
143 return ConvertToFrontendParameters(
144 controller_private_arg, controller_user_arg_new.header,
145 controller_user_arg_new.enable_explain_text,
146 std::vector<IdentificationColor>(
147 controller_user_arg_new.identification_colors.begin(),
148 controller_user_arg_new.identification_colors.end()),
149 std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(),
150 controller_user_arg_new.explain_text.end()));
151 }
152 }();
153
154 is_single_mode = parameters.enable_single_mode;
155
156 LOG_DEBUG(Service_HID,
157 "Controller Parameters: min_players={}, max_players={}, "
158 "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, "
159 "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, "
160 "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}",
161 parameters.min_players, parameters.max_players,
162 parameters.keep_controllers_connected, parameters.enable_single_mode,
163 parameters.enable_border_color, parameters.enable_explain_text,
164 parameters.allow_pro_controller, parameters.allow_handheld,
165 parameters.allow_dual_joycons, parameters.allow_left_joycon,
166 parameters.allow_right_joycon);
167
168 frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
169 break;
170 }
171 case ControllerSupportMode::ShowControllerStrapGuide:
172 case ControllerSupportMode::ShowControllerFirmwareUpdate:
173 default: {
174 ConfigurationComplete();
175 break;
176 }
177 }
178}
179
180void Controller::ConfigurationComplete() {
181 ControllerSupportResultInfo result_info{};
182
183 const auto& players = Settings::values.players;
184
185 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
186 // Otherwise, only count connected players from P1-P8.
187 result_info.player_count =
188 is_single_mode ? 1
189 : static_cast<s8>(std::count_if(
190 players.begin(), players.end() - 2,
191 [](Settings::PlayerInput player) { return player.connected; }));
192
193 result_info.selected_id = HID::Controller_NPad::IndexToNPad(
194 std::distance(players.begin(),
195 std::find_if(players.begin(), players.end(),
196 [](Settings::PlayerInput player) { return player.connected; })));
197
198 result_info.result = 0;
199
200 LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
201 result_info.player_count, result_info.selected_id, result_info.result);
202
203 complete = true;
204 out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
205 std::memcpy(out_data.data(), &result_info, out_data.size());
206 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data)));
207 broker.SignalStateChanged();
208}
209
210} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
new file mode 100644
index 000000000..f7bb3fba9
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.h
@@ -0,0 +1,123 @@
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 <vector>
9
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "core/hle/result.h"
13#include "core/hle/service/am/applets/applets.h"
14
15namespace Core {
16class System;
17}
18
19namespace Service::AM::Applets {
20
21using IdentificationColor = std::array<u8, 4>;
22using ExplainText = std::array<char, 0x81>;
23
24enum class LibraryAppletVersion : u32_le {
25 Version3 = 0x3, // 1.0.0 - 2.3.0
26 Version4 = 0x4, // 3.0.0 - 5.1.0
27 Version5 = 0x5, // 6.0.0 - 7.0.1
28 Version7 = 0x7, // 8.0.0+
29};
30
31enum class ControllerSupportMode : u8 {
32 ShowControllerSupport = 0,
33 ShowControllerStrapGuide = 1,
34 ShowControllerFirmwareUpdate = 2,
35};
36
37enum class ControllerSupportCaller : u8 {
38 Application = 0,
39 System = 1,
40};
41
42struct ControllerSupportArgPrivate {
43 u32 arg_private_size{};
44 u32 arg_size{};
45 bool flag_0{};
46 bool flag_1{};
47 ControllerSupportMode mode{};
48 ControllerSupportCaller caller{};
49 u32 style_set{};
50 u32 joy_hold_type{};
51};
52static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
53 "ControllerSupportArgPrivate has incorrect size.");
54
55struct ControllerSupportArgHeader {
56 s8 player_count_min{};
57 s8 player_count_max{};
58 bool enable_take_over_connection{};
59 bool enable_left_justify{};
60 bool enable_permit_joy_dual{};
61 bool enable_single_mode{};
62 bool enable_identification_color{};
63};
64static_assert(sizeof(ControllerSupportArgHeader) == 0x7,
65 "ControllerSupportArgHeader has incorrect size.");
66
67// LibraryAppletVersion 0x3, 0x4, 0x5
68struct ControllerSupportArgOld {
69 ControllerSupportArgHeader header{};
70 std::array<IdentificationColor, 4> identification_colors{};
71 bool enable_explain_text{};
72 std::array<ExplainText, 4> explain_text{};
73};
74static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
75 "ControllerSupportArgOld has incorrect size.");
76
77// LibraryAppletVersion 0x7
78struct ControllerSupportArgNew {
79 ControllerSupportArgHeader header{};
80 std::array<IdentificationColor, 8> identification_colors{};
81 bool enable_explain_text{};
82 std::array<ExplainText, 8> explain_text{};
83};
84static_assert(sizeof(ControllerSupportArgNew) == 0x430,
85 "ControllerSupportArgNew has incorrect size.");
86
87struct ControllerSupportResultInfo {
88 s8 player_count{};
89 INSERT_PADDING_BYTES(3);
90 u32 selected_id{};
91 u32 result{};
92};
93static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
94 "ControllerSupportResultInfo has incorrect size.");
95
96class Controller final : public Applet {
97public:
98 explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
99 ~Controller() override;
100
101 void Initialize() override;
102
103 bool TransactionComplete() const override;
104 ResultCode GetStatus() const override;
105 void ExecuteInteractive() override;
106 void Execute() override;
107
108 void ConfigurationComplete();
109
110private:
111 const Core::Frontend::ControllerApplet& frontend;
112
113 LibraryAppletVersion library_applet_version;
114 ControllerSupportArgPrivate controller_private_arg;
115 ControllerSupportArgOld controller_user_arg_old;
116 ControllerSupportArgNew controller_user_arg_new;
117 bool complete{false};
118 ResultCode status{RESULT_SUCCESS};
119 bool is_single_mode{false};
120 std::vector<u8> out_data;
121};
122
123} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e742497e1..7818c098f 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -193,7 +193,8 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
193 controller.battery_level[0] = BATTERY_FULL; 193 controller.battery_level[0] = BATTERY_FULL;
194 controller.battery_level[1] = BATTERY_FULL; 194 controller.battery_level[1] = BATTERY_FULL;
195 controller.battery_level[2] = BATTERY_FULL; 195 controller.battery_level[2] = BATTERY_FULL;
196 styleset_changed_events[controller_idx].writable->Signal(); 196
197 SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
197} 198}
198 199
199void Controller_NPad::OnInit() { 200void Controller_NPad::OnInit() {
@@ -518,13 +519,17 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
518 last_processed_vibration = vibrations.back(); 519 last_processed_vibration = vibrations.back();
519} 520}
520 521
522Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
523 return last_processed_vibration;
524}
525
521std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 526std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 527 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 528 return styleset_event.readable;
524} 529}
525 530
526Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 531void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
527 return last_processed_vibration; 532 styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
528} 533}
529 534
530void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { 535void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
@@ -534,7 +539,7 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
534void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, 539void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 bool connected) { 540 bool connected) {
536 if (!connected) { 541 if (!connected) {
537 DisconnectNPad(IndexToNPad(npad_index)); 542 DisconnectNPadAtIndex(npad_index);
538 return; 543 return;
539 } 544 }
540 545
@@ -554,16 +559,19 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz
554} 559}
555 560
556void Controller_NPad::DisconnectNPad(u32 npad_id) { 561void Controller_NPad::DisconnectNPad(u32 npad_id) {
557 const auto npad_index = NPadIdToIndex(npad_id); 562 DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
558 connected_controllers[npad_index].is_connected = false; 563}
564
565void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
559 Settings::values.players[npad_index].connected = false; 566 Settings::values.players[npad_index].connected = false;
567 connected_controllers[npad_index].is_connected = false;
560 568
561 auto& controller = shared_memory_entries[npad_index]; 569 auto& controller = shared_memory_entries[npad_index];
562 controller.joy_styles.raw = 0; // Zero out 570 controller.joy_styles.raw = 0; // Zero out
563 controller.device_type.raw = 0; 571 controller.device_type.raw = 0;
564 controller.properties.raw = 0; 572 controller.properties.raw = 0;
565 573
566 styleset_changed_events[npad_index].writable->Signal(); 574 SignalStyleSetChangedEvent(IndexToNPad(npad_index));
567} 575}
568 576
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 577void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -666,13 +674,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
666} 674}
667 675
668void Controller_NPad::DisconnectAllConnectedControllers() { 676void Controller_NPad::DisconnectAllConnectedControllers() {
669 for (ControllerHolder& controller : connected_controllers) { 677 for (auto& controller : connected_controllers) {
670 controller.is_connected = false; 678 controller.is_connected = false;
671 } 679 }
672} 680}
673 681
674void Controller_NPad::ConnectAllDisconnectedControllers() { 682void Controller_NPad::ConnectAllDisconnectedControllers() {
675 for (ControllerHolder& controller : connected_controllers) { 683 for (auto& controller : connected_controllers) {
676 if (controller.type != NPadControllerType::None && !controller.is_connected) { 684 if (controller.type != NPadControllerType::None && !controller.is_connected) {
677 controller.is_connected = true; 685 controller.is_connected = true;
678 } 686 }
@@ -680,7 +688,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
680} 688}
681 689
682void Controller_NPad::ClearAllControllers() { 690void Controller_NPad::ClearAllControllers() {
683 for (ControllerHolder& controller : connected_controllers) { 691 for (auto& controller : connected_controllers) {
684 controller.type = NPadControllerType::None; 692 controller.type = NPadControllerType::None;
685 controller.is_connected = false; 693 controller.is_connected = false;
686 } 694 }
@@ -728,92 +736,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
728 return false; 736 return false;
729} 737}
730 738
731Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
732 NPadControllerType priority) const {
733 if (IsControllerSupported(priority)) {
734 return priority;
735 }
736 const auto is_docked = Settings::values.use_docked_mode;
737 if (is_docked && priority == NPadControllerType::Handheld) {
738 priority = NPadControllerType::JoyDual;
739 if (IsControllerSupported(priority)) {
740 return priority;
741 }
742 }
743 std::vector<NPadControllerType> priority_list;
744 switch (priority) {
745 case NPadControllerType::ProController:
746 priority_list.push_back(NPadControllerType::JoyDual);
747 if (!is_docked) {
748 priority_list.push_back(NPadControllerType::Handheld);
749 }
750 priority_list.push_back(NPadControllerType::JoyLeft);
751 priority_list.push_back(NPadControllerType::JoyRight);
752 priority_list.push_back(NPadControllerType::Pokeball);
753 break;
754 case NPadControllerType::Handheld:
755 priority_list.push_back(NPadControllerType::JoyDual);
756 priority_list.push_back(NPadControllerType::ProController);
757 priority_list.push_back(NPadControllerType::JoyLeft);
758 priority_list.push_back(NPadControllerType::JoyRight);
759 priority_list.push_back(NPadControllerType::Pokeball);
760 break;
761 case NPadControllerType::JoyDual:
762 if (!is_docked) {
763 priority_list.push_back(NPadControllerType::Handheld);
764 }
765 priority_list.push_back(NPadControllerType::ProController);
766 priority_list.push_back(NPadControllerType::JoyLeft);
767 priority_list.push_back(NPadControllerType::JoyRight);
768 priority_list.push_back(NPadControllerType::Pokeball);
769 break;
770 case NPadControllerType::JoyLeft:
771 priority_list.push_back(NPadControllerType::JoyRight);
772 priority_list.push_back(NPadControllerType::JoyDual);
773 if (!is_docked) {
774 priority_list.push_back(NPadControllerType::Handheld);
775 }
776 priority_list.push_back(NPadControllerType::ProController);
777 priority_list.push_back(NPadControllerType::Pokeball);
778 break;
779 case NPadControllerType::JoyRight:
780 priority_list.push_back(NPadControllerType::JoyLeft);
781 priority_list.push_back(NPadControllerType::JoyDual);
782 if (!is_docked) {
783 priority_list.push_back(NPadControllerType::Handheld);
784 }
785 priority_list.push_back(NPadControllerType::ProController);
786 priority_list.push_back(NPadControllerType::Pokeball);
787 break;
788 case NPadControllerType::Pokeball:
789 priority_list.push_back(NPadControllerType::JoyLeft);
790 priority_list.push_back(NPadControllerType::JoyRight);
791 priority_list.push_back(NPadControllerType::JoyDual);
792 if (!is_docked) {
793 priority_list.push_back(NPadControllerType::Handheld);
794 }
795 priority_list.push_back(NPadControllerType::ProController);
796 break;
797 default:
798 priority_list.push_back(NPadControllerType::JoyDual);
799 if (!is_docked) {
800 priority_list.push_back(NPadControllerType::Handheld);
801 }
802 priority_list.push_back(NPadControllerType::ProController);
803 priority_list.push_back(NPadControllerType::JoyLeft);
804 priority_list.push_back(NPadControllerType::JoyRight);
805 priority_list.push_back(NPadControllerType::JoyDual);
806 break;
807 }
808
809 const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
810 [this](auto type) { return IsControllerSupported(type); });
811 if (iter == priority_list.end()) {
812 UNIMPLEMENTED_MSG("Could not find supported controller!");
813 return priority;
814 }
815
816 return *iter;
817}
818
819} // namespace Service::HID 739} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ad25c6fbf..e9788da8d 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -115,15 +115,19 @@ public:
115 void VibrateController(const std::vector<u32>& controller_ids, 115 void VibrateController(const std::vector<u32>& controller_ids,
116 const std::vector<Vibration>& vibrations); 116 const std::vector<Vibration>& vibrations);
117 117
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 118 Vibration GetLastVibration() const;
120 119
120 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
121 void SignalStyleSetChangedEvent(u32 npad_id) const;
122
121 // Adds a new controller at an index. 123 // Adds a new controller at an index.
122 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); 124 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
123 // Adds a new controller at an index with connection status. 125 // Adds a new controller at an index with connection status.
124 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); 126 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
125 127
126 void DisconnectNPad(u32 npad_id); 128 void DisconnectNPad(u32 npad_id);
129 void DisconnectNPadAtIndex(std::size_t index);
130
127 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 131 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
128 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 132 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
129 LedPattern GetLedPattern(u32 npad_id); 133 LedPattern GetLedPattern(u32 npad_id);
@@ -315,7 +319,6 @@ private:
315 319
316 void InitNewlyAddedController(std::size_t controller_idx); 320 void InitNewlyAddedController(std::size_t controller_idx);
317 bool IsControllerSupported(NPadControllerType controller) const; 321 bool IsControllerSupported(NPadControllerType controller) const;
318 NPadControllerType DecideBestController(NPadControllerType priority) const;
319 void RequestPadStateUpdate(u32 npad_id); 322 void RequestPadStateUpdate(u32 npad_id);
320 323
321 u32 press_state{}; 324 u32 press_state{};
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 3ea4e5601..cc0291b15 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -9,6 +9,9 @@ add_executable(yuzu
9 about_dialog.cpp 9 about_dialog.cpp
10 about_dialog.h 10 about_dialog.h
11 aboutdialog.ui 11 aboutdialog.ui
12 applets/controller.cpp
13 applets/controller.h
14 applets/controller.ui
12 applets/error.cpp 15 applets/error.cpp
13 applets/error.h 16 applets/error.h
14 applets/profile_select.cpp 17 applets/profile_select.cpp
@@ -62,12 +65,15 @@ add_executable(yuzu
62 configuration/configure_input.cpp 65 configuration/configure_input.cpp
63 configuration/configure_input.h 66 configuration/configure_input.h
64 configuration/configure_input.ui 67 configuration/configure_input.ui
65 configuration/configure_input_player.cpp
66 configuration/configure_input_player.h
67 configuration/configure_input_player.ui
68 configuration/configure_input_advanced.cpp 68 configuration/configure_input_advanced.cpp
69 configuration/configure_input_advanced.h 69 configuration/configure_input_advanced.h
70 configuration/configure_input_advanced.ui 70 configuration/configure_input_advanced.ui
71 configuration/configure_input_dialog.cpp
72 configuration/configure_input_dialog.h
73 configuration/configure_input_dialog.ui
74 configuration/configure_input_player.cpp
75 configuration/configure_input_player.h
76 configuration/configure_input_player.ui
71 configuration/configure_motion_touch.cpp 77 configuration/configure_motion_touch.cpp
72 configuration/configure_motion_touch.h 78 configuration/configure_motion_touch.h
73 configuration/configure_motion_touch.ui 79 configuration/configure_motion_touch.ui
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
new file mode 100644
index 000000000..9d45f2a01
--- /dev/null
+++ b/src/yuzu/applets/controller.cpp
@@ -0,0 +1,601 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/string_util.h"
9#include "core/core.h"
10#include "core/hle/lock.h"
11#include "core/hle/service/hid/controllers/npad.h"
12#include "core/hle/service/hid/hid.h"
13#include "core/hle/service/sm/sm.h"
14#include "ui_controller.h"
15#include "yuzu/applets/controller.h"
16#include "yuzu/configuration/configure_input_dialog.h"
17#include "yuzu/main.h"
18
19namespace {
20
21constexpr std::array<std::array<bool, 4>, 8> led_patterns = {{
22 {1, 0, 0, 0},
23 {1, 1, 0, 0},
24 {1, 1, 1, 0},
25 {1, 1, 1, 1},
26 {1, 0, 0, 1},
27 {1, 0, 1, 0},
28 {1, 0, 1, 1},
29 {0, 1, 1, 0},
30}};
31
32void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
33 bool connected) {
34 Core::System& system{Core::System::GetInstance()};
35
36 if (!system.IsPoweredOn()) {
37 return;
38 }
39
40 Service::SM::ServiceManager& sm = system.ServiceManager();
41
42 auto& npad =
43 sm.GetService<Service::HID::Hid>("hid")
44 ->GetAppletResource()
45 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
46
47 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
48}
49
50// Returns true if the given controller type is compatible with the given parameters.
51bool IsControllerCompatible(Settings::ControllerType controller_type,
52 Core::Frontend::ControllerParameters parameters) {
53 switch (controller_type) {
54 case Settings::ControllerType::ProController:
55 return parameters.allow_pro_controller;
56 case Settings::ControllerType::DualJoyconDetached:
57 return parameters.allow_dual_joycons;
58 case Settings::ControllerType::LeftJoycon:
59 return parameters.allow_left_joycon;
60 case Settings::ControllerType::RightJoycon:
61 return parameters.allow_right_joycon;
62 case Settings::ControllerType::Handheld:
63 return parameters.enable_single_mode && parameters.allow_handheld;
64 default:
65 return false;
66 }
67}
68
69/// Maps the controller type combobox index to Controller Type enum
70constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
71 switch (index) {
72 case 0:
73 default:
74 return Settings::ControllerType::ProController;
75 case 1:
76 return Settings::ControllerType::DualJoyconDetached;
77 case 2:
78 return Settings::ControllerType::LeftJoycon;
79 case 3:
80 return Settings::ControllerType::RightJoycon;
81 case 4:
82 return Settings::ControllerType::Handheld;
83 }
84}
85
86/// Maps the Controller Type enum to controller type combobox index
87constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
88 switch (type) {
89 case Settings::ControllerType::ProController:
90 default:
91 return 0;
92 case Settings::ControllerType::DualJoyconDetached:
93 return 1;
94 case Settings::ControllerType::LeftJoycon:
95 return 2;
96 case Settings::ControllerType::RightJoycon:
97 return 3;
98 case Settings::ControllerType::Handheld:
99 return 4;
100 }
101}
102
103} // namespace
104
105QtControllerSelectorDialog::QtControllerSelectorDialog(
106 QWidget* parent, Core::Frontend::ControllerParameters parameters_,
107 InputCommon::InputSubsystem* input_subsystem_)
108 : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
109 parameters(std::move(parameters_)), input_subsystem(input_subsystem_) {
110 ui->setupUi(this);
111
112 player_widgets = {
113 ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
114 ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
115 };
116
117 player_groupboxes = {
118 ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
119 ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
120 ui->groupPlayer7Connected, ui->groupPlayer8Connected,
121 };
122
123 connected_controller_icons = {
124 ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
125 ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
126 };
127
128 led_patterns_boxes = {{
129 {ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
130 ui->checkboxPlayer1LED4},
131 {ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
132 ui->checkboxPlayer2LED4},
133 {ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
134 ui->checkboxPlayer3LED4},
135 {ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
136 ui->checkboxPlayer4LED4},
137 {ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
138 ui->checkboxPlayer5LED4},
139 {ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
140 ui->checkboxPlayer6LED4},
141 {ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
142 ui->checkboxPlayer7LED4},
143 {ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
144 ui->checkboxPlayer8LED4},
145 }};
146
147 explain_text_labels = {
148 ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
149 ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
150 ui->labelPlayer7Explain, ui->labelPlayer8Explain,
151 };
152
153 emulated_controllers = {
154 ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
155 ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
156 ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
157 };
158
159 player_labels = {
160 ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
161 ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
162 };
163
164 connected_controller_labels = {
165 ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
166 ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
167 ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
168 };
169
170 connected_controller_checkboxes = {
171 ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
172 ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
173 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
174 };
175
176 // Setup/load everything prior to setting up connections.
177 // This avoids unintentionally changing the states of elements while loading them in.
178 SetSupportedControllers();
179 DisableUnsupportedPlayers();
180 LoadConfiguration();
181
182 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
183 SetExplainText(i);
184 UpdateControllerIcon(i);
185 UpdateLEDPattern(i);
186 UpdateBorderColor(i);
187
188 connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
189 if (checked) {
190 for (std::size_t index = 0; index <= i; ++index) {
191 connected_controller_checkboxes[index]->setChecked(checked);
192 }
193 } else {
194 for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
195 connected_controller_checkboxes[index]->setChecked(checked);
196 }
197 }
198 });
199
200 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
201 [this, i](int) {
202 UpdateControllerIcon(i);
203 UpdateControllerState(i);
204 UpdateLEDPattern(i);
205 CheckIfParametersMet();
206 });
207
208 connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
209 player_groupboxes[i]->setChecked(state == Qt::Checked);
210 UpdateControllerIcon(i);
211 UpdateControllerState(i);
212 UpdateLEDPattern(i);
213 UpdateBorderColor(i);
214 CheckIfParametersMet();
215 });
216
217 if (i == 0) {
218 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
219 [this](int index) {
220 UpdateDockedState(GetControllerTypeFromIndex(index) ==
221 Settings::ControllerType::Handheld);
222 });
223 }
224 }
225
226 connect(ui->inputConfigButton, &QPushButton::clicked, this,
227 &QtControllerSelectorDialog::CallConfigureInputDialog);
228
229 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
230 &QtControllerSelectorDialog::ApplyConfiguration);
231
232 // If keep_controllers_connected is false, forcefully disconnect all controllers
233 if (!parameters.keep_controllers_connected) {
234 for (auto player : player_groupboxes) {
235 player->setChecked(false);
236 }
237 }
238
239 CheckIfParametersMet();
240
241 resize(0, 0);
242}
243
244QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
245
246void QtControllerSelectorDialog::ApplyConfiguration() {
247 // Update the controller state once more, just to be sure they are properly applied.
248 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
249 UpdateControllerState(index);
250 }
251
252 const bool pre_docked_mode = Settings::values.use_docked_mode;
253 Settings::values.use_docked_mode = ui->radioDocked->isChecked();
254 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
255
256 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
257}
258
259void QtControllerSelectorDialog::LoadConfiguration() {
260 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
261 const auto connected = Settings::values.players[index].connected ||
262 (index == 0 && Settings::values.players[8].connected);
263 player_groupboxes[index]->setChecked(connected);
264 connected_controller_checkboxes[index]->setChecked(connected);
265 emulated_controllers[index]->setCurrentIndex(
266 GetIndexFromControllerType(Settings::values.players[index].controller_type));
267 }
268
269 UpdateDockedState(Settings::values.players[8].connected);
270
271 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
272}
273
274void QtControllerSelectorDialog::CallConfigureInputDialog() {
275 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
276
277 ConfigureInputDialog dialog(this, max_supported_players, input_subsystem);
278
279 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
280 Qt::WindowSystemMenuHint);
281 dialog.setWindowModality(Qt::WindowModal);
282 dialog.exec();
283
284 dialog.ApplyConfiguration();
285
286 LoadConfiguration();
287 CheckIfParametersMet();
288}
289
290void QtControllerSelectorDialog::CheckIfParametersMet() {
291 // Here, we check and validate the current configuration against all applicable parameters.
292 const auto num_connected_players = static_cast<int>(
293 std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
294 [this](const QGroupBox* player) { return player->isChecked(); }));
295
296 const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
297 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
298
299 // First, check against the number of connected players.
300 if (num_connected_players < min_supported_players ||
301 num_connected_players > max_supported_players) {
302 parameters_met = false;
303 ui->buttonBox->setEnabled(parameters_met);
304 return;
305 }
306
307 // Next, check against all connected controllers.
308 const auto all_controllers_compatible = [this] {
309 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
310 // Skip controllers that are not used, we only care about the currently connected ones.
311 if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
312 continue;
313 }
314
315 const auto compatible = IsControllerCompatible(
316 GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
317 parameters);
318
319 // If any controller is found to be incompatible, return false early.
320 if (!compatible) {
321 return false;
322 }
323 }
324
325 // Reaching here means all currently connected controllers are compatible.
326 return true;
327 }();
328
329 if (!all_controllers_compatible) {
330 parameters_met = false;
331 ui->buttonBox->setEnabled(parameters_met);
332 return;
333 }
334
335 parameters_met = true;
336 ui->buttonBox->setEnabled(parameters_met);
337}
338
339void QtControllerSelectorDialog::SetSupportedControllers() {
340 const QString theme = [this] {
341 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
342 return QStringLiteral("_dark");
343 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
344 return QStringLiteral("_midnight");
345 } else {
346 return QString{};
347 }
348 }();
349
350 if (parameters.enable_single_mode && parameters.allow_handheld) {
351 ui->controllerSupported1->setStyleSheet(
352 QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
353 } else {
354 ui->controllerSupported1->setStyleSheet(
355 QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
356 }
357
358 if (parameters.allow_dual_joycons) {
359 ui->controllerSupported2->setStyleSheet(
360 QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
361 } else {
362 ui->controllerSupported2->setStyleSheet(
363 QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
364 }
365
366 if (parameters.allow_left_joycon) {
367 ui->controllerSupported3->setStyleSheet(
368 QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
369 } else {
370 ui->controllerSupported3->setStyleSheet(
371 QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
372 }
373
374 if (parameters.allow_right_joycon) {
375 ui->controllerSupported4->setStyleSheet(
376 QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
377 } else {
378 ui->controllerSupported4->setStyleSheet(
379 QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
380 }
381
382 if (parameters.allow_pro_controller) {
383 ui->controllerSupported5->setStyleSheet(
384 QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
385 } else {
386 ui->controllerSupported5->setStyleSheet(
387 QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
388 .arg(theme));
389 }
390
391 // enable_single_mode overrides min_players and max_players.
392 if (parameters.enable_single_mode) {
393 ui->numberSupportedLabel->setText(QStringLiteral("1"));
394 return;
395 }
396
397 if (parameters.min_players == parameters.max_players) {
398 ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
399 } else {
400 ui->numberSupportedLabel->setText(
401 QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
402 }
403}
404
405void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
406 if (!player_groupboxes[player_index]->isChecked()) {
407 connected_controller_icons[player_index]->setStyleSheet(QString{});
408 player_labels[player_index]->show();
409 return;
410 }
411
412 const QString stylesheet = [this, player_index] {
413 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
414 case Settings::ControllerType::ProController:
415 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
416 case Settings::ControllerType::DualJoyconDetached:
417 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
418 case Settings::ControllerType::LeftJoycon:
419 return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
420 case Settings::ControllerType::RightJoycon:
421 return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
422 case Settings::ControllerType::Handheld:
423 return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
424 default:
425 return QString{};
426 }
427 }();
428
429 const QString theme = [this] {
430 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
431 return QStringLiteral("_dark");
432 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
433 return QStringLiteral("_midnight");
434 } else {
435 return QString{};
436 }
437 }();
438
439 connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
440 player_labels[player_index]->hide();
441}
442
443void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
444 auto& player = Settings::values.players[player_index];
445
446 player.controller_type =
447 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
448 player.connected = player_groupboxes[player_index]->isChecked();
449
450 // Player 2-8
451 if (player_index != 0) {
452 UpdateController(player.controller_type, player_index, player.connected);
453 return;
454 }
455
456 // Player 1 and Handheld
457 auto& handheld = Settings::values.players[8];
458 // If Handheld is selected, copy all the settings from Player 1 to Handheld.
459 if (player.controller_type == Settings::ControllerType::Handheld) {
460 handheld = player;
461 handheld.connected = player_groupboxes[player_index]->isChecked();
462 player.connected = false; // Disconnect Player 1
463 } else {
464 player.connected = player_groupboxes[player_index]->isChecked();
465 handheld.connected = false; // Disconnect Handheld
466 }
467
468 UpdateController(player.controller_type, player_index, player.connected);
469 UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
470}
471
472void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
473 if (!player_groupboxes[player_index]->isChecked() ||
474 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
475 Settings::ControllerType::Handheld) {
476 led_patterns_boxes[player_index][0]->setChecked(false);
477 led_patterns_boxes[player_index][1]->setChecked(false);
478 led_patterns_boxes[player_index][2]->setChecked(false);
479 led_patterns_boxes[player_index][3]->setChecked(false);
480 return;
481 }
482
483 led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
484 led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
485 led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
486 led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
487}
488
489void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
490 if (!parameters.enable_border_color ||
491 player_index >= static_cast<std::size_t>(parameters.max_players) ||
492 player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
493 return;
494 }
495
496 player_groupboxes[player_index]->setStyleSheet(
497 player_groupboxes[player_index]->styleSheet().append(
498 QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
499 "{ border: 1px solid rgba(%2, %3, %4, %5); }")
500 .arg(player_index + 1)
501 .arg(parameters.border_colors[player_index][0])
502 .arg(parameters.border_colors[player_index][1])
503 .arg(parameters.border_colors[player_index][2])
504 .arg(parameters.border_colors[player_index][3])));
505}
506
507void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
508 if (!parameters.enable_explain_text ||
509 player_index >= static_cast<std::size_t>(parameters.max_players)) {
510 return;
511 }
512
513 explain_text_labels[player_index]->setText(QString::fromStdString(
514 Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
515 parameters.explain_text[player_index].size())));
516}
517
518void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
519 // Disallow changing the console mode if the controller type is handheld.
520 ui->radioDocked->setEnabled(!is_handheld);
521 ui->radioUndocked->setEnabled(!is_handheld);
522
523 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
524 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
525
526 // Also force into undocked mode if the controller type is handheld.
527 if (is_handheld) {
528 ui->radioUndocked->setChecked(true);
529 }
530}
531
532void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
533 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
534
535 switch (max_supported_players) {
536 case 0:
537 default:
538 UNREACHABLE();
539 return;
540 case 1:
541 ui->widgetSpacer->hide();
542 ui->widgetSpacer2->hide();
543 ui->widgetSpacer3->hide();
544 ui->widgetSpacer4->hide();
545 break;
546 case 2:
547 ui->widgetSpacer->hide();
548 ui->widgetSpacer2->hide();
549 ui->widgetSpacer3->hide();
550 break;
551 case 3:
552 ui->widgetSpacer->hide();
553 ui->widgetSpacer2->hide();
554 break;
555 case 4:
556 ui->widgetSpacer->hide();
557 break;
558 case 5:
559 case 6:
560 case 7:
561 case 8:
562 break;
563 }
564
565 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
566 // Disconnect any unsupported players here and disable or hide them if applicable.
567 Settings::values.players[index].connected = false;
568 UpdateController(Settings::values.players[index].controller_type, index, false);
569 // Hide the player widgets when max_supported_controllers is less than or equal to 4.
570 if (max_supported_players <= 4) {
571 player_widgets[index]->hide();
572 }
573
574 // Disable and hide the following to prevent these from interaction.
575 player_widgets[index]->setDisabled(true);
576 connected_controller_checkboxes[index]->setDisabled(true);
577 connected_controller_labels[index]->hide();
578 connected_controller_checkboxes[index]->hide();
579 }
580}
581
582QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
583 connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
584 &GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
585 connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
586 &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
587}
588
589QtControllerSelector::~QtControllerSelector() = default;
590
591void QtControllerSelector::ReconfigureControllers(
592 std::function<void()> callback, Core::Frontend::ControllerParameters parameters) const {
593 this->callback = std::move(callback);
594 emit MainWindowReconfigureControllers(parameters);
595}
596
597void QtControllerSelector::MainWindowReconfigureFinished() {
598 // Acquire the HLE mutex
599 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
600 callback();
601}
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
new file mode 100644
index 000000000..2d6d588c6
--- /dev/null
+++ b/src/yuzu/applets/controller.h
@@ -0,0 +1,133 @@
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 <QDialog>
10#include "core/frontend/applets/controller.h"
11
12class GMainWindow;
13class QCheckBox;
14class QComboBox;
15class QDialogButtonBox;
16class QGroupBox;
17class QLabel;
18
19namespace InputCommon {
20class InputSubsystem;
21}
22
23namespace Ui {
24class QtControllerSelectorDialog;
25}
26
27class QtControllerSelectorDialog final : public QDialog {
28 Q_OBJECT
29
30public:
31 explicit QtControllerSelectorDialog(QWidget* parent,
32 Core::Frontend::ControllerParameters parameters_,
33 InputCommon::InputSubsystem* input_subsystem_);
34 ~QtControllerSelectorDialog() override;
35
36private:
37 // Applies the current configuration.
38 void ApplyConfiguration();
39
40 // Loads the current input configuration into the frontend applet.
41 void LoadConfiguration();
42
43 // Initializes the "Configure Input" Dialog.
44 void CallConfigureInputDialog();
45
46 // Checks the current configuration against the given parameters and
47 // sets the value of parameters_met.
48 void CheckIfParametersMet();
49
50 // Sets the controller icons for "Supported Controller Types".
51 void SetSupportedControllers();
52
53 // Updates the controller icons per player.
54 void UpdateControllerIcon(std::size_t player_index);
55
56 // Updates the controller state (type and connection status) per player.
57 void UpdateControllerState(std::size_t player_index);
58
59 // Updates the LED pattern per player.
60 void UpdateLEDPattern(std::size_t player_index);
61
62 // Updates the border color per player.
63 void UpdateBorderColor(std::size_t player_index);
64
65 // Sets the "Explain Text" per player.
66 void SetExplainText(std::size_t player_index);
67
68 // Updates the console mode.
69 void UpdateDockedState(bool is_handheld);
70
71 // Disables and disconnects unsupported players based on the given parameters.
72 void DisableUnsupportedPlayers();
73
74 std::unique_ptr<Ui::QtControllerSelectorDialog> ui;
75
76 // Parameters sent in from the backend HLE applet.
77 Core::Frontend::ControllerParameters parameters;
78
79 InputCommon::InputSubsystem* input_subsystem;
80
81 // This is true if and only if all parameters are met. Otherwise, this is false.
82 // This determines whether the "OK" button can be clicked to exit the applet.
83 bool parameters_met{false};
84
85 static constexpr std::size_t NUM_PLAYERS = 8;
86
87 // Widgets encapsulating the groupboxes and comboboxes per player.
88 std::array<QWidget*, NUM_PLAYERS> player_widgets;
89
90 // Groupboxes encapsulating the controller icons and LED patterns per player.
91 std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;
92
93 // Icons for currently connected controllers/players.
94 std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;
95
96 // Labels that represent the player numbers in place of the controller icons.
97 std::array<QLabel*, NUM_PLAYERS> player_labels;
98
99 // LED patterns for currently connected controllers/players.
100 std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;
101
102 // Labels representing additional information known as "Explain Text" per player.
103 std::array<QLabel*, NUM_PLAYERS> explain_text_labels;
104
105 // Comboboxes with a list of emulated controllers per player.
106 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
107
108 // Labels representing the number of connected controllers
109 // above the "Connected Controllers" checkboxes.
110 std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
111
112 // Checkboxes representing the "Connected Controllers".
113 std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
114};
115
116class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
117 Q_OBJECT
118
119public:
120 explicit QtControllerSelector(GMainWindow& parent);
121 ~QtControllerSelector() override;
122
123 void ReconfigureControllers(std::function<void()> callback,
124 Core::Frontend::ControllerParameters parameters) const override;
125
126signals:
127 void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const;
128
129private:
130 void MainWindowReconfigureFinished();
131
132 mutable std::function<void()> callback;
133};
diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui
new file mode 100644
index 000000000..c4108a979
--- /dev/null
+++ b/src/yuzu/applets/controller.ui
@@ -0,0 +1,2672 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>QtControllerSelectorDialog</class>
4 <widget class="QDialog" name="QtControllerSelectorDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>839</width>
10 <height>630</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Controller Applet</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
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>
32 <item>
33 <widget class="QWidget" name="mainControllerApplet" native="true">
34 <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,3,0">
35 <property name="spacing">
36 <number>0</number>
37 </property>
38 <property name="leftMargin">
39 <number>0</number>
40 </property>
41 <property name="topMargin">
42 <number>0</number>
43 </property>
44 <property name="rightMargin">
45 <number>0</number>
46 </property>
47 <property name="bottomMargin">
48 <number>0</number>
49 </property>
50 <item>
51 <widget class="QWidget" name="topControllerApplet" native="true">
52 <layout class="QHBoxLayout" name="horizontalLayout">
53 <property name="spacing">
54 <number>10</number>
55 </property>
56 <property name="leftMargin">
57 <number>0</number>
58 </property>
59 <property name="topMargin">
60 <number>10</number>
61 </property>
62 <property name="rightMargin">
63 <number>0</number>
64 </property>
65 <property name="bottomMargin">
66 <number>10</number>
67 </property>
68 <item>
69 <spacer name="controllerAppletHorizontalSpacer2">
70 <property name="orientation">
71 <enum>Qt::Horizontal</enum>
72 </property>
73 <property name="sizeHint" stdset="0">
74 <size>
75 <width>40</width>
76 <height>20</height>
77 </size>
78 </property>
79 </spacer>
80 </item>
81 <item>
82 <widget class="QWidget" name="controllersSupported" native="true">
83 <property name="minimumSize">
84 <size>
85 <width>70</width>
86 <height>70</height>
87 </size>
88 </property>
89 <property name="maximumSize">
90 <size>
91 <width>70</width>
92 <height>70</height>
93 </size>
94 </property>
95 <layout class="QVBoxLayout" name="verticalLayout_21">
96 <property name="leftMargin">
97 <number>0</number>
98 </property>
99 <property name="topMargin">
100 <number>0</number>
101 </property>
102 <property name="rightMargin">
103 <number>0</number>
104 </property>
105 <property name="bottomMargin">
106 <number>0</number>
107 </property>
108 <item>
109 <widget class="QLabel" name="controllersSupportedLabel">
110 <property name="minimumSize">
111 <size>
112 <width>70</width>
113 <height>70</height>
114 </size>
115 </property>
116 <property name="maximumSize">
117 <size>
118 <width>70</width>
119 <height>70</height>
120 </size>
121 </property>
122 <property name="font">
123 <font>
124 <weight>75</weight>
125 <bold>true</bold>
126 </font>
127 </property>
128 <property name="text">
129 <string>Supported Controller Types:</string>
130 </property>
131 <property name="alignment">
132 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
133 </property>
134 <property name="wordWrap">
135 <bool>true</bool>
136 </property>
137 </widget>
138 </item>
139 </layout>
140 </widget>
141 </item>
142 <item>
143 <widget class="QWidget" name="controllerSupported1" native="true">
144 <property name="sizePolicy">
145 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
146 <horstretch>0</horstretch>
147 <verstretch>0</verstretch>
148 </sizepolicy>
149 </property>
150 <property name="minimumSize">
151 <size>
152 <width>70</width>
153 <height>70</height>
154 </size>
155 </property>
156 <property name="maximumSize">
157 <size>
158 <width>70</width>
159 <height>70</height>
160 </size>
161 </property>
162 <property name="styleSheet">
163 <string notr="true"/>
164 </property>
165 </widget>
166 </item>
167 <item>
168 <widget class="QWidget" name="controllerSupported2" native="true">
169 <property name="minimumSize">
170 <size>
171 <width>70</width>
172 <height>70</height>
173 </size>
174 </property>
175 <property name="maximumSize">
176 <size>
177 <width>70</width>
178 <height>70</height>
179 </size>
180 </property>
181 <property name="styleSheet">
182 <string notr="true"/>
183 </property>
184 </widget>
185 </item>
186 <item>
187 <widget class="QWidget" name="controllerSupported3" native="true">
188 <property name="minimumSize">
189 <size>
190 <width>70</width>
191 <height>70</height>
192 </size>
193 </property>
194 <property name="maximumSize">
195 <size>
196 <width>70</width>
197 <height>70</height>
198 </size>
199 </property>
200 <property name="styleSheet">
201 <string notr="true"/>
202 </property>
203 </widget>
204 </item>
205 <item>
206 <widget class="QWidget" name="controllerSupported4" native="true">
207 <property name="minimumSize">
208 <size>
209 <width>70</width>
210 <height>70</height>
211 </size>
212 </property>
213 <property name="maximumSize">
214 <size>
215 <width>70</width>
216 <height>70</height>
217 </size>
218 </property>
219 <property name="styleSheet">
220 <string notr="true"/>
221 </property>
222 </widget>
223 </item>
224 <item>
225 <widget class="QWidget" name="controllerSupported5" native="true">
226 <property name="minimumSize">
227 <size>
228 <width>70</width>
229 <height>70</height>
230 </size>
231 </property>
232 <property name="maximumSize">
233 <size>
234 <width>70</width>
235 <height>70</height>
236 </size>
237 </property>
238 <property name="styleSheet">
239 <string notr="true"/>
240 </property>
241 </widget>
242 </item>
243 <item>
244 <widget class="QWidget" name="playersSupported" native="true">
245 <property name="minimumSize">
246 <size>
247 <width>70</width>
248 <height>70</height>
249 </size>
250 </property>
251 <property name="maximumSize">
252 <size>
253 <width>70</width>
254 <height>70</height>
255 </size>
256 </property>
257 <layout class="QVBoxLayout" name="verticalLayout_20">
258 <property name="spacing">
259 <number>0</number>
260 </property>
261 <property name="leftMargin">
262 <number>0</number>
263 </property>
264 <property name="topMargin">
265 <number>16</number>
266 </property>
267 <property name="rightMargin">
268 <number>14</number>
269 </property>
270 <property name="bottomMargin">
271 <number>16</number>
272 </property>
273 <item>
274 <widget class="QLabel" name="maxSupportedLabel">
275 <property name="font">
276 <font>
277 <weight>75</weight>
278 <bold>true</bold>
279 </font>
280 </property>
281 <property name="text">
282 <string>Players:</string>
283 </property>
284 <property name="alignment">
285 <set>Qt::AlignCenter</set>
286 </property>
287 <property name="wordWrap">
288 <bool>false</bool>
289 </property>
290 </widget>
291 </item>
292 <item>
293 <widget class="QLabel" name="numberSupportedLabel">
294 <property name="font">
295 <font>
296 <pointsize>14</pointsize>
297 </font>
298 </property>
299 <property name="text">
300 <string>1 - 8</string>
301 </property>
302 <property name="alignment">
303 <set>Qt::AlignCenter</set>
304 </property>
305 </widget>
306 </item>
307 </layout>
308 </widget>
309 </item>
310 <item>
311 <spacer name="controllerAppletHorizontalSpacer3">
312 <property name="orientation">
313 <enum>Qt::Horizontal</enum>
314 </property>
315 <property name="sizeHint" stdset="0">
316 <size>
317 <width>40</width>
318 <height>20</height>
319 </size>
320 </property>
321 </spacer>
322 </item>
323 </layout>
324 </widget>
325 </item>
326 <item>
327 <widget class="QWidget" name="middleControllerApplet" native="true">
328 <layout class="QVBoxLayout" name="verticalLayout_3">
329 <property name="spacing">
330 <number>0</number>
331 </property>
332 <property name="leftMargin">
333 <number>0</number>
334 </property>
335 <property name="topMargin">
336 <number>0</number>
337 </property>
338 <property name="rightMargin">
339 <number>0</number>
340 </property>
341 <property name="bottomMargin">
342 <number>0</number>
343 </property>
344 <item>
345 <layout class="QGridLayout" name="gridLayout">
346 <property name="spacing">
347 <number>5</number>
348 </property>
349 <item row="1" column="7">
350 <widget class="QWidget" name="widgetPlayer4" native="true">
351 <layout class="QVBoxLayout" name="verticalLayout_27">
352 <property name="spacing">
353 <number>5</number>
354 </property>
355 <property name="leftMargin">
356 <number>0</number>
357 </property>
358 <property name="topMargin">
359 <number>0</number>
360 </property>
361 <property name="rightMargin">
362 <number>0</number>
363 </property>
364 <property name="bottomMargin">
365 <number>0</number>
366 </property>
367 <item alignment="Qt::AlignHCenter">
368 <widget class="QGroupBox" name="groupPlayer4Connected">
369 <property name="minimumSize">
370 <size>
371 <width>100</width>
372 <height>100</height>
373 </size>
374 </property>
375 <property name="maximumSize">
376 <size>
377 <width>100</width>
378 <height>100</height>
379 </size>
380 </property>
381 <property name="title">
382 <string/>
383 </property>
384 <property name="checkable">
385 <bool>true</bool>
386 </property>
387 <property name="checked">
388 <bool>false</bool>
389 </property>
390 <layout class="QVBoxLayout" name="verticalLayout_7" stretch="1,0">
391 <property name="spacing">
392 <number>7</number>
393 </property>
394 <property name="leftMargin">
395 <number>14</number>
396 </property>
397 <property name="topMargin">
398 <number>7</number>
399 </property>
400 <property name="rightMargin">
401 <number>14</number>
402 </property>
403 <property name="bottomMargin">
404 <number>4</number>
405 </property>
406 <item>
407 <widget class="QWidget" name="controllerPlayer4" native="true">
408 <property name="styleSheet">
409 <string notr="true"/>
410 </property>
411 <layout class="QVBoxLayout" name="verticalLayout_15">
412 <property name="topMargin">
413 <number>16</number>
414 </property>
415 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
416 <widget class="QLabel" name="labelPlayer4">
417 <property name="text">
418 <string>P4</string>
419 </property>
420 </widget>
421 </item>
422 </layout>
423 </widget>
424 </item>
425 <item alignment="Qt::AlignHCenter">
426 <widget class="QWidget" name="Player4LEDs" native="true">
427 <property name="enabled">
428 <bool>false</bool>
429 </property>
430 <property name="minimumSize">
431 <size>
432 <width>0</width>
433 <height>10</height>
434 </size>
435 </property>
436 <layout class="QHBoxLayout" name="horizontalLayout_10">
437 <property name="spacing">
438 <number>4</number>
439 </property>
440 <property name="leftMargin">
441 <number>0</number>
442 </property>
443 <property name="topMargin">
444 <number>0</number>
445 </property>
446 <property name="rightMargin">
447 <number>0</number>
448 </property>
449 <property name="bottomMargin">
450 <number>0</number>
451 </property>
452 <item>
453 <widget class="QCheckBox" name="checkboxPlayer4LED1"/>
454 </item>
455 <item>
456 <widget class="QCheckBox" name="checkboxPlayer4LED2"/>
457 </item>
458 <item>
459 <widget class="QCheckBox" name="checkboxPlayer4LED3"/>
460 </item>
461 <item>
462 <widget class="QCheckBox" name="checkboxPlayer4LED4"/>
463 </item>
464 </layout>
465 </widget>
466 </item>
467 </layout>
468 </widget>
469 </item>
470 <item>
471 <widget class="QWidget" name="Player4Explain" native="true">
472 <property name="minimumSize">
473 <size>
474 <width>0</width>
475 <height>10</height>
476 </size>
477 </property>
478 <property name="maximumSize">
479 <size>
480 <width>150</width>
481 <height>16777215</height>
482 </size>
483 </property>
484 <layout class="QVBoxLayout" name="verticalLayout_39">
485 <property name="spacing">
486 <number>0</number>
487 </property>
488 <property name="leftMargin">
489 <number>0</number>
490 </property>
491 <property name="topMargin">
492 <number>0</number>
493 </property>
494 <property name="rightMargin">
495 <number>0</number>
496 </property>
497 <property name="bottomMargin">
498 <number>0</number>
499 </property>
500 <item>
501 <widget class="QLabel" name="labelPlayer4Explain">
502 <property name="alignment">
503 <set>Qt::AlignCenter</set>
504 </property>
505 </widget>
506 </item>
507 </layout>
508 </widget>
509 </item>
510 <item>
511 <widget class="QComboBox" name="comboPlayer4Emulated">
512 <item>
513 <property name="text">
514 <string>Pro Controller</string>
515 </property>
516 </item>
517 <item>
518 <property name="text">
519 <string>Dual Joycons</string>
520 </property>
521 </item>
522 <item>
523 <property name="text">
524 <string>Left Joycon</string>
525 </property>
526 </item>
527 <item>
528 <property name="text">
529 <string>Right Joycon</string>
530 </property>
531 </item>
532 </widget>
533 </item>
534 <item>
535 <widget class="QComboBox" name="comboPlayer4Profile">
536 <item>
537 <property name="text">
538 <string>Use Current Config</string>
539 </property>
540 </item>
541 </widget>
542 </item>
543 </layout>
544 </widget>
545 </item>
546 <item row="1" column="3">
547 <widget class="QWidget" name="widgetPlayer2" native="true">
548 <layout class="QVBoxLayout" name="verticalLayout_29">
549 <property name="spacing">
550 <number>5</number>
551 </property>
552 <property name="leftMargin">
553 <number>0</number>
554 </property>
555 <property name="topMargin">
556 <number>0</number>
557 </property>
558 <property name="rightMargin">
559 <number>0</number>
560 </property>
561 <property name="bottomMargin">
562 <number>0</number>
563 </property>
564 <item alignment="Qt::AlignHCenter">
565 <widget class="QGroupBox" name="groupPlayer2Connected">
566 <property name="minimumSize">
567 <size>
568 <width>100</width>
569 <height>100</height>
570 </size>
571 </property>
572 <property name="maximumSize">
573 <size>
574 <width>100</width>
575 <height>100</height>
576 </size>
577 </property>
578 <property name="title">
579 <string/>
580 </property>
581 <property name="checkable">
582 <bool>true</bool>
583 </property>
584 <property name="checked">
585 <bool>false</bool>
586 </property>
587 <layout class="QVBoxLayout" name="verticalLayout_5" stretch="1,0">
588 <property name="spacing">
589 <number>7</number>
590 </property>
591 <property name="leftMargin">
592 <number>14</number>
593 </property>
594 <property name="topMargin">
595 <number>7</number>
596 </property>
597 <property name="rightMargin">
598 <number>14</number>
599 </property>
600 <property name="bottomMargin">
601 <number>4</number>
602 </property>
603 <item>
604 <widget class="QWidget" name="controllerPlayer2" native="true">
605 <property name="styleSheet">
606 <string notr="true"/>
607 </property>
608 <layout class="QVBoxLayout" name="verticalLayout_13">
609 <property name="topMargin">
610 <number>16</number>
611 </property>
612 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
613 <widget class="QLabel" name="labelPlayer2">
614 <property name="text">
615 <string>P2</string>
616 </property>
617 </widget>
618 </item>
619 </layout>
620 </widget>
621 </item>
622 <item alignment="Qt::AlignHCenter">
623 <widget class="QWidget" name="Player2LEDs" native="true">
624 <property name="enabled">
625 <bool>false</bool>
626 </property>
627 <property name="minimumSize">
628 <size>
629 <width>0</width>
630 <height>10</height>
631 </size>
632 </property>
633 <layout class="QHBoxLayout" name="horizontalLayout_8">
634 <property name="spacing">
635 <number>4</number>
636 </property>
637 <property name="leftMargin">
638 <number>0</number>
639 </property>
640 <property name="topMargin">
641 <number>0</number>
642 </property>
643 <property name="rightMargin">
644 <number>0</number>
645 </property>
646 <property name="bottomMargin">
647 <number>0</number>
648 </property>
649 <item>
650 <widget class="QCheckBox" name="checkboxPlayer2LED1"/>
651 </item>
652 <item>
653 <widget class="QCheckBox" name="checkboxPlayer2LED2"/>
654 </item>
655 <item>
656 <widget class="QCheckBox" name="checkboxPlayer2LED3"/>
657 </item>
658 <item>
659 <widget class="QCheckBox" name="checkboxPlayer2LED4"/>
660 </item>
661 </layout>
662 </widget>
663 </item>
664 </layout>
665 </widget>
666 </item>
667 <item>
668 <widget class="QWidget" name="Player2Explain" native="true">
669 <property name="minimumSize">
670 <size>
671 <width>0</width>
672 <height>10</height>
673 </size>
674 </property>
675 <property name="maximumSize">
676 <size>
677 <width>150</width>
678 <height>16777215</height>
679 </size>
680 </property>
681 <layout class="QVBoxLayout" name="verticalLayout_37">
682 <property name="spacing">
683 <number>0</number>
684 </property>
685 <property name="leftMargin">
686 <number>0</number>
687 </property>
688 <property name="topMargin">
689 <number>0</number>
690 </property>
691 <property name="rightMargin">
692 <number>0</number>
693 </property>
694 <property name="bottomMargin">
695 <number>0</number>
696 </property>
697 <item>
698 <widget class="QLabel" name="labelPlayer2Explain">
699 <property name="alignment">
700 <set>Qt::AlignCenter</set>
701 </property>
702 </widget>
703 </item>
704 </layout>
705 </widget>
706 </item>
707 <item>
708 <widget class="QComboBox" name="comboPlayer2Emulated">
709 <item>
710 <property name="text">
711 <string>Pro Controller</string>
712 </property>
713 </item>
714 <item>
715 <property name="text">
716 <string>Dual Joycons</string>
717 </property>
718 </item>
719 <item>
720 <property name="text">
721 <string>Left Joycon</string>
722 </property>
723 </item>
724 <item>
725 <property name="text">
726 <string>Right Joycon</string>
727 </property>
728 </item>
729 </widget>
730 </item>
731 <item>
732 <widget class="QComboBox" name="comboPlayer2Profile">
733 <item>
734 <property name="text">
735 <string>Use Current Config</string>
736 </property>
737 </item>
738 </widget>
739 </item>
740 </layout>
741 </widget>
742 </item>
743 <item row="1" column="1">
744 <widget class="QWidget" name="widgetPlayer1" native="true">
745 <layout class="QVBoxLayout" name="verticalLayout_30">
746 <property name="spacing">
747 <number>5</number>
748 </property>
749 <property name="leftMargin">
750 <number>0</number>
751 </property>
752 <property name="topMargin">
753 <number>0</number>
754 </property>
755 <property name="rightMargin">
756 <number>0</number>
757 </property>
758 <property name="bottomMargin">
759 <number>0</number>
760 </property>
761 <item alignment="Qt::AlignHCenter">
762 <widget class="QGroupBox" name="groupPlayer1Connected">
763 <property name="minimumSize">
764 <size>
765 <width>100</width>
766 <height>100</height>
767 </size>
768 </property>
769 <property name="maximumSize">
770 <size>
771 <width>100</width>
772 <height>100</height>
773 </size>
774 </property>
775 <property name="title">
776 <string/>
777 </property>
778 <property name="checkable">
779 <bool>true</bool>
780 </property>
781 <property name="checked">
782 <bool>false</bool>
783 </property>
784 <layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,0">
785 <property name="spacing">
786 <number>7</number>
787 </property>
788 <property name="leftMargin">
789 <number>14</number>
790 </property>
791 <property name="topMargin">
792 <number>7</number>
793 </property>
794 <property name="rightMargin">
795 <number>14</number>
796 </property>
797 <property name="bottomMargin">
798 <number>4</number>
799 </property>
800 <item>
801 <widget class="QWidget" name="controllerPlayer1" native="true">
802 <property name="styleSheet">
803 <string notr="true"/>
804 </property>
805 <layout class="QVBoxLayout" name="verticalLayout_12">
806 <property name="topMargin">
807 <number>16</number>
808 </property>
809 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
810 <widget class="QLabel" name="labelPlayer1">
811 <property name="text">
812 <string>P1</string>
813 </property>
814 </widget>
815 </item>
816 </layout>
817 </widget>
818 </item>
819 <item alignment="Qt::AlignHCenter">
820 <widget class="QWidget" name="Player1LEDs" native="true">
821 <property name="enabled">
822 <bool>false</bool>
823 </property>
824 <property name="minimumSize">
825 <size>
826 <width>0</width>
827 <height>10</height>
828 </size>
829 </property>
830 <layout class="QHBoxLayout" name="horizontalLayout_2">
831 <property name="spacing">
832 <number>4</number>
833 </property>
834 <property name="leftMargin">
835 <number>0</number>
836 </property>
837 <property name="topMargin">
838 <number>0</number>
839 </property>
840 <property name="rightMargin">
841 <number>0</number>
842 </property>
843 <property name="bottomMargin">
844 <number>0</number>
845 </property>
846 <item>
847 <widget class="QCheckBox" name="checkboxPlayer1LED1">
848 <property name="layoutDirection">
849 <enum>Qt::LeftToRight</enum>
850 </property>
851 </widget>
852 </item>
853 <item>
854 <widget class="QCheckBox" name="checkboxPlayer1LED2"/>
855 </item>
856 <item>
857 <widget class="QCheckBox" name="checkboxPlayer1LED3"/>
858 </item>
859 <item>
860 <widget class="QCheckBox" name="checkboxPlayer1LED4"/>
861 </item>
862 </layout>
863 </widget>
864 </item>
865 </layout>
866 </widget>
867 </item>
868 <item>
869 <widget class="QWidget" name="Player1Explain" native="true">
870 <property name="minimumSize">
871 <size>
872 <width>0</width>
873 <height>10</height>
874 </size>
875 </property>
876 <property name="maximumSize">
877 <size>
878 <width>150</width>
879 <height>16777215</height>
880 </size>
881 </property>
882 <layout class="QVBoxLayout" name="verticalLayout_36">
883 <property name="spacing">
884 <number>0</number>
885 </property>
886 <property name="leftMargin">
887 <number>0</number>
888 </property>
889 <property name="topMargin">
890 <number>0</number>
891 </property>
892 <property name="rightMargin">
893 <number>0</number>
894 </property>
895 <property name="bottomMargin">
896 <number>0</number>
897 </property>
898 <item>
899 <widget class="QLabel" name="labelPlayer1Explain">
900 <property name="alignment">
901 <set>Qt::AlignCenter</set>
902 </property>
903 </widget>
904 </item>
905 </layout>
906 </widget>
907 </item>
908 <item>
909 <widget class="QComboBox" name="comboPlayer1Emulated">
910 <item>
911 <property name="text">
912 <string>Pro Controller</string>
913 </property>
914 </item>
915 <item>
916 <property name="text">
917 <string>Dual Joycons</string>
918 </property>
919 </item>
920 <item>
921 <property name="text">
922 <string>Left Joycon</string>
923 </property>
924 </item>
925 <item>
926 <property name="text">
927 <string>Right Joycon</string>
928 </property>
929 </item>
930 <item>
931 <property name="text">
932 <string>Handheld</string>
933 </property>
934 </item>
935 </widget>
936 </item>
937 <item>
938 <widget class="QComboBox" name="comboPlayer1Profile">
939 <item>
940 <property name="text">
941 <string>Use Current Config</string>
942 </property>
943 </item>
944 </widget>
945 </item>
946 </layout>
947 </widget>
948 </item>
949 <item row="1" column="8">
950 <widget class="QWidget" name="widgetSpacer2" native="true">
951 <property name="minimumSize">
952 <size>
953 <width>25</width>
954 <height>0</height>
955 </size>
956 </property>
957 <layout class="QVBoxLayout" name="verticalLayout_31">
958 <property name="spacing">
959 <number>0</number>
960 </property>
961 <property name="leftMargin">
962 <number>0</number>
963 </property>
964 <property name="topMargin">
965 <number>0</number>
966 </property>
967 <property name="rightMargin">
968 <number>0</number>
969 </property>
970 <property name="bottomMargin">
971 <number>0</number>
972 </property>
973 <item>
974 <spacer name="controllerAppletHorizontalSpacer8">
975 <property name="orientation">
976 <enum>Qt::Horizontal</enum>
977 </property>
978 <property name="sizeHint" stdset="0">
979 <size>
980 <width>25</width>
981 <height>20</height>
982 </size>
983 </property>
984 </spacer>
985 </item>
986 </layout>
987 </widget>
988 </item>
989 <item row="1" column="4">
990 <widget class="QWidget" name="widgetSpacer4" native="true">
991 <layout class="QVBoxLayout" name="verticalLayout_33">
992 <property name="spacing">
993 <number>0</number>
994 </property>
995 <property name="leftMargin">
996 <number>0</number>
997 </property>
998 <property name="topMargin">
999 <number>0</number>
1000 </property>
1001 <property name="rightMargin">
1002 <number>0</number>
1003 </property>
1004 <property name="bottomMargin">
1005 <number>0</number>
1006 </property>
1007 <item>
1008 <spacer name="controllerAppletHorizontalSpacer6">
1009 <property name="orientation">
1010 <enum>Qt::Horizontal</enum>
1011 </property>
1012 <property name="sizeHint" stdset="0">
1013 <size>
1014 <width>0</width>
1015 <height>20</height>
1016 </size>
1017 </property>
1018 </spacer>
1019 </item>
1020 </layout>
1021 </widget>
1022 </item>
1023 <item row="1" column="6">
1024 <widget class="QWidget" name="widgetSpacer3" native="true">
1025 <layout class="QVBoxLayout" name="verticalLayout_32">
1026 <property name="spacing">
1027 <number>0</number>
1028 </property>
1029 <property name="leftMargin">
1030 <number>0</number>
1031 </property>
1032 <property name="topMargin">
1033 <number>0</number>
1034 </property>
1035 <property name="rightMargin">
1036 <number>0</number>
1037 </property>
1038 <property name="bottomMargin">
1039 <number>0</number>
1040 </property>
1041 <item>
1042 <spacer name="controllerAppletHorizontalSpacer7">
1043 <property name="orientation">
1044 <enum>Qt::Horizontal</enum>
1045 </property>
1046 <property name="sizeHint" stdset="0">
1047 <size>
1048 <width>0</width>
1049 <height>20</height>
1050 </size>
1051 </property>
1052 </spacer>
1053 </item>
1054 </layout>
1055 </widget>
1056 </item>
1057 <item row="1" column="5">
1058 <widget class="QWidget" name="widgetPlayer3" native="true">
1059 <layout class="QVBoxLayout" name="verticalLayout_28">
1060 <property name="spacing">
1061 <number>5</number>
1062 </property>
1063 <property name="leftMargin">
1064 <number>0</number>
1065 </property>
1066 <property name="topMargin">
1067 <number>0</number>
1068 </property>
1069 <property name="rightMargin">
1070 <number>0</number>
1071 </property>
1072 <property name="bottomMargin">
1073 <number>0</number>
1074 </property>
1075 <item alignment="Qt::AlignHCenter">
1076 <widget class="QGroupBox" name="groupPlayer3Connected">
1077 <property name="minimumSize">
1078 <size>
1079 <width>100</width>
1080 <height>100</height>
1081 </size>
1082 </property>
1083 <property name="maximumSize">
1084 <size>
1085 <width>100</width>
1086 <height>100</height>
1087 </size>
1088 </property>
1089 <property name="title">
1090 <string/>
1091 </property>
1092 <property name="checkable">
1093 <bool>true</bool>
1094 </property>
1095 <property name="checked">
1096 <bool>false</bool>
1097 </property>
1098 <layout class="QVBoxLayout" name="verticalLayout_6" stretch="1,0">
1099 <property name="spacing">
1100 <number>7</number>
1101 </property>
1102 <property name="leftMargin">
1103 <number>14</number>
1104 </property>
1105 <property name="topMargin">
1106 <number>7</number>
1107 </property>
1108 <property name="rightMargin">
1109 <number>14</number>
1110 </property>
1111 <property name="bottomMargin">
1112 <number>4</number>
1113 </property>
1114 <item>
1115 <widget class="QWidget" name="controllerPlayer3" native="true">
1116 <property name="styleSheet">
1117 <string notr="true"/>
1118 </property>
1119 <layout class="QVBoxLayout" name="verticalLayout_14">
1120 <property name="topMargin">
1121 <number>16</number>
1122 </property>
1123 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1124 <widget class="QLabel" name="labelPlayer3">
1125 <property name="text">
1126 <string>P3</string>
1127 </property>
1128 </widget>
1129 </item>
1130 </layout>
1131 </widget>
1132 </item>
1133 <item alignment="Qt::AlignHCenter">
1134 <widget class="QWidget" name="Player3LEDs" native="true">
1135 <property name="enabled">
1136 <bool>false</bool>
1137 </property>
1138 <property name="minimumSize">
1139 <size>
1140 <width>0</width>
1141 <height>10</height>
1142 </size>
1143 </property>
1144 <layout class="QHBoxLayout" name="horizontalLayout_9">
1145 <property name="spacing">
1146 <number>4</number>
1147 </property>
1148 <property name="leftMargin">
1149 <number>0</number>
1150 </property>
1151 <property name="topMargin">
1152 <number>0</number>
1153 </property>
1154 <property name="rightMargin">
1155 <number>0</number>
1156 </property>
1157 <property name="bottomMargin">
1158 <number>0</number>
1159 </property>
1160 <item>
1161 <widget class="QCheckBox" name="checkboxPlayer3LED1"/>
1162 </item>
1163 <item>
1164 <widget class="QCheckBox" name="checkboxPlayer3LED2"/>
1165 </item>
1166 <item>
1167 <widget class="QCheckBox" name="checkboxPlayer3LED3"/>
1168 </item>
1169 <item>
1170 <widget class="QCheckBox" name="checkboxPlayer3LED4"/>
1171 </item>
1172 </layout>
1173 </widget>
1174 </item>
1175 </layout>
1176 </widget>
1177 </item>
1178 <item>
1179 <widget class="QWidget" name="Player3Explain" native="true">
1180 <property name="minimumSize">
1181 <size>
1182 <width>0</width>
1183 <height>10</height>
1184 </size>
1185 </property>
1186 <property name="maximumSize">
1187 <size>
1188 <width>150</width>
1189 <height>16777215</height>
1190 </size>
1191 </property>
1192 <layout class="QVBoxLayout" name="verticalLayout_38">
1193 <property name="spacing">
1194 <number>0</number>
1195 </property>
1196 <property name="leftMargin">
1197 <number>0</number>
1198 </property>
1199 <property name="topMargin">
1200 <number>0</number>
1201 </property>
1202 <property name="rightMargin">
1203 <number>0</number>
1204 </property>
1205 <property name="bottomMargin">
1206 <number>0</number>
1207 </property>
1208 <item>
1209 <widget class="QLabel" name="labelPlayer3Explain">
1210 <property name="alignment">
1211 <set>Qt::AlignCenter</set>
1212 </property>
1213 </widget>
1214 </item>
1215 </layout>
1216 </widget>
1217 </item>
1218 <item>
1219 <widget class="QComboBox" name="comboPlayer3Emulated">
1220 <property name="editable">
1221 <bool>false</bool>
1222 </property>
1223 <item>
1224 <property name="text">
1225 <string>Pro Controller</string>
1226 </property>
1227 </item>
1228 <item>
1229 <property name="text">
1230 <string>Dual Joycons</string>
1231 </property>
1232 </item>
1233 <item>
1234 <property name="text">
1235 <string>Left Joycon</string>
1236 </property>
1237 </item>
1238 <item>
1239 <property name="text">
1240 <string>Right Joycon</string>
1241 </property>
1242 </item>
1243 </widget>
1244 </item>
1245 <item>
1246 <widget class="QComboBox" name="comboPlayer3Profile">
1247 <item>
1248 <property name="text">
1249 <string>Use Current Config</string>
1250 </property>
1251 </item>
1252 </widget>
1253 </item>
1254 </layout>
1255 </widget>
1256 </item>
1257 <item row="0" column="1">
1258 <widget class="QWidget" name="widgetSpacer5" native="true">
1259 <property name="minimumSize">
1260 <size>
1261 <width>0</width>
1262 <height>25</height>
1263 </size>
1264 </property>
1265 <layout class="QVBoxLayout" name="verticalLayout_34">
1266 <property name="spacing">
1267 <number>0</number>
1268 </property>
1269 <property name="leftMargin">
1270 <number>0</number>
1271 </property>
1272 <property name="topMargin">
1273 <number>0</number>
1274 </property>
1275 <property name="rightMargin">
1276 <number>0</number>
1277 </property>
1278 <property name="bottomMargin">
1279 <number>0</number>
1280 </property>
1281 <item>
1282 <spacer name="controllerAppletVerticalSpacer3">
1283 <property name="orientation">
1284 <enum>Qt::Vertical</enum>
1285 </property>
1286 <property name="sizeHint" stdset="0">
1287 <size>
1288 <width>20</width>
1289 <height>25</height>
1290 </size>
1291 </property>
1292 </spacer>
1293 </item>
1294 </layout>
1295 </widget>
1296 </item>
1297 <item row="6" column="5">
1298 <widget class="QWidget" name="widgetPlayer7" native="true">
1299 <layout class="QVBoxLayout" name="verticalLayout_25">
1300 <property name="spacing">
1301 <number>5</number>
1302 </property>
1303 <property name="leftMargin">
1304 <number>0</number>
1305 </property>
1306 <property name="topMargin">
1307 <number>0</number>
1308 </property>
1309 <property name="rightMargin">
1310 <number>0</number>
1311 </property>
1312 <property name="bottomMargin">
1313 <number>0</number>
1314 </property>
1315 <item alignment="Qt::AlignHCenter">
1316 <widget class="QGroupBox" name="groupPlayer7Connected">
1317 <property name="minimumSize">
1318 <size>
1319 <width>100</width>
1320 <height>100</height>
1321 </size>
1322 </property>
1323 <property name="maximumSize">
1324 <size>
1325 <width>100</width>
1326 <height>100</height>
1327 </size>
1328 </property>
1329 <property name="title">
1330 <string/>
1331 </property>
1332 <property name="checkable">
1333 <bool>true</bool>
1334 </property>
1335 <property name="checked">
1336 <bool>false</bool>
1337 </property>
1338 <layout class="QVBoxLayout" name="verticalLayout_10" stretch="1,0">
1339 <property name="spacing">
1340 <number>7</number>
1341 </property>
1342 <property name="leftMargin">
1343 <number>14</number>
1344 </property>
1345 <property name="topMargin">
1346 <number>7</number>
1347 </property>
1348 <property name="rightMargin">
1349 <number>14</number>
1350 </property>
1351 <property name="bottomMargin">
1352 <number>4</number>
1353 </property>
1354 <item>
1355 <widget class="QWidget" name="controllerPlayer7" native="true">
1356 <property name="styleSheet">
1357 <string notr="true"/>
1358 </property>
1359 <layout class="QVBoxLayout" name="verticalLayout_18">
1360 <property name="topMargin">
1361 <number>16</number>
1362 </property>
1363 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1364 <widget class="QLabel" name="labelPlayer7">
1365 <property name="text">
1366 <string>P7</string>
1367 </property>
1368 </widget>
1369 </item>
1370 </layout>
1371 </widget>
1372 </item>
1373 <item alignment="Qt::AlignHCenter">
1374 <widget class="QWidget" name="Player7LEDs" native="true">
1375 <property name="enabled">
1376 <bool>false</bool>
1377 </property>
1378 <property name="minimumSize">
1379 <size>
1380 <width>0</width>
1381 <height>10</height>
1382 </size>
1383 </property>
1384 <layout class="QHBoxLayout" name="horizontalLayout_13">
1385 <property name="spacing">
1386 <number>4</number>
1387 </property>
1388 <property name="leftMargin">
1389 <number>0</number>
1390 </property>
1391 <property name="topMargin">
1392 <number>0</number>
1393 </property>
1394 <property name="rightMargin">
1395 <number>0</number>
1396 </property>
1397 <property name="bottomMargin">
1398 <number>0</number>
1399 </property>
1400 <item>
1401 <widget class="QCheckBox" name="checkboxPlayer7LED1"/>
1402 </item>
1403 <item>
1404 <widget class="QCheckBox" name="checkboxPlayer7LED2"/>
1405 </item>
1406 <item>
1407 <widget class="QCheckBox" name="checkboxPlayer7LED3"/>
1408 </item>
1409 <item>
1410 <widget class="QCheckBox" name="checkboxPlayer7LED4"/>
1411 </item>
1412 </layout>
1413 </widget>
1414 </item>
1415 </layout>
1416 </widget>
1417 </item>
1418 <item>
1419 <widget class="QWidget" name="Player7Explain" native="true">
1420 <property name="minimumSize">
1421 <size>
1422 <width>0</width>
1423 <height>10</height>
1424 </size>
1425 </property>
1426 <property name="maximumSize">
1427 <size>
1428 <width>150</width>
1429 <height>16777215</height>
1430 </size>
1431 </property>
1432 <layout class="QVBoxLayout" name="verticalLayout_42">
1433 <property name="spacing">
1434 <number>0</number>
1435 </property>
1436 <property name="leftMargin">
1437 <number>0</number>
1438 </property>
1439 <property name="topMargin">
1440 <number>0</number>
1441 </property>
1442 <property name="rightMargin">
1443 <number>0</number>
1444 </property>
1445 <property name="bottomMargin">
1446 <number>0</number>
1447 </property>
1448 <item>
1449 <widget class="QLabel" name="labelPlayer7Explain">
1450 <property name="alignment">
1451 <set>Qt::AlignCenter</set>
1452 </property>
1453 </widget>
1454 </item>
1455 </layout>
1456 </widget>
1457 </item>
1458 <item>
1459 <widget class="QComboBox" name="comboPlayer7Emulated">
1460 <item>
1461 <property name="text">
1462 <string>Pro Controller</string>
1463 </property>
1464 </item>
1465 <item>
1466 <property name="text">
1467 <string>Dual Joycons</string>
1468 </property>
1469 </item>
1470 <item>
1471 <property name="text">
1472 <string>Left Joycon</string>
1473 </property>
1474 </item>
1475 <item>
1476 <property name="text">
1477 <string>Right Joycon</string>
1478 </property>
1479 </item>
1480 </widget>
1481 </item>
1482 <item>
1483 <widget class="QComboBox" name="comboPlayer7Profile">
1484 <item>
1485 <property name="text">
1486 <string>Use Current Config</string>
1487 </property>
1488 </item>
1489 </widget>
1490 </item>
1491 </layout>
1492 </widget>
1493 </item>
1494 <item row="6" column="7">
1495 <widget class="QWidget" name="widgetPlayer8" native="true">
1496 <layout class="QVBoxLayout" name="verticalLayout_26">
1497 <property name="spacing">
1498 <number>5</number>
1499 </property>
1500 <property name="leftMargin">
1501 <number>0</number>
1502 </property>
1503 <property name="topMargin">
1504 <number>0</number>
1505 </property>
1506 <property name="rightMargin">
1507 <number>0</number>
1508 </property>
1509 <property name="bottomMargin">
1510 <number>0</number>
1511 </property>
1512 <item alignment="Qt::AlignHCenter">
1513 <widget class="QGroupBox" name="groupPlayer8Connected">
1514 <property name="minimumSize">
1515 <size>
1516 <width>100</width>
1517 <height>100</height>
1518 </size>
1519 </property>
1520 <property name="maximumSize">
1521 <size>
1522 <width>100</width>
1523 <height>100</height>
1524 </size>
1525 </property>
1526 <property name="title">
1527 <string/>
1528 </property>
1529 <property name="checkable">
1530 <bool>true</bool>
1531 </property>
1532 <property name="checked">
1533 <bool>false</bool>
1534 </property>
1535 <layout class="QVBoxLayout" name="verticalLayout_11" stretch="1,0">
1536 <property name="spacing">
1537 <number>7</number>
1538 </property>
1539 <property name="leftMargin">
1540 <number>14</number>
1541 </property>
1542 <property name="topMargin">
1543 <number>7</number>
1544 </property>
1545 <property name="rightMargin">
1546 <number>14</number>
1547 </property>
1548 <property name="bottomMargin">
1549 <number>4</number>
1550 </property>
1551 <item>
1552 <widget class="QWidget" name="controllerPlayer8" native="true">
1553 <property name="styleSheet">
1554 <string notr="true"/>
1555 </property>
1556 <layout class="QVBoxLayout" name="verticalLayout_19">
1557 <property name="topMargin">
1558 <number>16</number>
1559 </property>
1560 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1561 <widget class="QLabel" name="labelPlayer8">
1562 <property name="text">
1563 <string>P8</string>
1564 </property>
1565 </widget>
1566 </item>
1567 </layout>
1568 </widget>
1569 </item>
1570 <item alignment="Qt::AlignHCenter">
1571 <widget class="QWidget" name="Player8LEDs" native="true">
1572 <property name="enabled">
1573 <bool>false</bool>
1574 </property>
1575 <property name="minimumSize">
1576 <size>
1577 <width>0</width>
1578 <height>10</height>
1579 </size>
1580 </property>
1581 <layout class="QHBoxLayout" name="horizontalLayout_14">
1582 <property name="spacing">
1583 <number>4</number>
1584 </property>
1585 <property name="leftMargin">
1586 <number>0</number>
1587 </property>
1588 <property name="topMargin">
1589 <number>0</number>
1590 </property>
1591 <property name="rightMargin">
1592 <number>0</number>
1593 </property>
1594 <property name="bottomMargin">
1595 <number>0</number>
1596 </property>
1597 <item>
1598 <widget class="QCheckBox" name="checkboxPlayer8LED1"/>
1599 </item>
1600 <item>
1601 <widget class="QCheckBox" name="checkboxPlayer8LED2"/>
1602 </item>
1603 <item>
1604 <widget class="QCheckBox" name="checkboxPlayer8LED3"/>
1605 </item>
1606 <item>
1607 <widget class="QCheckBox" name="checkboxPlayer8LED4"/>
1608 </item>
1609 </layout>
1610 </widget>
1611 </item>
1612 </layout>
1613 </widget>
1614 </item>
1615 <item>
1616 <widget class="QWidget" name="Player8Explain" native="true">
1617 <property name="minimumSize">
1618 <size>
1619 <width>0</width>
1620 <height>10</height>
1621 </size>
1622 </property>
1623 <property name="maximumSize">
1624 <size>
1625 <width>150</width>
1626 <height>16777215</height>
1627 </size>
1628 </property>
1629 <layout class="QVBoxLayout" name="verticalLayout_35">
1630 <property name="spacing">
1631 <number>0</number>
1632 </property>
1633 <property name="leftMargin">
1634 <number>0</number>
1635 </property>
1636 <property name="topMargin">
1637 <number>0</number>
1638 </property>
1639 <property name="rightMargin">
1640 <number>0</number>
1641 </property>
1642 <property name="bottomMargin">
1643 <number>0</number>
1644 </property>
1645 <item>
1646 <widget class="QLabel" name="labelPlayer8Explain">
1647 <property name="alignment">
1648 <set>Qt::AlignCenter</set>
1649 </property>
1650 </widget>
1651 </item>
1652 </layout>
1653 </widget>
1654 </item>
1655 <item>
1656 <widget class="QComboBox" name="comboPlayer8Emulated">
1657 <item>
1658 <property name="text">
1659 <string>Pro Controller</string>
1660 </property>
1661 </item>
1662 <item>
1663 <property name="text">
1664 <string>Dual Joycons</string>
1665 </property>
1666 </item>
1667 <item>
1668 <property name="text">
1669 <string>Left Joycon</string>
1670 </property>
1671 </item>
1672 <item>
1673 <property name="text">
1674 <string>Right Joycon</string>
1675 </property>
1676 </item>
1677 </widget>
1678 </item>
1679 <item>
1680 <widget class="QComboBox" name="comboPlayer8Profile">
1681 <item>
1682 <property name="text">
1683 <string>Use Current Config</string>
1684 </property>
1685 </item>
1686 </widget>
1687 </item>
1688 </layout>
1689 </widget>
1690 </item>
1691 <item row="6" column="1">
1692 <widget class="QWidget" name="widgetPlayer5" native="true">
1693 <layout class="QVBoxLayout" name="verticalLayout_23">
1694 <property name="spacing">
1695 <number>5</number>
1696 </property>
1697 <property name="leftMargin">
1698 <number>0</number>
1699 </property>
1700 <property name="topMargin">
1701 <number>0</number>
1702 </property>
1703 <property name="rightMargin">
1704 <number>0</number>
1705 </property>
1706 <property name="bottomMargin">
1707 <number>0</number>
1708 </property>
1709 <item alignment="Qt::AlignHCenter">
1710 <widget class="QGroupBox" name="groupPlayer5Connected">
1711 <property name="minimumSize">
1712 <size>
1713 <width>100</width>
1714 <height>100</height>
1715 </size>
1716 </property>
1717 <property name="maximumSize">
1718 <size>
1719 <width>100</width>
1720 <height>100</height>
1721 </size>
1722 </property>
1723 <property name="title">
1724 <string/>
1725 </property>
1726 <property name="checkable">
1727 <bool>true</bool>
1728 </property>
1729 <property name="checked">
1730 <bool>false</bool>
1731 </property>
1732 <layout class="QVBoxLayout" name="verticalLayout_8" stretch="1,0">
1733 <property name="spacing">
1734 <number>7</number>
1735 </property>
1736 <property name="leftMargin">
1737 <number>14</number>
1738 </property>
1739 <property name="topMargin">
1740 <number>7</number>
1741 </property>
1742 <property name="rightMargin">
1743 <number>14</number>
1744 </property>
1745 <property name="bottomMargin">
1746 <number>4</number>
1747 </property>
1748 <item>
1749 <widget class="QWidget" name="controllerPlayer5" native="true">
1750 <property name="styleSheet">
1751 <string notr="true"/>
1752 </property>
1753 <layout class="QVBoxLayout" name="verticalLayout_16">
1754 <property name="topMargin">
1755 <number>16</number>
1756 </property>
1757 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1758 <widget class="QLabel" name="labelPlayer5">
1759 <property name="text">
1760 <string>P5</string>
1761 </property>
1762 </widget>
1763 </item>
1764 </layout>
1765 </widget>
1766 </item>
1767 <item alignment="Qt::AlignHCenter">
1768 <widget class="QWidget" name="Player5LEDs" native="true">
1769 <property name="enabled">
1770 <bool>false</bool>
1771 </property>
1772 <property name="minimumSize">
1773 <size>
1774 <width>0</width>
1775 <height>10</height>
1776 </size>
1777 </property>
1778 <layout class="QHBoxLayout" name="horizontalLayout_11">
1779 <property name="spacing">
1780 <number>4</number>
1781 </property>
1782 <property name="leftMargin">
1783 <number>0</number>
1784 </property>
1785 <property name="topMargin">
1786 <number>0</number>
1787 </property>
1788 <property name="rightMargin">
1789 <number>0</number>
1790 </property>
1791 <property name="bottomMargin">
1792 <number>0</number>
1793 </property>
1794 <item>
1795 <widget class="QCheckBox" name="checkboxPlayer5LED1">
1796 <property name="layoutDirection">
1797 <enum>Qt::LeftToRight</enum>
1798 </property>
1799 </widget>
1800 </item>
1801 <item>
1802 <widget class="QCheckBox" name="checkboxPlayer5LED2"/>
1803 </item>
1804 <item>
1805 <widget class="QCheckBox" name="checkboxPlayer5LED3"/>
1806 </item>
1807 <item>
1808 <widget class="QCheckBox" name="checkboxPlayer5LED4"/>
1809 </item>
1810 </layout>
1811 </widget>
1812 </item>
1813 </layout>
1814 </widget>
1815 </item>
1816 <item>
1817 <widget class="QWidget" name="Player5Explain" native="true">
1818 <property name="minimumSize">
1819 <size>
1820 <width>0</width>
1821 <height>10</height>
1822 </size>
1823 </property>
1824 <property name="maximumSize">
1825 <size>
1826 <width>150</width>
1827 <height>16777215</height>
1828 </size>
1829 </property>
1830 <layout class="QVBoxLayout" name="verticalLayout_40">
1831 <property name="spacing">
1832 <number>0</number>
1833 </property>
1834 <property name="leftMargin">
1835 <number>0</number>
1836 </property>
1837 <property name="topMargin">
1838 <number>0</number>
1839 </property>
1840 <property name="rightMargin">
1841 <number>0</number>
1842 </property>
1843 <property name="bottomMargin">
1844 <number>0</number>
1845 </property>
1846 <item>
1847 <widget class="QLabel" name="labelPlayer5Explain">
1848 <property name="alignment">
1849 <set>Qt::AlignCenter</set>
1850 </property>
1851 </widget>
1852 </item>
1853 </layout>
1854 </widget>
1855 </item>
1856 <item>
1857 <widget class="QComboBox" name="comboPlayer5Emulated">
1858 <item>
1859 <property name="text">
1860 <string>Pro Controller</string>
1861 </property>
1862 </item>
1863 <item>
1864 <property name="text">
1865 <string>Dual Joycons</string>
1866 </property>
1867 </item>
1868 <item>
1869 <property name="text">
1870 <string>Left Joycon</string>
1871 </property>
1872 </item>
1873 <item>
1874 <property name="text">
1875 <string>Right Joycon</string>
1876 </property>
1877 </item>
1878 </widget>
1879 </item>
1880 <item>
1881 <widget class="QComboBox" name="comboPlayer5Profile">
1882 <item>
1883 <property name="text">
1884 <string>Use Current Config</string>
1885 </property>
1886 </item>
1887 </widget>
1888 </item>
1889 </layout>
1890 </widget>
1891 </item>
1892 <item row="6" column="3">
1893 <widget class="QWidget" name="widgetPlayer6" native="true">
1894 <layout class="QVBoxLayout" name="verticalLayout_24">
1895 <property name="spacing">
1896 <number>5</number>
1897 </property>
1898 <property name="leftMargin">
1899 <number>0</number>
1900 </property>
1901 <property name="topMargin">
1902 <number>0</number>
1903 </property>
1904 <property name="rightMargin">
1905 <number>0</number>
1906 </property>
1907 <property name="bottomMargin">
1908 <number>0</number>
1909 </property>
1910 <item alignment="Qt::AlignHCenter">
1911 <widget class="QGroupBox" name="groupPlayer6Connected">
1912 <property name="minimumSize">
1913 <size>
1914 <width>100</width>
1915 <height>100</height>
1916 </size>
1917 </property>
1918 <property name="maximumSize">
1919 <size>
1920 <width>100</width>
1921 <height>100</height>
1922 </size>
1923 </property>
1924 <property name="title">
1925 <string/>
1926 </property>
1927 <property name="checkable">
1928 <bool>true</bool>
1929 </property>
1930 <property name="checked">
1931 <bool>false</bool>
1932 </property>
1933 <layout class="QVBoxLayout" name="verticalLayout_9" stretch="1,0">
1934 <property name="spacing">
1935 <number>7</number>
1936 </property>
1937 <property name="leftMargin">
1938 <number>14</number>
1939 </property>
1940 <property name="topMargin">
1941 <number>7</number>
1942 </property>
1943 <property name="rightMargin">
1944 <number>14</number>
1945 </property>
1946 <property name="bottomMargin">
1947 <number>4</number>
1948 </property>
1949 <item>
1950 <widget class="QWidget" name="controllerPlayer6" native="true">
1951 <property name="styleSheet">
1952 <string notr="true"/>
1953 </property>
1954 <layout class="QVBoxLayout" name="verticalLayout_17">
1955 <property name="topMargin">
1956 <number>16</number>
1957 </property>
1958 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1959 <widget class="QLabel" name="labelPlayer6">
1960 <property name="text">
1961 <string>P6</string>
1962 </property>
1963 </widget>
1964 </item>
1965 </layout>
1966 </widget>
1967 </item>
1968 <item alignment="Qt::AlignHCenter">
1969 <widget class="QWidget" name="Player6LEDs" native="true">
1970 <property name="enabled">
1971 <bool>false</bool>
1972 </property>
1973 <property name="minimumSize">
1974 <size>
1975 <width>0</width>
1976 <height>10</height>
1977 </size>
1978 </property>
1979 <layout class="QHBoxLayout" name="horizontalLayout_12">
1980 <property name="spacing">
1981 <number>4</number>
1982 </property>
1983 <property name="leftMargin">
1984 <number>0</number>
1985 </property>
1986 <property name="topMargin">
1987 <number>0</number>
1988 </property>
1989 <property name="rightMargin">
1990 <number>0</number>
1991 </property>
1992 <property name="bottomMargin">
1993 <number>0</number>
1994 </property>
1995 <item>
1996 <widget class="QCheckBox" name="checkboxPlayer6LED1"/>
1997 </item>
1998 <item>
1999 <widget class="QCheckBox" name="checkboxPlayer6LED2"/>
2000 </item>
2001 <item>
2002 <widget class="QCheckBox" name="checkboxPlayer6LED3"/>
2003 </item>
2004 <item>
2005 <widget class="QCheckBox" name="checkboxPlayer6LED4"/>
2006 </item>
2007 </layout>
2008 </widget>
2009 </item>
2010 </layout>
2011 </widget>
2012 </item>
2013 <item>
2014 <widget class="QWidget" name="Player6Explain" native="true">
2015 <property name="minimumSize">
2016 <size>
2017 <width>0</width>
2018 <height>10</height>
2019 </size>
2020 </property>
2021 <property name="maximumSize">
2022 <size>
2023 <width>150</width>
2024 <height>16777215</height>
2025 </size>
2026 </property>
2027 <layout class="QVBoxLayout" name="verticalLayout_41">
2028 <property name="spacing">
2029 <number>0</number>
2030 </property>
2031 <property name="leftMargin">
2032 <number>0</number>
2033 </property>
2034 <property name="topMargin">
2035 <number>0</number>
2036 </property>
2037 <property name="rightMargin">
2038 <number>0</number>
2039 </property>
2040 <property name="bottomMargin">
2041 <number>0</number>
2042 </property>
2043 <item>
2044 <widget class="QLabel" name="labelPlayer6Explain">
2045 <property name="alignment">
2046 <set>Qt::AlignCenter</set>
2047 </property>
2048 </widget>
2049 </item>
2050 </layout>
2051 </widget>
2052 </item>
2053 <item>
2054 <widget class="QComboBox" name="comboPlayer6Emulated">
2055 <item>
2056 <property name="text">
2057 <string>Pro Controller</string>
2058 </property>
2059 </item>
2060 <item>
2061 <property name="text">
2062 <string>Dual Joycons</string>
2063 </property>
2064 </item>
2065 <item>
2066 <property name="text">
2067 <string>Left Joycon</string>
2068 </property>
2069 </item>
2070 <item>
2071 <property name="text">
2072 <string>Right Joycon</string>
2073 </property>
2074 </item>
2075 </widget>
2076 </item>
2077 <item>
2078 <widget class="QComboBox" name="comboPlayer6Profile">
2079 <item>
2080 <property name="text">
2081 <string>Use Current Config</string>
2082 </property>
2083 </item>
2084 </widget>
2085 </item>
2086 </layout>
2087 </widget>
2088 </item>
2089 <item row="10" column="1">
2090 <widget class="QWidget" name="widgetSpacer" native="true">
2091 <property name="minimumSize">
2092 <size>
2093 <width>0</width>
2094 <height>25</height>
2095 </size>
2096 </property>
2097 <layout class="QVBoxLayout" name="verticalLayout_22">
2098 <property name="spacing">
2099 <number>0</number>
2100 </property>
2101 <property name="leftMargin">
2102 <number>0</number>
2103 </property>
2104 <property name="topMargin">
2105 <number>0</number>
2106 </property>
2107 <property name="rightMargin">
2108 <number>0</number>
2109 </property>
2110 <property name="bottomMargin">
2111 <number>0</number>
2112 </property>
2113 <item>
2114 <spacer name="controllerAppletVerticalSpacer">
2115 <property name="orientation">
2116 <enum>Qt::Vertical</enum>
2117 </property>
2118 <property name="sizeHint" stdset="0">
2119 <size>
2120 <width>20</width>
2121 <height>25</height>
2122 </size>
2123 </property>
2124 </spacer>
2125 </item>
2126 </layout>
2127 </widget>
2128 </item>
2129 <item row="1" column="2">
2130 <widget class="QWidget" name="widgetSpacer6" native="true">
2131 <layout class="QHBoxLayout" name="horizontalLayout_15">
2132 <property name="spacing">
2133 <number>0</number>
2134 </property>
2135 <property name="leftMargin">
2136 <number>0</number>
2137 </property>
2138 <property name="topMargin">
2139 <number>0</number>
2140 </property>
2141 <property name="rightMargin">
2142 <number>0</number>
2143 </property>
2144 <property name="bottomMargin">
2145 <number>0</number>
2146 </property>
2147 <item>
2148 <spacer name="controllerAppletHorizontalSpacer5">
2149 <property name="orientation">
2150 <enum>Qt::Horizontal</enum>
2151 </property>
2152 <property name="sizeHint" stdset="0">
2153 <size>
2154 <width>0</width>
2155 <height>20</height>
2156 </size>
2157 </property>
2158 </spacer>
2159 </item>
2160 </layout>
2161 </widget>
2162 </item>
2163 <item row="1" column="0">
2164 <widget class="QWidget" name="widgetSpacer7" native="true">
2165 <property name="minimumSize">
2166 <size>
2167 <width>25</width>
2168 <height>0</height>
2169 </size>
2170 </property>
2171 <layout class="QHBoxLayout" name="horizontalLayout_16">
2172 <property name="spacing">
2173 <number>0</number>
2174 </property>
2175 <property name="leftMargin">
2176 <number>0</number>
2177 </property>
2178 <property name="topMargin">
2179 <number>0</number>
2180 </property>
2181 <property name="rightMargin">
2182 <number>0</number>
2183 </property>
2184 <property name="bottomMargin">
2185 <number>0</number>
2186 </property>
2187 <item>
2188 <spacer name="controllerAppletHorizontalSpacer4">
2189 <property name="orientation">
2190 <enum>Qt::Horizontal</enum>
2191 </property>
2192 <property name="sizeHint" stdset="0">
2193 <size>
2194 <width>25</width>
2195 <height>20</height>
2196 </size>
2197 </property>
2198 </spacer>
2199 </item>
2200 </layout>
2201 </widget>
2202 </item>
2203 <item row="2" column="1">
2204 <widget class="QWidget" name="widgetSpacer9" native="true">
2205 <property name="minimumSize">
2206 <size>
2207 <width>0</width>
2208 <height>25</height>
2209 </size>
2210 </property>
2211 <layout class="QHBoxLayout" name="horizontalLayout_17">
2212 <property name="spacing">
2213 <number>0</number>
2214 </property>
2215 <property name="leftMargin">
2216 <number>0</number>
2217 </property>
2218 <property name="topMargin">
2219 <number>0</number>
2220 </property>
2221 <property name="rightMargin">
2222 <number>0</number>
2223 </property>
2224 <property name="bottomMargin">
2225 <number>0</number>
2226 </property>
2227 <item>
2228 <spacer name="controllerAppletVerticalSpacer2">
2229 <property name="orientation">
2230 <enum>Qt::Vertical</enum>
2231 </property>
2232 <property name="sizeHint" stdset="0">
2233 <size>
2234 <width>20</width>
2235 <height>25</height>
2236 </size>
2237 </property>
2238 </spacer>
2239 </item>
2240 </layout>
2241 </widget>
2242 </item>
2243 </layout>
2244 </item>
2245 </layout>
2246 </widget>
2247 </item>
2248 <item>
2249 <widget class="QWidget" name="bottomControllerApplet" native="true">
2250 <layout class="QHBoxLayout" name="horizontalLayout_6">
2251 <property name="spacing">
2252 <number>15</number>
2253 </property>
2254 <property name="leftMargin">
2255 <number>15</number>
2256 </property>
2257 <property name="topMargin">
2258 <number>8</number>
2259 </property>
2260 <property name="rightMargin">
2261 <number>15</number>
2262 </property>
2263 <property name="bottomMargin">
2264 <number>15</number>
2265 </property>
2266 <item>
2267 <widget class="QGroupBox" name="handheldGroup">
2268 <property name="maximumSize">
2269 <size>
2270 <width>16777215</width>
2271 <height>16777215</height>
2272 </size>
2273 </property>
2274 <property name="title">
2275 <string>Console Mode</string>
2276 </property>
2277 <layout class="QHBoxLayout" name="horizontalLayout_3">
2278 <property name="spacing">
2279 <number>6</number>
2280 </property>
2281 <property name="leftMargin">
2282 <number>6</number>
2283 </property>
2284 <property name="topMargin">
2285 <number>6</number>
2286 </property>
2287 <property name="rightMargin">
2288 <number>3</number>
2289 </property>
2290 <property name="bottomMargin">
2291 <number>6</number>
2292 </property>
2293 <item>
2294 <widget class="QRadioButton" name="radioDocked">
2295 <property name="text">
2296 <string>Docked</string>
2297 </property>
2298 <property name="checked">
2299 <bool>true</bool>
2300 </property>
2301 </widget>
2302 </item>
2303 <item>
2304 <widget class="QRadioButton" name="radioUndocked">
2305 <property name="text">
2306 <string>Undocked</string>
2307 </property>
2308 </widget>
2309 </item>
2310 </layout>
2311 </widget>
2312 </item>
2313 <item>
2314 <widget class="QGroupBox" name="vibrationGroup">
2315 <property name="title">
2316 <string>Vibration</string>
2317 </property>
2318 <property name="checkable">
2319 <bool>true</bool>
2320 </property>
2321 <layout class="QHBoxLayout" name="horizontalLayout_5">
2322 <property name="leftMargin">
2323 <number>3</number>
2324 </property>
2325 <property name="topMargin">
2326 <number>3</number>
2327 </property>
2328 <property name="rightMargin">
2329 <number>3</number>
2330 </property>
2331 <property name="bottomMargin">
2332 <number>3</number>
2333 </property>
2334 <item>
2335 <widget class="QSpinBox" name="vibrationSpin">
2336 <property name="minimumSize">
2337 <size>
2338 <width>65</width>
2339 <height>0</height>
2340 </size>
2341 </property>
2342 <property name="maximumSize">
2343 <size>
2344 <width>65</width>
2345 <height>16777215</height>
2346 </size>
2347 </property>
2348 <property name="suffix">
2349 <string>%</string>
2350 </property>
2351 <property name="minimum">
2352 <number>1</number>
2353 </property>
2354 <property name="maximum">
2355 <number>200</number>
2356 </property>
2357 <property name="value">
2358 <number>100</number>
2359 </property>
2360 </widget>
2361 </item>
2362 </layout>
2363 </widget>
2364 </item>
2365 <item>
2366 <widget class="QGroupBox" name="motionGroup">
2367 <property name="title">
2368 <string>Motion</string>
2369 </property>
2370 <property name="checkable">
2371 <bool>true</bool>
2372 </property>
2373 <layout class="QHBoxLayout" name="horizontalLayout_4">
2374 <property name="leftMargin">
2375 <number>3</number>
2376 </property>
2377 <property name="topMargin">
2378 <number>3</number>
2379 </property>
2380 <property name="rightMargin">
2381 <number>3</number>
2382 </property>
2383 <property name="bottomMargin">
2384 <number>3</number>
2385 </property>
2386 <item>
2387 <widget class="QPushButton" name="motionButton">
2388 <property name="minimumSize">
2389 <size>
2390 <width>57</width>
2391 <height>0</height>
2392 </size>
2393 </property>
2394 <property name="maximumSize">
2395 <size>
2396 <width>55</width>
2397 <height>16777215</height>
2398 </size>
2399 </property>
2400 <property name="styleSheet">
2401 <string notr="true">min-width: 55px;</string>
2402 </property>
2403 <property name="text">
2404 <string>Configure</string>
2405 </property>
2406 </widget>
2407 </item>
2408 </layout>
2409 </widget>
2410 </item>
2411 <item>
2412 <widget class="QGroupBox" name="inputConfigGroup">
2413 <property name="title">
2414 <string>Input Config</string>
2415 </property>
2416 <layout class="QHBoxLayout" name="horizontalLayout_7">
2417 <property name="leftMargin">
2418 <number>3</number>
2419 </property>
2420 <property name="topMargin">
2421 <number>3</number>
2422 </property>
2423 <property name="rightMargin">
2424 <number>3</number>
2425 </property>
2426 <property name="bottomMargin">
2427 <number>3</number>
2428 </property>
2429 <item>
2430 <widget class="QPushButton" name="inputConfigButton">
2431 <property name="maximumSize">
2432 <size>
2433 <width>65</width>
2434 <height>16777215</height>
2435 </size>
2436 </property>
2437 <property name="styleSheet">
2438 <string notr="true">min-width: 55px;</string>
2439 </property>
2440 <property name="text">
2441 <string>Open</string>
2442 </property>
2443 </widget>
2444 </item>
2445 </layout>
2446 </widget>
2447 </item>
2448 <item>
2449 <widget class="QWidget" name="connectedControllers" native="true">
2450 <layout class="QGridLayout" name="gridLayout_2">
2451 <property name="leftMargin">
2452 <number>5</number>
2453 </property>
2454 <property name="topMargin">
2455 <number>0</number>
2456 </property>
2457 <property name="rightMargin">
2458 <number>0</number>
2459 </property>
2460 <property name="bottomMargin">
2461 <number>0</number>
2462 </property>
2463 <property name="spacing">
2464 <number>3</number>
2465 </property>
2466 <item row="1" column="4">
2467 <widget class="QCheckBox" name="checkboxPlayer4Connected">
2468 <property name="text">
2469 <string/>
2470 </property>
2471 </widget>
2472 </item>
2473 <item row="1" column="0">
2474 <widget class="QLabel" name="labelControllers">
2475 <property name="text">
2476 <string>Controllers</string>
2477 </property>
2478 </widget>
2479 </item>
2480 <item row="1" column="2">
2481 <widget class="QCheckBox" name="checkboxPlayer2Connected">
2482 <property name="text">
2483 <string/>
2484 </property>
2485 </widget>
2486 </item>
2487 <item row="0" column="1">
2488 <widget class="QLabel" name="labelConnectedPlayer1">
2489 <property name="text">
2490 <string>1</string>
2491 </property>
2492 <property name="alignment">
2493 <set>Qt::AlignCenter</set>
2494 </property>
2495 </widget>
2496 </item>
2497 <item row="1" column="3">
2498 <widget class="QCheckBox" name="checkboxPlayer3Connected">
2499 <property name="text">
2500 <string/>
2501 </property>
2502 </widget>
2503 </item>
2504 <item row="1" column="1">
2505 <widget class="QCheckBox" name="checkboxPlayer1Connected">
2506 <property name="layoutDirection">
2507 <enum>Qt::LeftToRight</enum>
2508 </property>
2509 <property name="checked">
2510 <bool>false</bool>
2511 </property>
2512 </widget>
2513 </item>
2514 <item row="0" column="2">
2515 <widget class="QLabel" name="labelConnectedPlayer2">
2516 <property name="text">
2517 <string>2</string>
2518 </property>
2519 <property name="alignment">
2520 <set>Qt::AlignCenter</set>
2521 </property>
2522 </widget>
2523 </item>
2524 <item row="0" column="4">
2525 <widget class="QLabel" name="labelConnectedPlayer4">
2526 <property name="text">
2527 <string>4</string>
2528 </property>
2529 <property name="alignment">
2530 <set>Qt::AlignCenter</set>
2531 </property>
2532 </widget>
2533 </item>
2534 <item row="0" column="3">
2535 <widget class="QLabel" name="labelConnectedPlayer3">
2536 <property name="text">
2537 <string>3</string>
2538 </property>
2539 <property name="alignment">
2540 <set>Qt::AlignCenter</set>
2541 </property>
2542 </widget>
2543 </item>
2544 <item row="0" column="0">
2545 <widget class="QLabel" name="labelConnected">
2546 <property name="text">
2547 <string>Connected</string>
2548 </property>
2549 </widget>
2550 </item>
2551 <item row="1" column="7">
2552 <widget class="QCheckBox" name="checkboxPlayer7Connected">
2553 <property name="text">
2554 <string/>
2555 </property>
2556 </widget>
2557 </item>
2558 <item row="0" column="5">
2559 <widget class="QLabel" name="labelConnectedPlayer5">
2560 <property name="text">
2561 <string>5</string>
2562 </property>
2563 <property name="alignment">
2564 <set>Qt::AlignCenter</set>
2565 </property>
2566 </widget>
2567 </item>
2568 <item row="1" column="6">
2569 <widget class="QCheckBox" name="checkboxPlayer6Connected">
2570 <property name="text">
2571 <string/>
2572 </property>
2573 </widget>
2574 </item>
2575 <item row="0" column="7">
2576 <widget class="QLabel" name="labelConnectedPlayer7">
2577 <property name="text">
2578 <string>7</string>
2579 </property>
2580 <property name="alignment">
2581 <set>Qt::AlignCenter</set>
2582 </property>
2583 </widget>
2584 </item>
2585 <item row="1" column="5">
2586 <widget class="QCheckBox" name="checkboxPlayer5Connected">
2587 <property name="text">
2588 <string/>
2589 </property>
2590 </widget>
2591 </item>
2592 <item row="0" column="6">
2593 <widget class="QLabel" name="labelConnectedPlayer6">
2594 <property name="text">
2595 <string>6</string>
2596 </property>
2597 <property name="alignment">
2598 <set>Qt::AlignCenter</set>
2599 </property>
2600 </widget>
2601 </item>
2602 <item row="0" column="8">
2603 <widget class="QLabel" name="labelConnectedPlayer8">
2604 <property name="text">
2605 <string>8</string>
2606 </property>
2607 <property name="alignment">
2608 <set>Qt::AlignCenter</set>
2609 </property>
2610 </widget>
2611 </item>
2612 <item row="1" column="8">
2613 <widget class="QCheckBox" name="checkboxPlayer8Connected">
2614 <property name="text">
2615 <string/>
2616 </property>
2617 </widget>
2618 </item>
2619 </layout>
2620 </widget>
2621 </item>
2622 <item>
2623 <spacer name="controllerAppletHorizontalSpacer">
2624 <property name="orientation">
2625 <enum>Qt::Horizontal</enum>
2626 </property>
2627 <property name="sizeHint" stdset="0">
2628 <size>
2629 <width>0</width>
2630 <height>20</height>
2631 </size>
2632 </property>
2633 </spacer>
2634 </item>
2635 <item alignment="Qt::AlignBottom">
2636 <widget class="QDialogButtonBox" name="buttonBox">
2637 <property name="enabled">
2638 <bool>true</bool>
2639 </property>
2640 <property name="standardButtons">
2641 <set>QDialogButtonBox::Ok</set>
2642 </property>
2643 </widget>
2644 </item>
2645 </layout>
2646 </widget>
2647 </item>
2648 </layout>
2649 </widget>
2650 </item>
2651 </layout>
2652 </widget>
2653 <resources/>
2654 <connections>
2655 <connection>
2656 <sender>buttonBox</sender>
2657 <signal>accepted()</signal>
2658 <receiver>QtControllerSelectorDialog</receiver>
2659 <slot>accept()</slot>
2660 <hints>
2661 <hint type="sourcelabel">
2662 <x>20</x>
2663 <y>20</y>
2664 </hint>
2665 <hint type="destinationlabel">
2666 <x>20</x>
2667 <y>20</y>
2668 </hint>
2669 </hints>
2670 </connection>
2671 </connections>
2672</ui>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index ae3e31762..7ea17a4db 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -70,7 +70,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
70 70
71ConfigureInput::~ConfigureInput() = default; 71ConfigureInput::~ConfigureInput() = default;
72 72
73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) { 73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
74 std::size_t max_players) {
74 player_controllers = { 75 player_controllers = {
75 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem), 76 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
76 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem), 77 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
@@ -93,6 +94,11 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
93 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, 94 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
94 }; 95 };
95 96
97 std::array<QLabel*, 8> player_connected_labels = {
98 ui->label, ui->label_3, ui->label_4, ui->label_5,
99 ui->label_6, ui->label_7, ui->label_8, ui->label_9,
100 };
101
96 for (std::size_t i = 0; i < player_tabs.size(); ++i) { 102 for (std::size_t i = 0; i < player_tabs.size(); ++i) {
97 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); 103 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
98 player_tabs[i]->layout()->addWidget(player_controllers[i]); 104 player_tabs[i]->layout()->addWidget(player_controllers[i]);
@@ -112,6 +118,13 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
112 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { 118 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
113 player_controllers[i]->ConnectPlayer(state == Qt::Checked); 119 player_controllers[i]->ConnectPlayer(state == Qt::Checked);
114 }); 120 });
121
122 // Remove/hide all the elements that exceed max_players, if applicable.
123 if (i >= max_players) {
124 ui->tabWidget->removeTab(static_cast<int>(max_players));
125 player_connected[i]->hide();
126 player_connected_labels[i]->hide();
127 }
115 } 128 }
116 // Only the first player can choose handheld mode so connect the signal just to player 1 129 // Only the first player can choose handheld mode so connect the signal just to player 1
117 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, 130 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
@@ -175,8 +188,7 @@ void ConfigureInput::RetranslateUI() {
175 188
176void ConfigureInput::LoadConfiguration() { 189void ConfigureInput::LoadConfiguration() {
177 LoadPlayerControllerIndices(); 190 LoadPlayerControllerIndices();
178 UpdateDockedState(Settings::values.players[0].controller_type == 191 UpdateDockedState(Settings::values.players[8].connected);
179 Settings::ControllerType::Handheld);
180 192
181 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); 193 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
182} 194}
@@ -208,14 +220,14 @@ void ConfigureInput::RestoreDefaults() {
208} 220}
209 221
210void ConfigureInput::UpdateDockedState(bool is_handheld) { 222void ConfigureInput::UpdateDockedState(bool is_handheld) {
211 // If the controller type is handheld only, disallow changing docked mode 223 // Disallow changing the console mode if the controller type is handheld.
212 ui->radioDocked->setEnabled(!is_handheld); 224 ui->radioDocked->setEnabled(!is_handheld);
213 ui->radioUndocked->setEnabled(!is_handheld); 225 ui->radioUndocked->setEnabled(!is_handheld);
214 226
215 ui->radioDocked->setChecked(Settings::values.use_docked_mode); 227 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
216 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); 228 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
217 229
218 // If its handheld only, force docked mode off (since you can't play handheld in a dock) 230 // Also force into undocked mode if the controller type is handheld.
219 if (is_handheld) { 231 if (is_handheld) {
220 ui->radioUndocked->setChecked(true); 232 ui->radioUndocked->setChecked(true);
221 } 233 }
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index d08a24f96..0e8b2fd4e 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -37,7 +37,7 @@ public:
37 ~ConfigureInput() override; 37 ~ConfigureInput() override;
38 38
39 /// Initializes the input dialog with the given input subsystem. 39 /// Initializes the input dialog with the given input subsystem.
40 void Initialize(InputCommon::InputSubsystem* input_subsystem_); 40 void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8);
41 41
42 /// Save all button configurations to settings file. 42 /// Save all button configurations to settings file.
43 void ApplyConfiguration(); 43 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input_dialog.cpp b/src/yuzu/configuration/configure_input_dialog.cpp
new file mode 100644
index 000000000..1866003c2
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_dialog.cpp
@@ -0,0 +1,37 @@
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_input_dialog.h"
6#include "yuzu/configuration/configure_input_dialog.h"
7
8ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players,
9 InputCommon::InputSubsystem* input_subsystem)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()),
11 input_widget(new ConfigureInput(this)) {
12 ui->setupUi(this);
13
14 input_widget->Initialize(input_subsystem, max_players);
15
16 ui->inputLayout->addWidget(input_widget);
17
18 RetranslateUI();
19}
20
21ConfigureInputDialog::~ConfigureInputDialog() = default;
22
23void ConfigureInputDialog::ApplyConfiguration() {
24 input_widget->ApplyConfiguration();
25}
26
27void ConfigureInputDialog::changeEvent(QEvent* event) {
28 if (event->type() == QEvent::LanguageChange) {
29 RetranslateUI();
30 }
31
32 QDialog::changeEvent(event);
33}
34
35void ConfigureInputDialog::RetranslateUI() {
36 ui->retranslateUi(this);
37}
diff --git a/src/yuzu/configuration/configure_input_dialog.h b/src/yuzu/configuration/configure_input_dialog.h
new file mode 100644
index 000000000..d1bd865f9
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_dialog.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.h"
10
11class QPushButton;
12
13namespace InputCommon {
14class InputSubsystem;
15}
16
17namespace Ui {
18class ConfigureInputDialog;
19}
20
21class ConfigureInputDialog : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players,
26 InputCommon::InputSubsystem* input_subsystem);
27 ~ConfigureInputDialog() override;
28
29 void ApplyConfiguration();
30
31private:
32 void changeEvent(QEvent* event) override;
33 void RetranslateUI();
34
35 std::unique_ptr<Ui::ConfigureInputDialog> ui;
36
37 ConfigureInput* input_widget;
38};
diff --git a/src/yuzu/configuration/configure_input_dialog.ui b/src/yuzu/configuration/configure_input_dialog.ui
new file mode 100644
index 000000000..b92ddb200
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_dialog.ui
@@ -0,0 +1,57 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputDialog</class>
4 <widget class="QDialog" name="ConfigureInputDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>70</width>
10 <height>540</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</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="inputLayout"/>
34 </item>
35 <item>
36 <layout class="QHBoxLayout" name="horizontalLayout">
37 <item>
38 <widget class="QDialogButtonBox" name="buttonBox">
39 <property name="standardButtons">
40 <set>QDialogButtonBox::Ok</set>
41 </property>
42 </widget>
43 </item>
44 </layout>
45 </item>
46 </layout>
47 </widget>
48 <resources/>
49 <connections>
50 <connection>
51 <sender>buttonBox</sender>
52 <signal>accepted()</signal>
53 <receiver>ConfigureInputDialog</receiver>
54 <slot>accept()</slot>
55 </connection>
56 </connections>
57</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a1b61d119..68ad43a80 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -11,6 +11,7 @@
11#endif 11#endif
12 12
13// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 13// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
14#include "applets/controller.h"
14#include "applets/error.h" 15#include "applets/error.h"
15#include "applets/profile_select.h" 16#include "applets/profile_select.h"
16#include "applets/software_keyboard.h" 17#include "applets/software_keyboard.h"
@@ -19,7 +20,9 @@
19#include "configuration/configure_per_game.h" 20#include "configuration/configure_per_game.h"
20#include "core/file_sys/vfs.h" 21#include "core/file_sys/vfs.h"
21#include "core/file_sys/vfs_real.h" 22#include "core/file_sys/vfs_real.h"
23#include "core/frontend/applets/controller.h"
22#include "core/frontend/applets/general_frontend.h" 24#include "core/frontend/applets/general_frontend.h"
25#include "core/frontend/applets/software_keyboard.h"
23#include "core/hle/service/acc/profile_manager.h" 26#include "core/hle/service/acc/profile_manager.h"
24#include "core/hle/service/am/applet_ae.h" 27#include "core/hle/service/am/applet_ae.h"
25#include "core/hle/service/am/applet_oe.h" 28#include "core/hle/service/am/applet_oe.h"
@@ -84,7 +87,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
84#include "core/file_sys/romfs.h" 87#include "core/file_sys/romfs.h"
85#include "core/file_sys/savedata_factory.h" 88#include "core/file_sys/savedata_factory.h"
86#include "core/file_sys/submission_package.h" 89#include "core/file_sys/submission_package.h"
87#include "core/frontend/applets/software_keyboard.h"
88#include "core/hle/kernel/process.h" 90#include "core/hle/kernel/process.h"
89#include "core/hle/service/am/am.h" 91#include "core/hle/service/am/am.h"
90#include "core/hle/service/filesystem/filesystem.h" 92#include "core/hle/service/filesystem/filesystem.h"
@@ -283,6 +285,23 @@ GMainWindow::~GMainWindow() {
283 delete render_window; 285 delete render_window;
284} 286}
285 287
288void GMainWindow::ControllerSelectorReconfigureControllers(
289 const Core::Frontend::ControllerParameters& parameters) {
290 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
291 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
292 Qt::WindowSystemMenuHint);
293 dialog.setWindowModality(Qt::WindowModal);
294 dialog.exec();
295
296 emit ControllerSelectorReconfigureFinished();
297
298 // Don't forget to apply settings.
299 Settings::Apply();
300 config->Save();
301
302 UpdateStatusButtons();
303}
304
286void GMainWindow::ProfileSelectorSelectProfile() { 305void GMainWindow::ProfileSelectorSelectProfile() {
287 const Service::Account::ProfileManager manager; 306 const Service::Account::ProfileManager manager;
288 int index = 0; 307 int index = 0;
@@ -291,10 +310,12 @@ void GMainWindow::ProfileSelectorSelectProfile() {
291 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 310 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
292 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 311 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
293 dialog.setWindowModality(Qt::WindowModal); 312 dialog.setWindowModality(Qt::WindowModal);
313
294 if (dialog.exec() == QDialog::Rejected) { 314 if (dialog.exec() == QDialog::Rejected) {
295 emit ProfileSelectorFinishedSelection(std::nullopt); 315 emit ProfileSelectorFinishedSelection(std::nullopt);
296 return; 316 return;
297 } 317 }
318
298 index = dialog.GetIndex(); 319 index = dialog.GetIndex();
299 } 320 }
300 321
@@ -966,13 +987,14 @@ bool GMainWindow::LoadROM(const QString& filename) {
966 system.SetFilesystem(vfs); 987 system.SetFilesystem(vfs);
967 988
968 system.SetAppletFrontendSet({ 989 system.SetAppletFrontendSet({
969 nullptr, // Parental Controls 990 std::make_unique<QtControllerSelector>(*this), // Controller Selector
970 std::make_unique<QtErrorDisplay>(*this), // 991 nullptr, // E-Commerce
971 nullptr, // Photo Viewer 992 std::make_unique<QtErrorDisplay>(*this), // Error Display
972 std::make_unique<QtProfileSelector>(*this), // 993 nullptr, // Parental Controls
973 std::make_unique<QtSoftwareKeyboard>(*this), // 994 nullptr, // Photo Viewer
974 std::make_unique<QtWebBrowser>(*this), // 995 std::make_unique<QtProfileSelector>(*this), // Profile Selector
975 nullptr, // E-Commerce 996 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
997 std::make_unique<QtWebBrowser>(*this), // Web Browser
976 }); 998 });
977 999
978 system.RegisterHostThread(); 1000 system.RegisterHostThread();
@@ -2047,6 +2069,7 @@ void GMainWindow::OnStartGame() {
2047 2069
2048 emu_thread->SetRunning(true); 2070 emu_thread->SetRunning(true);
2049 2071
2072 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
2050 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>( 2073 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
2051 "Core::Frontend::SoftwareKeyboardParameters"); 2074 "Core::Frontend::SoftwareKeyboardParameters");
2052 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); 2075 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0ce66a1ca..afcfa68a9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -37,6 +37,7 @@ enum class InstalledEntryType;
37class GameListPlaceholder; 37class GameListPlaceholder;
38 38
39namespace Core::Frontend { 39namespace Core::Frontend {
40struct ControllerParameters;
40struct SoftwareKeyboardParameters; 41struct SoftwareKeyboardParameters;
41} // namespace Core::Frontend 42} // namespace Core::Frontend
42 43
@@ -116,9 +117,12 @@ signals:
116 117
117 void UpdateInstallProgress(); 118 void UpdateInstallProgress();
118 119
120 void ControllerSelectorReconfigureFinished();
121
119 void ErrorDisplayFinished(); 122 void ErrorDisplayFinished();
120 123
121 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); 124 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
125
122 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); 126 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
123 void SoftwareKeyboardFinishedCheckDialog(); 127 void SoftwareKeyboardFinishedCheckDialog();
124 128
@@ -127,6 +131,8 @@ signals:
127 131
128public slots: 132public slots:
129 void OnLoadComplete(); 133 void OnLoadComplete();
134 void ControllerSelectorReconfigureControllers(
135 const Core::Frontend::ControllerParameters& parameters);
130 void ErrorDisplayDisplayError(QString body); 136 void ErrorDisplayDisplayError(QString body);
131 void ProfileSelectorSelectProfile(); 137 void ProfileSelectorSelectProfile();
132 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); 138 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);