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();
|
||||
if(NumJoysticks > 0)
|
||||
{
|
||||
const int NumJoysticks = SDL_NumJoysticks();
|
||||
dbg_msg("joystick", "%d joystick(s) found", NumJoysticks);
|
||||
int ActualIndex = 0;
|
||||
for(int i = 0; i < NumJoysticks; i++)
|
||||
{
|
||||
SDL_Joystick *pJoystick = SDL_JoystickOpen(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", "Could not open joystick %d: '%s'", i, SDL_GetError());
|
||||
continue;
|
||||
dbg_msg("joystick", "Could not open joystick %d: '%s'", JoystickIndex, SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
m_vJoysticks.emplace_back(this, ActualIndex, pJoystick);
|
||||
if(std::find_if(m_vJoysticks.begin(), m_vJoysticks.end(), [pJoystick](const CJoystick &Joystick) -> bool { return Joystick.m_pDelegate == pJoystick; }) != m_vJoysticks.end())
|
||||
{
|
||||
// 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)", i, Joystick.GetName(),
|
||||
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());
|
||||
ActualIndex++;
|
||||
}
|
||||
if(ActualIndex > 0)
|
||||
{
|
||||
UpdateActiveJoystick();
|
||||
Console()->Chain("joystick_guid", ConchainJoystickGuidChanged, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_msg("joystick", "No joysticks found");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CInput::UpdateActiveJoystick()
|
||||
{
|
||||
m_pActiveJoystick = nullptr;
|
||||
if(m_vJoysticks.empty())
|
||||
return;
|
||||
m_pActiveJoystick = nullptr;
|
||||
for(auto &Joystick : m_vJoysticks)
|
||||
{
|
||||
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)
|
||||
return;
|
||||
CJoystick *pJoystick = GetActiveJoystick();
|
||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.jaxis.which)
|
||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.which)
|
||||
return;
|
||||
if(Event.jaxis.axis >= NUM_JOYSTICK_AXES)
|
||||
if(Event.axis >= NUM_JOYSTICK_AXES)
|
||||
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 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_aInputCount[LeftKey] = m_InputCounter;
|
||||
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;
|
||||
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_aInputCount[RightKey] = m_InputCounter;
|
||||
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;
|
||||
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)
|
||||
return;
|
||||
CJoystick *pJoystick = GetActiveJoystick();
|
||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.jbutton.which)
|
||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.which)
|
||||
return;
|
||||
if(Event.jbutton.button >= NUM_JOYSTICK_BUTTONS)
|
||||
if(Event.button >= NUM_JOYSTICK_BUTTONS)
|
||||
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)
|
||||
{
|
||||
|
@ -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)
|
||||
return;
|
||||
CJoystick *pJoystick = GetActiveJoystick();
|
||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.jhat.which)
|
||||
if(!pJoystick || pJoystick->GetInstanceID() != Event.which)
|
||||
return;
|
||||
if(Event.jhat.hat >= NUM_JOYSTICK_HATS)
|
||||
if(Event.hat >= NUM_JOYSTICK_HATS)
|
||||
return;
|
||||
|
||||
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])
|
||||
{
|
||||
|
@ -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()
|
||||
{
|
||||
return m_NumTextInputInstances > 0;
|
||||
|
@ -610,16 +633,24 @@ int CInput::Update()
|
|||
|
||||
// handle the joystick events
|
||||
case SDL_JOYAXISMOTION:
|
||||
HandleJoystickAxisMotionEvent(Event);
|
||||
HandleJoystickAxisMotionEvent(Event.jaxis);
|
||||
break;
|
||||
|
||||
case SDL_JOYBUTTONUP:
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
HandleJoystickButtonEvent(Event);
|
||||
HandleJoystickButtonEvent(Event.jbutton);
|
||||
break;
|
||||
|
||||
case SDL_JOYHATMOTION:
|
||||
HandleJoystickHatMotionEvent(Event);
|
||||
HandleJoystickHatMotionEvent(Event.jhat);
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED:
|
||||
HandleJoystickAddedEvent(Event.jdevice);
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
HandleJoystickRemovedEvent(Event.jdevice);
|
||||
break;
|
||||
|
||||
// handle mouse buttons
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#ifndef ENGINE_CLIENT_INPUT_H
|
||||
#define ENGINE_CLIENT_INPUT_H
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_joystick.h>
|
||||
|
||||
#include <engine/input.h>
|
||||
#include <engine/keys.h>
|
||||
|
||||
|
@ -13,6 +16,8 @@ class CInput : public IEngineInput
|
|||
public:
|
||||
class CJoystick : public IJoystick
|
||||
{
|
||||
friend class CInput;
|
||||
|
||||
CInput *m_pInput;
|
||||
int m_Index;
|
||||
char m_aName[64];
|
||||
|
@ -58,6 +63,7 @@ private:
|
|||
std::vector<CJoystick> m_vJoysticks;
|
||||
CJoystick *m_pActiveJoystick = nullptr;
|
||||
void InitJoysticks();
|
||||
bool OpenJoystick(int JoystickIndex);
|
||||
void CloseJoysticks();
|
||||
void UpdateActiveJoystick();
|
||||
static void ConchainJoystickGuidChanged(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
|
@ -82,9 +88,11 @@ private:
|
|||
|
||||
void UpdateMouseState();
|
||||
void UpdateJoystickState();
|
||||
void HandleJoystickAxisMotionEvent(const SDL_Event &Event);
|
||||
void HandleJoystickButtonEvent(const SDL_Event &Event);
|
||||
void HandleJoystickHatMotionEvent(const SDL_Event &Event);
|
||||
void HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event);
|
||||
void HandleJoystickButtonEvent(const SDL_JoyButtonEvent &Event);
|
||||
void HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event);
|
||||
void HandleJoystickAddedEvent(const SDL_JoyDeviceEvent &Event);
|
||||
void HandleJoystickRemovedEvent(const SDL_JoyDeviceEvent &Event);
|
||||
|
||||
// IME support
|
||||
int m_NumTextInputInstances;
|
||||
|
|
|
@ -1115,7 +1115,7 @@ float CMenus::RenderSettingsControlsJoystick(CUIRect View)
|
|||
{
|
||||
View.HSplitTop((View.h - ButtonHeight) / 2.0f, 0, &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