diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 34 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.h | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.cpp | 5 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_advanced.cpp | 9 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_advanced.h | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_advanced.ui | 14 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ringcon.cpp | 424 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ringcon.h | 85 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ringcon.ui | 278 | 
11 files changed, 856 insertions, 2 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index b1467d016..2ee21f751 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -99,6 +99,9 @@ add_executable(yuzu      configuration/configure_profile_manager.cpp      configuration/configure_profile_manager.h      configuration/configure_profile_manager.ui +    configuration/configure_ringcon.cpp +    configuration/configure_ringcon.h +    configuration/configure_ringcon.ui      configuration/configure_network.cpp      configuration/configure_network.h      configuration/configure_network.ui diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index d2e735f48..ac26b885b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -60,6 +60,11 @@ const std::array<int, 2> Config::default_stick_mod = {      0,  }; +const std::array<int, 2> Config::default_ringcon_analogs{{ +    Qt::Key_A, +    Qt::Key_D, +}}; +  // This shouldn't have anything except static initializers (no functions). So  // QKeySequence(...).toString() is NOT ALLOWED HERE.  // This must be in alphabetical order according to action name as it must have the same order as @@ -346,6 +351,23 @@ void Config::ReadTouchscreenValues() {          ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();  } +void Config::ReadHidbusValues() { +    Settings::values.enable_ring_controller = +        ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool(); + +    const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( +        0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); +    auto& ringcon_analogs = Settings::values.ringcon_analogs; + +    ringcon_analogs = +        qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param)) +            .toString() +            .toStdString(); +    if (ringcon_analogs.empty()) { +        ringcon_analogs = default_param; +    } +} +  void Config::ReadAudioValues() {      qt_config->beginGroup(QStringLiteral("Audio")); @@ -369,6 +391,7 @@ void Config::ReadControlValues() {      ReadMouseValues();      ReadTouchscreenValues();      ReadMotionTouchValues(); +    ReadHidbusValues();  #ifdef _WIN32      ReadBasicSetting(Settings::values.enable_raw_input); @@ -962,6 +985,16 @@ void Config::SaveMotionTouchValues() {      qt_config->endArray();  } +void Config::SaveHidbusValues() { +    WriteBasicSetting(Settings::values.enable_ring_controller); + +    const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( +        0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); +    WriteSetting(QStringLiteral("ring_controller"), +                 QString::fromStdString(Settings::values.ringcon_analogs), +                 QString::fromStdString(default_param)); +} +  void Config::SaveValues() {      if (global) {          SaveControlValues(); @@ -1002,6 +1035,7 @@ void Config::SaveControlValues() {      SaveMouseValues();      SaveTouchscreenValues();      SaveMotionTouchValues(); +    SaveHidbusValues();      WriteGlobalSetting(Settings::values.use_docked_mode);      WriteGlobalSetting(Settings::values.vibration_enabled); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index ae3e36a11..f0ab6bdaa 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -42,6 +42,7 @@ public:      static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;      static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;      static const std::array<int, 2> default_stick_mod; +    static const std::array<int, 2> default_ringcon_analogs;      static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>          default_mouse_buttons;      static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; @@ -66,6 +67,7 @@ private:      void ReadMouseValues();      void ReadTouchscreenValues();      void ReadMotionTouchValues(); +    void ReadHidbusValues();      // Read functions bases off the respective config section names.      void ReadAudioValues(); @@ -93,6 +95,7 @@ private:      void SaveMouseValues();      void SaveTouchscreenValues();      void SaveMotionTouchValues(); +    void SaveHidbusValues();      // Save functions based off the respective config section names.      void SaveAudioValues(); diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 4ca74a5f7..73d7ba24b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -20,6 +20,7 @@  #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" @@ -158,6 +159,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,              [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(ui->vibrationButton, &QPushButton::clicked,              [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 20fc2599d..8fd1f4a38 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -79,13 +79,17 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)              &ConfigureInputAdvanced::UpdateUIEnabled);      connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,              &ConfigureInputAdvanced::UpdateUIEnabled); +    connect(ui->enable_ring_controller, &QCheckBox::stateChanged, this, +            &ConfigureInputAdvanced::UpdateUIEnabled);      connect(ui->debug_configure, &QPushButton::clicked, this,              [this] { CallDebugControllerDialog(); });      connect(ui->touchscreen_advanced, &QPushButton::clicked, this,              [this] { CallTouchscreenConfigDialog(); });      connect(ui->buttonMotionTouch, &QPushButton::clicked, this, -            &ConfigureInputAdvanced::CallMotionTouchConfigDialog); +            [this] { CallMotionTouchConfigDialog(); }); +    connect(ui->ring_controller_configure, &QPushButton::clicked, this, +            [this] { CallRingControllerDialog(); });  #ifndef _WIN32      ui->enable_raw_input->setVisible(false); @@ -132,6 +136,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {      Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();      Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();      Settings::values.controller_navigation = ui->controller_navigation->isChecked(); +    Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();  }  void ConfigureInputAdvanced::LoadConfiguration() { @@ -164,6 +169,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {      ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());      ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());      ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); +    ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());      UpdateUIEnabled();  } @@ -185,4 +191,5 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {      ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());      ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());      ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); +    ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());  } diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index 3083d55c1..4472cb846 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -29,6 +29,7 @@ signals:      void CallMouseConfigDialog();      void CallTouchscreenConfigDialog();      void CallMotionTouchConfigDialog(); +    void CallRingControllerDialog();  private:      void changeEvent(QEvent* event) override; diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 66f2075f2..14403cb10 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2603,6 +2603,20 @@                   </property>                  </widget>                 </item> +               <item row="4" column="0"> +                <widget class="QCheckBox" name="enable_ring_controller"> +                 <property name="text"> +                  <string>Ring Controller</string> +                 </property> +                </widget> +               </item> +               <item row="4" column="2"> +                <widget class="QPushButton" name="ring_controller_configure"> +                 <property name="text"> +                  <string>Configure</string> +                 </property> +                </widget> +               </item>                </layout>               </widget>              </item> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 8ef3596dd..291e37acf 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -1407,10 +1407,10 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {  }  void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { -    event->ignore();      if (!input_setter || !event) {          return;      } +    event->ignore();      if (event->key() != Qt::Key_Escape) {          input_subsystem->GetKeyboard()->PressKey(event->key());      } diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp new file mode 100644 index 000000000..f1cdd4fed --- /dev/null +++ b/src/yuzu/configuration/configure_ringcon.cpp @@ -0,0 +1,424 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include <QKeyEvent> +#include <QMenu> +#include <QTimer> + +#include "core/hid/emulated_devices.h" +#include "core/hid/hid_core.h" +#include "input_common/drivers/keyboard.h" +#include "input_common/drivers/mouse.h" +#include "input_common/main.h" +#include "ui_configure_ringcon.h" +#include "yuzu/bootmanager.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/configure_ringcon.h" + +const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM> +    ConfigureRingController::analog_sub_buttons{{ +        "left", +        "right", +    }}; + +namespace { + +QString GetKeyName(int key_code) { +    switch (key_code) { +    case Qt::Key_Shift: +        return QObject::tr("Shift"); +    case Qt::Key_Control: +        return QObject::tr("Ctrl"); +    case Qt::Key_Alt: +        return QObject::tr("Alt"); +    case Qt::Key_Meta: +        return {}; +    default: +        return QKeySequence(key_code).toString(); +    } +} + +QString GetButtonName(Common::Input::ButtonNames button_name) { +    switch (button_name) { +    case Common::Input::ButtonNames::ButtonLeft: +        return QObject::tr("Left"); +    case Common::Input::ButtonNames::ButtonRight: +        return QObject::tr("Right"); +    case Common::Input::ButtonNames::ButtonDown: +        return QObject::tr("Down"); +    case Common::Input::ButtonNames::ButtonUp: +        return QObject::tr("Up"); +    case Common::Input::ButtonNames::TriggerZ: +        return QObject::tr("Z"); +    case Common::Input::ButtonNames::TriggerR: +        return QObject::tr("R"); +    case Common::Input::ButtonNames::TriggerL: +        return QObject::tr("L"); +    case Common::Input::ButtonNames::ButtonA: +        return QObject::tr("A"); +    case Common::Input::ButtonNames::ButtonB: +        return QObject::tr("B"); +    case Common::Input::ButtonNames::ButtonX: +        return QObject::tr("X"); +    case Common::Input::ButtonNames::ButtonY: +        return QObject::tr("Y"); +    case Common::Input::ButtonNames::ButtonStart: +        return QObject::tr("Start"); +    case Common::Input::ButtonNames::L1: +        return QObject::tr("L1"); +    case Common::Input::ButtonNames::L2: +        return QObject::tr("L2"); +    case Common::Input::ButtonNames::L3: +        return QObject::tr("L3"); +    case Common::Input::ButtonNames::R1: +        return QObject::tr("R1"); +    case Common::Input::ButtonNames::R2: +        return QObject::tr("R2"); +    case Common::Input::ButtonNames::R3: +        return QObject::tr("R3"); +    case Common::Input::ButtonNames::Circle: +        return QObject::tr("Circle"); +    case Common::Input::ButtonNames::Cross: +        return QObject::tr("Cross"); +    case Common::Input::ButtonNames::Square: +        return QObject::tr("Square"); +    case Common::Input::ButtonNames::Triangle: +        return QObject::tr("Triangle"); +    case Common::Input::ButtonNames::Share: +        return QObject::tr("Share"); +    case Common::Input::ButtonNames::Options: +        return QObject::tr("Options"); +    default: +        return QObject::tr("[undefined]"); +    } +} + +void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, +                    const std::string& button_name) { +    // The poller returned a complete axis, so set all the buttons +    if (input_param.Has("axis_x") && input_param.Has("axis_y")) { +        analog_param = input_param; +        return; +    } +    // Check if the current configuration has either no engine or an axis binding. +    // Clears out the old binding and adds one with analog_from_button. +    if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) { +        analog_param = { +            {"engine", "analog_from_button"}, +        }; +    } +    analog_param.Set(button_name, input_param.Serialize()); +} +} // namespace + +ConfigureRingController::ConfigureRingController(QWidget* parent, +                                                 InputCommon::InputSubsystem* input_subsystem_, +                                                 Core::HID::HIDCore& hid_core_) +    : QDialog(parent), timeout_timer(std::make_unique<QTimer>()), +      poll_timer(std::make_unique<QTimer>()), input_subsystem{input_subsystem_}, + +      ui(std::make_unique<Ui::ConfigureRingController>()) { +    ui->setupUi(this); + +    analog_map_buttons = { +        ui->buttonRingAnalogPull, +        ui->buttonRingAnalogPush, +    }; + +    emulated_device = hid_core_.GetEmulatedDevices(); +    emulated_device->SaveCurrentConfig(); +    emulated_device->EnableConfiguration(); + +    LoadConfiguration(); + +    for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { +        auto* const analog_button = analog_map_buttons[sub_button_id]; + +        if (analog_button == nullptr) { +            continue; +        } + +        connect(analog_button, &QPushButton::clicked, [=, this] { +            HandleClick( +                analog_map_buttons[sub_button_id], +                [=, this](const Common::ParamPackage& params) { +                    Common::ParamPackage param = emulated_device->GetRingParam(); +                    SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); +                    emulated_device->SetRingParam(param); +                }, +                InputCommon::Polling::InputType::Stick); +        }); + +        analog_button->setContextMenuPolicy(Qt::CustomContextMenu); + +        connect(analog_button, &QPushButton::customContextMenuRequested, +                [=, this](const QPoint& menu_location) { +                    QMenu context_menu; +                    Common::ParamPackage param = emulated_device->GetRingParam(); +                    context_menu.addAction(tr("Clear"), [&] { +                        emulated_device->SetRingParam({}); +                        analog_map_buttons[sub_button_id]->setText(tr("[not set]")); +                    }); +                    context_menu.addAction(tr("Invert axis"), [&] { +                        const bool invert_value = param.Get("invert_x", "+") == "-"; +                        const std::string invert_str = invert_value ? "+" : "-"; +                        param.Set("invert_x", invert_str); +                        emulated_device->SetRingParam(param); +                        for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; +                             ++sub_button_id) { +                            analog_map_buttons[sub_button_id]->setText( +                                AnalogToText(param, analog_sub_buttons[sub_button_id])); +                        } +                    }); +                    context_menu.exec( +                        analog_map_buttons[sub_button_id]->mapToGlobal(menu_location)); +                }); +    } + +    connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] { +        Common::ParamPackage param = emulated_device->GetRingParam(); +        const auto slider_value = ui->sliderRingAnalogDeadzone->value(); +        ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value)); +        param.Set("deadzone", slider_value / 100.0f); +        emulated_device->SetRingParam(param); +    }); + +    connect(ui->restore_defaults_button, &QPushButton::clicked, this, +            &ConfigureRingController::RestoreDefaults); + +    timeout_timer->setSingleShot(true); +    connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); + +    connect(poll_timer.get(), &QTimer::timeout, [this] { +        const auto& params = input_subsystem->GetNextInput(); +        if (params.Has("engine") && IsInputAcceptable(params)) { +            SetPollingResult(params, false); +            return; +        } +    }); + +    resize(0, 0); +} + +ConfigureRingController::~ConfigureRingController() { +    emulated_device->DisableConfiguration(); +}; + +void ConfigureRingController::changeEvent(QEvent* event) { +    if (event->type() == QEvent::LanguageChange) { +        RetranslateUI(); +    } + +    QDialog::changeEvent(event); +} + +void ConfigureRingController::RetranslateUI() { +    ui->retranslateUi(this); +} + +void ConfigureRingController::UpdateUI() { +    RetranslateUI(); +    const Common::ParamPackage param = emulated_device->GetRingParam(); + +    for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { +        auto* const analog_button = analog_map_buttons[sub_button_id]; + +        if (analog_button == nullptr) { +            continue; +        } + +        analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id])); +    } + +    const auto deadzone_label = ui->labelRingAnalogDeadzone; +    const auto deadzone_slider = ui->sliderRingAnalogDeadzone; + +    int slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100); +    deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value)); +    deadzone_slider->setValue(slider_value); +} + +void ConfigureRingController::ApplyConfiguration() { +    emulated_device->DisableConfiguration(); +    emulated_device->SaveCurrentConfig(); +    emulated_device->EnableConfiguration(); +} + +void ConfigureRingController::LoadConfiguration() { +    UpdateUI(); +} + +void ConfigureRingController::RestoreDefaults() { +    const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( +        0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); +    emulated_device->SetRingParam(Common::ParamPackage(default_ring_string)); +    UpdateUI(); +} + +void ConfigureRingController::HandleClick( +    QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, +    InputCommon::Polling::InputType type) { +    button->setText(tr("[waiting]")); +    button->setFocus(); + +    input_setter = new_input_setter; + +    input_subsystem->BeginMapping(type); + +    QWidget::grabMouse(); +    QWidget::grabKeyboard(); + +    timeout_timer->start(2500); // Cancel after 2.5 seconds +    poll_timer->start(25);      // Check for new inputs every 25ms +} + +void ConfigureRingController::SetPollingResult(const Common::ParamPackage& params, bool abort) { +    timeout_timer->stop(); +    poll_timer->stop(); +    input_subsystem->StopMapping(); + +    QWidget::releaseMouse(); +    QWidget::releaseKeyboard(); + +    if (!abort) { +        (*input_setter)(params); +    } + +    UpdateUI(); + +    input_setter = std::nullopt; +} + +bool ConfigureRingController::IsInputAcceptable(const Common::ParamPackage& params) const { +    return true; +} + +void ConfigureRingController::mousePressEvent(QMouseEvent* event) { +    if (!input_setter || !event) { +        return; +    } + +    const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); +    input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button); +} + +void ConfigureRingController::keyPressEvent(QKeyEvent* event) { +    if (!input_setter || !event) { +        return; +    } +    event->ignore(); +    if (event->key() != Qt::Key_Escape) { +        input_subsystem->GetKeyboard()->PressKey(event->key()); +    } +} + +QString ConfigureRingController::ButtonToText(const Common::ParamPackage& param) { +    if (!param.Has("engine")) { +        return QObject::tr("[not set]"); +    } + +    const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); +    const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); +    const auto common_button_name = input_subsystem->GetButtonName(param); + +    // Retrieve the names from Qt +    if (param.Get("engine", "") == "keyboard") { +        const QString button_str = GetKeyName(param.Get("code", 0)); +        return QObject::tr("%1%2").arg(toggle, button_str); +    } + +    if (common_button_name == Common::Input::ButtonNames::Invalid) { +        return QObject::tr("[invalid]"); +    } + +    if (common_button_name == Common::Input::ButtonNames::Engine) { +        return QString::fromStdString(param.Get("engine", "")); +    } + +    if (common_button_name == Common::Input::ButtonNames::Value) { +        if (param.Has("hat")) { +            const QString hat = QString::fromStdString(param.Get("direction", "")); +            return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); +        } +        if (param.Has("axis")) { +            const QString axis = QString::fromStdString(param.Get("axis", "")); +            return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis); +        } +        if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) { +            const QString axis_x = QString::fromStdString(param.Get("axis_x", "")); +            const QString axis_y = QString::fromStdString(param.Get("axis_y", "")); +            const QString axis_z = QString::fromStdString(param.Get("axis_z", "")); +            return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z); +        } +        if (param.Has("motion")) { +            const QString motion = QString::fromStdString(param.Get("motion", "")); +            return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion); +        } +        if (param.Has("button")) { +            const QString button = QString::fromStdString(param.Get("button", "")); +            return QObject::tr("%1%2Button %3").arg(toggle, inverted, button); +        } +    } + +    QString button_name = GetButtonName(common_button_name); +    if (param.Has("hat")) { +        return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name); +    } +    if (param.Has("axis")) { +        return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); +    } +    if (param.Has("motion")) { +        return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); +    } +    if (param.Has("button")) { +        return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name); +    } + +    return QObject::tr("[unknown]"); +} + +QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param, +                                              const std::string& dir) { +    if (!param.Has("engine")) { +        return QObject::tr("[not set]"); +    } + +    if (param.Get("engine", "") == "analog_from_button") { +        return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); +    } + +    if (!param.Has("axis_x") || !param.Has("axis_y")) { +        return QObject::tr("[unknown]"); +    } + +    const auto engine_str = param.Get("engine", ""); +    const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); +    const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); +    const bool invert_x = param.Get("invert_x", "+") == "-"; +    const bool invert_y = param.Get("invert_y", "+") == "-"; + +    if (dir == "modifier") { +        return QObject::tr("[unused]"); +    } + +    if (dir == "left") { +        const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-"); +        return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); +    } +    if (dir == "right") { +        const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+"); +        return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); +    } +    if (dir == "up") { +        const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+"); +        return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); +    } +    if (dir == "down") { +        const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-"); +        return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); +    } + +    return QObject::tr("[unknown]"); +}
\ No newline at end of file diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h new file mode 100644 index 000000000..cf9e54f09 --- /dev/null +++ b/src/yuzu/configuration/configure_ringcon.h @@ -0,0 +1,85 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include <memory> +#include <QDialog> + +namespace InputCommon { +class InputSubsystem; +} // namespace InputCommon + +namespace Core::HID { +class HIDCore; +class EmulatedDevices; +} // namespace Core::HID + +namespace Ui { +class ConfigureRingController; +} // namespace Ui + +class ConfigureRingController : public QDialog { +    Q_OBJECT + +public: +    explicit ConfigureRingController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_, +                                     Core::HID::HIDCore& hid_core_); +    ~ConfigureRingController() override; + +    void ApplyConfiguration(); + +private: +    void changeEvent(QEvent* event) override; +    void RetranslateUI(); + +    void UpdateUI(); + +    /// Load configuration settings. +    void LoadConfiguration(); + +    /// Restore all buttons to their default values. +    void RestoreDefaults(); + +    /// Called when the button was pressed. +    void HandleClick(QPushButton* button, +                     std::function<void(const Common::ParamPackage&)> new_input_setter, +                     InputCommon::Polling::InputType type); + +    /// Finish polling and configure input using the input_setter. +    void SetPollingResult(const Common::ParamPackage& params, bool abort); + +    /// Checks whether a given input can be accepted. +    bool IsInputAcceptable(const Common::ParamPackage& params) const; + +    /// Handle mouse button press events. +    void mousePressEvent(QMouseEvent* event) override; + +    /// Handle key press events. +    void keyPressEvent(QKeyEvent* event) override; + +    QString ButtonToText(const Common::ParamPackage& param); + +    QString AnalogToText(const Common::ParamPackage& param, const std::string& dir); + +    static constexpr int ANALOG_SUB_BUTTONS_NUM = 2; + +    // A group of four QPushButtons represent one analog input. The buttons each represent left, +    // right, respectively. +    std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM> analog_map_buttons; + +    static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; + +    std::unique_ptr<QTimer> timeout_timer; +    std::unique_ptr<QTimer> poll_timer; + +    /// This will be the the setting function when an input is awaiting configuration. +    std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; + +    InputCommon::InputSubsystem* input_subsystem; +    Core::HID::EmulatedDevices* emulated_device; + +    std::unique_ptr<Ui::ConfigureRingController> ui; +}; diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui new file mode 100644 index 000000000..9ec634dd4 --- /dev/null +++ b/src/yuzu/configuration/configure_ringcon.ui @@ -0,0 +1,278 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureRingController</class> + <widget class="QDialog" name="ConfigureRingController"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>298</width> +    <height>339</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Configure Ring Controller</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <item> +    <widget class="QLabel" name="label_2"> +     <property name="minimumSize"> +      <size> +       <width>280</width> +       <height>0</height> +      </size> +     </property> +     <property name="text"> +      <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string> +     </property> +     <property name="wordWrap"> +      <bool>true</bool> +     </property> +    </widget> +   </item> +   <item> +    <spacer name="verticalSpacer_2"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeType"> +      <enum>QSizePolicy::Fixed</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>20</width> +       <height>10</height> +      </size> +     </property> +    </spacer> +   </item> +  <item> +  <widget class="QGroupBox" name="RingAnalog"> +    <property name="title"> +    <string>Ring Sensor Parameters</string> +    </property> +    <property name="alignment"> +    <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> +    </property> +    <layout class="QVBoxLayout" name="verticalLayout_3"> +    <property name="spacing"> +      <number>0</number> +    </property> +    <property name="sizeConstraint"> +      <enum>QLayout::SetDefaultConstraint</enum> +    </property> +    <property name="leftMargin"> +      <number>3</number> +    </property> +    <property name="topMargin"> +      <number>6</number> +    </property> +    <property name="rightMargin"> +      <number>3</number> +    </property> +    <property name="bottomMargin"> +      <number>0</number> +    </property> +    <item> +      <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout"> +      <property name="spacing"> +        <number>3</number> +      </property> +      <item alignment="Qt::AlignHCenter"> +        <widget class="QGroupBox" name="buttonRingAnalogPullGroup"> +        <property name="title"> +          <string>Pull</string> +        </property> +        <property name="alignment"> +          <set>Qt::AlignCenter</set> +        </property> +        <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout"> +          <property name="spacing"> +          <number>3</number> +          </property> +          <property name="leftMargin"> +          <number>3</number> +          </property> +          <property name="topMargin"> +          <number>3</number> +          </property> +          <property name="rightMargin"> +          <number>3</number> +          </property> +          <property name="bottomMargin"> +          <number>3</number> +          </property> +          <item> +          <widget class="QPushButton" name="buttonRingAnalogPull"> +            <property name="minimumSize"> +            <size> +              <width>68</width> +              <height>0</height> +            </size> +            </property> +            <property name="maximumSize"> +            <size> +              <width>68</width> +              <height>16777215</height> +            </size> +            </property> +            <property name="styleSheet"> +            <string notr="true">min-width: 68px;</string> +            </property> +            <property name="text"> +            <string>Pull</string> +            </property> +          </widget> +          </item> +        </layout> +        </widget> +      </item> +      <item alignment="Qt::AlignHCenter"> +        <widget class="QGroupBox" name="buttonRingAnalogPushGroup"> +        <property name="title"> +          <string>Push</string> +        </property> +        <property name="alignment"> +          <set>Qt::AlignCenter</set> +        </property> +        <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout"> +          <property name="spacing"> +          <number>3</number> +          </property> +          <property name="leftMargin"> +          <number>3</number> +          </property> +          <property name="topMargin"> +          <number>3</number> +          </property> +          <property name="rightMargin"> +          <number>3</number> +          </property> +          <property name="bottomMargin"> +          <number>3</number> +          </property> +          <item> +          <widget class="QPushButton" name="buttonRingAnalogPush"> +            <property name="minimumSize"> +            <size> +              <width>68</width> +              <height>0</height> +            </size> +            </property> +            <property name="maximumSize"> +            <size> +              <width>68</width> +              <height>16777215</height> +            </size> +            </property> +            <property name="styleSheet"> +            <string notr="true">min-width: 68px;</string> +            </property> +            <property name="text"> +            <string>Push</string> +            </property> +          </widget> +          </item> +        </layout> +        </widget> +      </item> +      </layout> +    </item> +    <item> +      <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout"> +      <property name="spacing"> +        <number>3</number> +      </property> +      <property name="sizeConstraint"> +        <enum>QLayout::SetDefaultConstraint</enum> +      </property> +      <property name="leftMargin"> +        <number>0</number> +      </property> +      <property name="topMargin"> +        <number>10</number> +      </property> +      <property name="rightMargin"> +        <number>0</number> +      </property> +      <property name="bottomMargin"> +        <number>3</number> +      </property> +      <item> +        <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout"> +        <item> +          <widget class="QLabel" name="labelRingAnalogDeadzone"> +          <property name="text"> +            <string>Deadzone: 0%</string> +          </property> +          <property name="alignment"> +            <set>Qt::AlignHCenter</set> +          </property> +          </widget> +        </item> +        </layout> +      </item> +      <item> +        <widget class="QSlider" name="sliderRingAnalogDeadzone"> +        <property name="maximum"> +          <number>100</number> +        </property> +        <property name="orientation"> +          <enum>Qt::Horizontal</enum> +        </property> +        </widget> +      </item> +      </layout> +    </item> +    </layout> +  </widget> +  </item> +   <item> +    <spacer name="verticalSpacer"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>20</width> +       <height>40</height> +      </size> +     </property> +    </spacer> +   </item> +   <item> +    <layout class="QHBoxLayout" name="horizontalLayout"> +     <item> +      <widget class="QPushButton" name="restore_defaults_button"> +       <property name="text"> +        <string>Restore Defaults</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QDialogButtonBox" name="buttonBox"> +       <property name="standardButtons"> +        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> +       </property> +      </widget> +     </item> +    </layout> +   </item> +  </layout> + </widget> + <resources/> + <connections> +  <connection> +   <sender>buttonBox</sender> +   <signal>accepted()</signal> +   <receiver>ConfigureRingController</receiver> +   <slot>accept()</slot> +  </connection> +  <connection> +   <sender>buttonBox</sender> +   <signal>rejected()</signal> +   <receiver>ConfigureRingController</receiver> +   <slot>reject()</slot> +   </connection> + </connections> +</ui>  | 
