[Windows] Remove redundant DirectSound error codes
[xbmc.git] / xbmc / platform / android / peripherals / AndroidJoystickState.cpp
blob3b1099dd3b375a23295e974cd6c43d4a45986722
1 /*
2 * Copyright (C) 2016-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "AndroidJoystickState.h"
11 #include "AndroidJoystickTranslator.h"
12 #include "games/controllers/ControllerIDs.h"
13 #include "games/controllers/DefaultController.h"
14 #include "input/joysticks/DriverPrimitive.h"
15 #include "input/joysticks/JoystickTypes.h"
16 #include "input/joysticks/interfaces/IButtonMap.h"
17 #include "utils/StringUtils.h"
18 #include "utils/log.h"
20 #include <algorithm>
21 #include <mutex>
22 #include <utility>
24 #include <android/input.h>
25 #include <androidjni/View.h>
27 using namespace KODI;
28 using namespace PERIPHERALS;
30 namespace
32 // clang-format off
33 static const std::vector<int> ButtonKeycodes{
34 // add the usual suspects
35 AKEYCODE_BUTTON_A,
36 AKEYCODE_BUTTON_B,
37 AKEYCODE_BUTTON_C,
38 AKEYCODE_BUTTON_X,
39 AKEYCODE_BUTTON_Y,
40 AKEYCODE_BUTTON_Z,
41 AKEYCODE_BACK,
42 AKEYCODE_MENU,
43 AKEYCODE_HOME,
44 AKEYCODE_BUTTON_SELECT,
45 AKEYCODE_BUTTON_MODE,
46 AKEYCODE_BUTTON_START,
47 AKEYCODE_BUTTON_L1,
48 AKEYCODE_BUTTON_R1,
49 AKEYCODE_BUTTON_L2,
50 AKEYCODE_BUTTON_R2,
51 AKEYCODE_BUTTON_THUMBL,
52 AKEYCODE_BUTTON_THUMBR,
53 AKEYCODE_DPAD_UP,
54 AKEYCODE_DPAD_RIGHT,
55 AKEYCODE_DPAD_DOWN,
56 AKEYCODE_DPAD_LEFT,
57 AKEYCODE_DPAD_CENTER,
58 // add generic gamepad buttons for controllers that Android doesn't know
59 // how to map
60 AKEYCODE_BUTTON_1,
61 AKEYCODE_BUTTON_2,
62 AKEYCODE_BUTTON_3,
63 AKEYCODE_BUTTON_4,
64 AKEYCODE_BUTTON_5,
65 AKEYCODE_BUTTON_6,
66 AKEYCODE_BUTTON_7,
67 AKEYCODE_BUTTON_8,
68 AKEYCODE_BUTTON_9,
69 AKEYCODE_BUTTON_10,
70 AKEYCODE_BUTTON_11,
71 AKEYCODE_BUTTON_12,
72 AKEYCODE_BUTTON_13,
73 AKEYCODE_BUTTON_14,
74 AKEYCODE_BUTTON_15,
75 AKEYCODE_BUTTON_16,
76 // only add additional buttons at the end of the list
78 // clang-format on
80 // clang-format off
81 static const std::vector<int> AxisIDs{
82 AMOTION_EVENT_AXIS_HAT_X,
83 AMOTION_EVENT_AXIS_HAT_Y,
84 AMOTION_EVENT_AXIS_X,
85 AMOTION_EVENT_AXIS_Y,
86 AMOTION_EVENT_AXIS_Z,
87 AMOTION_EVENT_AXIS_RX,
88 AMOTION_EVENT_AXIS_RY,
89 AMOTION_EVENT_AXIS_RZ,
90 AMOTION_EVENT_AXIS_LTRIGGER,
91 AMOTION_EVENT_AXIS_RTRIGGER,
92 AMOTION_EVENT_AXIS_GAS,
93 AMOTION_EVENT_AXIS_BRAKE,
94 AMOTION_EVENT_AXIS_THROTTLE,
95 AMOTION_EVENT_AXIS_RUDDER,
96 AMOTION_EVENT_AXIS_WHEEL,
97 AMOTION_EVENT_AXIS_GENERIC_1,
98 AMOTION_EVENT_AXIS_GENERIC_2,
99 AMOTION_EVENT_AXIS_GENERIC_3,
100 AMOTION_EVENT_AXIS_GENERIC_4,
101 AMOTION_EVENT_AXIS_GENERIC_5,
102 AMOTION_EVENT_AXIS_GENERIC_6,
103 AMOTION_EVENT_AXIS_GENERIC_7,
104 AMOTION_EVENT_AXIS_GENERIC_8,
105 AMOTION_EVENT_AXIS_GENERIC_9,
106 AMOTION_EVENT_AXIS_GENERIC_10,
107 AMOTION_EVENT_AXIS_GENERIC_11,
108 AMOTION_EVENT_AXIS_GENERIC_12,
109 AMOTION_EVENT_AXIS_GENERIC_13,
110 AMOTION_EVENT_AXIS_GENERIC_14,
111 AMOTION_EVENT_AXIS_GENERIC_15,
112 AMOTION_EVENT_AXIS_GENERIC_16,
114 // clang-format on
116 static void MapAxisIds(int axisId,
117 int primaryAxisId,
118 int secondaryAxisId,
119 std::vector<int>& axisIds)
121 if (axisId != primaryAxisId && axisId != secondaryAxisId)
122 return;
124 if (axisIds.empty())
126 axisIds.emplace_back(primaryAxisId);
127 axisIds.emplace_back(secondaryAxisId);
130 if (axisIds.size() > 1)
131 return;
133 if (axisId == primaryAxisId)
134 axisIds.emplace_back(secondaryAxisId);
135 else if (axisId == secondaryAxisId)
136 axisIds.insert(axisIds.begin(), primaryAxisId);
138 } // namespace
140 CAndroidJoystickState::CAndroidJoystickState(CAndroidJoystickState&& other) noexcept
141 : m_deviceId(other.m_deviceId),
142 m_buttons(std::move(other.m_buttons)),
143 m_axes(std::move(other.m_axes)),
144 m_analogState(std::move(other.m_analogState)),
145 m_digitalEvents(std::move(other.m_digitalEvents))
149 CAndroidJoystickState::~CAndroidJoystickState()
151 Deinitialize();
154 bool CAndroidJoystickState::Initialize(const CJNIViewInputDevice& inputDevice)
156 if (!inputDevice)
157 return false;
159 const std::string deviceName = inputDevice.getName();
161 // get the device ID
162 m_deviceId = inputDevice.getId();
164 // get all motion ranges to be able to count the number of available buttons, hats and axis'
165 const CJNIList<CJNIViewInputDeviceMotionRange> motionRanges = inputDevice.getMotionRanges();
166 for (int index = 0; index < motionRanges.size(); ++index)
168 const CJNIViewInputDeviceMotionRange motionRange = motionRanges.get(index);
169 if (!motionRange.isFromSource(CJNIViewInputDevice::SOURCE_JOYSTICK) &&
170 !motionRange.isFromSource(CJNIViewInputDevice::SOURCE_GAMEPAD))
172 CLog::Log(LOGDEBUG,
173 "CAndroidJoystickState: axis {} has unexpected source {} for input device \"{}\" "
174 "with ID {}",
175 motionRange.getAxis(), motionRange.getSource(), deviceName, m_deviceId);
178 int axisId = motionRange.getAxis();
179 JoystickAxis axis{{axisId},
180 motionRange.getFlat(),
181 motionRange.getFuzz(),
182 motionRange.getMin(),
183 motionRange.getMax(),
184 motionRange.getRange(),
185 motionRange.getResolution()};
187 // check if the axis ID belongs to a D-pad, analogue stick, trigger or
188 // generic axis
189 if (std::find(AxisIDs.begin(), AxisIDs.end(), axisId) != AxisIDs.end())
191 CLog::Log(LOGDEBUG, "CAndroidJoystickState: axis found: {} ({})",
192 CAndroidJoystickTranslator::TranslateAxis(axisId), axisId);
194 // check if this axis is already known
195 if (ContainsAxis(axisId, m_axes))
196 continue;
198 // map AMOTION_EVENT_AXIS_GAS to AMOTION_EVENT_AXIS_RTRIGGER and
199 // AMOTION_EVENT_AXIS_BRAKE to AMOTION_EVENT_AXIS_LTRIGGER
200 // to avoid duplicate events on controllers sending both events
201 MapAxisIds(axisId, AMOTION_EVENT_AXIS_LTRIGGER, AMOTION_EVENT_AXIS_BRAKE, axis.ids);
202 MapAxisIds(axisId, AMOTION_EVENT_AXIS_RTRIGGER, AMOTION_EVENT_AXIS_GAS, axis.ids);
204 m_axes.emplace_back(std::move(axis));
206 else
207 CLog::Log(LOGWARNING,
208 "CAndroidJoystickState: ignoring unknown axis {} on input device \"{}\" with ID {}",
209 axisId, deviceName, m_deviceId);
212 // check for presence of buttons
213 auto results = inputDevice.hasKeys(ButtonKeycodes);
215 if (results.size() != ButtonKeycodes.size())
217 CLog::Log(LOGERROR, "CAndroidJoystickState: failed to get key status for {} buttons",
218 ButtonKeycodes.size());
219 return false;
222 // log positive results and assign results to buttons
223 for (unsigned int i = 0; i < ButtonKeycodes.size(); ++i)
225 if (results[i])
227 const int buttonKeycode = ButtonKeycodes[i];
228 CLog::Log(LOGDEBUG, "CAndroidJoystickState: button found: {} ({})",
229 CAndroidJoystickTranslator::TranslateKeyCode(buttonKeycode), buttonKeycode);
230 m_buttons.emplace_back(JoystickAxis{{buttonKeycode}});
234 // check if there are no buttons or axes at all
235 if (GetButtonCount() == 0 && GetAxisCount() == 0)
237 CLog::Log(LOGWARNING,
238 "CAndroidJoystickState: no buttons, hats or axes detected for input device \"{}\" "
239 "with ID {}",
240 deviceName, m_deviceId);
241 return false;
244 m_analogState.assign(GetAxisCount(), 0.0f);
246 return true;
249 void CAndroidJoystickState::Deinitialize(void)
251 m_buttons.clear();
252 m_axes.clear();
254 m_analogState.clear();
255 m_digitalEvents.clear();
258 bool CAndroidJoystickState::InitializeButtonMap(JOYSTICK::IButtonMap& buttonMap) const
260 // We only map the default controller
261 if (buttonMap.ControllerID() != DEFAULT_CONTROLLER_ID)
262 return false;
264 bool success = false;
266 // Map buttons
267 for (int buttonKeycode : ButtonKeycodes)
268 success |= MapButton(buttonMap, buttonKeycode);
270 // Map D-pad
271 success |= MapDpad(buttonMap, AMOTION_EVENT_AXIS_HAT_X, AMOTION_EVENT_AXIS_HAT_Y);
273 // Map triggers
274 // Note: This should come after buttons, because the PS4 controller uses
275 // both a digital button and an analog axis for the triggers, and we want
276 // the analog axis to override the button for full range of motion.
277 success |= MapTrigger(buttonMap, AMOTION_EVENT_AXIS_LTRIGGER,
278 GAME::CDefaultController::FEATURE_LEFT_TRIGGER);
279 success |= MapTrigger(buttonMap, AMOTION_EVENT_AXIS_RTRIGGER,
280 GAME::CDefaultController::FEATURE_RIGHT_TRIGGER);
282 // Map analog sticks
283 success |= MapAnalogStick(buttonMap, AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y,
284 GAME::CDefaultController::FEATURE_LEFT_STICK);
285 success |= MapAnalogStick(buttonMap, AMOTION_EVENT_AXIS_Z, AMOTION_EVENT_AXIS_RZ,
286 GAME::CDefaultController::FEATURE_RIGHT_STICK);
288 if (success)
290 // If the controller has both L2/R2 buttons and LTRIGGER/RTRIGGER axes, it's
291 // probably a PS controller
292 size_t indexL2 = 0;
293 size_t indexR2 = 0;
294 size_t indexLTrigger = 0;
295 size_t indexRTrigger = 0;
296 if (GetAxesIndex({AKEYCODE_BUTTON_L2}, m_buttons, indexL2) &&
297 GetAxesIndex({AKEYCODE_BUTTON_R2}, m_buttons, indexR2) &&
298 GetAxesIndex({AMOTION_EVENT_AXIS_LTRIGGER}, m_axes, indexLTrigger) &&
299 GetAxesIndex({AMOTION_EVENT_AXIS_RTRIGGER}, m_axes, indexRTrigger))
301 CLog::Log(LOGDEBUG, "Detected dual-input triggers, ignoring digital buttons");
302 std::vector<JOYSTICK::CDriverPrimitive> ignoredPrimitives{
303 {JOYSTICK::PRIMITIVE_TYPE::BUTTON, static_cast<unsigned int>(indexL2)},
304 {JOYSTICK::PRIMITIVE_TYPE::BUTTON, static_cast<unsigned int>(indexR2)},
306 buttonMap.SetIgnoredPrimitives(ignoredPrimitives);
308 CLog::Log(LOGDEBUG, "Setting appearance to {}", GAME::CONTROLLER_ID_PLAYSTATION);
309 buttonMap.SetAppearance(GAME::CONTROLLER_ID_PLAYSTATION);
312 // Save the buttonmap
313 buttonMap.SaveButtonMap();
316 return success;
319 bool CAndroidJoystickState::ProcessEvent(const AInputEvent* event)
321 int32_t type = AInputEvent_getType(event);
323 switch (type)
325 case AINPUT_EVENT_TYPE_KEY:
327 int32_t keycode = AKeyEvent_getKeyCode(event);
328 int32_t action = AKeyEvent_getAction(event);
330 JOYSTICK_STATE_BUTTON buttonState = JOYSTICK_STATE_BUTTON_UNPRESSED;
331 if (action == AKEY_EVENT_ACTION_DOWN)
332 buttonState = JOYSTICK_STATE_BUTTON_PRESSED;
334 CLog::Log(LOGDEBUG, "Android Key {} ({}) {}",
335 CAndroidJoystickTranslator::TranslateKeyCode(keycode), keycode,
336 (buttonState == JOYSTICK_STATE_BUTTON_UNPRESSED ? "released" : "pressed"));
338 bool result = SetButtonValue(keycode, buttonState);
340 return result;
343 case AINPUT_EVENT_TYPE_MOTION:
345 size_t count = AMotionEvent_getPointerCount(event);
347 bool success = false;
348 for (size_t pointer = 0; pointer < count; ++pointer)
350 // process all axes
351 for (const auto& axis : m_axes)
353 // get all potential values
354 std::vector<float> values;
355 values.reserve(axis.ids.size());
356 for (const auto& axisId : axis.ids)
357 values.emplace_back(AMotionEvent_getAxisValue(event, axisId, pointer));
359 // remove all zero values
360 values.erase(std::remove(values.begin(), values.end(), 0.0f), values.end());
362 float value = 0.0f;
363 // pick the first non-zero value
364 if (!values.empty())
365 value = values.front();
367 success |= SetAxisValue(axis.ids, value);
370 return success;
373 default:
374 CLog::Log(LOGWARNING,
375 "CAndroidJoystickState: unknown input event type {} from input device with ID {}",
376 type, m_deviceId);
377 break;
380 return false;
383 void CAndroidJoystickState::GetEvents(std::vector<kodi::addon::PeripheralEvent>& events)
385 GetButtonEvents(events);
386 GetAxisEvents(events);
389 void CAndroidJoystickState::GetButtonEvents(std::vector<kodi::addon::PeripheralEvent>& events)
391 std::unique_lock<CCriticalSection> lock(m_eventMutex);
393 // Only report a single event per button (avoids dropping rapid presses)
394 std::vector<kodi::addon::PeripheralEvent> repeatButtons;
396 for (const auto& digitalEvent : m_digitalEvents)
398 auto HasButton = [&digitalEvent](const kodi::addon::PeripheralEvent& event)
400 if (event.Type() == PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON)
401 return event.DriverIndex() == digitalEvent.DriverIndex();
402 return false;
405 if (std::find_if(events.begin(), events.end(), HasButton) == events.end())
406 events.emplace_back(digitalEvent);
407 else
408 repeatButtons.emplace_back(digitalEvent);
411 m_digitalEvents.swap(repeatButtons);
414 void CAndroidJoystickState::GetAxisEvents(std::vector<kodi::addon::PeripheralEvent>& events) const
416 for (unsigned int i = 0; i < m_analogState.size(); i++)
417 events.emplace_back(m_deviceId, i, m_analogState[i]);
420 bool CAndroidJoystickState::SetButtonValue(int axisId, JOYSTICK_STATE_BUTTON buttonValue)
422 size_t buttonIndex = 0;
423 if (!GetAxesIndex({axisId}, m_buttons, buttonIndex) || buttonIndex >= GetButtonCount())
424 return false;
426 std::unique_lock<CCriticalSection> lock(m_eventMutex);
428 m_digitalEvents.emplace_back(m_deviceId, buttonIndex, buttonValue);
430 return true;
433 bool CAndroidJoystickState::SetAxisValue(const std::vector<int>& axisIds,
434 JOYSTICK_STATE_AXIS axisValue)
436 size_t axisIndex = 0;
437 if (!GetAxesIndex(axisIds, m_axes, axisIndex) || axisIndex >= GetAxisCount())
438 return false;
440 const JoystickAxis& axis = m_axes[axisIndex];
442 // make sure that the axis value is in the valid range
443 axisValue = Contain(axisValue, axis.min, axis.max);
444 // apply deadzoning
445 axisValue = Deadzone(axisValue, axis.flat);
446 // scale the axis value down to a value between -1.0f and 1.0f
447 axisValue = Scale(axisValue, axis.max, 1.0f);
449 m_analogState[axisIndex] = axisValue;
450 return true;
453 bool CAndroidJoystickState::MapButton(JOYSTICK::IButtonMap& buttonMap, int buttonKeycode) const
455 size_t buttonIndex = 0;
456 std::string featureName;
458 if (!GetAxesIndex({buttonKeycode}, m_buttons, buttonIndex))
459 return false;
461 // Check if button is already mapped
462 JOYSTICK::CDriverPrimitive buttonPrimitive{JOYSTICK::PRIMITIVE_TYPE::BUTTON,
463 static_cast<unsigned int>(buttonIndex)};
464 if (buttonMap.GetFeature(buttonPrimitive, featureName))
465 return false;
467 // Translate the button
468 std::string controllerButton = CAndroidJoystickTranslator::TranslateJoystickButton(buttonKeycode);
469 if (controllerButton.empty())
470 return false;
472 // Check if feature is already mapped
473 if (buttonMap.GetFeatureType(controllerButton) != JOYSTICK::FEATURE_TYPE::UNKNOWN)
474 return false;
476 // Map the button
477 CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", controllerButton,
478 buttonPrimitive.ToString());
479 buttonMap.AddScalar(controllerButton, buttonPrimitive);
481 return true;
484 bool CAndroidJoystickState::MapTrigger(JOYSTICK::IButtonMap& buttonMap,
485 int axisId,
486 const std::string& triggerName) const
488 size_t axisIndex = 0;
489 std::string featureName;
491 if (!GetAxesIndex({axisId}, m_axes, axisIndex))
492 return false;
494 const JOYSTICK::CDriverPrimitive semiaxis{static_cast<unsigned int>(axisIndex), 0,
495 JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
496 if (buttonMap.GetFeature(semiaxis, featureName))
497 return false;
499 CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", triggerName, semiaxis.ToString());
500 buttonMap.AddScalar(triggerName, semiaxis);
502 return true;
505 bool CAndroidJoystickState::MapDpad(JOYSTICK::IButtonMap& buttonMap,
506 int horizAxisId,
507 int vertAxisId) const
509 bool success = false;
511 size_t axisIndex = 0;
512 std::string featureName;
514 // Map horizontal axis
515 if (GetAxesIndex({horizAxisId}, m_axes, axisIndex))
517 const JOYSTICK::CDriverPrimitive positiveSemiaxis{static_cast<unsigned int>(axisIndex), 0,
518 JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
519 const JOYSTICK::CDriverPrimitive negativeSemiaxis{static_cast<unsigned int>(axisIndex), 0,
520 JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
521 if (!buttonMap.GetFeature(positiveSemiaxis, featureName) &&
522 !buttonMap.GetFeature(negativeSemiaxis, featureName))
524 CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_LEFT,
525 negativeSemiaxis.ToString());
526 CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_RIGHT,
527 positiveSemiaxis.ToString());
528 buttonMap.AddScalar(GAME::CDefaultController::FEATURE_LEFT, negativeSemiaxis);
529 buttonMap.AddScalar(GAME::CDefaultController::FEATURE_RIGHT, positiveSemiaxis);
530 success |= true;
534 // Map vertical axis
535 if (GetAxesIndex({vertAxisId}, m_axes, axisIndex))
537 const JOYSTICK::CDriverPrimitive positiveSemiaxis{static_cast<unsigned int>(axisIndex), 0,
538 JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
539 const JOYSTICK::CDriverPrimitive negativeSemiaxis{static_cast<unsigned int>(axisIndex), 0,
540 JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
541 if (!buttonMap.GetFeature(positiveSemiaxis, featureName) &&
542 !buttonMap.GetFeature(negativeSemiaxis, featureName))
544 CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_UP,
545 negativeSemiaxis.ToString());
546 CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_DOWN,
547 positiveSemiaxis.ToString());
548 buttonMap.AddScalar(GAME::CDefaultController::FEATURE_DOWN, positiveSemiaxis);
549 buttonMap.AddScalar(GAME::CDefaultController::FEATURE_UP, negativeSemiaxis);
550 success |= true;
554 return success;
557 bool CAndroidJoystickState::MapAnalogStick(JOYSTICK::IButtonMap& buttonMap,
558 int horizAxisId,
559 int vertAxisId,
560 const std::string& analogStickName) const
562 size_t axisIndex1 = 0;
563 size_t axisIndex2 = 0;
564 std::string featureName;
566 if (!GetAxesIndex({horizAxisId}, m_axes, axisIndex1) ||
567 !GetAxesIndex({vertAxisId}, m_axes, axisIndex2))
568 return false;
570 const JOYSTICK::CDriverPrimitive upSemiaxis{static_cast<unsigned int>(axisIndex2), 0,
571 JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
572 const JOYSTICK::CDriverPrimitive downSemiaxis{static_cast<unsigned int>(axisIndex2), 0,
573 JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
574 const JOYSTICK::CDriverPrimitive leftSemiaxis{static_cast<unsigned int>(axisIndex1), 0,
575 JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
576 const JOYSTICK::CDriverPrimitive rightSemiaxis{static_cast<unsigned int>(axisIndex1), 0,
577 JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
578 if (buttonMap.GetFeature(upSemiaxis, featureName) ||
579 buttonMap.GetFeature(downSemiaxis, featureName) ||
580 buttonMap.GetFeature(leftSemiaxis, featureName) ||
581 buttonMap.GetFeature(rightSemiaxis, featureName))
582 return false;
584 CLog::Log(LOGDEBUG, "Automatically mapping {} to [{}, {}, {}, {}]", analogStickName,
585 upSemiaxis.ToString(), downSemiaxis.ToString(), leftSemiaxis.ToString(),
586 rightSemiaxis.ToString());
587 buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::UP, upSemiaxis);
588 buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::DOWN, downSemiaxis);
589 buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::LEFT, leftSemiaxis);
590 buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::RIGHT, rightSemiaxis);
592 return true;
595 float CAndroidJoystickState::Contain(float value, float min, float max)
597 if (value < min)
598 return min;
599 if (value > max)
600 return max;
602 return value;
605 float CAndroidJoystickState::Scale(float value, float max, float scaledMax)
607 return value * (scaledMax / max);
610 float CAndroidJoystickState::Deadzone(float value, float deadzone)
612 if ((value > 0.0f && value < deadzone) || (value < 0.0f && value > -deadzone))
613 return 0.0f;
615 return value;
618 CAndroidJoystickState::JoystickAxes::const_iterator CAndroidJoystickState::GetAxis(
619 const std::vector<int>& axisIds, const JoystickAxes& axes)
621 return std::find_if(axes.cbegin(), axes.cend(),
622 [&axisIds](const JoystickAxis& axis)
624 std::vector<int> matches(std::max(axisIds.size(), axis.ids.size()));
625 const auto& matchesEnd =
626 std::set_intersection(axisIds.begin(), axisIds.end(), axis.ids.begin(),
627 axis.ids.end(), matches.begin());
628 matches.resize(matchesEnd - matches.begin());
629 return !matches.empty();
633 bool CAndroidJoystickState::ContainsAxis(int axisId, const JoystickAxes& axes)
635 return GetAxis({axisId}, axes) != axes.cend();
638 bool CAndroidJoystickState::GetAxesIndex(const std::vector<int>& axisIds,
639 const JoystickAxes& axes,
640 size_t& axesIndex)
642 auto axesIt = GetAxis(axisIds, axes);
643 if (axesIt == axes.end())
644 return false;
646 axesIndex = std::distance(axes.begin(), axesIt);
647 return true;