summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/input_common/input_engine.cpp361
-rw-r--r--src/input_common/input_engine.h224
2 files changed, 585 insertions, 0 deletions
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
new file mode 100644
index 000000000..1534f24b0
--- /dev/null
+++ b/src/input_common/input_engine.cpp
@@ -0,0 +1,361 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/logging/log.h"
6#include "common/param_package.h"
7#include "input_common/input_engine.h"
8
9namespace InputCommon {
10
11void InputEngine::PreSetController(const PadIdentifier& identifier) {
12 std::lock_guard lock{mutex};
13 if (!controller_list.contains(identifier)) {
14 controller_list.insert_or_assign(identifier, ControllerData{});
15 }
16}
17
18void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
19 std::lock_guard lock{mutex};
20 ControllerData& controller = controller_list.at(identifier);
21 if (!controller.buttons.contains(button)) {
22 controller.buttons.insert_or_assign(button, false);
23 }
24}
25
26void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
27 std::lock_guard lock{mutex};
28 ControllerData& controller = controller_list.at(identifier);
29 if (!controller.hat_buttons.contains(button)) {
30 controller.hat_buttons.insert_or_assign(button, u8{0});
31 }
32}
33
34void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
35 std::lock_guard lock{mutex};
36 ControllerData& controller = controller_list.at(identifier);
37 if (!controller.axes.contains(axis)) {
38 controller.axes.insert_or_assign(axis, 0.0f);
39 }
40}
41
42void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
43 std::lock_guard lock{mutex};
44 ControllerData& controller = controller_list.at(identifier);
45 if (!controller.motions.contains(motion)) {
46 controller.motions.insert_or_assign(motion, BasicMotion{});
47 }
48}
49
50void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
51 {
52 std::lock_guard lock{mutex};
53 ControllerData& controller = controller_list.at(identifier);
54 if (!configuring) {
55 controller.buttons.insert_or_assign(button, value);
56 }
57 }
58 TriggerOnButtonChange(identifier, button, value);
59}
60
61void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
62 {
63 std::lock_guard lock{mutex};
64 ControllerData& controller = controller_list.at(identifier);
65 if (!configuring) {
66 controller.hat_buttons.insert_or_assign(button, value);
67 }
68 }
69 TriggerOnHatButtonChange(identifier, button, value);
70}
71
72void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
73 {
74 std::lock_guard lock{mutex};
75 ControllerData& controller = controller_list.at(identifier);
76 if (!configuring) {
77 controller.axes.insert_or_assign(axis, value);
78 }
79 }
80 TriggerOnAxisChange(identifier, axis, value);
81}
82
83void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) {
84 {
85 std::lock_guard lock{mutex};
86 ControllerData& controller = controller_list.at(identifier);
87 if (!configuring) {
88 controller.battery = value;
89 }
90 }
91 TriggerOnBatteryChange(identifier, value);
92}
93
94void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value) {
95 {
96 std::lock_guard lock{mutex};
97 ControllerData& controller = controller_list.at(identifier);
98 if (!configuring) {
99 controller.motions.insert_or_assign(motion, value);
100 }
101 }
102 TriggerOnMotionChange(identifier, motion, value);
103}
104
105bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
106 std::lock_guard lock{mutex};
107 if (!controller_list.contains(identifier)) {
108 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
109 identifier.pad, identifier.port);
110 return false;
111 }
112 ControllerData controller = controller_list.at(identifier);
113 if (!controller.buttons.contains(button)) {
114 LOG_ERROR(Input, "Invalid button {}", button);
115 return false;
116 }
117 return controller.buttons.at(button);
118}
119
120bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
121 std::lock_guard lock{mutex};
122 if (!controller_list.contains(identifier)) {
123 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
124 identifier.pad, identifier.port);
125 return false;
126 }
127 ControllerData controller = controller_list.at(identifier);
128 if (!controller.hat_buttons.contains(button)) {
129 LOG_ERROR(Input, "Invalid hat button {}", button);
130 return false;
131 }
132 return (controller.hat_buttons.at(button) & direction) != 0;
133}
134
135f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
136 std::lock_guard lock{mutex};
137 if (!controller_list.contains(identifier)) {
138 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
139 identifier.pad, identifier.port);
140 return 0.0f;
141 }
142 ControllerData controller = controller_list.at(identifier);
143 if (!controller.axes.contains(axis)) {
144 LOG_ERROR(Input, "Invalid axis {}", axis);
145 return 0.0f;
146 }
147 return controller.axes.at(axis);
148}
149
150BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
151 std::lock_guard lock{mutex};
152 if (!controller_list.contains(identifier)) {
153 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
154 identifier.pad, identifier.port);
155 return BatteryLevel::Charging;
156 }
157 ControllerData controller = controller_list.at(identifier);
158 return controller.battery;
159}
160
161BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
162 std::lock_guard lock{mutex};
163 if (!controller_list.contains(identifier)) {
164 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
165 identifier.pad, identifier.port);
166 return {};
167 }
168 ControllerData controller = controller_list.at(identifier);
169 return controller.motions.at(motion);
170}
171
172void InputEngine::ResetButtonState() {
173 for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
174 for (std::pair<int, bool> button : controller.second.buttons) {
175 SetButton(controller.first, button.first, false);
176 }
177 for (std::pair<int, bool> button : controller.second.hat_buttons) {
178 SetHatButton(controller.first, button.first, false);
179 }
180 }
181}
182
183void InputEngine::ResetAnalogState() {
184 for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
185 for (std::pair<int, float> axis : controller.second.axes) {
186 SetAxis(controller.first, axis.first, 0.0);
187 }
188 }
189}
190
191void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
192 std::lock_guard lock{mutex_callback};
193 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
194 const InputIdentifier& poller = poller_pair.second;
195 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
196 continue;
197 }
198 if (poller.callback.on_change) {
199 poller.callback.on_change();
200 }
201 }
202 if (!configuring || !mapping_callback.on_data) {
203 return;
204 }
205 if (value == GetButton(identifier, button)) {
206 return;
207 }
208 mapping_callback.on_data(MappingData{
209 .engine = GetEngineName(),
210 .pad = identifier,
211 .type = EngineInputType::Button,
212 .index = button,
213 .button_value = value,
214 });
215}
216
217void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
218 std::lock_guard lock{mutex_callback};
219 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
220 const InputIdentifier& poller = poller_pair.second;
221 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
222 continue;
223 }
224 if (poller.callback.on_change) {
225 poller.callback.on_change();
226 }
227 }
228 if (!configuring || !mapping_callback.on_data) {
229 return;
230 }
231 for (std::size_t index = 1; index < 0xff; index <<= 1) {
232 bool button_value = (value & index) != 0;
233 if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) {
234 continue;
235 }
236 mapping_callback.on_data(MappingData{
237 .engine = GetEngineName(),
238 .pad = identifier,
239 .type = EngineInputType::HatButton,
240 .index = button,
241 .hat_name = GetHatButtonName(static_cast<u8>(index)),
242 });
243 }
244}
245
246void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
247 std::lock_guard lock{mutex_callback};
248 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
249 const InputIdentifier& poller = poller_pair.second;
250 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
251 continue;
252 }
253 if (poller.callback.on_change) {
254 poller.callback.on_change();
255 }
256 }
257 if (!configuring || !mapping_callback.on_data) {
258 return;
259 }
260 if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) {
261 return;
262 }
263 mapping_callback.on_data(MappingData{
264 .engine = GetEngineName(),
265 .pad = identifier,
266 .type = EngineInputType::Analog,
267 .index = axis,
268 .axis_value = value,
269 });
270}
271
272void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
273 [[maybe_unused]] BatteryLevel value) {
274 std::lock_guard lock{mutex_callback};
275 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
276 const InputIdentifier& poller = poller_pair.second;
277 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
278 continue;
279 }
280 if (poller.callback.on_change) {
281 poller.callback.on_change();
282 }
283 }
284}
285
286void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
287 BasicMotion value) {
288 std::lock_guard lock{mutex_callback};
289 for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
290 const InputIdentifier& poller = poller_pair.second;
291 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
292 continue;
293 }
294 if (poller.callback.on_change) {
295 poller.callback.on_change();
296 }
297 }
298 if (!configuring || !mapping_callback.on_data) {
299 return;
300 }
301 if (std::abs(value.gyro_x) < 1.0f && std::abs(value.gyro_y) < 1.0f &&
302 std::abs(value.gyro_z) < 1.0f) {
303 return;
304 }
305 mapping_callback.on_data(MappingData{
306 .engine = GetEngineName(),
307 .pad = identifier,
308 .type = EngineInputType::Motion,
309 .index = motion,
310 .motion_value = value,
311 });
312}
313
314bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
315 const PadIdentifier& identifier, EngineInputType type,
316 std::size_t index) const {
317 if (input_identifier.type != type) {
318 return false;
319 }
320 if (input_identifier.index != index) {
321 return false;
322 }
323 if (input_identifier.identifier != identifier) {
324 return false;
325 }
326 return true;
327}
328
329void InputEngine::BeginConfiguration() {
330 configuring = true;
331}
332
333void InputEngine::EndConfiguration() {
334 configuring = false;
335}
336
337const std::string& InputEngine::GetEngineName() const {
338 return input_engine;
339}
340
341int InputEngine::SetCallback(InputIdentifier input_identifier) {
342 std::lock_guard lock{mutex_callback};
343 callback_list.insert_or_assign(last_callback_key, input_identifier);
344 return last_callback_key++;
345}
346
347void InputEngine::SetMappingCallback(MappingCallback callback) {
348 std::lock_guard lock{mutex_callback};
349 mapping_callback = std::move(callback);
350}
351
352void InputEngine::DeleteCallback(int key) {
353 std::lock_guard lock{mutex_callback};
354 if (!callback_list.contains(key)) {
355 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
356 return;
357 }
358 callback_list.erase(key);
359}
360
361} // namespace InputCommon
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
new file mode 100644
index 000000000..86a8e00d8
--- /dev/null
+++ b/src/input_common/input_engine.h
@@ -0,0 +1,224 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include <functional>
8#include <mutex>
9#include <unordered_map>
10
11#include "common/common_types.h"
12#include "common/input.h"
13#include "common/param_package.h"
14#include "common/uuid.h"
15#include "input_common/main.h"
16
17// Pad Identifier of data source
18struct PadIdentifier {
19 Common::UUID guid{};
20 std::size_t port{};
21 std::size_t pad{};
22
23 friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
24};
25
26// Basic motion data containing data from the sensors and a timestamp in microsecons
27struct BasicMotion {
28 float gyro_x;
29 float gyro_y;
30 float gyro_z;
31 float accel_x;
32 float accel_y;
33 float accel_z;
34 u64 delta_timestamp;
35};
36
37// Stages of a battery charge
38enum class BatteryLevel {
39 Empty,
40 Critical,
41 Low,
42 Medium,
43 Full,
44 Charging,
45};
46
47// Types of input that are stored in the engine
48enum class EngineInputType {
49 None,
50 Button,
51 HatButton,
52 Analog,
53 Motion,
54 Battery,
55};
56
57namespace std {
58// Hash used to create lists from PadIdentifier data
59template <>
60struct hash<PadIdentifier> {
61 size_t operator()(const PadIdentifier& pad_id) const noexcept {
62 u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0];
63 hash_value ^= (static_cast<u64>(pad_id.port) << 32);
64 hash_value ^= static_cast<u64>(pad_id.pad);
65 return static_cast<size_t>(hash_value);
66 }
67};
68
69} // namespace std
70
71namespace InputCommon {
72
73// Data from the engine and device needed for creating a ParamPackage
74struct MappingData {
75 std::string engine{};
76 PadIdentifier pad{};
77 EngineInputType type{};
78 int index{};
79 bool button_value{};
80 std::string hat_name{};
81 f32 axis_value{};
82 BasicMotion motion_value{};
83};
84
85// Triggered if data changed on the controller
86struct UpdateCallback {
87 std::function<void()> on_change;
88};
89
90// Triggered if data changed on the controller and the engine is on configuring mode
91struct MappingCallback {
92 std::function<void(MappingData)> on_data;
93};
94
95// Input Identifier of data source
96struct InputIdentifier {
97 PadIdentifier identifier;
98 EngineInputType type;
99 std::size_t index;
100 UpdateCallback callback;
101};
102
103class InputEngine {
104public:
105 explicit InputEngine(const std::string& input_engine_) : input_engine(input_engine_) {
106 callback_list.clear();
107 }
108
109 virtual ~InputEngine() = default;
110
111 // Enable configuring mode for mapping
112 void BeginConfiguration();
113
114 // Disable configuring mode for mapping
115 void EndConfiguration();
116
117 // Sets rumble to a controller
118 virtual bool SetRumble([[maybe_unused]] const PadIdentifier& identifier,
119 [[maybe_unused]] const Input::VibrationStatus vibration) {
120 return false;
121 }
122
123 // Sets a led pattern for a controller
124 virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
125 [[maybe_unused]] const Input::LedStatus led_status) {
126 return;
127 }
128
129 // Returns the engine name
130 [[nodiscard]] const std::string& GetEngineName() const;
131
132 /// Used for automapping features
133 virtual std::vector<Common::ParamPackage> GetInputDevices() const {
134 return {};
135 };
136
137 /// Retrieves the button mappings for the given device
138 virtual InputCommon::ButtonMapping GetButtonMappingForDevice(
139 [[maybe_unused]] const Common::ParamPackage& params) {
140 return {};
141 };
142
143 /// Retrieves the analog mappings for the given device
144 virtual InputCommon::AnalogMapping GetAnalogMappingForDevice(
145 [[maybe_unused]] const Common::ParamPackage& params) {
146 return {};
147 };
148
149 /// Retrieves the motion mappings for the given device
150 virtual InputCommon::MotionMapping GetMotionMappingForDevice(
151 [[maybe_unused]] const Common::ParamPackage& params) {
152 return {};
153 };
154
155 /// Retrieves the name of the given input.
156 virtual std::string GetUIName([[maybe_unused]] const Common::ParamPackage& params) const {
157 return GetEngineName();
158 };
159
160 /// Retrieves the index number of the given hat button direction
161 virtual u8 GetHatButtonId([[maybe_unused]] const std::string direction_name) const {
162 return 0;
163 };
164
165 void PreSetController(const PadIdentifier& identifier);
166 void PreSetButton(const PadIdentifier& identifier, int button);
167 void PreSetHatButton(const PadIdentifier& identifier, int button);
168 void PreSetAxis(const PadIdentifier& identifier, int axis);
169 void PreSetMotion(const PadIdentifier& identifier, int motion);
170 void ResetButtonState();
171 void ResetAnalogState();
172
173 bool GetButton(const PadIdentifier& identifier, int button) const;
174 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
175 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
176 BatteryLevel GetBattery(const PadIdentifier& identifier) const;
177 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
178
179 int SetCallback(InputIdentifier input_identifier);
180 void SetMappingCallback(MappingCallback callback);
181 void DeleteCallback(int key);
182
183protected:
184 void SetButton(const PadIdentifier& identifier, int button, bool value);
185 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
186 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
187 void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
188 void SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value);
189
190 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
191 return "Unknown";
192 }
193
194private:
195 struct ControllerData {
196 std::unordered_map<int, bool> buttons;
197 std::unordered_map<int, u8> hat_buttons;
198 std::unordered_map<int, float> axes;
199 std::unordered_map<int, BasicMotion> motions;
200 BatteryLevel battery;
201 };
202
203 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
204 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
205 void TriggerOnAxisChange(const PadIdentifier& identifier, int button, f32 value);
206 void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
207 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, BasicMotion value);
208
209 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
210 const PadIdentifier& identifier, EngineInputType type,
211 std::size_t index) const;
212
213 mutable std::mutex mutex;
214 mutable std::mutex mutex_callback;
215 bool configuring{false};
216 bool is_callback_enabled{true};
217 const std::string input_engine;
218 int last_callback_key = 0;
219 std::unordered_map<PadIdentifier, ControllerData> controller_list;
220 std::unordered_map<int, InputIdentifier> callback_list;
221 MappingCallback mapping_callback;
222};
223
224} // namespace InputCommon