mirror of
https://github.com/yuzu-emu/yuzu-android
synced 2025-07-31 20:02:24 -07:00
Merge pull request #4597 from Morph1984/mjolnir-p2
Project Mjölnir: Part 2 - Controller Applet
This commit is contained in:
@@ -9,6 +9,9 @@ add_executable(yuzu
|
||||
about_dialog.cpp
|
||||
about_dialog.h
|
||||
aboutdialog.ui
|
||||
applets/controller.cpp
|
||||
applets/controller.h
|
||||
applets/controller.ui
|
||||
applets/error.cpp
|
||||
applets/error.h
|
||||
applets/profile_select.cpp
|
||||
@@ -62,12 +65,15 @@ add_executable(yuzu
|
||||
configuration/configure_input.cpp
|
||||
configuration/configure_input.h
|
||||
configuration/configure_input.ui
|
||||
configuration/configure_input_player.cpp
|
||||
configuration/configure_input_player.h
|
||||
configuration/configure_input_player.ui
|
||||
configuration/configure_input_advanced.cpp
|
||||
configuration/configure_input_advanced.h
|
||||
configuration/configure_input_advanced.ui
|
||||
configuration/configure_input_dialog.cpp
|
||||
configuration/configure_input_dialog.h
|
||||
configuration/configure_input_dialog.ui
|
||||
configuration/configure_input_player.cpp
|
||||
configuration/configure_input_player.h
|
||||
configuration/configure_input_player.ui
|
||||
configuration/configure_motion_touch.cpp
|
||||
configuration/configure_motion_touch.h
|
||||
configuration/configure_motion_touch.ui
|
||||
|
601
src/yuzu/applets/controller.cpp
Normal file
601
src/yuzu/applets/controller.cpp
Normal file
@@ -0,0 +1,601 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "ui_controller.h"
|
||||
#include "yuzu/applets/controller.h"
|
||||
#include "yuzu/configuration/configure_input_dialog.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<std::array<bool, 4>, 8> led_patterns = {{
|
||||
{1, 0, 0, 0},
|
||||
{1, 1, 0, 0},
|
||||
{1, 1, 1, 0},
|
||||
{1, 1, 1, 1},
|
||||
{1, 0, 0, 1},
|
||||
{1, 0, 1, 0},
|
||||
{1, 0, 1, 1},
|
||||
{0, 1, 1, 0},
|
||||
}};
|
||||
|
||||
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
|
||||
bool connected) {
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
auto& npad =
|
||||
sm.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
|
||||
|
||||
npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
|
||||
}
|
||||
|
||||
// Returns true if the given controller type is compatible with the given parameters.
|
||||
bool IsControllerCompatible(Settings::ControllerType controller_type,
|
||||
Core::Frontend::ControllerParameters parameters) {
|
||||
switch (controller_type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
return parameters.allow_pro_controller;
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return parameters.allow_dual_joycons;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return parameters.allow_left_joycon;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return parameters.allow_right_joycon;
|
||||
case Settings::ControllerType::Handheld:
|
||||
return parameters.enable_single_mode && parameters.allow_handheld;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the controller type combobox index to Controller Type enum
|
||||
constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
default:
|
||||
return Settings::ControllerType::ProController;
|
||||
case 1:
|
||||
return Settings::ControllerType::DualJoyconDetached;
|
||||
case 2:
|
||||
return Settings::ControllerType::LeftJoycon;
|
||||
case 3:
|
||||
return Settings::ControllerType::RightJoycon;
|
||||
case 4:
|
||||
return Settings::ControllerType::Handheld;
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the Controller Type enum to controller type combobox index
|
||||
constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
|
||||
switch (type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
default:
|
||||
return 0;
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return 1;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return 2;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return 3;
|
||||
case Settings::ControllerType::Handheld:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QtControllerSelectorDialog::QtControllerSelectorDialog(
|
||||
QWidget* parent, Core::Frontend::ControllerParameters parameters_,
|
||||
InputCommon::InputSubsystem* input_subsystem_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
|
||||
parameters(std::move(parameters_)), input_subsystem(input_subsystem_) {
|
||||
ui->setupUi(this);
|
||||
|
||||
player_widgets = {
|
||||
ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
|
||||
ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
|
||||
};
|
||||
|
||||
player_groupboxes = {
|
||||
ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
|
||||
ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
|
||||
ui->groupPlayer7Connected, ui->groupPlayer8Connected,
|
||||
};
|
||||
|
||||
connected_controller_icons = {
|
||||
ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
|
||||
ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
|
||||
};
|
||||
|
||||
led_patterns_boxes = {{
|
||||
{ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
|
||||
ui->checkboxPlayer1LED4},
|
||||
{ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
|
||||
ui->checkboxPlayer2LED4},
|
||||
{ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
|
||||
ui->checkboxPlayer3LED4},
|
||||
{ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
|
||||
ui->checkboxPlayer4LED4},
|
||||
{ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
|
||||
ui->checkboxPlayer5LED4},
|
||||
{ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
|
||||
ui->checkboxPlayer6LED4},
|
||||
{ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
|
||||
ui->checkboxPlayer7LED4},
|
||||
{ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
|
||||
ui->checkboxPlayer8LED4},
|
||||
}};
|
||||
|
||||
explain_text_labels = {
|
||||
ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
|
||||
ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
|
||||
ui->labelPlayer7Explain, ui->labelPlayer8Explain,
|
||||
};
|
||||
|
||||
emulated_controllers = {
|
||||
ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
|
||||
ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
|
||||
ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
|
||||
};
|
||||
|
||||
player_labels = {
|
||||
ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
|
||||
ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
|
||||
};
|
||||
|
||||
connected_controller_labels = {
|
||||
ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
|
||||
ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
|
||||
ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
|
||||
};
|
||||
|
||||
connected_controller_checkboxes = {
|
||||
ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
|
||||
ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
|
||||
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
|
||||
};
|
||||
|
||||
// Setup/load everything prior to setting up connections.
|
||||
// This avoids unintentionally changing the states of elements while loading them in.
|
||||
SetSupportedControllers();
|
||||
DisableUnsupportedPlayers();
|
||||
LoadConfiguration();
|
||||
|
||||
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
|
||||
SetExplainText(i);
|
||||
UpdateControllerIcon(i);
|
||||
UpdateLEDPattern(i);
|
||||
UpdateBorderColor(i);
|
||||
|
||||
connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
|
||||
if (checked) {
|
||||
for (std::size_t index = 0; index <= i; ++index) {
|
||||
connected_controller_checkboxes[index]->setChecked(checked);
|
||||
}
|
||||
} else {
|
||||
for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
|
||||
connected_controller_checkboxes[index]->setChecked(checked);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this, i](int) {
|
||||
UpdateControllerIcon(i);
|
||||
UpdateControllerState(i);
|
||||
UpdateLEDPattern(i);
|
||||
CheckIfParametersMet();
|
||||
});
|
||||
|
||||
connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
|
||||
player_groupboxes[i]->setChecked(state == Qt::Checked);
|
||||
UpdateControllerIcon(i);
|
||||
UpdateControllerState(i);
|
||||
UpdateLEDPattern(i);
|
||||
UpdateBorderColor(i);
|
||||
CheckIfParametersMet();
|
||||
});
|
||||
|
||||
if (i == 0) {
|
||||
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this](int index) {
|
||||
UpdateDockedState(GetControllerTypeFromIndex(index) ==
|
||||
Settings::ControllerType::Handheld);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->inputConfigButton, &QPushButton::clicked, this,
|
||||
&QtControllerSelectorDialog::CallConfigureInputDialog);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
|
||||
&QtControllerSelectorDialog::ApplyConfiguration);
|
||||
|
||||
// If keep_controllers_connected is false, forcefully disconnect all controllers
|
||||
if (!parameters.keep_controllers_connected) {
|
||||
for (auto player : player_groupboxes) {
|
||||
player->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
CheckIfParametersMet();
|
||||
|
||||
resize(0, 0);
|
||||
}
|
||||
|
||||
QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
|
||||
|
||||
void QtControllerSelectorDialog::ApplyConfiguration() {
|
||||
// Update the controller state once more, just to be sure they are properly applied.
|
||||
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
|
||||
UpdateControllerState(index);
|
||||
}
|
||||
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode;
|
||||
Settings::values.use_docked_mode = ui->radioDocked->isChecked();
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
|
||||
|
||||
Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::LoadConfiguration() {
|
||||
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
|
||||
const auto connected = Settings::values.players[index].connected ||
|
||||
(index == 0 && Settings::values.players[8].connected);
|
||||
player_groupboxes[index]->setChecked(connected);
|
||||
connected_controller_checkboxes[index]->setChecked(connected);
|
||||
emulated_controllers[index]->setCurrentIndex(
|
||||
GetIndexFromControllerType(Settings::values.players[index].controller_type));
|
||||
}
|
||||
|
||||
UpdateDockedState(Settings::values.players[8].connected);
|
||||
|
||||
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::CallConfigureInputDialog() {
|
||||
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
|
||||
ConfigureInputDialog dialog(this, max_supported_players, input_subsystem);
|
||||
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
|
||||
dialog.ApplyConfiguration();
|
||||
|
||||
LoadConfiguration();
|
||||
CheckIfParametersMet();
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::CheckIfParametersMet() {
|
||||
// Here, we check and validate the current configuration against all applicable parameters.
|
||||
const auto num_connected_players = static_cast<int>(
|
||||
std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
|
||||
[this](const QGroupBox* player) { return player->isChecked(); }));
|
||||
|
||||
const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
|
||||
// First, check against the number of connected players.
|
||||
if (num_connected_players < min_supported_players ||
|
||||
num_connected_players > max_supported_players) {
|
||||
parameters_met = false;
|
||||
ui->buttonBox->setEnabled(parameters_met);
|
||||
return;
|
||||
}
|
||||
|
||||
// Next, check against all connected controllers.
|
||||
const auto all_controllers_compatible = [this] {
|
||||
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
|
||||
// Skip controllers that are not used, we only care about the currently connected ones.
|
||||
if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto compatible = IsControllerCompatible(
|
||||
GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
|
||||
parameters);
|
||||
|
||||
// If any controller is found to be incompatible, return false early.
|
||||
if (!compatible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching here means all currently connected controllers are compatible.
|
||||
return true;
|
||||
}();
|
||||
|
||||
if (!all_controllers_compatible) {
|
||||
parameters_met = false;
|
||||
ui->buttonBox->setEnabled(parameters_met);
|
||||
return;
|
||||
}
|
||||
|
||||
parameters_met = true;
|
||||
ui->buttonBox->setEnabled(parameters_met);
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetSupportedControllers() {
|
||||
const QString theme = [this] {
|
||||
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
|
||||
return QStringLiteral("_dark");
|
||||
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
|
||||
return QStringLiteral("_midnight");
|
||||
} else {
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
if (parameters.enable_single_mode && parameters.allow_handheld) {
|
||||
ui->controllerSupported1->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported1->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_dual_joycons) {
|
||||
ui->controllerSupported2->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported2->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_left_joycon) {
|
||||
ui->controllerSupported3->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported3->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_right_joycon) {
|
||||
ui->controllerSupported4->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported4->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_pro_controller) {
|
||||
ui->controllerSupported5->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported5->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
|
||||
.arg(theme));
|
||||
}
|
||||
|
||||
// enable_single_mode overrides min_players and max_players.
|
||||
if (parameters.enable_single_mode) {
|
||||
ui->numberSupportedLabel->setText(QStringLiteral("1"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.min_players == parameters.max_players) {
|
||||
ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
|
||||
} else {
|
||||
ui->numberSupportedLabel->setText(
|
||||
QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
|
||||
if (!player_groupboxes[player_index]->isChecked()) {
|
||||
connected_controller_icons[player_index]->setStyleSheet(QString{});
|
||||
player_labels[player_index]->show();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString stylesheet = [this, player_index] {
|
||||
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
|
||||
case Settings::ControllerType::ProController:
|
||||
return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
|
||||
case Settings::ControllerType::Handheld:
|
||||
return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
|
||||
default:
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
const QString theme = [this] {
|
||||
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
|
||||
return QStringLiteral("_dark");
|
||||
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
|
||||
return QStringLiteral("_midnight");
|
||||
} else {
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
|
||||
player_labels[player_index]->hide();
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
|
||||
auto& player = Settings::values.players[player_index];
|
||||
|
||||
player.controller_type =
|
||||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
|
||||
player.connected = player_groupboxes[player_index]->isChecked();
|
||||
|
||||
// Player 2-8
|
||||
if (player_index != 0) {
|
||||
UpdateController(player.controller_type, player_index, player.connected);
|
||||
return;
|
||||
}
|
||||
|
||||
// Player 1 and Handheld
|
||||
auto& handheld = Settings::values.players[8];
|
||||
// If Handheld is selected, copy all the settings from Player 1 to Handheld.
|
||||
if (player.controller_type == Settings::ControllerType::Handheld) {
|
||||
handheld = player;
|
||||
handheld.connected = player_groupboxes[player_index]->isChecked();
|
||||
player.connected = false; // Disconnect Player 1
|
||||
} else {
|
||||
player.connected = player_groupboxes[player_index]->isChecked();
|
||||
handheld.connected = false; // Disconnect Handheld
|
||||
}
|
||||
|
||||
UpdateController(player.controller_type, player_index, player.connected);
|
||||
UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
|
||||
if (!player_groupboxes[player_index]->isChecked() ||
|
||||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
|
||||
Settings::ControllerType::Handheld) {
|
||||
led_patterns_boxes[player_index][0]->setChecked(false);
|
||||
led_patterns_boxes[player_index][1]->setChecked(false);
|
||||
led_patterns_boxes[player_index][2]->setChecked(false);
|
||||
led_patterns_boxes[player_index][3]->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
|
||||
led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
|
||||
led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
|
||||
led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
|
||||
if (!parameters.enable_border_color ||
|
||||
player_index >= static_cast<std::size_t>(parameters.max_players) ||
|
||||
player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
player_groupboxes[player_index]->setStyleSheet(
|
||||
player_groupboxes[player_index]->styleSheet().append(
|
||||
QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
|
||||
"{ border: 1px solid rgba(%2, %3, %4, %5); }")
|
||||
.arg(player_index + 1)
|
||||
.arg(parameters.border_colors[player_index][0])
|
||||
.arg(parameters.border_colors[player_index][1])
|
||||
.arg(parameters.border_colors[player_index][2])
|
||||
.arg(parameters.border_colors[player_index][3])));
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
|
||||
if (!parameters.enable_explain_text ||
|
||||
player_index >= static_cast<std::size_t>(parameters.max_players)) {
|
||||
return;
|
||||
}
|
||||
|
||||
explain_text_labels[player_index]->setText(QString::fromStdString(
|
||||
Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
|
||||
parameters.explain_text[player_index].size())));
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
|
||||
// Disallow changing the console mode if the controller type is handheld.
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode);
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
|
||||
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
ui->radioUndocked->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
|
||||
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
|
||||
switch (max_supported_players) {
|
||||
case 0:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return;
|
||||
case 1:
|
||||
ui->widgetSpacer->hide();
|
||||
ui->widgetSpacer2->hide();
|
||||
ui->widgetSpacer3->hide();
|
||||
ui->widgetSpacer4->hide();
|
||||
break;
|
||||
case 2:
|
||||
ui->widgetSpacer->hide();
|
||||
ui->widgetSpacer2->hide();
|
||||
ui->widgetSpacer3->hide();
|
||||
break;
|
||||
case 3:
|
||||
ui->widgetSpacer->hide();
|
||||
ui->widgetSpacer2->hide();
|
||||
break;
|
||||
case 4:
|
||||
ui->widgetSpacer->hide();
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
break;
|
||||
}
|
||||
|
||||
for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
|
||||
// Disconnect any unsupported players here and disable or hide them if applicable.
|
||||
Settings::values.players[index].connected = false;
|
||||
UpdateController(Settings::values.players[index].controller_type, index, false);
|
||||
// Hide the player widgets when max_supported_controllers is less than or equal to 4.
|
||||
if (max_supported_players <= 4) {
|
||||
player_widgets[index]->hide();
|
||||
}
|
||||
|
||||
// Disable and hide the following to prevent these from interaction.
|
||||
player_widgets[index]->setDisabled(true);
|
||||
connected_controller_checkboxes[index]->setDisabled(true);
|
||||
connected_controller_labels[index]->hide();
|
||||
connected_controller_checkboxes[index]->hide();
|
||||
}
|
||||
}
|
||||
|
||||
QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
|
||||
connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
|
||||
&GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
|
||||
&QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtControllerSelector::~QtControllerSelector() = default;
|
||||
|
||||
void QtControllerSelector::ReconfigureControllers(
|
||||
std::function<void()> callback, Core::Frontend::ControllerParameters parameters) const {
|
||||
this->callback = std::move(callback);
|
||||
emit MainWindowReconfigureControllers(parameters);
|
||||
}
|
||||
|
||||
void QtControllerSelector::MainWindowReconfigureFinished() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
callback();
|
||||
}
|
133
src/yuzu/applets/controller.h
Normal file
133
src/yuzu/applets/controller.h
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "core/frontend/applets/controller.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class QtControllerSelectorDialog;
|
||||
}
|
||||
|
||||
class QtControllerSelectorDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtControllerSelectorDialog(QWidget* parent,
|
||||
Core::Frontend::ControllerParameters parameters_,
|
||||
InputCommon::InputSubsystem* input_subsystem_);
|
||||
~QtControllerSelectorDialog() override;
|
||||
|
||||
private:
|
||||
// Applies the current configuration.
|
||||
void ApplyConfiguration();
|
||||
|
||||
// Loads the current input configuration into the frontend applet.
|
||||
void LoadConfiguration();
|
||||
|
||||
// Initializes the "Configure Input" Dialog.
|
||||
void CallConfigureInputDialog();
|
||||
|
||||
// Checks the current configuration against the given parameters and
|
||||
// sets the value of parameters_met.
|
||||
void CheckIfParametersMet();
|
||||
|
||||
// Sets the controller icons for "Supported Controller Types".
|
||||
void SetSupportedControllers();
|
||||
|
||||
// Updates the controller icons per player.
|
||||
void UpdateControllerIcon(std::size_t player_index);
|
||||
|
||||
// Updates the controller state (type and connection status) per player.
|
||||
void UpdateControllerState(std::size_t player_index);
|
||||
|
||||
// Updates the LED pattern per player.
|
||||
void UpdateLEDPattern(std::size_t player_index);
|
||||
|
||||
// Updates the border color per player.
|
||||
void UpdateBorderColor(std::size_t player_index);
|
||||
|
||||
// Sets the "Explain Text" per player.
|
||||
void SetExplainText(std::size_t player_index);
|
||||
|
||||
// Updates the console mode.
|
||||
void UpdateDockedState(bool is_handheld);
|
||||
|
||||
// Disables and disconnects unsupported players based on the given parameters.
|
||||
void DisableUnsupportedPlayers();
|
||||
|
||||
std::unique_ptr<Ui::QtControllerSelectorDialog> ui;
|
||||
|
||||
// Parameters sent in from the backend HLE applet.
|
||||
Core::Frontend::ControllerParameters parameters;
|
||||
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
// This is true if and only if all parameters are met. Otherwise, this is false.
|
||||
// This determines whether the "OK" button can be clicked to exit the applet.
|
||||
bool parameters_met{false};
|
||||
|
||||
static constexpr std::size_t NUM_PLAYERS = 8;
|
||||
|
||||
// Widgets encapsulating the groupboxes and comboboxes per player.
|
||||
std::array<QWidget*, NUM_PLAYERS> player_widgets;
|
||||
|
||||
// Groupboxes encapsulating the controller icons and LED patterns per player.
|
||||
std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;
|
||||
|
||||
// Icons for currently connected controllers/players.
|
||||
std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;
|
||||
|
||||
// Labels that represent the player numbers in place of the controller icons.
|
||||
std::array<QLabel*, NUM_PLAYERS> player_labels;
|
||||
|
||||
// LED patterns for currently connected controllers/players.
|
||||
std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;
|
||||
|
||||
// Labels representing additional information known as "Explain Text" per player.
|
||||
std::array<QLabel*, NUM_PLAYERS> explain_text_labels;
|
||||
|
||||
// Comboboxes with a list of emulated controllers per player.
|
||||
std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
|
||||
|
||||
// Labels representing the number of connected controllers
|
||||
// above the "Connected Controllers" checkboxes.
|
||||
std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
|
||||
|
||||
// Checkboxes representing the "Connected Controllers".
|
||||
std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
|
||||
};
|
||||
|
||||
class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtControllerSelector(GMainWindow& parent);
|
||||
~QtControllerSelector() override;
|
||||
|
||||
void ReconfigureControllers(std::function<void()> callback,
|
||||
Core::Frontend::ControllerParameters parameters) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const;
|
||||
|
||||
private:
|
||||
void MainWindowReconfigureFinished();
|
||||
|
||||
mutable std::function<void()> callback;
|
||||
};
|
2672
src/yuzu/applets/controller.ui
Normal file
2672
src/yuzu/applets/controller.ui
Normal file
File diff suppressed because it is too large
Load Diff
@@ -70,7 +70,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
|
||||
ConfigureInput::~ConfigureInput() = default;
|
||||
|
||||
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
|
||||
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
||||
std::size_t max_players) {
|
||||
player_controllers = {
|
||||
new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
|
||||
new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
|
||||
@@ -93,6 +94,11 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
|
||||
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
|
||||
};
|
||||
|
||||
std::array<QLabel*, 8> player_connected_labels = {
|
||||
ui->label, ui->label_3, ui->label_4, ui->label_5,
|
||||
ui->label_6, ui->label_7, ui->label_8, ui->label_9,
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < player_tabs.size(); ++i) {
|
||||
player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
|
||||
player_tabs[i]->layout()->addWidget(player_controllers[i]);
|
||||
@@ -112,6 +118,13 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
|
||||
connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
|
||||
player_controllers[i]->ConnectPlayer(state == Qt::Checked);
|
||||
});
|
||||
|
||||
// Remove/hide all the elements that exceed max_players, if applicable.
|
||||
if (i >= max_players) {
|
||||
ui->tabWidget->removeTab(static_cast<int>(max_players));
|
||||
player_connected[i]->hide();
|
||||
player_connected_labels[i]->hide();
|
||||
}
|
||||
}
|
||||
// Only the first player can choose handheld mode so connect the signal just to player 1
|
||||
connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
|
||||
@@ -175,8 +188,7 @@ void ConfigureInput::RetranslateUI() {
|
||||
|
||||
void ConfigureInput::LoadConfiguration() {
|
||||
LoadPlayerControllerIndices();
|
||||
UpdateDockedState(Settings::values.players[0].controller_type ==
|
||||
Settings::ControllerType::Handheld);
|
||||
UpdateDockedState(Settings::values.players[8].connected);
|
||||
|
||||
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
|
||||
}
|
||||
@@ -208,14 +220,14 @@ void ConfigureInput::RestoreDefaults() {
|
||||
}
|
||||
|
||||
void ConfigureInput::UpdateDockedState(bool is_handheld) {
|
||||
// If the controller type is handheld only, disallow changing docked mode
|
||||
// Disallow changing the console mode if the controller type is handheld.
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode);
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
|
||||
|
||||
// If its handheld only, force docked mode off (since you can't play handheld in a dock)
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
ui->radioUndocked->setChecked(true);
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ public:
|
||||
~ConfigureInput() override;
|
||||
|
||||
/// Initializes the input dialog with the given input subsystem.
|
||||
void Initialize(InputCommon::InputSubsystem* input_subsystem_);
|
||||
void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8);
|
||||
|
||||
/// Save all button configurations to settings file.
|
||||
void ApplyConfiguration();
|
||||
|
37
src/yuzu/configuration/configure_input_dialog.cpp
Normal file
37
src/yuzu/configuration/configure_input_dialog.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "ui_configure_input_dialog.h"
|
||||
#include "yuzu/configuration/configure_input_dialog.h"
|
||||
|
||||
ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players,
|
||||
InputCommon::InputSubsystem* input_subsystem)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()),
|
||||
input_widget(new ConfigureInput(this)) {
|
||||
ui->setupUi(this);
|
||||
|
||||
input_widget->Initialize(input_subsystem, max_players);
|
||||
|
||||
ui->inputLayout->addWidget(input_widget);
|
||||
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
ConfigureInputDialog::~ConfigureInputDialog() = default;
|
||||
|
||||
void ConfigureInputDialog::ApplyConfiguration() {
|
||||
input_widget->ApplyConfiguration();
|
||||
}
|
||||
|
||||
void ConfigureInputDialog::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
||||
void ConfigureInputDialog::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
38
src/yuzu/configuration/configure_input_dialog.h
Normal file
38
src/yuzu/configuration/configure_input_dialog.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "yuzu/configuration/configure_input.h"
|
||||
|
||||
class QPushButton;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureInputDialog;
|
||||
}
|
||||
|
||||
class ConfigureInputDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players,
|
||||
InputCommon::InputSubsystem* input_subsystem);
|
||||
~ConfigureInputDialog() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void changeEvent(QEvent* event) override;
|
||||
void RetranslateUI();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureInputDialog> ui;
|
||||
|
||||
ConfigureInput* input_widget;
|
||||
};
|
57
src/yuzu/configuration/configure_input_dialog.ui
Normal file
57
src/yuzu/configuration/configure_input_dialog.ui
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureInputDialog</class>
|
||||
<widget class="QDialog" name="ConfigureInputDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>70</width>
|
||||
<height>540</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Configure Input</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="inputLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigureInputDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
||||
#include "applets/controller.h"
|
||||
#include "applets/error.h"
|
||||
#include "applets/profile_select.h"
|
||||
#include "applets/software_keyboard.h"
|
||||
@@ -19,7 +20,9 @@
|
||||
#include "configuration/configure_per_game.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
@@ -84,7 +87,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
@@ -283,6 +285,23 @@ GMainWindow::~GMainWindow() {
|
||||
delete render_window;
|
||||
}
|
||||
|
||||
void GMainWindow::ControllerSelectorReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters) {
|
||||
QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
|
||||
emit ControllerSelectorReconfigureFinished();
|
||||
|
||||
// Don't forget to apply settings.
|
||||
Settings::Apply();
|
||||
config->Save();
|
||||
|
||||
UpdateStatusButtons();
|
||||
}
|
||||
|
||||
void GMainWindow::ProfileSelectorSelectProfile() {
|
||||
const Service::Account::ProfileManager manager;
|
||||
int index = 0;
|
||||
@@ -291,10 +310,12 @@ void GMainWindow::ProfileSelectorSelectProfile() {
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
emit ProfileSelectorFinishedSelection(std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
index = dialog.GetIndex();
|
||||
}
|
||||
|
||||
@@ -966,13 +987,14 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
system.SetFilesystem(vfs);
|
||||
|
||||
system.SetAppletFrontendSet({
|
||||
nullptr, // Parental Controls
|
||||
std::make_unique<QtErrorDisplay>(*this), //
|
||||
nullptr, // Photo Viewer
|
||||
std::make_unique<QtProfileSelector>(*this), //
|
||||
std::make_unique<QtSoftwareKeyboard>(*this), //
|
||||
std::make_unique<QtWebBrowser>(*this), //
|
||||
nullptr, // E-Commerce
|
||||
std::make_unique<QtControllerSelector>(*this), // Controller Selector
|
||||
nullptr, // E-Commerce
|
||||
std::make_unique<QtErrorDisplay>(*this), // Error Display
|
||||
nullptr, // Parental Controls
|
||||
nullptr, // Photo Viewer
|
||||
std::make_unique<QtProfileSelector>(*this), // Profile Selector
|
||||
std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
|
||||
std::make_unique<QtWebBrowser>(*this), // Web Browser
|
||||
});
|
||||
|
||||
system.RegisterHostThread();
|
||||
@@ -2047,6 +2069,7 @@ void GMainWindow::OnStartGame() {
|
||||
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
|
||||
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
|
||||
"Core::Frontend::SoftwareKeyboardParameters");
|
||||
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
|
||||
|
@@ -37,6 +37,7 @@ enum class InstalledEntryType;
|
||||
class GameListPlaceholder;
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct ControllerParameters;
|
||||
struct SoftwareKeyboardParameters;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -116,9 +117,12 @@ signals:
|
||||
|
||||
void UpdateInstallProgress();
|
||||
|
||||
void ControllerSelectorReconfigureFinished();
|
||||
|
||||
void ErrorDisplayFinished();
|
||||
|
||||
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
|
||||
|
||||
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
|
||||
void SoftwareKeyboardFinishedCheckDialog();
|
||||
|
||||
@@ -127,6 +131,8 @@ signals:
|
||||
|
||||
public slots:
|
||||
void OnLoadComplete();
|
||||
void ControllerSelectorReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters);
|
||||
void ErrorDisplayDisplayError(QString body);
|
||||
void ProfileSelectorSelectProfile();
|
||||
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
||||
|
Reference in New Issue
Block a user