Android: Add controller interface class

This commit is contained in:
Connor McLaughlin 2020-12-27 17:50:29 +10:00
parent cbcb8b4b89
commit 6bf37f51be
5 changed files with 270 additions and 0 deletions

View file

@ -1,4 +1,6 @@
set(SRCS
android_controller_interface.cpp
android_controller_interface.h
android_host_interface.cpp
android_host_interface.h
android_progress_callback.cpp

View file

@ -0,0 +1,194 @@
#include "android_controller_interface.h"
#include "common/assert.h"
#include "common/file_system.h"
#include "common/log.h"
#include "core/controller.h"
#include "core/host_interface.h"
#include "core/system.h"
#include <cmath>
Log_SetChannel(AndroidControllerInterface);
AndroidControllerInterface::AndroidControllerInterface() = default;
AndroidControllerInterface::~AndroidControllerInterface() = default;
ControllerInterface::Backend AndroidControllerInterface::GetBackend() const
{
return ControllerInterface::Backend::Android;
}
bool AndroidControllerInterface::Initialize(CommonHostInterface* host_interface)
{
if (!ControllerInterface::Initialize(host_interface))
return false;
return true;
}
void AndroidControllerInterface::Shutdown()
{
ControllerInterface::Shutdown();
}
void AndroidControllerInterface::PollEvents() {}
void AndroidControllerInterface::ClearBindings()
{
for (ControllerData& cd : m_controllers)
{
cd.axis_mapping.fill({});
cd.button_mapping.fill({});
cd.axis_button_mapping.fill({});
cd.button_axis_mapping.fill({});
}
}
bool AndroidControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisSide axis_side,
AxisCallback callback)
{
if (static_cast<u32>(controller_index) >= m_controllers.size())
return false;
if (axis_number < 0 || axis_number >= NUM_AXISES)
return false;
m_controllers[controller_index].axis_mapping[axis_number][axis_side] = std::move(callback);
return true;
}
bool AndroidControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback)
{
if (static_cast<u32>(controller_index) >= m_controllers.size())
return false;
if (button_number < 0 || button_number >= NUM_BUTTONS)
return false;
m_controllers[controller_index].button_mapping[button_number] = std::move(callback);
return true;
}
bool AndroidControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
ButtonCallback callback)
{
if (static_cast<u32>(controller_index) >= m_controllers.size())
return false;
if (axis_number < 0 || axis_number >= NUM_AXISES)
return false;
m_controllers[controller_index].axis_button_mapping[axis_number][BoolToUInt8(direction)] = std::move(callback);
return true;
}
bool AndroidControllerInterface::BindControllerHatToButton(int controller_index, int hat_number,
std::string_view hat_position, ButtonCallback callback)
{
// Hats don't exist in XInput
return false;
}
bool AndroidControllerInterface::BindControllerButtonToAxis(int controller_index, int button_number,
AxisCallback callback)
{
if (static_cast<u32>(controller_index) >= m_controllers.size())
return false;
if (button_number < 0 || button_number >= NUM_BUTTONS)
return false;
m_controllers[controller_index].button_axis_mapping[button_number] = std::move(callback);
return true;
}
bool AndroidControllerInterface::HandleAxisEvent(u32 index, u32 axis, float value)
{
Log_DevPrintf("controller %u axis %u %f", index, static_cast<u32>(axis), value);
DebugAssert(index < NUM_CONTROLLERS);
if (DoEventHook(Hook::Type::Axis, index, static_cast<u32>(axis), value))
return true;
const AxisCallback& cb = m_controllers[index].axis_mapping[static_cast<u32>(axis)][AxisSide::Full];
if (cb)
{
// Extend triggers from a 0 - 1 range to a -1 - 1 range for consistency with other inputs
if (axis == 4 && axis == 5)
{
cb((value * 2.0f) - 1.0f);
}
else
{
cb(value);
}
return true;
}
// set the other direction to false so large movements don't leave the opposite on
const bool outside_deadzone = (std::abs(value) >= m_controllers[index].deadzone);
const bool positive = (value >= 0.0f);
const ButtonCallback& other_button_cb =
m_controllers[index].axis_button_mapping[static_cast<u32>(axis)][BoolToUInt8(!positive)];
const ButtonCallback& button_cb =
m_controllers[index].axis_button_mapping[static_cast<u32>(axis)][BoolToUInt8(positive)];
if (button_cb)
{
button_cb(outside_deadzone);
if (other_button_cb)
other_button_cb(false);
return true;
}
else if (other_button_cb)
{
other_button_cb(false);
return true;
}
else
{
return false;
}
}
bool AndroidControllerInterface::HandleButtonEvent(u32 index, u32 button, bool pressed)
{
Log_DevPrintf("controller %u button %u %s", index, button, pressed ? "pressed" : "released");
DebugAssert(index < NUM_CONTROLLERS);
if (DoEventHook(Hook::Type::Button, index, button, pressed ? 1.0f : 0.0f))
return true;
const ButtonCallback& cb = m_controllers[index].button_mapping[button];
if (cb)
{
cb(pressed);
return true;
}
const AxisCallback& axis_cb = m_controllers[index].button_axis_mapping[button];
if (axis_cb)
{
axis_cb(pressed ? 1.0f : -1.0f);
}
return true;
}
u32 AndroidControllerInterface::GetControllerRumbleMotorCount(int controller_index)
{
return 0;
}
void AndroidControllerInterface::SetControllerRumbleStrength(int controller_index, const float* strengths,
u32 num_motors)
{
}
bool AndroidControllerInterface::SetControllerDeadzone(int controller_index, float size /* = 0.25f */)
{
if (static_cast<u32>(controller_index) >= NUM_CONTROLLERS)
return false;
m_controllers[static_cast<u32>(controller_index)].deadzone = std::clamp(std::abs(size), 0.01f, 0.99f);
Log_InfoPrintf("Controller %d deadzone size set to %f", controller_index,
m_controllers[static_cast<u32>(controller_index)].deadzone);
return true;
}

View file

@ -0,0 +1,67 @@
#pragma once
#include "frontend-common/controller_interface.h"
#include "core/types.h"
#include <array>
#include <functional>
#include <mutex>
#include <vector>
class AndroidControllerInterface final : public ControllerInterface
{
public:
AndroidControllerInterface();
~AndroidControllerInterface() override;
Backend GetBackend() const override;
bool Initialize(CommonHostInterface* host_interface) override;
void Shutdown() override;
// Removes all bindings. Call before setting new bindings.
void ClearBindings() override;
// Binding to events. If a binding for this axis/button already exists, returns false.
bool BindControllerAxis(int controller_index, int axis_number, AxisSide axis_side, AxisCallback callback) override;
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
ButtonCallback callback) override;
bool BindControllerHatToButton(int controller_index, int hat_number, std::string_view hat_position,
ButtonCallback callback) override;
bool BindControllerButtonToAxis(int controller_index, int button_number, AxisCallback callback) override;
// Changing rumble strength.
u32 GetControllerRumbleMotorCount(int controller_index) override;
void SetControllerRumbleStrength(int controller_index, const float* strengths, u32 num_motors) override;
// Set deadzone that will be applied on axis-to-button mappings
bool SetControllerDeadzone(int controller_index, float size = 0.25f) override;
void PollEvents() override;
bool HandleAxisEvent(u32 index, u32 axis, float value);
bool HandleButtonEvent(u32 index, u32 button, bool pressed);
private:
enum : u32
{
NUM_CONTROLLERS = 1,
NUM_AXISES = 10,
NUM_BUTTONS = 18
};
struct ControllerData
{
float deadzone = 0.25f;
std::array<std::array<AxisCallback, 3>, NUM_AXISES> axis_mapping;
std::array<ButtonCallback, NUM_BUTTONS> button_mapping;
std::array<std::array<ButtonCallback, 2>, NUM_AXISES> axis_button_mapping;
std::array<AxisCallback, NUM_BUTTONS> button_axis_mapping;
};
using ControllerDataArray = std::array<ControllerData, NUM_CONTROLLERS>;
ControllerDataArray m_controllers;
std::mutex m_event_intercept_mutex;
Hook::Callback m_event_intercept_callback;
};

View file

@ -91,6 +91,10 @@ static constexpr std::array<const char*, static_cast<u32>(ControllerInterface::B
TRANSLATABLE("ControllerInterface", "XInput"),
TRANSLATABLE("ControllerInterface", "DInput"),
#endif
#ifdef ANDROID
// Deliberately not translated as it's not exposed to users.
"Android",
#endif
}};
std::optional<ControllerInterface::Backend> ControllerInterface::ParseBackendName(const char* name)

View file

@ -23,6 +23,9 @@ public:
#ifdef WIN32
XInput,
DInput,
#endif
#ifdef ANDROID
Android,
#endif
Count
};