mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-19 14:38:18 +00:00
Merge #6160
6160: Add support for hot-plugging gamecontrollers r=def- a=Robyt3 Handle the appropriate SDL events to open new joysticks when they are connected and remove joysticks when they are disconnected. If the active joystick gets disconnected, then the first joystick in the list will be activated as a fallback. If the previously activated joystick gets reconnected, it will be activated again automatically, as it is identified by the GUID stored in the configuration. The stored joystick GUID is only updated when the user manually selects a new joystick in the controls settings or with the console. Closes #6152. ## Checklist - [X] Tested the change ingame - [ ] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [ ] Considered possible null pointers and out of bounds array indexing - [ ] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
commit
3e458f8c10
|
@ -91,42 +91,40 @@ void CInput::InitJoysticks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int NumJoysticks = SDL_NumJoysticks();
|
const int NumJoysticks = SDL_NumJoysticks();
|
||||||
if(NumJoysticks > 0)
|
dbg_msg("joystick", "%d joystick(s) found", NumJoysticks);
|
||||||
|
for(int i = 0; i < NumJoysticks; i++)
|
||||||
|
OpenJoystick(i);
|
||||||
|
UpdateActiveJoystick();
|
||||||
|
|
||||||
|
Console()->Chain("joystick_guid", ConchainJoystickGuidChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInput::OpenJoystick(int JoystickIndex)
|
||||||
|
{
|
||||||
|
SDL_Joystick *pJoystick = SDL_JoystickOpen(JoystickIndex);
|
||||||
|
if(!pJoystick)
|
||||||
{
|
{
|
||||||
dbg_msg("joystick", "%d joystick(s) found", NumJoysticks);
|
dbg_msg("joystick", "Could not open joystick %d: '%s'", JoystickIndex, SDL_GetError());
|
||||||
int ActualIndex = 0;
|
return false;
|
||||||
for(int i = 0; i < NumJoysticks; i++)
|
|
||||||
{
|
|
||||||
SDL_Joystick *pJoystick = SDL_JoystickOpen(i);
|
|
||||||
if(!pJoystick)
|
|
||||||
{
|
|
||||||
dbg_msg("joystick", "Could not open joystick %d: '%s'", i, SDL_GetError());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
m_vJoysticks.emplace_back(this, ActualIndex, pJoystick);
|
|
||||||
const CJoystick &Joystick = m_vJoysticks[m_vJoysticks.size() - 1];
|
|
||||||
dbg_msg("joystick", "Opened Joystick %d '%s' (%d axes, %d buttons, %d balls, %d hats)", i, Joystick.GetName(),
|
|
||||||
Joystick.GetNumAxes(), Joystick.GetNumButtons(), Joystick.GetNumBalls(), Joystick.GetNumHats());
|
|
||||||
ActualIndex++;
|
|
||||||
}
|
|
||||||
if(ActualIndex > 0)
|
|
||||||
{
|
|
||||||
UpdateActiveJoystick();
|
|
||||||
Console()->Chain("joystick_guid", ConchainJoystickGuidChanged, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
if(std::find_if(m_vJoysticks.begin(), m_vJoysticks.end(), [pJoystick](const CJoystick &Joystick) -> bool { return Joystick.m_pDelegate == pJoystick; }) != m_vJoysticks.end())
|
||||||
{
|
{
|
||||||
dbg_msg("joystick", "No joysticks found");
|
// Joystick has already been added
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
m_vJoysticks.emplace_back(this, m_vJoysticks.size(), pJoystick);
|
||||||
|
const CJoystick &Joystick = m_vJoysticks[m_vJoysticks.size() - 1];
|
||||||
|
dbg_msg("joystick", "Opened joystick %d '%s' (%d axes, %d buttons, %d balls, %d hats)", JoystickIndex, Joystick.GetName(),
|
||||||
|
Joystick.GetNumAxes(), Joystick.GetNumButtons(), Joystick.GetNumBalls(), Joystick.GetNumHats());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInput::UpdateActiveJoystick()
|
void CInput::UpdateActiveJoystick()
|
||||||
{
|
{
|
||||||
|
m_pActiveJoystick = nullptr;
|
||||||
if(m_vJoysticks.empty())
|
if(m_vJoysticks.empty())
|
||||||
return;
|
return;
|
||||||
m_pActiveJoystick = nullptr;
|
|
||||||
for(auto &Joystick : m_vJoysticks)
|
for(auto &Joystick : m_vJoysticks)
|
||||||
{
|
{
|
||||||
if(str_comp(Joystick.GetGUID(), g_Config.m_InpControllerGUID) == 0)
|
if(str_comp(Joystick.GetGUID(), g_Config.m_InpControllerGUID) == 0)
|
||||||
|
@ -385,56 +383,56 @@ void CInput::UpdateJoystickState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInput::HandleJoystickAxisMotionEvent(const SDL_Event &Event)
|
void CInput::HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event)
|
||||||
{
|
{
|
||||||
if(!g_Config.m_InpControllerEnable)
|
if(!g_Config.m_InpControllerEnable)
|
||||||
return;
|
return;
|
||||||
CJoystick *pJoystick = GetActiveJoystick();
|
CJoystick *pJoystick = GetActiveJoystick();
|
||||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.jaxis.which)
|
if(!pJoystick || pJoystick->GetInstanceID() != Event.which)
|
||||||
return;
|
return;
|
||||||
if(Event.jaxis.axis >= NUM_JOYSTICK_AXES)
|
if(Event.axis >= NUM_JOYSTICK_AXES)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const int LeftKey = KEY_JOY_AXIS_0_LEFT + 2 * Event.jaxis.axis;
|
const int LeftKey = KEY_JOY_AXIS_0_LEFT + 2 * Event.axis;
|
||||||
const int RightKey = LeftKey + 1;
|
const int RightKey = LeftKey + 1;
|
||||||
const float DeadZone = GetJoystickDeadzone();
|
const float DeadZone = GetJoystickDeadzone();
|
||||||
|
|
||||||
if(Event.jaxis.value <= SDL_JOYSTICK_AXIS_MIN * DeadZone && !m_aInputState[LeftKey])
|
if(Event.value <= SDL_JOYSTICK_AXIS_MIN * DeadZone && !m_aInputState[LeftKey])
|
||||||
{
|
{
|
||||||
m_aInputState[LeftKey] = true;
|
m_aInputState[LeftKey] = true;
|
||||||
m_aInputCount[LeftKey] = m_InputCounter;
|
m_aInputCount[LeftKey] = m_InputCounter;
|
||||||
AddEvent(0, LeftKey, IInput::FLAG_PRESS);
|
AddEvent(0, LeftKey, IInput::FLAG_PRESS);
|
||||||
}
|
}
|
||||||
else if(Event.jaxis.value > SDL_JOYSTICK_AXIS_MIN * DeadZone && m_aInputState[LeftKey])
|
else if(Event.value > SDL_JOYSTICK_AXIS_MIN * DeadZone && m_aInputState[LeftKey])
|
||||||
{
|
{
|
||||||
m_aInputState[LeftKey] = false;
|
m_aInputState[LeftKey] = false;
|
||||||
AddEvent(0, LeftKey, IInput::FLAG_RELEASE);
|
AddEvent(0, LeftKey, IInput::FLAG_RELEASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Event.jaxis.value >= SDL_JOYSTICK_AXIS_MAX * DeadZone && !m_aInputState[RightKey])
|
if(Event.value >= SDL_JOYSTICK_AXIS_MAX * DeadZone && !m_aInputState[RightKey])
|
||||||
{
|
{
|
||||||
m_aInputState[RightKey] = true;
|
m_aInputState[RightKey] = true;
|
||||||
m_aInputCount[RightKey] = m_InputCounter;
|
m_aInputCount[RightKey] = m_InputCounter;
|
||||||
AddEvent(0, RightKey, IInput::FLAG_PRESS);
|
AddEvent(0, RightKey, IInput::FLAG_PRESS);
|
||||||
}
|
}
|
||||||
else if(Event.jaxis.value < SDL_JOYSTICK_AXIS_MAX * DeadZone && m_aInputState[RightKey])
|
else if(Event.value < SDL_JOYSTICK_AXIS_MAX * DeadZone && m_aInputState[RightKey])
|
||||||
{
|
{
|
||||||
m_aInputState[RightKey] = false;
|
m_aInputState[RightKey] = false;
|
||||||
AddEvent(0, RightKey, IInput::FLAG_RELEASE);
|
AddEvent(0, RightKey, IInput::FLAG_RELEASE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInput::HandleJoystickButtonEvent(const SDL_Event &Event)
|
void CInput::HandleJoystickButtonEvent(const SDL_JoyButtonEvent &Event)
|
||||||
{
|
{
|
||||||
if(!g_Config.m_InpControllerEnable)
|
if(!g_Config.m_InpControllerEnable)
|
||||||
return;
|
return;
|
||||||
CJoystick *pJoystick = GetActiveJoystick();
|
CJoystick *pJoystick = GetActiveJoystick();
|
||||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.jbutton.which)
|
if(!pJoystick || pJoystick->GetInstanceID() != Event.which)
|
||||||
return;
|
return;
|
||||||
if(Event.jbutton.button >= NUM_JOYSTICK_BUTTONS)
|
if(Event.button >= NUM_JOYSTICK_BUTTONS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const int Key = Event.jbutton.button + KEY_JOYSTICK_BUTTON_0;
|
const int Key = Event.button + KEY_JOYSTICK_BUTTON_0;
|
||||||
|
|
||||||
if(Event.type == SDL_JOYBUTTONDOWN)
|
if(Event.type == SDL_JOYBUTTONDOWN)
|
||||||
{
|
{
|
||||||
|
@ -449,20 +447,20 @@ void CInput::HandleJoystickButtonEvent(const SDL_Event &Event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInput::HandleJoystickHatMotionEvent(const SDL_Event &Event)
|
void CInput::HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event)
|
||||||
{
|
{
|
||||||
if(!g_Config.m_InpControllerEnable)
|
if(!g_Config.m_InpControllerEnable)
|
||||||
return;
|
return;
|
||||||
CJoystick *pJoystick = GetActiveJoystick();
|
CJoystick *pJoystick = GetActiveJoystick();
|
||||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.jhat.which)
|
if(!pJoystick || pJoystick->GetInstanceID() != Event.which)
|
||||||
return;
|
return;
|
||||||
if(Event.jhat.hat >= NUM_JOYSTICK_HATS)
|
if(Event.hat >= NUM_JOYSTICK_HATS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int HatKeys[2];
|
int HatKeys[2];
|
||||||
CJoystick::GetJoystickHatKeys(Event.jhat.hat, Event.jhat.value, HatKeys);
|
CJoystick::GetJoystickHatKeys(Event.hat, Event.value, HatKeys);
|
||||||
|
|
||||||
for(int Key = KEY_JOY_HAT0_UP + Event.jhat.hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key <= KEY_JOY_HAT0_DOWN + Event.jhat.hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key++)
|
for(int Key = KEY_JOY_HAT0_UP + Event.hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key <= KEY_JOY_HAT0_DOWN + Event.hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key++)
|
||||||
{
|
{
|
||||||
if(Key != HatKeys[0] && Key != HatKeys[1] && m_aInputState[Key])
|
if(Key != HatKeys[0] && Key != HatKeys[1] && m_aInputState[Key])
|
||||||
{
|
{
|
||||||
|
@ -482,6 +480,31 @@ void CInput::HandleJoystickHatMotionEvent(const SDL_Event &Event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CInput::HandleJoystickAddedEvent(const SDL_JoyDeviceEvent &Event)
|
||||||
|
{
|
||||||
|
if(OpenJoystick(Event.which))
|
||||||
|
{
|
||||||
|
UpdateActiveJoystick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::HandleJoystickRemovedEvent(const SDL_JoyDeviceEvent &Event)
|
||||||
|
{
|
||||||
|
auto RemovedJoystick = std::find_if(m_vJoysticks.begin(), m_vJoysticks.end(), [Event](const CJoystick &Joystick) -> bool { return Joystick.GetInstanceID() == Event.which; });
|
||||||
|
if(RemovedJoystick != m_vJoysticks.end())
|
||||||
|
{
|
||||||
|
dbg_msg("joystick", "Closed joystick %d '%s'", (*RemovedJoystick).GetIndex(), (*RemovedJoystick).GetName());
|
||||||
|
auto NextJoystick = m_vJoysticks.erase(RemovedJoystick);
|
||||||
|
// Adjust indices of following joysticks
|
||||||
|
while(NextJoystick != m_vJoysticks.end())
|
||||||
|
{
|
||||||
|
(*NextJoystick).m_Index--;
|
||||||
|
++NextJoystick;
|
||||||
|
}
|
||||||
|
UpdateActiveJoystick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CInput::GetIMEState()
|
bool CInput::GetIMEState()
|
||||||
{
|
{
|
||||||
return m_NumTextInputInstances > 0;
|
return m_NumTextInputInstances > 0;
|
||||||
|
@ -610,16 +633,24 @@ int CInput::Update()
|
||||||
|
|
||||||
// handle the joystick events
|
// handle the joystick events
|
||||||
case SDL_JOYAXISMOTION:
|
case SDL_JOYAXISMOTION:
|
||||||
HandleJoystickAxisMotionEvent(Event);
|
HandleJoystickAxisMotionEvent(Event.jaxis);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_JOYBUTTONUP:
|
case SDL_JOYBUTTONUP:
|
||||||
case SDL_JOYBUTTONDOWN:
|
case SDL_JOYBUTTONDOWN:
|
||||||
HandleJoystickButtonEvent(Event);
|
HandleJoystickButtonEvent(Event.jbutton);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_JOYHATMOTION:
|
case SDL_JOYHATMOTION:
|
||||||
HandleJoystickHatMotionEvent(Event);
|
HandleJoystickHatMotionEvent(Event.jhat);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEADDED:
|
||||||
|
HandleJoystickAddedEvent(Event.jdevice);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEREMOVED:
|
||||||
|
HandleJoystickRemovedEvent(Event.jdevice);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// handle mouse buttons
|
// handle mouse buttons
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
#ifndef ENGINE_CLIENT_INPUT_H
|
#ifndef ENGINE_CLIENT_INPUT_H
|
||||||
#define ENGINE_CLIENT_INPUT_H
|
#define ENGINE_CLIENT_INPUT_H
|
||||||
|
|
||||||
|
#include <SDL_events.h>
|
||||||
|
#include <SDL_joystick.h>
|
||||||
|
|
||||||
#include <engine/input.h>
|
#include <engine/input.h>
|
||||||
#include <engine/keys.h>
|
#include <engine/keys.h>
|
||||||
|
|
||||||
|
@ -13,6 +16,8 @@ class CInput : public IEngineInput
|
||||||
public:
|
public:
|
||||||
class CJoystick : public IJoystick
|
class CJoystick : public IJoystick
|
||||||
{
|
{
|
||||||
|
friend class CInput;
|
||||||
|
|
||||||
CInput *m_pInput;
|
CInput *m_pInput;
|
||||||
int m_Index;
|
int m_Index;
|
||||||
char m_aName[64];
|
char m_aName[64];
|
||||||
|
@ -58,6 +63,7 @@ private:
|
||||||
std::vector<CJoystick> m_vJoysticks;
|
std::vector<CJoystick> m_vJoysticks;
|
||||||
CJoystick *m_pActiveJoystick = nullptr;
|
CJoystick *m_pActiveJoystick = nullptr;
|
||||||
void InitJoysticks();
|
void InitJoysticks();
|
||||||
|
bool OpenJoystick(int JoystickIndex);
|
||||||
void CloseJoysticks();
|
void CloseJoysticks();
|
||||||
void UpdateActiveJoystick();
|
void UpdateActiveJoystick();
|
||||||
static void ConchainJoystickGuidChanged(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
static void ConchainJoystickGuidChanged(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||||
|
@ -82,9 +88,11 @@ private:
|
||||||
|
|
||||||
void UpdateMouseState();
|
void UpdateMouseState();
|
||||||
void UpdateJoystickState();
|
void UpdateJoystickState();
|
||||||
void HandleJoystickAxisMotionEvent(const SDL_Event &Event);
|
void HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event);
|
||||||
void HandleJoystickButtonEvent(const SDL_Event &Event);
|
void HandleJoystickButtonEvent(const SDL_JoyButtonEvent &Event);
|
||||||
void HandleJoystickHatMotionEvent(const SDL_Event &Event);
|
void HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event);
|
||||||
|
void HandleJoystickAddedEvent(const SDL_JoyDeviceEvent &Event);
|
||||||
|
void HandleJoystickRemovedEvent(const SDL_JoyDeviceEvent &Event);
|
||||||
|
|
||||||
// IME support
|
// IME support
|
||||||
int m_NumTextInputInstances;
|
int m_NumTextInputInstances;
|
||||||
|
|
|
@ -1115,7 +1115,7 @@ float CMenus::RenderSettingsControlsJoystick(CUIRect View)
|
||||||
{
|
{
|
||||||
View.HSplitTop((View.h - ButtonHeight) / 2.0f, 0, &View);
|
View.HSplitTop((View.h - ButtonHeight) / 2.0f, 0, &View);
|
||||||
View.HSplitTop(ButtonHeight, &Button, &View);
|
View.HSplitTop(ButtonHeight, &Button, &View);
|
||||||
UI()->DoLabel(&Button, Localize("No controller found. Plug in a controller and restart the game."), 13.0f, TEXTALIGN_CENTER);
|
UI()->DoLabel(&Button, Localize("No controller found. Plug in a controller."), 13.0f, TEXTALIGN_CENTER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue