|  |  |  | @@ -2,15 +2,24 @@ | 
		
	
		
			
				|  |  |  |  | // Licensed under GPLv2 or any later version | 
		
	
		
			
				|  |  |  |  | // Refer to the license.txt file included. | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | #include <algorithm> | 
		
	
		
			
				|  |  |  |  | #include <atomic> | 
		
	
		
			
				|  |  |  |  | #include <cmath> | 
		
	
		
			
				|  |  |  |  | #include <functional> | 
		
	
		
			
				|  |  |  |  | #include <iterator> | 
		
	
		
			
				|  |  |  |  | #include <mutex> | 
		
	
		
			
				|  |  |  |  | #include <string> | 
		
	
		
			
				|  |  |  |  | #include <thread> | 
		
	
		
			
				|  |  |  |  | #include <tuple> | 
		
	
		
			
				|  |  |  |  | #include <unordered_map> | 
		
	
		
			
				|  |  |  |  | #include <utility> | 
		
	
		
			
				|  |  |  |  | #include <vector> | 
		
	
		
			
				|  |  |  |  | #include <SDL.h> | 
		
	
		
			
				|  |  |  |  | #include "common/assert.h" | 
		
	
		
			
				|  |  |  |  | #include "common/logging/log.h" | 
		
	
		
			
				|  |  |  |  | #include "common/math_util.h" | 
		
	
		
			
				|  |  |  |  | #include "common/param_package.h" | 
		
	
		
			
				|  |  |  |  | #include "common/threadsafe_queue.h" | 
		
	
		
			
				|  |  |  |  | #include "input_common/main.h" | 
		
	
		
			
				|  |  |  |  | #include "input_common/sdl/sdl.h" | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -21,33 +30,51 @@ namespace SDL { | 
		
	
		
			
				|  |  |  |  | class SDLJoystick; | 
		
	
		
			
				|  |  |  |  | class SDLButtonFactory; | 
		
	
		
			
				|  |  |  |  | class SDLAnalogFactory; | 
		
	
		
			
				|  |  |  |  | static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// Map of GUID of a list of corresponding virtual Joysticks | 
		
	
		
			
				|  |  |  |  | static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; | 
		
	
		
			
				|  |  |  |  | static std::mutex joystick_map_mutex; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static std::shared_ptr<SDLButtonFactory> button_factory; | 
		
	
		
			
				|  |  |  |  | static std::shared_ptr<SDLAnalogFactory> analog_factory; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static bool initialized = false; | 
		
	
		
			
				|  |  |  |  | /// Used by the Pollers during config | 
		
	
		
			
				|  |  |  |  | static std::atomic<bool> polling; | 
		
	
		
			
				|  |  |  |  | static Common::SPSCQueue<SDL_Event> event_queue; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static std::atomic<bool> initialized = false; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static std::string GetGUID(SDL_Joystick* joystick) { | 
		
	
		
			
				|  |  |  |  |     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); | 
		
	
		
			
				|  |  |  |  |     char guid_str[33]; | 
		
	
		
			
				|  |  |  |  |     SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); | 
		
	
		
			
				|  |  |  |  |     return guid_str; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | class SDLJoystick { | 
		
	
		
			
				|  |  |  |  | public: | 
		
	
		
			
				|  |  |  |  |     explicit SDLJoystick(int joystick_index) | 
		
	
		
			
				|  |  |  |  |         : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { | 
		
	
		
			
				|  |  |  |  |         if (!joystick) { | 
		
	
		
			
				|  |  |  |  |             LOG_ERROR(Input, "failed to open joystick {}", joystick_index); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, | 
		
	
		
			
				|  |  |  |  |                 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) | 
		
	
		
			
				|  |  |  |  |         : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {} | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     void SetButton(int button, bool value) { | 
		
	
		
			
				|  |  |  |  |         std::lock_guard<std::mutex> lock(mutex); | 
		
	
		
			
				|  |  |  |  |         state.buttons[button] = value; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     bool GetButton(int button) const { | 
		
	
		
			
				|  |  |  |  |         if (!joystick) | 
		
	
		
			
				|  |  |  |  |             return {}; | 
		
	
		
			
				|  |  |  |  |         SDL_JoystickUpdate(); | 
		
	
		
			
				|  |  |  |  |         return SDL_JoystickGetButton(joystick.get(), button) == 1; | 
		
	
		
			
				|  |  |  |  |         std::lock_guard<std::mutex> lock(mutex); | 
		
	
		
			
				|  |  |  |  |         return state.buttons.at(button); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     void SetAxis(int axis, Sint16 value) { | 
		
	
		
			
				|  |  |  |  |         std::lock_guard<std::mutex> lock(mutex); | 
		
	
		
			
				|  |  |  |  |         state.axes[axis] = value; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     float GetAxis(int axis) const { | 
		
	
		
			
				|  |  |  |  |         if (!joystick) | 
		
	
		
			
				|  |  |  |  |             return {}; | 
		
	
		
			
				|  |  |  |  |         SDL_JoystickUpdate(); | 
		
	
		
			
				|  |  |  |  |         return SDL_JoystickGetAxis(joystick.get(), axis) / 32767.0f; | 
		
	
		
			
				|  |  |  |  |         std::lock_guard<std::mutex> lock(mutex); | 
		
	
		
			
				|  |  |  |  |         return state.axes.at(axis) / 32767.0f; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -67,18 +94,213 @@ public: | 
		
	
		
			
				|  |  |  |  |         return std::make_tuple(x, y); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     bool GetHatDirection(int hat, Uint8 direction) const { | 
		
	
		
			
				|  |  |  |  |         return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; | 
		
	
		
			
				|  |  |  |  |     void SetHat(int hat, Uint8 direction) { | 
		
	
		
			
				|  |  |  |  |         std::lock_guard<std::mutex> lock(mutex); | 
		
	
		
			
				|  |  |  |  |         state.hats[hat] = direction; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     SDL_JoystickID GetJoystickID() const { | 
		
	
		
			
				|  |  |  |  |         return SDL_JoystickInstanceID(joystick.get()); | 
		
	
		
			
				|  |  |  |  |     bool GetHatDirection(int hat, Uint8 direction) const { | 
		
	
		
			
				|  |  |  |  |         std::lock_guard<std::mutex> lock(mutex); | 
		
	
		
			
				|  |  |  |  |         return (state.hats.at(hat) & direction) != 0; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * The guid of the joystick | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     const std::string& GetGUID() const { | 
		
	
		
			
				|  |  |  |  |         return guid; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * The number of joystick from the same type that were connected before this joystick | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     int GetPort() const { | 
		
	
		
			
				|  |  |  |  |         return port; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     SDL_Joystick* GetSDLJoystick() const { | 
		
	
		
			
				|  |  |  |  |         return sdl_joystick.get(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     void SetSDLJoystick(SDL_Joystick* joystick, | 
		
	
		
			
				|  |  |  |  |                         decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) { | 
		
	
		
			
				|  |  |  |  |         sdl_joystick = | 
		
	
		
			
				|  |  |  |  |             std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | private: | 
		
	
		
			
				|  |  |  |  |     std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; | 
		
	
		
			
				|  |  |  |  |     struct State { | 
		
	
		
			
				|  |  |  |  |         std::unordered_map<int, bool> buttons; | 
		
	
		
			
				|  |  |  |  |         std::unordered_map<int, Sint16> axes; | 
		
	
		
			
				|  |  |  |  |         std::unordered_map<int, Uint8> hats; | 
		
	
		
			
				|  |  |  |  |     } state; | 
		
	
		
			
				|  |  |  |  |     std::string guid; | 
		
	
		
			
				|  |  |  |  |     int port; | 
		
	
		
			
				|  |  |  |  |     std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; | 
		
	
		
			
				|  |  |  |  |     mutable std::mutex mutex; | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /** | 
		
	
		
			
				|  |  |  |  |  * Get the nth joystick with the corresponding GUID | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) { | 
		
	
		
			
				|  |  |  |  |     std::lock_guard<std::mutex> lock(joystick_map_mutex); | 
		
	
		
			
				|  |  |  |  |     const auto it = joystick_map.find(guid); | 
		
	
		
			
				|  |  |  |  |     if (it != joystick_map.end()) { | 
		
	
		
			
				|  |  |  |  |         while (it->second.size() <= port) { | 
		
	
		
			
				|  |  |  |  |             auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr, | 
		
	
		
			
				|  |  |  |  |                                                           [](SDL_Joystick*) {}); | 
		
	
		
			
				|  |  |  |  |             it->second.emplace_back(std::move(joystick)); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return it->second[port]; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {}); | 
		
	
		
			
				|  |  |  |  |     return joystick_map[guid].emplace_back(std::move(joystick)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /** | 
		
	
		
			
				|  |  |  |  |  * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie | 
		
	
		
			
				|  |  |  |  |  * it to a SDLJoystick with the same guid and that port | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { | 
		
	
		
			
				|  |  |  |  |     std::lock_guard<std::mutex> lock(joystick_map_mutex); | 
		
	
		
			
				|  |  |  |  |     auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); | 
		
	
		
			
				|  |  |  |  |     const std::string guid = GetGUID(sdl_joystick); | 
		
	
		
			
				|  |  |  |  |     auto map_it = joystick_map.find(guid); | 
		
	
		
			
				|  |  |  |  |     if (map_it != joystick_map.end()) { | 
		
	
		
			
				|  |  |  |  |         auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), | 
		
	
		
			
				|  |  |  |  |                                    [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { | 
		
	
		
			
				|  |  |  |  |                                        return sdl_joystick == joystick->GetSDLJoystick(); | 
		
	
		
			
				|  |  |  |  |                                    }); | 
		
	
		
			
				|  |  |  |  |         if (vec_it != map_it->second.end()) { | 
		
	
		
			
				|  |  |  |  |             // This is the common case: There is already an existing SDL_Joystick maped to a | 
		
	
		
			
				|  |  |  |  |             // SDLJoystick. return the SDLJoystick | 
		
	
		
			
				|  |  |  |  |             return *vec_it; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         // Search for a SDLJoystick without a mapped SDL_Joystick... | 
		
	
		
			
				|  |  |  |  |         auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), | 
		
	
		
			
				|  |  |  |  |                                        [](const std::shared_ptr<SDLJoystick>& joystick) { | 
		
	
		
			
				|  |  |  |  |                                            return !joystick->GetSDLJoystick(); | 
		
	
		
			
				|  |  |  |  |                                        }); | 
		
	
		
			
				|  |  |  |  |         if (nullptr_it != map_it->second.end()) { | 
		
	
		
			
				|  |  |  |  |             // ... and map it | 
		
	
		
			
				|  |  |  |  |             (*nullptr_it)->SetSDLJoystick(sdl_joystick); | 
		
	
		
			
				|  |  |  |  |             return *nullptr_it; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         // There is no SDLJoystick without a mapped SDL_Joystick | 
		
	
		
			
				|  |  |  |  |         // Create a new SDLJoystick | 
		
	
		
			
				|  |  |  |  |         auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick); | 
		
	
		
			
				|  |  |  |  |         return map_it->second.emplace_back(std::move(joystick)); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); | 
		
	
		
			
				|  |  |  |  |     return joystick_map[guid].emplace_back(std::move(joystick)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void InitJoystick(int joystick_index) { | 
		
	
		
			
				|  |  |  |  |     std::lock_guard<std::mutex> lock(joystick_map_mutex); | 
		
	
		
			
				|  |  |  |  |     SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); | 
		
	
		
			
				|  |  |  |  |     if (!sdl_joystick) { | 
		
	
		
			
				|  |  |  |  |         LOG_ERROR(Input, "failed to open joystick {}", joystick_index); | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     std::string guid = GetGUID(sdl_joystick); | 
		
	
		
			
				|  |  |  |  |     if (joystick_map.find(guid) == joystick_map.end()) { | 
		
	
		
			
				|  |  |  |  |         auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); | 
		
	
		
			
				|  |  |  |  |         joystick_map[guid].emplace_back(std::move(joystick)); | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     auto& joystick_guid_list = joystick_map[guid]; | 
		
	
		
			
				|  |  |  |  |     const auto it = std::find_if( | 
		
	
		
			
				|  |  |  |  |         joystick_guid_list.begin(), joystick_guid_list.end(), | 
		
	
		
			
				|  |  |  |  |         [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); | 
		
	
		
			
				|  |  |  |  |     if (it != joystick_guid_list.end()) { | 
		
	
		
			
				|  |  |  |  |         (*it)->SetSDLJoystick(sdl_joystick); | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick); | 
		
	
		
			
				|  |  |  |  |     joystick_guid_list.emplace_back(std::move(joystick)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void CloseJoystick(SDL_Joystick* sdl_joystick) { | 
		
	
		
			
				|  |  |  |  |     std::lock_guard<std::mutex> lock(joystick_map_mutex); | 
		
	
		
			
				|  |  |  |  |     std::string guid = GetGUID(sdl_joystick); | 
		
	
		
			
				|  |  |  |  |     // This call to guid is save since the joystick is guranteed to be in that map | 
		
	
		
			
				|  |  |  |  |     auto& joystick_guid_list = joystick_map[guid]; | 
		
	
		
			
				|  |  |  |  |     const auto joystick_it = | 
		
	
		
			
				|  |  |  |  |         std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), | 
		
	
		
			
				|  |  |  |  |                      [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { | 
		
	
		
			
				|  |  |  |  |                          return joystick->GetSDLJoystick() == sdl_joystick; | 
		
	
		
			
				|  |  |  |  |                      }); | 
		
	
		
			
				|  |  |  |  |     (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {}); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void HandleGameControllerEvent(const SDL_Event& event) { | 
		
	
		
			
				|  |  |  |  |     switch (event.type) { | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYBUTTONUP: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); | 
		
	
		
			
				|  |  |  |  |         if (joystick) { | 
		
	
		
			
				|  |  |  |  |             joystick->SetButton(event.jbutton.button, false); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYBUTTONDOWN: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); | 
		
	
		
			
				|  |  |  |  |         if (joystick) { | 
		
	
		
			
				|  |  |  |  |             joystick->SetButton(event.jbutton.button, true); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYHATMOTION: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jhat.which); | 
		
	
		
			
				|  |  |  |  |         if (joystick) { | 
		
	
		
			
				|  |  |  |  |             joystick->SetHat(event.jhat.hat, event.jhat.value); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYAXISMOTION: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); | 
		
	
		
			
				|  |  |  |  |         if (joystick) { | 
		
	
		
			
				|  |  |  |  |             joystick->SetAxis(event.jaxis.axis, event.jaxis.value); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYDEVICEREMOVED: | 
		
	
		
			
				|  |  |  |  |         LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); | 
		
	
		
			
				|  |  |  |  |         CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYDEVICEADDED: | 
		
	
		
			
				|  |  |  |  |         LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which); | 
		
	
		
			
				|  |  |  |  |         InitJoystick(event.jdevice.which); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void CloseSDLJoysticks() { | 
		
	
		
			
				|  |  |  |  |     std::lock_guard<std::mutex> lock(joystick_map_mutex); | 
		
	
		
			
				|  |  |  |  |     joystick_map.clear(); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void PollLoop() { | 
		
	
		
			
				|  |  |  |  |     if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { | 
		
	
		
			
				|  |  |  |  |         LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     SDL_Event event; | 
		
	
		
			
				|  |  |  |  |     while (initialized) { | 
		
	
		
			
				|  |  |  |  |         // Wait for 10 ms or until an event happens | 
		
	
		
			
				|  |  |  |  |         if (SDL_WaitEventTimeout(&event, 10)) { | 
		
	
		
			
				|  |  |  |  |             // Don't handle the event if we are configuring | 
		
	
		
			
				|  |  |  |  |             if (polling) { | 
		
	
		
			
				|  |  |  |  |                 event_queue.Push(event); | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
		
			
				|  |  |  |  |                 HandleGameControllerEvent(event); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     CloseSDLJoysticks(); | 
		
	
		
			
				|  |  |  |  |     SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | class SDLButton final : public Input::ButtonDevice { | 
		
	
		
			
				|  |  |  |  | public: | 
		
	
		
			
				|  |  |  |  |     explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) | 
		
	
	
		
			
				
					
					|  |  |  | @@ -144,22 +366,14 @@ private: | 
		
	
		
			
				|  |  |  |  |     int axis_y; | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) { | 
		
	
		
			
				|  |  |  |  |     std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock(); | 
		
	
		
			
				|  |  |  |  |     if (!joystick) { | 
		
	
		
			
				|  |  |  |  |         joystick = std::make_shared<SDLJoystick>(joystick_index); | 
		
	
		
			
				|  |  |  |  |         joystick_list[joystick_index] = joystick; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     return joystick; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// A button device factory that creates button devices from SDL joystick | 
		
	
		
			
				|  |  |  |  | class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { | 
		
	
		
			
				|  |  |  |  | public: | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Creates a button device from a joystick button | 
		
	
		
			
				|  |  |  |  |      * @param params contains parameters for creating the device: | 
		
	
		
			
				|  |  |  |  |      *     - "joystick": the index of the joystick to bind | 
		
	
		
			
				|  |  |  |  |      *     - "guid": the guid of the joystick to bind | 
		
	
		
			
				|  |  |  |  |      *     - "port": the nth joystick of the same type to bind | 
		
	
		
			
				|  |  |  |  |      *     - "button"(optional): the index of the button to bind | 
		
	
		
			
				|  |  |  |  |      *     - "hat"(optional): the index of the hat to bind as direction buttons | 
		
	
		
			
				|  |  |  |  |      *     - "axis"(optional): the index of the axis to bind | 
		
	
	
		
			
				
					
					|  |  |  | @@ -167,12 +381,15 @@ public: | 
		
	
		
			
				|  |  |  |  |      *         "down", "left" or "right" | 
		
	
		
			
				|  |  |  |  |      *     - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is | 
		
	
		
			
				|  |  |  |  |      *         triggered if the axis value crosses | 
		
	
		
			
				|  |  |  |  |      *     - "direction"(only used for axis): "+" means the button is triggered when the axis value | 
		
	
		
			
				|  |  |  |  |      *         is greater than the threshold; "-" means the button is triggered when the axis value | 
		
	
		
			
				|  |  |  |  |      *         is smaller than the threshold | 
		
	
		
			
				|  |  |  |  |      *     - "direction"(only used for axis): "+" means the button is triggered when the axis | 
		
	
		
			
				|  |  |  |  |      * value is greater than the threshold; "-" means the button is triggered when the axis | 
		
	
		
			
				|  |  |  |  |      * value is smaller than the threshold | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { | 
		
	
		
			
				|  |  |  |  |         const int joystick_index = params.Get("joystick", 0); | 
		
	
		
			
				|  |  |  |  |         const std::string guid = params.Get("guid", "0"); | 
		
	
		
			
				|  |  |  |  |         const int port = params.Get("port", 0); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickByGUID(guid, port); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if (params.Has("hat")) { | 
		
	
		
			
				|  |  |  |  |             const int hat = params.Get("hat", 0); | 
		
	
	
		
			
				
					
					|  |  |  | @@ -189,8 +406,9 @@ public: | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
		
			
				|  |  |  |  |                 direction = 0; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat, | 
		
	
		
			
				|  |  |  |  |                                                         direction); | 
		
	
		
			
				|  |  |  |  |             // This is necessary so accessing GetHat with hat won't crash | 
		
	
		
			
				|  |  |  |  |             joystick->SetHat(hat, SDL_HAT_CENTERED); | 
		
	
		
			
				|  |  |  |  |             return std::make_unique<SDLDirectionButton>(joystick, hat, direction); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if (params.Has("axis")) { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -206,12 +424,15 @@ public: | 
		
	
		
			
				|  |  |  |  |                 trigger_if_greater = true; | 
		
	
		
			
				|  |  |  |  |                 LOG_ERROR(Input, "Unknown direction '{}'", direction_name); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold, | 
		
	
		
			
				|  |  |  |  |                                                    trigger_if_greater); | 
		
	
		
			
				|  |  |  |  |             // This is necessary so accessing GetAxis with axis won't crash | 
		
	
		
			
				|  |  |  |  |             joystick->SetAxis(axis, 0); | 
		
	
		
			
				|  |  |  |  |             return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         const int button = params.Get("button", 0); | 
		
	
		
			
				|  |  |  |  |         return std::make_unique<SDLButton>(GetJoystick(joystick_index), button); | 
		
	
		
			
				|  |  |  |  |         // This is necessary so accessing GetButton with button won't crash | 
		
	
		
			
				|  |  |  |  |         joystick->SetButton(button, false); | 
		
	
		
			
				|  |  |  |  |         return std::make_unique<SDLButton>(joystick, button); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -221,27 +442,32 @@ public: | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Creates analog device from joystick axes | 
		
	
		
			
				|  |  |  |  |      * @param params contains parameters for creating the device: | 
		
	
		
			
				|  |  |  |  |      *     - "joystick": the index of the joystick to bind | 
		
	
		
			
				|  |  |  |  |      *     - "guid": the guid of the joystick to bind | 
		
	
		
			
				|  |  |  |  |      *     - "port": the nth joystick of the same type | 
		
	
		
			
				|  |  |  |  |      *     - "axis_x": the index of the axis to be bind as x-axis | 
		
	
		
			
				|  |  |  |  |      *     - "axis_y": the index of the axis to be bind as y-axis | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { | 
		
	
		
			
				|  |  |  |  |         const int joystick_index = params.Get("joystick", 0); | 
		
	
		
			
				|  |  |  |  |         const std::string guid = params.Get("guid", "0"); | 
		
	
		
			
				|  |  |  |  |         const int port = params.Get("port", 0); | 
		
	
		
			
				|  |  |  |  |         const int axis_x = params.Get("axis_x", 0); | 
		
	
		
			
				|  |  |  |  |         const int axis_y = params.Get("axis_y", 1); | 
		
	
		
			
				|  |  |  |  |         return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickByGUID(guid, port); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // This is necessary so accessing GetAxis with axis_x and axis_y won't crash | 
		
	
		
			
				|  |  |  |  |         joystick->SetAxis(axis_x, 0); | 
		
	
		
			
				|  |  |  |  |         joystick->SetAxis(axis_y, 0); | 
		
	
		
			
				|  |  |  |  |         return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void Init() { | 
		
	
		
			
				|  |  |  |  |     if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { | 
		
	
		
			
				|  |  |  |  |         LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |         using namespace Input; | 
		
	
		
			
				|  |  |  |  |         RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); | 
		
	
		
			
				|  |  |  |  |         RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); | 
		
	
		
			
				|  |  |  |  |         initialized = true; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     using namespace Input; | 
		
	
		
			
				|  |  |  |  |     RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); | 
		
	
		
			
				|  |  |  |  |     RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); | 
		
	
		
			
				|  |  |  |  |     polling = false; | 
		
	
		
			
				|  |  |  |  |     initialized = true; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void Shutdown() { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -249,30 +475,17 @@ void Shutdown() { | 
		
	
		
			
				|  |  |  |  |         using namespace Input; | 
		
	
		
			
				|  |  |  |  |         UnregisterFactory<ButtonDevice>("sdl"); | 
		
	
		
			
				|  |  |  |  |         UnregisterFactory<AnalogDevice>("sdl"); | 
		
	
		
			
				|  |  |  |  |         SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | 
		
	
		
			
				|  |  |  |  |         initialized = false; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /** | 
		
	
		
			
				|  |  |  |  |  * This function converts a joystick ID used in SDL events to the device index. This is necessary | 
		
	
		
			
				|  |  |  |  |  * because Citra opens joysticks using their indices, not their IDs. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | static int JoystickIDToDeviceIndex(SDL_JoystickID id) { | 
		
	
		
			
				|  |  |  |  |     int num_joysticks = SDL_NumJoysticks(); | 
		
	
		
			
				|  |  |  |  |     for (int i = 0; i < num_joysticks; i++) { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetJoystick(i); | 
		
	
		
			
				|  |  |  |  |         if (joystick->GetJoystickID() == id) { | 
		
	
		
			
				|  |  |  |  |             return i; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     return -1; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { | 
		
	
		
			
				|  |  |  |  |     Common::ParamPackage params({{"engine", "sdl"}}); | 
		
	
		
			
				|  |  |  |  |     switch (event.type) { | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYAXISMOTION: | 
		
	
		
			
				|  |  |  |  |         params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which)); | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYAXISMOTION: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); | 
		
	
		
			
				|  |  |  |  |         params.Set("port", joystick->GetPort()); | 
		
	
		
			
				|  |  |  |  |         params.Set("guid", joystick->GetGUID()); | 
		
	
		
			
				|  |  |  |  |         params.Set("axis", event.jaxis.axis); | 
		
	
		
			
				|  |  |  |  |         if (event.jaxis.value > 0) { | 
		
	
		
			
				|  |  |  |  |             params.Set("direction", "+"); | 
		
	
	
		
			
				
					
					|  |  |  | @@ -282,12 +495,18 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { | 
		
	
		
			
				|  |  |  |  |             params.Set("threshold", "-0.5"); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYBUTTONUP: | 
		
	
		
			
				|  |  |  |  |         params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which)); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYBUTTONUP: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); | 
		
	
		
			
				|  |  |  |  |         params.Set("port", joystick->GetPort()); | 
		
	
		
			
				|  |  |  |  |         params.Set("guid", joystick->GetGUID()); | 
		
	
		
			
				|  |  |  |  |         params.Set("button", event.jbutton.button); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYHATMOTION: | 
		
	
		
			
				|  |  |  |  |         params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which)); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     case SDL_JOYHATMOTION: { | 
		
	
		
			
				|  |  |  |  |         auto joystick = GetSDLJoystickBySDLID(event.jhat.which); | 
		
	
		
			
				|  |  |  |  |         params.Set("port", joystick->GetPort()); | 
		
	
		
			
				|  |  |  |  |         params.Set("guid", joystick->GetGUID()); | 
		
	
		
			
				|  |  |  |  |         params.Set("hat", event.jhat.hat); | 
		
	
		
			
				|  |  |  |  |         switch (event.jhat.value) { | 
		
	
		
			
				|  |  |  |  |         case SDL_HAT_UP: | 
		
	
	
		
			
				
					
					|  |  |  | @@ -307,6 +526,7 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     return params; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -315,31 +535,20 @@ namespace Polling { | 
		
	
		
			
				|  |  |  |  | class SDLPoller : public InputCommon::Polling::DevicePoller { | 
		
	
		
			
				|  |  |  |  | public: | 
		
	
		
			
				|  |  |  |  |     void Start() override { | 
		
	
		
			
				|  |  |  |  |         // SDL joysticks must be opened, otherwise they don't generate events | 
		
	
		
			
				|  |  |  |  |         SDL_JoystickUpdate(); | 
		
	
		
			
				|  |  |  |  |         int num_joysticks = SDL_NumJoysticks(); | 
		
	
		
			
				|  |  |  |  |         for (int i = 0; i < num_joysticks; i++) { | 
		
	
		
			
				|  |  |  |  |             joysticks_opened.emplace_back(GetJoystick(i)); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         // Empty event queue to get rid of old events. citra-qt doesn't use the queue | 
		
	
		
			
				|  |  |  |  |         SDL_Event dummy; | 
		
	
		
			
				|  |  |  |  |         while (SDL_PollEvent(&dummy)) { | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         event_queue.Clear(); | 
		
	
		
			
				|  |  |  |  |         polling = true; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     void Stop() override { | 
		
	
		
			
				|  |  |  |  |         joysticks_opened.clear(); | 
		
	
		
			
				|  |  |  |  |         polling = false; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | private: | 
		
	
		
			
				|  |  |  |  |     std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened; | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | class SDLButtonPoller final : public SDLPoller { | 
		
	
		
			
				|  |  |  |  | public: | 
		
	
		
			
				|  |  |  |  |     Common::ParamPackage GetNextInput() override { | 
		
	
		
			
				|  |  |  |  |         SDL_Event event; | 
		
	
		
			
				|  |  |  |  |         while (SDL_PollEvent(&event)) { | 
		
	
		
			
				|  |  |  |  |         while (event_queue.Pop(event)) { | 
		
	
		
			
				|  |  |  |  |             switch (event.type) { | 
		
	
		
			
				|  |  |  |  |             case SDL_JOYAXISMOTION: | 
		
	
		
			
				|  |  |  |  |                 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -367,7 +576,7 @@ public: | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     Common::ParamPackage GetNextInput() override { | 
		
	
		
			
				|  |  |  |  |         SDL_Event event; | 
		
	
		
			
				|  |  |  |  |         while (SDL_PollEvent(&event)) { | 
		
	
		
			
				|  |  |  |  |         while (event_queue.Pop(event)) { | 
		
	
		
			
				|  |  |  |  |             if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { | 
		
	
		
			
				|  |  |  |  |                 continue; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
	
		
			
				
					
					|  |  |  | @@ -384,8 +593,10 @@ public: | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         Common::ParamPackage params; | 
		
	
		
			
				|  |  |  |  |         if (analog_xaxis != -1 && analog_yaxis != -1) { | 
		
	
		
			
				|  |  |  |  |             auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); | 
		
	
		
			
				|  |  |  |  |             params.Set("engine", "sdl"); | 
		
	
		
			
				|  |  |  |  |             params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick)); | 
		
	
		
			
				|  |  |  |  |             params.Set("port", joystick->GetPort()); | 
		
	
		
			
				|  |  |  |  |             params.Set("guid", joystick->GetGUID()); | 
		
	
		
			
				|  |  |  |  |             params.Set("axis_x", analog_xaxis); | 
		
	
		
			
				|  |  |  |  |             params.Set("axis_y", analog_yaxis); | 
		
	
		
			
				|  |  |  |  |             analog_xaxis = -1; | 
		
	
	
		
			
				
					
					|  |  |  |   |