pineapple/src/yuzu/configuration/configure_input.cpp

291 lines
12 KiB
C++
Raw Normal View History

2022-07-27 20:06:50 +02:00
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
2020-12-28 16:15:37 +01:00
#include <memory>
2021-01-01 22:50:16 +01:00
#include <thread>
2020-12-28 16:15:37 +01:00
#include "core/core.h"
2021-11-15 02:13:48 +01:00
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
2020-12-28 16:15:37 +01:00
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/sm/sm.h"
#include "ui_configure_input.h"
#include "ui_configure_input_advanced.h"
#include "ui_configure_input_player.h"
2022-07-25 19:06:59 +02:00
#include "yuzu/configuration/configure_camera.h"
2020-12-28 16:15:37 +01:00
#include "yuzu/configuration/configure_debug_controller.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_motion_touch.h"
2022-04-16 16:44:44 +02:00
#include "yuzu/configuration/configure_ringcon.h"
2020-12-28 16:15:37 +01:00
#include "yuzu/configuration/configure_touchscreen_advanced.h"
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
Dialog dialog(&parent, std::forward<Args>(args)...);
const auto res = dialog.exec();
if (res == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
}
} // Anonymous namespace
2021-10-09 02:29:53 +02:00
void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system) {
2020-12-28 16:15:37 +01:00
if (last_state == new_state) {
return;
}
if (!system.IsPoweredOn()) {
return;
}
Service::SM::ServiceManager& sm = system.ServiceManager();
// Message queue is shared between these services, we just need to signal an operation
// change to one and it will handle both automatically
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
bool has_signalled = false;
if (applet_oe != nullptr) {
applet_oe->GetMessageQueue()->OperationModeChanged();
has_signalled = true;
}
if (applet_ae != nullptr && !has_signalled) {
applet_ae->GetMessageQueue()->OperationModeChanged();
}
}
2021-10-09 02:29:53 +02:00
ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent)
2020-12-28 16:15:37 +01:00
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
2022-09-05 05:41:15 +02:00
profiles(std::make_unique<InputProfiles>()), system{system_} {
2020-12-28 16:15:37 +01:00
ui->setupUi(this);
}
ConfigureInput::~ConfigureInput() = default;
2021-11-05 16:31:40 +01:00
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
2020-12-28 16:15:37 +01:00
std::size_t max_players) {
2021-11-15 02:13:48 +01:00
const bool is_powered_on = system.IsPoweredOn();
auto& hid_core = system.HIDCore();
2020-12-28 16:15:37 +01:00
player_controllers = {
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2021-10-09 02:29:53 +02:00
new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(),
2021-11-15 02:13:48 +01:00
hid_core, is_powered_on),
2020-12-28 16:15:37 +01:00
};
player_tabs = {
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
};
player_connected = {
ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
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]);
connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
2021-11-15 02:13:48 +01:00
// Ensures that the controllers are always connected in sequential order
2020-12-28 16:15:37 +01:00
if (is_connected) {
for (std::size_t index = 0; index <= i; ++index) {
player_connected[index]->setChecked(is_connected);
}
} else {
for (std::size_t index = i; index < player_tabs.size(); ++index) {
player_connected[index]->setChecked(is_connected);
}
}
});
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
&ConfigureInput::UpdateAllInputDevices);
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this,
&ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection);
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,
[this](bool is_handheld) { UpdateDockedState(is_handheld); });
advanced = new ConfigureInputAdvanced(this);
ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
ui->tabAdvanced->layout()->addWidget(advanced);
2021-11-15 02:13:48 +01:00
connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog,
[this, input_subsystem, &hid_core, is_powered_on] {
CallConfigureDialog<ConfigureDebugController>(
*this, input_subsystem, profiles.get(), hid_core, is_powered_on);
});
2020-12-28 16:15:37 +01:00
connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
[this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog,
[this, input_subsystem] {
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
2022-04-16 16:44:44 +02:00
connect(advanced, &ConfigureInputAdvanced::CallRingControllerDialog,
[this, input_subsystem, &hid_core] {
CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
});
2022-09-29 01:25:57 +02:00
connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, [this, input_subsystem] {
CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
});
2020-12-28 16:15:37 +01:00
connect(ui->vibrationButton, &QPushButton::clicked,
2022-02-04 07:14:21 +01:00
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
2020-12-28 16:15:37 +01:00
connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
RetranslateUI();
LoadConfiguration();
}
QList<QWidget*> ConfigureInput::GetSubTabs() const {
return {
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
};
}
void ConfigureInput::ApplyConfiguration() {
2021-01-01 22:50:16 +01:00
for (auto* controller : player_controllers) {
2020-12-28 16:15:37 +01:00
controller->ApplyConfiguration();
2021-11-14 23:19:09 +01:00
}
2020-12-28 16:15:37 +01:00
advanced->ApplyConfiguration();
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
2021-10-09 02:29:53 +02:00
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system);
2020-12-28 16:15:37 +01:00
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
}
void ConfigureInput::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureInput::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureInput::LoadConfiguration() {
2021-11-15 02:13:48 +01:00
const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
2020-12-28 16:15:37 +01:00
LoadPlayerControllerIndices();
2021-11-15 02:13:48 +01:00
UpdateDockedState(handheld->IsConnected());
2020-12-28 16:15:37 +01:00
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
}
void ConfigureInput::LoadPlayerControllerIndices() {
for (std::size_t i = 0; i < player_connected.size(); ++i) {
2021-11-15 02:13:48 +01:00
if (i == 0) {
auto* handheld =
system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (handheld->IsConnected()) {
player_connected[i]->setChecked(true);
continue;
}
}
const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i);
player_connected[i]->setChecked(controller->IsConnected());
2020-12-28 16:15:37 +01:00
}
}
void ConfigureInput::ClearAll() {
// We don't have a good way to know what tab is active, but we can find out by getting the
// parent of the consoleInputSettings
auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
player_tab->ClearAll();
}
void ConfigureInput::RestoreDefaults() {
// We don't have a good way to know what tab is active, but we can find out by getting the
// parent of the consoleInputSettings
auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
player_tab->RestoreDefaults();
ui->radioDocked->setChecked(true);
ui->radioUndocked->setChecked(false);
ui->vibrationGroup->setChecked(true);
ui->motionGroup->setChecked(true);
}
void ConfigureInput::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.GetValue());
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
// Also force into undocked mode if the controller type is handheld.
if (is_handheld) {
ui->radioUndocked->setChecked(true);
}
}
void ConfigureInput::UpdateAllInputDevices() {
for (const auto& player : player_controllers) {
player->UpdateInputDeviceCombobox();
}
}
void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) {
for (std::size_t i = 0; i < player_controllers.size(); ++i) {
if (i == player_index) {
continue;
}
player_controllers[i]->UpdateInputProfiles();
}
}