|
@@ -18,6 +18,7 @@
|
|
|
#include "core/hle/service/sm/sm.h"
|
|
#include "core/hle/service/sm/sm.h"
|
|
|
#include "input_common/gcadapter/gc_poller.h"
|
|
#include "input_common/gcadapter/gc_poller.h"
|
|
|
#include "input_common/main.h"
|
|
#include "input_common/main.h"
|
|
|
|
|
+#include "input_common/udp/udp.h"
|
|
|
#include "ui_configure_input_player.h"
|
|
#include "ui_configure_input_player.h"
|
|
|
#include "yuzu/configuration/config.h"
|
|
#include "yuzu/configuration/config.h"
|
|
|
#include "yuzu/configuration/configure_input_player.h"
|
|
#include "yuzu/configuration/configure_input_player.h"
|
|
@@ -149,6 +150,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
|
|
return GetKeyName(param.Get("code", 0));
|
|
return GetKeyName(param.Get("code", 0));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (param.Get("engine", "") == "cemuhookudp") {
|
|
|
|
|
+ if (param.Has("pad_index")) {
|
|
|
|
|
+ const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
|
|
|
|
|
+ return QObject::tr("Motion %1").arg(motion_str);
|
|
|
|
|
+ }
|
|
|
|
|
+ return GetKeyName(param.Get("code", 0));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (param.Get("engine", "") == "sdl") {
|
|
if (param.Get("engine", "") == "sdl") {
|
|
|
if (param.Has("hat")) {
|
|
if (param.Has("hat")) {
|
|
|
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
|
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
|
@@ -262,6 +271,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
},
|
|
},
|
|
|
}};
|
|
}};
|
|
|
|
|
|
|
|
|
|
+ motion_map = {
|
|
|
|
|
+ ui->buttonMotionLeft,
|
|
|
|
|
+ ui->buttonMotionRight,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
|
|
analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
|
|
|
analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
|
|
analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
|
|
|
analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
|
|
analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
|
|
@@ -304,6 +318,32 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
Config::default_buttons[button_id]);
|
|
Config::default_buttons[button_id]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
|
|
|
|
+ auto* const button = motion_map[motion_id];
|
|
|
|
|
+ if (button == nullptr) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ button->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
+ connect(button, &QPushButton::clicked, [=, this] {
|
|
|
|
|
+ HandleClick(
|
|
|
|
|
+ motion_map[motion_id],
|
|
|
|
|
+ [=, this](Common::ParamPackage params) {
|
|
|
|
|
+ motions_param[motion_id] = std::move(params);
|
|
|
|
|
+ },
|
|
|
|
|
+ InputCommon::Polling::DeviceType::Motion);
|
|
|
|
|
+ });
|
|
|
|
|
+ connect(button, &QPushButton::customContextMenuRequested,
|
|
|
|
|
+ [=, this](const QPoint& menu_location) {
|
|
|
|
|
+ QMenu context_menu;
|
|
|
|
|
+ context_menu.addAction(tr("Clear"), [&] {
|
|
|
|
|
+ motions_param[motion_id].Clear();
|
|
|
|
|
+ motion_map[motion_id]->setText(tr("[not set]"));
|
|
|
|
|
+ });
|
|
|
|
|
+ context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Handle clicks for the modifier buttons as well.
|
|
// Handle clicks for the modifier buttons as well.
|
|
|
ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
|
|
ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
|
|
|
ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
|
|
ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
|
|
@@ -385,9 +425,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
|
|
|
UpdateControllerIcon();
|
|
UpdateControllerIcon();
|
|
|
UpdateControllerAvailableButtons();
|
|
UpdateControllerAvailableButtons();
|
|
|
|
|
+ UpdateMotionButtons();
|
|
|
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
|
|
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
|
|
|
UpdateControllerIcon();
|
|
UpdateControllerIcon();
|
|
|
UpdateControllerAvailableButtons();
|
|
UpdateControllerAvailableButtons();
|
|
|
|
|
+ UpdateMotionButtons();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
|
connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
|
@@ -417,6 +459,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ if (input_subsystem->GetUDPMotions()->IsPolling()) {
|
|
|
|
|
+ params = input_subsystem->GetUDPMotions()->GetNextInput();
|
|
|
|
|
+ if (params.Has("engine")) {
|
|
|
|
|
+ SetPollingResult(params, false);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
for (auto& poller : device_pollers) {
|
|
for (auto& poller : device_pollers) {
|
|
|
params = poller->GetNextInput();
|
|
params = poller->GetNextInput();
|
|
|
if (params.Has("engine")) {
|
|
if (params.Has("engine")) {
|
|
@@ -448,6 +497,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ auto& motions = player.motions;
|
|
|
|
|
+ std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
|
|
|
|
|
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
|
|
|
|
|
+
|
|
|
player.controller_type =
|
|
player.controller_type =
|
|
|
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
|
|
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
|
|
|
player.connected = ui->groupConnectedController->isChecked();
|
|
player.connected = ui->groupConnectedController->isChecked();
|
|
@@ -501,6 +554,8 @@ void ConfigureInputPlayer::LoadConfiguration() {
|
|
|
[](const std::string& str) { return Common::ParamPackage(str); });
|
|
[](const std::string& str) { return Common::ParamPackage(str); });
|
|
|
std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
|
|
std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
|
|
|
[](const std::string& str) { return Common::ParamPackage(str); });
|
|
[](const std::string& str) { return Common::ParamPackage(str); });
|
|
|
|
|
+ std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
|
|
|
|
|
+ [](const std::string& str) { return Common::ParamPackage(str); });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
UpdateUI();
|
|
UpdateUI();
|
|
@@ -544,6 +599,12 @@ void ConfigureInputPlayer::RestoreDefaults() {
|
|
|
SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
|
|
SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
|
|
|
|
+ motions_param[motion_id] = Common::ParamPackage{
|
|
|
|
|
+ InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
UpdateUI();
|
|
UpdateUI();
|
|
|
UpdateInputDevices();
|
|
UpdateInputDevices();
|
|
|
ui->comboControllerType->setCurrentIndex(0);
|
|
ui->comboControllerType->setCurrentIndex(0);
|
|
@@ -573,6 +634,15 @@ void ConfigureInputPlayer::ClearAll() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
|
|
|
|
+ const auto* const button = motion_map[motion_id];
|
|
|
|
|
+ if (button == nullptr || !button->isEnabled()) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ motions_param[motion_id].Clear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
UpdateUI();
|
|
UpdateUI();
|
|
|
UpdateInputDevices();
|
|
UpdateInputDevices();
|
|
|
}
|
|
}
|
|
@@ -582,6 +652,10 @@ void ConfigureInputPlayer::UpdateUI() {
|
|
|
button_map[button]->setText(ButtonToText(buttons_param[button]));
|
|
button_map[button]->setText(ButtonToText(buttons_param[button]));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
|
|
|
|
+ motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
|
|
ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
|
|
|
ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
|
|
ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
|
|
|
|
|
|
|
@@ -659,7 +733,11 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
|
|
|
void ConfigureInputPlayer::HandleClick(
|
|
void ConfigureInputPlayer::HandleClick(
|
|
|
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
|
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
|
|
InputCommon::Polling::DeviceType type) {
|
|
InputCommon::Polling::DeviceType type) {
|
|
|
- button->setText(tr("[waiting]"));
|
|
|
|
|
|
|
+ if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
|
|
|
|
|
+ button->setText(tr("Shake!"));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ button->setText(tr("[waiting]"));
|
|
|
|
|
+ }
|
|
|
button->setFocus();
|
|
button->setFocus();
|
|
|
|
|
|
|
|
// The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
|
|
// The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
|
|
@@ -683,6 +761,10 @@ void ConfigureInputPlayer::HandleClick(
|
|
|
input_subsystem->GetGCAnalogs()->BeginConfiguration();
|
|
input_subsystem->GetGCAnalogs()->BeginConfiguration();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (type == InputCommon::Polling::DeviceType::Motion) {
|
|
|
|
|
+ input_subsystem->GetUDPMotions()->BeginConfiguration();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
|
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
|
|
poll_timer->start(50); // Check for new inputs every 50ms
|
|
poll_timer->start(50); // Check for new inputs every 50ms
|
|
|
}
|
|
}
|
|
@@ -700,6 +782,8 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
|
|
|
input_subsystem->GetGCButtons()->EndConfiguration();
|
|
input_subsystem->GetGCButtons()->EndConfiguration();
|
|
|
input_subsystem->GetGCAnalogs()->EndConfiguration();
|
|
input_subsystem->GetGCAnalogs()->EndConfiguration();
|
|
|
|
|
|
|
|
|
|
+ input_subsystem->GetUDPMotions()->EndConfiguration();
|
|
|
|
|
+
|
|
|
if (!abort) {
|
|
if (!abort) {
|
|
|
(*input_setter)(params);
|
|
(*input_setter)(params);
|
|
|
}
|
|
}
|
|
@@ -832,6 +916,37 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void ConfigureInputPlayer::UpdateMotionButtons() {
|
|
|
|
|
+ if (debug) {
|
|
|
|
|
+ // Motion isn't used with the debug controller, hide both groupboxes.
|
|
|
|
|
+ ui->buttonMotionLeftGroup->hide();
|
|
|
|
|
+ ui->buttonMotionRightGroup->hide();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
|
|
|
|
|
+ switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
|
|
|
|
|
+ case Settings::ControllerType::ProController:
|
|
|
|
|
+ case Settings::ControllerType::LeftJoycon:
|
|
|
|
|
+ case Settings::ControllerType::Handheld:
|
|
|
|
|
+ // Show "Motion 1" and hide "Motion 2".
|
|
|
|
|
+ ui->buttonMotionLeftGroup->show();
|
|
|
|
|
+ ui->buttonMotionRightGroup->hide();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case Settings::ControllerType::RightJoycon:
|
|
|
|
|
+ // Show "Motion 2" and hide "Motion 1".
|
|
|
|
|
+ ui->buttonMotionLeftGroup->hide();
|
|
|
|
|
+ ui->buttonMotionRightGroup->show();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case Settings::ControllerType::DualJoyconDetached:
|
|
|
|
|
+ default:
|
|
|
|
|
+ // Show both "Motion 1/2".
|
|
|
|
|
+ ui->buttonMotionLeftGroup->show();
|
|
|
|
|
+ ui->buttonMotionRightGroup->show();
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
|
|
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
|
|
|
if (bottom_row == nullptr) {
|
|
if (bottom_row == nullptr) {
|
|
|
return;
|
|
return;
|