summaryrefslogtreecommitdiff
path: root/Source/Core
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
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')
-rw-r--r--Source/Core/Common/StringUtil.h8
-rw-r--r--Source/Core/DolphinLib.props2
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp10
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp139
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/MappingCommon.h19
-rw-r--r--Source/Core/InputCommon/CMakeLists.txt2
-rw-r--r--Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp149
-rw-r--r--Source/Core/InputCommon/ControllerInterface/MappingCommon.h29
8 files changed, 204 insertions, 154 deletions
diff --git a/Source/Core/Common/StringUtil.h b/Source/Core/Common/StringUtil.h
index f008dc55bb..56fa756a46 100644
--- a/Source/Core/Common/StringUtil.h
+++ b/Source/Core/Common/StringUtil.h
@@ -235,6 +235,14 @@ inline bool IsPrintableCharacter(char c)
return std::isprint(c, std::locale::classic());
}
+/// Returns whether a character is a letter, i.e. whether 'a' <= c <= 'z' || 'A' <= c <= 'Z'
+/// is true. Use this instead of calling std::isalpha directly to ensure
+/// the C locale is being used and to avoid possibly undefined behaviour.
+inline bool IsAlpha(char c)
+{
+ return std::isalpha(c, std::locale::classic());
+}
+
#ifdef _WIN32
std::vector<std::string> CommandLineToUtf8Argv(const wchar_t* command_line);
#endif
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 79557b58c7..208ad54df7 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -482,6 +482,7 @@
<ClInclude Include="InputCommon\ControllerInterface\DualShockUDPClient\DualShockUDPProto.h" />
<ClInclude Include="InputCommon\ControllerInterface\evdev\evdev.h" />
<ClInclude Include="InputCommon\ControllerInterface\ForceFeedback\ForceFeedbackDevice.h" />
+ <ClInclude Include="InputCommon\ControllerInterface\MappingCommon.h" />
<ClInclude Include="InputCommon\ControllerInterface\Wiimote\WiimoteController.h" />
<ClInclude Include="InputCommon\ControllerInterface\Win32\Win32.h" />
<ClInclude Include="InputCommon\ControllerInterface\XInput\XInput.h" />
@@ -1069,6 +1070,7 @@
<ClCompile Include="InputCommon\ControllerInterface\DInput\XInputFilter.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\DualShockUDPClient\DualShockUDPClient.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
+ <ClCompile Include="InputCommon\ControllerInterface\MappingCommon.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\Wiimote\WiimoteController.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\Win32\Win32.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\XInput\XInput.cpp" />
diff --git a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
index 6e178a2ed8..50492e0767 100644
--- a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
@@ -34,6 +34,7 @@
#include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
+#include "InputCommon/ControllerInterface/MappingCommon.h"
constexpr int SLIDER_TICK_COUNT = 100;
@@ -485,9 +486,10 @@ void IOWindow::AppendSelectedOption()
if (m_option_list->currentRow() < 0)
return;
- m_expression_text->insertPlainText(MappingCommon::GetExpressionForControl(
- m_option_list->item(m_option_list->currentRow(), 0)->text(), m_devq,
- m_controller->GetDefaultDevice()));
+ m_expression_text->insertPlainText(
+ QString::fromStdString(ciface::MappingCommon::GetExpressionForControl(
+ m_option_list->item(m_option_list->currentRow(), 0)->text().toStdString(), m_devq,
+ m_controller->GetDefaultDevice())));
}
void IOWindow::OnDeviceChanged()
@@ -526,7 +528,7 @@ void IOWindow::OnDetectButtonPressed()
{
const auto expression =
MappingCommon::DetectExpression(m_detect_button, g_controller_interface, {m_devq.ToString()},
- m_devq, MappingCommon::Quote::Off);
+ m_devq, ciface::MappingCommon::Quote::Off);
if (expression.isEmpty())
return;
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
index fde917c2b1..0fdcb376af 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
@@ -3,8 +3,7 @@
#include "DolphinQt/Config/Mapping/MappingCommon.h"
-#include <tuple>
-#include <vector>
+#include <chrono>
#include <QApplication>
#include <QPushButton>
@@ -14,6 +13,7 @@
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "InputCommon/ControlReference/ControlReference.h"
+#include "InputCommon/ControllerInterface/MappingCommon.h"
#include "Common/Thread.h"
@@ -25,44 +25,10 @@ constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
constexpr auto OUTPUT_TEST_TIME = std::chrono::seconds(2);
-// 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);
-
-QString GetExpressionForControl(const QString& control_name,
- const ciface::Core::DeviceQualifier& control_device,
- const ciface::Core::DeviceQualifier& default_device, Quote quote)
-{
- QString expr;
-
- // non-default device
- if (control_device != default_device)
- {
- expr += QString::fromStdString(control_device.ToString());
- expr += QLatin1Char{':'};
- }
-
- // append the control name
- expr += control_name;
-
- if (quote == Quote::On)
- {
- // If our expression contains any non-alpha characters
- // we should quote it
- const QRegularExpression reg(QStringLiteral("[^a-zA-Z]"));
- if (reg.match(expr).hasMatch())
- expr = QStringLiteral("`%1`").arg(expr);
- }
-
- return expr;
-}
-
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,
+ ciface::MappingCommon::Quote quote)
{
const auto filter = new BlockUserInputFilter(button);
@@ -83,7 +49,7 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev
device_container.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME,
INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME);
- RemoveSpuriousTriggerCombinations(&detections);
+ ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
const auto timer = new QTimer(button);
@@ -100,7 +66,7 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev
button->setText(old_text);
- return BuildExpression(detections, default_device, quote);
+ return QString::fromStdString(BuildExpression(detections, default_device, quote));
}
void TestOutput(QPushButton* button, OutputReference* reference)
@@ -118,97 +84,4 @@ void TestOutput(QPushButton* button, OutputReference* reference)
button->setText(old_text);
}
-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());
-}
-
-QString
-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;
-
- QStringList 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(QString::fromStdString(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;
-
- QStringList 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(QStringLiteral("@(%1)").arg(alternation.join(QLatin1Char('+'))));
- }
- else
- {
- alternation.sort();
- alternations.push_back(alternation.join(QLatin1Char('&')));
- }
- };
-
- 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();
-
- alternations.removeDuplicates();
- return alternations.join(QLatin1Char('|'));
-}
-
} // namespace MappingCommon
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
index aa96ad76d7..f1385ebb6b 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
@@ -7,6 +7,7 @@
#include <vector>
#include "InputCommon/ControllerInterface/CoreDevice.h"
+#include "InputCommon/ControllerInterface/MappingCommon.h"
class QString;
class OutputReference;
@@ -14,27 +15,11 @@ class QPushButton;
namespace MappingCommon
{
-enum class Quote
-{
- On,
- Off
-};
-
-QString GetExpressionForControl(const QString& control_name,
- const ciface::Core::DeviceQualifier& control_device,
- const ciface::Core::DeviceQualifier& default_device,
- Quote quote = Quote::On);
-
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);
+ ciface::MappingCommon::Quote quote = ciface::MappingCommon::Quote::On);
void TestOutput(QPushButton* button, OutputReference* reference);
-void RemoveSpuriousTriggerCombinations(std::vector<ciface::Core::DeviceContainer::InputDetection>*);
-
-QString BuildExpression(const std::vector<ciface::Core::DeviceContainer::InputDetection>&,
- const ciface::Core::DeviceQualifier& default_device, Quote quote);
-
} // namespace MappingCommon
diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt
index 576e87e711..e600004cda 100644
--- a/Source/Core/InputCommon/CMakeLists.txt
+++ b/Source/Core/InputCommon/CMakeLists.txt
@@ -54,6 +54,8 @@ add_library(inputcommon
ControllerInterface/ControllerInterface.h
ControllerInterface/CoreDevice.cpp
ControllerInterface/CoreDevice.h
+ ControllerInterface/MappingCommon.cpp
+ ControllerInterface/MappingCommon.h
ControllerInterface/Wiimote/WiimoteController.cpp
ControllerInterface/Wiimote/WiimoteController.h
ControlReference/ControlReference.cpp
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