summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2019-02-27 18:10:18 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2019-03-03 18:39:02 -0600
commitc389d68186ea7fa95d763d7bb55bc09e5a45c4cd (patch)
tree9712b3e25f700cdbeb5b75652078867f5d8e548a /Source/Core
parent48b69ca01841ba724049895a83d8d88ec95d66de (diff)
ControllerInterface/DolphinQt: Make mapping "all devices" way less hacky.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp41
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/IOWindow.h1
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp112
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/MappingButton.h1
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp65
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/MappingCommon.h19
-rw-r--r--Source/Core/InputCommon/ControlReference/ControlReference.cpp89
-rw-r--r--Source/Core/InputCommon/ControlReference/ControlReference.h6
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Device.cpp87
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Device.h3
10 files changed, 192 insertions, 232 deletions
diff --git a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
index 8bf3ab0630..5f1b32ba83 100644
--- a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
@@ -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();
-
- btn->setText(QStringLiteral("..."));
-
- const auto expr = MappingCommon::DetectExpression(
- m_reference, g_controller_interface.FindDevice(m_devq).get(), m_devq,
- MappingCommon::Quote::Off);
-
- btn->setText(old_label);
+ if (expression.isEmpty())
+ return;
- if (!expr.isEmpty())
- {
- const auto list = m_option_list->findItems(expr, Qt::MatchFixedString);
+ const auto list = m_option_list->findItems(expression, Qt::MatchFixedString);
- if (!list.empty())
- m_option_list->setCurrentItem(list[0]);
- }
+ 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)
diff --git a/Source/Core/DolphinQt/Config/Mapping/IOWindow.h b/Source/Core/DolphinQt/Config/Mapping/IOWindow.h
index 0117393272..250b016309 100644
--- a/Source/Core/DolphinQt/Config/Mapping/IOWindow.h
+++ b/Source/Core/DolphinQt/Config/Mapping/IOWindow.h
@@ -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);
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp
index 6e1a2fa813..a068397d09 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp
@@ -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();
-
- // Make sure that we don't block event handling
- QueueOnObject(this, [this] {
- setText(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::vector<std::future<std::pair<QString, QString>>> futures;
- QString expr;
+ const auto default_device_qualifier = m_parent->GetController()->GetDefaultDevice();
- 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());
- }
+ QString expression;
- releaseKeyboard();
- removeEventFilter(BlockUserInputFilter::Instance());
+ 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);
+ }
- if (!expr.isEmpty())
- {
- m_reference->SetExpression(expr.toStdString());
- m_parent->SaveSettings();
- Update();
- m_parent->GetController()->UpdateReferences(g_controller_interface);
+ if (expression.isEmpty())
+ return;
- if (m_parent->IsIterativeInput())
- m_parent->NextButton(this);
- }
- else
- {
- OnButtonTimeout();
- }
- });
-}
+ m_reference->SetExpression(expression.toStdString());
+ m_parent->SaveSettings();
+ Update();
+ m_parent->GetController()->UpdateReferences(g_controller_interface);
-void MappingButton::OnButtonTimeout()
-{
- setText(ToDisplayString(QString::fromStdString(m_reference->GetExpression())));
+ if (m_parent->IsIterativeInput())
+ m_parent->NextButton(this);
}
void MappingButton::Clear()
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingButton.h b/Source/Core/DolphinQt/Config/Mapping/MappingButton.h
index 040b7fd75a..96847775c9 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingButton.h
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingButton.h
@@ -30,7 +30,6 @@ signals:
private:
void mouseReleaseEvent(QMouseEvent* event) override;
- void OnButtonTimeout();
void Connect();
MappingWidget* m_parent;
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
index 4c1f230c4b..dabc4d8530 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
@@ -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
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
index 1ee3ae1ead..5e213b3221 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
@@ -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
diff --git a/Source/Core/InputCommon/ControlReference/ControlReference.cpp b/Source/Core/InputCommon/ControlReference/ControlReference.cpp
index 3132baf07b..1f392c4e3a 100644
--- a/Source/Core/InputCommon/ControlReference/ControlReference.cpp
+++ b/Source/Core/InputCommon/ControlReference/ControlReference.cpp
@@ -4,9 +4,6 @@
#include "InputCommon/ControlReference/ControlReference.h"
-#include <vector>
-
-#include "Common/Thread.h"
// For InputGateOn()
// This is a bad layering violation, but it's the cleanest
// place I could find to put it.
@@ -15,12 +12,6 @@
using namespace ciface::ExpressionParser;
-namespace
-{
-// Compared to an input's current state (ideally 1.0) minus abs(initial_state) (ideally 0.0).
-constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55;
-} // namespace
-
bool ControlReference::InputGateOn()
{
return SConfig::GetInstance().m_BackgroundInput || Host_RendererHasFocus() ||
@@ -115,83 +106,3 @@ ControlState OutputReference::State(const ControlState state)
m_parsed_expression->SetValue(state * range);
return 0.0;
}
-
-// Wait for input on a particular device.
-// Inputs are considered if they are first seen in a neutral state.
-// This is useful for crazy flightsticks that have certain buttons that are always held down
-// and also properly handles detection when using "FullAnalogSurface" inputs.
-// Upon input, return a pointer to the detected Control, else return nullptr.
-ciface::Core::Device::Control* InputReference::Detect(const unsigned int ms,
- ciface::Core::Device* const device)
-{
- struct InputState
- {
- ciface::Core::Device::Input& input;
- ControlState initial_state;
- };
-
- std::vector<InputState> input_states;
- for (auto* input : device->Inputs())
- {
- // Don't detect things like absolute cursor position.
- if (!input->IsDetectable())
- continue;
-
- // Undesirable axes will have negative values here when trying to map a "FullAnalogSurface".
- input_states.push_back({*input, input->GetState()});
- }
-
- if (input_states.empty())
- return nullptr;
-
- unsigned int time = 0;
- while (time < ms)
- {
- Common::SleepCurrentThread(10);
- time += 10;
-
- device->UpdateInput();
- for (auto& input_state : input_states)
- {
- // We want an input that was initially 0.0 and currently 1.0.
- const auto detection_score =
- (input_state.input.GetState() - std::abs(input_state.initial_state));
-
- if (detection_score > INPUT_DETECT_THRESHOLD)
- return &input_state.input;
- }
- }
-
- // No input was detected. :'(
- return nullptr;
-}
-
-//
-// OutputReference :: Detect
-//
-// Totally different from the inputReference detect / I have them combined so it was simpler to make
-// the GUI.
-// The GUI doesn't know the difference between an input and an output / it's odd but I was lazy and
-// it was easy
-//
-// set all binded outputs to <range> power for x milliseconds return false
-//
-ciface::Core::Device::Control* OutputReference::Detect(const unsigned int ms,
- ciface::Core::Device* const device)
-{
- // ignore device
-
- // don't hang if we don't even have any controls mapped
- if (BoundCount() > 0)
- {
- State(1);
- unsigned int slept = 0;
-
- // this loop is to make stuff like flashing keyboard LEDs work
- while (ms > (slept += 10))
- Common::SleepCurrentThread(10);
-
- State(0);
- }
- return nullptr;
-}
diff --git a/Source/Core/InputCommon/ControlReference/ControlReference.h b/Source/Core/InputCommon/ControlReference/ControlReference.h
index 6740ed8be0..83fa288676 100644
--- a/Source/Core/InputCommon/ControlReference/ControlReference.h
+++ b/Source/Core/InputCommon/ControlReference/ControlReference.h
@@ -26,8 +26,6 @@ public:
virtual ~ControlReference();
virtual ControlState State(const ControlState state = 0) = 0;
- virtual ciface::Core::Device::Control* Detect(const unsigned int ms,
- ciface::Core::Device* const device) = 0;
virtual bool IsInput() const = 0;
int BoundCount() const;
@@ -57,8 +55,6 @@ public:
InputReference();
bool IsInput() const override;
ControlState State(const ControlState state) override;
- ciface::Core::Device::Control* Detect(const unsigned int ms,
- ciface::Core::Device* const device) override;
};
//
@@ -72,6 +68,4 @@ public:
OutputReference();
bool IsInput() const override;
ControlState State(const ControlState state) override;
- ciface::Core::Device::Control* Detect(const unsigned int ms,
- ciface::Core::Device* const device) override;
};
diff --git a/Source/Core/InputCommon/ControllerInterface/Device.cpp b/Source/Core/InputCommon/ControllerInterface/Device.cpp
index efe1372711..14f5cb23d9 100644
--- a/Source/Core/InputCommon/ControllerInterface/Device.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/Device.cpp
@@ -11,16 +11,15 @@
#include <tuple>
#include "Common/StringUtil.h"
+#include "Common/Thread.h"
namespace ciface
{
namespace Core
{
-//
-// Device :: ~Device
-//
-// Destructor, delete all inputs/outputs on device destruction
-//
+// Compared to an input's current state (ideally 1.0) minus abs(initial_state) (ideally 0.0).
+constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55;
+
Device::~Device()
{
// delete inputs
@@ -220,5 +219,83 @@ bool DeviceContainer::HasConnectedDevice(const DeviceQualifier& qualifier) const
const auto device = FindDevice(qualifier);
return device != nullptr && device->IsValid();
}
+
+// Wait for input on a particular device.
+// Inputs are considered if they are first seen in a neutral state.
+// This is useful for crazy flightsticks that have certain buttons that are always held down
+// and also properly handles detection when using "FullAnalogSurface" inputs.
+// Upon input, return the detected Device and Input, else return nullptrs
+std::pair<std::shared_ptr<Device>, Device::Input*>
+DeviceContainer::DetectInput(u32 wait_ms, std::vector<std::string> device_strings)
+{
+ struct InputState
+ {
+ ciface::Core::Device::Input& input;
+ ControlState initial_state;
+ };
+
+ struct DeviceState
+ {
+ std::shared_ptr<Device> device;
+
+ std::vector<InputState> input_states;
+ };
+
+ // Acquire devices and initial input states.
+ std::vector<DeviceState> device_states;
+ for (auto& device_string : device_strings)
+ {
+ DeviceQualifier dq;
+ dq.FromString(device_string);
+ auto device = FindDevice(dq);
+
+ if (!device)
+ continue;
+
+ std::vector<InputState> input_states;
+
+ for (auto* input : device->Inputs())
+ {
+ // Don't detect things like absolute cursor position.
+ if (!input->IsDetectable())
+ continue;
+
+ // Undesirable axes will have negative values here when trying to map a
+ // "FullAnalogSurface".
+ input_states.push_back({*input, input->GetState()});
+ }
+
+ if (!input_states.empty())
+ device_states.emplace_back(DeviceState{std::move(device), std::move(input_states)});
+ }
+
+ if (device_states.empty())
+ return {};
+
+ u32 time = 0;
+ while (time < wait_ms)
+ {
+ Common::SleepCurrentThread(10);
+ time += 10;
+
+ for (auto& device_state : device_states)
+ {
+ device_state.device->UpdateInput();
+ for (auto& input_state : device_state.input_states)
+ {
+ // We want an input that was initially 0.0 and currently 1.0.
+ const auto detection_score =
+ (input_state.input.GetState() - std::abs(input_state.initial_state));
+
+ if (detection_score > INPUT_DETECT_THRESHOLD)
+ return {device_state.device, &input_state.input};
+ }
+ }
+ }
+
+ // No input was detected. :'(
+ return {};
+}
+
} // namespace Core
} // namespace ciface
diff --git a/Source/Core/InputCommon/ControllerInterface/Device.h b/Source/Core/InputCommon/ControllerInterface/Device.h
index 0ee2b31444..7d7623ba03 100644
--- a/Source/Core/InputCommon/ControllerInterface/Device.h
+++ b/Source/Core/InputCommon/ControllerInterface/Device.h
@@ -172,6 +172,9 @@ public:
bool HasConnectedDevice(const DeviceQualifier& qualifier) const;
+ std::pair<std::shared_ptr<Device>, Device::Input*>
+ DetectInput(u32 wait_ms, std::vector<std::string> device_strings);
+
protected:
mutable std::mutex m_devices_mutex;
std::vector<std::shared_ptr<Device>> m_devices;