MappingCommon.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2022 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "InputCommon/ControllerInterface/MappingCommon.h"
  4. #include <algorithm>
  5. #include <chrono>
  6. #include <cmath>
  7. #include <ranges>
  8. #include <string>
  9. #include <vector>
  10. #include <fmt/format.h>
  11. #include <fmt/ranges.h>
  12. #include "Common/MathUtil.h"
  13. #include "Common/StringUtil.h"
  14. #include "InputCommon/ControllerEmu/ControllerEmu.h"
  15. #include "InputCommon/ControllerEmu/StickGate.h"
  16. #include "InputCommon/ControllerInterface/ControllerInterface.h"
  17. #include "InputCommon/ControllerInterface/CoreDevice.h"
  18. #include "InputCommon/InputConfig.h"
  19. namespace ciface::MappingCommon
  20. {
  21. // Pressing inputs at the same time will result in the & operator vs a hotkey expression.
  22. constexpr auto HOTKEY_VS_CONJUNCION_THRESHOLD = std::chrono::milliseconds(50);
  23. // Some devices (e.g. DS4) provide an analog and digital input for the trigger.
  24. // We prefer just the analog input for simultaneous digital+analog input detections.
  25. constexpr auto SPURIOUS_TRIGGER_COMBO_THRESHOLD = std::chrono::milliseconds(150);
  26. std::string GetExpressionForControl(const std::string& control_name,
  27. const ciface::Core::DeviceQualifier& control_device,
  28. const ciface::Core::DeviceQualifier& default_device,
  29. Quote quote)
  30. {
  31. std::string expr;
  32. // non-default device
  33. if (control_device != default_device)
  34. {
  35. expr += control_device.ToString();
  36. expr += ':';
  37. }
  38. // append the control name
  39. expr += control_name;
  40. if (quote == Quote::On)
  41. {
  42. // If our expression contains any non-alpha characters
  43. // we should quote it
  44. if (!std::ranges::all_of(expr, Common::IsAlpha))
  45. expr = fmt::format("`{}`", expr);
  46. }
  47. return expr;
  48. }
  49. std::string BuildExpression(const Core::InputDetector::Results& detections,
  50. const ciface::Core::DeviceQualifier& default_device, Quote quote)
  51. {
  52. std::vector<const Core::InputDetector::Detection*> pressed_inputs;
  53. std::vector<std::string> alternations;
  54. const auto get_control_expression = [&](auto& detection) {
  55. // Return the parent-most name if there is one for better hotkey strings.
  56. // Detection of L/R_Ctrl will be changed to just Ctrl.
  57. // Users can manually map L_Ctrl if they so desire.
  58. const auto input = (quote == Quote::On) ?
  59. detection.device->GetParentMostInput(detection.input) :
  60. detection.input;
  61. ciface::Core::DeviceQualifier device_qualifier;
  62. device_qualifier.FromDevice(detection.device.get());
  63. return MappingCommon::GetExpressionForControl(input->GetName(), device_qualifier,
  64. default_device, quote);
  65. };
  66. bool new_alternation = false;
  67. const auto handle_press = [&](auto& detection) {
  68. pressed_inputs.emplace_back(&detection);
  69. new_alternation = true;
  70. };
  71. const auto handle_release = [&] {
  72. if (!new_alternation)
  73. return;
  74. new_alternation = false;
  75. std::vector<std::string> alternation;
  76. for (auto* input : pressed_inputs)
  77. alternation.push_back(get_control_expression(*input));
  78. const bool is_hotkey = pressed_inputs.size() >= 2 &&
  79. (pressed_inputs[1]->press_time - pressed_inputs[0]->press_time) >
  80. HOTKEY_VS_CONJUNCION_THRESHOLD;
  81. if (is_hotkey)
  82. {
  83. alternations.push_back(fmt::format("@({})", fmt::join(alternation, "+")));
  84. }
  85. else
  86. {
  87. std::ranges::sort(alternation);
  88. alternations.push_back(fmt::to_string(fmt::join(alternation, "&")));
  89. }
  90. };
  91. for (auto& detection : detections)
  92. {
  93. // Remove since-released inputs.
  94. for (auto it = pressed_inputs.begin(); it != pressed_inputs.end();)
  95. {
  96. if ((*it)->release_time && (*it)->release_time <= detection.press_time)
  97. {
  98. handle_release();
  99. it = pressed_inputs.erase(it);
  100. }
  101. else
  102. {
  103. ++it;
  104. }
  105. }
  106. handle_press(detection);
  107. }
  108. handle_release();
  109. // Remove duplicates
  110. std::ranges::sort(alternations);
  111. const auto unique_result = std::ranges::unique(alternations);
  112. alternations.erase(unique_result.begin(), unique_result.end());
  113. return fmt::to_string(fmt::join(alternations, "|"));
  114. }
  115. void RemoveSpuriousTriggerCombinations(Core::InputDetector::Results* detections)
  116. {
  117. const auto is_spurious = [&](const auto& detection) {
  118. return std::ranges::any_of(*detections, [&](const auto& d) {
  119. // This is a spurious digital detection if a "smooth" (analog) detection is temporally near.
  120. return &d != &detection && d.IsAnalogPress() && !detection.IsAnalogPress() &&
  121. abs(d.press_time - detection.press_time) < SPURIOUS_TRIGGER_COMBO_THRESHOLD;
  122. });
  123. };
  124. std::erase_if(*detections, is_spurious);
  125. }
  126. void RemoveDetectionsAfterTimePoint(Core::InputDetector::Results* results, Clock::time_point after)
  127. {
  128. const auto is_after_time = [&](const Core::InputDetector::Detection& detection) {
  129. return detection.release_time.value_or(after) >= after;
  130. };
  131. std::erase_if(*results, is_after_time);
  132. }
  133. bool ContainsCompleteDetection(const Core::InputDetector::Results& results)
  134. {
  135. return std::ranges::any_of(results, [](const Core::InputDetector::Detection& detection) {
  136. return detection.release_time.has_value();
  137. });
  138. }
  139. ReshapableInputMapper::ReshapableInputMapper(const Core::DeviceContainer& container,
  140. std::span<const std::string> device_strings)
  141. {
  142. m_input_detector.Start(container, device_strings);
  143. }
  144. bool ReshapableInputMapper::Update()
  145. {
  146. const auto prev_size = m_input_detector.GetResults().size();
  147. constexpr auto wait_time = std::chrono::seconds{4};
  148. m_input_detector.Update(wait_time, wait_time, wait_time * REQUIRED_INPUT_COUNT);
  149. return m_input_detector.GetResults().size() != prev_size;
  150. }
  151. float ReshapableInputMapper::GetCurrentAngle() const
  152. {
  153. constexpr auto quarter_circle = float(MathUtil::TAU) * 0.25f;
  154. return quarter_circle - (float(m_input_detector.GetResults().size()) * quarter_circle);
  155. }
  156. bool ReshapableInputMapper::IsComplete() const
  157. {
  158. return m_input_detector.GetResults().size() >= REQUIRED_INPUT_COUNT ||
  159. m_input_detector.IsComplete();
  160. }
  161. bool ReshapableInputMapper::IsCalibrationNeeded() const
  162. {
  163. return std::ranges::any_of(m_input_detector.GetResults() | std::views::take(REQUIRED_INPUT_COUNT),
  164. &ciface::Core::InputDetector::Detection::IsAnalogPress);
  165. }
  166. bool ReshapableInputMapper::ApplyResults(ControllerEmu::EmulatedController* controller,
  167. ControllerEmu::ReshapableInput* stick)
  168. {
  169. auto const detections = m_input_detector.TakeResults();
  170. if (detections.size() < REQUIRED_INPUT_COUNT)
  171. return false;
  172. // Transpose URDL to UDLR.
  173. const std::array results{detections[0], detections[2], detections[3], detections[1]};
  174. const auto default_device = controller->GetDefaultDevice();
  175. for (std::size_t i = 0; i != results.size(); ++i)
  176. {
  177. ciface::Core::DeviceQualifier device_qualifier;
  178. device_qualifier.FromDevice(results[i].device.get());
  179. stick->controls[i]->control_ref->SetExpression(ciface::MappingCommon::GetExpressionForControl(
  180. results[i].input->GetName(), device_qualifier, default_device,
  181. ciface::MappingCommon::Quote::On));
  182. controller->UpdateSingleControlReference(g_controller_interface,
  183. stick->controls[i]->control_ref.get());
  184. }
  185. controller->GetConfig()->GenerateControllerTextures();
  186. return true;
  187. }
  188. CalibrationBuilder::CalibrationBuilder(std::optional<Common::DVec2> center)
  189. : m_calibration_data(ControllerEmu::ReshapableInput::CALIBRATION_SAMPLE_COUNT, 0.0),
  190. m_center{center}
  191. {
  192. }
  193. void CalibrationBuilder::Update(Common::DVec2 point)
  194. {
  195. if (!m_center.has_value())
  196. m_center = point;
  197. const auto new_point = point - *m_center;
  198. ControllerEmu::ReshapableInput::UpdateCalibrationData(m_calibration_data, m_prev_point,
  199. new_point);
  200. m_prev_point = new_point;
  201. }
  202. bool CalibrationBuilder::IsCalibrationDataSensible() const
  203. {
  204. // Even the GC controller's small range would pass this test.
  205. constexpr double REASONABLE_AVERAGE_RADIUS = 0.6;
  206. // Test that the average input radius is not below a threshold.
  207. // This will make sure the user has actually moved their stick from neutral.
  208. MathUtil::RunningVariance<ControlState> stats;
  209. for (const auto x : m_calibration_data)
  210. stats.Push(x);
  211. if (stats.Mean() < REASONABLE_AVERAGE_RADIUS)
  212. return false;
  213. // Test that the standard deviation is below a threshold.
  214. // This will make sure the user has not just filled in one side of their input.
  215. // Approx. deviation of a square input gate, anything much more than that would be unusual.
  216. constexpr double REASONABLE_DEVIATION = 0.14;
  217. return stats.StandardDeviation() < REASONABLE_DEVIATION;
  218. }
  219. bool CalibrationBuilder::IsComplete() const
  220. {
  221. if (!IsCalibrationDataSensible())
  222. return false;
  223. const auto half_calibration =
  224. 0.5 * GetCalibrationRadiusAtAngle(std::atan2(m_prev_point.y, m_prev_point.x) + MathUtil::TAU);
  225. return m_prev_point.LengthSquared() < (half_calibration * half_calibration);
  226. }
  227. ControlState CalibrationBuilder::GetCalibrationRadiusAtAngle(double angle) const
  228. {
  229. return ControllerEmu::ReshapableInput::GetCalibrationDataRadiusAtAngle(m_calibration_data, angle);
  230. }
  231. void CalibrationBuilder::ApplyResults(ControllerEmu::ReshapableInput* stick)
  232. {
  233. stick->SetCenter(GetCenter());
  234. stick->SetCalibrationData(std::move(m_calibration_data));
  235. }
  236. Common::DVec2 CalibrationBuilder::GetCenter() const
  237. {
  238. return m_center.value_or(Common::DVec2{});
  239. }
  240. } // namespace ciface::MappingCommon