summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
diff options
context:
space:
mode:
authorMichael M <mchtly@gmail.com>2017-11-09 14:06:58 -0800
committerLéo Lam <leo@innovatetechnologi.es>2018-06-04 17:50:08 +0200
commit932ca644aa92c01d60827206562938bfd877ecb3 (patch)
treeaec69af7d6ef6e7012ec024ad1cb6a6a3ee779c1 /Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
parent7062967b5b50cf57bc7f3d11741ff0dd893c4ee0 (diff)
Add hotplug support to SDL2 controller backend
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp159
1 files changed, 137 insertions, 22 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
index e17c307d1d..43ad1cf8c0 100644
--- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
@@ -2,13 +2,20 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include "InputCommon/ControllerInterface/SDL/SDL.h"
+
#include <algorithm>
#include <map>
#include <sstream>
+#include <thread>
+#include <SDL_events.h>
+
+#include "Common/Event.h"
+#include "Common/Logging/Log.h"
+#include "Common/ScopeGuard.h"
#include "Common/StringUtil.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
-#include "InputCommon/ControllerInterface/SDL/SDL.h"
#ifdef _WIN32
#pragma comment(lib, "SDL2.lib")
@@ -33,39 +40,142 @@ static std::string GetJoystickName(int index)
#endif
}
-void Init()
+static void OpenAndAddDevice(int index)
{
-#ifdef USE_SDL_HAPTIC
- if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) >= 0)
+ SDL_Joystick* dev = SDL_JoystickOpen(index);
+ if (dev)
{
- // Correctly initialized
+ auto js = std::make_shared<Joystick>(dev, index);
+ // only add if it has some inputs/outputs
+ if (!js->Inputs().empty() || !js->Outputs().empty())
+ g_controller_interface.AddDevice(std::move(js));
}
- else
-#endif
- if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
+}
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+static Common::Event s_init_event;
+static Uint32 s_stop_event_type;
+static Uint32 s_populate_event_type;
+static Common::Event s_populated_event;
+static std::thread s_hotplug_thread;
+
+static bool HandleEventAndContinue(const SDL_Event& e)
+{
+ if (e.type == SDL_JOYDEVICEADDED)
{
- // Failed to initialize
- return;
+ OpenAndAddDevice(e.jdevice.which);
+ g_controller_interface.InvokeDevicesChangedCallbacks();
}
+ 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;
+ });
+ g_controller_interface.InvokeDevicesChangedCallbacks();
+ }
+ else if (e.type == s_populate_event_type)
+ {
+ for (int i = 0; i < SDL_NumJoysticks(); ++i)
+ OpenAndAddDevice(i);
+ s_populated_event.Set();
+ }
+ else if (e.type == s_stop_event_type)
+ {
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+void Init()
+{
+#if !SDL_VERSION_ATLEAST(2, 0, 0)
+ if (SDL_Init(SDL_INIT_JOYSTICK) != 0)
+ ERROR_LOG(SERIALINTERFACE, "SDL failed to initialize");
+ return;
+#else
+ 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)
+ {
+ ERROR_LOG(SERIALINTERFACE, "SDL failed to initialize");
+ return;
+ }
+
+ Uint32 custom_events_start = SDL_RegisterEvents(2);
+ if (custom_events_start == static_cast<Uint32>(-1))
+ {
+ ERROR_LOG(SERIALINTERFACE, "SDL failed to register custom events");
+ return;
+ }
+ s_stop_event_type = custom_events_start;
+ s_populate_event_type = custom_events_start + 1;
+
+ // Drain all of the events and add the initial joysticks before returning. Otherwise, the
+ // individual joystick events as well as the custom populate event will be handled _after_
+ // ControllerInterface::Init/RefreshDevices has cleared its list of devices, resulting in
+ // duplicate devices.
+ SDL_Event e;
+ while (SDL_PollEvent(&e) != 0)
+ {
+ if (!HandleEventAndContinue(e))
+ return;
+ }
+ }
+
+ SDL_Event e;
+ while (SDL_WaitEvent(&e) != 0)
+ {
+ if (!HandleEventAndContinue(e))
+ return;
+ }
+ });
+
+ s_init_event.Wait();
+#endif
+}
+
+void DeInit()
+{
+#if !SDL_VERSION_ATLEAST(2, 0, 0)
+ SDL_Quit();
+#else
+ if (!s_hotplug_thread.joinable())
+ return;
+
+ SDL_Event stop_event{s_stop_event_type};
+ SDL_PushEvent(&stop_event);
+
+ s_hotplug_thread.join();
+#endif
}
void PopulateDevices()
{
- if (!(SDL_WasInit(SDL_INIT_EVERYTHING) & SDL_INIT_JOYSTICK))
+#if !SDL_VERSION_ATLEAST(2, 0, 0)
+ if (!SDL_WasInit(SDL_INIT_JOYSTICK))
return;
- // joysticks
for (int i = 0; i < SDL_NumJoysticks(); ++i)
- {
- SDL_Joystick* dev = SDL_JoystickOpen(i);
- if (dev)
- {
- auto js = std::make_shared<Joystick>(dev, i);
- // only add if it has some inputs/outputs
- if (js->Inputs().size() || js->Outputs().size())
- g_controller_interface.AddDevice(std::move(js));
- }
- }
+ OpenAndAddDevice(i);
+#else
+ if (!s_hotplug_thread.joinable())
+ return;
+
+ SDL_Event populate_event{s_populate_event_type};
+ SDL_PushEvent(&populate_event);
+
+ s_populated_event.Wait();
+#endif
}
Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index)
@@ -292,6 +402,11 @@ std::string Joystick::GetSource() const
return "SDL";
}
+SDL_Joystick* Joystick::GetSDLJoystick() const
+{
+ return m_joystick;
+}
+
std::string Joystick::Button::GetName() const
{
std::ostringstream ss;