mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 22:35:39 +00:00
Android: Add controller interface class
This commit is contained in:
parent
cbcb8b4b89
commit
6bf37f51be
|
@ -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
|
||||
|
|
194
android/app/src/cpp/android_controller_interface.cpp
Normal file
194
android/app/src/cpp/android_controller_interface.cpp
Normal 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;
|
||||
}
|
67
android/app/src/cpp/android_controller_interface.h
Normal file
67
android/app/src/cpp/android_controller_interface.h
Normal 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;
|
||||
};
|
|
@ -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)
|
||||
|
|
|
@ -23,6 +23,9 @@ public:
|
|||
#ifdef WIN32
|
||||
XInput,
|
||||
DInput,
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
Android,
|
||||
#endif
|
||||
Count
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue