|
|
@ -29,6 +29,7 @@ |
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
#include "common/logging/log.h"
|
|
|
#include "common/logging/log.h"
|
|
|
|
|
|
#include "common/math_util.h"
|
|
|
#include "common/param_package.h"
|
|
|
#include "common/param_package.h"
|
|
|
#include "common/settings_input.h"
|
|
|
#include "common/settings_input.h"
|
|
|
#include "common/threadsafe_queue.h"
|
|
|
#include "common/threadsafe_queue.h"
|
|
|
@ -68,13 +69,57 @@ public: |
|
|
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, |
|
|
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, |
|
|
SDL_GameController* game_controller) |
|
|
SDL_GameController* game_controller) |
|
|
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, |
|
|
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, |
|
|
sdl_controller{game_controller, &SDL_GameControllerClose} {} |
|
|
|
|
|
|
|
|
sdl_controller{game_controller, &SDL_GameControllerClose} { |
|
|
|
|
|
EnableMotion(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void EnableMotion() { |
|
|
|
|
|
if (sdl_controller) { |
|
|
|
|
|
SDL_GameController* controller = sdl_controller.get(); |
|
|
|
|
|
if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) { |
|
|
|
|
|
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); |
|
|
|
|
|
has_accel = true; |
|
|
|
|
|
} |
|
|
|
|
|
if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) { |
|
|
|
|
|
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); |
|
|
|
|
|
has_gyro = true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void SetButton(int button, bool value) { |
|
|
void SetButton(int button, bool value) { |
|
|
std::lock_guard lock{mutex}; |
|
|
std::lock_guard lock{mutex}; |
|
|
state.buttons.insert_or_assign(button, value); |
|
|
state.buttons.insert_or_assign(button, value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SetMotion(SDL_ControllerSensorEvent event) { |
|
|
|
|
|
constexpr float gravity_constant = 9.80665f; |
|
|
|
|
|
std::lock_guard lock{mutex}; |
|
|
|
|
|
u64 time_difference = event.timestamp - last_motion_update; |
|
|
|
|
|
last_motion_update = event.timestamp; |
|
|
|
|
|
switch (event.sensor) { |
|
|
|
|
|
case SDL_SENSOR_ACCEL: { |
|
|
|
|
|
const Common::Vec3f acceleration = {-event.data[0], event.data[2], -event.data[1]}; |
|
|
|
|
|
motion.SetAcceleration(acceleration / gravity_constant); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
case SDL_SENSOR_GYRO: { |
|
|
|
|
|
const Common::Vec3f gyroscope = {event.data[0], -event.data[2], event.data[1]}; |
|
|
|
|
|
motion.SetGyroscope(gyroscope / (Common::PI * 2)); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Ignore duplicated timestamps
|
|
|
|
|
|
if (time_difference == 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
motion.SetGyroThreshold(0.0001f); |
|
|
|
|
|
motion.UpdateRotation(time_difference * 1000); |
|
|
|
|
|
motion.UpdateOrientation(time_difference * 1000); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
bool GetButton(int button) const { |
|
|
bool GetButton(int button) const { |
|
|
std::lock_guard lock{mutex}; |
|
|
std::lock_guard lock{mutex}; |
|
|
return state.buttons.at(button); |
|
|
return state.buttons.at(button); |
|
|
@ -121,6 +166,14 @@ public: |
|
|
return std::make_tuple(x, y); |
|
|
return std::make_tuple(x, y); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool HasGyro() const { |
|
|
|
|
|
return has_gyro; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool HasAccel() const { |
|
|
|
|
|
return has_accel; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const MotionInput& GetMotion() const { |
|
|
const MotionInput& GetMotion() const { |
|
|
return motion; |
|
|
return motion; |
|
|
} |
|
|
} |
|
|
@ -173,8 +226,11 @@ private: |
|
|
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; |
|
|
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; |
|
|
mutable std::mutex mutex; |
|
|
mutable std::mutex mutex; |
|
|
|
|
|
|
|
|
// Motion is initialized without PID values as motion input is not aviable for SDL2
|
|
|
|
|
|
MotionInput motion{0.0f, 0.0f, 0.0f}; |
|
|
|
|
|
|
|
|
// Motion is initialized with the PID values
|
|
|
|
|
|
MotionInput motion{0.3f, 0.005f, 0.0f}; |
|
|
|
|
|
u64 last_motion_update{}; |
|
|
|
|
|
bool has_gyro{false}; |
|
|
|
|
|
bool has_accel{false}; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { |
|
|
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { |
|
|
@ -296,6 +352,12 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) { |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
case SDL_CONTROLLERSENSORUPDATE: { |
|
|
|
|
|
if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) { |
|
|
|
|
|
joystick->SetMotion(event.csensor); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
case SDL_JOYDEVICEREMOVED: |
|
|
case SDL_JOYDEVICEREMOVED: |
|
|
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); |
|
|
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); |
|
|
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); |
|
|
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); |
|
|
@ -449,6 +511,18 @@ private: |
|
|
std::shared_ptr<SDLJoystick> joystick; |
|
|
std::shared_ptr<SDLJoystick> joystick; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class SDLMotion final : public Input::MotionDevice { |
|
|
|
|
|
public: |
|
|
|
|
|
explicit SDLMotion(std::shared_ptr<SDLJoystick> joystick_) : joystick(std::move(joystick_)) {} |
|
|
|
|
|
|
|
|
|
|
|
Input::MotionStatus GetStatus() const override { |
|
|
|
|
|
return joystick->GetMotion().GetMotion(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
std::shared_ptr<SDLJoystick> joystick; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
class SDLDirectionMotion final : public Input::MotionDevice { |
|
|
class SDLDirectionMotion final : public Input::MotionDevice { |
|
|
public: |
|
|
public: |
|
|
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) |
|
|
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) |
|
|
@ -658,6 +732,10 @@ public: |
|
|
|
|
|
|
|
|
auto joystick = state.GetSDLJoystickByGUID(guid, port); |
|
|
auto joystick = state.GetSDLJoystickByGUID(guid, port); |
|
|
|
|
|
|
|
|
|
|
|
if (params.Has("motion")) { |
|
|
|
|
|
return std::make_unique<SDLMotion>(joystick); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (params.Has("hat")) { |
|
|
if (params.Has("hat")) { |
|
|
const int hat = params.Get("hat", 0); |
|
|
const int hat = params.Get("hat", 0); |
|
|
const std::string direction_name = params.Get("direction", ""); |
|
|
const std::string direction_name = params.Get("direction", ""); |
|
|
@ -717,6 +795,17 @@ SDLState::SDLState() { |
|
|
RegisterFactory<VibrationDevice>("sdl", vibration_factory); |
|
|
RegisterFactory<VibrationDevice>("sdl", vibration_factory); |
|
|
RegisterFactory<MotionDevice>("sdl", motion_factory); |
|
|
RegisterFactory<MotionDevice>("sdl", motion_factory); |
|
|
|
|
|
|
|
|
|
|
|
// Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
|
|
|
|
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); |
|
|
|
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); |
|
|
|
|
|
|
|
|
|
|
|
// Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
|
|
|
|
|
|
// GameController and not a generic one
|
|
|
|
|
|
SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1"); |
|
|
|
|
|
|
|
|
|
|
|
// Turn off Pro controller home led
|
|
|
|
|
|
SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0"); |
|
|
|
|
|
|
|
|
// If the frontend is going to manage the event loop, then we don't start one here
|
|
|
// If the frontend is going to manage the event loop, then we don't start one here
|
|
|
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0; |
|
|
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0; |
|
|
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { |
|
|
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { |
|
|
@ -853,6 +942,13 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s |
|
|
return params; |
|
|
return params; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Common::ParamPackage BuildMotionParam(int port, std::string guid) { |
|
|
|
|
|
Common::ParamPackage params({{"engine", "sdl"}, {"motion", "0"}}); |
|
|
|
|
|
params.Set("port", port); |
|
|
|
|
|
params.Set("guid", std::move(guid)); |
|
|
|
|
|
return params; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { |
|
|
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { |
|
|
switch (event.type) { |
|
|
switch (event.type) { |
|
|
case SDL_JOYAXISMOTION: { |
|
|
case SDL_JOYAXISMOTION: { |
|
|
@ -907,6 +1003,35 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
case SDL_CONTROLLERSENSORUPDATE: { |
|
|
|
|
|
bool is_motion_shaking = false; |
|
|
|
|
|
constexpr float gyro_threshold = 5.0f; |
|
|
|
|
|
constexpr float accel_threshold = 11.0f; |
|
|
|
|
|
if (event.csensor.sensor == SDL_SENSOR_ACCEL) { |
|
|
|
|
|
const Common::Vec3f acceleration = {-event.csensor.data[0], event.csensor.data[2], |
|
|
|
|
|
-event.csensor.data[1]}; |
|
|
|
|
|
if (acceleration.Length() > accel_threshold) { |
|
|
|
|
|
is_motion_shaking = true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (event.csensor.sensor == SDL_SENSOR_GYRO) { |
|
|
|
|
|
const Common::Vec3f gyroscope = {event.csensor.data[0], -event.csensor.data[2], |
|
|
|
|
|
event.csensor.data[1]}; |
|
|
|
|
|
if (gyroscope.Length() > gyro_threshold) { |
|
|
|
|
|
is_motion_shaking = true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!is_motion_shaking) { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (const auto joystick = state.GetSDLJoystickBySDLID(event.csensor.which)) { |
|
|
|
|
|
return BuildMotionParam(joystick->GetPort(), joystick->GetGUID()); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
return {}; |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
@ -1036,6 +1161,27 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa |
|
|
return mapping; |
|
|
return mapping; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& params) { |
|
|
|
|
|
if (!params.Has("guid") || !params.Has("port")) { |
|
|
|
|
|
return {}; |
|
|
|
|
|
} |
|
|
|
|
|
const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); |
|
|
|
|
|
auto* controller = joystick->GetSDLGameController(); |
|
|
|
|
|
if (controller == nullptr) { |
|
|
|
|
|
return {}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
joystick->EnableMotion(); |
|
|
|
|
|
|
|
|
|
|
|
if (!joystick->HasGyro() && !joystick->HasAccel()) { |
|
|
|
|
|
return {}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
MotionMapping mapping = {}; |
|
|
|
|
|
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, |
|
|
|
|
|
BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); |
|
|
|
|
|
return mapping; |
|
|
|
|
|
} |
|
|
namespace Polling { |
|
|
namespace Polling { |
|
|
class SDLPoller : public InputCommon::Polling::DevicePoller { |
|
|
class SDLPoller : public InputCommon::Polling::DevicePoller { |
|
|
public: |
|
|
public: |
|
|
@ -1149,6 +1295,7 @@ public: |
|
|
[[fallthrough]]; |
|
|
[[fallthrough]]; |
|
|
case SDL_JOYBUTTONUP: |
|
|
case SDL_JOYBUTTONUP: |
|
|
case SDL_JOYHATMOTION: |
|
|
case SDL_JOYHATMOTION: |
|
|
|
|
|
case SDL_CONTROLLERSENSORUPDATE: |
|
|
return {SDLEventToMotionParamPackage(state, event)}; |
|
|
return {SDLEventToMotionParamPackage(state, event)}; |
|
|
} |
|
|
} |
|
|
return std::nullopt; |
|
|
return std::nullopt; |
|
|
|