InputManager: Support inverted full axis

i.e. pedals
This commit is contained in:
Connor McLaughlin
2023-01-15 14:00:51 +10:00
parent 01270bac35
commit 395e9a934b
39 changed files with 1022 additions and 366 deletions

View File

@ -370,7 +370,7 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
for (u32 i = 0; i < cinfo->num_bindings; i++)
{
const Controller::ControllerBindingInfo& bi = cinfo->bindings[i];
if (bi.type == Controller::ControllerBindingType::Motor)
if (bi.type == InputBindingInfo::Type::Motor)
continue;
QListWidgetItem* item = new QListWidgetItem();
@ -383,7 +383,8 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
m_frequency = dialog->getIntValue(section.c_str(), fmt::format("Macro{}Frequency", index + 1u).c_str(), 0);
updateFrequencyText();
m_ui.trigger->initialize(dialog->getProfileSettingsInterface(), section, fmt::format("Macro{}", index + 1u));
m_ui.trigger->initialize(dialog->getProfileSettingsInterface(), InputBindingInfo::Type::Macro, section,
fmt::format("Macro{}", index + 1u));
connect(m_ui.increaseFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(1); });
connect(m_ui.decreateFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(-1); });
@ -453,7 +454,7 @@ void ControllerMacroEditWidget::updateBinds()
for (u32 i = 0, bind_index = 0; i < cinfo->num_bindings; i++)
{
const Controller::ControllerBindingInfo& bi = cinfo->bindings[i];
if (bi.type == Controller::ControllerBindingType::Motor)
if (bi.type == InputBindingInfo::Type::Motor)
continue;
const QListWidgetItem* item = m_ui.bindList->item(static_cast<int>(bind_index));
@ -740,17 +741,18 @@ void ControllerBindingWidget_Base::initBindingWidgets()
for (u32 i = 0; i < cinfo->num_bindings; i++)
{
const Controller::ControllerBindingInfo& bi = cinfo->bindings[i];
if (bi.type == Controller::ControllerBindingType::Unknown || bi.type == Controller::ControllerBindingType::Motor)
continue;
InputBindingWidget* widget = findChild<InputBindingWidget*>(QString::fromUtf8(bi.name));
if (!widget)
if (bi.type == InputBindingInfo::Type::Axis || bi.type == InputBindingInfo::Type::HalfAxis ||
bi.type == InputBindingInfo::Type::Button || bi.type == InputBindingInfo::Type::Pointer)
{
Log_ErrorPrintf("No widget found for '%s' (%s)", bi.name, cinfo->name);
continue;
}
InputBindingWidget* widget = findChild<InputBindingWidget*>(QString::fromUtf8(bi.name));
if (!widget)
{
Log_ErrorPrintf("No widget found for '%s' (%s)", bi.name, cinfo->name);
continue;
}
widget->initialize(sif, config_section, bi.name);
widget->initialize(sif, bi.type, config_section, bi.name);
}
}
switch (cinfo->vibration_caps)

View File

@ -237,7 +237,7 @@ void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QList<InputBind
for (const InputBindingKey key : motors)
{
const std::string key_str(InputManager::ConvertInputBindingKeyToString(key));
const std::string key_str(InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type::Motor, key));
if (!key_str.empty())
m_vibration_motors.push_back(QString::fromStdString(key_str));
}

View File

@ -73,8 +73,8 @@ void HotkeySettingsWidget::createButtons()
QLabel* label = new QLabel(qApp->translate("Hotkeys", hotkey->display_name), m_container);
layout->addWidget(label, target_row, 0);
InputBindingWidget* bind =
new InputBindingWidget(m_container, m_dialog->getProfileSettingsInterface(), "Hotkeys", hotkey->name);
InputBindingWidget* bind = new InputBindingWidget(m_container, m_dialog->getProfileSettingsInterface(),
InputBindingInfo::Type::Button, "Hotkeys", hotkey->name);
bind->setMinimumWidth(300);
layout->addWidget(bind, target_row, 1);
}

View File

