CoreDevice.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Copyright 2013 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "InputCommon/ControllerInterface/CoreDevice.h"
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <memory>
  7. #include <sstream>
  8. #include <string>
  9. #include <tuple>
  10. #include <fmt/format.h>
  11. #include "Common/MathUtil.h"
  12. #include "Common/Thread.h"
  13. namespace ciface::Core
  14. {
  15. // Compared to an input's current state (ideally 1.0) minus abs(initial_state) (ideally 0.0).
  16. // Note: Detect() logic assumes this is greater than 0.5.
  17. constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55;
  18. class CombinedInput final : public Device::Input
  19. {
  20. public:
  21. using Inputs = std::pair<Device::Input*, Device::Input*>;
  22. CombinedInput(std::string name, const Inputs& inputs) : m_name(std::move(name)), m_inputs(inputs)
  23. {
  24. }
  25. ControlState GetState() const override
  26. {
  27. ControlState result = 0;
  28. if (m_inputs.first)
  29. result = m_inputs.first->GetState();
  30. if (m_inputs.second)
  31. result = std::max(result, m_inputs.second->GetState());
  32. return result;
  33. }
  34. std::string GetName() const override { return m_name; }
  35. bool IsDetectable() const override { return false; }
  36. bool IsChild(const Input* input) const override
  37. {
  38. return m_inputs.first == input || m_inputs.second == input;
  39. }
  40. private:
  41. const std::string m_name;
  42. const std::pair<Device::Input*, Device::Input*> m_inputs;
  43. };
  44. Device::~Device()
  45. {
  46. // delete inputs
  47. for (Device::Input* input : m_inputs)
  48. delete input;
  49. // delete outputs
  50. for (Device::Output* output : m_outputs)
  51. delete output;
  52. }
  53. std::optional<int> Device::GetPreferredId() const
  54. {
  55. return {};
  56. }
  57. void Device::AddInput(Device::Input* const i)
  58. {
  59. m_inputs.push_back(i);
  60. }
  61. void Device::AddOutput(Device::Output* const o)
  62. {
  63. m_outputs.push_back(o);
  64. }
  65. std::string Device::GetQualifiedName() const
  66. {
  67. return fmt::format("{}/{}/{}", GetSource(), GetId(), GetName());
  68. }
  69. auto Device::GetParentMostInput(Input* child) const -> Input*
  70. {
  71. for (auto* input : m_inputs)
  72. {
  73. if (input->IsChild(child))
  74. {
  75. // Running recursively is currently unnecessary but it doesn't hurt.
  76. return GetParentMostInput(input);
  77. }
  78. }
  79. return child;
  80. }
  81. Device::Input* Device::FindInput(std::string_view name) const
  82. {
  83. for (Input* input : m_inputs)
  84. {
  85. if (input->IsMatchingName(name))
  86. return input;
  87. }
  88. return nullptr;
  89. }
  90. Device::Output* Device::FindOutput(std::string_view name) const
  91. {
  92. for (Output* output : m_outputs)
  93. {
  94. if (output->IsMatchingName(name))
  95. return output;
  96. }
  97. return nullptr;
  98. }
  99. bool Device::Control::IsMatchingName(std::string_view name) const
  100. {
  101. return GetName() == name;
  102. }
  103. bool Device::Control::IsHidden() const
  104. {
  105. return false;
  106. }
  107. class FullAnalogSurface final : public Device::Input
  108. {
  109. public:
  110. FullAnalogSurface(Input* low, Input* high) : m_low(*low), m_high(*high) {}
  111. ControlState GetState() const override
  112. {
  113. return (1 + std::max(0.0, m_high.GetState()) - std::max(0.0, m_low.GetState())) / 2;
  114. }
  115. std::string GetName() const override
  116. {
  117. // E.g. "Full Axis X+"
  118. return "Full " + m_high.GetName();
  119. }
  120. bool IsDetectable() const override { return m_low.IsDetectable() && m_high.IsDetectable(); }
  121. bool IsHidden() const override { return m_low.IsHidden() && m_high.IsHidden(); }
  122. bool IsMatchingName(std::string_view name) const override
  123. {
  124. if (Control::IsMatchingName(name))
  125. return true;
  126. // Old naming scheme was "Axis X-+" which is too visually similar to "Axis X+".
  127. // This has caused countless problems for users with mysterious misconfigurations.
  128. // We match this old name to support old configurations.
  129. const auto old_name = m_low.GetName() + *m_high.GetName().rbegin();
  130. return old_name == name;
  131. }
  132. private:
  133. Input& m_low;
  134. Input& m_high;
  135. };
  136. void Device::AddFullAnalogSurfaceInputs(Input* low, Input* high)
  137. {
  138. AddInput(low);
  139. AddInput(high);
  140. AddInput(new FullAnalogSurface(low, high));
  141. AddInput(new FullAnalogSurface(high, low));
  142. }
  143. void Device::AddCombinedInput(std::string name, const std::pair<std::string, std::string>& inputs)
  144. {
  145. AddInput(new CombinedInput(std::move(name), {FindInput(inputs.first), FindInput(inputs.second)}));
  146. }
  147. //
  148. // DeviceQualifier :: ToString
  149. //
  150. // Get string from a device qualifier / serialize
  151. //
  152. std::string DeviceQualifier::ToString() const
  153. {
  154. if (source.empty() && (cid < 0) && name.empty())
  155. return {};
  156. if (cid > -1)
  157. return fmt::format("{}/{}/{}", source, cid, name);
  158. else
  159. return fmt::format("{}//{}", source, name);
  160. }
  161. //
  162. // DeviceQualifier :: FromString
  163. //
  164. // Set a device qualifier from a string / unserialize
  165. //
  166. void DeviceQualifier::FromString(const std::string& str)
  167. {
  168. *this = {};
  169. std::istringstream ss(str);
  170. std::getline(ss, source, '/');
  171. // silly
  172. std::getline(ss, name, '/');
  173. std::istringstream(name) >> cid;
  174. std::getline(ss, name);
  175. }
  176. //
  177. // DeviceQualifier :: FromDevice
  178. //
  179. // Set a device qualifier from a device
  180. //
  181. void DeviceQualifier::FromDevice(const Device* const dev)
  182. {
  183. name = dev->GetName();
  184. cid = dev->GetId();
  185. source = dev->GetSource();
  186. }
  187. bool DeviceQualifier::operator==(const Device* const dev) const
  188. {
  189. if (dev->GetId() == cid)
  190. if (dev->GetName() == name)
  191. if (dev->GetSource() == source)
  192. return true;
  193. return false;
  194. }
  195. bool DeviceQualifier::operator==(const DeviceQualifier& devq) const
  196. {
  197. return std::tie(cid, name, source) == std::tie(devq.cid, devq.name, devq.source);
  198. }
  199. std::shared_ptr<Device> DeviceContainer::FindDevice(const DeviceQualifier& devq) const
  200. {
  201. std::lock_guard lk(m_devices_mutex);
  202. for (const auto& d : m_devices)
  203. {
  204. if (devq == d.get())
  205. return d;
  206. }
  207. return nullptr;
  208. }
  209. std::vector<std::shared_ptr<Device>> DeviceContainer::GetAllDevices() const
  210. {
  211. std::lock_guard lk(m_devices_mutex);
  212. std::vector<std::shared_ptr<Device>> devices;
  213. for (const auto& d : m_devices)
  214. devices.emplace_back(d);
  215. return devices;
  216. }
  217. std::vector<std::string> DeviceContainer::GetAllDeviceStrings() const
  218. {
  219. std::lock_guard lk(m_devices_mutex);
  220. std::vector<std::string> device_strings;
  221. DeviceQualifier device_qualifier;
  222. for (const auto& d : m_devices)
  223. {
  224. device_qualifier.FromDevice(d.get());
  225. device_strings.emplace_back(device_qualifier.ToString());
  226. }
  227. return device_strings;
  228. }
  229. bool DeviceContainer::HasDefaultDevice() const
  230. {
  231. std::lock_guard lk(m_devices_mutex);
  232. // Devices are already sorted by priority
  233. return !m_devices.empty() && m_devices[0]->GetSortPriority() >= 0;
  234. }
  235. std::string DeviceContainer::GetDefaultDeviceString() const
  236. {
  237. std::lock_guard lk(m_devices_mutex);
  238. // Devices are already sorted by priority
  239. if (m_devices.empty() || m_devices[0]->GetSortPriority() < 0)
  240. return "";
  241. DeviceQualifier device_qualifier;
  242. device_qualifier.FromDevice(m_devices[0].get());
  243. return device_qualifier.ToString();
  244. }
  245. Device::Input* DeviceContainer::FindInput(std::string_view name, const Device* def_dev) const
  246. {
  247. if (def_dev)
  248. {
  249. Device::Input* const inp = def_dev->FindInput(name);
  250. if (inp)
  251. return inp;
  252. }
  253. std::lock_guard lk(m_devices_mutex);
  254. for (const auto& d : m_devices)
  255. {
  256. Device::Input* const i = d->FindInput(name);
  257. if (i)
  258. return i;
  259. }
  260. return nullptr;
  261. }
  262. Device::Output* DeviceContainer::FindOutput(std::string_view name, const Device* def_dev) const
  263. {
  264. return def_dev->FindOutput(name);
  265. }
  266. bool DeviceContainer::HasConnectedDevice(const DeviceQualifier& qualifier) const
  267. {
  268. const auto device = FindDevice(qualifier);
  269. return device != nullptr && device->IsValid();
  270. }
  271. // Wait for inputs on supplied devices.
  272. // Inputs are only considered if they are first seen in a neutral state.
  273. // This is useful for crazy flightsticks that have certain buttons that are always held down
  274. // and also properly handles detection when using "FullAnalogSurface" inputs.
  275. // Multiple detections are returned until the various timeouts have been reached.
  276. auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings,
  277. std::chrono::milliseconds initial_wait,
  278. std::chrono::milliseconds confirmation_wait,
  279. std::chrono::milliseconds maximum_wait) const
  280. -> std::vector<InputDetection>
  281. {
  282. InputDetector input_detector;
  283. input_detector.Start(*this, device_strings);
  284. while (!input_detector.IsComplete())
  285. {
  286. Common::SleepCurrentThread(10);
  287. input_detector.Update(initial_wait, confirmation_wait, maximum_wait);
  288. }
  289. return input_detector.TakeResults();
  290. }
  291. struct InputDetector::Impl
  292. {
  293. struct InputState
  294. {
  295. InputState(ciface::Core::Device::Input* input_) : input{input_} { stats.Push(0.0); }
  296. ciface::Core::Device::Input* input;
  297. ControlState initial_state = input->GetState();
  298. ControlState last_state = initial_state;
  299. MathUtil::RunningVariance<ControlState> stats;
  300. // Prevent multiple detections until after release.
  301. bool is_ready = true;
  302. void Update()
  303. {
  304. const auto new_state = input->GetState();
  305. if (!is_ready && new_state < (1 - INPUT_DETECT_THRESHOLD))
  306. {
  307. last_state = new_state;
  308. is_ready = true;
  309. stats.Clear();
  310. }
  311. const auto difference = new_state - last_state;
  312. stats.Push(difference);
  313. last_state = new_state;
  314. }
  315. bool IsPressed()
  316. {
  317. if (!is_ready)
  318. return false;
  319. // We want an input that was initially 0.0 and currently 1.0.
  320. const auto detection_score = (last_state - std::abs(initial_state));
  321. return detection_score > INPUT_DETECT_THRESHOLD;
  322. }
  323. };
  324. struct DeviceState
  325. {
  326. std::shared_ptr<Device> device;
  327. std::vector<InputState> input_states;
  328. };
  329. std::vector<DeviceState> device_states;
  330. };
  331. InputDetector::InputDetector() : m_start_time{}, m_state{}
  332. {
  333. }
  334. void InputDetector::Start(const DeviceContainer& container,
  335. const std::vector<std::string>& device_strings)
  336. {
  337. m_start_time = Clock::now();
  338. m_detections = {};
  339. m_state = std::make_unique<Impl>();
  340. // Acquire devices and initial input states.
  341. for (const auto& device_string : device_strings)
  342. {
  343. DeviceQualifier dq;
  344. dq.FromString(device_string);
  345. auto device = container.FindDevice(dq);
  346. if (!device)
  347. continue;
  348. std::vector<Impl::InputState> input_states;
  349. for (auto* input : device->Inputs())
  350. {
  351. // Don't detect things like absolute cursor positions, accelerometers, or gyroscopes.
  352. if (!input->IsDetectable())
  353. continue;
  354. // Undesirable axes will have negative values here when trying to map a
  355. // "FullAnalogSurface".
  356. input_states.push_back(Impl::InputState{input});
  357. }
  358. if (!input_states.empty())
  359. {
  360. m_state->device_states.emplace_back(
  361. Impl::DeviceState{std::move(device), std::move(input_states)});
  362. }
  363. }
  364. // If no inputs were found via the supplied device strings, immediately complete.
  365. if (m_state->device_states.empty())
  366. m_state.reset();
  367. }
  368. void InputDetector::Update(std::chrono::milliseconds initial_wait,
  369. std::chrono::milliseconds confirmation_wait,
  370. std::chrono::milliseconds maximum_wait)
  371. {
  372. if (m_state)
  373. {
  374. const auto now = Clock::now();
  375. const auto elapsed_time = now - m_start_time;
  376. if (elapsed_time >= maximum_wait || (m_detections.empty() && elapsed_time >= initial_wait) ||
  377. (!m_detections.empty() && m_detections.back().release_time.has_value() &&
  378. now >= *m_detections.back().release_time + confirmation_wait))
  379. {
  380. m_state.reset();
  381. return;
  382. }
  383. for (auto& device_state : m_state->device_states)
  384. {
  385. for (auto& input_state : device_state.input_states)
  386. {
  387. input_state.Update();
  388. if (input_state.IsPressed())
  389. {
  390. input_state.is_ready = false;
  391. // Digital presses will evaluate as 1 here.
  392. // Analog presses will evaluate greater than 1.
  393. const auto smoothness =
  394. 1 / std::sqrt(input_state.stats.Variance() / input_state.stats.Mean());
  395. Detection new_detection;
  396. new_detection.device = device_state.device;
  397. new_detection.input = input_state.input;
  398. new_detection.press_time = now;
  399. new_detection.smoothness = smoothness;
  400. // We found an input. Add it to our detections.
  401. m_detections.emplace_back(std::move(new_detection));
  402. }
  403. }
  404. }
  405. // Check for any releases of our detected inputs.
  406. for (auto& d : m_detections)
  407. {
  408. if (!d.release_time.has_value() && d.input->GetState() < (1 - INPUT_DETECT_THRESHOLD))
  409. d.release_time = Clock::now();
  410. }
  411. }
  412. }
  413. InputDetector::~InputDetector() = default;
  414. bool InputDetector::IsComplete() const
  415. {
  416. return !m_state;
  417. }
  418. auto InputDetector::GetResults() const -> const Results&
  419. {
  420. return m_detections;
  421. }
  422. auto InputDetector::TakeResults() -> Results
  423. {
  424. return std::move(m_detections);
  425. }
  426. } // namespace ciface::Core