mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 05:47:56 -07:00
ControllerInterface/DolphinQt: Make mapping "all devices" way less hacky.
This commit is contained in:
parent
48b69ca018
commit
c389d68186
@ -136,8 +136,8 @@ void IOWindow::ConnectWidgets()
|
|||||||
connect(m_or_button, &QPushButton::clicked, [this] { AppendSelectedOption(" | "); });
|
connect(m_or_button, &QPushButton::clicked, [this] { AppendSelectedOption(" | "); });
|
||||||
connect(m_not_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,
|
connect(m_detect_button, &QPushButton::clicked, this, &IOWindow::OnDetectButtonPressed);
|
||||||
this, &IOWindow::OnDetectButtonPressed);
|
connect(m_test_button, &QPushButton::clicked, this, &IOWindow::OnTestButtonPressed);
|
||||||
|
|
||||||
connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed);
|
connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed);
|
||||||
connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged);
|
connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged);
|
||||||
@ -180,35 +180,22 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button)
|
|||||||
|
|
||||||
void IOWindow::OnDetectButtonPressed()
|
void IOWindow::OnDetectButtonPressed()
|
||||||
{
|
{
|
||||||
installEventFilter(BlockUserInputFilter::Instance());
|
const auto expression =
|
||||||
grabKeyboard();
|
MappingCommon::DetectExpression(m_detect_button, g_controller_interface, {m_devq.ToString()},
|
||||||
grabMouse();
|
m_devq, MappingCommon::Quote::Off);
|
||||||
|
|
||||||
std::thread([this] {
|
if (expression.isEmpty())
|
||||||
auto* btn = m_type == IOWindow::Type::Input ? m_detect_button : m_test_button;
|
return;
|
||||||
const auto old_label = btn->text();
|
|
||||||
|
|
||||||
btn->setText(QStringLiteral("..."));
|
const auto list = m_option_list->findItems(expression, Qt::MatchFixedString);
|
||||||
|
|
||||||
const auto expr = MappingCommon::DetectExpression(
|
if (!list.empty())
|
||||||
m_reference, g_controller_interface.FindDevice(m_devq).get(), m_devq,
|
m_option_list->setCurrentItem(list[0]);
|
||||||
MappingCommon::Quote::Off);
|
}
|
||||||
|
|
||||||
btn->setText(old_label);
|
void IOWindow::OnTestButtonPressed()
|
||||||
|
{
|
||||||
if (!expr.isEmpty())
|
MappingCommon::TestOutput(m_test_button, static_cast<OutputReference*>(m_reference));
|
||||||
{
|
|
||||||
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::OnRangeChanged(int value)
|
void IOWindow::OnRangeChanged(int value)
|
||||||
|
@ -48,6 +48,7 @@ private:
|
|||||||
void OnDialogButtonPressed(QAbstractButton* button);
|
void OnDialogButtonPressed(QAbstractButton* button);
|
||||||
void OnDeviceChanged(const QString& device);
|
void OnDeviceChanged(const QString& device);
|
||||||
void OnDetectButtonPressed();
|
void OnDetectButtonPressed();
|
||||||
|
void OnTestButtonPressed();
|
||||||
void OnRangeChanged(int range);
|
void OnRangeChanged(int range);
|
||||||
|
|
||||||
void AppendSelectedOption(const std::string& prefix);
|
void AppendSelectedOption(const std::string& prefix);
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <future>
|
#include "DolphinQt/Config/Mapping/MappingButton.h"
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
@ -12,8 +11,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "DolphinQt/Config/Mapping/MappingButton.h"
|
|
||||||
|
|
||||||
#include "Common/Thread.h"
|
#include "Common/Thread.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
|
||||||
@ -103,96 +100,33 @@ void MappingButton::Detect()
|
|||||||
if (m_parent->GetDevice() == nullptr || !m_reference->IsInput())
|
if (m_parent->GetDevice() == nullptr || !m_reference->IsInput())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
installEventFilter(BlockUserInputFilter::Instance());
|
const auto default_device_qualifier = m_parent->GetController()->GetDefaultDevice();
|
||||||
grabKeyboard();
|
|
||||||
|
|
||||||
// Make sure that we don't block event handling
|
QString expression;
|
||||||
QueueOnObject(this, [this] {
|
|
||||||
setText(QStringLiteral("..."));
|
|
||||||
|
|
||||||
// The button text won't be updated if we don't process events here
|
if (m_parent->GetParent()->IsMappingAllDevices())
|
||||||
QApplication::processEvents();
|
{
|
||||||
|
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
|
if (expression.isEmpty())
|
||||||
Common::SleepCurrentThread(100);
|
return;
|
||||||
|
|
||||||
std::vector<std::future<std::pair<QString, QString>>> futures;
|
m_reference->SetExpression(expression.toStdString());
|
||||||
QString expr;
|
m_parent->SaveSettings();
|
||||||
|
Update();
|
||||||
|
m_parent->GetController()->UpdateReferences(g_controller_interface);
|
||||||
|
|
||||||
if (m_parent->GetParent()->IsMappingAllDevices())
|
if (m_parent->IsIterativeInput())
|
||||||
{
|
m_parent->NextButton(this);
|
||||||
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())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappingButton::Clear()
|
void MappingButton::Clear()
|
||||||
|
@ -30,7 +30,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
void OnButtonTimeout();
|
|
||||||
void Connect();
|
void Connect();
|
||||||
|
|
||||||
MappingWidget* m_parent;
|
MappingWidget* m_parent;
|
||||||
|
@ -4,16 +4,23 @@
|
|||||||
|
|
||||||
#include "DolphinQt/Config/Mapping/MappingCommon.h"
|
#include "DolphinQt/Config/Mapping/MappingCommon.h"
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
|
||||||
#include "InputCommon/ControlReference/ControlReference.h"
|
#include "InputCommon/ControlReference/ControlReference.h"
|
||||||
#include "InputCommon/ControllerInterface/Device.h"
|
#include "InputCommon/ControllerInterface/Device.h"
|
||||||
|
|
||||||
|
#include "Common/Thread.h"
|
||||||
|
|
||||||
namespace MappingCommon
|
namespace MappingCommon
|
||||||
{
|
{
|
||||||
constexpr int INPUT_DETECT_TIME = 3000;
|
constexpr int INPUT_DETECT_TIME = 3000;
|
||||||
constexpr int OUTPUT_DETECT_TIME = 2000;
|
constexpr int OUTPUT_TEST_TIME = 2000;
|
||||||
|
|
||||||
QString GetExpressionForControl(const QString& control_name,
|
QString GetExpressionForControl(const QString& control_name,
|
||||||
const ciface::Core::DeviceQualifier& control_device,
|
const ciface::Core::DeviceQualifier& control_device,
|
||||||
@ -41,18 +48,56 @@ QString GetExpressionForControl(const QString& control_name,
|
|||||||
return expr;
|
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 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)
|
// The button text won't be updated if we don't process events here
|
||||||
{
|
QApplication::processEvents();
|
||||||
return MappingCommon::GetExpressionForControl(QString::fromStdString(ctrl->GetName()),
|
|
||||||
default_device, default_device, quote);
|
// Avoid that the button press itself is registered as an event
|
||||||
}
|
Common::SleepCurrentThread(100);
|
||||||
return QStringLiteral("");
|
|
||||||
|
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
|
} // namespace MappingCommon
|
||||||
|
@ -4,17 +4,21 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
class ControlReference;
|
class OutputReference;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
namespace ciface
|
namespace ciface
|
||||||
{
|
{
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
class Device;
|
class DeviceContainer;
|
||||||
class DeviceQualifier;
|
class DeviceQualifier;
|
||||||
}
|
} // namespace Core
|
||||||
}
|
} // namespace ciface
|
||||||
|
|
||||||
namespace MappingCommon
|
namespace MappingCommon
|
||||||
{
|
{
|
||||||
@ -28,7 +32,12 @@ QString GetExpressionForControl(const QString& control_name,
|
|||||||
const ciface::Core::DeviceQualifier& control_device,
|
const ciface::Core::DeviceQualifier& control_device,
|
||||||
const ciface::Core::DeviceQualifier& default_device,
|
const ciface::Core::DeviceQualifier& default_device,
|
||||||
Quote quote = Quote::On);
|
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,
|
const ciface::Core::DeviceQualifier& default_device,
|
||||||
Quote quote = Quote::On);
|
Quote quote = Quote::On);
|
||||||
|
|
||||||
|
void TestOutput(QPushButton* button, OutputReference* reference);
|
||||||
|
|
||||||
} // namespace MappingCommon
|
} // namespace MappingCommon
|
||||||
|
@ -4,9 +4,6 @@
|
|||||||
|
|
||||||
#include "InputCommon/ControlReference/ControlReference.h"
|
#include "InputCommon/ControlReference/ControlReference.h"
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/Thread.h"
|
|
||||||
// For InputGateOn()
|
// For InputGateOn()
|
||||||
// This is a bad layering violation, but it's the cleanest
|
// This is a bad layering violation, but it's the cleanest
|
||||||
// place I could find to put it.
|
// place I could find to put it.
|
||||||
@ -15,12 +12,6 @@
|
|||||||
|
|
||||||
using namespace ciface::ExpressionParser;
|
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()
|
bool ControlReference::InputGateOn()
|
||||||
{
|
{
|
||||||
return SConfig::GetInstance().m_BackgroundInput || Host_RendererHasFocus() ||
|
return SConfig::GetInstance().m_BackgroundInput || Host_RendererHasFocus() ||
|
||||||
@ -115,83 +106,3 @@ ControlState OutputReference::State(const ControlState state)
|
|||||||
m_parsed_expression->SetValue(state * range);
|
m_parsed_expression->SetValue(state * range);
|
||||||
return 0.0;
|
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;
|
|
||||||
}
|
|
||||||
|
@ -26,8 +26,6 @@ public:
|
|||||||
|
|
||||||
virtual ~ControlReference();
|
virtual ~ControlReference();
|
||||||
virtual ControlState State(const ControlState state = 0) = 0;
|
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;
|
virtual bool IsInput() const = 0;
|
||||||
|
|
||||||
int BoundCount() const;
|
int BoundCount() const;
|
||||||
@ -57,8 +55,6 @@ public:
|
|||||||
InputReference();
|
InputReference();
|
||||||
bool IsInput() const override;
|
bool IsInput() const override;
|
||||||
ControlState State(const ControlState state) 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();
|
OutputReference();
|
||||||
bool IsInput() const override;
|
bool IsInput() const override;
|
||||||
ControlState State(const ControlState state) override;
|
ControlState State(const ControlState state) override;
|
||||||
ciface::Core::Device::Control* Detect(const unsigned int ms,
|
|
||||||
ciface::Core::Device* const device) override;
|
|
||||||
};
|
};
|
||||||
|
@ -11,16 +11,15 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
#include "Common/Thread.h"
|
||||||
|
|
||||||
namespace ciface
|
namespace ciface
|
||||||
{
|
{
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
//
|
// Compared to an input's current state (ideally 1.0) minus abs(initial_state) (ideally 0.0).
|
||||||
// Device :: ~Device
|
constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55;
|
||||||
//
|
|
||||||
// Destructor, delete all inputs/outputs on device destruction
|
|
||||||
//
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
{
|
{
|
||||||
// delete inputs
|
// delete inputs
|
||||||
@ -220,5 +219,83 @@ bool DeviceContainer::HasConnectedDevice(const DeviceQualifier& qualifier) const
|
|||||||
const auto device = FindDevice(qualifier);
|
const auto device = FindDevice(qualifier);
|
||||||
return device != nullptr && device->IsValid();
|
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 Core
|
||||||
} // namespace ciface
|
} // namespace ciface
|
||||||
|
@ -172,6 +172,9 @@ public:
|
|||||||
|
|
||||||
bool HasConnectedDevice(const DeviceQualifier& qualifier) const;
|
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:
|
protected:
|
||||||
mutable std::mutex m_devices_mutex;
|
mutable std::mutex m_devices_mutex;
|
||||||
std::vector<std::shared_ptr<Device>> m_devices;
|
std::vector<std::shared_ptr<Device>> m_devices;
|
||||||
|
Loading…
Reference in New Issue
Block a user