controller.cpp 23 KB

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