@ -11,10 +11,11 @@
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
InputBindingDialog::InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name,
InputBindingDialog::InputBindingDialog(SettingsInterface* sif, InputBindingInfo::Type bind_type,
std::string section_name, std::string key_name,
std::vector<std::string> bindings, QWidget* parent)
: QDialog(parent), m_sif(sif), m_section_name(std::move(section_name)), m_key_name(std::move(key_name)),
m_bindings(std::move(bindings))
: QDialog(parent), m_sif(sif), m_bind_type(bind_type), m_section_name(std::move(section_name)),
m_key_name(std::move(key_name)), m_bindings(std::move(bindings))
{
m_ui.setupUi(this);
m_ui.title->setText(
@ -53,8 +54,9 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick)
{
// double clicks get triggered if we click bind, then click again quickly.
unsigned button_index = CountTrailingZeros(static_cast<u32>(static_cast<const QMouseEvent*>(event)->button()));
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
return true;
}
else if (event_type == QEvent::Wheel)
@ -64,7 +66,7 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
if (dx != 0.0f)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::WheelX));
key.negative = (dx < 0.0f);
key.modifier = dx < 0.0f ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
}
@ -72,7 +74,7 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
if (dy != 0.0f)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::WheelY));
key.negative = (dy < 0.0f);
key.modifier = dy < 0.0f ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
}
@ -89,20 +91,20 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
// if we've moved more than a decent distance from the center of the widget, bind it.
// this is so we don't accidentally bind to the mouse if you bump it while reaching for your pad.
static constexpr const s32 THRESHOLD = 50;
const QPointF diff(static_cast<QMouseEvent*>(event)->globalPosition() - m_input_listen_start_position);
const QPoint diff(static_cast<QMouseEvent*>(event)->globalPosition().toPoint() - m_input_listen_start_position);
bool has_one = false;
if (std::abs(diff.x()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::X));
key.negative = (diff.x() < 0);
key.modifier = diff.x() < 0 ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
has_one = true;
}
if (std::abs(diff.y()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::Y));
key.negative = (diff.y() < 0);
key.modifier = diff.y() < 0 ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
has_one = true;
}
@ -132,6 +134,7 @@ void InputBindingDialog::onInputListenTimerTimeout()
void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds)
{
m_value_ranges.clear();
m_new_bindings.clear();
m_mouse_mapping_enabled = InputBindingWidget::isMouseMappingEnabled();
m_input_listen_start_position = QCursor::pos();
@ -179,7 +182,7 @@ void InputBindingDialog::addNewBinding()
return;
const std::string new_binding(
InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size()));
InputManager::ConvertInputBindingKeysToString(m_bind_type, m_new_bindings.data(), m_new_bindings.size()));
if (!new_binding.empty())
{
if (std::find(m_bindings.begin(), m_bindings.end(), new_binding) != m_bindings.end())
@ -248,14 +251,37 @@ void InputBindingDialog::saveListToSettings()
void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float value)
{
const float abs_value = std::abs(value);
if (!isListeningForInput())
return;
for (InputBindingKey other_key : m_new_bindings)
float initial_value = value;
float min_value = value;
auto it = std::find_if(m_value_ranges.begin(), m_value_ranges.end(),
[key](const auto& it) { return it.first.bits == key.bits; });
if (it != m_value_ranges.end())
{
initial_value = it->second.first;
min_value = it->second.second = std::min(it->second.second, value);
}
else
{
m_value_ranges.emplace_back(key, std::make_pair(initial_value, min_value));
}
const float abs_value = std::abs(value);
const bool reverse_threshold = (key.source_subtype == InputSubclass::ControllerAxis && initial_value > 0.5f);
for (InputBindingKey& other_key : m_new_bindings)
{
if (other_key.MaskDirection() == key.MaskDirection())
{
if (abs_value < 0.5f)
// for pedals, we wait for it to go back to near its starting point to commit the binding
if ((reverse_threshold ? ((initial_value - value) <= 0.25f) : (abs_value < 0.5f)))
{
// did we go the full range?
if (reverse_threshold && initial_value > 0.5f && min_value <= -0.5f)
other_key.modifier = InputModifier::FullAxis;
// if this key is in our new binding list, it's a "release", and we're done
addNewBinding();
stopListeningForInput();
@ -268,10 +294,11 @@ void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float val
}
// new binding, add it to the list, but wait for a decent distance first, and then wait for release
if (abs_value >= 0.5f)
if ((reverse_threshold ? (abs_value < 0.5f) : (abs_value >= 0.5f)))
{
InputBindingKey key_to_add = key;
key_to_add.negative = (value < 0.0f);
key_to_add.modifier = (value < 0.0f && !reverse_threshold) ? InputModifier::Negate : InputModifier::None;
key_to_add.invert = reverse_threshold;
m_new_bindings.push_back(key_to_add);
}
}

View File

@ -17,8 +17,8 @@ class InputBindingDialog : public QDialog
Q_OBJECT
public:
InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name,
std::vector<std::string> bindings, QWidget* parent);
InputBindingDialog(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name,
std::string key_name, std::vector<std::string> bindings, QWidget* parent);
~InputBindingDialog();
protected Q_SLOTS:
@ -51,13 +51,15 @@ protected:
Ui::InputBindingDialog m_ui;
SettingsInterface* m_sif;
InputBindingInfo::Type m_bind_type;
std::string m_section_name;
std::string m_key_name;
std::vector<std::string> m_bindings;
std::vector<InputBindingKey> m_new_bindings;
std::vector<std::pair<InputBindingKey, std::pair<float, float>>> m_value_ranges;
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
QPointF m_input_listen_start_position{};
QPoint m_input_listen_start_position{};
bool m_mouse_mapping_enabled = false;
};

