summaryrefslogtreecommitdiff
path: root/src/input_common/sdl/sdl_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/sdl/sdl_impl.cpp')
-rw-r--r--src/input_common/sdl/sdl_impl.cpp193
1 files changed, 108 insertions, 85 deletions
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