mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
input: add android input APIs
This commit is contained in:
parent
8b1f104a57
commit
43d3766468
@ -88,6 +88,21 @@ if (SUPPORTS_WIIMOTE)
|
||||
endif ()
|
||||
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(CemuInput PRIVATE
|
||||
api/Android/AndroidController.cpp
|
||||
api/Android/AndroidController.h
|
||||
api/Android/AndroidControllerProvider.cpp
|
||||
api/Android/AndroidControllerProvider.h
|
||||
api/Android/ControllerManager.cpp
|
||||
api/Android/ControllerManager.h
|
||||
api/Device/DeviceController.cpp
|
||||
api/Device/DeviceController.h
|
||||
api/Device/DeviceControllerProvider.cpp
|
||||
api/Device/DeviceControllerProvider.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SDL)
|
||||
target_sources(CemuInput PRIVATE
|
||||
api/SDL/SDLController.cpp
|
||||
|
||||
@ -22,6 +22,11 @@
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#endif
|
||||
|
||||
#if BOOST_PLAT_ANDROID
|
||||
#include "input/api/Android/AndroidController.h"
|
||||
#include "input/api/Device/DeviceController.h"
|
||||
#endif
|
||||
|
||||
ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::string_view uuid,
|
||||
std::string_view display_name)
|
||||
{
|
||||
@ -104,6 +109,12 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin
|
||||
const auto index = ConvertString<uint32>(uuid);
|
||||
return std::make_shared<NativeWiimoteController>(index);
|
||||
}
|
||||
#endif
|
||||
#if BOOST_PLAT_ANDROID
|
||||
case InputAPI::Android:
|
||||
return std::make_shared<AndroidController>(uuid, display_name);
|
||||
case InputAPI::Device:
|
||||
return std::make_shared<DeviceController>();
|
||||
#endif
|
||||
default:
|
||||
throw std::invalid_argument(fmt::format("unhandled controller api: {}", api));
|
||||
@ -175,6 +186,12 @@ ControllerProviderPtr ControllerFactory::CreateControllerProvider(InputAPI::Type
|
||||
#if HAS_WIIMOTE
|
||||
case InputAPI::Wiimote:
|
||||
return std::make_shared<WiimoteControllerProvider>();
|
||||
#endif
|
||||
#if BOOST_PLAT_ANDROID
|
||||
case InputAPI::Android:
|
||||
return std::make_shared<AndroidControllerProvider>();
|
||||
case InputAPI::Device:
|
||||
return std::make_shared<DeviceControllerProvider>();
|
||||
#endif
|
||||
default:
|
||||
cemu_assert_debug(false);
|
||||
|
||||
@ -46,6 +46,10 @@ InputManager::InputManager()
|
||||
#if HAS_WIIMOTE
|
||||
create_provider<WiimoteControllerProvider>();
|
||||
#endif
|
||||
#if BOOST_PLAT_ANDROID
|
||||
create_provider<AndroidControllerProvider>();
|
||||
create_provider<DeviceControllerProvider>();
|
||||
#endif
|
||||
|
||||
m_update_thread_shutdown.store(false);
|
||||
m_update_thread = std::thread(&InputManager::update_thread, this);
|
||||
|
||||
@ -17,6 +17,11 @@
|
||||
#include "input/api/DSU/DSUControllerProvider.h"
|
||||
#include "input/api/GameCube/GameCubeControllerProvider.h"
|
||||
|
||||
#if BOOST_PLAT_ANDROID
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
#include "input/api/Device/DeviceControllerProvider.h"
|
||||
#endif
|
||||
|
||||
#include "input/emulated/VPADController.h"
|
||||
#include "input/emulated/WPADController.h"
|
||||
|
||||
|
||||
112
src/input/api/Android/AndroidController.cpp
Normal file
112
src/input/api/Android/AndroidController.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "input/api/Android/AndroidController.h"
|
||||
#include "ControllerManager.h"
|
||||
|
||||
#include <android/keycodes.h>
|
||||
|
||||
AndroidController::AndroidController(std::string_view device_descriptor, std::string_view device_name)
|
||||
: base_type(device_descriptor, device_name)
|
||||
{
|
||||
m_state = ControllerManager::instance().get_state(m_uuid);
|
||||
}
|
||||
|
||||
ControllerState AndroidController::raw_state()
|
||||
{
|
||||
std::shared_lock lock{m_state->mutex};
|
||||
return m_state->inputs;
|
||||
}
|
||||
|
||||
bool AndroidController::has_rumble()
|
||||
{
|
||||
std::shared_lock lock{m_state->mutex};
|
||||
return m_state->hasRumble;
|
||||
}
|
||||
|
||||
void AndroidController::start_rumble()
|
||||
{
|
||||
if (is_connected() && !has_rumble())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_settings.rumble <= 0 || m_settings.rumble > 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ControllerManager::instance().vibrate_controller(
|
||||
m_uuid,
|
||||
5000,
|
||||
static_cast<int>(255.0f * m_settings.rumble));
|
||||
}
|
||||
|
||||
void AndroidController::stop_rumble()
|
||||
{
|
||||
if (is_connected() && !has_rumble())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_settings.rumble <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ControllerManager::instance().cancel_controller_vibration(m_uuid);
|
||||
}
|
||||
|
||||
MotionSample AndroidController::get_motion_sample()
|
||||
{
|
||||
std::shared_lock lock{m_state->mutex};
|
||||
return m_state->motionHandler.getMotionSample();
|
||||
}
|
||||
|
||||
bool AndroidController::has_motion()
|
||||
{
|
||||
std::shared_lock lock{m_state->mutex};
|
||||
return m_state->hasMotion;
|
||||
}
|
||||
|
||||
bool AndroidController::is_connected()
|
||||
{
|
||||
std::shared_lock lock{m_state->mutex};
|
||||
return m_state->isConnected;
|
||||
}
|
||||
|
||||
std::string AndroidController::get_button_name(uint64 button) const
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case AKEYCODE_BUTTON_A: return "Button A";
|
||||
case AKEYCODE_BUTTON_B: return "Button B";
|
||||
case AKEYCODE_BUTTON_C: return "Button C";
|
||||
case AKEYCODE_BUTTON_X: return "Button X";
|
||||
case AKEYCODE_BUTTON_Y: return "Button Y";
|
||||
case AKEYCODE_BUTTON_Z: return "Button Z";
|
||||
case AKEYCODE_BUTTON_L1: return "Button L1";
|
||||
case AKEYCODE_BUTTON_R1: return "Button R1";
|
||||
case AKEYCODE_BUTTON_L2: return "Button L2";
|
||||
case AKEYCODE_BUTTON_R2: return "Button R2";
|
||||
case AKEYCODE_BUTTON_THUMBL: return "Button ThumbL";
|
||||
case AKEYCODE_BUTTON_THUMBR: return "Button ThumbR";
|
||||
case AKEYCODE_BUTTON_START: return "Button Start";
|
||||
case AKEYCODE_BUTTON_SELECT: return "Button Select";
|
||||
case AKEYCODE_BUTTON_MODE: return "Button Mode";
|
||||
case AKEYCODE_BUTTON_1: return "Button 1";
|
||||
case AKEYCODE_BUTTON_2: return "Button 2";
|
||||
case AKEYCODE_BUTTON_3: return "Button 3";
|
||||
case AKEYCODE_BUTTON_4: return "Button 4";
|
||||
case AKEYCODE_BUTTON_5: return "Button 5";
|
||||
case AKEYCODE_BUTTON_6: return "Button 6";
|
||||
case AKEYCODE_BUTTON_7: return "Button 7";
|
||||
case AKEYCODE_BUTTON_8: return "Button 8";
|
||||
case AKEYCODE_BUTTON_9: return "Button 9";
|
||||
case AKEYCODE_BUTTON_10: return "Button 10";
|
||||
case AKEYCODE_BUTTON_11: return "Button 11";
|
||||
case AKEYCODE_BUTTON_12: return "Button 12";
|
||||
case AKEYCODE_BUTTON_13: return "Button 13";
|
||||
case AKEYCODE_BUTTON_14: return "Button 14";
|
||||
case AKEYCODE_BUTTON_15: return "Button 15";
|
||||
case AKEYCODE_BUTTON_16: return "Button 16";
|
||||
default: return base_type::get_button_name(button);
|
||||
}
|
||||
}
|
||||
41
src/input/api/Android/AndroidController.h
Normal file
41
src/input/api/Android/AndroidController.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
#include "input/api/Controller.h"
|
||||
#include "input/motion/MotionSample.h"
|
||||
#include "ControllerManager.h"
|
||||
|
||||
class AndroidController : public Controller<AndroidControllerProvider>
|
||||
{
|
||||
public:
|
||||
AndroidController(std::string_view device_descriptor, std::string_view device_name);
|
||||
|
||||
std::string_view api_name() const override
|
||||
{
|
||||
static_assert(to_string(InputAPI::Android) == "Android");
|
||||
return to_string(InputAPI::Android);
|
||||
}
|
||||
|
||||
InputAPI::Type api() const override
|
||||
{
|
||||
return InputAPI::Android;
|
||||
}
|
||||
|
||||
bool is_connected() override;
|
||||
|
||||
bool has_motion() override;
|
||||
|
||||
MotionSample get_motion_sample() override;
|
||||
|
||||
bool has_rumble() override;
|
||||
|
||||
void start_rumble() override;
|
||||
void stop_rumble() override;
|
||||
|
||||
std::string get_button_name(uint64 button) const override;
|
||||
|
||||
ControllerState raw_state() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ControllerManager::ControllerRuntimeState> m_state;
|
||||
};
|
||||
18
src/input/api/Android/AndroidControllerProvider.cpp
Normal file
18
src/input/api/Android/AndroidControllerProvider.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
|
||||
#include "ControllerManager.h"
|
||||
#include "AndroidController.h"
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> AndroidControllerProvider::get_controllers()
|
||||
{
|
||||
std::vector<std::shared_ptr<ControllerBase>> result;
|
||||
auto controllers = ControllerManager::instance().get_controllers();
|
||||
result.reserve(controllers.size());
|
||||
|
||||
for (const auto& controller : controllers)
|
||||
{
|
||||
result.push_back(std::make_shared<AndroidController>(controller.descriptor, controller.name));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
16
src/input/api/Android/AndroidControllerProvider.h
Normal file
16
src/input/api/Android/AndroidControllerProvider.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "input/api/ControllerProvider.h"
|
||||
|
||||
class AndroidControllerProvider : public ControllerProviderBase
|
||||
{
|
||||
public:
|
||||
inline static InputAPI::Type kAPIType = InputAPI::Android;
|
||||
|
||||
InputAPI::Type api() const override
|
||||
{
|
||||
return kAPIType;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
|
||||
};
|
||||
192
src/input/api/Android/ControllerManager.cpp
Normal file
192
src/input/api/Android/ControllerManager.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
#include "ControllerManager.h"
|
||||
|
||||
#include "api/Controller.h"
|
||||
|
||||
ControllerManager& ControllerManager::instance()
|
||||
{
|
||||
static ControllerManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ControllerManager::vibrate_controller(std::string_view deviceDescriptor, sint64 milliseconds, sint32 amplitude)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (!m_controllerCallbacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_controllerCallbacks->vibrate_controller(deviceDescriptor, milliseconds, amplitude);
|
||||
}
|
||||
|
||||
void ControllerManager::cancel_controller_vibration(std::string_view deviceDescriptor)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (!m_controllerCallbacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_controllerCallbacks->cancel_controller_vibration(deviceDescriptor);
|
||||
}
|
||||
|
||||
void ControllerManager::set_controller_callbacks(std::shared_ptr<ControllerCallbacks> callbacks)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_controllerCallbacks = std::move(callbacks);
|
||||
}
|
||||
|
||||
void ControllerManager::set_controllers(const std::vector<ControllerInfo>& controllers)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& [descriptor, controller] : m_states)
|
||||
{
|
||||
std::scoped_lock stateLock{controller->mutex};
|
||||
controller->isConnected = false;
|
||||
}
|
||||
|
||||
for (const auto& ctrl : controllers)
|
||||
{
|
||||
auto it = m_states.find(ctrl.descriptor);
|
||||
|
||||
if (it != m_states.end())
|
||||
{
|
||||
auto statePtr = it->second;
|
||||
auto& state = *statePtr;
|
||||
|
||||
std::scoped_lock stateLock{state.mutex};
|
||||
|
||||
state.name = ctrl.name;
|
||||
state.hasRumble = ctrl.hasRumble;
|
||||
state.hasMotion = ctrl.hasMotion;
|
||||
state.isConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_controllers = controllers;
|
||||
}
|
||||
|
||||
std::vector<ControllerManager::ControllerInfo> ControllerManager::get_controllers()
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_controllers;
|
||||
}
|
||||
|
||||
std::shared_ptr<ControllerManager::ControllerRuntimeState> ControllerManager::get_state(const std::string& deviceDescriptor)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto it = m_states.find(deviceDescriptor);
|
||||
|
||||
if (it != m_states.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto state = std::make_shared<ControllerRuntimeState>();
|
||||
|
||||
m_states[deviceDescriptor] = state;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void ControllerManager::process_key_event(const std::string& deviceDescriptor, int nativeKeyCode, bool isPressed)
|
||||
{
|
||||
update_state(deviceDescriptor, [&](auto& state) {
|
||||
state.inputs.buttons.SetButtonState(nativeKeyCode, isPressed);
|
||||
});
|
||||
}
|
||||
|
||||
void ControllerManager::process_axis_event(const std::string& deviceDescriptor, int nativeAxisCode, float value)
|
||||
{
|
||||
update_state(deviceDescriptor, [&](auto& state) {
|
||||
auto& controllerState = state.inputs;
|
||||
|
||||
switch (nativeAxisCode)
|
||||
{
|
||||
case AMOTION_EVENT_AXIS_X:
|
||||
controllerState.axis.x = value;
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_Y:
|
||||
controllerState.axis.y = value;
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_RX:
|
||||
case AMOTION_EVENT_AXIS_Z:
|
||||
controllerState.rotation.x = value;
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_RY:
|
||||
case AMOTION_EVENT_AXIS_RZ:
|
||||
controllerState.rotation.y = value;
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_LTRIGGER:
|
||||
controllerState.trigger.x = value;
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_RTRIGGER:
|
||||
controllerState.trigger.y = value;
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_HAT_X:
|
||||
if (value == 0.0f)
|
||||
{
|
||||
controllerState.buttons.SetButtonState(kButtonRight, false);
|
||||
controllerState.buttons.SetButtonState(kButtonLeft, false);
|
||||
return;
|
||||
}
|
||||
else if (value > 0.0f)
|
||||
controllerState.buttons.SetButtonState(kButtonRight, true);
|
||||
else
|
||||
controllerState.buttons.SetButtonState(kButtonLeft, true);
|
||||
break;
|
||||
case AMOTION_EVENT_AXIS_HAT_Y:
|
||||
if (value == 0.0f)
|
||||
{
|
||||
controllerState.buttons.SetButtonState(kButtonUp, false);
|
||||
controllerState.buttons.SetButtonState(kButtonDown, false);
|
||||
return;
|
||||
}
|
||||
else if (value > 0.0f)
|
||||
controllerState.buttons.SetButtonState(kButtonDown, true);
|
||||
else
|
||||
controllerState.buttons.SetButtonState(kButtonUp, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ControllerManager::process_motion(
|
||||
const std::string& deviceDescriptor,
|
||||
std::chrono::steady_clock::time_point timestamp,
|
||||
float gyroX, float gyroY, float gyroZ,
|
||||
float accelX, float accelY, float accelZ)
|
||||
{
|
||||
update_state(deviceDescriptor, [&](auto& state) {
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const auto dif = timestamp - state.lastMotionTimestamp;
|
||||
state.lastMotionTimestamp = timestamp;
|
||||
|
||||
if (dif <= 0ms)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dif >= 10s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr auto maxDuration = 1s;
|
||||
auto clamped = std::min<std::common_type_t<decltype(dif), decltype(maxDuration)>>(dif, maxDuration);
|
||||
|
||||
float deltaSeconds = std::chrono::duration_cast<std::chrono::duration<float>>(dif).count();
|
||||
|
||||
state.motionHandler.processMotionSample(
|
||||
deltaSeconds,
|
||||
gyroX, gyroY, gyroZ,
|
||||
accelX, accelY, accelZ);
|
||||
});
|
||||
}
|
||||
97
src/input/api/Android/ControllerManager.h
Normal file
97
src/input/api/Android/ControllerManager.h
Normal file
@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include "input/api/ControllerState.h"
|
||||
#include "input/motion/MotionHandler.h"
|
||||
|
||||
#include <android/input.h>
|
||||
|
||||
class ControllerManager
|
||||
{
|
||||
public:
|
||||
struct ControllerRuntimeState
|
||||
{
|
||||
bool isConnected{};
|
||||
|
||||
std::string name{};
|
||||
|
||||
ControllerState inputs{};
|
||||
|
||||
bool hasRumble{};
|
||||
bool hasMotion{};
|
||||
|
||||
std::chrono::steady_clock::time_point lastMotionTimestamp{};
|
||||
WiiUMotionHandler motionHandler{};
|
||||
|
||||
std::shared_mutex mutex{};
|
||||
};
|
||||
|
||||
struct ControllerInfo
|
||||
{
|
||||
sint32 id{};
|
||||
std::string descriptor{};
|
||||
std::string name{};
|
||||
bool hasRumble{};
|
||||
bool hasMotion{};
|
||||
};
|
||||
|
||||
class ControllerCallbacks
|
||||
{
|
||||
public:
|
||||
virtual void vibrate_controller(std::string_view deviceDescriptor, sint64 milliseconds, sint32 amplitude) = 0;
|
||||
virtual void cancel_controller_vibration(std::string_view deviceDescriptor) = 0;
|
||||
};
|
||||
|
||||
static ControllerManager& instance();
|
||||
|
||||
void vibrate_controller(std::string_view deviceDescriptor, sint64 milliseconds, sint32 amplitude);
|
||||
|
||||
void cancel_controller_vibration(std::string_view deviceDescriptor);
|
||||
|
||||
void set_controller_callbacks(std::shared_ptr<ControllerCallbacks> callbacks);
|
||||
|
||||
void set_controllers(const std::vector<ControllerInfo>& controllers);
|
||||
|
||||
std::vector<ControllerInfo> get_controllers();
|
||||
|
||||
std::shared_ptr<ControllerRuntimeState> get_state(const std::string& deviceDescriptor);
|
||||
|
||||
void process_key_event(const std::string& deviceDescriptor, int nativeKeyCode, bool isPressed);
|
||||
|
||||
void process_axis_event(const std::string& deviceDescriptor, int nativeAxisCode, float value);
|
||||
|
||||
void process_motion(
|
||||
const std::string& deviceDescriptor,
|
||||
std::chrono::steady_clock::time_point timestamp,
|
||||
float gyroX,
|
||||
float gyroY,
|
||||
float gyroZ,
|
||||
float accelX,
|
||||
float accelY,
|
||||
float accelZ);
|
||||
|
||||
private:
|
||||
ControllerManager() = default;
|
||||
|
||||
inline void update_state(const std::string& deviceDescriptor, std::invocable<ControllerRuntimeState&> auto f)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto it = m_states.find(deviceDescriptor);
|
||||
|
||||
if (it == m_states.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto statePtr = it->second;
|
||||
auto& state = *statePtr;
|
||||
std::scoped_lock stateLock{state.mutex};
|
||||
|
||||
f(state);
|
||||
}
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::shared_ptr<ControllerCallbacks> m_controllerCallbacks;
|
||||
std::vector<ControllerInfo> m_controllers;
|
||||
std::unordered_map<std::string, std::shared_ptr<ControllerRuntimeState>> m_states;
|
||||
};
|
||||
95
src/input/api/Device/DeviceController.cpp
Normal file
95
src/input/api/Device/DeviceController.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include "DeviceController.h"
|
||||
|
||||
DeviceController::DeviceController() : base_type("device", "device")
|
||||
{
|
||||
}
|
||||
|
||||
void DeviceController::set_callbacks(std::shared_ptr<ControllerCallbacks> callbacks)
|
||||
{
|
||||
std::scoped_lock lock(s_mutex);
|
||||
s_callbacks = std::move(callbacks);
|
||||
}
|
||||
|
||||
bool DeviceController::is_connected()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceController::has_rumble()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceController::has_motion()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
MotionSample DeviceController::get_motion_sample()
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
return s_motion_handler.getMotionSample();
|
||||
}
|
||||
|
||||
ControllerState DeviceController::raw_state()
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
return s_raw_state;
|
||||
}
|
||||
|
||||
void DeviceController::start_rumble()
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
|
||||
if (m_settings.rumble <= 0 || m_settings.rumble > 1 || !s_callbacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_callbacks->start_rumble(5000, m_settings.rumble);
|
||||
}
|
||||
|
||||
void DeviceController::stop_rumble()
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
|
||||
if (s_callbacks)
|
||||
{
|
||||
s_callbacks->stop_rumble();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceController::process_motion(std::chrono::steady_clock::time_point timestamp, float gx, float gy, float gz, float accx, float accy, float accz)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::scoped_lock lock{s_mutex};
|
||||
|
||||
auto dif = timestamp - s_last_motion_timestamp;
|
||||
s_last_motion_timestamp = timestamp;
|
||||
|
||||
if (dif <= 0ms)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dif >= 10s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr auto maxDuration = 1s;
|
||||
auto clamped = std::min<std::common_type_t<decltype(dif), decltype(maxDuration)>>(dif, maxDuration);
|
||||
|
||||
float deltaSeconds = std::chrono::duration_cast<std::chrono::duration<float>>(dif).count();
|
||||
|
||||
s_motion_handler.processMotionSample(
|
||||
deltaSeconds,
|
||||
gx, gy, gz,
|
||||
accx, accy, accz);
|
||||
}
|
||||
|
||||
void DeviceController::set_motion_enabled(bool enabled)
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
s_has_motion = enabled;
|
||||
}
|
||||
57
src/input/api/Device/DeviceController.h
Normal file
57
src/input/api/Device/DeviceController.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "input/api/InputAPI.h"
|
||||
#include "input/api/Controller.h"
|
||||
#include "DeviceControllerProvider.h"
|
||||
|
||||
class DeviceController : public Controller<DeviceControllerProvider>
|
||||
{
|
||||
public:
|
||||
DeviceController();
|
||||
|
||||
class ControllerCallbacks
|
||||
{
|
||||
public:
|
||||
virtual void start_rumble(sint64 duration, float rumble) = 0;
|
||||
virtual void stop_rumble() = 0;
|
||||
};
|
||||
|
||||
std::string_view api_name() const override
|
||||
{
|
||||
static_assert(to_string(InputAPI::Device) == "Device");
|
||||
return to_string(InputAPI::Device);
|
||||
}
|
||||
|
||||
InputAPI::Type api() const override
|
||||
{
|
||||
return InputAPI::Device;
|
||||
}
|
||||
|
||||
static void set_callbacks(std::shared_ptr<ControllerCallbacks> callbacks);
|
||||
|
||||
bool is_connected() override;
|
||||
|
||||
bool has_rumble() override;
|
||||
|
||||
bool has_motion() override;
|
||||
|
||||
MotionSample get_motion_sample() override;
|
||||
|
||||
ControllerState raw_state() override;
|
||||
|
||||
void start_rumble() override;
|
||||
|
||||
void stop_rumble() override;
|
||||
|
||||
static void process_motion(std::chrono::steady_clock::time_point timestamp, float gx, float gy, float gz, float accx, float accy, float accz);
|
||||
|
||||
static void set_motion_enabled(bool enabled);
|
||||
|
||||
private:
|
||||
inline static bool s_has_motion{};
|
||||
inline static std::mutex s_mutex{};
|
||||
inline static WiiUMotionHandler s_motion_handler{};
|
||||
inline static std::chrono::steady_clock::time_point s_last_motion_timestamp{};
|
||||
inline static std::shared_ptr<ControllerCallbacks> s_callbacks{};
|
||||
inline static ControllerState s_raw_state{};
|
||||
};
|
||||
7
src/input/api/Device/DeviceControllerProvider.cpp
Normal file
7
src/input/api/Device/DeviceControllerProvider.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "DeviceControllerProvider.h"
|
||||
#include "DeviceController.h"
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> DeviceControllerProvider::get_controllers()
|
||||
{
|
||||
return {std::make_shared<DeviceController>()};
|
||||
}
|
||||
16
src/input/api/Device/DeviceControllerProvider.h
Normal file
16
src/input/api/Device/DeviceControllerProvider.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "input/api/ControllerProvider.h"
|
||||
|
||||
class DeviceControllerProvider : public ControllerProviderBase
|
||||
{
|
||||
public:
|
||||
inline static InputAPI::Type kAPIType = InputAPI::Device;
|
||||
|
||||
InputAPI::Type api() const override
|
||||
{
|
||||
return kAPIType;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
|
||||
};
|
||||
@ -14,6 +14,10 @@ namespace InputAPI
|
||||
GameCube,
|
||||
Wiimote,
|
||||
|
||||
Android,
|
||||
|
||||
Device,
|
||||
|
||||
WGIGamepad,
|
||||
WGIRawController,
|
||||
|
||||
@ -42,6 +46,10 @@ namespace InputAPI
|
||||
return "WGIRawController";
|
||||
case SDLController:
|
||||
return "SDLController";
|
||||
case Android:
|
||||
return "Android";
|
||||
case Device:
|
||||
return "Device";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -65,6 +73,10 @@ namespace InputAPI
|
||||
return DSUClient;
|
||||
else if (str == to_string(SDLController))
|
||||
return SDLController;
|
||||
else if (str == to_string(Android))
|
||||
return Android;
|
||||
else if (str == to_string(Device))
|
||||
return Device;
|
||||
else if (str == "DSU") // legacy
|
||||
return DSUClient;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user