View File

@ -22,8 +22,8 @@ InputBindingWidget::InputBindingWidget(QWidget* parent) : QPushButton(parent)
connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked);
}
InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name,
std::string key_name)
InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif, InputBindingInfo::Type bind_type,
std::string section_name, std::string key_name)
: QPushButton(parent)
{
setMinimumWidth(225);
@ -31,7 +31,7 @@ InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif,
connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked);
initialize(sif, std::move(section_name), std::move(key_name));
initialize(sif, bind_type, std::move(section_name), std::move(key_name));
}
InputBindingWidget::~InputBindingWidget()
@ -44,9 +44,11 @@ bool InputBindingWidget::isMouseMappingEnabled()
return Host::GetBaseBoolSettingValue("UI", "EnableMouseMapping", false);
}
void InputBindingWidget::initialize(SettingsInterface* sif, std::string section_name, std::string key_name)
void InputBindingWidget::initialize(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name,
std::string key_name)
{
m_sif = sif;
m_bind_type = bind_type;
m_section_name = std::move(section_name);
m_key_name = std::move(key_name);
reloadBinding();
@ -109,8 +111,9 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick)
{
// double clicks get triggered if we click bind, then click again quickly.
const u32 button_index = CountTrailingZeros(static_cast<u32>(static_cast<const QMouseEvent*>(event)->button()));
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
return true;
}
else if (event_type == QEvent::Wheel)
@ -120,7 +123,7 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
if (dx != 0.0f)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::WheelX));
key.negative = (dx < 0.0f);
key.modifier = dx < 0.0f ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
}
@ -128,7 +131,7 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
if (dy != 0.0f)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::WheelY));
key.negative = (dy < 0.0f);
key.modifier = dy < 0.0f ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
}
@ -145,20 +148,20 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
// if we've moved more than a decent distance from the center of the widget, bind it.
// this is so we don't accidentally bind to the mouse if you bump it while reaching for your pad.
static constexpr const s32 THRESHOLD = 50;
const QPointF diff(static_cast<QMouseEvent*>(event)->globalPosition() - m_input_listen_start_position);
const QPoint diff(static_cast<QMouseEvent*>(event)->globalPosition().toPoint() - m_input_listen_start_position);
bool has_one = false;
if (std::abs(diff.x()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::X));
key.negative = (diff.x() < 0);
key.modifier = diff.x() < 0 ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
has_one = true;
}
if (std::abs(diff.y()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::Y));
key.negative = (diff.y() < 0);
key.modifier = diff.y() < 0 ? InputModifier::Negate : InputModifier::None;
m_new_bindings.push_back(key);
has_one = true;
}
@ -205,8 +208,8 @@ void InputBindingWidget::setNewBinding()
if (m_new_bindings.empty())
return;
const std::string new_binding(
InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size()));
std::string new_binding(
InputManager::ConvertInputBindingKeysToString(m_bind_type, m_new_bindings.data(), m_new_bindings.size()));
if (!new_binding.empty())
{
if (m_sif)
@ -280,6 +283,7 @@ void InputBindingWidget::onInputListenTimerTimeout()
void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds)
{
m_value_ranges.clear();
m_new_bindings.clear();
m_mouse_mapping_enabled = isMouseMappingEnabled();
m_input_listen_start_position = QCursor::pos();
@ -315,14 +319,37 @@ void InputBindingWidget::stopListeningForInput()
void InputBindingWidget::inputManagerHookCallback(InputBindingKey key, float value)
{
const float abs_value = std::abs(value);
if (!isListeningForInput())
return;
for (InputBindingKey other_key : m_new_bindings)
float initial_value = value;
float min_value = value;
auto it = std::find_if(m_value_ranges.begin(), m_value_ranges.end(),
[key](const auto& it) { return it.first.bits == key.bits; });
if (it != m_value_ranges.end())
{
initial_value = it->second.first;
min_value = it->second.second = std::min(it->second.second, value);
}
else
{
m_value_ranges.emplace_back(key, std::make_pair(initial_value, min_value));
}
const float abs_value = std::abs(value);
const bool reverse_threshold = (key.source_subtype == InputSubclass::ControllerAxis && initial_value > 0.5f);
for (InputBindingKey& other_key : m_new_bindings)
{
if (other_key.MaskDirection() == key.MaskDirection())
{
if (abs_value < 0.5f)
// for pedals, we wait for it to go back to near its starting point to commit the binding
if ((reverse_threshold ? ((initial_value - value) <= 0.25f) : (abs_value < 0.5f)))
{
// did we go the full range?
if (reverse_threshold && initial_value > 0.5f && min_value <= -0.5f)
other_key.modifier = InputModifier::FullAxis;
// if this key is in our new binding list, it's a "release", and we're done
setNewBinding();
stopListeningForInput();
@ -335,10 +362,11 @@ void InputBindingWidget::inputManagerHookCallback(InputBindingKey key, float val
}
// new binding, add it to the list, but wait for a decent distance first, and then wait for release
if (abs_value >= 0.5f)
if ((reverse_threshold ? (abs_value < 0.5f) : (abs_value >= 0.5f)))
{
InputBindingKey key_to_add = key;
key_to_add.negative = (value < 0.0f);
key_to_add.modifier = (value < 0.0f && !reverse_threshold) ? InputModifier::Negate : InputModifier::None;
key_to_add.invert = reverse_threshold;
m_new_bindings.push_back(key_to_add);
}
}
@ -359,7 +387,8 @@ void InputBindingWidget::unhookInputManager()
void InputBindingWidget::openDialog()
{
InputBindingDialog binding_dialog(m_sif, m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this));
InputBindingDialog binding_dialog(m_sif, m_bind_type, m_section_name, m_key_name, m_bindings,
QtUtils::GetRootWidget(this));
binding_dialog.exec();
reloadBinding();
}

View File

@ -18,12 +18,14 @@ class InputBindingWidget : public QPushButton
public:
InputBindingWidget(QWidget* parent);
InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name);
InputBindingWidget(QWidget* parent, SettingsInterface* sif, InputBindingInfo::Type bind_type,
std::string section_name, std::string key_name);
~InputBindingWidget();
static bool isMouseMappingEnabled();
void initialize(SettingsInterface* sif, std::string section_name, std::string key_name);
void initialize(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name,
std::string key_name);
public Q_SLOTS:
void clearBinding();
@ -57,13 +59,15 @@ protected:
void unhookInputManager();
SettingsInterface* m_sif = nullptr;
InputBindingInfo::Type m_bind_type = InputBindingInfo::Type::Unknown;
std::string m_section_name;
std::string m_key_name;
std::vector<std::string> m_bindings;
std::vector<InputBindingKey> m_new_bindings;
std::vector<std::pair<InputBindingKey, std::pair<float, float>>> m_value_ranges;
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
QPointF m_input_listen_start_position{};
QPoint m_input_listen_start_position{};
bool m_mouse_mapping_enabled = false;
};