summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar James Rowe2018-09-20 00:28:05 -0600
committerGravatar fearlessTobi2019-03-02 19:09:34 +0100
commit09ac66388c01187ed6a402efcad76984c8af9a2e (patch)
treebeed8a86e7b7e02e0c5526723bbf812920341d12 /src
parentInput: Copy current SDL.h/cpp files to impl (diff)
downloadyuzu-09ac66388c01187ed6a402efcad76984c8af9a2e.tar.gz
yuzu-09ac66388c01187ed6a402efcad76984c8af9a2e.tar.xz
yuzu-09ac66388c01187ed6a402efcad76984c8af9a2e.zip
Input: Remove global variables from SDL Input
Changes the interface as well to remove any unique methods that frontends needed to call such as StartJoystickEventHandler by conditionally starting the polling thread only if the frontend hasn't started it already. Additionally, moves all global state into a single SDLState class in order to guarantee that the destructors are called in the proper order
Diffstat (limited to 'src')
-rw-r--r--src/input_common/CMakeLists.txt15
-rw-r--r--src/input_common/main.cpp23
-rw-r--r--src/input_common/main.h2
-rw-r--r--src/input_common/sdl/sdl.cpp636
-rw-r--r--src/input_common/sdl/sdl.h53
-rw-r--r--src/input_common/sdl/sdl_impl.cpp193
-rw-r--r--src/input_common/sdl/sdl_impl.h79
-rw-r--r--src/yuzu/bootmanager.cpp1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
9 files changed, 206 insertions, 809 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 1c7db28c0..5b4e032bd 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,15 +7,18 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 10 sdl/sdl.cpp
11 $<$<BOOL:${SDL2_FOUND}>:sdl/sdl.cpp sdl/sdl.h> 11 sdl/sdl.h
12) 12)
13 13
14create_target_directory_groups(input_common)
15
16target_link_libraries(input_common PUBLIC core PRIVATE common)
17
18if(SDL2_FOUND) 14if(SDL2_FOUND)
15 target_sources(input_common PRIVATE
16 sdl/sdl_impl.cpp
17 sdl/sdl_impl.h
18 )
19 target_link_libraries(input_common PRIVATE SDL2) 19 target_link_libraries(input_common PRIVATE SDL2)
20 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 20 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
21endif() 21endif()
22
23create_target_directory_groups(input_common)
24target_link_libraries(input_common PUBLIC core PRIVATE common)
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 37f572853..8e66c1b15 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -17,10 +17,7 @@ namespace InputCommon {
17 17
18static std::shared_ptr<Keyboard> keyboard; 18static std::shared_ptr<Keyboard> keyboard;
19static std::shared_ptr<MotionEmu> motion_emu; 19static std::shared_ptr<MotionEmu> motion_emu;
20 20static std::unique_ptr<SDL::State> sdl;
21#ifdef HAVE_SDL2
22static std::thread poll_thread;
23#endif
24 21
25void Init() { 22void Init() {
26 keyboard = std::make_shared<Keyboard>(); 23 keyboard = std::make_shared<Keyboard>();
@@ -30,15 +27,7 @@ void Init() {
30 motion_emu = std::make_shared<MotionEmu>(); 27 motion_emu = std::make_shared<MotionEmu>();
31 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu); 28 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
32 29
33#ifdef HAVE_SDL2 30 sdl = SDL::Init();
34 SDL::Init();
35#endif
36}
37
38void StartJoystickEventHandler() {
39#ifdef HAVE_SDL2
40 poll_thread = std::thread(SDL::PollLoop);
41#endif
42} 31}
43 32
44void Shutdown() { 33void Shutdown() {
@@ -47,11 +36,7 @@ void Shutdown() {
47 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 36 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
48 Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); 37 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
49 motion_emu.reset(); 38 motion_emu.reset();
50 39 sdl.reset();
51#ifdef HAVE_SDL2
52 SDL::Shutdown();
53 poll_thread.join();
54#endif
55} 40}
56 41
57Keyboard* GetKeyboard() { 42Keyboard* GetKeyboard() {
@@ -88,7 +73,7 @@ namespace Polling {
88 73
89std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) { 74std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
90#ifdef HAVE_SDL2 75#ifdef HAVE_SDL2
91 return SDL::Polling::GetPollers(type); 76 return sdl->GetPollers(type);
92#else 77#else
93 return {}; 78 return {};
94#endif 79#endif
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 9eb13106e..77a0ce90b 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -20,8 +20,6 @@ void Init();
20/// Deregisters all built-in input device factories and shuts them down. 20/// Deregisters all built-in input device factories and shuts them down.
21void Shutdown(); 21void Shutdown();
22 22
23void StartJoystickEventHandler();
24
25class Keyboard; 23class Keyboard;
26 24
27/// Gets the keyboard button device factory. 25/// Gets the keyboard button device factory.
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index faf3c1fa3..644db3448 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -1,631 +1,19 @@
1// Copyright 2017 Citra Emulator Project 1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <atomic>
7#include <cmath>
8#include <functional>
9#include <iterator>
10#include <mutex>
11#include <string>
12#include <thread>
13#include <tuple>
14#include <unordered_map>
15#include <utility>
16#include <vector>
17#include <SDL.h>
18#include "common/assert.h"
19#include "common/logging/log.h"
20#include "common/math_util.h"
21#include "common/param_package.h"
22#include "common/threadsafe_queue.h"
23#include "input_common/main.h"
24#include "input_common/sdl/sdl.h" 5#include "input_common/sdl/sdl.h"
6#ifdef HAVE_SDL2
7#include "input_common/sdl/sdl_impl.h"
8#endif
25 9
26namespace InputCommon { 10namespace InputCommon::SDL {
27 11
28namespace SDL { 12std::unique_ptr<State> Init() {
29 13#ifdef HAVE_SDL2
30class SDLJoystick; 14 return std::make_unique<SDLState>();
31class SDLButtonFactory; 15#else
32class SDLAnalogFactory; 16 return std::make_unique<NullState>();
33 17#endif
34/// Map of GUID of a list of corresponding virtual Joysticks
35static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
36static std::mutex joystick_map_mutex;
37
38static std::shared_ptr<SDLButtonFactory> button_factory;
39static std::shared_ptr<SDLAnalogFactory> analog_factory;
40
41/// Used by the Pollers during config
42static std::atomic<bool> polling;
43static Common::SPSCQueue<SDL_Event> event_queue;
44
45static std::atomic<bool> initialized = false;
46
47static std::string GetGUID(SDL_Joystick* joystick) {
48 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
49 char guid_str[33];
50 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
51 return guid_str;
52}
53
54class SDLJoystick {
55public:
56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
57 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
59
60 void SetButton(int button, bool value) {
61 std::lock_guard<std::mutex> lock(mutex);
62 state.buttons[button] = value;
63 }
64
65 bool GetButton(int button) const {
66 std::lock_guard<std::mutex> lock(mutex);
67 return state.buttons.at(button);
68 }
69
70 void SetAxis(int axis, Sint16 value) {
71 std::lock_guard<std::mutex> lock(mutex);
72 state.axes[axis] = value;
73 }
74
75 float GetAxis(int axis) const {
76 std::lock_guard<std::mutex> lock(mutex);
77 return state.axes.at(axis) / 32767.0f;
78 }
79
80 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
81 float x = GetAxis(axis_x);
82 float y = GetAxis(axis_y);
83 y = -y; // 3DS uses an y-axis inverse from SDL
84
85 // Make sure the coordinates are in the unit circle,
86 // otherwise normalize it.
87 float r = x * x + y * y;
88 if (r > 1.0f) {
89 r = std::sqrt(r);
90 x /= r;
91 y /= r;
92 }
93
94 return std::make_tuple(x, y);
95 }
96
97 void SetHat(int hat, Uint8 direction) {
98 std::lock_guard<std::mutex> lock(mutex);
99 state.hats[hat] = direction;
100 }
101
102 bool GetHatDirection(int hat, Uint8 direction) const {
103 std::lock_guard<std::mutex> lock(mutex);
104 return (state.hats.at(hat) & direction) != 0;
105 }
106 /**
107 * The guid of the joystick
108 */
109 const std::string& GetGUID() const {
110 return guid;
111 }
112
113 /**
114 * The number of joystick from the same type that were connected before this joystick
115 */
116 int GetPort() const {
117 return port;
118 }
119
120 SDL_Joystick* GetSDLJoystick() const {
121 return sdl_joystick.get();
122 }
123
124 void SetSDLJoystick(SDL_Joystick* joystick,
125 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
126 sdl_joystick =
127 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
128 }
129
130private:
131 struct State {
132 std::unordered_map<int, bool> buttons;
133 std::unordered_map<int, Sint16> axes;
134 std::unordered_map<int, Uint8> hats;
135 } state;
136 std::string guid;
137 int port;
138 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
139 mutable std::mutex mutex;
140};
141
142/**
143 * Get the nth joystick with the corresponding GUID
144 */
145static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) {
146 std::lock_guard<std::mutex> lock(joystick_map_mutex);
147 const auto it = joystick_map.find(guid);
148 if (it != joystick_map.end()) {
149 while (it->second.size() <= port) {
150 auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
151 [](SDL_Joystick*) {});
152 it->second.emplace_back(std::move(joystick));
153 }
154 return it->second[port];
155 }
156 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
157 return joystick_map[guid].emplace_back(std::move(joystick));
158}
159
160/**
161 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
162 * it to a SDLJoystick with the same guid and that port
163 */
164static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
165 std::lock_guard<std::mutex> lock(joystick_map_mutex);
166 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
167 const std::string guid = GetGUID(sdl_joystick);
168 auto map_it = joystick_map.find(guid);
169 if (map_it != joystick_map.end()) {
170 auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
171 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
172 return sdl_joystick == joystick->GetSDLJoystick();
173 });
174 if (vec_it != map_it->second.end()) {
175 // This is the common case: There is already an existing SDL_Joystick maped to a
176 // SDLJoystick. return the SDLJoystick
177 return *vec_it;
178 }
179 // Search for a SDLJoystick without a mapped SDL_Joystick...
180 auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
181 [](const std::shared_ptr<SDLJoystick>& joystick) {
182 return !joystick->GetSDLJoystick();
183 });
184 if (nullptr_it != map_it->second.end()) {
185 // ... and map it
186 (*nullptr_it)->SetSDLJoystick(sdl_joystick);
187 return *nullptr_it;
188 }
189 // There is no SDLJoystick without a mapped SDL_Joystick
190 // Create a new SDLJoystick
191 auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
192 return map_it->second.emplace_back(std::move(joystick));
193 }
194 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
195 return joystick_map[guid].emplace_back(std::move(joystick));
196}
197
198void InitJoystick(int joystick_index) {
199 std::lock_guard<std::mutex> lock(joystick_map_mutex);
200 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
201 if (!sdl_joystick) {
202 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
203 return;
204 }
205 std::string guid = GetGUID(sdl_joystick);
206 if (joystick_map.find(guid) == joystick_map.end()) {
207 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
208 joystick_map[guid].emplace_back(std::move(joystick));
209 return;
210 }
211 auto& joystick_guid_list = joystick_map[guid];
212 const auto it = std::find_if(
213 joystick_guid_list.begin(), joystick_guid_list.end(),
214 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
215 if (it != joystick_guid_list.end()) {
216 (*it)->SetSDLJoystick(sdl_joystick);
217 return;
218 }
219 auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
220 joystick_guid_list.emplace_back(std::move(joystick));
221}
222
223void CloseJoystick(SDL_Joystick* sdl_joystick) {
224 std::lock_guard<std::mutex> lock(joystick_map_mutex);
225 std::string guid = GetGUID(sdl_joystick);
226 // This call to guid is save since the joystick is guranteed to be in that map
227 auto& joystick_guid_list = joystick_map[guid];
228 const auto joystick_it =
229 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
230 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
231 return joystick->GetSDLJoystick() == sdl_joystick;
232 });
233 (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
234}
235
236void HandleGameControllerEvent(const SDL_Event& event) {
237 switch (event.type) {
238 case SDL_JOYBUTTONUP: {
239 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
240 if (joystick) {
241 joystick->SetButton(event.jbutton.button, false);
242 }
243 break;
244 }
245 case SDL_JOYBUTTONDOWN: {
246 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
247 if (joystick) {
248 joystick->SetButton(event.jbutton.button, true);
249 }
250 break;
251 }
252 case SDL_JOYHATMOTION: {
253 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
254 if (joystick) {
255 joystick->SetHat(event.jhat.hat, event.jhat.value);
256 }
257 break;
258 }
259 case SDL_JOYAXISMOTION: {
260 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
261 if (joystick) {
262 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
263 }
264 break;
265 }
266 case SDL_JOYDEVICEREMOVED:
267 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
268 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
269 break;
270 case SDL_JOYDEVICEADDED:
271 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
272 InitJoystick(event.jdevice.which);
273 break;
274 }
275}
276
277void CloseSDLJoysticks() {
278 std::lock_guard<std::mutex> lock(joystick_map_mutex);
279 joystick_map.clear();
280}
281
282void PollLoop() {
283 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
284 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
285 return;
286 }
287
288 SDL_Event event;
289 while (initialized) {
290 // Wait for 10 ms or until an event happens
291 if (SDL_WaitEventTimeout(&event, 10)) {
292 // Don't handle the event if we are configuring
293 if (polling) {
294 event_queue.Push(event);
295 } else {
296 HandleGameControllerEvent(event);
297 }
298 }
299 }
300 CloseSDLJoysticks();
301 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
302}
303
304class SDLButton final : public Input::ButtonDevice {
305public:
306 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
307 : joystick(std::move(joystick_)), button(button_) {}
308
309 bool GetStatus() const override {
310 return joystick->GetButton(button);
311 }
312
313private:
314 std::shared_ptr<SDLJoystick> joystick;
315 int button;
316};
317
318class SDLDirectionButton final : public Input::ButtonDevice {
319public:
320 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
321 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
322
323 bool GetStatus() const override {
324 return joystick->GetHatDirection(hat, direction);
325 }
326
327private:
328 std::shared_ptr<SDLJoystick> joystick;
329 int hat;
330 Uint8 direction;
331};
332
333class SDLAxisButton final : public Input::ButtonDevice {
334public:
335 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
336 bool trigger_if_greater_)
337 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
338 trigger_if_greater(trigger_if_greater_) {}
339
340 bool GetStatus() const override {
341 float axis_value = joystick->GetAxis(axis);
342 if (trigger_if_greater)
343 return axis_value > threshold;
344 return axis_value < threshold;
345 }
346
347private:
348 std::shared_ptr<SDLJoystick> joystick;
349 int axis;
350 float threshold;
351 bool trigger_if_greater;
352};
353
354class SDLAnalog final : public Input::AnalogDevice {
355public:
356 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
357 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_) {}
358
359 std::tuple<float, float> GetStatus() const override {
360 return joystick->GetAnalog(axis_x, axis_y);
361 }
362
363private:
364 std::shared_ptr<SDLJoystick> joystick;
365 int axis_x;
366 int axis_y;
367};
368
369/// A button device factory that creates button devices from SDL joystick
370class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
371public:
372 /**
373 * Creates a button device from a joystick button
374 * @param params contains parameters for creating the device:
375 * - "guid": the guid of the joystick to bind
376 * - "port": the nth joystick of the same type to bind
377 * - "button"(optional): the index of the button to bind
378 * - "hat"(optional): the index of the hat to bind as direction buttons
379 * - "axis"(optional): the index of the axis to bind
380 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
381 * "down", "left" or "right"
382 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
383 * triggered if the axis value crosses
384 * - "direction"(only used for axis): "+" means the button is triggered when the axis
385 * value is greater than the threshold; "-" means the button is triggered when the axis
386 * value is smaller than the threshold
387 */
388 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
389 const std::string guid = params.Get("guid", "0");
390 const int port = params.Get("port", 0);
391
392 auto joystick = GetSDLJoystickByGUID(guid, port);
393
394 if (params.Has("hat")) {
395 const int hat = params.Get("hat", 0);
396 const std::string direction_name = params.Get("direction", "");
397 Uint8 direction;
398 if (direction_name == "up") {
399 direction = SDL_HAT_UP;
400 } else if (direction_name == "down") {
401 direction = SDL_HAT_DOWN;
402 } else if (direction_name == "left") {
403 direction = SDL_HAT_LEFT;
404 } else if (direction_name == "right") {
405 direction = SDL_HAT_RIGHT;
406 } else {
407 direction = 0;
408 }
409 // This is necessary so accessing GetHat with hat won't crash
410 joystick->SetHat(hat, SDL_HAT_CENTERED);
411 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
412 }
413
414 if (params.Has("axis")) {
415 const int axis = params.Get("axis", 0);
416 const float threshold = params.Get("threshold", 0.5f);
417 const std::string direction_name = params.Get("direction", "");
418 bool trigger_if_greater;
419 if (direction_name == "+") {
420 trigger_if_greater = true;
421 } else if (direction_name == "-") {
422 trigger_if_greater = false;
423 } else {
424 trigger_if_greater = true;
425 LOG_ERROR(Input, "Unknown direction '{}'", direction_name);
426 }
427 // This is necessary so accessing GetAxis with axis won't crash
428 joystick->SetAxis(axis, 0);
429 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
430 }
431
432 const int button = params.Get("button", 0);
433 // This is necessary so accessing GetButton with button won't crash
434 joystick->SetButton(button, false);
435 return std::make_unique<SDLButton>(joystick, button);
436 }
437};
438
439/// An analog device factory that creates analog devices from SDL joystick
440class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
441public:
442 /**
443 * Creates analog device from joystick axes
444 * @param params contains parameters for creating the device:
445 * - "guid": the guid of the joystick to bind
446 * - "port": the nth joystick of the same type
447 * - "axis_x": the index of the axis to be bind as x-axis
448 * - "axis_y": the index of the axis to be bind as y-axis
449 */
450 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
451 const std::string guid = params.Get("guid", "0");
452 const int port = params.Get("port", 0);
453 const int axis_x = params.Get("axis_x", 0);
454 const int axis_y = params.Get("axis_y", 1);
455
456 auto joystick = GetSDLJoystickByGUID(guid, port);
457
458 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
459 joystick->SetAxis(axis_x, 0);
460 joystick->SetAxis(axis_y, 0);
461 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y);
462 }
463};
464
465void Init() {
466 using namespace Input;
467 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
468 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
469 polling = false;
470 initialized = true;
471}
472
473void Shutdown() {
474 if (initialized) {
475 using namespace Input;
476 UnregisterFactory<ButtonDevice>("sdl");
477 UnregisterFactory<AnalogDevice>("sdl");
478 initialized = false;
479 }
480}
481
482Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
483 Common::ParamPackage params({{"engine", "sdl"}});
484 switch (event.type) {
485 case SDL_JOYAXISMOTION: {
486 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
487 params.Set("port", joystick->GetPort());
488 params.Set("guid", joystick->GetGUID());
489 params.Set("axis", event.jaxis.axis);
490 if (event.jaxis.value > 0) {
491 params.Set("direction", "+");
492 params.Set("threshold", "0.5");
493 } else {
494 params.Set("direction", "-");
495 params.Set("threshold", "-0.5");
496 }
497 break;
498 }
499 case SDL_JOYBUTTONUP: {
500 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
501 params.Set("port", joystick->GetPort());
502 params.Set("guid", joystick->GetGUID());
503 params.Set("button", event.jbutton.button);
504 break;
505 }
506 case SDL_JOYHATMOTION: {
507 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
508 params.Set("port", joystick->GetPort());
509 params.Set("guid", joystick->GetGUID());
510 params.Set("hat", event.jhat.hat);
511 switch (event.jhat.value) {
512 case SDL_HAT_UP:
513 params.Set("direction", "up");
514 break;
515 case SDL_HAT_DOWN:
516 params.Set("direction", "down");
517 break;
518 case SDL_HAT_LEFT:
519 params.Set("direction", "left");
520 break;
521 case SDL_HAT_RIGHT:
522 params.Set("direction", "right");
523 break;
524 default:
525 return {};
526 }
527 break;
528 }
529 }
530 return params;
531}
532
533namespace Polling {
534
535class SDLPoller : public InputCommon::Polling::DevicePoller {
536public:
537 void Start() override {
538 event_queue.Clear();
539 polling = true;
540 }
541
542 void Stop() override {
543 polling = false;
544 }
545};
546
547class SDLButtonPoller final : public SDLPoller {
548public:
549 Common::ParamPackage GetNextInput() override {
550 SDL_Event event;
551 while (event_queue.Pop(event)) {
552 switch (event.type) {
553 case SDL_JOYAXISMOTION:
554 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
555 break;
556 }
557 case SDL_JOYBUTTONUP:
558 case SDL_JOYHATMOTION:
559 return SDLEventToButtonParamPackage(event);
560 }
561 }
562 return {};
563 }
564};
565
566class SDLAnalogPoller final : public SDLPoller {
567public:
568 void Start() override {
569 SDLPoller::Start();
570
571 // Reset stored axes
572 analog_xaxis = -1;
573 analog_yaxis = -1;
574 analog_axes_joystick = -1;
575 }
576
577 Common::ParamPackage GetNextInput() override {
578 SDL_Event event;
579 while (event_queue.Pop(event)) {
580 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
581 continue;
582 }
583 // An analog device needs two axes, so we need to store the axis for later and wait for
584 // a second SDL event. The axes also must be from the same joystick.
585 int axis = event.jaxis.axis;
586 if (analog_xaxis == -1) {
587 analog_xaxis = axis;
588 analog_axes_joystick = event.jaxis.which;
589 } else if (analog_yaxis == -1 && analog_xaxis != axis &&
590 analog_axes_joystick == event.jaxis.which) {
591 analog_yaxis = axis;
592 }
593 }
594 Common::ParamPackage params;
595 if (analog_xaxis != -1 && analog_yaxis != -1) {
596 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
597 params.Set("engine", "sdl");
598 params.Set("port", joystick->GetPort());
599 params.Set("guid", joystick->GetGUID());
600 params.Set("axis_x", analog_xaxis);
601 params.Set("axis_y", analog_yaxis);
602 analog_xaxis = -1;
603 analog_yaxis = -1;
604 analog_axes_joystick = -1;
605 return params;
606 }
607 return params;
608 }
609
610private:
611 int analog_xaxis = -1;
612 int analog_yaxis = -1;
613 SDL_JoystickID analog_axes_joystick = -1;
614};
615
616std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
617 InputCommon::Polling::DeviceType type) {
618 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
619 switch (type) {
620 case InputCommon::Polling::DeviceType::Analog:
621 pollers.push_back(std::make_unique<SDLAnalogPoller>());
622 break;
623 case InputCommon::Polling::DeviceType::Button:
624 pollers.push_back(std::make_unique<SDLButtonPoller>());
625 break;
626 }
627 return pollers;
628} 18}
629} // namespace Polling 19} // namespace InputCommon::SDL
630} // namespace SDL
631} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 0206860d3..02a8d2e2c 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -1,4 +1,4 @@
1// Copyright 2017 Citra Emulator Project 1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -7,45 +7,36 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "core/frontend/input.h" 9#include "core/frontend/input.h"
10#include "input_common/main.h"
10 11
11union SDL_Event; 12union SDL_Event;
13
12namespace Common { 14namespace Common {
13class ParamPackage; 15class ParamPackage;
14} 16} // namespace Common
15namespace InputCommon { 17
16namespace Polling { 18namespace InputCommon::Polling {
17class DevicePoller; 19class DevicePoller;
18enum class DeviceType; 20enum class DeviceType;
19} // namespace Polling 21} // namespace InputCommon::Polling
20} // namespace InputCommon
21
22namespace InputCommon {
23namespace SDL {
24
25/// Initializes and registers SDL device factories
26void Init();
27
28/// Unresisters SDL device factories and shut them down.
29void Shutdown();
30
31/// Needs to be called before SDL_QuitSubSystem.
32void CloseSDLJoysticks();
33 22
34/// Handle SDL_Events for joysticks from SDL_PollEvent 23namespace InputCommon::SDL {
35void HandleGameControllerEvent(const SDL_Event& event);
36 24
37/// A Loop that calls HandleGameControllerEvent until Shutdown is called 25class State {
38void PollLoop(); 26public:
27 /// Unresisters SDL device factories and shut them down.
28 virtual ~State() = default;
39 29
40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 30 virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
41Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); 31 InputCommon::Polling::DeviceType type) = 0;
32};
42 33
43namespace Polling { 34class NullState : public State {
35public:
36 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
37 InputCommon::Polling::DeviceType type) override {}
38};
44 39
45/// Get all DevicePoller that use the SDL backend for a specific device type 40std::unique_ptr<State> Init();
46std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
47 InputCommon::Polling::DeviceType type);
48 41
49} // namespace Polling 42} // namespace InputCommon::SDL
50} // namespace SDL
51} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 7c1ecc2e0..83fcf354e 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -1,4 +1,4 @@
1// Copyright 2017 Citra Emulator Project 1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -20,30 +20,13 @@
20#include "common/math_util.h" 20#include "common/math_util.h"
21#include "common/param_package.h" 21#include "common/param_package.h"
22#include "common/threadsafe_queue.h" 22#include "common/threadsafe_queue.h"
23#include "input_common/main.h" 23#include "core/frontend/input.h"
24#include "input_common/sdl/sdl.h" 24#include "input_common/sdl/sdl_impl.h"
25 25
26namespace InputCommon { 26namespace InputCommon {
27 27
28namespace SDL { 28namespace SDL {
29 29
30class SDLJoystick;
31class SDLButtonFactory;
32class SDLAnalogFactory;
33
34/// Map of GUID of a list of corresponding virtual Joysticks
35static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
36static std::mutex joystick_map_mutex;
37
38static std::shared_ptr<SDLButtonFactory> button_factory;
39static std::shared_ptr<SDLAnalogFactory> analog_factory;
40
41/// Used by the Pollers during config
42static std::atomic<bool> polling;
43static Common::SPSCQueue<SDL_Event> event_queue;
44
45static std::atomic<bool> initialized = false;
46
47static std::string GetGUID(SDL_Joystick* joystick) { 30static std::string GetGUID(SDL_Joystick* joystick) {
48 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 31 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
49 char guid_str[33]; 32 char guid_str[33];
@@ -51,6 +34,20 @@ static std::string GetGUID(SDL_Joystick* joystick) {
51 return guid_str; 34 return guid_str;
52} 35}
53 36
37/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
38static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
39
40static int SDLEventWatcher(void* userdata, SDL_Event* event) {
41 SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata);
42 // Don't handle the event if we are configuring
43 if (sdl_state->polling) {
44 sdl_state->event_queue.Push(*event);
45 } else {
46 sdl_state->HandleGameControllerEvent(*event);
47 }
48 return 0;
49}
50
54class SDLJoystick { 51class SDLJoystick {
55public: 52public:
56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, 53 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
@@ -142,7 +139,7 @@ private:
142/** 139/**
143 * Get the nth joystick with the corresponding GUID 140 * Get the nth joystick with the corresponding GUID
144 */ 141 */
145static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) { 142std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
146 std::lock_guard<std::mutex> lock(joystick_map_mutex); 143 std::lock_guard<std::mutex> lock(joystick_map_mutex);
147 const auto it = joystick_map.find(guid); 144 const auto it = joystick_map.find(guid);
148 if (it != joystick_map.end()) { 145 if (it != joystick_map.end()) {
@@ -161,7 +158,7 @@ static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid
161 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie 158 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
162 * it to a SDLJoystick with the same guid and that port 159 * it to a SDLJoystick with the same guid and that port
163 */ 160 */
164static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 161std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
165 std::lock_guard<std::mutex> lock(joystick_map_mutex); 162 std::lock_guard<std::mutex> lock(joystick_map_mutex);
166 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
167 const std::string guid = GetGUID(sdl_joystick); 164 const std::string guid = GetGUID(sdl_joystick);
@@ -195,7 +192,7 @@ static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id)
195 return joystick_map[guid].emplace_back(std::move(joystick)); 192 return joystick_map[guid].emplace_back(std::move(joystick));
196} 193}
197 194
198void InitJoystick(int joystick_index) { 195void SDLState::InitJoystick(int joystick_index) {
199 std::lock_guard<std::mutex> lock(joystick_map_mutex); 196 std::lock_guard<std::mutex> lock(joystick_map_mutex);
200 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 197 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
201 if (!sdl_joystick) { 198 if (!sdl_joystick) {
@@ -220,10 +217,10 @@ void InitJoystick(int joystick_index) {
220 joystick_guid_list.emplace_back(std::move(joystick)); 217 joystick_guid_list.emplace_back(std::move(joystick));
221} 218}
222 219
223void CloseJoystick(SDL_Joystick* sdl_joystick) { 220void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
224 std::lock_guard<std::mutex> lock(joystick_map_mutex); 221 std::lock_guard<std::mutex> lock(joystick_map_mutex);
225 std::string guid = GetGUID(sdl_joystick); 222 std::string guid = GetGUID(sdl_joystick);
226 // This call to guid is save since the joystick is guranteed to be in that map 223 // This call to guid is safe since the joystick is guaranteed to be in the map
227 auto& joystick_guid_list = joystick_map[guid]; 224 auto& joystick_guid_list = joystick_map[guid];
228 const auto joystick_it = 225 const auto joystick_it =
229 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), 226 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
@@ -233,32 +230,28 @@ void CloseJoystick(SDL_Joystick* sdl_joystick) {
233 (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {}); 230 (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
234} 231}
235 232
236void HandleGameControllerEvent(const SDL_Event& event) { 233void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
237 switch (event.type) { 234 switch (event.type) {
238 case SDL_JOYBUTTONUP: { 235 case SDL_JOYBUTTONUP: {
239 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); 236 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
240 if (joystick) {
241 joystick->SetButton(event.jbutton.button, false); 237 joystick->SetButton(event.jbutton.button, false);
242 } 238 }
243 break; 239 break;
244 } 240 }
245 case SDL_JOYBUTTONDOWN: { 241 case SDL_JOYBUTTONDOWN: {
246 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); 242 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
247 if (joystick) {
248 joystick->SetButton(event.jbutton.button, true); 243 joystick->SetButton(event.jbutton.button, true);
249 } 244 }
250 break; 245 break;
251 } 246 }
252 case SDL_JOYHATMOTION: { 247 case SDL_JOYHATMOTION: {
253 auto joystick = GetSDLJoystickBySDLID(event.jhat.which); 248 if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
254 if (joystick) {
255 joystick->SetHat(event.jhat.hat, event.jhat.value); 249 joystick->SetHat(event.jhat.hat, event.jhat.value);
256 } 250 }
257 break; 251 break;
258 } 252 }
259 case SDL_JOYAXISMOTION: { 253 case SDL_JOYAXISMOTION: {
260 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); 254 if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
261 if (joystick) {
262 joystick->SetAxis(event.jaxis.axis, event.jaxis.value); 255 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
263 } 256 }
264 break; 257 break;
@@ -274,33 +267,11 @@ void HandleGameControllerEvent(const SDL_Event& event) {
274 } 267 }
275} 268}
276 269
277void CloseSDLJoysticks() { 270void SDLState::CloseJoysticks() {
278 std::lock_guard<std::mutex> lock(joystick_map_mutex); 271 std::lock_guard<std::mutex> lock(joystick_map_mutex);
279 joystick_map.clear(); 272 joystick_map.clear();
280} 273}
281 274
282void PollLoop() {
283 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
284 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
285 return;
286 }
287
288 SDL_Event event;
289 while (initialized) {
290 // Wait for 10 ms or until an event happens
291 if (SDL_WaitEventTimeout(&event, 10)) {
292 // Don't handle the event if we are configuring
293 if (polling) {
294 event_queue.Push(event);
295 } else {
296 HandleGameControllerEvent(event);
297 }
298 }
299 }
300 CloseSDLJoysticks();
301 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
302}
303
304class SDLButton final : public Input::ButtonDevice { 275class SDLButton final : public Input::ButtonDevice {
305public: 276public:
306 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) 277 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
@@ -369,6 +340,8 @@ private:
369/// A button device factory that creates button devices from SDL joystick 340/// A button device factory that creates button devices from SDL joystick
370class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { 341class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
371public: 342public:
343 explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
344
372 /** 345 /**
373 * Creates a button device from a joystick button 346 * Creates a button device from a joystick button
374 * @param params contains parameters for creating the device: 347 * @param params contains parameters for creating the device:
@@ -389,7 +362,7 @@ public:
389 const std::string guid = params.Get("guid", "0"); 362 const std::string guid = params.Get("guid", "0");
390 const int port = params.Get("port", 0); 363 const int port = params.Get("port", 0);
391 364
392 auto joystick = GetSDLJoystickByGUID(guid, port); 365 auto joystick = state.GetSDLJoystickByGUID(guid, port);
393 366
394 if (params.Has("hat")) { 367 if (params.Has("hat")) {
395 const int hat = params.Get("hat", 0); 368 const int hat = params.Get("hat", 0);
@@ -434,11 +407,15 @@ public:
434 joystick->SetButton(button, false); 407 joystick->SetButton(button, false);
435 return std::make_unique<SDLButton>(joystick, button); 408 return std::make_unique<SDLButton>(joystick, button);
436 } 409 }
410
411private:
412 SDLState& state;
437}; 413};
438 414
439/// An analog device factory that creates analog devices from SDL joystick 415/// An analog device factory that creates analog devices from SDL joystick
440class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> { 416class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
441public: 417public:
418 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
442 /** 419 /**
443 * Creates analog device from joystick axes 420 * Creates analog device from joystick axes
444 * @param params contains parameters for creating the device: 421 * @param params contains parameters for creating the device:
@@ -453,37 +430,71 @@ public:
453 const int axis_x = params.Get("axis_x", 0); 430 const int axis_x = params.Get("axis_x", 0);
454 const int axis_y = params.Get("axis_y", 1); 431 const int axis_y = params.Get("axis_y", 1);
455 432
456 auto joystick = GetSDLJoystickByGUID(guid, port); 433 auto joystick = state.GetSDLJoystickByGUID(guid, port);
457 434
458 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 435 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
459 joystick->SetAxis(axis_x, 0); 436 joystick->SetAxis(axis_x, 0);
460 joystick->SetAxis(axis_y, 0); 437 joystick->SetAxis(axis_y, 0);
461 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y); 438 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y);
462 } 439 }
440
441private:
442 SDLState& state;
463}; 443};
464 444
465void Init() { 445SDLState::SDLState() {
466 using namespace Input; 446 using namespace Input;
467 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); 447 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
468 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); 448 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
469 polling = false; 449
450 // If the frontend is going to manage the event loop, then we dont start one here
451 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
452 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
453 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
454 return;
455 }
456
457 SDL_AddEventWatch(&SDLEventWatcher, this);
458
470 initialized = true; 459 initialized = true;
460 if (start_thread) {
461 poll_thread = std::thread([&] {
462 using namespace std::chrono_literals;
463 SDL_Event event;
464 while (initialized) {
465 SDL_PumpEvents();
466 std::this_thread::sleep_for(std::chrono::duration(10ms));
467 }
468 });
469 }
470 // Because the events for joystick connection happens before we have our event watcher added, we
471 // can just open all the joysticks right here
472 for (int i = 0; i < SDL_NumJoysticks(); ++i) {
473 InitJoystick(i);
474 }
471} 475}
472 476
473void Shutdown() { 477SDLState::~SDLState() {
474 if (initialized) { 478 using namespace Input;
475 using namespace Input; 479 UnregisterFactory<ButtonDevice>("sdl");
476 UnregisterFactory<ButtonDevice>("sdl"); 480 UnregisterFactory<AnalogDevice>("sdl");
477 UnregisterFactory<AnalogDevice>("sdl"); 481
478 initialized = false; 482 CloseJoysticks();
483 SDL_DelEventWatch(&SDLEventWatcher, this);
484
485 initialized = false;
486 if (start_thread) {
487 poll_thread.join();
488 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
479 } 489 }
480} 490}
481 491
482Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { 492Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
483 Common::ParamPackage params({{"engine", "sdl"}}); 493 Common::ParamPackage params({{"engine", "sdl"}});
494
484 switch (event.type) { 495 switch (event.type) {
485 case SDL_JOYAXISMOTION: { 496 case SDL_JOYAXISMOTION: {
486 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); 497 auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
487 params.Set("port", joystick->GetPort()); 498 params.Set("port", joystick->GetPort());
488 params.Set("guid", joystick->GetGUID()); 499 params.Set("guid", joystick->GetGUID());
489 params.Set("axis", event.jaxis.axis); 500 params.Set("axis", event.jaxis.axis);
@@ -497,14 +508,14 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
497 break; 508 break;
498 } 509 }
499 case SDL_JOYBUTTONUP: { 510 case SDL_JOYBUTTONUP: {
500 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); 511 auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
501 params.Set("port", joystick->GetPort()); 512 params.Set("port", joystick->GetPort());
502 params.Set("guid", joystick->GetGUID()); 513 params.Set("guid", joystick->GetGUID());
503 params.Set("button", event.jbutton.button); 514 params.Set("button", event.jbutton.button);
504 break; 515 break;
505 } 516 }
506 case SDL_JOYHATMOTION: { 517 case SDL_JOYHATMOTION: {
507 auto joystick = GetSDLJoystickBySDLID(event.jhat.which); 518 auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
508 params.Set("port", joystick->GetPort()); 519 params.Set("port", joystick->GetPort());
509 params.Set("guid", joystick->GetGUID()); 520 params.Set("guid", joystick->GetGUID());
510 params.Set("hat", event.jhat.hat); 521 params.Set("hat", event.jhat.hat);
@@ -534,21 +545,28 @@ namespace Polling {
534 545
535class SDLPoller : public InputCommon::Polling::DevicePoller { 546class SDLPoller : public InputCommon::Polling::DevicePoller {
536public: 547public:
548 explicit SDLPoller(SDLState& state_) : state(state_) {}
549
537 void Start() override { 550 void Start() override {
538 event_queue.Clear(); 551 state.event_queue.Clear();
539 polling = true; 552 state.polling = true;
540 } 553 }
541 554
542 void Stop() override { 555 void Stop() override {
543 polling = false; 556 state.polling = false;
544 } 557 }
558
559protected:
560 SDLState& state;
545}; 561};
546 562
547class SDLButtonPoller final : public SDLPoller { 563class SDLButtonPoller final : public SDLPoller {
548public: 564public:
565 explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
566
549 Common::ParamPackage GetNextInput() override { 567 Common::ParamPackage GetNextInput() override {
550 SDL_Event event; 568 SDL_Event event;
551 while (event_queue.Pop(event)) { 569 while (state.event_queue.Pop(event)) {
552 switch (event.type) { 570 switch (event.type) {
553 case SDL_JOYAXISMOTION: 571 case SDL_JOYAXISMOTION:
554 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 572 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
@@ -556,7 +574,7 @@ public:
556 } 574 }
557 case SDL_JOYBUTTONUP: 575 case SDL_JOYBUTTONUP:
558 case SDL_JOYHATMOTION: 576 case SDL_JOYHATMOTION:
559 return SDLEventToButtonParamPackage(event); 577 return SDLEventToButtonParamPackage(state, event);
560 } 578 }
561 } 579 }
562 return {}; 580 return {};
@@ -565,6 +583,8 @@ public:
565 583
566class SDLAnalogPoller final : public SDLPoller { 584class SDLAnalogPoller final : public SDLPoller {
567public: 585public:
586 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
587
568 void Start() override { 588 void Start() override {
569 SDLPoller::Start(); 589 SDLPoller::Start();
570 590
@@ -576,7 +596,7 @@ public:
576 596
577 Common::ParamPackage GetNextInput() override { 597 Common::ParamPackage GetNextInput() override {
578 SDL_Event event; 598 SDL_Event event;
579 while (event_queue.Pop(event)) { 599 while (state.event_queue.Pop(event)) {
580 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 600 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
581 continue; 601 continue;
582 } 602 }
@@ -593,7 +613,7 @@ public:
593 } 613 }
594 Common::ParamPackage params; 614 Common::ParamPackage params;
595 if (analog_xaxis != -1 && analog_yaxis != -1) { 615 if (analog_xaxis != -1 && analog_yaxis != -1) {
596 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); 616 auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
597 params.Set("engine", "sdl"); 617 params.Set("engine", "sdl");
598 params.Set("port", joystick->GetPort()); 618 params.Set("port", joystick->GetPort());
599 params.Set("guid", joystick->GetGUID()); 619 params.Set("guid", joystick->GetGUID());
@@ -612,18 +632,21 @@ private:
612 int analog_yaxis = -1; 632 int analog_yaxis = -1;
613 SDL_JoystickID analog_axes_joystick = -1; 633 SDL_JoystickID analog_axes_joystick = -1;
614}; 634};
635} // namespace Polling
615 636
616void GetPollers(InputCommon::Polling::DeviceType type, 637std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers(
617 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>>& pollers) { 638 InputCommon::Polling::DeviceType type) {
639 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
618 switch (type) { 640 switch (type) {
619 case InputCommon::Polling::DeviceType::Analog: 641 case InputCommon::Polling::DeviceType::Analog:
620 pollers.emplace_back(std::make_unique<SDLAnalogPoller>()); 642 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
621 break; 643 break;
622 case InputCommon::Polling::DeviceType::Button: 644 case InputCommon::Polling::DeviceType::Button:
623 pollers.emplace_back(std::make_unique<SDLButtonPoller>()); 645 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
624 break; 646 break;
647 return pollers;
625 } 648 }
626} 649}
627} // namespace Polling 650
628} // namespace SDL 651} // namespace SDL
629} // namespace InputCommon 652} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index c152fa747..fec82fbe6 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -1,51 +1,64 @@
1// Copyright 2017 Citra Emulator Project 1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <vector> 9#include <thread>
9#include "core/frontend/input.h" 10#include "common/threadsafe_queue.h"
11#include "input_common/sdl/sdl.h"
10 12
11union SDL_Event; 13union SDL_Event;
12namespace Common { 14using SDL_Joystick = struct _SDL_Joystick;
13class ParamPackage; 15using SDL_JoystickID = s32;
14}
15namespace InputCommon {
16namespace Polling {
17class DevicePoller;
18enum class DeviceType;
19} // namespace Polling
20} // namespace InputCommon
21 16
22namespace InputCommon { 17namespace InputCommon::SDL {
23namespace SDL {
24 18
25/// Initializes and registers SDL device factories 19class SDLJoystick;
26void Init(); 20class SDLButtonFactory;
21class SDLAnalogFactory;
27 22
28/// Unresisters SDL device factories and shut them down. 23class SDLState : public State {
29void Shutdown(); 24public:
25 /// Initializes and registers SDL device factories
26 SDLState();
30 27
31/// Needs to be called before SDL_QuitSubSystem. 28 /// Unresisters SDL device factories and shut them down.
32void CloseSDLJoysticks(); 29 ~SDLState() override;
33 30
34/// Handle SDL_Events for joysticks from SDL_PollEvent 31 /// Handle SDL_Events for joysticks from SDL_PollEvent
35void HandleGameControllerEvent(const SDL_Event& event); 32 void HandleGameControllerEvent(const SDL_Event& event);
36 33
37/// A Loop that calls HandleGameControllerEvent until Shutdown is called 34 std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
38void PollLoop(); 35 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
39 36
40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 37 /// Get all DevicePoller that use the SDL backend for a specific device type
41Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); 38 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
39 InputCommon::Polling::DeviceType type) override;
42 40
43namespace Polling { 41 /// Used by the Pollers during config
42 std::atomic<bool> polling = false;
43 Common::SPSCQueue<SDL_Event> event_queue;
44 44
45/// Get all DevicePoller that use the SDL backend for a specific device type 45private:
46void GetPollers(InputCommon::Polling::DeviceType type, 46 void InitJoystick(int joystick_index);
47 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>>& pollers); 47 void CloseJoystick(SDL_Joystick* sdl_joystick);
48 48
49} // namespace Polling 49 /// Needs to be called before SDL_QuitSubSystem.
50} // namespace SDL 50 void CloseJoysticks();
51} // namespace InputCommon 51
52 /// Map of GUID of a list of corresponding virtual Joysticks
53 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
54 std::mutex joystick_map_mutex;
55
56 std::shared_ptr<SDLButtonFactory> button_factory;
57 std::shared_ptr<SDLAnalogFactory> analog_factory;
58
59 bool start_thread = false;
60 std::atomic<bool> initialized = false;
61
62 std::thread poll_thread;
63};
64} // namespace InputCommon::SDL
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 73b04b749..b2a087aa5 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -121,7 +121,6 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
121 setAttribute(Qt::WA_AcceptTouchEvents); 121 setAttribute(Qt::WA_AcceptTouchEvents);
122 122
123 InputCommon::Init(); 123 InputCommon::Init();
124 InputCommon::StartJoystickEventHandler();
125 connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent), 124 connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
126 &GMainWindow::OnLoadComplete); 125 &GMainWindow::OnLoadComplete);
127} 126}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 7df8eff53..de7a26e14 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -135,16 +135,16 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
135} 135}
136 136
137EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { 137EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
138 InputCommon::Init();
139
140 SDL_SetMainReady();
141
142 // Initialize the window 138 // Initialize the window
143 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 139 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
144 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 140 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
145 exit(1); 141 exit(1);
146 } 142 }
147 143
144 InputCommon::Init();
145
146 SDL_SetMainReady();
147
148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
149 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 149 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
150 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 150 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
@@ -201,11 +201,9 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
201} 201}
202 202
203EmuWindow_SDL2::~EmuWindow_SDL2() { 203EmuWindow_SDL2::~EmuWindow_SDL2() {
204 InputCommon::SDL::CloseSDLJoysticks(); 204 InputCommon::Shutdown();
205 SDL_GL_DeleteContext(gl_context); 205 SDL_GL_DeleteContext(gl_context);
206 SDL_Quit(); 206 SDL_Quit();
207
208 InputCommon::Shutdown();
209} 207}
210 208
211void EmuWindow_SDL2::SwapBuffers() { 209void EmuWindow_SDL2::SwapBuffers() {
@@ -262,7 +260,6 @@ void EmuWindow_SDL2::PollEvents() {
262 is_open = false; 260 is_open = false;
263 break; 261 break;
264 default: 262 default:
265 InputCommon::SDL::HandleGameControllerEvent(event);
266 break; 263 break;
267 } 264 }
268 } 265 }