InputCommon: Support detecting combinations of inputs. (Hotkeys)

This commit is contained in:
Jordan Woyak 2019-10-23 17:20:42 -05:00
parent e6ba495486
commit b3acc7403f
3 changed files with 44 additions and 15 deletions

View File

@ -68,7 +68,7 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev
// Avoid that the button press itself is registered as an event
Common::SleepCurrentThread(50);
const auto [device, input] = device_container.DetectInput(INPUT_DETECT_TIME, device_strings);
const auto detections = device_container.DetectInput(INPUT_DETECT_TIME, device_strings);
const auto timer = new QTimer(button);
@ -83,14 +83,24 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev
button->setText(old_text);
if (!input)
return {};
QString full_expression;
ciface::Core::DeviceQualifier device_qualifier;
device_qualifier.FromDevice(device.get());
for (auto [device, input] : detections)
{
ciface::Core::DeviceQualifier device_qualifier;
device_qualifier.FromDevice(device.get());
return MappingCommon::GetExpressionForControl(QString::fromStdString(input->GetName()),
device_qualifier, default_device, quote);
if (!full_expression.isEmpty())
full_expression += QChar::fromLatin1('+');
full_expression += MappingCommon::GetExpressionForControl(
QString::fromStdString(input->GetName()), device_qualifier, default_device, quote);
}
if (detections.size() > 1)
return QStringLiteral("@(%1)").arg(std::move(full_expression));
return full_expression;
}
void TestOutput(QPushButton* button, OutputReference* reference)

View File

@ -18,6 +18,7 @@
namespace ciface::Core
{
// Compared to an input's current state (ideally 1.0) minus abs(initial_state) (ideally 0.0).
// Note: Detect() logic assumes this is greater than 0.5.
constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55;
Device::~Device()
@ -253,13 +254,14 @@ bool DeviceContainer::HasConnectedDevice(const DeviceQualifier& qualifier) const
// 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*>
// Detects multiple inputs if they are pressed before others are released.
// Upon input, return the detected Device and Input pairs, else return an empty container
std::vector<std::pair<std::shared_ptr<Device>, Device::Input*>>
DeviceContainer::DetectInput(u32 wait_ms, const std::vector<std::string>& device_strings) const
{
struct InputState
{
ciface::Core::Device::Input& input;
ciface::Core::Device::Input* input;
ControlState initial_state;
};
@ -291,7 +293,7 @@ DeviceContainer::DetectInput(u32 wait_ms, const std::vector<std::string>& device
// Undesirable axes will have negative values here when trying to map a
// "FullAnalogSurface".
input_states.push_back({*input, input->GetState()});
input_states.push_back({input, input->GetState()});
}
if (!input_states.empty())
@ -301,6 +303,8 @@ DeviceContainer::DetectInput(u32 wait_ms, const std::vector<std::string>& device
if (device_states.empty())
return {};
std::vector<std::pair<std::shared_ptr<Device>, Device::Input*>> detections;
u32 time = 0;
while (time < wait_ms)
{
@ -309,16 +313,31 @@ DeviceContainer::DetectInput(u32 wait_ms, const std::vector<std::string>& device
for (auto& device_state : device_states)
{
for (auto& input_state : device_state.input_states)
for (std::size_t i = 0; i != device_state.input_states.size(); ++i)
{
auto& input_state = device_state.input_states[i];
// 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));
(input_state.input->GetState() - std::abs(input_state.initial_state));
if (detection_score > INPUT_DETECT_THRESHOLD)
return {device_state.device, &input_state.input};
{
// We found an input. Add it to our detections.
detections.emplace_back(device_state.device, input_state.input);
// And remove from input_states to prevent more detections.
device_state.input_states.erase(device_state.input_states.begin() + i--);
}
}
}
for (auto& detection : detections)
{
// If one of our detected inputs is released we are done.
if (detection.second->GetState() < (1 - INPUT_DETECT_THRESHOLD))
return detections;
}
}
// No input was detected. :'(

View File

@ -194,7 +194,7 @@ public:
bool HasConnectedDevice(const DeviceQualifier& qualifier) const;
std::pair<std::shared_ptr<Device>, Device::Input*>
std::vector<std::pair<std::shared_ptr<Device>, Device::Input*>>
DetectInput(u32 wait_ms, const std::vector<std::string>& device_strings) const;
protected: