diff options
author | Zephyron <zephyron@citron-emu.org> | 2024-12-31 16:19:25 +1000 |
---|---|---|
committer | Zephyron <zephyron@citron-emu.org> | 2024-12-31 16:19:25 +1000 |
commit | 9427e27e24a7135880ee2881c3c44988e174b41a (patch) | |
tree | 83f0062a35be144f6b162eaa823c5b3c7620146e /src/citron/configuration/configure_input.cpp | |
parent | b35ae725d20960411e8588b11c12a2d55f86c9d0 (diff) |
chore: update project branding to citron
Diffstat (limited to 'src/citron/configuration/configure_input.cpp')
-rw-r--r-- | src/citron/configuration/configure_input.cpp | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/citron/configuration/configure_input.cpp b/src/citron/configuration/configure_input.cpp new file mode 100644 index 000000000..28c3baf08 --- /dev/null +++ b/src/citron/configuration/configure_input.cpp @@ -0,0 +1,309 @@ +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <memory> +#include <thread> + +#include "common/settings.h" +#include "common/settings_enums.h" +#include "core/core.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "ui_configure_input.h" +#include "ui_configure_input_advanced.h" +#include "ui_configure_input_player.h" +#include "yuzu/configuration/configure_camera.h" +#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" +#include "yuzu/configuration/configure_ringcon.h" +#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 + +void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system) { + if (last_state == new_state) { + return; + } + + if (!system.IsPoweredOn()) { + return; + } + + system.GetAppletManager().OperationModeChanged(); +} + +ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), + profiles(std::make_unique<InputProfiles>()), system{system_} { + ui->setupUi(this); +} + +ConfigureInput::~ConfigureInput() = default; + +void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, + std::size_t max_players) { + const bool is_powered_on = system.IsPoweredOn(); + auto& hid_core = system.HIDCore(); + player_controllers = { + new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(), + hid_core, is_powered_on), + }; + + player_tabs = { + ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, + ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, + }; + + connected_controller_checkboxes = { + ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected, + ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected, + ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, + }; + + std::array<QLabel*, 8> connected_controller_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, [this, i](bool checked) { + // Ensures that connecting a controller changes the number of players + if (connected_controller_checkboxes[i]->isChecked() != checked) { + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked); + } + }); + connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) { + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && + i < connected_controller_checkboxes.size() - 1 && + connected_controller_checkboxes[i + 1]->isChecked(); + + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); + }); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, + &ConfigureInput::UpdateAllInputDevices); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this, + &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection); + connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) { + // Keep activated controllers synced with the "Connected Controllers" checkboxes + 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)); + connected_controller_checkboxes[i]->hide(); + connected_controller_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(hid_core, this); + ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); + ui->tabAdvanced->layout()->addWidget(advanced); + + connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, + [this, input_subsystem, &hid_core, is_powered_on] { + CallConfigureDialog<ConfigureDebugController>( + *this, input_subsystem, profiles.get(), hid_core, is_powered_on); + }); + connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, + [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); + connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog, + [this, input_subsystem] { + CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); + }); + connect(advanced, &ConfigureInputAdvanced::CallRingControllerDialog, + [this, input_subsystem, &hid_core] { + CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core); + }); + connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, [this, input_subsystem] { + CallConfigureDialog<ConfigureCamera>(*this, input_subsystem); + }); + + connect(ui->vibrationButton, &QPushButton::clicked, + [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); + + 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(); +} + +void ConfigureInput::PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current) { + connected_controller_checkboxes[player_index]->setChecked(checked); + + if (checked) { + // Check all previous buttons when checked + if (player_index > 0) { + PropagatePlayerNumberChanged(player_index - 1, checked); + } + } else { + // Unchecked all following buttons when unchecked + if (player_index < connected_controller_checkboxes.size() - 1) { + PropagatePlayerNumberChanged(player_index + 1, checked); + } + } + + if (reconnect_current) { + connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked); + } +} + +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() { + const bool was_global = Settings::values.players.UsingGlobal(); + Settings::values.players.SetGlobal(true); + for (auto* controller : player_controllers) { + controller->ApplyConfiguration(); + } + + advanced->ApplyConfiguration(); + + const bool pre_docked_mode = Settings::IsDockedMode(); + const bool docked_mode_selected = ui->radioDocked->isChecked(); + Settings::values.use_docked_mode.SetValue( + docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld); + OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system); + + Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); + Settings::values.players.SetGlobal(was_global); +} + +void ConfigureInput::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void ConfigureInput::RetranslateUI() { + ui->retranslateUi(this); +} + +void ConfigureInput::LoadConfiguration() { + const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + + LoadPlayerControllerIndices(); + UpdateDockedState(handheld->IsConnected()); + + 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 < connected_controller_checkboxes.size(); ++i) { + if (i == 0) { + auto* handheld = + system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + if (handheld->IsConnected()) { + connected_controller_checkboxes[i]->setChecked(true); + continue; + } + } + const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i); + connected_controller_checkboxes[i]->setChecked(controller->IsConnected()); + } +} + +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::IsDockedMode()); + ui->radioUndocked->setChecked(!Settings::IsDockedMode()); + + // 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(); + } +} |