mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Merge pull request #7829 from jordan-woyak/detect-input-improve
ControllerInterface/DolphinQt: Improve input detection.
This commit is contained in:
@ -136,8 +136,8 @@ void IOWindow::ConnectWidgets()
|
||||
connect(m_or_button, &QPushButton::clicked, [this] { AppendSelectedOption(" | "); });
|
||||
connect(m_not_button, &QPushButton::clicked, [this] { AppendSelectedOption("!"); });
|
||||
|
||||
connect(m_type == IOWindow::Type::Input ? m_detect_button : m_test_button, &QPushButton::clicked,
|
||||
this, &IOWindow::OnDetectButtonPressed);
|
||||
connect(m_detect_button, &QPushButton::clicked, this, &IOWindow::OnDetectButtonPressed);
|
||||
connect(m_test_button, &QPushButton::clicked, this, &IOWindow::OnTestButtonPressed);
|
||||
|
||||
connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed);
|
||||
connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged);
|
||||
@ -180,35 +180,22 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button)
|
||||
|
||||
void IOWindow::OnDetectButtonPressed()
|
||||
{
|
||||
installEventFilter(BlockUserInputFilter::Instance());
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
const auto expression =
|
||||
MappingCommon::DetectExpression(m_detect_button, g_controller_interface, {m_devq.ToString()},
|
||||
m_devq, MappingCommon::Quote::Off);
|
||||
|
||||
std::thread([this] {
|
||||
auto* btn = m_type == IOWindow::Type::Input ? m_detect_button : m_test_button;
|
||||
const auto old_label = btn->text();
|
||||
if (expression.isEmpty())
|
||||
return;
|
||||
|
||||
btn->setText(QStringLiteral("..."));
|
||||
const auto list = m_option_list->findItems(expression, Qt::MatchFixedString);
|
||||
|
||||
const auto expr = MappingCommon::DetectExpression(
|
||||
m_reference, g_controller_interface.FindDevice(m_devq).get(), m_devq,
|
||||
MappingCommon::Quote::Off);
|
||||
if (!list.empty())
|
||||
m_option_list->setCurrentItem(list[0]);
|
||||
}
|
||||
|
||||
btn->setText(old_label);
|
||||
|
||||
if (!expr.isEmpty())
|
||||
{
|
||||
const auto list = m_option_list->findItems(expr, Qt::MatchFixedString);
|
||||
|
||||
if (!list.empty())
|
||||
m_option_list->setCurrentItem(list[0]);
|
||||
}
|
||||
|
||||
releaseMouse();
|
||||
releaseKeyboard();
|
||||
removeEventFilter(BlockUserInputFilter::Instance());
|
||||
})
|
||||
.detach();
|
||||
void IOWindow::OnTestButtonPressed()
|
||||
{
|
||||
MappingCommon::TestOutput(m_test_button, static_cast<OutputReference*>(m_reference));
|
||||
}
|
||||
|
||||
void IOWindow::OnRangeChanged(int value)
|
||||
|
@ -48,6 +48,7 @@ private:
|
||||
void OnDialogButtonPressed(QAbstractButton* button);
|
||||
void OnDeviceChanged(const QString& device);
|
||||
void OnDetectButtonPressed();
|
||||
void OnTestButtonPressed();
|
||||
void OnRangeChanged(int range);
|
||||
|
||||
void AppendSelectedOption(const std::string& prefix);
|
||||
|
@ -2,8 +2,7 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <future>
|
||||
#include <utility>
|
||||
#include "DolphinQt/Config/Mapping/MappingButton.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFontMetrics>
|
||||
@ -12,8 +11,6 @@
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
#include "DolphinQt/Config/Mapping/MappingButton.h"
|
||||
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
@ -103,96 +100,33 @@ void MappingButton::Detect()
|
||||
if (m_parent->GetDevice() == nullptr || !m_reference->IsInput())
|
||||
return;
|
||||
|
||||
installEventFilter(BlockUserInputFilter::Instance());
|
||||
grabKeyboard();
|
||||
const auto default_device_qualifier = m_parent->GetController()->GetDefaultDevice();
|
||||
|
||||
// Make sure that we don't block event handling
|
||||
QueueOnObject(this, [this] {
|
||||
setText(QStringLiteral("..."));
|
||||
QString expression;
|
||||
|
||||
// The button text won't be updated if we don't process events here
|
||||
QApplication::processEvents();
|
||||
if (m_parent->GetParent()->IsMappingAllDevices())
|
||||
{
|
||||
expression = MappingCommon::DetectExpression(this, g_controller_interface,
|
||||
g_controller_interface.GetAllDeviceStrings(),
|
||||
default_device_qualifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
expression = MappingCommon::DetectExpression(this, g_controller_interface,
|
||||
{default_device_qualifier.ToString()},
|
||||
default_device_qualifier);
|
||||
}
|
||||
|
||||
// Avoid that the button press itself is registered as an event
|
||||
Common::SleepCurrentThread(100);
|
||||
if (expression.isEmpty())
|
||||
return;
|
||||
|
||||
std::vector<std::future<std::pair<QString, QString>>> futures;
|
||||
QString expr;
|
||||
m_reference->SetExpression(expression.toStdString());
|
||||
m_parent->SaveSettings();
|
||||
Update();
|
||||
m_parent->GetController()->UpdateReferences(g_controller_interface);
|
||||
|
||||
if (m_parent->GetParent()->IsMappingAllDevices())
|
||||
{
|
||||
for (const std::string& device_str : g_controller_interface.GetAllDeviceStrings())
|
||||
{
|
||||
ciface::Core::DeviceQualifier devq;
|
||||
devq.FromString(device_str);
|
||||
|
||||
auto dev = g_controller_interface.FindDevice(devq);
|
||||
|
||||
auto future = std::async(std::launch::async, [this, devq, dev, device_str] {
|
||||
return std::make_pair(
|
||||
QString::fromStdString(device_str),
|
||||
MappingCommon::DetectExpression(m_reference, dev.get(),
|
||||
m_parent->GetController()->GetDefaultDevice()));
|
||||
});
|
||||
|
||||
futures.push_back(std::move(future));
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
for (auto& future : futures)
|
||||
{
|
||||
const auto status = future.wait_for(std::chrono::milliseconds(10));
|
||||
if (status == std::future_status::ready)
|
||||
{
|
||||
const auto pair = future.get();
|
||||
|
||||
done = true;
|
||||
|
||||
if (pair.second.isEmpty())
|
||||
break;
|
||||
|
||||
expr = QStringLiteral("`%1:%2`")
|
||||
.arg(pair.first)
|
||||
.arg(pair.second.startsWith(QLatin1Char('`')) ? pair.second.mid(1) :
|
||||
pair.second);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto dev = m_parent->GetDevice();
|
||||
expr = MappingCommon::DetectExpression(m_reference, dev.get(),
|
||||
m_parent->GetController()->GetDefaultDevice());
|
||||
}
|
||||
|
||||
releaseKeyboard();
|
||||
removeEventFilter(BlockUserInputFilter::Instance());
|
||||
|
||||
if (!expr.isEmpty())
|
||||
{
|
||||
m_reference->SetExpression(expr.toStdString());
|
||||
m_parent->SaveSettings();
|
||||
Update();
|
||||
m_parent->GetController()->UpdateReferences(g_controller_interface);
|
||||
|
||||
if (m_parent->IsIterativeInput())
|
||||
m_parent->NextButton(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnButtonTimeout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MappingButton::OnButtonTimeout()
|
||||
{
|
||||
setText(ToDisplayString(QString::fromStdString(m_reference->GetExpression())));
|
||||
if (m_parent->IsIterativeInput())
|
||||
m_parent->NextButton(this);
|
||||
}
|
||||
|
||||
void MappingButton::Clear()
|
||||
|
@ -30,7 +30,6 @@ signals:
|
||||
private:
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
void OnButtonTimeout();
|
||||
void Connect();
|
||||
|
||||
MappingWidget* m_parent;
|
||||
|
@ -4,16 +4,23 @@
|
||||
|
||||
#include "DolphinQt/Config/Mapping/MappingCommon.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
|
||||
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
|
||||
#include "InputCommon/ControlReference/ControlReference.h"
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
|
||||
#include "Common/Thread.h"
|
||||
|
||||
namespace MappingCommon
|
||||
{
|
||||
constexpr int INPUT_DETECT_TIME = 3000;
|
||||
constexpr int OUTPUT_DETECT_TIME = 2000;
|
||||
constexpr int OUTPUT_TEST_TIME = 2000;
|
||||
|
||||
QString GetExpressionForControl(const QString& control_name,
|
||||
const ciface::Core::DeviceQualifier& control_device,
|
||||
@ -41,18 +48,56 @@ QString GetExpressionForControl(const QString& control_name,
|
||||
return expr;
|
||||
}
|
||||
|
||||
QString DetectExpression(ControlReference* reference, ciface::Core::Device* device,
|
||||
QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& device_container,
|
||||
const std::vector<std::string>& device_strings,
|
||||
const ciface::Core::DeviceQualifier& default_device, Quote quote)
|
||||
{
|
||||
const int ms = reference->IsInput() ? INPUT_DETECT_TIME : OUTPUT_DETECT_TIME;
|
||||
button->installEventFilter(BlockUserInputFilter::Instance());
|
||||
button->grabKeyboard();
|
||||
button->grabMouse();
|
||||
|
||||
ciface::Core::Device::Control* const ctrl = reference->Detect(ms, device);
|
||||
const auto old_text = button->text();
|
||||
button->setText(QStringLiteral("..."));
|
||||
|
||||
if (ctrl)
|
||||
{
|
||||
return MappingCommon::GetExpressionForControl(QString::fromStdString(ctrl->GetName()),
|
||||
default_device, default_device, quote);
|
||||
}
|
||||
return QStringLiteral("");
|
||||
// The button text won't be updated if we don't process events here
|
||||
QApplication::processEvents();
|
||||
|
||||
// Avoid that the button press itself is registered as an event
|
||||
Common::SleepCurrentThread(100);
|
||||
|
||||
std::shared_ptr<ciface::Core::Device> device;
|
||||
ciface::Core::Device::Input* input;
|
||||
std::tie(device, input) = device_container.DetectInput(INPUT_DETECT_TIME, device_strings);
|
||||
|
||||
button->releaseMouse();
|
||||
button->releaseKeyboard();
|
||||
button->removeEventFilter(BlockUserInputFilter::Instance());
|
||||
|
||||
button->setText(old_text);
|
||||
|
||||
if (!input)
|
||||
return {};
|
||||
|
||||
ciface::Core::DeviceQualifier device_qualifier;
|
||||
device_qualifier.FromDevice(device.get());
|
||||
|
||||
return MappingCommon::GetExpressionForControl(QString::fromStdString(input->GetName()),
|
||||
device_qualifier, default_device, quote);
|
||||
}
|
||||
|
||||
void TestOutput(QPushButton* button, OutputReference* reference)
|
||||
{
|
||||
const auto old_text = button->text();
|
||||
button->setText(QStringLiteral("..."));
|
||||
|
||||
// The button text won't be updated if we don't process events here
|
||||
QApplication::processEvents();
|
||||
|
||||
reference->State(1.0);
|
||||
Common::SleepCurrentThread(OUTPUT_TEST_TIME);
|
||||
reference->State(0.0);
|
||||
|
||||
button->setText(old_text);
|
||||
}
|
||||
|
||||
} // namespace MappingCommon
|
||||
|
@ -4,17 +4,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class QString;
|
||||
class ControlReference;
|
||||
class OutputReference;
|
||||
class QPushButton;
|
||||
|
||||
namespace ciface
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
class Device;
|
||||
class DeviceContainer;
|
||||
class DeviceQualifier;
|
||||
}
|
||||
}
|
||||
} // namespace Core
|
||||
} // namespace ciface
|
||||
|
||||
namespace MappingCommon
|
||||
{
|
||||
@ -28,7 +32,12 @@ QString GetExpressionForControl(const QString& control_name,
|
||||
const ciface::Core::DeviceQualifier& control_device,
|
||||
const ciface::Core::DeviceQualifier& default_device,
|
||||
Quote quote = Quote::On);
|
||||
QString DetectExpression(ControlReference* reference, ciface::Core::Device* device,
|
||||
|
||||
QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& device_container,
|
||||
const std::vector<std::string>& device_strings,
|
||||
const ciface::Core::DeviceQualifier& default_device,
|
||||
Quote quote = Quote::On);
|
||||
|
||||
void TestOutput(QPushButton* button, OutputReference* reference);
|
||||
|
||||
} // namespace MappingCommon
|
||||
|
Reference in New Issue
Block a user