aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Brown <themagnificentmrb@gmail.com>2024-01-30 13:23:30 -0800
committerGarrett Brown <themagnificentmrb@gmail.com>2024-06-26 14:42:29 -0700
commitd23e6184029698e234e464c1553ce1ee214256f6 (patch)
tree96f318d93a3a9264b24655ce8901cf6f75c37a1f
parent87ed582b5e06539957cf9a4666a7eadc325f6a80 (diff)
[Android][Peripherals] Initialize buttonmap with Android mapping, if possible
-rw-r--r--xbmc/games/controllers/ControllerIDs.h11
-rw-r--r--xbmc/peripherals/Peripherals.cpp2
-rw-r--r--xbmc/peripherals/addons/AddonButtonMap.cpp29
-rw-r--r--xbmc/peripherals/addons/AddonButtonMap.h5
-rw-r--r--xbmc/peripherals/addons/AddonButtonMapping.cpp2
-rw-r--r--xbmc/peripherals/addons/AddonInputHandling.cpp24
-rw-r--r--xbmc/peripherals/addons/AddonInputHandling.h11
-rw-r--r--xbmc/peripherals/bus/PeripheralBus.h17
-rw-r--r--xbmc/peripherals/devices/Peripheral.cpp6
-rw-r--r--xbmc/peripherals/devices/PeripheralJoystick.cpp3
-rw-r--r--xbmc/platform/android/peripherals/AndroidJoystickState.cpp265
-rw-r--r--xbmc/platform/android/peripherals/AndroidJoystickState.h30
-rw-r--r--xbmc/platform/android/peripherals/AndroidJoystickTranslator.cpp52
-rw-r--r--xbmc/platform/android/peripherals/AndroidJoystickTranslator.h9
-rw-r--r--xbmc/platform/android/peripherals/PeripheralBusAndroid.cpp45
-rw-r--r--xbmc/platform/android/peripherals/PeripheralBusAndroid.h2
16 files changed, 470 insertions, 43 deletions
diff --git a/xbmc/games/controllers/ControllerIDs.h b/xbmc/games/controllers/ControllerIDs.h
index a9254e015d..68d100d156 100644
--- a/xbmc/games/controllers/ControllerIDs.h
+++ b/xbmc/games/controllers/ControllerIDs.h
@@ -13,3 +13,14 @@
#define DEFAULT_KEYBOARD_ID "game.controller.keyboard"
#define DEFAULT_MOUSE_ID "game.controller.mouse"
#define DEFAULT_REMOTE_ID "game.controller.remote"
+
+namespace KODI
+{
+namespace GAME
+{
+
+// Used to set the appearance of PlayStation controllers
+constexpr const char* CONTROLLER_ID_PLAYSTATION = "game.controller.ps.dualanalog";
+
+} // namespace GAME
+} // namespace KODI
diff --git a/xbmc/peripherals/Peripherals.cpp b/xbmc/peripherals/Peripherals.cpp
index e282fbf700..1276cf52d6 100644
--- a/xbmc/peripherals/Peripherals.cpp
+++ b/xbmc/peripherals/Peripherals.cpp
@@ -917,7 +917,7 @@ void CPeripherals::ResetButtonMaps(const std::string& controllerId)
PeripheralAddonPtr addon;
if (addonBus->GetAddonWithButtonMap(peripheral.get(), addon))
{
- CAddonButtonMap buttonMap(peripheral.get(), addon, controllerId);
+ CAddonButtonMap buttonMap(peripheral.get(), addon, controllerId, *this);
buttonMap.Reset();
}
}
diff --git a/xbmc/peripherals/addons/AddonButtonMap.cpp b/xbmc/peripherals/addons/AddonButtonMap.cpp
index e9764ba81c..0471d293eb 100644
--- a/xbmc/peripherals/addons/AddonButtonMap.cpp
+++ b/xbmc/peripherals/addons/AddonButtonMap.cpp
@@ -10,6 +10,7 @@
#include "PeripheralAddonTranslator.h"
#include "input/joysticks/JoystickUtils.h"
+#include "peripherals/Peripherals.h"
#include "peripherals/devices/Peripheral.h"
#include "utils/log.h"
@@ -24,8 +25,9 @@ using namespace PERIPHERALS;
CAddonButtonMap::CAddonButtonMap(CPeripheral* device,
const std::weak_ptr<CPeripheralAddon>& addon,
- const std::string& strControllerId)
- : m_device(device), m_addon(addon), m_strControllerId(strControllerId)
+ const std::string& strControllerId,
+ CPeripherals& manager)
+ : m_device(device), m_addon(addon), m_strControllerId(strControllerId), m_manager(manager)
{
auto peripheralAddon = m_addon.lock();
assert(peripheralAddon != nullptr);
@@ -59,6 +61,29 @@ bool CAddonButtonMap::Load(void)
bSuccess |= addon->GetIgnoredPrimitives(m_device, ignoredPrimitives);
}
+ if (features.empty())
+ {
+ // Check if we can initialize a buttonmap from the peripheral bus
+ PeripheralBusPtr peripheralBus = m_manager.GetBusByType(m_device->GetBusType());
+ if (peripheralBus)
+ {
+ CLog::Log(LOGDEBUG,
+ "Buttonmap not found for {}, attempting to initialize from peripheral bus",
+ m_device->Location());
+ if (peripheralBus->InitializeButtonMap(*m_device, *this))
+ {
+ bSuccess = true;
+
+ if (auto addon = m_addon.lock())
+ {
+ addon->GetAppearance(m_device, controllerAppearance);
+ addon->GetFeatures(m_device, m_strControllerId, features);
+ addon->GetIgnoredPrimitives(m_device, ignoredPrimitives);
+ }
+ }
+ }
+ }
+
// GetFeatures() was changed to always return false if no features were
// retrieved. Check here, just in case its contract is changed or violated in
// the future.
diff --git a/xbmc/peripherals/addons/AddonButtonMap.h b/xbmc/peripherals/addons/AddonButtonMap.h
index b4b6463fee..7bbc91947f 100644
--- a/xbmc/peripherals/addons/AddonButtonMap.h
+++ b/xbmc/peripherals/addons/AddonButtonMap.h
@@ -18,6 +18,7 @@
namespace PERIPHERALS
{
class CPeripheral;
+class CPeripherals;
/*!
* \ingroup peripherals
@@ -27,7 +28,8 @@ class CAddonButtonMap : public KODI::JOYSTICK::IButtonMap
public:
CAddonButtonMap(CPeripheral* device,
const std::weak_ptr<CPeripheralAddon>& addon,
- const std::string& strControllerId);
+ const std::string& strControllerId,
+ CPeripherals& manager);
~CAddonButtonMap(void) override;
@@ -133,6 +135,7 @@ private:
CPeripheral* const m_device;
const std::weak_ptr<CPeripheralAddon> m_addon;
const std::string m_strControllerId;
+ CPeripherals& m_manager;
// Button map state
std::string m_controllerAppearance;
diff --git a/xbmc/peripherals/addons/AddonButtonMapping.cpp b/xbmc/peripherals/addons/AddonButtonMapping.cpp
index 3be55df3f6..0da424ab27 100644
--- a/xbmc/peripherals/addons/AddonButtonMapping.cpp
+++ b/xbmc/peripherals/addons/AddonButtonMapping.cpp
@@ -33,7 +33,7 @@ CAddonButtonMapping::CAddonButtonMapping(CPeripherals& manager,
else
{
const std::string controllerId = mapper->ControllerID();
- m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, controllerId);
+ m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, controllerId, manager);
if (m_buttonMap->Load())
{
KEYMAP::IKeymap* keymap = peripheral->GetKeymap(controllerId);
diff --git a/xbmc/peripherals/addons/AddonInputHandling.cpp b/xbmc/peripherals/addons/AddonInputHandling.cpp
index 088d01a2f1..4643e84e3f 100644
--- a/xbmc/peripherals/addons/AddonInputHandling.cpp
+++ b/xbmc/peripherals/addons/AddonInputHandling.cpp
@@ -24,28 +24,38 @@ using namespace KODI;
using namespace JOYSTICK;
using namespace PERIPHERALS;
-CAddonInputHandling::CAddonInputHandling(CPeripheral* peripheral,
+CAddonInputHandling::CAddonInputHandling(CPeripherals& manager,
+ CPeripheral* peripheral,
std::shared_ptr<CPeripheralAddon> addon,
IInputHandler* handler,
IDriverReceiver* receiver)
- : m_peripheral(peripheral),
+ : m_manager(manager),
+ m_peripheral(peripheral),
m_addon(std::move(addon)),
m_joystickInputHandler(handler),
m_joystickDriverReceiver(receiver)
{
}
-CAddonInputHandling::CAddonInputHandling(CPeripheral* peripheral,
+CAddonInputHandling::CAddonInputHandling(CPeripherals& manager,
+ CPeripheral* peripheral,
std::shared_ptr<CPeripheralAddon> addon,
KEYBOARD::IKeyboardInputHandler* handler)
- : m_peripheral(peripheral), m_addon(std::move(addon)), m_keyboardInputHandler(handler)
+ : m_manager(manager),
+ m_peripheral(peripheral),
+ m_addon(std::move(addon)),
+ m_keyboardInputHandler(handler)
{
}
-CAddonInputHandling::CAddonInputHandling(CPeripheral* peripheral,
+CAddonInputHandling::CAddonInputHandling(CPeripherals& manager,
+ CPeripheral* peripheral,
std::shared_ptr<CPeripheralAddon> addon,
MOUSE::IMouseInputHandler* handler)
- : m_peripheral(peripheral), m_addon(std::move(addon)), m_mouseInputHandler(handler)
+ : m_manager(manager),
+ m_peripheral(peripheral),
+ m_addon(std::move(addon)),
+ m_mouseInputHandler(handler)
{
}
@@ -69,7 +79,7 @@ bool CAddonInputHandling::Load()
controllerId = m_mouseInputHandler->ControllerID();
if (!controllerId.empty())
- m_buttonMap = std::make_unique<CAddonButtonMap>(m_peripheral, m_addon, controllerId);
+ m_buttonMap = std::make_unique<CAddonButtonMap>(m_peripheral, m_addon, controllerId, m_manager);
if (m_buttonMap && m_buttonMap->Load())
{
diff --git a/xbmc/peripherals/addons/AddonInputHandling.h b/xbmc/peripherals/addons/AddonInputHandling.h
index e5c98e3748..19816a6ca5 100644
--- a/xbmc/peripherals/addons/AddonInputHandling.h
+++ b/xbmc/peripherals/addons/AddonInputHandling.h
@@ -38,6 +38,7 @@ class IMouseInputHandler;
namespace PERIPHERALS
{
class CPeripheral;
+class CPeripherals;
class CPeripheralAddon;
/*!
@@ -49,16 +50,19 @@ class CAddonInputHandling : public KODI::JOYSTICK::IDriverHandler,
public KODI::MOUSE::IMouseDriverHandler
{
public:
- CAddonInputHandling(CPeripheral* peripheral,
+ CAddonInputHandling(CPeripherals& manager,
+ CPeripheral* peripheral,
std::shared_ptr<CPeripheralAddon> addon,
KODI::JOYSTICK::IInputHandler* handler,
KODI::JOYSTICK::IDriverReceiver* receiver);
- CAddonInputHandling(CPeripheral* peripheral,
+ CAddonInputHandling(CPeripherals& manager,
+ CPeripheral* peripheral,
std::shared_ptr<CPeripheralAddon> addon,
KODI::KEYBOARD::IKeyboardInputHandler* handler);
- CAddonInputHandling(CPeripheral* peripheral,
+ CAddonInputHandling(CPeripherals& manager,
+ CPeripheral* peripheral,
std::shared_ptr<CPeripheralAddon> addon,
KODI::MOUSE::IMouseInputHandler* handler);
@@ -89,6 +93,7 @@ public:
private:
// Construction parameters
+ CPeripherals& m_manager;
CPeripheral* const m_peripheral;
const std::shared_ptr<CPeripheralAddon> m_addon;
KODI::JOYSTICK::IInputHandler* const m_joystickInputHandler{nullptr};
diff --git a/xbmc/peripherals/bus/PeripheralBus.h b/xbmc/peripherals/bus/PeripheralBus.h
index 424d012d9f..33594a0faa 100644
--- a/xbmc/peripherals/bus/PeripheralBus.h
+++ b/xbmc/peripherals/bus/PeripheralBus.h
@@ -17,6 +17,14 @@
class CFileItemList;
+namespace KODI
+{
+namespace JOYSTICK
+{
+class IButtonMap;
+} // namespace JOYSTICK
+} // namespace KODI
+
namespace PERIPHERALS
{
class CPeripheral;
@@ -60,6 +68,15 @@ public:
virtual bool InitializeProperties(CPeripheral& peripheral);
/*!
+ * \brief Initialize a joystick buttonmap, if possible
+ */
+ virtual bool InitializeButtonMap(const CPeripheral& peripheral,
+ KODI::JOYSTICK::IButtonMap& buttonMap) const
+ {
+ return false;
+ }
+
+ /*!
* @brief Get the instance of the peripheral at the given location.
* @param strLocation The location.
* @return The peripheral or NULL if it wasn't found.
diff --git a/xbmc/peripherals/devices/Peripheral.cpp b/xbmc/peripherals/devices/Peripheral.cpp
index ff9910c2ef..bb00d516a3 100644
--- a/xbmc/peripherals/devices/Peripheral.cpp
+++ b/xbmc/peripherals/devices/Peripheral.cpp
@@ -597,7 +597,7 @@ void CPeripheral::RegisterInputHandler(IInputHandler* handler, bool bPromiscuous
if (addon)
{
std::unique_ptr<CAddonInputHandling> addonInput = std::make_unique<CAddonInputHandling>(
- this, std::move(addon), handler, GetDriverReceiver());
+ m_manager, this, std::move(addon), handler, GetDriverReceiver());
if (addonInput->Load())
{
RegisterJoystickDriverHandler(addonInput.get(), bPromiscuous);
@@ -638,7 +638,7 @@ void CPeripheral::RegisterKeyboardHandler(KEYBOARD::IKeyboardInputHandler* handl
if (addon)
{
std::unique_ptr<CAddonInputHandling> addonInput =
- std::make_unique<CAddonInputHandling>(this, std::move(addon), handler);
+ std::make_unique<CAddonInputHandling>(m_manager, this, std::move(addon), handler);
if (addonInput->Load())
keyboardDriverHandler = std::move(addonInput);
}
@@ -689,7 +689,7 @@ void CPeripheral::RegisterMouseHandler(MOUSE::IMouseInputHandler* handler,
if (addon)
{
std::unique_ptr<CAddonInputHandling> addonInput =
- std::make_unique<CAddonInputHandling>(this, std::move(addon), handler);
+ std::make_unique<CAddonInputHandling>(m_manager, this, std::move(addon), handler);
if (addonInput->Load())
mouseDriverHandler = std::move(addonInput);
}
diff --git a/xbmc/peripherals/devices/PeripheralJoystick.cpp b/xbmc/peripherals/devices/PeripheralJoystick.cpp
index a6029561d6..c5285c245e 100644
--- a/xbmc/peripherals/devices/PeripheralJoystick.cpp
+++ b/xbmc/peripherals/devices/PeripheralJoystick.cpp
@@ -92,7 +92,8 @@ bool CPeripheralJoystick::InitialiseFeature(const PeripheralFeature feature)
if (bSuccess)
{
- m_buttonMap = std::make_unique<CAddonButtonMap>(this, addon, DEFAULT_CONTROLLER_ID);
+ m_buttonMap =
+ std::make_unique<CAddonButtonMap>(this, addon, DEFAULT_CONTROLLER_ID, m_manager);
if (m_buttonMap->Load())
{
InitializeDeadzoneFiltering(*m_buttonMap);
diff --git a/xbmc/platform/android/peripherals/AndroidJoystickState.cpp b/xbmc/platform/android/peripherals/AndroidJoystickState.cpp
index 1a8244fbc9..c3032b4352 100644
--- a/xbmc/platform/android/peripherals/AndroidJoystickState.cpp
+++ b/xbmc/platform/android/peripherals/AndroidJoystickState.cpp
@@ -9,6 +9,11 @@
#include "AndroidJoystickState.h"
#include "AndroidJoystickTranslator.h"
+#include "games/controllers/ControllerIDs.h"
+#include "games/controllers/DefaultController.h"
+#include "input/joysticks/DriverPrimitive.h"
+#include "input/joysticks/JoystickTypes.h"
+#include "input/joysticks/interfaces/IButtonMap.h"
#include "utils/StringUtils.h"
#include "utils/log.h"
@@ -19,8 +24,42 @@
#include <android/input.h>
#include <androidjni/View.h>
+using namespace KODI;
using namespace PERIPHERALS;
+namespace
+{
+// clang-format off
+static const std::vector<int> ButtonKeycodes{
+ // add the usual suspects
+ AKEYCODE_BUTTON_A,
+ AKEYCODE_BUTTON_B,
+ AKEYCODE_BUTTON_C,
+ AKEYCODE_BUTTON_X,
+ AKEYCODE_BUTTON_Y,
+ AKEYCODE_BUTTON_Z,
+ AKEYCODE_BACK,
+ AKEYCODE_MENU,
+ AKEYCODE_HOME,
+ AKEYCODE_BUTTON_SELECT,
+ AKEYCODE_BUTTON_MODE,
+ AKEYCODE_BUTTON_START,
+ AKEYCODE_BUTTON_L1,
+ AKEYCODE_BUTTON_R1,
+ AKEYCODE_BUTTON_L2,
+ AKEYCODE_BUTTON_R2,
+ AKEYCODE_BUTTON_THUMBL,
+ AKEYCODE_BUTTON_THUMBR,
+ AKEYCODE_DPAD_UP,
+ AKEYCODE_DPAD_RIGHT,
+ AKEYCODE_DPAD_DOWN,
+ AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_CENTER,
+ // only add additional buttons at the end of the list
+};
+// clang-format on
+} // namespace
+
static std::string PrintAxisIds(const std::vector<int>& axisIds)
{
if (axisIds.empty())
@@ -151,30 +190,9 @@ bool CAndroidJoystickState::Initialize(const CJNIViewInputDevice& inputDevice)
axisId, deviceName, m_deviceId);
}
- // add the usual suspects
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_A}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_B}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_C}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_X}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_Y}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_Z}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BACK}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_MENU}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_HOME}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_SELECT}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_MODE}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_START}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_L1}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_R1}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_L2}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_R2}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_THUMBL}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_BUTTON_THUMBR}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_DPAD_UP}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_DPAD_RIGHT}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_DPAD_DOWN}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_DPAD_LEFT}});
- m_buttons.emplace_back(JoystickAxis{{AKEYCODE_DPAD_CENTER}});
+ // map buttons
+ for (int buttonKeycode : ButtonKeycodes)
+ m_buttons.emplace_back(JoystickAxis{{buttonKeycode}});
// check if there are no buttons or axes at all
if (GetButtonCount() == 0 && GetAxisCount() == 0)
@@ -200,6 +218,67 @@ void CAndroidJoystickState::Deinitialize(void)
m_digitalEvents.clear();
}
+bool CAndroidJoystickState::InitializeButtonMap(JOYSTICK::IButtonMap& buttonMap) const
+{
+ // We only map the default controller
+ if (buttonMap.ControllerID() != DEFAULT_CONTROLLER_ID)
+ return false;
+
+ bool success = false;
+
+ // Map buttons
+ for (int buttonKeycode : ButtonKeycodes)
+ success |= MapButton(buttonMap, buttonKeycode);
+
+ // Map D-pad
+ success |= MapDpad(buttonMap, AMOTION_EVENT_AXIS_HAT_X, AMOTION_EVENT_AXIS_HAT_Y);
+
+ // Map triggers
+ // Note: This should come after buttons, because the PS4 controller uses
+ // both a digital button and an analog axis for the triggers, and we want
+ // the analog axis to override the button for full range of motion.
+ success |= MapTrigger(buttonMap, AMOTION_EVENT_AXIS_LTRIGGER,
+ GAME::CDefaultController::FEATURE_LEFT_TRIGGER);
+ success |= MapTrigger(buttonMap, AMOTION_EVENT_AXIS_RTRIGGER,
+ GAME::CDefaultController::FEATURE_RIGHT_TRIGGER);
+
+ // Map analog sticks
+ success |= MapAnalogStick(buttonMap, AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y,
+ GAME::CDefaultController::FEATURE_LEFT_STICK);
+ success |= MapAnalogStick(buttonMap, AMOTION_EVENT_AXIS_Z, AMOTION_EVENT_AXIS_RZ,
+ GAME::CDefaultController::FEATURE_RIGHT_STICK);
+
+ if (success)
+ {
+ // If the controller has both L2/R2 buttons and LTRIGGER/RTRIGGER axes, it's
+ // probably a PS controller
+ size_t indexL2 = 0;
+ size_t indexR2 = 0;
+ size_t indexLTrigger = 0;
+ size_t indexRTrigger = 0;
+ if (GetAxesIndex({AKEYCODE_BUTTON_L2}, m_buttons, indexL2) &&
+ GetAxesIndex({AKEYCODE_BUTTON_R2}, m_buttons, indexR2) &&
+ GetAxesIndex({AMOTION_EVENT_AXIS_LTRIGGER}, m_axes, indexLTrigger) &&
+ GetAxesIndex({AMOTION_EVENT_AXIS_RTRIGGER}, m_axes, indexRTrigger))
+ {
+ CLog::Log(LOGDEBUG, "Detected dual-input triggers, ignoring digital buttons");
+ std::vector<JOYSTICK::CDriverPrimitive> ignoredPrimitives{
+ {JOYSTICK::PRIMITIVE_TYPE::BUTTON, static_cast<unsigned int>(indexL2)},
+ {JOYSTICK::PRIMITIVE_TYPE::BUTTON, static_cast<unsigned int>(indexR2)},
+ };
+ buttonMap.SetIgnoredPrimitives(ignoredPrimitives);
+
+ CLog::Log(LOGDEBUG, "Setting appearance to {}", GAME::CONTROLLER_ID_PLAYSTATION);
+ buttonMap.SetAppearance(GAME::CONTROLLER_ID_PLAYSTATION);
+ }
+
+ // Save the buttonmap
+ buttonMap.SaveButtonMap();
+ }
+
+ return success;
+}
+
bool CAndroidJoystickState::ProcessEvent(const AInputEvent* event)
{
int32_t type = AInputEvent_getType(event);
@@ -334,6 +413,144 @@ bool CAndroidJoystickState::SetAxisValue(const std::vector<int>& axisIds,
return true;
}
+bool CAndroidJoystickState::MapButton(JOYSTICK::IButtonMap& buttonMap, int buttonKeycode) const
+{
+ size_t buttonIndex = 0;
+ std::string featureName;
+
+ if (!GetAxesIndex({buttonKeycode}, m_buttons, buttonIndex))
+ return false;
+
+ // Check if button is already mapped
+ JOYSTICK::CDriverPrimitive buttonPrimitive{JOYSTICK::PRIMITIVE_TYPE::BUTTON,
+ static_cast<unsigned int>(buttonIndex)};
+ if (buttonMap.GetFeature(buttonPrimitive, featureName))
+ return false;
+
+ // Translate the button
+ std::string controllerButton = CAndroidJoystickTranslator::TranslateJoystickButton(buttonKeycode);
+ if (controllerButton.empty())
+ return false;
+
+ // Map the button
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", controllerButton,
+ buttonPrimitive.ToString());
+ buttonMap.AddScalar(controllerButton, buttonPrimitive);
+
+ return true;
+}
+
+bool CAndroidJoystickState::MapTrigger(JOYSTICK::IButtonMap& buttonMap,
+ int axisId,
+ const std::string& triggerName) const
+{
+ size_t axisIndex = 0;
+ std::string featureName;
+
+ if (!GetAxesIndex({axisId}, m_axes, axisIndex))
+ return false;
+
+ const JOYSTICK::CDriverPrimitive semiaxis{static_cast<unsigned int>(axisIndex), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
+ if (buttonMap.GetFeature(semiaxis, featureName))
+ return false;
+
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", triggerName, semiaxis.ToString());
+ buttonMap.AddScalar(triggerName, semiaxis);
+
+ return true;
+}
+
+bool CAndroidJoystickState::MapDpad(JOYSTICK::IButtonMap& buttonMap,
+ int horizAxisId,
+ int vertAxisId) const
+{
+ bool success = false;
+
+ size_t axisIndex = 0;
+ std::string featureName;
+
+ // Map horizontal axis
+ if (GetAxesIndex({horizAxisId}, m_axes, axisIndex))
+ {
+ const JOYSTICK::CDriverPrimitive positiveSemiaxis{static_cast<unsigned int>(axisIndex), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
+ const JOYSTICK::CDriverPrimitive negativeSemiaxis{static_cast<unsigned int>(axisIndex), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
+ if (!buttonMap.GetFeature(positiveSemiaxis, featureName) &&
+ !buttonMap.GetFeature(negativeSemiaxis, featureName))
+ {
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_LEFT,
+ negativeSemiaxis.ToString());
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_RIGHT,
+ positiveSemiaxis.ToString());
+ buttonMap.AddScalar(GAME::CDefaultController::FEATURE_LEFT, negativeSemiaxis);
+ buttonMap.AddScalar(GAME::CDefaultController::FEATURE_RIGHT, positiveSemiaxis);
+ success |= true;
+ }
+ }
+
+ // Map vertical axis
+ if (GetAxesIndex({vertAxisId}, m_axes, axisIndex))
+ {
+ const JOYSTICK::CDriverPrimitive positiveSemiaxis{static_cast<unsigned int>(axisIndex), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
+ const JOYSTICK::CDriverPrimitive negativeSemiaxis{static_cast<unsigned int>(axisIndex), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
+ if (!buttonMap.GetFeature(positiveSemiaxis, featureName) &&
+ !buttonMap.GetFeature(negativeSemiaxis, featureName))
+ {
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_UP,
+ negativeSemiaxis.ToString());
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to {}", GAME::CDefaultController::FEATURE_DOWN,
+ positiveSemiaxis.ToString());
+ buttonMap.AddScalar(GAME::CDefaultController::FEATURE_DOWN, positiveSemiaxis);
+ buttonMap.AddScalar(GAME::CDefaultController::FEATURE_UP, negativeSemiaxis);
+ success |= true;
+ }
+ }
+
+ return success;
+}
+
+bool CAndroidJoystickState::MapAnalogStick(JOYSTICK::IButtonMap& buttonMap,
+ int horizAxisId,
+ int vertAxisId,
+ const std::string& analogStickName) const
+{
+ size_t axisIndex1 = 0;
+ size_t axisIndex2 = 0;
+ std::string featureName;
+
+ if (!GetAxesIndex({horizAxisId}, m_axes, axisIndex1) ||
+ !GetAxesIndex({vertAxisId}, m_axes, axisIndex2))
+ return false;
+
+ const JOYSTICK::CDriverPrimitive upSemiaxis{static_cast<unsigned int>(axisIndex2), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
+ const JOYSTICK::CDriverPrimitive downSemiaxis{static_cast<unsigned int>(axisIndex2), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
+ const JOYSTICK::CDriverPrimitive leftSemiaxis{static_cast<unsigned int>(axisIndex1), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::NEGATIVE, 1};
+ const JOYSTICK::CDriverPrimitive rightSemiaxis{static_cast<unsigned int>(axisIndex1), 0,
+ JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1};
+ if (buttonMap.GetFeature(upSemiaxis, featureName) ||
+ buttonMap.GetFeature(downSemiaxis, featureName) ||
+ buttonMap.GetFeature(leftSemiaxis, featureName) ||
+ buttonMap.GetFeature(rightSemiaxis, featureName))
+ return false;
+
+ CLog::Log(LOGDEBUG, "Automatically mapping {} to [{}, {}, {}, {}]", analogStickName,
+ upSemiaxis.ToString(), downSemiaxis.ToString(), leftSemiaxis.ToString(),
+ rightSemiaxis.ToString());
+ buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::UP, upSemiaxis);
+ buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::DOWN, downSemiaxis);
+ buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::LEFT, leftSemiaxis);
+ buttonMap.AddAnalogStick(analogStickName, JOYSTICK::ANALOG_STICK_DIRECTION::RIGHT, rightSemiaxis);
+
+ return true;
+}
+
float CAndroidJoystickState::Contain(float value, float min, float max)
{
if (value < min)
diff --git a/xbmc/platform/android/peripherals/AndroidJoystickState.h b/xbmc/platform/android/peripherals/AndroidJoystickState.h
index e146193541..550222d6f7 100644
--- a/xbmc/platform/android/peripherals/AndroidJoystickState.h
+++ b/xbmc/platform/android/peripherals/AndroidJoystickState.h
@@ -18,6 +18,14 @@
struct AInputEvent;
class CJNIViewInputDevice;
+namespace KODI
+{
+namespace JOYSTICK
+{
+class IButtonMap;
+} // namespace JOYSTICK
+} // namespace KODI
+
namespace PERIPHERALS
{
class CAndroidJoystickState
@@ -40,6 +48,18 @@ public:
bool Initialize(const CJNIViewInputDevice& inputDevice);
/*!
+ * \brief Initialize a joystick buttonmap, if possible
+ *
+ * Android has a large database of buttonmaps, which it uses to provide
+ * mapped button keycodes such as AKEYCODE_BUTTON_A. We can take advantage of
+ * this to initialize a default buttonmap based on these mappings.
+ *
+ * If Android can't map the buttons, it will use generic button keycodes such
+ * as AKEYCODE_BUTTON_1, in which case we can't initialize the buttonmap.
+ */
+ bool InitializeButtonMap(KODI::JOYSTICK::IButtonMap& buttonMap) const;
+
+ /*!
* \brief Deinitialize the joystick object
*
* GetEvents() will not be called after deinitialization.
@@ -63,6 +83,16 @@ private:
void GetButtonEvents(std::vector<kodi::addon::PeripheralEvent>& events);
void GetAxisEvents(std::vector<kodi::addon::PeripheralEvent>& events) const;
+ bool MapButton(KODI::JOYSTICK::IButtonMap& buttonMap, int buttonKeycode) const;
+ bool MapTrigger(KODI::JOYSTICK::IButtonMap& buttonMap,
+ int axisId,
+ const std::string& triggerName) const;
+ bool MapDpad(KODI::JOYSTICK::IButtonMap& buttonMap, int horizAxisId, int vertAxisId) const;
+ bool MapAnalogStick(KODI::JOYSTICK::IButtonMap& buttonMap,
+ int horizAxisId,
+ int vertAxisId,
+ const std::string& analogStickName) const;
+
static float Contain(float value, float min, float max);
static float Scale(float value, float max, float scaledMax);
static float Deadzone(float value, float deadzone);
diff --git a/xbmc/platform/android/peripherals/AndroidJoystickTranslator.cpp b/xbmc/platform/android/peripherals/AndroidJoystickTranslator.cpp
index 3717616413..f8f638cbde 100644
--- a/xbmc/platform/android/peripherals/AndroidJoystickTranslator.cpp
+++ b/xbmc/platform/android/peripherals/AndroidJoystickTranslator.cpp
@@ -8,9 +8,12 @@
#include "AndroidJoystickTranslator.h"
+#include "games/controllers/DefaultController.h"
+
#include <android/input.h>
#include <android/keycodes.h>
+using namespace KODI;
using namespace PERIPHERALS;
const char* CAndroidJoystickTranslator::TranslateAxis(int axisId)
@@ -726,3 +729,52 @@ const char* CAndroidJoystickTranslator::TranslateKeyCode(int keyCode)
return "unknown";
}
+
+const char* CAndroidJoystickTranslator::TranslateJoystickButton(int buttonKeycode)
+{
+ switch (buttonKeycode)
+ {
+ case AKEYCODE_BUTTON_A:
+ case AKEYCODE_DPAD_CENTER:
+ return GAME::CDefaultController::FEATURE_A;
+ case AKEYCODE_BUTTON_B:
+ return GAME::CDefaultController::FEATURE_B;
+ case AKEYCODE_BUTTON_X:
+ return GAME::CDefaultController::FEATURE_X;
+ case AKEYCODE_BUTTON_Y:
+ return GAME::CDefaultController::FEATURE_Y;
+ case AKEYCODE_BUTTON_START:
+ case AKEYCODE_MENU:
+ return GAME::CDefaultController::FEATURE_START;
+ case AKEYCODE_BUTTON_SELECT:
+ case AKEYCODE_BACK:
+ return GAME::CDefaultController::FEATURE_BACK;
+ case AKEYCODE_BUTTON_MODE:
+ case AKEYCODE_HOME:
+ return GAME::CDefaultController::FEATURE_GUIDE;
+ case AKEYCODE_DPAD_UP:
+ return GAME::CDefaultController::FEATURE_UP;
+ case AKEYCODE_DPAD_RIGHT:
+ return GAME::CDefaultController::FEATURE_RIGHT;
+ case AKEYCODE_DPAD_DOWN:
+ return GAME::CDefaultController::FEATURE_DOWN;
+ case AKEYCODE_DPAD_LEFT:
+ return GAME::CDefaultController::FEATURE_LEFT;
+ case AKEYCODE_BUTTON_L1:
+ return GAME::CDefaultController::FEATURE_LEFT_BUMPER;
+ case AKEYCODE_BUTTON_R1:
+ return GAME::CDefaultController::FEATURE_RIGHT_BUMPER;
+ case AKEYCODE_BUTTON_L2:
+ return GAME::CDefaultController::FEATURE_LEFT_TRIGGER;
+ case AKEYCODE_BUTTON_R2:
+ return GAME::CDefaultController::FEATURE_RIGHT_TRIGGER;
+ case AKEYCODE_BUTTON_THUMBL:
+ return GAME::CDefaultController::FEATURE_LEFT_THUMB;
+ case AKEYCODE_BUTTON_THUMBR:
+ return GAME::CDefaultController::FEATURE_RIGHT_THUMB;
+ default:
+ break;
+ }
+
+ return "";
+}
diff --git a/xbmc/platform/android/peripherals/AndroidJoystickTranslator.h b/xbmc/platform/android/peripherals/AndroidJoystickTranslator.h
index a5bb8e33d5..8681de0c12 100644
--- a/xbmc/platform/android/peripherals/AndroidJoystickTranslator.h
+++ b/xbmc/platform/android/peripherals/AndroidJoystickTranslator.h
@@ -30,5 +30,14 @@ public:
* \return The translated enum label, or "unknown" if unknown
*/
static const char* TranslateKeyCode(int keyCode);
+
+ /*!
+ * \brief Translate a button key code to a feature on the default controller
+ *
+ * \param buttonKeycode The key code given in <android/keycodes.h>
+ *
+ * \return The translated feature, or "" if unknown
+ */
+ static const char* TranslateJoystickButton(int buttonKeycode);
};
} // namespace PERIPHERALS
diff --git a/xbmc/platform/android/peripherals/PeripheralBusAndroid.cpp b/xbmc/platform/android/peripherals/PeripheralBusAndroid.cpp
index 4fef6fed69..f8282eeb55 100644
--- a/xbmc/platform/android/peripherals/PeripheralBusAndroid.cpp
+++ b/xbmc/platform/android/peripherals/PeripheralBusAndroid.cpp
@@ -16,6 +16,7 @@
#include "utils/log.h"
#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/peripherals/AndroidJoystickState.h"
#include <algorithm>
#include <mutex>
@@ -120,6 +121,50 @@ bool CPeripheralBusAndroid::InitializeProperties(CPeripheral& peripheral)
return true;
}
+bool CPeripheralBusAndroid::InitializeButtonMap(const CPeripheral& peripheral,
+ JOYSTICK::IButtonMap& buttonMap) const
+{
+ int deviceId;
+ if (!GetDeviceId(peripheral.Location(), deviceId))
+ {
+ CLog::Log(LOGWARNING,
+ "CPeripheralBusAndroid: failed to initialize buttonmap due to unknown device ID for "
+ "peripheral \"{}\"",
+ peripheral.Location());
+ return false;
+ }
+
+ // get the joystick state
+ auto it = m_joystickStates.find(deviceId);
+ if (it == m_joystickStates.end())
+ {
+ CLog::Log(LOGWARNING,
+ "CPeripheralBusAndroid: joystick with device ID {} not found for peripheral \"{}\"",
+ deviceId, peripheral.Location());
+ return false;
+ }
+
+ const CAndroidJoystickState& joystick = it->second;
+ if (joystick.GetButtonCount() == 0 && joystick.GetAxisCount() == 0)
+ {
+ CLog::Log(LOGDEBUG,
+ "CPeripheralBusAndroid: joystick has no buttons or axes for peripheral \"{}\"",
+ peripheral.Location());
+ return false;
+ }
+
+ if (!joystick.InitializeButtonMap(buttonMap))
+ {
+ CLog::Log(
+ LOGDEBUG,
+ "CPeripheralBusAndroid: failed to initialize joystick buttonmap for peripheral \"{}\"",
+ peripheral.Location());
+ return false;
+ }
+
+ return true;
+}
+
void CPeripheralBusAndroid::Initialise(void)
{
CPeripheralBus::Initialise();
diff --git a/xbmc/platform/android/peripherals/PeripheralBusAndroid.h b/xbmc/platform/android/peripherals/PeripheralBusAndroid.h
index c3c42aa730..5ee9480970 100644
--- a/xbmc/platform/android/peripherals/PeripheralBusAndroid.h
+++ b/xbmc/platform/android/peripherals/PeripheralBusAndroid.h
@@ -35,6 +35,8 @@ public:
// specialisation of CPeripheralBus
bool InitializeProperties(CPeripheral& peripheral) override;
+ bool InitializeButtonMap(const CPeripheral& peripheral,
+ KODI::JOYSTICK::IButtonMap& buttonMap) const override;
void Initialise(void) override;
void ProcessEvents() override;