From f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 22 Jul 2020 10:39:53 -0400 Subject: Project Mjölnir: Part 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: James Rowe Co-authored-by: Its-Rei --- src/input_common/sdl/sdl.h | 19 +- src/input_common/sdl/sdl_impl.cpp | 403 +++++++++++++++++++++++++++++--------- src/input_common/sdl/sdl_impl.h | 8 + 3 files changed, 336 insertions(+), 94 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index 5306daa70..f3554be9a 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h @@ -6,6 +6,7 @@ #include #include +#include "common/param_package.h" #include "input_common/main.h" namespace InputCommon::Polling { @@ -22,14 +23,24 @@ public: /// Unregisters SDL device factories and shut them down. virtual ~State() = default; - virtual Pollers GetPollers(Polling::DeviceType type) = 0; + virtual Pollers GetPollers(Polling::DeviceType type) { + return {}; + } + + virtual std::vector GetInputDevices() { + return {}; + } + + virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) { + return {}; + } + virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) { + return {}; + } }; class NullState : public State { public: - Pollers GetPollers(Polling::DeviceType type) override { - return {}; - } }; std::unique_ptr Init(); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d76c279d3..35a9d45ec 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,7 +25,8 @@ namespace InputCommon::SDL { -static std::string GetGUID(SDL_Joystick* joystick) { +namespace { +std::string GetGUID(SDL_Joystick* joystick) { const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); char guid_str[33]; SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); @@ -31,7 +34,8 @@ static std::string GetGUID(SDL_Joystick* joystick) { } /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice -static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); +Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); +} // Anonymous namespace static int SDLEventWatcher(void* user_data, SDL_Event* event) { auto* const sdl_state = static_cast(user_data); @@ -48,8 +52,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) { class SDLJoystick { public: - SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) - : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} + SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, + SDL_GameController* gamecontroller) + : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, + sdl_controller{gamecontroller, &SDL_GameControllerClose} {} void SetButton(int button, bool value) { std::lock_guard lock{mutex}; @@ -115,10 +121,15 @@ public: return sdl_joystick.get(); } - void SetSDLJoystick(SDL_Joystick* joystick) { + void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { + sdl_controller.reset(controller); sdl_joystick.reset(joystick); } + SDL_GameController* GetSDLGameController() const { + return sdl_controller.get(); + } + private: struct State { std::unordered_map buttons; @@ -128,6 +139,7 @@ private: std::string guid; int port; std::unique_ptr sdl_joystick; + std::unique_ptr sdl_controller; mutable std::mutex mutex; }; @@ -136,18 +148,19 @@ std::shared_ptr SDLState::GetSDLJoystickByGUID(const std::string& g const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { while (it->second.size() <= static_cast(port)) { - auto joystick = - std::make_shared(guid, static_cast(it->second.size()), nullptr); + auto joystick = std::make_shared(guid, static_cast(it->second.size()), + nullptr, nullptr); it->second.emplace_back(std::move(joystick)); } return it->second[port]; } - auto joystick = std::make_shared(guid, 0, nullptr); + auto joystick = std::make_shared(guid, 0, nullptr, nullptr); return joystick_map[guid].emplace_back(std::move(joystick)); } std::shared_ptr SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); + auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id); const std::string guid = GetGUID(sdl_joystick); std::lock_guard lock{joystick_map_mutex}; @@ -171,23 +184,27 @@ std::shared_ptr SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_ }); if (nullptr_it != map_it->second.end()) { // ... and map it - (*nullptr_it)->SetSDLJoystick(sdl_joystick); + (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller); return *nullptr_it; } // There is no SDLJoystick without a mapped SDL_Joystick // Create a new SDLJoystick const int port = static_cast(map_it->second.size()); - auto joystick = std::make_shared(guid, port, sdl_joystick); + auto joystick = std::make_shared(guid, port, sdl_joystick, sdl_controller); return map_it->second.emplace_back(std::move(joystick)); } - auto joystick = std::make_shared(guid, 0, sdl_joystick); + auto joystick = std::make_shared(guid, 0, sdl_joystick, sdl_controller); return joystick_map[guid].emplace_back(std::move(joystick)); } void SDLState::InitJoystick(int joystick_index) { SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); + SDL_GameController* sdl_gamecontroller = nullptr; + if (SDL_IsGameController(joystick_index)) { + sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); + } if (!sdl_joystick) { LOG_ERROR(Input, "failed to open joystick {}", joystick_index); return; @@ -196,7 +213,7 @@ void SDLState::InitJoystick(int joystick_index) { std::lock_guard lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { - auto joystick = std::make_shared(guid, 0, sdl_joystick); + auto joystick = std::make_shared(guid, 0, sdl_joystick, sdl_gamecontroller); joystick_map[guid].emplace_back(std::move(joystick)); return; } @@ -205,11 +222,11 @@ void SDLState::InitJoystick(int joystick_index) { joystick_guid_list.begin(), joystick_guid_list.end(), [](const std::shared_ptr& joystick) { return !joystick->GetSDLJoystick(); }); if (it != joystick_guid_list.end()) { - (*it)->SetSDLJoystick(sdl_joystick); + (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); return; } const int port = static_cast(joystick_guid_list.size()); - auto joystick = std::make_shared(guid, port, sdl_joystick); + auto joystick = std::make_shared(guid, port, sdl_joystick, sdl_gamecontroller); joystick_guid_list.emplace_back(std::move(joystick)); } @@ -231,7 +248,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { // Destruct SDL_Joystick outside the lock guard because SDL can internally call the // event callback which locks the mutex again. - joystick->SetSDLJoystick(nullptr); + joystick->SetSDLJoystick(nullptr, nullptr); } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { @@ -460,7 +477,7 @@ public: 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); - const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); + const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); auto joystick = state.GetSDLJoystickByGUID(guid, port); @@ -476,8 +493,10 @@ private: SDLState::SDLState() { using namespace Input; - RegisterFactory("sdl", std::make_shared(*this)); - RegisterFactory("sdl", std::make_shared(*this)); + analog_factory = std::make_shared(*this); + button_factory = std::make_shared(*this); + RegisterFactory("sdl", analog_factory); + RegisterFactory("sdl", button_factory); // If the frontend is going to manage the event loop, then we dont start one here start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); @@ -485,6 +504,7 @@ SDLState::SDLState() { LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); return; } + has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); } @@ -497,7 +517,7 @@ SDLState::SDLState() { using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); - std::this_thread::sleep_for(10ms); + std::this_thread::sleep_for(5ms); } }); } @@ -523,65 +543,233 @@ SDLState::~SDLState() { } } -static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { +std::vector SDLState::GetInputDevices() { + std::scoped_lock lock(joystick_map_mutex); + std::vector devices = {}; + for (const auto& [key, value] : joystick_map) { + for (const auto& joystick : value) { + auto controller = joystick->GetSDLGameController(); + auto joy = joystick->GetSDLJoystick(); + if (controller) { + std::string name = + fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); + devices.emplace_back(Common::ParamPackage{ + {"class", "sdl"}, + {"display", name}, + {"guid", joystick->GetGUID()}, + {"port", std::to_string(joystick->GetPort())}, + }); + } else if (joy) { + std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); + devices.emplace_back(Common::ParamPackage{ + {"class", "sdl"}, + {"display", name}, + {"guid", joystick->GetGUID()}, + {"port", std::to_string(joystick->GetPort())}, + }); + } + } + } + return devices; +} + +namespace { +Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, + float value = 0.1) { + Common::ParamPackage params({{"engine", "sdl"}}); + params.Set("port", port); + params.Set("guid", guid); + params.Set("axis", axis); + if (value > 0) { + params.Set("direction", "+"); + params.Set("threshold", "0.5"); + } else { + params.Set("direction", "-"); + params.Set("threshold", "-0.5"); + } + return params; +} + +Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { + Common::ParamPackage params({{"engine", "sdl"}}); + params.Set("port", port); + params.Set("guid", guid); + params.Set("button", button); + return params; +} + +Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) { Common::ParamPackage params({{"engine", "sdl"}}); + params.Set("port", port); + params.Set("guid", guid); + params.Set("hat", hat); + switch (value) { + case SDL_HAT_UP: + params.Set("direction", "up"); + break; + case SDL_HAT_DOWN: + params.Set("direction", "down"); + break; + case SDL_HAT_LEFT: + params.Set("direction", "left"); + break; + case SDL_HAT_RIGHT: + params.Set("direction", "right"); + break; + default: + return {}; + } + return params; +} + +Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { + Common::ParamPackage params{}; + switch (event.type) { case SDL_JOYAXISMOTION: { const auto joystick = state.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", "+"); - params.Set("threshold", "0.5"); - } else { - params.Set("direction", "-"); - params.Set("threshold", "-0.5"); - } + params = BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jaxis.axis, event.jaxis.value); break; } case SDL_JOYBUTTONUP: { const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); - params.Set("port", joystick->GetPort()); - params.Set("guid", joystick->GetGUID()); - params.Set("button", event.jbutton.button); + params = BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jbutton.button); break; } case SDL_JOYHATMOTION: { const auto joystick = state.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: - params.Set("direction", "up"); - break; - case SDL_HAT_DOWN: - params.Set("direction", "down"); - break; - case SDL_HAT_LEFT: - params.Set("direction", "left"); - break; - case SDL_HAT_RIGHT: - params.Set("direction", "right"); - break; - default: - return {}; - } + params = BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jhat.hat, event.jhat.value); break; } } return params; } -namespace Polling { +Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, + const SDL_GameControllerButtonBind& binding) { + Common::ParamPackage out{}; + switch (binding.bindType) { + case SDL_CONTROLLER_BINDTYPE_AXIS: + out = BuildAnalogParamPackageForButton(port, guid, binding.value.axis); + break; + case SDL_CONTROLLER_BINDTYPE_BUTTON: + out = BuildButtonParamPackageForButton(port, guid, binding.value.button); + break; + case SDL_CONTROLLER_BINDTYPE_HAT: + out = BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, + binding.value.hat.hat_mask); + break; + default: + break; + } + return out; +}; + +Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, + int axis_y) { + Common::ParamPackage params{}; + params.Set("engine", "sdl"); + params.Set("port", port); + params.Set("guid", guid); + params.Set("axis_x", axis_x); + params.Set("axis_y", axis_y); + return params; +} +} // Anonymous namespace + +ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) { + // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. + // We will add those afterwards + // This list also excludes Screenshot since theres not really a mapping for that + std::unordered_map + switch_to_sdl_button = { + {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, + {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, + {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, + {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, + {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, + {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, + {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, + {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, + {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, + {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, + }; + if (!params.Has("guid") || !params.Has("port")) { + return {}; + } + const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + auto controller = joystick->GetSDLGameController(); + if (!controller) { + return {}; + } + + ButtonMapping mapping{}; + for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { + const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); + mapping[switch_button] = + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); + } + + // Add the missing bindings for ZL/ZR + std::unordered_map switch_to_sdl_axis = + { + {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, + {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, + }; + for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { + const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); + mapping[switch_button] = + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); + } + + return mapping; +} + +AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { + if (!params.Has("guid") || !params.Has("port")) { + return {}; + } + const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + auto controller = joystick->GetSDLGameController(); + if (!controller) { + return {}; + } + + AnalogMapping mapping = {}; + const auto& binding_left_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); + const auto& binding_left_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); + mapping[Settings::NativeAnalog::LStick] = + BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + binding_left_x.value.axis, binding_left_y.value.axis); + const auto& binding_right_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); + const auto& binding_right_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + mapping[Settings::NativeAnalog::RStick] = + BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + binding_right_x.value.axis, binding_right_y.value.axis); + return mapping; +} +namespace Polling { class SDLPoller : public InputCommon::Polling::DevicePoller { public: explicit SDLPoller(SDLState& state_) : state(state_) {} - void Start() override { + void Start(std::string device_id) override { state.event_queue.Clear(); state.polling = true; } @@ -601,71 +789,106 @@ public: Common::ParamPackage GetNextInput() override { SDL_Event event; while (state.event_queue.Pop(event)) { - switch (event.type) { - case SDL_JOYAXISMOTION: - if (std::abs(event.jaxis.value / 32767.0) < 0.5) { - break; - } - [[fallthrough]]; - case SDL_JOYBUTTONUP: - case SDL_JOYHATMOTION: - return SDLEventToButtonParamPackage(state, event); + const auto package = FromEvent(event); + if (package) { + return *package; } } return {}; } + std::optional FromEvent(const SDL_Event& event) { + switch (event.type) { + case SDL_JOYAXISMOTION: + if (std::abs(event.jaxis.value / 32767.0) < 0.5) { + break; + } + [[fallthrough]]; + case SDL_JOYBUTTONUP: + case SDL_JOYHATMOTION: + return {SDLEventToButtonParamPackage(state, event)}; + } + return {}; + } }; -class SDLAnalogPoller final : public SDLPoller { +/** + * Attempts to match the press to a controller joy axis (left/right stick) and if a match + * isn't found, checks if the event matches anything from SDLButtonPoller and uses that + * instead + */ +class SDLAnalogPreferredPoller final : public SDLPoller { public: - explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} - - void Start() override { - SDLPoller::Start(); + explicit SDLAnalogPreferredPoller(SDLState& state_) + : SDLPoller(state_), button_poller(state_) {} + void Start(std::string device_id) override { + SDLPoller::Start(device_id); + // Load the game controller // Reset stored axes analog_x_axis = -1; analog_y_axis = -1; - analog_axes_joystick = -1; } Common::ParamPackage GetNextInput() override { SDL_Event event; while (state.event_queue.Pop(event)) { - if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { + // Filter out axis events that are below a threshold + if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { continue; } - // An analog device needs two axes, so we need to store the axis for later and wait for - // a second SDL event. The axes also must be from the same joystick. - const int axis = event.jaxis.axis; - if (analog_x_axis == -1) { - analog_x_axis = axis; - analog_axes_joystick = event.jaxis.which; - } else if (analog_y_axis == -1 && analog_x_axis != axis && - analog_axes_joystick == event.jaxis.which) { - analog_y_axis = axis; + // Simplify controller config by testing if game controller support is enabled. + if (event.type == SDL_JOYAXISMOTION) { + const auto axis = event.jaxis.axis; + const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); + const auto controller = joystick->GetSDLGameController(); + if (controller) { + const auto axis_left_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) + .value.axis; + const auto axis_left_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) + .value.axis; + const auto axis_right_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) + .value.axis; + const auto axis_right_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) + .value.axis; + + if (axis == axis_left_x || axis == axis_left_y) { + analog_x_axis = axis_left_x; + analog_y_axis = axis_left_y; + break; + } else if (axis == axis_right_x || axis == axis_right_y) { + analog_x_axis = axis_right_x; + analog_y_axis = axis_right_y; + break; + } + } + } + + // If the press wasn't accepted as a joy axis, check for a button press + auto button_press = button_poller.FromEvent(event); + if (button_press) { + return *button_press; } } - Common::ParamPackage params; + if (analog_x_axis != -1 && analog_y_axis != -1) { const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - params.Set("engine", "sdl"); - params.Set("port", joystick->GetPort()); - params.Set("guid", joystick->GetGUID()); - params.Set("axis_x", analog_x_axis); - params.Set("axis_y", analog_y_axis); + auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + analog_x_axis, analog_y_axis); analog_x_axis = -1; analog_y_axis = -1; - analog_axes_joystick = -1; return params; } - return params; + return {}; } private: int analog_x_axis = -1; int analog_y_axis = -1; - SDL_JoystickID analog_axes_joystick = -1; + SDLButtonPoller button_poller; }; } // namespace Polling @@ -673,8 +896,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { Pollers pollers; switch (type) { - case InputCommon::Polling::DeviceType::Analog: - pollers.emplace_back(std::make_unique(*this)); + case InputCommon::Polling::DeviceType::AnalogPreferred: + pollers.emplace_back(std::make_unique(*this)); break; case InputCommon::Polling::DeviceType::Button: pollers.emplace_back(std::make_unique(*this)); diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 606a32c5b..bd19ba61d 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -50,6 +50,11 @@ public: std::atomic polling = false; Common::SPSCQueue event_queue; + std::vector GetInputDevices() override; + + ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; + AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; + private: void InitJoystick(int joystick_index); void CloseJoystick(SDL_Joystick* sdl_joystick); @@ -57,6 +62,9 @@ private: /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); + // Set to true if SDL supports game controller subsystem + bool has_gamecontroller = false; + /// Map of GUID of a list of corresponding virtual Joysticks std::unordered_map>> joystick_map; std::mutex joystick_map_mutex; -- cgit v1.2.3 From efa0b7a056b73dffb8789c95ebf8a9c09e55f539 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 15 Aug 2020 15:26:29 -0400 Subject: Address feedback --- src/input_common/main.cpp | 15 ++--- src/input_common/main.h | 2 +- src/input_common/sdl/sdl_impl.cpp | 52 +++++++--------- src/input_common/udp/udp.cpp | 5 +- src/input_common/udp/udp.h | 2 +- .../configuration/configure_debug_controller.cpp | 4 +- .../configuration/configure_debug_controller.h | 4 +- src/yuzu/configuration/configure_input.cpp | 13 ++-- src/yuzu/configuration/configure_input.h | 2 +- .../configuration/configure_input_advanced.cpp | 2 +- src/yuzu/configuration/configure_input_advanced.h | 1 + src/yuzu/configuration/configure_input_player.cpp | 70 ++++++++++------------ src/yuzu/uisettings.h | 1 - 13 files changed, 77 insertions(+), 96 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b8725e9af..7bad2c45b 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -118,37 +118,38 @@ std::vector GetInputDevices() { std::unordered_map GetButtonMappingForDevice( const Common::ParamPackage& params) { - std::unordered_map mappings{}; + std::unordered_map mappings; if (!params.Has("class") || params.Get("class", "") == "any") { - return mappings; + return {}; } if (params.Get("class", "") == "key") { // TODO consider returning the SDL key codes for the default keybindings + return {}; } #ifdef HAVE_SDL2 if (params.Get("class", "") == "sdl") { return sdl->GetButtonMappingForDevice(params); } #endif - return mappings; + return {}; } std::unordered_map GetAnalogMappingForDevice( const Common::ParamPackage& params) { - std::unordered_map mappings{}; + std::unordered_map mappings; if (!params.Has("class") || params.Get("class", "") == "any") { - return mappings; + return {}; } if (params.Get("class", "") == "key") { // TODO consider returning the SDL key codes for the default keybindings - return mappings; + return {}; } #ifdef HAVE_SDL2 if (params.Get("class", "") == "sdl") { return sdl->GetAnalogMappingForDevice(params); } #endif - return mappings; + return {}; } namespace Polling { diff --git a/src/input_common/main.h b/src/input_common/main.h index ebc7f9533..e706c3750 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -76,7 +76,7 @@ public: /// Setup and start polling for inputs, should be called before GetNextInput /// If a device_id is provided, events should be filtered to only include events from this /// device id - virtual void Start(std::string device_id = "") = 0; + virtual void Start(const std::string& device_id = "") = 0; /// Stop polling virtual void Stop() = 0; /** diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 35a9d45ec..dec7540e2 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -545,17 +545,16 @@ SDLState::~SDLState() { std::vector SDLState::GetInputDevices() { std::scoped_lock lock(joystick_map_mutex); - std::vector devices = {}; + std::vector devices; for (const auto& [key, value] : joystick_map) { for (const auto& joystick : value) { - auto controller = joystick->GetSDLGameController(); auto joy = joystick->GetSDLJoystick(); - if (controller) { + if (auto controller = joystick->GetSDLGameController()) { std::string name = fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, - {"display", name}, + {"display", std::move(name)}, {"guid", joystick->GetGUID()}, {"port", std::to_string(joystick->GetPort())}, }); @@ -563,7 +562,7 @@ std::vector SDLState::GetInputDevices() { std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, - {"display", name}, + {"display", std::move(name)}, {"guid", joystick->GetGUID()}, {"port", std::to_string(joystick->GetPort())}, }); @@ -624,54 +623,43 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u } Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { - Common::ParamPackage params{}; - switch (event.type) { case SDL_JOYAXISMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - params = BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jaxis.axis, event.jaxis.value); - break; + return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jaxis.axis, event.jaxis.value); } case SDL_JOYBUTTONUP: { const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); - params = BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jbutton.button); - break; + return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jbutton.button); } case SDL_JOYHATMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); - params = BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jhat.hat, event.jhat.value); - break; + return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jhat.hat, event.jhat.value); } } - return params; + return {}; } Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) { - Common::ParamPackage out{}; switch (binding.bindType) { case SDL_CONTROLLER_BINDTYPE_AXIS: - out = BuildAnalogParamPackageForButton(port, guid, binding.value.axis); - break; + return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); case SDL_CONTROLLER_BINDTYPE_BUTTON: - out = BuildButtonParamPackageForButton(port, guid, binding.value.button); - break; + return BuildButtonParamPackageForButton(port, guid, binding.value.button); case SDL_CONTROLLER_BINDTYPE_HAT: - out = BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, - binding.value.hat.hat_mask); - break; - default: - break; + return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, + binding.value.hat.hat_mask); } - return out; -}; + return {}; +} Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, int axis_y) { - Common::ParamPackage params{}; + Common::ParamPackage params; params.Set("engine", "sdl"); params.Set("port", port); params.Set("guid", guid); @@ -769,7 +757,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller { public: explicit SDLPoller(SDLState& state_) : state(state_) {} - void Start(std::string device_id) override { + void Start(const std::string& device_id) override { state.event_queue.Clear(); state.polling = true; } @@ -821,7 +809,7 @@ public: explicit SDLAnalogPreferredPoller(SDLState& state_) : SDLPoller(state_), button_poller(state_) {} - void Start(std::string device_id) override { + void Start(const std::string& device_id) override { SDLPoller::Start(device_id); // Load the game controller // Reset stored axes diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 60cf47123..4b347e47e 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -89,10 +89,9 @@ State::~State() { Input::UnregisterFactory("cemuhookudp"); } -std::vector State::GetInputDevices() { - std::vector devices = {}; +std::vector State::GetInputDevices() const { // TODO support binding udp devices - return devices; + return {}; } void State::ReloadUDPClient() { diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index 24f6e0857..672a5c812 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h @@ -19,7 +19,7 @@ public: State(); ~State(); void ReloadUDPClient(); - std::vector GetInputDevices(); + std::vector GetInputDevices() const; private: std::unique_ptr client; diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp index 45996b73f..72885b4b8 100644 --- a/src/yuzu/configuration/configure_debug_controller.cpp +++ b/src/yuzu/configuration/configure_debug_controller.cpp @@ -6,10 +6,10 @@ #include "yuzu/configuration/configure_debug_controller.h" ConfigureDebugController::ConfigureDebugController(QWidget* parent) - : QDialog(parent), ui(std::make_unique()) { + : QDialog(parent), ui(std::make_unique()), + debug_controller(new ConfigureInputPlayer(this, 9, nullptr, true)) { ui->setupUi(this); - debug_controller = new ConfigureInputPlayer(this, 9, nullptr, true); ui->controllerLayout->addWidget(debug_controller); connect(ui->clear_all_button, &QPushButton::clicked, this, diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h index df359a4f3..36475bbea 100644 --- a/src/yuzu/configuration/configure_debug_controller.h +++ b/src/yuzu/configuration/configure_debug_controller.h @@ -27,7 +27,7 @@ private: void changeEvent(QEvent* event) override; void RetranslateUI(); - ConfigureInputPlayer* debug_controller; - std::unique_ptr ui; + + ConfigureInputPlayer* debug_controller; }; diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 5200d2d0e..0d004c2f7 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -103,13 +103,14 @@ ConfigureInput::ConfigureInput(QWidget* parent) } }); connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, - [&] { UpdateAllInputDevices(); }); - connect(player_connected[i], &QCheckBox::stateChanged, - [&, i](int state) { player_controllers[i]->ConnectPlayer(state == Qt::Checked); }); + [this] { UpdateAllInputDevices(); }); + connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { + player_controllers[i]->ConnectPlayer(state == Qt::Checked); + }); } // Only the first player can choose handheld mode so connect the signal just to player 1 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, - [&](bool is_handheld) { UpdateDockedState(is_handheld); }); + [this](bool is_handheld) { UpdateDockedState(is_handheld); }); advanced = new ConfigureInputAdvanced(this); ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); @@ -182,14 +183,14 @@ void ConfigureInput::LoadPlayerControllerIndices() { void ConfigureInput::ClearAll() { // We don't have a good way to know what tab is active, but we can find out by getting the // parent of the consoleInputSettings - auto player_tab = static_cast(ui->consoleInputSettings->parent()); + auto* player_tab = static_cast(ui->consoleInputSettings->parent()); player_tab->ClearAll(); } void ConfigureInput::RestoreDefaults() { // We don't have a good way to know what tab is active, but we can find out by getting the // parent of the consoleInputSettings - auto player_tab = static_cast(ui->consoleInputSettings->parent()); + auto* player_tab = static_cast(ui->consoleInputSettings->parent()); player_tab->RestoreDefaults(); ui->radioDocked->setChecked(true); diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 8241d23ef..78ca659da 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -15,9 +15,9 @@ #include "ui_configure_input.h" +class QCheckBox; class QString; class QTimer; -class QCheckBox; namespace Ui { class ConfigureInput; diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 18db04e7e..db42b826b 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -9,7 +9,7 @@ #include "yuzu/configuration/configure_input_advanced.h" ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) - : QWidget(parent), ui(new Ui::ConfigureInputAdvanced) { + : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); controllers_color_buttons = {{ diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index d6e913675..d8fcec52d 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 4d79a51f3..68d0d5db7 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -348,22 +348,22 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i // Player Connected checkbox connect(ui->groupConnectedController, &QGroupBox::toggled, - [&](bool checked) { emit Connected(checked); }); + [this](bool checked) { emit Connected(checked); }); // Set up controller type. Only Player 1 can choose Handheld. ui->comboControllerType->clear(); QStringList controller_types = { - QStringLiteral("Pro Controller"), - QStringLiteral("Dual Joycons"), - QStringLiteral("Left Joycon"), - QStringLiteral("Right Joycon"), + tr("Pro Controller"), + tr("Dual Joycons"), + tr("Left Joycon"), + tr("Right Joycon"), }; if (player_index == 0) { - controller_types.append(QStringLiteral("Handheld")); + controller_types.append(tr("Handheld")); connect(ui->comboControllerType, qOverload(&QComboBox::currentIndexChanged), - [&](int index) { + [this](int index) { emit HandheldStateChanged(GetControllerTypeFromIndex(index) == Settings::ControllerType::Handheld); }); @@ -375,7 +375,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i ui->buttonHome->setEnabled(false); ui->groupConnectedController->setCheckable(false); QStringList debug_controller_types = { - QStringLiteral("Pro Controller"), + tr("Pro Controller"), }; ui->comboControllerType->addItems(debug_controller_types); } else { @@ -384,17 +384,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i UpdateControllerIcon(); UpdateControllerAvailableButtons(); - connect(ui->comboControllerType, qOverload(&QComboBox::currentIndexChanged), [&](int) { + connect(ui->comboControllerType, qOverload(&QComboBox::currentIndexChanged), [this](int) { UpdateControllerIcon(); UpdateControllerAvailableButtons(); }); - connect(ui->comboDevices, qOverload(&QComboBox::currentIndexChanged), - [&] { UpdateMappingWithDefaults(); }); + connect(ui->comboDevices, qOverload(&QComboBox::currentIndexChanged), this, + &ConfigureInputPlayer::UpdateMappingWithDefaults); ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); UpdateInputDevices(); - connect(ui->buttonRefreshDevices, &QPushButton::clicked, [&] { emit RefreshInputDevices(); }); + connect(ui->buttonRefreshDevices, &QPushButton::clicked, + [this] { emit RefreshInputDevices(); }); timeout_timer->setSingleShot(true); connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); @@ -707,26 +708,22 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { void ConfigureInputPlayer::UpdateControllerIcon() { // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its // "nonstandard" to use an image through the icon support) - QString stylesheet{}; - switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { - case Settings::ControllerType::ProController: - stylesheet = QStringLiteral("image: url(:/controller/pro_controller%0)"); - break; - case Settings::ControllerType::DualJoyconDetached: - stylesheet = QStringLiteral("image: url(:/controller/dual_joycon%0)"); - break; - case Settings::ControllerType::LeftJoycon: - stylesheet = QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); - break; - case Settings::ControllerType::RightJoycon: - stylesheet = QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); - break; - case Settings::ControllerType::Handheld: - stylesheet = QStringLiteral("image: url(:/controller/handheld%0)"); - break; - default: - break; - } + const QString stylesheet = [this] { + switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { + case Settings::ControllerType::ProController: + return QStringLiteral("image: url(:/controller/pro_controller%0)"); + case Settings::ControllerType::DualJoyconDetached: + return QStringLiteral("image: url(:/controller/dual_joycon%0)"); + case Settings::ControllerType::LeftJoycon: + return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); + case Settings::ControllerType::RightJoycon: + return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); + case Settings::ControllerType::Handheld: + return QStringLiteral("image: url(:/controller/handheld%0)"); + default: + return QString{}; + } + }(); const QString theme = [this] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { @@ -744,12 +741,12 @@ void ConfigureInputPlayer::UpdateControllerIcon() { void ConfigureInputPlayer::UpdateControllerAvailableButtons() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Settings::ControllerType::DualJoyconDetached; + layout = Settings::ControllerType::ProController; } // List of all the widgets that will be hidden by any of the following layouts that need // "unhidden" after the controller type changes - const std::vector layout_show = { + const std::array layout_show = { ui->buttonShoulderButtonsSLSR, ui->horizontalSpacerShoulderButtonsWidget, ui->horizontalSpacerShoulderButtonsWidget2, @@ -768,11 +765,6 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { std::vector layout_hidden; switch (layout) { case Settings::ControllerType::ProController: - layout_hidden = { - ui->buttonShoulderButtonsSLSR, - ui->horizontalSpacerShoulderButtonsWidget2, - }; - break; case Settings::ControllerType::DualJoyconDetached: case Settings::ControllerType::Handheld: layout_hidden = { diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 533815098..ce3945485 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -13,7 +13,6 @@ #include #include #include "common/common_types.h" -#include "core/settings.h" namespace UISettings { -- cgit v1.2.3 From de79897f042aa7d3cacf8579078195cca559f62f Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 19 Aug 2020 04:17:38 -0400 Subject: input_common: Fix directional deadzone values The hardware tested value is 0.5 which translates to SHRT_MAX / 2 --- src/input_common/gcadapter/gc_poller.cpp | 2 +- src/input_common/sdl/sdl_impl.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 85342bbe7..71cd85eeb 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -191,7 +191,7 @@ public: bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { const auto [x, y] = GetStatus(); - const float directional_deadzone = 0.4f; + const float directional_deadzone = 0.5f; switch (direction) { case Input::AnalogDirection::RIGHT: return x > directional_deadzone; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index dec7540e2..7605c884d 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -363,7 +363,7 @@ public: bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { const auto [x, y] = GetStatus(); - const float directional_deadzone = 0.4f; + const float directional_deadzone = 0.5f; switch (direction) { case Input::AnalogDirection::RIGHT: return x > directional_deadzone; -- cgit v1.2.3