controller.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <thread>
  6. #include "common/assert.h"
  7. #include "common/string_util.h"
  8. #include "core/core.h"
  9. #include "core/hle/lock.h"
  10. #include "core/hle/service/hid/controllers/npad.h"
  11. #include "core/hle/service/hid/hid.h"
  12. #include "core/hle/service/sm/sm.h"
  13. #include "ui_controller.h"
  14. #include "yuzu/applets/controller.h"
  15. #include "yuzu/configuration/configure_input.h"
  16. #include "yuzu/configuration/configure_input_profile_dialog.h"
  17. #include "yuzu/configuration/configure_vibration.h"
  18. #include "yuzu/configuration/input_profiles.h"
  19. #include "yuzu/main.h"
  20. namespace {
  21. constexpr std::size_t HANDHELD_INDEX = 8;
  22. constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
  23. {true, false, false, false},
  24. {true, true, false, false},
  25. {true, true, true, false},
  26. {true, true, true, true},
  27. {true, false, false, true},
  28. {true, false, true, false},
  29. {true, false, true, true},
  30. {false, true, true, false},
  31. }};
  32. void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
  33. bool connected) {
  34. Core::System& system{Core::System::GetInstance()};
  35. if (!system.IsPoweredOn()) {
  36. return;
  37. }
  38. Service::SM::ServiceManager& sm = system.ServiceManager();
  39. auto& npad =
  40. sm.GetService<Service::HID::Hid>("hid")
  41. ->GetAppletResource()
  42. ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
  43. npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
  44. }
  45. // Returns true if the given controller type is compatible with the given parameters.
  46. bool IsControllerCompatible(Settings::ControllerType controller_type,
  47. Core::Frontend::ControllerParameters parameters) {
  48. switch (controller_type) {
  49. case Settings::ControllerType::ProController:
  50. return parameters.allow_pro_controller;
  51. case Settings::ControllerType::DualJoyconDetached:
  52. return parameters.allow_dual_joycons;
  53. case Settings::ControllerType::LeftJoycon:
  54. return parameters.allow_left_joycon;
  55. case Settings::ControllerType::RightJoycon:
  56. return parameters.allow_right_joycon;
  57. case Settings::ControllerType::Handheld:
  58. return parameters.enable_single_mode && parameters.allow_handheld;
  59. default:
  60. return false;
  61. }
  62. }
  63. } // namespace
  64. QtControllerSelectorDialog::QtControllerSelectorDialog(
  65. QWidget* parent, Core::Frontend::ControllerParameters parameters_,
  66. InputCommon::InputSubsystem* input_subsystem_)
  67. : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
  68. parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
  69. input_profiles(std::make_unique<InputProfiles>()) {
  70. ui->setupUi(this);
  71. player_widgets = {
  72. ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
  73. ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
  74. };
  75. player_groupboxes = {
  76. ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
  77. ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
  78. ui->groupPlayer7Connected, ui->groupPlayer8Connected,
  79. };
  80. connected_controller_icons = {
  81. ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
  82. ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
  83. };
  84. led_patterns_boxes = {{
  85. {ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
  86. ui->checkboxPlayer1LED4},
  87. {ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
  88. ui->checkboxPlayer2LED4},
  89. {ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
  90. ui->checkboxPlayer3LED4},
  91. {ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
  92. ui->checkboxPlayer4LED4},
  93. {ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
  94. ui->checkboxPlayer5LED4},
  95. {ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
  96. ui->checkboxPlayer6LED4},
  97. {ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
  98. ui->checkboxPlayer7LED4},
  99. {ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
  100. ui->checkboxPlayer8LED4},
  101. }};
  102. explain_text_labels = {
  103. ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
  104. ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
  105. ui->labelPlayer7Explain, ui->labelPlayer8Explain,
  106. };
  107. emulated_controllers = {
  108. ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
  109. ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
  110. ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
  111. };
  112. player_labels = {
  113. ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
  114. ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
  115. };
  116. connected_controller_labels = {
  117. ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
  118. ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
  119. ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
  120. };
  121. connected_controller_checkboxes = {
  122. ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
  123. ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
  124. ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
  125. };
  126. // Setup/load everything prior to setting up connections.
  127. // This avoids unintentionally changing the states of elements while loading them in.
  128. SetSupportedControllers();
  129. DisableUnsupportedPlayers();
  130. for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
  131. SetEmulatedControllers(player_index);
  132. }
  133. LoadConfiguration();
  134. for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
  135. SetExplainText(i);
  136. UpdateControllerIcon(i);
  137. UpdateLEDPattern(i);
  138. UpdateBorderColor(i);
  139. connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
  140. if (checked) {
  141. for (std::size_t index = 0; index <= i; ++index) {
  142. connected_controller_checkboxes[index]->setChecked(checked);
  143. }
  144. } else {
  145. for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
  146. connected_controller_checkboxes[index]->setChecked(checked);
  147. }
  148. }
  149. });
  150. connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
  151. [this, i](int) {
  152. UpdateControllerIcon(i);
  153. UpdateControllerState(i);
  154. UpdateLEDPattern(i);
  155. CheckIfParametersMet();
  156. });
  157. connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
  158. player_groupboxes[i]->setChecked(state == Qt::Checked);
  159. UpdateControllerIcon(i);
  160. UpdateControllerState(i);
  161. UpdateLEDPattern(i);
  162. UpdateBorderColor(i);
  163. CheckIfParametersMet();
  164. });
  165. if (i == 0) {
  166. connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
  167. [this, i](int index) {
  168. UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
  169. Settings::ControllerType::Handheld);
  170. });
  171. }
  172. }
  173. connect(ui->vibrationButton, &QPushButton::clicked, this,
  174. &QtControllerSelectorDialog::CallConfigureVibrationDialog);
  175. connect(ui->inputConfigButton, &QPushButton::clicked, this,
  176. &QtControllerSelectorDialog::CallConfigureInputProfileDialog);
  177. connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
  178. &QtControllerSelectorDialog::ApplyConfiguration);
  179. // Enhancement: Check if the parameters have already been met before disconnecting controllers.
  180. // If all the parameters are met AND only allows a single player,
  181. // stop the constructor here as we do not need to continue.
  182. if (CheckIfParametersMet() && parameters.enable_single_mode) {
  183. return;
  184. }
  185. // If keep_controllers_connected is false, forcefully disconnect all controllers
  186. if (!parameters.keep_controllers_connected) {
  187. for (auto player : player_groupboxes) {
  188. player->setChecked(false);
  189. }
  190. }
  191. resize(0, 0);
  192. }
  193. QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
  194. int QtControllerSelectorDialog::exec() {
  195. if (parameters_met && parameters.enable_single_mode) {
  196. return QDialog::Accepted;
  197. }
  198. return QDialog::exec();
  199. }
  200. void QtControllerSelectorDialog::ApplyConfiguration() {
  201. const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
  202. Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
  203. OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
  204. Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
  205. Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
  206. }
  207. void QtControllerSelectorDialog::LoadConfiguration() {
  208. for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
  209. const auto connected =
  210. Settings::values.players.GetValue()[index].connected ||
  211. (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
  212. player_groupboxes[index]->setChecked(connected);
  213. connected_controller_checkboxes[index]->setChecked(connected);
  214. emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
  215. Settings::values.players.GetValue()[index].controller_type, index));
  216. }
  217. UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
  218. ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
  219. ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
  220. }
  221. void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
  222. ConfigureVibration dialog(this);
  223. dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
  224. Qt::WindowSystemMenuHint);
  225. dialog.setWindowModality(Qt::WindowModal);
  226. if (dialog.exec() == QDialog::Accepted) {
  227. dialog.ApplyConfiguration();
  228. }
  229. }
  230. void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
  231. ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get());
  232. dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
  233. Qt::WindowSystemMenuHint);
  234. dialog.setWindowModality(Qt::WindowModal);
  235. dialog.exec();
  236. }
  237. bool QtControllerSelectorDialog::CheckIfParametersMet() {
  238. // Here, we check and validate the current configuration against all applicable parameters.
  239. const auto num_connected_players = static_cast<int>(
  240. std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
  241. [this](const QGroupBox* player) { return player->isChecked(); }));
  242. const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
  243. const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
  244. // First, check against the number of connected players.
  245. if (num_connected_players < min_supported_players ||
  246. num_connected_players > max_supported_players) {
  247. parameters_met = false;
  248. ui->buttonBox->setEnabled(parameters_met);
  249. return parameters_met;
  250. }
  251. // Next, check against all connected controllers.
  252. const auto all_controllers_compatible = [this] {
  253. for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
  254. // Skip controllers that are not used, we only care about the currently connected ones.
  255. if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
  256. continue;
  257. }
  258. const auto compatible = IsControllerCompatible(
  259. GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
  260. parameters);
  261. // If any controller is found to be incompatible, return false early.
  262. if (!compatible) {
  263. return false;
  264. }
  265. }
  266. // Reaching here means all currently connected controllers are compatible.
  267. return true;
  268. }();
  269. parameters_met = all_controllers_compatible;
  270. ui->buttonBox->setEnabled(parameters_met);
  271. return parameters_met;
  272. }
  273. void QtControllerSelectorDialog::SetSupportedControllers() {
  274. const QString theme = [] {
  275. if (QIcon::themeName().contains(QStringLiteral("dark"))) {
  276. return QStringLiteral("_dark");
  277. } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
  278. return QStringLiteral("_midnight");
  279. } else {
  280. return QString{};
  281. }
  282. }();
  283. if (parameters.enable_single_mode && parameters.allow_handheld) {
  284. ui->controllerSupported1->setStyleSheet(
  285. QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
  286. } else {
  287. ui->controllerSupported1->setStyleSheet(
  288. QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
  289. }
  290. if (parameters.allow_dual_joycons) {
  291. ui->controllerSupported2->setStyleSheet(
  292. QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
  293. } else {
  294. ui->controllerSupported2->setStyleSheet(
  295. QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
  296. }
  297. if (parameters.allow_left_joycon) {
  298. ui->controllerSupported3->setStyleSheet(
  299. QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
  300. } else {
  301. ui->controllerSupported3->setStyleSheet(
  302. QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
  303. }
  304. if (parameters.allow_right_joycon) {
  305. ui->controllerSupported4->setStyleSheet(
  306. QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
  307. } else {
  308. ui->controllerSupported4->setStyleSheet(
  309. QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
  310. }
  311. if (parameters.allow_pro_controller) {
  312. ui->controllerSupported5->setStyleSheet(
  313. QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
  314. } else {
  315. ui->controllerSupported5->setStyleSheet(
  316. QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
  317. .arg(theme));
  318. }
  319. // enable_single_mode overrides min_players and max_players.
  320. if (parameters.enable_single_mode) {
  321. ui->numberSupportedLabel->setText(QStringLiteral("1"));
  322. return;
  323. }
  324. if (parameters.min_players == parameters.max_players) {
  325. ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
  326. } else {
  327. ui->numberSupportedLabel->setText(
  328. QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
  329. }
  330. }
  331. void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
  332. auto& pairs = index_controller_type_pairs[player_index];
  333. pairs.clear();
  334. emulated_controllers[player_index]->clear();
  335. pairs.emplace_back(emulated_controllers[player_index]->count(),
  336. Settings::ControllerType::ProController);
  337. emulated_controllers[player_index]->addItem(tr("Pro Controller"));
  338. pairs.emplace_back(emulated_controllers[player_index]->count(),
  339. Settings::ControllerType::DualJoyconDetached);
  340. emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
  341. pairs.emplace_back(emulated_controllers[player_index]->count(),
  342. Settings::ControllerType::LeftJoycon);
  343. emulated_controllers[player_index]->addItem(tr("Left Joycon"));
  344. pairs.emplace_back(emulated_controllers[player_index]->count(),
  345. Settings::ControllerType::RightJoycon);
  346. emulated_controllers[player_index]->addItem(tr("Right Joycon"));
  347. if (player_index == 0) {
  348. pairs.emplace_back(emulated_controllers[player_index]->count(),
  349. Settings::ControllerType::Handheld);
  350. emulated_controllers[player_index]->addItem(tr("Handheld"));
  351. }
  352. }
  353. Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
  354. int index, std::size_t player_index) const {
  355. const auto& pairs = index_controller_type_pairs[player_index];
  356. const auto it = std::find_if(pairs.begin(), pairs.end(),
  357. [index](const auto& pair) { return pair.first == index; });
  358. if (it == pairs.end()) {
  359. return Settings::ControllerType::ProController;
  360. }
  361. return it->second;
  362. }
  363. int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
  364. std::size_t player_index) const {
  365. const auto& pairs = index_controller_type_pairs[player_index];
  366. const auto it = std::find_if(pairs.begin(), pairs.end(),
  367. [type](const auto& pair) { return pair.second == type; });
  368. if (it == pairs.end()) {
  369. return 0;
  370. }
  371. return it->first;
  372. }
  373. void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
  374. if (!player_groupboxes[player_index]->isChecked()) {
  375. connected_controller_icons[player_index]->setStyleSheet(QString{});
  376. player_labels[player_index]->show();
  377. return;
  378. }
  379. const QString stylesheet = [this, player_index] {
  380. switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
  381. player_index)) {
  382. case Settings::ControllerType::ProController:
  383. return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
  384. case Settings::ControllerType::DualJoyconDetached:
  385. return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
  386. case Settings::ControllerType::LeftJoycon:
  387. return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
  388. case Settings::ControllerType::RightJoycon:
  389. return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
  390. case Settings::ControllerType::Handheld:
  391. return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
  392. default:
  393. return QString{};
  394. }
  395. }();
  396. if (stylesheet.isEmpty()) {
  397. connected_controller_icons[player_index]->setStyleSheet(QString{});
  398. player_labels[player_index]->show();
  399. return;
  400. }
  401. const QString theme = [] {
  402. if (QIcon::themeName().contains(QStringLiteral("dark"))) {
  403. return QStringLiteral("_dark");
  404. } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
  405. return QStringLiteral("_midnight");
  406. } else {
  407. return QString{};
  408. }
  409. }();
  410. connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
  411. player_labels[player_index]->hide();
  412. }
  413. void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
  414. auto& player = Settings::values.players.GetValue()[player_index];
  415. const auto controller_type = GetControllerTypeFromIndex(
  416. emulated_controllers[player_index]->currentIndex(), player_index);
  417. const auto player_connected = player_groupboxes[player_index]->isChecked() &&
  418. controller_type != Settings::ControllerType::Handheld;
  419. if (player.controller_type == controller_type && player.connected == player_connected) {
  420. // Set vibration devices in the event that the input device has changed.
  421. ConfigureVibration::SetVibrationDevices(player_index);
  422. return;
  423. }
  424. // Disconnect the controller first.
  425. UpdateController(controller_type, player_index, false);
  426. player.controller_type = controller_type;
  427. player.connected = player_connected;
  428. ConfigureVibration::SetVibrationDevices(player_index);
  429. // Handheld
  430. if (player_index == 0) {
  431. auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
  432. if (controller_type == Settings::ControllerType::Handheld) {
  433. handheld = player;
  434. }
  435. handheld.connected = player_groupboxes[player_index]->isChecked() &&
  436. controller_type == Settings::ControllerType::Handheld;
  437. UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
  438. }
  439. if (!player.connected) {
  440. return;
  441. }
  442. // This emulates a delay between disconnecting and reconnecting controllers as some games
  443. // do not respond to a change in controller type if it was instantaneous.
  444. using namespace std::chrono_literals;
  445. std::this_thread::sleep_for(60ms);
  446. UpdateController(controller_type, player_index, player_connected);
  447. }
  448. void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
  449. if (!player_groupboxes[player_index]->isChecked() ||
  450. GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
  451. player_index) == Settings::ControllerType::Handheld) {
  452. led_patterns_boxes[player_index][0]->setChecked(false);
  453. led_patterns_boxes[player_index][1]->setChecked(false);
  454. led_patterns_boxes[player_index][2]->setChecked(false);
  455. led_patterns_boxes[player_index][3]->setChecked(false);
  456. return;
  457. }
  458. led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
  459. led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
  460. led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
  461. led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
  462. }
  463. void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
  464. if (!parameters.enable_border_color ||
  465. player_index >= static_cast<std::size_t>(parameters.max_players) ||
  466. player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
  467. return;
  468. }
  469. player_groupboxes[player_index]->setStyleSheet(
  470. player_groupboxes[player_index]->styleSheet().append(
  471. QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
  472. "{ border: 1px solid rgba(%2, %3, %4, %5); }")
  473. .arg(player_index + 1)
  474. .arg(parameters.border_colors[player_index][0])
  475. .arg(parameters.border_colors[player_index][1])
  476. .arg(parameters.border_colors[player_index][2])
  477. .arg(parameters.border_colors[player_index][3])));
  478. }
  479. void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
  480. if (!parameters.enable_explain_text ||
  481. player_index >= static_cast<std::size_t>(parameters.max_players)) {
  482. return;
  483. }
  484. explain_text_labels[player_index]->setText(QString::fromStdString(
  485. Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
  486. parameters.explain_text[player_index].size())));
  487. }
  488. void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
  489. // Disallow changing the console mode if the controller type is handheld.
  490. ui->radioDocked->setEnabled(!is_handheld);
  491. ui->radioUndocked->setEnabled(!is_handheld);
  492. ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
  493. ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
  494. // Also force into undocked mode if the controller type is handheld.
  495. if (is_handheld) {
  496. ui->radioUndocked->setChecked(true);
  497. }
  498. }
  499. void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
  500. const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
  501. switch (max_supported_players) {
  502. case 0:
  503. default:
  504. UNREACHABLE();
  505. return;
  506. case 1:
  507. ui->widgetSpacer->hide();
  508. ui->widgetSpacer2->hide();
  509. ui->widgetSpacer3->hide();
  510. ui->widgetSpacer4->hide();
  511. break;
  512. case 2:
  513. ui->widgetSpacer->hide();
  514. ui->widgetSpacer2->hide();
  515. ui->widgetSpacer3->hide();
  516. break;
  517. case 3:
  518. ui->widgetSpacer->hide();
  519. ui->widgetSpacer2->hide();
  520. break;
  521. case 4:
  522. ui->widgetSpacer->hide();
  523. break;
  524. case 5:
  525. case 6:
  526. case 7:
  527. case 8:
  528. break;
  529. }
  530. for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
  531. // Disconnect any unsupported players here and disable or hide them if applicable.
  532. Settings::values.players.GetValue()[index].connected = false;
  533. UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false);
  534. // Hide the player widgets when max_supported_controllers is less than or equal to 4.
  535. if (max_supported_players <= 4) {
  536. player_widgets[index]->hide();
  537. }
  538. // Disable and hide the following to prevent these from interaction.
  539. player_widgets[index]->setDisabled(true);
  540. connected_controller_checkboxes[index]->setDisabled(true);
  541. connected_controller_labels[index]->hide();
  542. connected_controller_checkboxes[index]->hide();
  543. }
  544. }
  545. QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
  546. connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
  547. &GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
  548. connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
  549. &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
  550. }
  551. QtControllerSelector::~QtControllerSelector() = default;
  552. void QtControllerSelector::ReconfigureControllers(
  553. std::function<void()> callback_, const Core::Frontend::ControllerParameters& parameters) const {
  554. callback = std::move(callback_);
  555. emit MainWindowReconfigureControllers(parameters);
  556. }
  557. void QtControllerSelector::MainWindowReconfigureFinished() {
  558. // Acquire the HLE mutex
  559. std::lock_guard lock(HLE::g_hle_lock);
  560. callback();
  561. }