summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/input_common/main.cpp12
-rw-r--r--src/input_common/main.h2
-rw-r--r--src/input_common/sdl/sdl.cpp387
-rw-r--r--src/input_common/sdl/sdl.h9
-rw-r--r--src/yuzu/bootmanager.cpp1
-rw-r--r--src/yuzu/configuration/configure_input.cpp22
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp7
7 files changed, 339 insertions, 101 deletions
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b12623d55..37f572853 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <thread>
6#include "common/param_package.h" 7#include "common/param_package.h"
7#include "input_common/analog_from_button.h" 8#include "input_common/analog_from_button.h"
8#include "input_common/keyboard.h" 9#include "input_common/keyboard.h"
@@ -17,6 +18,10 @@ namespace InputCommon {
17static std::shared_ptr<Keyboard> keyboard; 18static std::shared_ptr<Keyboard> keyboard;
18static std::shared_ptr<MotionEmu> motion_emu; 19static std::shared_ptr<MotionEmu> motion_emu;
19 20
21#ifdef HAVE_SDL2
22static std::thread poll_thread;
23#endif
24
20void Init() { 25void Init() {
21 keyboard = std::make_shared<Keyboard>(); 26 keyboard = std::make_shared<Keyboard>();
22 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 27 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -30,6 +35,12 @@ void Init() {
30#endif 35#endif
31} 36}
32 37
38void StartJoystickEventHandler() {
39#ifdef HAVE_SDL2
40 poll_thread = std::thread(SDL::PollLoop);
41#endif
42}
43
33void Shutdown() { 44void Shutdown() {
34 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 45 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
35 keyboard.reset(); 46 keyboard.reset();
@@ -39,6 +50,7 @@ void Shutdown() {
39 50
40#ifdef HAVE_SDL2 51#ifdef HAVE_SDL2
41 SDL::Shutdown(); 52 SDL::Shutdown();
53 poll_thread.join();
42#endif 54#endif
43} 55}
44 56
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 77a0ce90b..9eb13106e 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -20,6 +20,8 @@ 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
23class Keyboard; 25class Keyboard;
24 26
25/// Gets the keyboard button device factory. 27/// Gets the keyboard button device factory.
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index d1b960fd7..faf3c1fa3 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -2,15 +2,24 @@
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>
5#include <cmath> 7#include <cmath>
8#include <functional>
9#include <iterator>
10#include <mutex>
6#include <string> 11#include <string>
12#include <thread>
7#include <tuple> 13#include <tuple>
8#include <unordered_map> 14#include <unordered_map>
9#include <utility> 15#include <utility>
16#include <vector>
10#include <SDL.h> 17#include <SDL.h>
18#include "common/assert.h"
11#include "common/logging/log.h" 19#include "common/logging/log.h"
12#include "common/math_util.h" 20#include "common/math_util.h"
13#include "common/param_package.h" 21#include "common/param_package.h"
22#include "common/threadsafe_queue.h"
14#include "input_common/main.h" 23#include "input_common/main.h"
15#include "input_common/sdl/sdl.h" 24#include "input_common/sdl/sdl.h"
16 25
@@ -21,33 +30,51 @@ namespace SDL {
21class SDLJoystick; 30class SDLJoystick;
22class SDLButtonFactory; 31class SDLButtonFactory;
23class SDLAnalogFactory; 32class SDLAnalogFactory;
24static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list; 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
25static std::shared_ptr<SDLButtonFactory> button_factory; 38static std::shared_ptr<SDLButtonFactory> button_factory;
26static std::shared_ptr<SDLAnalogFactory> analog_factory; 39static std::shared_ptr<SDLAnalogFactory> analog_factory;
27 40
28static bool initialized = false; 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}
29 53
30class SDLJoystick { 54class SDLJoystick {
31public: 55public:
32 explicit SDLJoystick(int joystick_index) 56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
33 : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { 57 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
34 if (!joystick) { 58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
35 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 59
36 } 60 void SetButton(int button, bool value) {
61 std::lock_guard<std::mutex> lock(mutex);
62 state.buttons[button] = value;
37 } 63 }
38 64
39 bool GetButton(int button) const { 65 bool GetButton(int button) const {
40 if (!joystick) 66 std::lock_guard<std::mutex> lock(mutex);
41 return {}; 67 return state.buttons.at(button);
42 SDL_JoystickUpdate(); 68 }
43 return SDL_JoystickGetButton(joystick.get(), button) == 1; 69
70 void SetAxis(int axis, Sint16 value) {
71 std::lock_guard<std::mutex> lock(mutex);
72 state.axes[axis] = value;
44 } 73 }
45 74
46 float GetAxis(int axis) const { 75 float GetAxis(int axis) const {
47 if (!joystick) 76 std::lock_guard<std::mutex> lock(mutex);
48 return {}; 77 return state.axes.at(axis) / 32767.0f;
49 SDL_JoystickUpdate();
50 return SDL_JoystickGetAxis(joystick.get(), axis) / 32767.0f;
51 } 78 }
52 79
53 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { 80 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
@@ -67,18 +94,213 @@ public:
67 return std::make_tuple(x, y); 94 return std::make_tuple(x, y);
68 } 95 }
69 96
97 void SetHat(int hat, Uint8 direction) {
98 std::lock_guard<std::mutex> lock(mutex);
99 state.hats[hat] = direction;
100 }
101
70 bool GetHatDirection(int hat, Uint8 direction) const { 102 bool GetHatDirection(int hat, Uint8 direction) const {
71 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; 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();
72 } 122 }
73 123
74 SDL_JoystickID GetJoystickID() const { 124 void SetSDLJoystick(SDL_Joystick* joystick,
75 return SDL_JoystickInstanceID(joystick.get()); 125 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
126 sdl_joystick =
127 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
76 } 128 }
77 129
78private: 130private:
79 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; 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;
80}; 140};
81 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
82class SDLButton final : public Input::ButtonDevice { 304class SDLButton final : public Input::ButtonDevice {
83public: 305public:
84 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) 306 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
@@ -144,22 +366,14 @@ private:
144 int axis_y; 366 int axis_y;
145}; 367};
146 368
147static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) {
148 std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock();
149 if (!joystick) {
150 joystick = std::make_shared<SDLJoystick>(joystick_index);
151 joystick_list[joystick_index] = joystick;
152 }
153 return joystick;
154}
155
156/// A button device factory that creates button devices from SDL joystick 369/// A button device factory that creates button devices from SDL joystick
157class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { 370class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
158public: 371public:
159 /** 372 /**
160 * Creates a button device from a joystick button 373 * Creates a button device from a joystick button
161 * @param params contains parameters for creating the device: 374 * @param params contains parameters for creating the device:
162 * - "joystick": the index of the joystick to bind 375 * - "guid": the guid of the joystick to bind
376 * - "port": the nth joystick of the same type to bind
163 * - "button"(optional): the index of the button to bind 377 * - "button"(optional): the index of the button to bind
164 * - "hat"(optional): the index of the hat to bind as direction buttons 378 * - "hat"(optional): the index of the hat to bind as direction buttons
165 * - "axis"(optional): the index of the axis to bind 379 * - "axis"(optional): the index of the axis to bind
@@ -167,12 +381,15 @@ public:
167 * "down", "left" or "right" 381 * "down", "left" or "right"
168 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is 382 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
169 * triggered if the axis value crosses 383 * triggered if the axis value crosses
170 * - "direction"(only used for axis): "+" means the button is triggered when the axis value 384 * - "direction"(only used for axis): "+" means the button is triggered when the axis
171 * is greater than the threshold; "-" means the button is triggered when the axis value 385 * value is greater than the threshold; "-" means the button is triggered when the axis
172 * is smaller than the threshold 386 * value is smaller than the threshold
173 */ 387 */
174 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { 388 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
175 const int joystick_index = params.Get("joystick", 0); 389 const std::string guid = params.Get("guid", "0");
390 const int port = params.Get("port", 0);
391
392 auto joystick = GetSDLJoystickByGUID(guid, port);
176 393
177 if (params.Has("hat")) { 394 if (params.Has("hat")) {
178 const int hat = params.Get("hat", 0); 395 const int hat = params.Get("hat", 0);
@@ -189,8 +406,9 @@ public:
189 } else { 406 } else {
190 direction = 0; 407 direction = 0;
191 } 408 }
192 return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat, 409 // This is necessary so accessing GetHat with hat won't crash
193 direction); 410 joystick->SetHat(hat, SDL_HAT_CENTERED);
411 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
194 } 412 }
195 413
196 if (params.Has("axis")) { 414 if (params.Has("axis")) {
@@ -206,12 +424,15 @@ public:
206 trigger_if_greater = true; 424 trigger_if_greater = true;
207 LOG_ERROR(Input, "Unknown direction '{}'", direction_name); 425 LOG_ERROR(Input, "Unknown direction '{}'", direction_name);
208 } 426 }
209 return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold, 427 // This is necessary so accessing GetAxis with axis won't crash
210 trigger_if_greater); 428 joystick->SetAxis(axis, 0);
429 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
211 } 430 }
212 431
213 const int button = params.Get("button", 0); 432 const int button = params.Get("button", 0);
214 return std::make_unique<SDLButton>(GetJoystick(joystick_index), button); 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);
215 } 436 }
216}; 437};
217 438
@@ -221,27 +442,32 @@ public:
221 /** 442 /**
222 * Creates analog device from joystick axes 443 * Creates analog device from joystick axes
223 * @param params contains parameters for creating the device: 444 * @param params contains parameters for creating the device:
224 * - "joystick": the index of the joystick to bind 445 * - "guid": the guid of the joystick to bind
446 * - "port": the nth joystick of the same type
225 * - "axis_x": the index of the axis to be bind as x-axis 447 * - "axis_x": the index of the axis to be bind as x-axis
226 * - "axis_y": the index of the axis to be bind as y-axis 448 * - "axis_y": the index of the axis to be bind as y-axis
227 */ 449 */
228 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { 450 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
229 const int joystick_index = params.Get("joystick", 0); 451 const std::string guid = params.Get("guid", "0");
452 const int port = params.Get("port", 0);
230 const int axis_x = params.Get("axis_x", 0); 453 const int axis_x = params.Get("axis_x", 0);
231 const int axis_y = params.Get("axis_y", 1); 454 const int axis_y = params.Get("axis_y", 1);
232 return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y); 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);
233 } 462 }
234}; 463};
235 464
236void Init() { 465void Init() {
237 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { 466 using namespace Input;
238 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 467 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
239 } else { 468 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
240 using namespace Input; 469 polling = false;
241 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); 470 initialized = true;
242 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
243 initialized = true;
244 }
245} 471}
246 472
247void Shutdown() { 473void Shutdown() {
@@ -249,30 +475,17 @@ void Shutdown() {
249 using namespace Input; 475 using namespace Input;
250 UnregisterFactory<ButtonDevice>("sdl"); 476 UnregisterFactory<ButtonDevice>("sdl");
251 UnregisterFactory<AnalogDevice>("sdl"); 477 UnregisterFactory<AnalogDevice>("sdl");
252 SDL_QuitSubSystem(SDL_INIT_JOYSTICK); 478 initialized = false;
253 } 479 }
254} 480}
255 481
256/**
257 * This function converts a joystick ID used in SDL events to the device index. This is necessary
258 * because Citra opens joysticks using their indices, not their IDs.
259 */
260static int JoystickIDToDeviceIndex(SDL_JoystickID id) {
261 int num_joysticks = SDL_NumJoysticks();
262 for (int i = 0; i < num_joysticks; i++) {
263 auto joystick = GetJoystick(i);
264 if (joystick->GetJoystickID() == id) {
265 return i;
266 }
267 }
268 return -1;
269}
270
271Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { 482Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
272 Common::ParamPackage params({{"engine", "sdl"}}); 483 Common::ParamPackage params({{"engine", "sdl"}});
273 switch (event.type) { 484 switch (event.type) {
274 case SDL_JOYAXISMOTION: 485 case SDL_JOYAXISMOTION: {
275 params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which)); 486 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
487 params.Set("port", joystick->GetPort());
488 params.Set("guid", joystick->GetGUID());
276 params.Set("axis", event.jaxis.axis); 489 params.Set("axis", event.jaxis.axis);
277 if (event.jaxis.value > 0) { 490 if (event.jaxis.value > 0) {
278 params.Set("direction", "+"); 491 params.Set("direction", "+");
@@ -282,12 +495,18 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
282 params.Set("threshold", "-0.5"); 495 params.Set("threshold", "-0.5");
283 } 496 }
284 break; 497 break;
285 case SDL_JOYBUTTONUP: 498 }
286 params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which)); 499 case SDL_JOYBUTTONUP: {
500 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
501 params.Set("port", joystick->GetPort());
502 params.Set("guid", joystick->GetGUID());
287 params.Set("button", event.jbutton.button); 503 params.Set("button", event.jbutton.button);
288 break; 504 break;
289 case SDL_JOYHATMOTION: 505 }
290 params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which)); 506 case SDL_JOYHATMOTION: {
507 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
508 params.Set("port", joystick->GetPort());
509 params.Set("guid", joystick->GetGUID());
291 params.Set("hat", event.jhat.hat); 510 params.Set("hat", event.jhat.hat);
292 switch (event.jhat.value) { 511 switch (event.jhat.value) {
293 case SDL_HAT_UP: 512 case SDL_HAT_UP:
@@ -307,6 +526,7 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
307 } 526 }
308 break; 527 break;
309 } 528 }
529 }
310 return params; 530 return params;
311} 531}
312 532
@@ -315,31 +535,20 @@ namespace Polling {
315class SDLPoller : public InputCommon::Polling::DevicePoller { 535class SDLPoller : public InputCommon::Polling::DevicePoller {
316public: 536public:
317 void Start() override { 537 void Start() override {
318 // SDL joysticks must be opened, otherwise they don't generate events 538 event_queue.Clear();
319 SDL_JoystickUpdate(); 539 polling = true;
320 int num_joysticks = SDL_NumJoysticks();
321 for (int i = 0; i < num_joysticks; i++) {
322 joysticks_opened.emplace_back(GetJoystick(i));
323 }
324 // Empty event queue to get rid of old events. citra-qt doesn't use the queue
325 SDL_Event dummy;
326 while (SDL_PollEvent(&dummy)) {
327 }
328 } 540 }
329 541
330 void Stop() override { 542 void Stop() override {
331 joysticks_opened.clear(); 543 polling = false;
332 } 544 }
333
334private:
335 std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened;
336}; 545};
337 546
338class SDLButtonPoller final : public SDLPoller { 547class SDLButtonPoller final : public SDLPoller {
339public: 548public:
340 Common::ParamPackage GetNextInput() override { 549 Common::ParamPackage GetNextInput() override {
341 SDL_Event event; 550 SDL_Event event;
342 while (SDL_PollEvent(&event)) { 551 while (event_queue.Pop(event)) {
343 switch (event.type) { 552 switch (event.type) {
344 case SDL_JOYAXISMOTION: 553 case SDL_JOYAXISMOTION:
345 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 554 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
@@ -367,7 +576,7 @@ public:
367 576
368 Common::ParamPackage GetNextInput() override { 577 Common::ParamPackage GetNextInput() override {
369 SDL_Event event; 578 SDL_Event event;
370 while (SDL_PollEvent(&event)) { 579 while (event_queue.Pop(event)) {
371 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 580 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
372 continue; 581 continue;
373 } 582 }
@@ -384,8 +593,10 @@ public:
384 } 593 }
385 Common::ParamPackage params; 594 Common::ParamPackage params;
386 if (analog_xaxis != -1 && analog_yaxis != -1) { 595 if (analog_xaxis != -1 && analog_yaxis != -1) {
596 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
387 params.Set("engine", "sdl"); 597 params.Set("engine", "sdl");
388 params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick)); 598 params.Set("port", joystick->GetPort());
599 params.Set("guid", joystick->GetGUID());
389 params.Set("axis_x", analog_xaxis); 600 params.Set("axis_x", analog_xaxis);
390 params.Set("axis_y", analog_yaxis); 601 params.Set("axis_y", analog_yaxis);
391 analog_xaxis = -1; 602 analog_xaxis = -1;
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 7934099d4..0206860d3 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -28,6 +28,15 @@ void Init();
28/// Unresisters SDL device factories and shut them down. 28/// Unresisters SDL device factories and shut them down.
29void Shutdown(); 29void Shutdown();
30 30
31/// Needs to be called before SDL_QuitSubSystem.
32void CloseSDLJoysticks();
33
34/// Handle SDL_Events for joysticks from SDL_PollEvent
35void HandleGameControllerEvent(const SDL_Event& event);
36
37/// A Loop that calls HandleGameControllerEvent until Shutdown is called
38void PollLoop();
39
31/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
32Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); 41Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event);
33 42
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 4a60f450a..4e4c108ab 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -112,6 +112,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
112 setWindowTitle(QString::fromStdString(window_title)); 112 setWindowTitle(QString::fromStdString(window_title));
113 113
114 InputCommon::Init(); 114 InputCommon::Init();
115 InputCommon::StartJoystickEventHandler();
115} 116}
116 117
117GRenderWindow::~GRenderWindow() { 118GRenderWindow::~GRenderWindow() {
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 5e7badedf..d29abb74b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -1,4 +1,4 @@
1// Copyright 2016 Citra Emulator Project 1// Copyright 2016 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
@@ -53,19 +53,18 @@ static QString ButtonToText(const Common::ParamPackage& param) {
53 } else if (param.Get("engine", "") == "keyboard") { 53 } else if (param.Get("engine", "") == "keyboard") {
54 return getKeyName(param.Get("code", 0)); 54 return getKeyName(param.Get("code", 0));
55 } else if (param.Get("engine", "") == "sdl") { 55 } else if (param.Get("engine", "") == "sdl") {
56 QString text = QString(QObject::tr("Joystick %1")).arg(param.Get("joystick", "").c_str());
57 if (param.Has("hat")) { 56 if (param.Has("hat")) {
58 text += QString(QObject::tr(" Hat %1 %2")) 57 return QString(QObject::tr("Hat %1 %2"))
59 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); 58 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
60 } 59 }
61 if (param.Has("axis")) { 60 if (param.Has("axis")) {
62 text += QString(QObject::tr(" Axis %1%2")) 61 return QString(QObject::tr("Axis %1%2"))
63 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); 62 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
64 } 63 }
65 if (param.Has("button")) { 64 if (param.Has("button")) {
66 text += QString(QObject::tr(" Button %1")).arg(param.Get("button", "").c_str()); 65 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
67 } 66 }
68 return text; 67 return QString();
69 } else { 68 } else {
70 return QObject::tr("[unknown]"); 69 return QObject::tr("[unknown]");
71 } 70 }
@@ -81,13 +80,12 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
81 return QString(QObject::tr("[unused]")); 80 return QString(QObject::tr("[unused]"));
82 } 81 }
83 82
84 QString text = QString(QObject::tr("Joystick %1")).arg(param.Get("joystick", "").c_str());
85 if (dir == "left" || dir == "right") { 83 if (dir == "left" || dir == "right") {
86 text += QString(QObject::tr(" Axis %1")).arg(param.Get("axis_x", "").c_str()); 84 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
87 } else if (dir == "up" || dir == "down") { 85 } else if (dir == "up" || dir == "down") {
88 text += QString(QObject::tr(" Axis %1")).arg(param.Get("axis_y", "").c_str()); 86 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
89 } 87 }
90 return text; 88 return QString();
91 } else { 89 } else {
92 return QObject::tr("[unknown]"); 90 return QObject::tr("[unknown]");
93 } 91 }
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 2f7916256..1c4717123 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -16,6 +16,7 @@
16#include "input_common/keyboard.h" 16#include "input_common/keyboard.h"
17#include "input_common/main.h" 17#include "input_common/main.h"
18#include "input_common/motion_emu.h" 18#include "input_common/motion_emu.h"
19#include "input_common/sdl/sdl.h"
19#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 20#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
20 21
21void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@@ -116,7 +117,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
116 SDL_SetMainReady(); 117 SDL_SetMainReady();
117 118
118 // Initialize the window 119 // Initialize the window
119 if (SDL_Init(SDL_INIT_VIDEO) < 0) { 120 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
120 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 121 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
121 exit(1); 122 exit(1);
122 } 123 }
@@ -176,6 +177,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
176} 177}
177 178
178EmuWindow_SDL2::~EmuWindow_SDL2() { 179EmuWindow_SDL2::~EmuWindow_SDL2() {
180 InputCommon::SDL::CloseSDLJoysticks();
179 SDL_GL_DeleteContext(gl_context); 181 SDL_GL_DeleteContext(gl_context);
180 SDL_Quit(); 182 SDL_Quit();
181 183
@@ -220,6 +222,9 @@ void EmuWindow_SDL2::PollEvents() {
220 case SDL_QUIT: 222 case SDL_QUIT:
221 is_open = false; 223 is_open = false;
222 break; 224 break;
225 default:
226 InputCommon::SDL::HandleGameControllerEvent(event);
227 break;
223 } 228 }
224 } 229 }
225} 230}