Merge pull request #4594 from german77/MotionHID

hid/configuration: Implement motion controls to HID
This commit is contained in:
bunnei
2020-09-17 12:39:01 -07:00
committed by GitHub
23 changed files with 940 additions and 156 deletions

View File

@ -36,6 +36,11 @@ const std::array<int, Settings::NativeButton::NumButtons> Config::default_button
Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
};
const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
Qt::Key_7,
Qt::Key_8,
};
const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{
Qt::Key_Up,
@ -284,6 +289,22 @@ void Config::ReadPlayerValues() {
}
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param =
InputCommon::GenerateKeyboardParam(default_motions[i]);
auto& player_motions = player.motions[i];
player_motions = qt_config
->value(QStringLiteral("player_%1_").arg(p) +
QString::fromUtf8(Settings::NativeMotion::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_motions.empty()) {
player_motions = default_param;
}
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@ -424,6 +445,7 @@ void Config::ReadControlValues() {
Settings::values.vibration_enabled =
ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool();
Settings::values.use_docked_mode =
ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
@ -922,6 +944,14 @@ void Config::SavePlayerValues() {
QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param =
InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteSetting(QStringLiteral("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeMotion::mapping[i]),
QString::fromStdString(player.motions[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@ -1062,6 +1092,7 @@ void Config::SaveControlValues() {
SaveMotionTouchValues();
WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
WriteSetting(QStringLiteral("motion_device"),
QString::fromStdString(Settings::values.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));

View File

@ -23,6 +23,7 @@ public:
void Save();
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
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, Settings::NativeMouseButton::NumMouseButtons>

View File

@ -146,6 +146,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
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(); });
@ -172,6 +176,7 @@ void ConfigureInput::ApplyConfiguration() {
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
Settings::values.motion_enabled = ui->motionGroup->isChecked();
}
void ConfigureInput::changeEvent(QEvent* event) {
@ -191,6 +196,7 @@ void ConfigureInput::LoadConfiguration() {
UpdateDockedState(Settings::values.players[8].connected);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
ui->motionGroup->setChecked(Settings::values.motion_enabled);
}
void ConfigureInput::LoadPlayerControllerIndices() {
@ -217,6 +223,7 @@ void ConfigureInput::RestoreDefaults() {
ui->radioDocked->setChecked(true);
ui->radioUndocked->setChecked(false);
ui->vibrationGroup->setChecked(true);
ui->motionGroup->setChecked(true);
}
void ConfigureInput::UpdateDockedState(bool is_handheld) {

View File

@ -18,6 +18,7 @@
#include "core/hle/service/sm/sm.h"
#include "input_common/gcadapter/gc_poller.h"
#include "input_common/main.h"
#include "input_common/udp/udp.h"
#include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
@ -149,6 +150,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
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.Has("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_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
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]);
}
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.
ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
@ -385,9 +425,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
UpdateControllerIcon();
UpdateControllerAvailableButtons();
UpdateMotionButtons();
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
UpdateControllerIcon();
UpdateControllerAvailableButtons();
UpdateMotionButtons();
});
connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
@ -417,6 +459,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
return;
}
}
if (input_subsystem->GetUDPMotions()->IsPolling()) {
params = input_subsystem->GetUDPMotions()->GetNextInput();
if (params.Has("engine")) {
SetPollingResult(params, false);
return;
}
}
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
if (params.Has("engine")) {
@ -448,6 +497,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
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 =
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
player.connected = ui->groupConnectedController->isChecked();
@ -501,6 +554,8 @@ void ConfigureInputPlayer::LoadConfiguration() {
[](const std::string& str) { return Common::ParamPackage(str); });
std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
[](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();
@ -544,6 +599,12 @@ void ConfigureInputPlayer::RestoreDefaults() {
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();
UpdateInputDevices();
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();
UpdateInputDevices();
}
@ -582,6 +652,10 @@ void ConfigureInputPlayer::UpdateUI() {
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->buttonRStickMod->setText(ButtonToText(rstick_mod));
@ -659,7 +733,11 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
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();
// 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();
}
if (type == InputCommon::Polling::DeviceType::Motion) {
input_subsystem->GetUDPMotions()->BeginConfiguration();
}
timeout_timer->start(2500); // Cancel after 2.5 seconds
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->GetGCAnalogs()->EndConfiguration();
input_subsystem->GetUDPMotions()->EndConfiguration();
if (!abort) {
(*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) {
if (bottom_row == nullptr) {
return;

View File

@ -107,6 +107,9 @@ private:
/// Hides and disables controller settings based on the current controller type.
void UpdateControllerAvailableButtons();
/// Shows or hides motion groupboxes based on the current controller type.
void UpdateMotionButtons();
/// Gets the default controller mapping for this device and auto configures the input to match.
void UpdateMappingWithDefaults();
@ -128,11 +131,14 @@ private:
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
/// Each button input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
/// Each motion input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map;
/// Extra buttons for the modifiers.
Common::ParamPackage lstick_mod;
Common::ParamPackage rstick_mod;

View File

@ -1983,6 +1983,9 @@
<property name="spacing">
<number>3</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacerMiscButtons1">
<property name="orientation">
@ -1990,12 +1993,110 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>0</height>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="buttonMotionLeftGroup">
<property name="title">
<string>Motion 1</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
<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="buttonMotionLeft">
<property name="minimumSize">
<size>
<width>57</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
</property>
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="buttonMotionRightGroup">
<property name="title">
<string>Motion 2</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
<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="buttonMotionRight">
<property name="minimumSize">
<size>
<width>57</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
</property>
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacerMiscButtons4">
<property name="orientation">
@ -2003,8 +2104,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>0</height>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>