Expand Up
@@ -15,6 +15,8 @@
#include " InputCommon/ControllerInterface/ControllerInterface.h"
#ifdef _WIN32
#include < Windows.h>
#pragma comment(lib, "SDL2.lib")
#endif
Expand Down
Expand Up
@@ -56,8 +58,9 @@ static bool HandleEventAndContinue(const SDL_Event& e)
else if (e.type == SDL_JOYDEVICEREMOVED)
{
g_controller_interface.RemoveDevice ([&e](const auto * device) {
const Joystick* joystick = dynamic_cast <const Joystick*>(device);
return joystick && SDL_JoystickInstanceID (joystick->GetSDLJoystick ()) == e.jdevice .which ;
return device->GetSource () == " SDL" &&
SDL_JoystickInstanceID (static_cast <const Joystick*>(device)->GetSDLJoystick ()) ==
e.jdevice .which ;
});
}
else if (e.type == s_populate_event_type)
Expand All
@@ -76,10 +79,77 @@ static bool HandleEventAndContinue(const SDL_Event& e)
}
#endif
static void EnableSDLLogging ()
{
SDL_LogSetAllPriority (SDL_LOG_PRIORITY_VERBOSE);
SDL_LogSetOutputFunction (
[](void *, int category, SDL_LogPriority priority, const char * message) {
std::string category_name;
switch (category)
{
case SDL_LOG_CATEGORY_APPLICATION:
category_name = " app" ;
break ;
case SDL_LOG_CATEGORY_ERROR:
category_name = " error" ;
break ;
case SDL_LOG_CATEGORY_ASSERT:
category_name = " assert" ;
break ;
case SDL_LOG_CATEGORY_SYSTEM:
category_name = " system" ;
break ;
case SDL_LOG_CATEGORY_AUDIO:
category_name = " audio" ;
break ;
case SDL_LOG_CATEGORY_VIDEO:
category_name = " video" ;
break ;
case SDL_LOG_CATEGORY_RENDER:
category_name = " render" ;
break ;
case SDL_LOG_CATEGORY_INPUT:
category_name = " input" ;
break ;
case SDL_LOG_CATEGORY_TEST:
category_name = " test" ;
break ;
default :
category_name = fmt::format (" unknown({})" , category);
break ;
}
auto log_level = Common::Log::LogLevel::LNOTICE;
switch (priority)
{
case SDL_LOG_PRIORITY_VERBOSE:
case SDL_LOG_PRIORITY_DEBUG:
log_level = Common::Log::LogLevel::LDEBUG;
break ;
case SDL_LOG_PRIORITY_INFO:
log_level = Common::Log::LogLevel::LINFO;
break ;
case SDL_LOG_PRIORITY_WARN:
log_level = Common::Log::LogLevel::LWARNING;
break ;
case SDL_LOG_PRIORITY_ERROR:
log_level = Common::Log::LogLevel::LERROR;
break ;
case SDL_LOG_PRIORITY_CRITICAL:
log_level = Common::Log::LogLevel::LNOTICE;
break ;
}
GENERIC_LOG_FMT (Common::Log::LogType::CONTROLLERINTERFACE, log_level, " {}: {}" ,
category_name, message);
},
nullptr );
}
void Init ()
{
#if !SDL_VERSION_ATLEAST(2, 0, 0)
if (SDL_Init (SDL_INIT_JOYSTICK) != 0 )
if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC ) != 0 )
ERROR_LOG_FMT (CONTROLLERINTERFACE, " SDL failed to initialize" );
return ;
#else
Expand All
@@ -88,16 +158,27 @@ void Init()
SDL_InitSubSystem (SDL_INIT_JOYSTICK);
SDL_QuitSubSystem (SDL_INIT_JOYSTICK);
#endif
EnableSDLLogging ();
#if SDL_VERSION_ATLEAST(2, 0, 14)
// This is required on windows so that SDL's joystick code properly pumps window messages
SDL_SetHint (SDL_HINT_JOYSTICK_THREAD, " 1" );
#endif
#if SDL_VERSION_ATLEAST(2, 0, 9)
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, " 1" );
#endif
s_hotplug_thread = std::thread ([] {
Common::ScopeGuard quit_guard ([] {
// TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up
SDL_Quit ();
});
{
Common::ScopeGuard init_guard ([] { s_init_event.Set (); });
if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0 )
if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER ) != 0 )
{
ERROR_LOG_FMT (CONTROLLERINTERFACE, " SDL failed to initialize" );
return ;
Expand Down
Expand Up
@@ -125,11 +206,34 @@ void Init()
}
}
#ifdef _WIN32
// This is a hack to workaround SDL_hidapi using window messages to detect device
// removal/arrival, yet no part of SDL pumps messages for it. It can hopefully be removed in the
// future when SDL fixes the issue. Note this is a separate issue from SDL_HINT_JOYSTICK_THREAD.
// Also note that SDL_WaitEvent may block while device detection window messages get queued up,
// causing some noticible stutter. This is just another reason it should be fixed properly by
// SDL...
const auto window_handle =
FindWindowEx (HWND_MESSAGE, nullptr , TEXT (" SDL_HIDAPI_DEVICE_DETECTION" ), nullptr );
#endif
SDL_Event e;
while (SDL_WaitEvent (&e) != 0 )
{
if (!HandleEventAndContinue (e))
return ;
#ifdef _WIN32
MSG msg;
while (window_handle && PeekMessage (&msg, window_handle, 0 , 0 , PM_NOREMOVE))
{
if (GetMessageA (&msg, window_handle, 0 , 0 ) != 0 )
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
#endif
}
});
Expand Down
Expand Up
@@ -172,18 +276,21 @@ void PopulateDevices()
Joystick::Joystick (SDL_Joystick* const joystick, const int sdl_index)
: m_joystick(joystick), m_name(StripSpaces(GetJoystickName(sdl_index)))
{
// really bad HACKS:
// to not use SDL for an XInput device
// too many people on the forums pick the SDL device and ask:
// "why don't my 360 gamepad triggers/rumble work correctly"
// really bad HACKS:
// to not use SDL for an XInput device
// too many people on the forums pick the SDL device and ask:
// "why don't my 360 gamepad triggers/rumble work correctly"
// XXX x360 controllers _should_ work on modern SDL2, so it's unclear why they're
// still broken. Perhaps it's because we're not pumping window messages, which SDL seems to
// expect.
#ifdef _WIN32
// checking the name is probably good (and hacky) enough
// but I'll double check with the num of buttons/axes
std::string lcasename = GetName ();
Common::ToLower (&lcasename);
if ((std::string::npos != lcasename.find (" xbox 360" )) &&
(10 == SDL_JoystickNumButtons (joystick)) && (5 == SDL_JoystickNumAxes (joystick)) &&
(11 == SDL_JoystickNumButtons (joystick)) && (6 == SDL_JoystickNumAxes (joystick)) &&
(1 == SDL_JoystickNumHats (joystick)) && (0 == SDL_JoystickNumBalls (joystick)))
{
// this device won't be used
Expand Down
Expand Up
@@ -220,53 +327,110 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index)
}
#ifdef USE_SDL_HAPTIC
m_haptic = SDL_HapticOpenFromJoystick (m_joystick);
if (!m_haptic)
return ;
if (SDL_JoystickIsHaptic (m_joystick))
{
m_haptic = SDL_HapticOpenFromJoystick (m_joystick);
if (m_haptic)
{
const unsigned int supported_effects = SDL_HapticQuery (m_haptic);
const unsigned int supported_effects = SDL_HapticQuery (m_haptic);
// Disable autocenter:
if (supported_effects & SDL_HAPTIC_AUTOCENTER)
SDL_HapticSetAutocenter (m_haptic, 0 );
// Disable autocenter:
if (supported_effects & SDL_HAPTIC_AUTOCENTER )
SDL_HapticSetAutocenter (m_haptic, 0 );
// Constant
if (supported_effects & SDL_HAPTIC_CONSTANT )
AddOutput ( new ConstantEffect (m_haptic) );
// Constant
if (supported_effects & SDL_HAPTIC_CONSTANT )
AddOutput (new ConstantEffect (m_haptic));
// Ramp
if (supported_effects & SDL_HAPTIC_RAMP )
AddOutput (new RampEffect (m_haptic));
// Ramp
if (supported_effects & SDL_HAPTIC_RAMP)
AddOutput (new RampEffect (m_haptic));
// Periodic
for (auto waveform :
{SDL_HAPTIC_SINE, SDL_HAPTIC_TRIANGLE, SDL_HAPTIC_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHDOWN})
{
if (supported_effects & waveform)
AddOutput (new PeriodicEffect (m_haptic, waveform));
}
// Periodic
for (auto waveform :
{SDL_HAPTIC_SINE, SDL_HAPTIC_TRIANGLE, SDL_HAPTIC_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHDOWN})
// LeftRight
if (supported_effects & SDL_HAPTIC_LEFTRIGHT)
{
AddOutput (new LeftRightEffect (m_haptic, LeftRightEffect::Motor::Strong));
AddOutput (new LeftRightEffect (m_haptic, LeftRightEffect::Motor::Weak));
}
}
}
#endif
#if SDL_VERSION_ATLEAST(2, 0, 9)
if (!m_haptic)
{
if (supported_effects & waveform)
AddOutput (new PeriodicEffect (m_haptic, waveform));
AddOutput (new Motor (m_joystick));
}
#endif
// LeftRight
if (supported_effects & SDL_HAPTIC_LEFTRIGHT )
# ifdef USE_SDL_GAMECONTROLLER
if (SDL_IsGameController (sdl_index) )
{
AddOutput (new LeftRightEffect (m_haptic, LeftRightEffect::Motor::Strong));
AddOutput (new LeftRightEffect (m_haptic, LeftRightEffect::Motor::Weak));
m_controller = SDL_GameControllerOpen (sdl_index);
if (m_controller)
{
if (SDL_GameControllerSetSensorEnabled (m_controller, SDL_SENSOR_ACCEL, SDL_TRUE) == 0 )
{
AddInput (new MotionInput (" Accel Up" , m_controller, SDL_SENSOR_ACCEL, 1 , 1 ));
AddInput (new MotionInput (" Accel Down" , m_controller, SDL_SENSOR_ACCEL, 1 , -1 ));
AddInput (new MotionInput (" Accel Left" , m_controller, SDL_SENSOR_ACCEL, 0 , -1 ));
AddInput (new MotionInput (" Accel Right" , m_controller, SDL_SENSOR_ACCEL, 0 , 1 ));
AddInput (new MotionInput (" Accel Forward" , m_controller, SDL_SENSOR_ACCEL, 2 , -1 ));
AddInput (new MotionInput (" Accel Backward" , m_controller, SDL_SENSOR_ACCEL, 2 , 1 ));
}
if (SDL_GameControllerSetSensorEnabled (m_controller, SDL_SENSOR_GYRO, SDL_TRUE) == 0 )
{
AddInput (new MotionInput (" Gyro Pitch Up" , m_controller, SDL_SENSOR_GYRO, 0 , 1 ));
AddInput (new MotionInput (" Gyro Pitch Down" , m_controller, SDL_SENSOR_GYRO, 0 , -1 ));
AddInput (new MotionInput (" Gyro Roll Left" , m_controller, SDL_SENSOR_GYRO, 2 , 1 ));
AddInput (new MotionInput (" Gyro Roll Right" , m_controller, SDL_SENSOR_GYRO, 2 , -1 ));
AddInput (new MotionInput (" Gyro Yaw Left" , m_controller, SDL_SENSOR_GYRO, 1 , 1 ));
AddInput (new MotionInput (" Gyro Yaw Right" , m_controller, SDL_SENSOR_GYRO, 1 , -1 ));
}
}
}
#endif
}
Joystick::~Joystick ()
{
#ifdef USE_SDL_GAMECONTROLLER
if (m_controller)
{
SDL_GameControllerClose (m_controller);
m_controller = nullptr ;
}
#endif
#ifdef USE_SDL_HAPTIC
if (m_haptic)
{
// stop/destroy all effects
SDL_HapticStopAll (m_haptic);
// close haptic first
// close haptic before joystick
SDL_HapticClose (m_haptic);
m_haptic = nullptr ;
}
#endif
#if SDL_VERSION_ATLEAST(2, 0, 9)
// stop all rumble
SDL_JoystickRumble (m_joystick, 0 , 0 , 0 );
#endif
// close joystick
SDL_JoystickClose (m_joystick);
}
Expand Down
Expand Up
@@ -442,6 +606,19 @@ bool Joystick::LeftRightEffect::UpdateParameters(s16 value)
}
#endif
#if SDL_VERSION_ATLEAST(2, 0, 9)
std::string Joystick::Motor::GetName () const
{
return " Motor" ;
}
void Joystick::Motor::SetState (ControlState state)
{
Uint16 rumble = state * std::numeric_limits<Uint16>::max ();
SDL_JoystickRumble (m_js, rumble, rumble, std::numeric_limits<Uint32>::max ());
}
#endif
void Joystick::UpdateInput ()
{
// TODO: Don't call this for every Joystick, only once per ControllerInterface::UpdateInput()
Expand Down
Expand Up
@@ -492,4 +669,14 @@ ControlState Joystick::Hat::GetState() const
{
return (SDL_JoystickGetHat (m_js, m_index) & (1 << m_direction)) > 0 ;
}
#ifdef USE_SDL_GAMECONTROLLER
ControlState Joystick::MotionInput::GetState () const
{
std::array<float , 3 > data{};
SDL_GameControllerGetSensorData (m_gc, m_type, data.data (), (int )data.size ());
return m_scale * data[m_index];
}
#endif
} // namespace ciface::SDL