summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2022-02-20 18:32:39 +0100
committerJosJuice <josjuice@gmail.com>2022-03-06 14:30:49 +0100
commit1bc057614e6d101e4f26db18fa60af2e9e2d0be7 (patch)
tree95894784ebd817c0f43e17c6d4ee6bb851ca206b /Source/Core/InputCommon/ControllerInterface
parent9af9e791f6a19d62197ad0d2e0fc39ed744cd506 (diff)
Move parts of MappingCommon out of DolphinQt
Some of the functions in MappingCommon would be useful to use on mobile in the future.
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp149
-rw-r--r--Source/Core/InputCommon/ControllerInterface/MappingCommon.h29
2 files changed, 178 insertions, 0 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp b/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp
new file mode 100644
index 0000000000..5adc8daf35
--- /dev/null
+++ b/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp
@@ -0,0 +1,149 @@
+// Copyright 2022 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "InputCommon/ControllerInterface/MappingCommon.h"
+
+#include <algorithm>
+#include <chrono>
+#include <string>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "Common/StringUtil.h"
+#include "InputCommon/ControllerInterface/CoreDevice.h"
+
+namespace ciface::MappingCommon
+{
+// Pressing inputs at the same time will result in the & operator vs a hotkey expression.
+constexpr auto HOTKEY_VS_CONJUNCION_THRESHOLD = std::chrono::milliseconds(50);
+
+// Some devices (e.g. DS4) provide an analog and digital input for the trigger.
+// We prefer just the analog input for simultaneous digital+analog input detections.
+constexpr auto SPURIOUS_TRIGGER_COMBO_THRESHOLD = std::chrono::milliseconds(150);
+
+std::string GetExpressionForControl(const std::string& control_name,
+ const ciface::Core::DeviceQualifier& control_device,
+ const ciface::Core::DeviceQualifier& default_device,
+ Quote quote)
+{
+ std::string expr;
+
+ // non-default device
+ if (control_device != default_device)
+ {
+ expr += control_device.ToString();
+ expr += ':';
+ }
+
+ // append the control name
+ expr += control_name;
+
+ if (quote == Quote::On)
+ {
+ // If our expression contains any non-alpha characters
+ // we should quote it
+ if (!std::all_of(expr.begin(), expr.end(), IsAlpha))
+ expr = fmt::format("`{}`", expr);
+ }
+
+ return expr;
+}
+
+std::string
+BuildExpression(const std::vector<ciface::Core::DeviceContainer::InputDetection>& detections,
+ const ciface::Core::DeviceQualifier& default_device, Quote quote)
+{
+ std::vector<const ciface::Core::DeviceContainer::InputDetection*> pressed_inputs;
+
+ std::vector<std::string> alternations;
+
+ const auto get_control_expression = [&](auto& detection) {
+ // Return the parent-most name if there is one for better hotkey strings.
+ // Detection of L/R_Ctrl will be changed to just Ctrl.
+ // Users can manually map L_Ctrl if they so desire.
+ const auto input = (quote == Quote::On) ?
+ detection.device->GetParentMostInput(detection.input) :
+ detection.input;
+
+ ciface::Core::DeviceQualifier device_qualifier;
+ device_qualifier.FromDevice(detection.device.get());
+
+ return MappingCommon::GetExpressionForControl(input->GetName(), device_qualifier,
+ default_device, quote);
+ };
+
+ bool new_alternation = false;
+
+ const auto handle_press = [&](auto& detection) {
+ pressed_inputs.emplace_back(&detection);
+ new_alternation = true;
+ };
+
+ const auto handle_release = [&]() {
+ if (!new_alternation)
+ return;
+
+ new_alternation = false;
+
+ std::vector<std::string> alternation;
+ for (auto* input : pressed_inputs)
+ alternation.push_back(get_control_expression(*input));
+
+ const bool is_hotkey = pressed_inputs.size() >= 2 &&
+ (pressed_inputs[1]->press_time - pressed_inputs[0]->press_time) >
+ HOTKEY_VS_CONJUNCION_THRESHOLD;
+
+ if (is_hotkey)
+ {
+ alternations.push_back(fmt::format("@({})", JoinStrings(alternation, "+")));
+ }
+ else
+ {
+ std::sort(alternation.begin(), alternation.end());
+ alternations.push_back(JoinStrings(alternation, "&"));
+ }
+ };
+
+ for (auto& detection : detections)
+ {
+ // Remove since released inputs.
+ for (auto it = pressed_inputs.begin(); it != pressed_inputs.end();)
+ {
+ if (!((*it)->release_time > detection.press_time))
+ {
+ handle_release();
+ it = pressed_inputs.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ handle_press(detection);
+ }
+
+ handle_release();
+
+ // Remove duplicates
+ std::sort(alternations.begin(), alternations.end());
+ alternations.erase(std::unique(alternations.begin(), alternations.end()), alternations.end());
+
+ return JoinStrings(alternations, "|");
+}
+
+void RemoveSpuriousTriggerCombinations(
+ std::vector<ciface::Core::DeviceContainer::InputDetection>* detections)
+{
+ const auto is_spurious = [&](auto& detection) {
+ return std::any_of(detections->begin(), detections->end(), [&](auto& d) {
+ // This is a suprious digital detection if a "smooth" (analog) detection is temporally near.
+ return &d != &detection && d.smoothness > 1 &&
+ abs(d.press_time - detection.press_time) < SPURIOUS_TRIGGER_COMBO_THRESHOLD;
+ });
+ };
+
+ detections->erase(std::remove_if(detections->begin(), detections->end(), is_spurious),
+ detections->end());
+}
+
+} // namespace ciface::MappingCommon
diff --git a/Source/Core/InputCommon/ControllerInterface/MappingCommon.h b/Source/Core/InputCommon/ControllerInterface/MappingCommon.h
new file mode 100644
index 0000000000..1822125557
--- /dev/null
+++ b/Source/Core/InputCommon/ControllerInterface/MappingCommon.h
@@ -0,0 +1,29 @@
+// Copyright 2022 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "InputCommon/ControllerInterface/CoreDevice.h"
+
+namespace ciface::MappingCommon
+{
+enum class Quote
+{
+ On,
+ Off
+};
+
+std::string GetExpressionForControl(const std::string& control_name,
+ const ciface::Core::DeviceQualifier& control_device,
+ const ciface::Core::DeviceQualifier& default_device,
+ Quote quote = Quote::On);
+
+std::string BuildExpression(const std::vector<ciface::Core::DeviceContainer::InputDetection>&,
+ const ciface::Core::DeviceQualifier& default_device, Quote quote);
+
+void RemoveSpuriousTriggerCombinations(std::vector<ciface::Core::DeviceContainer::InputDetection>*);
+
+} // namespace ciface::MappingCommon