summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2020-09-10 19:28:23 -0400
committerGravatar GitHub2020-09-10 19:28:23 -0400
commit03179ecafe8a5e9dedcab57f593f47ecc71b6877 (patch)
treeac862b62f9d1e8c3c50915dcefc3f34863140e93 /src/core
parentMerge pull request #4608 from lioncash/sign3 (diff)
parentResolve spacing inconsistencies in style.qrc/qss files (diff)
downloadyuzu-03179ecafe8a5e9dedcab57f593f47ecc71b6877.tar.gz
yuzu-03179ecafe8a5e9dedcab57f593f47ecc71b6877.tar.xz
yuzu-03179ecafe8a5e9dedcab57f593f47ecc71b6877.zip
Merge pull request #4597 from Morph1984/mjolnir-p2
Project Mjölnir: Part 2 - Controller Applet
Diffstat (limited to 'src/core')
-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
9 files changed, 548 insertions, 131 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{};