Skip to content

Commit

Permalink
Move parts of MappingCommon out of DolphinQt
Browse files Browse the repository at this point in the history
Some of the functions in MappingCommon would be useful to use on
mobile in the future.
  • Loading branch information
JosJuice committed Mar 6, 2022
1 parent 9af9e79 commit 1bc0576
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 154 deletions.
8 changes: 8 additions & 0 deletions Source/Core/Common/StringUtil.h
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/DolphinLib.props
Expand Up @@ -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" />
Expand Down Expand Up @@ -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" />
Expand Down
10 changes: 6 additions & 4 deletions Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp
Expand Up @@ -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;

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand Down
139 changes: 6 additions & 133 deletions Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp
Expand Up @@ -3,8 +3,7 @@

#include "DolphinQt/Config/Mapping/MappingCommon.h"

#include <tuple>
#include <vector>
#include <chrono>

#include <QApplication>
#include <QPushButton>
Expand All @@ -14,6 +13,7 @@

#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"

#include "Common/Thread.h"

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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)
Expand All @@ -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
19 changes: 2 additions & 17 deletions Source/Core/DolphinQt/Config/Mapping/MappingCommon.h
Expand Up @@ -7,34 +7,19 @@
#include <vector>

#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"

class QString;
class OutputReference;
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
2 changes: 2 additions & 0 deletions Source/Core/InputCommon/CMakeLists.txt
Expand Up @@ -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
Expand Down

0 comments on commit 1bc0576

Please sign in to comment.