From 03b574ae2272fc8465e7d38f21b198fcb1885186 Mon Sep 17 00:00:00 2001 From: german Date: Thu, 17 Sep 2020 20:26:34 -0500 Subject: Add random motion input to SDL --- src/input_common/sdl/sdl_impl.cpp | 190 ++++++++++++++++++++++++++++++++++++++ src/input_common/sdl/sdl_impl.h | 2 + 2 files changed, 192 insertions(+) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index a9e676f4b..0b0095978 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -21,6 +21,7 @@ #include "common/param_package.h" #include "common/threadsafe_queue.h" #include "core/frontend/input.h" +#include "input_common/motion_input.h" #include "input_common/sdl/sdl_impl.h" #include "input_common/settings.h" @@ -95,6 +96,10 @@ public: return std::make_tuple(x, y); } + const InputCommon::MotionInput& GetMotion() const { + return motion; + } + void SetHat(int hat, Uint8 direction) { std::lock_guard lock{mutex}; state.hats.insert_or_assign(hat, direction); @@ -142,6 +147,9 @@ private: std::unique_ptr sdl_joystick; std::unique_ptr sdl_controller; mutable std::mutex mutex; + + // motion is initalized without PID values as motion input is not aviable for SDL2 + InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; }; std::shared_ptr SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { @@ -386,6 +394,68 @@ private: const float range; }; +class SDLDirectionMotion final : public Input::MotionDevice { +public: + explicit SDLDirectionMotion(std::shared_ptr joystick_, int hat_, Uint8 direction_) + : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {} + + Input::MotionStatus GetStatus() const override { + if (joystick->GetHatDirection(hat, direction)) { + return joystick->GetMotion().GetRandomMotion(2, 6); + } + return joystick->GetMotion().GetRandomMotion(0, 0); + } + +private: + std::shared_ptr joystick; + int hat; + Uint8 direction; +}; + +class SDLAxisMotion final : public Input::MotionDevice { +public: + explicit SDLAxisMotion(std::shared_ptr joystick_, int axis_, float threshold_, + bool trigger_if_greater_) + : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_), + trigger_if_greater(trigger_if_greater_) {} + + Input::MotionStatus GetStatus() const override { + const float axis_value = joystick->GetAxis(axis, 1.0f); + bool trigger = axis_value < threshold; + if (trigger_if_greater) { + trigger = axis_value > threshold; + } + + if (trigger) { + return joystick->GetMotion().GetRandomMotion(2, 6); + } + return joystick->GetMotion().GetRandomMotion(0, 0); + } + +private: + std::shared_ptr joystick; + int axis; + float threshold; + bool trigger_if_greater; +}; + +class SDLButtonMotion final : public Input::MotionDevice { +public: + explicit SDLButtonMotion(std::shared_ptr joystick_, int button_) + : joystick(std::move(joystick_)), button(button_) {} + + Input::MotionStatus GetStatus() const override { + if (joystick->GetButton(button)) { + return joystick->GetMotion().GetRandomMotion(2, 6); + } + return joystick->GetMotion().GetRandomMotion(0, 0); + } + +private: + std::shared_ptr joystick; + int button; +}; + /// A button device factory that creates button devices from SDL joystick class SDLButtonFactory final : public Input::Factory { public: @@ -492,12 +562,78 @@ private: SDLState& state; }; +/// A motion device factory that creates motion devices from SDL joystick +class SDLMotionFactory final : public Input::Factory { +public: + explicit SDLMotionFactory(SDLState& state_) : state(state_) {} + /** + * Creates motion device from joystick axes + * @param params contains parameters for creating the device: + * - "guid": the guid of the joystick to bind + * - "port": the nth joystick of the same type + */ + std::unique_ptr Create(const Common::ParamPackage& params) override { + const std::string guid = params.Get("guid", "0"); + const int port = params.Get("port", 0); + + auto joystick = state.GetSDLJoystickByGUID(guid, port); + + if (params.Has("hat")) { + const int hat = params.Get("hat", 0); + const std::string direction_name = params.Get("direction", ""); + Uint8 direction; + if (direction_name == "up") { + direction = SDL_HAT_UP; + } else if (direction_name == "down") { + direction = SDL_HAT_DOWN; + } else if (direction_name == "left") { + direction = SDL_HAT_LEFT; + } else if (direction_name == "right") { + direction = SDL_HAT_RIGHT; + } else { + direction = 0; + } + // This is necessary so accessing GetHat with hat won't crash + joystick->SetHat(hat, SDL_HAT_CENTERED); + return std::make_unique(joystick, hat, direction); + } + + if (params.Has("axis")) { + const int axis = params.Get("axis", 0); + const float threshold = params.Get("threshold", 0.5f); + const std::string direction_name = params.Get("direction", ""); + bool trigger_if_greater; + if (direction_name == "+") { + trigger_if_greater = true; + } else if (direction_name == "-") { + trigger_if_greater = false; + } else { + trigger_if_greater = true; + LOG_ERROR(Input, "Unknown direction {}", direction_name); + } + // This is necessary so accessing GetAxis with axis won't crash + joystick->SetAxis(axis, 0); + return std::make_unique(joystick, axis, threshold, trigger_if_greater); + } + + const int button = params.Get("button", 0); + // This is necessary so accessing GetButton with button won't crash + joystick->SetButton(button, false); + return std::make_unique(joystick, button); + } + +private: + SDLState& state; +}; + SDLState::SDLState() { using namespace Input; analog_factory = std::make_shared(*this); button_factory = std::make_shared(*this); + motion_factory = std::make_shared(*this); RegisterFactory("sdl", analog_factory); RegisterFactory("sdl", button_factory); + RegisterFactory("sdl", motion_factory); // If the frontend is going to manage the event loop, then we dont start one here start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); @@ -533,6 +669,7 @@ SDLState::~SDLState() { using namespace Input; UnregisterFactory("sdl"); UnregisterFactory("sdl"); + UnregisterFactory("sdl"); CloseJoysticks(); SDL_DelEventWatch(&SDLEventWatcher, this); @@ -644,6 +781,27 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve return {}; } +Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) { + switch (event.type) { + case SDL_JOYAXISMOTION: { + const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); + return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jaxis.axis, event.jaxis.value); + } + case SDL_JOYBUTTONUP: { + const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); + return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jbutton.button); + } + case SDL_JOYHATMOTION: { + const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); + return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jhat.hat, event.jhat.value); + } + } + return {}; +} + Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) { switch (binding.bindType) { @@ -809,6 +967,35 @@ public: } }; +class SDLMotionPoller final : public SDLPoller { +public: + explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {} + + Common::ParamPackage GetNextInput() override { + SDL_Event event; + while (state.event_queue.Pop(event)) { + const auto package = FromEvent(event); + if (package) { + return *package; + } + } + return {}; + } + [[nodiscard]] std::optional FromEvent(const SDL_Event& event) const { + 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 {SDLEventToMotionParamPackage(state, event)}; + } + return std::nullopt; + } +}; + /** * 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 @@ -900,6 +1087,9 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { case InputCommon::Polling::DeviceType::Button: pollers.emplace_back(std::make_unique(*this)); break; + case InputCommon::Polling::DeviceType::Motion: + pollers.emplace_back(std::make_unique(*this)); + break; } return pollers; diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index bd19ba61d..b9bb4dc56 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -21,6 +21,7 @@ namespace InputCommon::SDL { class SDLAnalogFactory; class SDLButtonFactory; +class SDLMotionFactory; class SDLJoystick; class SDLState : public State { @@ -71,6 +72,7 @@ private: std::shared_ptr button_factory; std::shared_ptr analog_factory; + std::shared_ptr motion_factory; bool start_thread = false; std::atomic initialized = false; -- cgit v1.2.3 From ab88c2f6112edba35bfa91ee8864e760728d16e8 Mon Sep 17 00:00:00 2001 From: german Date: Fri, 10 Jul 2020 21:20:50 -0500 Subject: First implementation of controller rumble --- src/core/frontend/input.h | 3 +++ src/core/hle/service/hid/controllers/npad.cpp | 25 ++++++++++++----- src/core/hle/service/hid/controllers/npad.h | 2 +- src/core/hle/service/hid/hid.cpp | 8 +++--- src/input_common/sdl/sdl_impl.cpp | 39 ++++++++++++++++++++++++++- 5 files changed, 63 insertions(+), 14 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 9da0d2829..277b70e53 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -33,6 +33,9 @@ public: virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { return {}; } + virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { + return {}; + } }; /// An abstract class template for a factory that can create input devices. diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 620386cd1..83c3beab6 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -609,20 +609,31 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) } } -void Controller_NPad::VibrateController(const std::vector& controller_ids, +void Controller_NPad::VibrateController(const std::vector& controllers, const std::vector& vibrations) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); + LOG_TRACE(Service_HID, "called"); if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { return; } - for (std::size_t i = 0; i < controller_ids.size(); i++) { - std::size_t controller_pos = NPadIdToIndex(static_cast(i)); - if (connected_controllers[controller_pos].is_connected) { - // TODO(ogniK): Vibrate the physical controller + bool success = true; + for (std::size_t i = 0; i < controllers.size(); ++i) { + if (!connected_controllers[i].is_connected) { + continue; + } + using namespace Settings::NativeButton; + const auto& button_state = buttons[i]; + if (button_state[A - BUTTON_HID_BEGIN]) { + if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( + vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, + vibrations[0].freq_low)) { + success = false; + } } } - last_processed_vibration = vibrations.back(); + if (success) { + last_processed_vibration = vibrations.back(); + } } Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 654d97c3f..0cff6821f 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -121,7 +121,7 @@ public: void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); - void VibrateController(const std::vector& controller_ids, + void VibrateController(const std::vector& controllers, const std::vector& vibrations); Vibration GetLastVibration() const; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 395e83b3f..dc198791d 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -802,18 +802,18 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller_id{rp.Pop()}; + const auto controller{rp.Pop()}; const auto vibration_values{rp.PopRaw()}; const auto applet_resource_user_id{rp.Pop()}; - LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, + LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); applet_resource->GetController(HidController::NPad) - .VibrateController({controller_id}, {vibration_values}); + .VibrateController({controller}, {vibration_values}); } void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { @@ -831,8 +831,6 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { std::memcpy(controller_list.data(), controllers.data(), controllers.size()); std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); - std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(), - [](u32 controller_id) { return controller_id - 3; }); applet_resource->GetController(HidController::NPad) .VibrateController(controller_list, vibration_list); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index a9e676f4b..27a96c18b 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,33 @@ public: return state.axes.at(axis) / (32767.0f * range); } + bool RumblePlay(f32 amp_low, f32 amp_high, int time) { + const u16 raw_amp_low = static_cast(amp_low * 0xFFFF); + const u16 raw_amp_high = static_cast(amp_high * 0xFFFF); + // Lower drastically the number of state changes + if (raw_amp_low >> 11 == last_state_rumble_low >> 11 && + raw_amp_high >> 11 == last_state_rumble_high >> 11) { + if (raw_amp_low + raw_amp_high != 0 || + last_state_rumble_low + last_state_rumble_high == 0) { + return false; + } + } + // Don't change state if last vibration was < 20ms + const auto now = std::chrono::system_clock::now(); + if (std::chrono::duration_cast(now - last_vibration) < + std::chrono::milliseconds(20)) { + return raw_amp_low + raw_amp_high == 0; + } + + last_vibration = now; + last_state_rumble_low = raw_amp_low; + last_state_rumble_high = raw_amp_high; + if (sdl_joystick) { + SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time); + } + return false; + } + std::tuple GetAnalog(int axis_x, int axis_y, float range) const { float x = GetAxis(axis_x, range); float y = GetAxis(axis_y, range); @@ -139,6 +167,9 @@ private: } state; std::string guid; int port; + u16 last_state_rumble_high; + u16 last_state_rumble_low; + std::chrono::time_point last_vibration; std::unique_ptr sdl_joystick; std::unique_ptr sdl_controller; mutable std::mutex mutex; @@ -207,7 +238,7 @@ void SDLState::InitJoystick(int joystick_index) { sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); } if (!sdl_joystick) { - LOG_ERROR(Input, "failed to open joystick {}", joystick_index); + LOG_ERROR(Input, "Failed to open joystick {}", joystick_index); return; } const std::string guid = GetGUID(sdl_joystick); @@ -303,6 +334,12 @@ public: return joystick->GetButton(button); } + bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { + const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)); + const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)); + return joystick->RumblePlay(new_amp_low, new_amp_high, 250); + } + private: std::shared_ptr joystick; int button; -- cgit v1.2.3 From 046c0c91a3ed665531f20955e7cfb86fe5b73213 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 14 Oct 2020 02:51:14 -0400 Subject: input_common/CMakeLists: Make some warnings errors Makes the input_common code warnings consistent with the rest of the codebase. --- src/common/math_util.h | 4 +- src/common/vector_math.h | 75 +++++++++++++++++++--- src/input_common/CMakeLists.txt | 29 +++++++++ src/input_common/analog_from_button.cpp | 18 +++--- src/input_common/gcadapter/gc_adapter.cpp | 35 ++++++----- src/input_common/gcadapter/gc_adapter.h | 6 +- src/input_common/gcadapter/gc_poller.cpp | 48 +++++++------- src/input_common/keyboard.cpp | 5 +- src/input_common/main.cpp | 4 ++ src/input_common/motion_emu.cpp | 41 ++++++------ src/input_common/motion_from_button.cpp | 2 +- src/input_common/motion_input.cpp | 38 +++++------ src/input_common/motion_input.h | 10 +-- src/input_common/sdl/sdl_impl.cpp | 77 ++++++++++++----------- src/input_common/touch_from_button.cpp | 8 ++- src/input_common/udp/client.cpp | 65 ++++++++++--------- src/input_common/udp/client.h | 14 ++--- src/input_common/udp/udp.cpp | 28 ++++----- src/yuzu/configuration/configure_motion_touch.cpp | 2 +- 19 files changed, 306 insertions(+), 203 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/common/math_util.h b/src/common/math_util.h index b35ad8507..7cec80d57 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -20,8 +20,8 @@ struct Rectangle { constexpr Rectangle() = default; - constexpr Rectangle(T left, T top, T right, T bottom) - : left(left), top(top), right(right), bottom(bottom) {} + constexpr Rectangle(T left_, T top_, T right_, T bottom_) + : left(left_), top(top_), right(right_), bottom(bottom_) {} [[nodiscard]] T GetWidth() const { if constexpr (std::is_floating_point_v) { diff --git a/src/common/vector_math.h b/src/common/vector_math.h index 2a0fcf541..22dba3c2d 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -87,7 +87,13 @@ public: template [[nodiscard]] constexpr Vec2 operator*(const V& f) const { - return {x * f, y * f}; + using TV = decltype(T{} * V{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(x) * static_cast(f)), + static_cast(static_cast(y) * static_cast(f)), + }; } template @@ -98,7 +104,13 @@ public: template [[nodiscard]] constexpr Vec2 operator/(const V& f) const { - return {x / f, y / f}; + using TV = decltype(T{} / V{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(x) / static_cast(f)), + static_cast(static_cast(y) / static_cast(f)), + }; } template @@ -168,7 +180,10 @@ public: template [[nodiscard]] constexpr Vec2 operator*(const V& f, const Vec2& vec) { - return Vec2(f * vec.x, f * vec.y); + using C = std::common_type_t; + + return Vec2(static_cast(static_cast(f) * static_cast(vec.x)), + static_cast(static_cast(f) * static_cast(vec.y))); } using Vec2f = Vec2; @@ -237,7 +252,14 @@ public: template [[nodiscard]] constexpr Vec3 operator*(const V& f) const { - return {x * f, y * f, z * f}; + using TV = decltype(T{} * V{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(x) * static_cast(f)), + static_cast(static_cast(y) * static_cast(f)), + static_cast(static_cast(z) * static_cast(f)), + }; } template @@ -247,7 +269,14 @@ public: } template [[nodiscard]] constexpr Vec3 operator/(const V& f) const { - return {x / f, y / f, z / f}; + using TV = decltype(T{} / V{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(x) / static_cast(f)), + static_cast(static_cast(y) / static_cast(f)), + static_cast(static_cast(z) / static_cast(f)), + }; } template @@ -367,7 +396,11 @@ public: template [[nodiscard]] constexpr Vec3 operator*(const V& f, const Vec3& vec) { - return Vec3(f * vec.x, f * vec.y, f * vec.z); + using C = std::common_type_t; + + return Vec3(static_cast(static_cast(f) * static_cast(vec.x)), + static_cast(static_cast(f) * static_cast(vec.y)), + static_cast(static_cast(f) * static_cast(vec.z))); } template <> @@ -446,7 +479,15 @@ public: template [[nodiscard]] constexpr Vec4 operator*(const V& f) const { - return {x * f, y * f, z * f, w * f}; + using TV = decltype(T{} * V{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(x) * static_cast(f)), + static_cast(static_cast(y) * static_cast(f)), + static_cast(static_cast(z) * static_cast(f)), + static_cast(static_cast(w) * static_cast(f)), + }; } template @@ -457,7 +498,15 @@ public: template [[nodiscard]] constexpr Vec4 operator/(const V& f) const { - return {x / f, y / f, z / f, w / f}; + using TV = decltype(T{} / V{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(x) / static_cast(f)), + static_cast(static_cast(y) / static_cast(f)), + static_cast(static_cast(z) / static_cast(f)), + static_cast(static_cast(w) / static_cast(f)), + }; } template @@ -582,7 +631,15 @@ public: template [[nodiscard]] constexpr Vec4 operator*(const V& f, const Vec4& vec) { - return {f * vec.x, f * vec.y, f * vec.z, f * vec.w}; + using TV = decltype(V{} * T{}); + using C = std::common_type_t; + + return { + static_cast(static_cast(f) * static_cast(vec.x)), + static_cast(static_cast(f) * static_cast(vec.y)), + static_cast(static_cast(f) * static_cast(vec.z)), + static_cast(static_cast(f) * static_cast(vec.w)), + }; } using Vec4f = Vec4; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index c84685214..7b39a38c1 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -29,6 +29,35 @@ add_library(input_common STATIC udp/udp.h ) +if (MSVC) + target_compile_options(input_common PRIVATE + # 'expression' : signed/unsigned mismatch + /we4018 + # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) + /we4244 + # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch + /we4245 + # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4254 + # 'var' : conversion from 'size_t' to 'type', possible loss of data + /we4267 + # 'context' : truncation from 'type1' to 'type2' + /we4305 + ) +else() + target_compile_options(input_common PRIVATE + -Werror=conversion + -Werror=ignored-qualifiers + -Werror=implicit-fallthrough + -Werror=reorder + -Werror=shadow + -Werror=sign-compare + -Werror=unused-but-set-parameter + -Werror=unused-but-set-variable + -Werror=unused-variable + ) +endif() + if(SDL2_FOUND) target_sources(input_common PRIVATE sdl/sdl_impl.cpp diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index 6cabdaa3c..74744d7f3 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -20,18 +20,22 @@ public: constexpr float SQRT_HALF = 0.707106781f; int x = 0, y = 0; - if (right->GetStatus()) + if (right->GetStatus()) { ++x; - if (left->GetStatus()) + } + if (left->GetStatus()) { --x; - if (up->GetStatus()) + } + if (up->GetStatus()) { ++y; - if (down->GetStatus()) + } + if (down->GetStatus()) { --y; + } - float coef = modifier->GetStatus() ? modifier_scale : 1.0f; - return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF), - y * coef * (x == 0 ? 1.0f : SQRT_HALF)); + const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; + return std::make_tuple(static_cast(x) * coef * (y == 0 ? 1.0f : SQRT_HALF), + static_cast(y) * coef * (x == 0 ? 1.0f : SQRT_HALF)); } bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index 89c148aba..c95feb0d7 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -21,7 +21,7 @@ namespace GCAdapter { -/// Used to loop through and assign button in poller +// Used to loop through and assign button in poller constexpr std::array PadButtonArray{ PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R, @@ -29,6 +29,18 @@ constexpr std::array PadButtonArray{ PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START, }; +static void PadToState(const GCPadStatus& pad, GCState& out_state) { + for (const auto& button : PadButtonArray) { + const auto button_key = static_cast(button); + const auto button_value = (pad.button & button_key) != 0; + out_state.buttons.insert_or_assign(static_cast(button_key), button_value); + } + + for (std::size_t i = 0; i < pad.axis_values.size(); ++i) { + out_state.axes.insert_or_assign(static_cast(i), pad.axis_values[i]); + } +} + Adapter::Adapter() { if (usb_adapter_handle != nullptr) { return; @@ -78,17 +90,17 @@ GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array& ad for (std::size_t i = 0; i < b1_buttons.size(); ++i) { if ((b1 & (1U << i)) != 0) { - pad.button |= static_cast(b1_buttons[i]); + pad.button = static_cast(pad.button | static_cast(b1_buttons[i])); } } for (std::size_t j = 0; j < b2_buttons.size(); ++j) { if ((b2 & (1U << j)) != 0) { - pad.button |= static_cast(b2_buttons[j]); + pad.button = static_cast(pad.button | static_cast(b2_buttons[j])); } } for (PadAxes axis : axes) { - const std::size_t index = static_cast(axis); + const auto index = static_cast(axis); pad.axis_values[index] = adapter_payload[offset + 3 + index]; } @@ -100,17 +112,6 @@ GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array& ad return pad; } -void Adapter::PadToState(const GCPadStatus& pad, GCState& state) { - for (const auto& button : PadButtonArray) { - const u16 button_value = static_cast(button); - state.buttons.insert_or_assign(button_value, pad.button & button_value); - } - - for (size_t i = 0; i < pad.axis_values.size(); ++i) { - state.axes.insert_or_assign(static_cast(i), pad.axis_values[i]); - } -} - void Adapter::Read() { LOG_DEBUG(Input, "GC Adapter Read() thread started"); @@ -250,7 +251,7 @@ void Adapter::GetGCEndpoint(libusb_device* device) { const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i]; for (u8 e = 0; e < interface->bNumEndpoints; e++) { const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; - if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { + if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) != 0) { input_endpoint = endpoint->bEndpointAddress; } else { output_endpoint = endpoint->bEndpointAddress; @@ -419,7 +420,7 @@ const std::array& Adapter::GetPadState() const { return state; } -int Adapter::GetOriginValue(int port, int axis) const { +int Adapter::GetOriginValue(u32 port, u32 axis) const { return origin_status[port].axis_values[axis]; } diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index 75bf9fe74..4f5f3de8e 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h @@ -60,7 +60,7 @@ struct GCPadStatus { struct GCState { std::unordered_map buttons; - std::unordered_map axes; + std::unordered_map axes; }; enum class ControllerTypes { None, Wired, Wireless }; @@ -89,13 +89,11 @@ public: std::array& GetPadState(); const std::array& GetPadState() const; - int GetOriginValue(int port, int axis) const; + int GetOriginValue(u32 port, u32 axis) const; private: GCPadStatus GetPadStatus(std::size_t port, const std::array& adapter_payload); - void PadToState(const GCPadStatus& pad, GCState& state); - void Read(); /// Resets status of device connected to port diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 92e9e8e89..893556916 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -15,7 +15,7 @@ namespace InputCommon { class GCButton final : public Input::ButtonDevice { public: - explicit GCButton(int port_, int button_, const GCAdapter::Adapter* adapter) + explicit GCButton(u32 port_, int button_, const GCAdapter::Adapter* adapter) : port(port_), button(button_), gcadapter(adapter) {} ~GCButton() override; @@ -28,14 +28,14 @@ public: } private: - const int port; + const u32 port; const int button; const GCAdapter::Adapter* gcadapter; }; class GCAxisButton final : public Input::ButtonDevice { public: - explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, + explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, const GCAdapter::Adapter* adapter) : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), gcadapter(adapter), @@ -56,8 +56,8 @@ public: } private: - const int port; - const int axis; + const u32 port; + const u32 axis; float threshold; bool trigger_if_greater; const GCAdapter::Adapter* gcadapter; @@ -70,8 +70,8 @@ GCButtonFactory::GCButtonFactory(std::shared_ptr adapter_) GCButton::~GCButton() = default; std::unique_ptr GCButtonFactory::Create(const Common::ParamPackage& params) { - const int button_id = params.Get("button", 0); - const int port = params.Get("port", 0); + const auto button_id = params.Get("button", 0); + const auto port = static_cast(params.Get("port", 0)); constexpr int PAD_STICK_ID = static_cast(GCAdapter::PadButton::PAD_STICK); @@ -149,25 +149,27 @@ void GCButtonFactory::EndConfiguration() { class GCAnalog final : public Input::AnalogDevice { public: - GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, - const GCAdapter::Adapter* adapter, float range_) + explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, + const GCAdapter::Adapter* adapter, float range_) : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), origin_value_x(static_cast(adapter->GetOriginValue(port_, axis_x_))), origin_value_y(static_cast(adapter->GetOriginValue(port_, axis_y_))), range(range_) {} - float GetAxis(int axis) const { + float GetAxis(u32 axis) const { if (gcadapter->DeviceConnected(port)) { std::lock_guard lock{mutex}; const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; - return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range); + const auto axis_value = + static_cast(gcadapter->GetPadState()[port].axes.at(axis)); + return (axis_value - origin_value) / (100.0f * range); } return 0.0f; } - std::pair GetAnalog(int axis_x, int axis_y) const { - float x = GetAxis(axis_x); - float y = GetAxis(axis_y); + std::pair GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { + float x = GetAxis(analog_axis_x); + float y = GetAxis(analog_axis_y); // Make sure the coordinates are in the unit circle, // otherwise normalize it. @@ -208,9 +210,9 @@ public: } private: - const int port; - const int axis_x; - const int axis_y; + const u32 port; + const u32 axis_x; + const u32 axis_y; const float deadzone; const GCAdapter::Adapter* gcadapter; const float origin_value_x; @@ -231,11 +233,11 @@ GCAnalogFactory::GCAnalogFactory(std::shared_ptr adapter_) * - "axis_y": the index of the axis to be bind as y-axis */ std::unique_ptr GCAnalogFactory::Create(const Common::ParamPackage& params) { - 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, 1.0f); - const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); + const auto port = static_cast(params.Get("port", 0)); + const auto axis_x = static_cast(params.Get("axis_x", 0)); + const auto axis_y = static_cast(params.Get("axis_y", 1)); + const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); + const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); return std::make_unique(port, axis_x, axis_y, deadzone, adapter.get(), range); } @@ -256,7 +258,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { for (std::size_t port = 0; port < queue.size(); ++port) { while (queue[port].Pop(pad)) { if (pad.axis == GCAdapter::PadAxes::Undefined || - std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) { + std::abs((static_cast(pad.axis_value) - 128.0f) / 128.0f) < 0.1f) { continue; } // An analog device needs two axes, so we need to store the axis for later and wait for diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index afb8e6612..24a6f7a33 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp @@ -49,8 +49,9 @@ public: void ChangeKeyStatus(int key_code, bool pressed) { std::lock_guard guard{mutex}; for (const KeyButtonPair& pair : list) { - if (pair.key_code == key_code) + if (pair.key_code == key_code) { pair.key_button->status.store(pressed); + } } } @@ -73,7 +74,7 @@ KeyButton::~KeyButton() { } std::unique_ptr Keyboard::Create(const Common::ParamPackage& params) { - int key_code = params.Get("code", 0); + const int key_code = params.Get("code", 0); std::unique_ptr button = std::make_unique(key_button_list); key_button_list->AddKeyButton(key_code, button.get()); return button; diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 3d97d95f7..d32fd8b81 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -196,6 +196,10 @@ ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPacka return impl->GetButtonMappingForDevice(device); } +MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const { + return impl->GetMotionMappingForDevice(device); +} + GCAnalogFactory* InputSubsystem::GetGCAnalogs() { return impl->gcanalog.get(); } diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index 69fd3c1d2..d4da5596b 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp @@ -18,11 +18,11 @@ namespace InputCommon { // Implementation class of the motion emulation device class MotionEmuDevice { public: - MotionEmuDevice(int update_millisecond, float sensitivity) - : update_millisecond(update_millisecond), + explicit MotionEmuDevice(int update_millisecond_, float sensitivity_) + : update_millisecond(update_millisecond_), update_duration(std::chrono::duration_cast( std::chrono::milliseconds(update_millisecond))), - sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {} + sensitivity(sensitivity_), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {} ~MotionEmuDevice() { if (motion_emu_thread.joinable()) { @@ -37,16 +37,18 @@ public: } void Tilt(int x, int y) { - auto mouse_move = Common::MakeVec(x, y) - mouse_origin; - if (is_tilting) { - std::lock_guard guard{tilt_mutex}; - if (mouse_move.x == 0 && mouse_move.y == 0) { - tilt_angle = 0; - } else { - tilt_direction = mouse_move.Cast(); - tilt_angle = - std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f); - } + if (!is_tilting) { + return; + } + + std::lock_guard guard{tilt_mutex}; + const auto mouse_move = Common::MakeVec(x, y) - mouse_origin; + if (mouse_move.x == 0 && mouse_move.y == 0) { + tilt_angle = 0; + } else { + tilt_direction = mouse_move.Cast(); + tilt_angle = + std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f); } } @@ -86,11 +88,10 @@ private: void MotionEmuThread() { auto update_time = std::chrono::steady_clock::now(); Common::Quaternion q = Common::MakeQuaternion(Common::Vec3(), 0); - Common::Quaternion old_q; while (!shutdown_event.WaitUntil(update_time)) { update_time += update_duration; - old_q = q; + const Common::Quaternion old_q = q; { std::lock_guard guard{tilt_mutex}; @@ -100,14 +101,14 @@ private: Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle); } - auto inv_q = q.Inverse(); + const auto inv_q = q.Inverse(); // Set the gravity vector in world space auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f); // Find the angular rate vector in world space auto angular_rate = ((q - old_q) * inv_q).xyz * 2; - angular_rate *= 1000 / update_millisecond / Common::PI * 180; + angular_rate *= static_cast(1000 / update_millisecond) / Common::PI * 180.0f; // Transform the two vectors from world space to 3DS space gravity = QuaternionRotate(inv_q, gravity); @@ -136,7 +137,7 @@ private: // can forward all the inputs to the implementation only when it is valid. class MotionEmuDeviceWrapper : public Input::MotionDevice { public: - MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) { + explicit MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) { device = std::make_shared(update_millisecond, sensitivity); } @@ -148,8 +149,8 @@ public: }; std::unique_ptr MotionEmu::Create(const Common::ParamPackage& params) { - int update_period = params.Get("update_period", 100); - float sensitivity = params.Get("sensitivity", 0.01f); + const int update_period = params.Get("update_period", 100); + const float sensitivity = params.Get("sensitivity", 0.01f); auto device_wrapper = std::make_unique(update_period, sensitivity); // Previously created device is disconnected here. Having two motion devices for 3DS is not // expected. diff --git a/src/input_common/motion_from_button.cpp b/src/input_common/motion_from_button.cpp index 9d459f963..29045a673 100644 --- a/src/input_common/motion_from_button.cpp +++ b/src/input_common/motion_from_button.cpp @@ -11,7 +11,7 @@ class MotionKey final : public Input::MotionDevice { public: using Button = std::unique_ptr; - MotionKey(Button key_) : key(std::move(key_)) {} + explicit MotionKey(Button key_) : key(std::move(key_)) {} Input::MotionStatus GetStatus() const override { diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp index e89019723..f77ba535d 100644 --- a/src/input_common/motion_input.cpp +++ b/src/input_common/motion_input.cpp @@ -8,8 +8,7 @@ namespace InputCommon { -MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) - : kp(new_kp), ki(new_ki), kd(new_kd), quat{{0, 0, -1}, 0} {} +MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {} void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { accel = acceleration; @@ -59,7 +58,7 @@ bool MotionInput::IsCalibrated(f32 sensitivity) const { } void MotionInput::UpdateRotation(u64 elapsed_time) { - const f32 sample_period = elapsed_time / 1000000.0f; + const auto sample_period = static_cast(elapsed_time) / 1000000.0f; if (sample_period > 0.1f) { return; } @@ -75,7 +74,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) { f32 q2 = quat.xyz[0]; f32 q3 = quat.xyz[1]; f32 q4 = quat.xyz[2]; - const f32 sample_period = elapsed_time / 1000000.0f; + const auto sample_period = static_cast(elapsed_time) / 1000000.0f; // Ignore invalid elapsed time if (sample_period > 0.1f) { @@ -203,21 +202,21 @@ Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_m std::random_device device; std::mt19937 gen(device()); std::uniform_int_distribution distribution(-1000, 1000); - const Common::Vec3f gyroscope = { - distribution(gen) * 0.001f, - distribution(gen) * 0.001f, - distribution(gen) * 0.001f, + const Common::Vec3f gyroscope{ + static_cast(distribution(gen)) * 0.001f, + static_cast(distribution(gen)) * 0.001f, + static_cast(distribution(gen)) * 0.001f, }; - const Common::Vec3f accelerometer = { - distribution(gen) * 0.001f, - distribution(gen) * 0.001f, - distribution(gen) * 0.001f, + const Common::Vec3f accelerometer{ + static_cast(distribution(gen)) * 0.001f, + static_cast(distribution(gen)) * 0.001f, + static_cast(distribution(gen)) * 0.001f, }; - const Common::Vec3f rotation = {}; - const std::array orientation = { - Common::Vec3f{1.0f, 0, 0}, - Common::Vec3f{0, 1.0f, 0}, - Common::Vec3f{0, 0, 1.0f}, + constexpr Common::Vec3f rotation; + constexpr std::array orientation{ + Common::Vec3f{1.0f, 0.0f, 0.0f}, + Common::Vec3f{0.0f, 1.0f, 0.0f}, + Common::Vec3f{0.0f, 0.0f, 1.0f}, }; return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation}; } @@ -247,9 +246,6 @@ void MotionInput::SetOrientationFromAccelerometer() { const f32 sample_period = 0.015f; const auto normal_accel = accel.Normalized(); - const f32 ax = -normal_accel.x; - const f32 ay = normal_accel.y; - const f32 az = -normal_accel.z; while (!IsCalibrated(0.01f) && ++iterations < 100) { // Short name local variable for readability @@ -258,7 +254,7 @@ void MotionInput::SetOrientationFromAccelerometer() { f32 q3 = quat.xyz[1]; f32 q4 = quat.xyz[2]; - Common::Vec3f rad_gyro = {}; + Common::Vec3f rad_gyro; const f32 ax = -normal_accel.x; const f32 ay = normal_accel.y; const f32 az = -normal_accel.z; diff --git a/src/input_common/motion_input.h b/src/input_common/motion_input.h index 6342d0318..abb957f04 100644 --- a/src/input_common/motion_input.h +++ b/src/input_common/motion_input.h @@ -22,7 +22,7 @@ public: MotionInput& operator=(MotionInput&&) = default; void SetAcceleration(const Common::Vec3f& acceleration); - void SetGyroscope(const Common::Vec3f& acceleration); + void SetGyroscope(const Common::Vec3f& gyroscope); void SetQuaternion(const Common::Quaternion& quaternion); void SetGyroDrift(const Common::Vec3f& drift); void SetGyroThreshold(f32 threshold); @@ -49,16 +49,16 @@ private: void SetOrientationFromAccelerometer(); // PID constants - const f32 kp; - const f32 ki; - const f32 kd; + f32 kp; + f32 ki; + f32 kd; // PID errors Common::Vec3f real_error; Common::Vec3f integral_error; Common::Vec3f derivative_error; - Common::Quaternion quat; + Common::Quaternion quat{{0.0f, 0.0f, -1.0f}, 0.0f}; Common::Vec3f rotations; Common::Vec3f accel; Common::Vec3f gyro; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index bd480570a..8c2cef35d 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -56,9 +56,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) { class SDLJoystick { public: SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, - SDL_GameController* gamecontroller) + SDL_GameController* game_controller) : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, - sdl_controller{gamecontroller, &SDL_GameControllerClose} {} + sdl_controller{game_controller, &SDL_GameControllerClose} {} void SetButton(int button, bool value) { std::lock_guard lock{mutex}; @@ -77,10 +77,10 @@ public: float GetAxis(int axis, float range) const { std::lock_guard lock{mutex}; - return state.axes.at(axis) / (32767.0f * range); + return static_cast(state.axes.at(axis)) / (32767.0f * range); } - bool RumblePlay(f32 amp_low, f32 amp_high, int time) { + bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) { const u16 raw_amp_low = static_cast(amp_low * 0xFFFF); const u16 raw_amp_high = static_cast(amp_high * 0xFFFF); // Lower drastically the number of state changes @@ -124,7 +124,7 @@ public: return std::make_tuple(x, y); } - const InputCommon::MotionInput& GetMotion() const { + const MotionInput& GetMotion() const { return motion; } @@ -172,15 +172,15 @@ private: } state; std::string guid; int port; - u16 last_state_rumble_high; - u16 last_state_rumble_low; + u16 last_state_rumble_high = 0; + u16 last_state_rumble_low = 0; std::chrono::time_point last_vibration; std::unique_ptr sdl_joystick; std::unique_ptr sdl_controller; mutable std::mutex mutex; - // motion is initalized without PID values as motion input is not aviable for SDL2 - InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; + // Motion is initialized without PID values as motion input is not aviable for SDL2 + MotionInput motion{0.0f, 0.0f, 0.0f}; }; std::shared_ptr SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { @@ -192,7 +192,7 @@ std::shared_ptr SDLState::GetSDLJoystickByGUID(const std::string& g nullptr, nullptr); it->second.emplace_back(std::move(joystick)); } - return it->second[port]; + return it->second[static_cast(port)]; } auto joystick = std::make_shared(guid, 0, nullptr, nullptr); return joystick_map[guid].emplace_back(std::move(joystick)); @@ -212,7 +212,7 @@ std::shared_ptr SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_ 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 + // This is the common case: There is already an existing SDL_Joystick mapped to a // SDLJoystick. return the SDLJoystick return *vec_it; } @@ -220,7 +220,7 @@ std::shared_ptr SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_ // Search for a SDLJoystick without a mapped SDL_Joystick... const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), [](const std::shared_ptr& joystick) { - return !joystick->GetSDLJoystick(); + return joystick->GetSDLJoystick() == nullptr; }); if (nullptr_it != map_it->second.end()) { // ... and map it @@ -273,22 +273,21 @@ void SDLState::InitJoystick(int joystick_index) { void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { const std::string guid = GetGUID(sdl_joystick); - std::shared_ptr joystick; + std::shared_ptr found_joystick; { std::lock_guard lock{joystick_map_mutex}; // This call to guid is safe since the joystick is guaranteed to be in the map const 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& joystick) { - return joystick->GetSDLJoystick() == sdl_joystick; - }); - joystick = *joystick_it; + const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), + [&sdl_joystick](const auto& joystick) { + return joystick->GetSDLJoystick() == sdl_joystick; + }); + found_joystick = *joystick_it; } // Destruct SDL_Joystick outside the lock guard because SDL can internally call the // event callback which locks the mutex again. - joystick->SetSDLJoystick(nullptr, nullptr); + found_joystick->SetSDLJoystick(nullptr, nullptr); } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { @@ -392,8 +391,8 @@ private: class SDLAnalog final : public Input::AnalogDevice { public: - SDLAnalog(std::shared_ptr joystick_, int axis_x_, int axis_y_, float deadzone_, - float range_) + explicit SDLAnalog(std::shared_ptr joystick_, int axis_x_, int axis_y_, + float deadzone_, float range_) : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), range(range_) {} @@ -672,13 +671,13 @@ SDLState::SDLState() { RegisterFactory("sdl", button_factory); RegisterFactory("sdl", motion_factory); - // If the frontend is going to manage the event loop, then we dont start one here - start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); + // If the frontend is going to manage the event loop, then we don't start one here + start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0; if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); return; } - has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); + has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0; 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()); } @@ -723,8 +722,8 @@ std::vector SDLState::GetInputDevices() { std::vector devices; for (const auto& [key, value] : joystick_map) { for (const auto& joystick : value) { - auto joy = joystick->GetSDLJoystick(); - if (auto controller = joystick->GetSDLGameController()) { + auto* joy = joystick->GetSDLJoystick(); + if (auto* controller = joystick->GetSDLGameController()) { std::string name = fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ @@ -748,7 +747,7 @@ std::vector SDLState::GetInputDevices() { } namespace { -Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, +Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, float value = 0.1f) { Common::ParamPackage params({{"engine", "sdl"}}); params.Set("port", port); @@ -764,7 +763,7 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid return params; } -Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { +Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) { Common::ParamPackage params({{"engine", "sdl"}}); params.Set("port", port); params.Set("guid", std::move(guid)); @@ -772,7 +771,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid return params; } -Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) { +Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) { Common::ParamPackage params({{"engine", "sdl"}}); params.Set("port", port); @@ -802,17 +801,19 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve case SDL_JOYAXISMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jaxis.axis, event.jaxis.value); + static_cast(event.jaxis.axis), + event.jaxis.value); } case SDL_JOYBUTTONUP: { const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jbutton.button); + static_cast(event.jbutton.button)); } case SDL_JOYHATMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jhat.hat, event.jhat.value); + static_cast(event.jhat.hat), + static_cast(event.jhat.value)); } } return {}; @@ -823,17 +824,19 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve case SDL_JOYAXISMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jaxis.axis, event.jaxis.value); + static_cast(event.jaxis.axis), + event.jaxis.value); } case SDL_JOYBUTTONUP: { const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jbutton.button); + static_cast(event.jbutton.button)); } case SDL_JOYHATMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - event.jhat.hat, event.jhat.value); + static_cast(event.jhat.hat), + static_cast(event.jhat.value)); } } return {}; @@ -1062,7 +1065,7 @@ public: if (event.type == SDL_JOYAXISMOTION) { const auto axis = event.jaxis.axis; const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - const auto controller = joystick->GetSDLGameController(); + auto* const controller = joystick->GetSDLGameController(); if (controller) { const auto axis_left_x = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index 98da0ef1a..c37716aae 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -11,9 +11,11 @@ namespace InputCommon { class TouchFromButtonDevice final : public Input::TouchDevice { public: TouchFromButtonDevice() { - for (const auto& config_entry : - Settings::values.touch_from_button_maps[Settings::values.touch_from_button_map_index] - .buttons) { + const auto button_index = + static_cast(Settings::values.touch_from_button_map_index); + const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons; + + for (const auto& config_entry : buttons) { const Common::ParamPackage package{config_entry}; map.emplace_back( Input::CreateDevice(config_entry), diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 9d0b9f31d..bb109562c 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -26,11 +26,11 @@ class Socket { public: using clock = std::chrono::system_clock; - explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, - SocketCallback callback) - : callback(std::move(callback)), timer(io_service), - socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), - pad_index(pad_index) { + explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_, + SocketCallback callback_) + : callback(std::move(callback_)), timer(io_service), + socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_), + pad_index(pad_index_) { boost::system::error_code ec{}; auto ipv4 = boost::asio::ip::make_address_v4(host, ec); if (ec.value() != boost::system::errc::success) { @@ -93,13 +93,17 @@ private: void HandleSend(const boost::system::error_code& error) { boost::system::error_code _ignored{}; // Send a request for getting port info for the pad - Request::PortInfo port_info{1, {pad_index, 0, 0, 0}}; + const Request::PortInfo port_info{1, {static_cast(pad_index), 0, 0, 0}}; const auto port_message = Request::Create(port_info, client_id); std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); // Send a request for getting pad data for the pad - Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS}; + const Request::PadData pad_data{ + Request::PadData::Flags::Id, + static_cast(pad_index), + EMPTY_MAC_ADDRESS, + }; const auto pad_message = Request::Create(pad_data, client_id); std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored); @@ -112,7 +116,7 @@ private: udp::socket socket; u32 client_id{}; - u8 pad_index{}; + std::size_t pad_index{}; static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message); static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message); @@ -133,7 +137,7 @@ static void SocketLoop(Socket* socket) { Client::Client() { LOG_INFO(Input, "Udp Initialization started"); for (std::size_t client = 0; client < clients.size(); client++) { - u8 pad = client % 4; + const auto pad = client % 4; StartCommunication(client, Settings::values.udp_input_address, Settings::values.udp_input_port, pad, 24872); // Set motion parameters @@ -166,9 +170,9 @@ std::vector Client::GetInputDevices() const { bool Client::DeviceConnected(std::size_t pad) const { // Use last timestamp to detect if the socket has stopped sending data const auto now = std::chrono::system_clock::now(); - u64 time_difference = + const auto time_difference = static_cast( std::chrono::duration_cast(now - clients[pad].last_motion_update) - .count(); + .count()); return time_difference < 1000 && clients[pad].active == 1; } @@ -177,9 +181,9 @@ void Client::ReloadUDPClient() { ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client); } } -void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { +void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_index, u32 client_id) { // client number must be determined from host / port and pad index - std::size_t client = pad_index; + const std::size_t client = pad_index; clients[client].socket->Stop(); clients[client].thread.join(); StartCommunication(client, host, port, pad_index, client_id); @@ -194,8 +198,8 @@ void Client::OnPortInfo(Response::PortInfo data) { } void Client::OnPadData(Response::PadData data) { - // client number must be determined from host / port and pad index - std::size_t client = data.info.id; + // Client number must be determined from host / port and pad index + const std::size_t client = data.info.id; LOG_TRACE(Input, "PadData packet received"); if (data.packet_counter == clients[client].packet_sequence) { LOG_WARNING( @@ -207,11 +211,12 @@ void Client::OnPadData(Response::PadData data) { clients[client].active = data.info.is_pad_active; clients[client].packet_sequence = data.packet_counter; const auto now = std::chrono::system_clock::now(); - u64 time_difference = std::chrono::duration_cast( - now - clients[client].last_motion_update) - .count(); + const auto time_difference = + static_cast(std::chrono::duration_cast( + now - clients[client].last_motion_update) + .count()); clients[client].last_motion_update = now; - Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; + const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); // Gyroscope values are not it the correct scale from better joy. // Dividing by 312 allows us to make one full turn = 1 turn @@ -237,9 +242,11 @@ void Client::OnPadData(Response::PadData data) { const u16 min_y = clients[client].status.touch_calibration->min_y; const u16 max_y = clients[client].status.touch_calibration->max_y; - x = (std::clamp(static_cast(data.touch_1.x), min_x, max_x) - min_x) / + x = static_cast(std::clamp(static_cast(data.touch_1.x), min_x, max_x) - + min_x) / static_cast(max_x - min_x); - y = (std::clamp(static_cast(data.touch_1.y), min_y, max_y) - min_y) / + y = static_cast(std::clamp(static_cast(data.touch_1.y), min_y, max_y) - + min_y) / static_cast(max_y - min_y); } @@ -253,8 +260,8 @@ void Client::OnPadData(Response::PadData data) { } } -void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, - u32 client_id) { +void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, + std::size_t pad_index, u32 client_id) { SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, [this](Response::PortInfo info) { OnPortInfo(info); }, [this](Response::PadData data) { OnPadData(data); }}; @@ -264,9 +271,9 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16 } void Client::Reset() { - for (std::size_t client = 0; client < clients.size(); client++) { - clients[client].socket->Stop(); - clients[client].thread.join(); + for (auto& client : clients) { + client.socket->Stop(); + client.thread.join(); } } @@ -325,7 +332,7 @@ const std::array, 4>& Client::GetPadQueue() cons return pad_queue; } -void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, +void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, std::function success_callback, std::function failure_callback) { std::thread([=] { @@ -346,7 +353,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie } CalibrationConfigurationJob::CalibrationConfigurationJob( - const std::string& host, u16 port, u8 pad_index, u32 client_id, + const std::string& host, u16 port, std::size_t pad_index, u32 client_id, std::function status_callback, std::function data_callback) { @@ -366,7 +373,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( current_status = Status::Ready; status_callback(current_status); } - if (!data.touch_1.is_active) { + if (data.touch_1.is_active == 0) { return; } LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 523dc6a7a..2491a03a2 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -84,8 +84,8 @@ public: bool DeviceConnected(std::size_t pad) const; void ReloadUDPClient(); - void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, - u32 client_id = 24872); + void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, + std::size_t pad_index = 0, u32 client_id = 24872); std::array, 4>& GetPadQueue(); const std::array, 4>& GetPadQueue() const; @@ -99,7 +99,7 @@ private: DeviceStatus status; std::thread thread; u64 packet_sequence = 0; - u8 active; + u8 active = 0; // Realtime values // motion is initalized with PID values for drift correction on joycons @@ -113,8 +113,8 @@ private: void OnVersion(Response::Version); void OnPortInfo(Response::PortInfo); void OnPadData(Response::PadData); - void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, - u32 client_id); + void StartCommunication(std::size_t client, const std::string& host, u16 port, + std::size_t pad_index, u32 client_id); void UpdateYuzuSettings(std::size_t client, const Common::Vec3& acc, const Common::Vec3& gyro, bool touch); @@ -139,7 +139,7 @@ public: * @param status_callback Callback for job status updates * @param data_callback Called when calibration data is ready */ - explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index, + explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, std::function status_callback, std::function data_callback); ~CalibrationConfigurationJob(); @@ -149,7 +149,7 @@ private: Common::Event complete_event; }; -void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, +void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, std::function success_callback, std::function failure_callback); diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index eba077a36..71a76a7aa 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include #include #include #include "common/assert.h" @@ -15,8 +13,8 @@ namespace InputCommon { class UDPMotion final : public Input::MotionDevice { public: - UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) - : ip(ip_), port(port_), pad(pad_), client(client_) {} + explicit UDPMotion(std::string ip_, int port_, u32 pad_, CemuhookUDP::Client* client_) + : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} Input::MotionStatus GetStatus() const override { return client->GetPadState(pad).motion_status; @@ -25,7 +23,7 @@ public: private: const std::string ip; const int port; - const int pad; + const u32 pad; CemuhookUDP::Client* client; mutable std::mutex mutex; }; @@ -40,11 +38,11 @@ UDPMotionFactory::UDPMotionFactory(std::shared_ptr client_) * - "port": the nth jcpad on the adapter */ std::unique_ptr UDPMotionFactory::Create(const Common::ParamPackage& params) { - const std::string ip = params.Get("ip", "127.0.0.1"); - const int port = params.Get("port", 26760); - const int pad = params.Get("pad_index", 0); + auto ip = params.Get("ip", "127.0.0.1"); + const auto port = params.Get("port", 26760); + const auto pad = static_cast(params.Get("pad_index", 0)); - return std::make_unique(ip, port, pad, client.get()); + return std::make_unique(std::move(ip), port, pad, client.get()); } void UDPMotionFactory::BeginConfiguration() { @@ -79,7 +77,7 @@ Common::ParamPackage UDPMotionFactory::GetNextInput() { class UDPTouch final : public Input::TouchDevice { public: - UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) + explicit UDPTouch(std::string ip_, int port_, u32 pad_, CemuhookUDP::Client* client_) : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} std::tuple GetStatus() const override { @@ -89,7 +87,7 @@ public: private: const std::string ip; const int port; - const int pad; + const u32 pad; CemuhookUDP::Client* client; mutable std::mutex mutex; }; @@ -104,11 +102,11 @@ UDPTouchFactory::UDPTouchFactory(std::shared_ptr client_) * - "port": the nth jcpad on the adapter */ std::unique_ptr UDPTouchFactory::Create(const Common::ParamPackage& params) { - const std::string ip = params.Get("ip", "127.0.0.1"); - const int port = params.Get("port", 26760); - const int pad = params.Get("pad_index", 0); + auto ip = params.Get("ip", "127.0.0.1"); + const auto port = params.Get("port", 26760); + const auto pad = static_cast(params.Get("pad_index", 0)); - return std::make_unique(ip, port, pad, client.get()); + return std::make_unique(std::move(ip), port, pad, client.get()); } void UDPTouchFactory::BeginConfiguration() { diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index c7d085151..170574d9b 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -193,7 +193,7 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() { udp_test_in_progress = true; InputCommon::CemuhookUDP::TestCommunication( ui->udp_server->text().toStdString(), static_cast(ui->udp_port->text().toInt()), - static_cast(ui->udp_pad_index->currentIndex()), 24872, + static_cast(ui->udp_pad_index->currentIndex()), 24872, [this] { LOG_INFO(Frontend, "UDP input test success"); QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); -- cgit v1.2.3 From 7b3f5845d28fdf245967b930452d392458fd8535 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 3 Sep 2020 11:07:57 -0400 Subject: sdl_impl: Erase the SDLJoystick entry after removing a controller Previously, disconnecting a controller still leaves a null SDLJoystick entry within the vector of SDLJoysticks mapped by GUID. When a DirectInput device of the same GUID is reconnected, it adds that device to a new port causing non-detectable input. Furthermore, opening the "Configure" menu would cause yuzu to crash since it first tries to resolve the name of a null SDLJoystick entry that was not removed. Resolve this by properly erasing the SDLJoystick entry from the vector. --- src/input_common/sdl/sdl_impl.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 8c2cef35d..9c3035920 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -273,21 +273,19 @@ void SDLState::InitJoystick(int joystick_index) { void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { const std::string guid = GetGUID(sdl_joystick); - std::shared_ptr found_joystick; - { - std::lock_guard lock{joystick_map_mutex}; - // This call to guid is safe since the joystick is guaranteed to be in the map - const 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 auto& joystick) { - return joystick->GetSDLJoystick() == sdl_joystick; - }); - found_joystick = *joystick_it; - } - - // Destruct SDL_Joystick outside the lock guard because SDL can internally call the - // event callback which locks the mutex again. - found_joystick->SetSDLJoystick(nullptr, nullptr); + std::lock_guard lock{joystick_map_mutex}; + auto& joystick_guid_list = joystick_map[guid]; + auto joystick_it = std::find_if( + joystick_guid_list.begin(), joystick_guid_list.end(), + [&sdl_joystick](auto& joystick) { return joystick->GetSDLJoystick() == sdl_joystick; }); + + if (joystick_it != joystick_guid_list.end()) { + (*joystick_it)->SetSDLJoystick(nullptr, nullptr); + joystick_guid_list.erase(joystick_it); + if (joystick_guid_list.empty()) { + joystick_map.erase(guid); + } + } } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { -- cgit v1.2.3 From 2f852f182a807d828985e08d0b70f1129194fbad Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 21 Oct 2020 08:42:11 -0400 Subject: sdl_impl: Fix controller reconnection issues It turns out that after a controller is disconnected, there is a chance that events from the previous controller are sent/processed after it has been disconnected. This causes the previously disconnected controller to reappear as connected due to GetSDLJoystickBySDLID() emplacing this controller back to the map. Fix this by only returning an SDLJoystick if and only if it exists in the map. --- src/input_common/sdl/sdl_impl.cpp | 169 +++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 85 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 9c3035920..10883e2d9 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -155,15 +155,15 @@ public: return sdl_joystick.get(); } - void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { - sdl_controller.reset(controller); - sdl_joystick.reset(joystick); - } - SDL_GameController* GetSDLGameController() const { return sdl_controller.get(); } + void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { + sdl_joystick.reset(joystick); + sdl_controller.reset(controller); + } + private: struct State { std::unordered_map buttons; @@ -186,69 +186,58 @@ private: std::shared_ptr SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { std::lock_guard lock{joystick_map_mutex}; 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, nullptr); it->second.emplace_back(std::move(joystick)); } + return it->second[static_cast(port)]; } + 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}; const auto map_it = joystick_map.find(guid); - if (map_it != joystick_map.end()) { - const auto vec_it = - std::find_if(map_it->second.begin(), map_it->second.end(), - [&sdl_joystick](const std::shared_ptr& 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 mapped to a - // SDLJoystick. return the SDLJoystick - return *vec_it; - } - // Search for a SDLJoystick without a mapped SDL_Joystick... - const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), - [](const std::shared_ptr& joystick) { - return joystick->GetSDLJoystick() == nullptr; - }); - if (nullptr_it != map_it->second.end()) { - // ... and map it - (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller); - return *nullptr_it; - } + if (map_it == joystick_map.end()) { + return nullptr; + } - // 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, sdl_controller); - return map_it->second.emplace_back(std::move(joystick)); + const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), + [&sdl_joystick](const auto& joystick) { + return joystick->GetSDLJoystick() == sdl_joystick; + }); + + if (vec_it == map_it->second.end()) { + return nullptr; } - auto joystick = std::make_shared(guid, 0, sdl_joystick, sdl_controller); - return joystick_map[guid].emplace_back(std::move(joystick)); + return *vec_it; } 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; } + const std::string guid = GetGUID(sdl_joystick); std::lock_guard lock{joystick_map_mutex}; @@ -257,14 +246,17 @@ void SDLState::InitJoystick(int joystick_index) { 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& joystick) { return !joystick->GetSDLJoystick(); }); - if (it != joystick_guid_list.end()) { - (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); + const auto joystick_it = + std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), + [](const auto& joystick) { return !joystick->GetSDLJoystick(); }); + + if (joystick_it != joystick_guid_list.end()) { + (*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, sdl_gamecontroller); joystick_guid_list.emplace_back(std::move(joystick)); @@ -274,18 +266,14 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { const std::string guid = GetGUID(sdl_joystick); std::lock_guard lock{joystick_map_mutex}; - auto& joystick_guid_list = joystick_map[guid]; - auto joystick_it = std::find_if( - joystick_guid_list.begin(), joystick_guid_list.end(), - [&sdl_joystick](auto& joystick) { return joystick->GetSDLJoystick() == sdl_joystick; }); - - if (joystick_it != joystick_guid_list.end()) { - (*joystick_it)->SetSDLJoystick(nullptr, nullptr); - joystick_guid_list.erase(joystick_it); - if (joystick_guid_list.empty()) { - joystick_map.erase(guid); - } - } + // This call to guid is safe since the joystick is guaranteed to be in the map + const 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 auto& joystick) { + return joystick->GetSDLJoystick() == sdl_joystick; + }); + + (*joystick_it)->SetSDLJoystick(nullptr, nullptr); } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { @@ -720,8 +708,7 @@ std::vector SDLState::GetInputDevices() { std::vector devices; for (const auto& [key, value] : joystick_map) { for (const auto& joystick : value) { - auto* joy = joystick->GetSDLJoystick(); - if (auto* controller = joystick->GetSDLGameController()) { + if (auto* const controller = joystick->GetSDLGameController()) { std::string name = fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ @@ -730,7 +717,7 @@ std::vector SDLState::GetInputDevices() { {"guid", joystick->GetGUID()}, {"port", std::to_string(joystick->GetPort())}, }); - } else if (joy) { + } else if (auto* const joy = joystick->GetSDLJoystick()) { std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, @@ -797,21 +784,27 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { switch (event.type) { case SDL_JOYAXISMOTION: { - const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - static_cast(event.jaxis.axis), - event.jaxis.value); + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { + return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + static_cast(event.jaxis.axis), + event.jaxis.value); + } + break; } case SDL_JOYBUTTONUP: { - const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); - return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - static_cast(event.jbutton.button)); + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) { + return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + static_cast(event.jbutton.button)); + } + break; } case SDL_JOYHATMOTION: { - const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); - return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - static_cast(event.jhat.hat), - static_cast(event.jhat.value)); + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) { + return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + static_cast(event.jhat.hat), + static_cast(event.jhat.value)); + } + break; } } return {}; @@ -820,21 +813,27 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) { switch (event.type) { case SDL_JOYAXISMOTION: { - const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - static_cast(event.jaxis.axis), - event.jaxis.value); + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { + return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + static_cast(event.jaxis.axis), + event.jaxis.value); + } + break; } case SDL_JOYBUTTONUP: { - const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); - return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - static_cast(event.jbutton.button)); + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) { + return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + static_cast(event.jbutton.button)); + } + break; } case SDL_JOYHATMOTION: { - const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); - return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), - static_cast(event.jhat.hat), - static_cast(event.jhat.value)); + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) { + return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + static_cast(event.jhat.hat), + static_cast(event.jhat.value)); + } + break; } } return {}; @@ -1062,9 +1061,8 @@ public: // 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); - auto* const controller = joystick->GetSDLGameController(); - if (controller) { + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); + auto* const controller = joystick->GetSDLGameController()) { const auto axis_left_x = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) .value.axis; @@ -1098,12 +1096,13 @@ public: } if (analog_x_axis != -1 && analog_y_axis != -1) { - const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), - analog_x_axis, analog_y_axis); - analog_x_axis = -1; - analog_y_axis = -1; - return params; + if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { + auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + analog_x_axis, analog_y_axis); + analog_x_axis = -1; + analog_y_axis = -1; + return params; + } } return {}; } -- cgit v1.2.3 From 9b501af8e3d0f6457fafb0fdfbcc11f6da4f0e8a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 10 Oct 2020 09:03:47 -0400 Subject: controllers/npad: Add heuristics to reduce rumble state changes Sending too many state changes in a short period of time can cause massive performance issues. As a result, we have to use several heuristics to reduce the number of state changes to minimize/eliminate this performance impact while maintaining the quality of these vibrations as much as possible. --- src/core/frontend/input.h | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 51 ++++++++++++++++++++++--- src/input_common/sdl/sdl_impl.cpp | 54 +++++++++++++-------------- 3 files changed, 72 insertions(+), 35 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 277b70e53..fb2ce2514 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -33,7 +33,7 @@ public: virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { return {}; } - virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { + virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const { return {}; } }; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 924f209c0..cc54b164d 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -698,16 +698,57 @@ void Controller_NPad::VibrateController(const std::vector& vibrati continue; } + // Filter out vibrations with equivalent values to reduce unnecessary state changes. + if (vibration_values[i].amp_low == + latest_vibration_values[npad_index][device_index].amp_low && + vibration_values[i].amp_high == + latest_vibration_values[npad_index][device_index].amp_high) { + continue; + } + + // Filter out non-zero vibrations that are within 0.015625 absolute amplitude of each other. + if ((vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) && + (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || + latest_vibration_values[npad_index][device_index].amp_high != 0.0f) && + (abs(vibration_values[i].amp_low - + latest_vibration_values[npad_index][device_index].amp_low) < 0.015625f && + abs(vibration_values[i].amp_high - + latest_vibration_values[npad_index][device_index].amp_high) < 0.015625f)) { + continue; + } + using namespace Settings::NativeButton; const auto& button_state = buttons[npad_index]; // TODO: Vibrate left/right vibration motors independently if possible. - button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100, + const bool success = button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( vibration_values[i].amp_low * Settings::values.vibration_strength.GetValue() / 100, - vibration_values[i].freq_high, vibration_values[i].freq_low); - - latest_vibration_values[npad_index][device_index] = vibration_values[i]; + vibration_values[i].freq_low, + vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100, + vibration_values[i].freq_high); + + if (success) { + switch (connected_controllers[npad_index].type) { + case NPadControllerType::None: + UNREACHABLE(); + break; + case NPadControllerType::ProController: + case NPadControllerType::Handheld: + case NPadControllerType::JoyDual: + // Since we can't vibrate motors independently yet, we can reduce state changes by + // assigning all 3 device indices the current vibration value. + latest_vibration_values[npad_index][0] = vibration_values[i]; + latest_vibration_values[npad_index][1] = vibration_values[i]; + latest_vibration_values[npad_index][2] = vibration_values[i]; + break; + case NPadControllerType::JoyLeft: + case NPadControllerType::JoyRight: + case NPadControllerType::Pokeball: + default: + latest_vibration_values[npad_index][device_index] = vibration_values[i]; + break; + } + } } } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 10883e2d9..18fb2ac5e 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -80,30 +80,24 @@ public: return static_cast(state.axes.at(axis)) / (32767.0f * range); } - bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) { - const u16 raw_amp_low = static_cast(amp_low * 0xFFFF); - const u16 raw_amp_high = static_cast(amp_high * 0xFFFF); - // Lower drastically the number of state changes - if (raw_amp_low >> 11 == last_state_rumble_low >> 11 && - raw_amp_high >> 11 == last_state_rumble_high >> 11) { - if (raw_amp_low + raw_amp_high != 0 || - last_state_rumble_low + last_state_rumble_high == 0) { - return false; - } - } - // Don't change state if last vibration was < 20ms - const auto now = std::chrono::system_clock::now(); - if (std::chrono::duration_cast(now - last_vibration) < - std::chrono::milliseconds(20)) { - return raw_amp_low + raw_amp_high == 0; + bool RumblePlay(u16 amp_low, u16 amp_high) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::steady_clock; + + // Prevent vibrations less than 10ms apart from each other. + if (duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { + return false; + }; + + last_vibration = steady_clock::now(); + + if (sdl_controller != nullptr) { + return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; + } else if (sdl_joystick != nullptr) { + return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; } - last_vibration = now; - last_state_rumble_low = raw_amp_low; - last_state_rumble_high = raw_amp_high; - if (sdl_joystick) { - SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time); - } return false; } @@ -172,13 +166,13 @@ private: } state; std::string guid; int port; - u16 last_state_rumble_high = 0; - u16 last_state_rumble_low = 0; - std::chrono::time_point last_vibration; std::unique_ptr sdl_joystick; std::unique_ptr sdl_controller; mutable std::mutex mutex; + // This is the timepoint of the last vibration and is used to ensure vibrations are 10ms apart. + std::chrono::steady_clock::time_point last_vibration; + // Motion is initialized without PID values as motion input is not aviable for SDL2 MotionInput motion{0.0f, 0.0f, 0.0f}; }; @@ -327,10 +321,12 @@ public: return joystick->GetButton(button); } - bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { - const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)); - const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)); - return joystick->RumblePlay(new_amp_low, new_amp_high, 250); + bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + const u16 processed_amp_low = + static_cast(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF); + const u16 processed_amp_high = + static_cast(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF); + return joystick->RumblePlay(processed_amp_low, processed_amp_high); } private: -- cgit v1.2.3 From e9e1876e821b8bd1bb5c8254ec93e2cc479e16dd Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 20 Oct 2020 13:55:25 -0400 Subject: input_common: Add VibrationDevice and VibrationDeviceFactory A vibration device is an input device that returns an unsigned byte as status. It represents whether the vibration device supports vibration or not. If the status returns 1, it supports vibration. Otherwise, it does not support vibration. --- src/core/frontend/input.h | 7 ++ src/core/hle/service/hid/controllers/npad.cpp | 48 +++++--------- src/core/hle/service/hid/controllers/npad.h | 11 +++- src/core/hle/service/hid/hid.cpp | 1 + src/input_common/gcadapter/gc_adapter.cpp | 6 +- src/input_common/gcadapter/gc_adapter.h | 4 +- src/input_common/gcadapter/gc_poller.cpp | 50 +++++++++++--- src/input_common/gcadapter/gc_poller.h | 11 ++++ src/input_common/main.cpp | 5 ++ src/input_common/sdl/sdl_impl.cpp | 74 ++++++++++++++++----- src/input_common/sdl/sdl_impl.h | 2 + src/input_common/settings.cpp | 21 ++++-- src/input_common/settings.h | 32 +++++++-- src/yuzu/applets/controller.cpp | 2 + src/yuzu/configuration/config.cpp | 61 ++++++++++------- src/yuzu/configuration/configure_input_player.cpp | 7 ++ src/yuzu/configuration/configure_vibration.cpp | 80 +++++++++++++++++++++++ src/yuzu/configuration/configure_vibration.h | 3 + src/yuzu/main.cpp | 3 + 19 files changed, 327 insertions(+), 101 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index fb2ce2514..25ac5af46 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -121,6 +121,13 @@ using ButtonDevice = InputDevice; */ using AnalogDevice = InputDevice>; +/** + * A vibration device is an input device that returns an unsigned byte as status. + * It represents whether the vibration device supports vibration or not. + * If the status returns 1, it supports vibration. Otherwise, it does not support vibration. + */ +using VibrationDevice = InputDevice; + /** * A motion status is an object that returns a tuple of accelerometer state vector, * gyroscope state vector, rotation state vector and orientation state matrix. diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index dc9954377..27099de24 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -271,6 +271,10 @@ void Controller_NPad::OnLoadInputDevices() { std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks[i].begin(), Input::CreateDevice); + std::transform(players[i].vibrations.begin() + + Settings::NativeVibration::VIBRATION_HID_BEGIN, + players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END, + vibrations[i].begin(), Input::CreateDevice); std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions[i].begin(), Input::CreateDevice); @@ -278,8 +282,10 @@ void Controller_NPad::OnLoadInputDevices() { } void Controller_NPad::OnRelease() { - for (std::size_t index = 0; index < connected_controllers.size(); ++index) { - VibrateControllerAtIndex(index, {}); + for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { + for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { + VibrateControllerAtIndex(npad_idx, device_idx); + } } } @@ -674,9 +680,9 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) } } -bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, +bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, const VibrationValue& vibration_value) { - if (!connected_controllers[npad_index].is_connected) { + if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { return false; } @@ -686,10 +692,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, return false; } - using namespace Settings::NativeButton; - const auto& button_state = buttons[npad_index]; - - return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( + return vibrations[npad_index][device_index]->SetRumblePlay( std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), vibration_value.freq_low, std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f), @@ -717,6 +720,11 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat continue; } + if (vibration_device_handles[i].device_index == DeviceIndex::None) { + UNREACHABLE_MSG("DeviceIndex should never be None!"); + continue; + } + // Some games try to send mismatched parameters in the device handle, block these. if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && (vibration_device_handles[i].npad_type == NpadType::JoyconRight || @@ -747,28 +755,8 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat continue; } - // TODO: Vibrate left/right vibration motors independently if possible. - if (VibrateControllerAtIndex(npad_index, vibration_values[i])) { - switch (connected_controllers[npad_index].type) { - case NPadControllerType::None: - UNREACHABLE(); - break; - case NPadControllerType::ProController: - case NPadControllerType::Handheld: - case NPadControllerType::JoyDual: - // Since we can't vibrate motors independently yet, we can reduce state changes by - // assigning all 3 device indices the current vibration value. - latest_vibration_values[npad_index][0] = vibration_values[i]; - latest_vibration_values[npad_index][1] = vibration_values[i]; - latest_vibration_values[npad_index][2] = vibration_values[i]; - break; - case NPadControllerType::JoyLeft: - case NPadControllerType::JoyRight: - case NPadControllerType::Pokeball: - default: - latest_vibration_values[npad_index][device_index] = vibration_values[i]; - break; - } + if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) { + latest_vibration_values[npad_index][device_index] = vibration_values[i]; } } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 576ef1558..3ae9fb8e6 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -148,7 +148,8 @@ public: void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); - bool VibrateControllerAtIndex(std::size_t npad_index, const VibrationValue& vibration_value); + bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, + const VibrationValue& vibration_value = {}); void VibrateControllers(const std::vector& vibration_device_handles, const std::vector& vibration_values); @@ -399,18 +400,22 @@ private: using StickArray = std::array< std::array, Settings::NativeAnalog::NUM_STICKS_HID>, 10>; + using VibrationArray = std::array, + Settings::NativeVibration::NUM_VIBRATIONS_HID>, + 10>; using MotionArray = std::array< - std::array, Settings::NativeMotion::NUM_MOTION_HID>, + std::array, Settings::NativeMotion::NUM_MOTIONS_HID>, 10>; ButtonArray buttons; StickArray sticks; + VibrationArray vibrations; MotionArray motions; std::vector supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array styleset_changed_events; - std::array, 10> latest_vibration_values; + std::array, 10> latest_vibration_values{}; std::array connected_controllers{}; std::array unintended_home_button_input_protection{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e88f30d6a..1d882a977 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -998,6 +998,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { break; case Controller_NPad::DeviceIndex::None: default: + UNREACHABLE_MSG("DeviceIndex should never be None!"); vibration_device_info.position = VibrationDevicePosition::None; break; } diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index b912188b6..d80195c82 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -230,10 +230,8 @@ void Adapter::SendVibrations() { vibration_changed = false; } -bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { - amplitude = std::clamp(amplitude, 0.0f, 1.0f); - const auto raw_amp = static_cast(amplitude * 0x8); - pads[port].rumble_amplitude = raw_amp; +bool Adapter::RumblePlay(std::size_t port, u8 amplitude) { + pads[port].rumble_amplitude = amplitude; return rumble_enabled; } diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index d28dcfad3..f1256c9da 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h @@ -77,8 +77,8 @@ public: Adapter(); ~Adapter(); - /// Request a vibration for a controlelr - bool RumblePlay(std::size_t port, f32 amplitude); + /// Request a vibration for a controller + bool RumblePlay(std::size_t port, u8 amplitude); /// Used for polling void BeginConfiguration(); diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 6bd6f57fc..fe57c13a5 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -15,7 +15,7 @@ namespace InputCommon { class GCButton final : public Input::ButtonDevice { public: - explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter) + explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter) : port(port_), button(button_), gcadapter(adapter) {} ~GCButton() override; @@ -27,18 +27,10 @@ public: return false; } - bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { - const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f; - const auto new_amp = - static_cast(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f))); - - return gcadapter->RumblePlay(port, new_amp); - } - private: const u32 port; const s32 button; - GCAdapter::Adapter* gcadapter; + const GCAdapter::Adapter* gcadapter; }; class GCAxisButton final : public Input::ButtonDevice { @@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { return params; } +class GCVibration final : public Input::VibrationDevice { +public: + explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter) + : port(port_), gcadapter(adapter) {} + + u8 GetStatus() const override { + return gcadapter->RumblePlay(port, 0); + } + + bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + const auto mean_amplitude = (amp_low + amp_high) * 0.5f; + const auto processed_amplitude = static_cast( + pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8); + + return gcadapter->RumblePlay(port, processed_amplitude); + } + +private: + const u32 port; + GCAdapter::Adapter* gcadapter; +}; + +/// An vibration device factory that creates vibration devices from GC Adapter +GCVibrationFactory::GCVibrationFactory(std::shared_ptr adapter_) + : adapter(std::move(adapter_)) {} + +/** + * Creates a vibration device from a joystick + * @param params contains parameters for creating the device: + * - "port": the nth gcpad on the adapter + */ +std::unique_ptr GCVibrationFactory::Create( + const Common::ParamPackage& params) { + const auto port = static_cast(params.Get("port", 0)); + + return std::make_unique(port, adapter.get()); +} + } // namespace InputCommon diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h index 0527f328f..d1271e3ea 100644 --- a/src/input_common/gcadapter/gc_poller.h +++ b/src/input_common/gcadapter/gc_poller.h @@ -64,4 +64,15 @@ private: bool polling = false; }; +/// A vibration device factory creates vibration devices from GC Adapter +class GCVibrationFactory final : public Input::Factory { +public: + explicit GCVibrationFactory(std::shared_ptr adapter_); + + std::unique_ptr Create(const Common::ParamPackage& params) override; + +private: + std::shared_ptr adapter; +}; + } // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b438482cc..e59ad4ff5 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -28,6 +28,8 @@ struct InputSubsystem::Impl { Input::RegisterFactory("gcpad", gcbuttons); gcanalog = std::make_shared(gcadapter); Input::RegisterFactory("gcpad", gcanalog); + gcvibration = std::make_shared(gcadapter); + Input::RegisterFactory("gcpad", gcvibration); keyboard = std::make_shared(); Input::RegisterFactory("keyboard", keyboard); @@ -64,9 +66,11 @@ struct InputSubsystem::Impl { #endif Input::UnregisterFactory("gcpad"); Input::UnregisterFactory("gcpad"); + Input::UnregisterFactory("gcpad"); gcbuttons.reset(); gcanalog.reset(); + gcvibration.reset(); Input::UnregisterFactory("cemuhookudp"); Input::UnregisterFactory("cemuhookudp"); @@ -142,6 +146,7 @@ struct InputSubsystem::Impl { #endif std::shared_ptr gcbuttons; std::shared_ptr gcanalog; + std::shared_ptr gcvibration; std::shared_ptr udpmotion; std::shared_ptr udptouch; std::shared_ptr udp; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 18fb2ac5e..a2a83cdc9 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -85,16 +85,17 @@ public: using std::chrono::milliseconds; using std::chrono::steady_clock; - // Prevent vibrations less than 10ms apart from each other. - if (duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { + // Block non-zero vibrations less than 10ms apart from each other. + if ((amp_low != 0 || amp_high != 0) && + duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { return false; - }; + } last_vibration = steady_clock::now(); - if (sdl_controller != nullptr) { + if (sdl_controller) { return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; - } else if (sdl_joystick != nullptr) { + } else if (sdl_joystick) { return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; } @@ -321,14 +322,6 @@ public: return joystick->GetButton(button); } - bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { - const u16 processed_amp_low = - static_cast(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF); - const u16 processed_amp_high = - static_cast(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF); - return joystick->RumblePlay(processed_amp_low, processed_amp_high); - } - private: std::shared_ptr joystick; int button; @@ -412,6 +405,32 @@ private: const float range; }; +class SDLVibration final : public Input::VibrationDevice { +public: + explicit SDLVibration(std::shared_ptr joystick_) + : joystick(std::move(joystick_)) {} + + u8 GetStatus() const override { + joystick->RumblePlay(1, 1); + return joystick->RumblePlay(0, 0); + } + + bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + const auto process_amplitude = [](f32 amplitude) { + return static_cast(std::pow(amplitude, 0.5f) * + (3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF); + }; + + const auto processed_amp_low = process_amplitude(amp_low); + const auto processed_amp_high = process_amplitude(amp_high); + + return joystick->RumblePlay(processed_amp_low, processed_amp_high); + } + +private: + std::shared_ptr joystick; +}; + class SDLDirectionMotion final : public Input::MotionDevice { public: explicit SDLDirectionMotion(std::shared_ptr joystick_, int hat_, Uint8 direction_) @@ -554,7 +573,7 @@ class SDLAnalogFactory final : public Input::Factory { public: explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} /** - * Creates analog device from joystick axes + * Creates an analog device from joystick axes * @param params contains parameters for creating the device: * - "guid": the guid of the joystick to bind * - "port": the nth joystick of the same type @@ -580,6 +599,26 @@ private: SDLState& state; }; +/// An vibration device factory that creates vibration devices from SDL joystick +class SDLVibrationFactory final : public Input::Factory { +public: + explicit SDLVibrationFactory(SDLState& state_) : state(state_) {} + /** + * Creates a vibration device from a joystick + * @param params contains parameters for creating the device: + * - "guid": the guid of the joystick to bind + * - "port": the nth joystick of the same type + */ + std::unique_ptr Create(const Common::ParamPackage& params) override { + const std::string guid = params.Get("guid", "0"); + const int port = params.Get("port", 0); + return std::make_unique(state.GetSDLJoystickByGUID(guid, port)); + } + +private: + SDLState& state; +}; + /// A motion device factory that creates motion devices from SDL joystick class SDLMotionFactory final : public Input::Factory { public: @@ -646,11 +685,13 @@ private: SDLState::SDLState() { using namespace Input; - analog_factory = std::make_shared(*this); button_factory = std::make_shared(*this); + analog_factory = std::make_shared(*this); + vibration_factory = std::make_shared(*this); motion_factory = std::make_shared(*this); - RegisterFactory("sdl", analog_factory); RegisterFactory("sdl", button_factory); + RegisterFactory("sdl", analog_factory); + RegisterFactory("sdl", vibration_factory); RegisterFactory("sdl", motion_factory); // If the frontend is going to manage the event loop, then we don't start one here @@ -687,6 +728,7 @@ SDLState::~SDLState() { using namespace Input; UnregisterFactory("sdl"); UnregisterFactory("sdl"); + UnregisterFactory("sdl"); UnregisterFactory("sdl"); CloseJoysticks(); diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index b9bb4dc56..08044b00d 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -22,6 +22,7 @@ namespace InputCommon::SDL { class SDLAnalogFactory; class SDLButtonFactory; class SDLMotionFactory; +class SDLVibrationFactory; class SDLJoystick; class SDLState : public State { @@ -72,6 +73,7 @@ private: std::shared_ptr button_factory; std::shared_ptr analog_factory; + std::shared_ptr vibration_factory; std::shared_ptr motion_factory; bool start_thread = false; diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp index b66c05856..557e7a9a0 100644 --- a/src/input_common/settings.cpp +++ b/src/input_common/settings.cpp @@ -14,13 +14,6 @@ const std::array mapping = {{ }}; } -namespace NativeMotion { -const std::array mapping = {{ - "motionleft", - "motionright", -}}; -} - namespace NativeAnalog { const std::array mapping = {{ "lstick", @@ -28,6 +21,20 @@ const std::array mapping = {{ }}; } +namespace NativeVibration { +const std::array mapping = {{ + "left_vibration_device", + "right_vibration_device", +}}; +} + +namespace NativeMotion { +const std::array mapping = {{ + "motionleft", + "motionright", +}}; +} + namespace NativeMouseButton { const std::array mapping = {{ "left", diff --git a/src/input_common/settings.h b/src/input_common/settings.h index 2763ed991..75486554b 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs; extern const std::array mapping; } // namespace NativeAnalog +namespace NativeVibration { +enum Values : int { + LeftVibrationDevice, + RightVibrationDevice, + + NumVibrations, +}; + +constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice; +constexpr int VIBRATION_HID_END = NumVibrations; +constexpr int NUM_VIBRATIONS_HID = NumVibrations; + +extern const std::array mapping; +}; // namespace NativeVibration + namespace NativeMotion { enum Values : int { - MOTIONLEFT, - MOTIONRIGHT, + MotionLeft, + MotionRight, NumMotions, }; -constexpr int MOTION_HID_BEGIN = MOTIONLEFT; +constexpr int MOTION_HID_BEGIN = MotionLeft; constexpr int MOTION_HID_END = NumMotions; -constexpr int NUM_MOTION_HID = NumMotions; +constexpr int NUM_MOTIONS_HID = NumMotions; extern const std::array mapping; } // namespace NativeMotion @@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; } // namespace NativeKeyboard -using ButtonsRaw = std::array; using AnalogsRaw = std::array; -using MotionRaw = std::array; +using ButtonsRaw = std::array; +using MotionsRaw = std::array; +using VibrationsRaw = std::array; + using MouseButtonsRaw = std::array; using KeyboardKeysRaw = std::array; using KeyboardModsRaw = std::array; @@ -330,7 +347,8 @@ struct PlayerInput { ControllerType controller_type; ButtonsRaw buttons; AnalogsRaw analogs; - MotionRaw motions; + VibrationsRaw vibrations; + MotionsRaw motions; bool vibration_enabled; int vibration_strength; diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index c5e671309..cdcd3d28d 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -478,6 +478,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) return; } + ConfigureVibration::SetVibrationDevices(player_index); + // Player 1 and Handheld auto& handheld = Settings::values.players.GetValue()[8]; // If Handheld is selected, copy all the settings from Player 1 to Handheld. diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7f66f29aa..6fa842cd5 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -344,21 +344,6 @@ void Config::ReadPlayerValue(std::size_t player_index) { } } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeMotion::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_motions.empty()) { - player_motions = default_param; - } - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], @@ -375,6 +360,33 @@ void Config::ReadPlayerValue(std::size_t player_index) { player_analogs = default_param; } } + + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + auto& player_vibrations = player.vibrations[i]; + + player_vibrations = + qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeVibration::mapping[i]), + QString{}) + .toString() + .toStdString(); + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeMotion::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_motions.empty()) { + player_motions = default_param; + } + } } void Config::ReadDebugValues() { @@ -1014,13 +1026,6 @@ void Config::SavePlayerValue(std::size_t player_index) { QString::fromStdString(player.buttons[i]), QString::fromStdString(default_param)); } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeMotion::mapping[i]), - QString::fromStdString(player.motions[i]), - QString::fromStdString(default_param)); - } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], @@ -1030,6 +1035,18 @@ void Config::SavePlayerValue(std::size_t player_index) { QString::fromStdString(player.analogs[i]), QString::fromStdString(default_param)); } + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeVibration::mapping[i]), + QString::fromStdString(player.vibrations[i]), QString{}); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeMotion::mapping[i]), + QString::fromStdString(player.motions[i]), + QString::fromStdString(default_param)); + } } void Config::SaveDebugValues() { diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 460ff08a4..3e785c224 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -22,6 +22,7 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" @@ -39,6 +40,10 @@ namespace { void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, bool connected) { + auto& player = Settings::values.players.GetValue()[npad_index]; + player.controller_type = controller_type; + player.connected = connected; + Core::System& system{Core::System::GetInstance()}; if (!system.IsPoweredOn()) { return; @@ -565,6 +570,8 @@ void ConfigureInputPlayer::ApplyConfiguration() { static_cast(ui->comboControllerType->currentIndex()); player.connected = ui->groupConnectedController->isChecked(); + ConfigureVibration::SetVibrationDevices(player_index); + // Player 2-8 if (player_index != 0) { UpdateController(player.controller_type, player_index, player.connected); diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp index 1c68f28f3..714db5b80 100644 --- a/src/yuzu/configuration/configure_vibration.cpp +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -2,6 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include + +#include + +#include "common/param_package.h" #include "core/settings.h" #include "ui_configure_vibration.h" #include "yuzu/configuration/configure_vibration.h" @@ -53,6 +59,80 @@ void ConfigureVibration::ApplyConfiguration() { ui->checkBoxAccurateVibration->isChecked()); } +void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { + using namespace Settings::NativeButton; + static constexpr std::array, 2> buttons{{ + {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons + {A, B, X, Y, R, ZR}, // Right Buttons + }}; + + auto& player = Settings::values.players.GetValue()[player_index]; + + for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) { + std::unordered_map params_count; + + for (const auto button_index : buttons[device_idx]) { + const auto& player_button = player.buttons[button_index]; + + if (params_count.find(player_button) != params_count.end()) { + ++params_count[player_button]; + continue; + } + + params_count.insert_or_assign(player_button, 1); + } + + const auto it = std::max_element( + params_count.begin(), params_count.end(), + [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }); + + auto& vibration_param_str = player.vibrations[device_idx]; + vibration_param_str.clear(); + + if (it->first.empty()) { + continue; + } + + const auto param = Common::ParamPackage(it->first); + + const auto engine = param.Get("engine", ""); + const auto guid = param.Get("guid", ""); + const auto port = param.Get("port", ""); + + if (engine.empty() || engine == "keyboard") { + continue; + } + + vibration_param_str += fmt::format("engine:{}", engine); + + if (!port.empty()) { + vibration_param_str += fmt::format(",port:{}", port); + } + if (!guid.empty()) { + vibration_param_str += fmt::format(",guid:{}", guid); + } + } + + if (player.vibrations[0] != player.vibrations[1]) { + return; + } + + if (!player.vibrations[0].empty() && + player.controller_type != Settings::ControllerType::RightJoycon) { + player.vibrations[1].clear(); + } else if (!player.vibrations[1].empty() && + player.controller_type == Settings::ControllerType::RightJoycon) { + player.vibrations[0].clear(); + } +} + +void ConfigureVibration::SetAllVibrationDevices() { + // Set vibration devices for all player indices including handheld + for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) { + SetVibrationDevices(player_idx); + } +} + void ConfigureVibration::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h index 37bbc2653..07411a86f 100644 --- a/src/yuzu/configuration/configure_vibration.h +++ b/src/yuzu/configuration/configure_vibration.h @@ -24,6 +24,9 @@ public: void ApplyConfiguration(); + static void SetVibrationDevices(std::size_t player_index); + static void SetAllVibrationDevices(); + private: void changeEvent(QEvent* event) override; void RetranslateUI(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 54a46827f..76a5c32f4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -18,6 +18,7 @@ #include "applets/web_browser.h" #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" +#include "configuration/configure_vibration.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/frontend/applets/controller.h" @@ -1096,6 +1097,8 @@ void GMainWindow::BootGame(const QString& filename) { Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); } + ConfigureVibration::SetAllVibrationDevices(); + Settings::LogSettings(); if (UISettings::values.select_user_on_boot) { -- cgit v1.2.3 From 30e0d1c973290f4813b040eecf83ff4a2c7432c3 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 25 Oct 2020 07:30:23 -0400 Subject: controllers/npad: Remove the old vibration filter Previously we used a vibration filter that filters out amplitudes close to each other. It turns out there are cases where this results into vibrations that are too inaccurate. Remove this and move the 100Hz vibration filter (Only allowing a maximum of 100 vibrations per second) from sdl_impl to npad when enable_accurate_vibrations is set to false. --- src/core/hle/service/hid/controllers/npad.cpp | 108 ++++++++++++++------------ src/core/hle/service/hid/controllers/npad.h | 4 + src/core/hle/service/hid/hid.cpp | 2 +- src/input_common/sdl/sdl_impl.cpp | 15 ---- 4 files changed, 64 insertions(+), 65 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index ecc33bc08..cfafabbd8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -703,6 +703,23 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size return false; } + if (!Settings::values.enable_accurate_vibrations.GetValue()) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::steady_clock; + + const auto now = steady_clock::now(); + + // Filter out non-zero vibrations that are within 10ms of each other. + if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && + duration_cast(now - last_vibration_timepoints[npad_index][device_index]) < + milliseconds(10)) { + return false; + } + + last_vibration_timepoints[npad_index][device_index] = now; + } + return vibrations[npad_index][device_index]->SetRumblePlay( std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), vibration_value.freq_low, @@ -710,66 +727,59 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size vibration_value.freq_high); } -void Controller_NPad::VibrateControllers(const std::vector& vibration_device_handles, - const std::vector& vibration_values) { - LOG_TRACE(Service_HID, "called"); - +void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, + const VibrationValue& vibration_value) { if (!Settings::values.vibration_enabled.GetValue()) { return; } - ASSERT_MSG(vibration_device_handles.size() == vibration_values.size(), - "The amount of device handles does not match with the amount of vibration values," - "this is undefined behavior!"); + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast(vibration_device_handle.device_index); - for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { - const auto npad_index = NPadIdToIndex(vibration_device_handles[i].npad_id); - const auto device_index = - static_cast(vibration_device_handles[i].device_index); + if (!vibration_devices_mounted[npad_index][device_index] || + !connected_controllers[npad_index].is_connected) { + return; + } - if (!vibration_devices_mounted[npad_index][device_index] || - !connected_controllers[npad_index].is_connected) { - continue; - } + if (vibration_device_handle.device_index == DeviceIndex::None) { + UNREACHABLE_MSG("DeviceIndex should never be None!"); + return; + } - if (vibration_device_handles[i].device_index == DeviceIndex::None) { - UNREACHABLE_MSG("DeviceIndex should never be None!"); - continue; - } + // Some games try to send mismatched parameters in the device handle, block these. + if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && + (vibration_device_handle.npad_type == NpadType::JoyconRight || + vibration_device_handle.device_index == DeviceIndex::Right)) || + (connected_controllers[npad_index].type == NPadControllerType::JoyRight && + (vibration_device_handle.npad_type == NpadType::JoyconLeft || + vibration_device_handle.device_index == DeviceIndex::Left))) { + return; + } - // Some games try to send mismatched parameters in the device handle, block these. - if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && - (vibration_device_handles[i].npad_type == NpadType::JoyconRight || - vibration_device_handles[i].device_index == DeviceIndex::Right)) || - (connected_controllers[npad_index].type == NPadControllerType::JoyRight && - (vibration_device_handles[i].npad_type == NpadType::JoyconLeft || - vibration_device_handles[i].device_index == DeviceIndex::Left))) { - continue; - } + // Filter out vibrations with equivalent values to reduce unnecessary state changes. + if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && + vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { + return; + } - // Filter out vibrations with equivalent values to reduce unnecessary state changes. - if (vibration_values[i].amp_low == - latest_vibration_values[npad_index][device_index].amp_low && - vibration_values[i].amp_high == - latest_vibration_values[npad_index][device_index].amp_high) { - continue; - } + if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { + latest_vibration_values[npad_index][device_index] = vibration_value; + } +} - // Filter out non-zero vibrations that are within 0.015625 absolute amplitude of each other. - if (!Settings::values.enable_accurate_vibrations.GetValue() && - (vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) && - (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || - latest_vibration_values[npad_index][device_index].amp_high != 0.0f) && - (abs(vibration_values[i].amp_low - - latest_vibration_values[npad_index][device_index].amp_low) < 0.015625f && - abs(vibration_values[i].amp_high - - latest_vibration_values[npad_index][device_index].amp_high) < 0.015625f)) { - continue; - } +void Controller_NPad::VibrateControllers(const std::vector& vibration_device_handles, + const std::vector& vibration_values) { + if (!Settings::values.vibration_enabled.GetValue()) { + return; + } - if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) { - latest_vibration_values[npad_index][device_index] = vibration_values[i]; - } + ASSERT_OR_EXECUTE_MSG( + vibration_device_handles.size() == vibration_values.size(), { return; }, + "The amount of device handles does not match with the amount of vibration values," + "this is undefined behavior!"); + + for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { + VibrateController(vibration_device_handles[i], vibration_values[i]); } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 30e3cb02f..f5122124c 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -151,6 +151,9 @@ public: bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, const VibrationValue& vibration_value = {}); + void VibrateController(const DeviceHandle& vibration_device_handle, + const VibrationValue& vibration_value); + void VibrateControllers(const std::vector& vibration_device_handles, const std::vector& vibration_values); @@ -421,6 +424,7 @@ private: NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array styleset_changed_events; + std::array, 10> last_vibration_timepoints; std::array, 10> latest_vibration_values{}; std::array, 10> vibration_devices_mounted{}; std::array connected_controllers{}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ecaa847b2..2e9682bed 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1029,7 +1029,7 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .VibrateControllers({parameters.vibration_device_handle}, {parameters.vibration_value}); + .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index a2a83cdc9..a9f7e5103 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -81,18 +81,6 @@ public: } bool RumblePlay(u16 amp_low, u16 amp_high) { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::steady_clock; - - // Block non-zero vibrations less than 10ms apart from each other. - if ((amp_low != 0 || amp_high != 0) && - duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { - return false; - } - - last_vibration = steady_clock::now(); - if (sdl_controller) { return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; } else if (sdl_joystick) { @@ -171,9 +159,6 @@ private: std::unique_ptr sdl_controller; mutable std::mutex mutex; - // This is the timepoint of the last vibration and is used to ensure vibrations are 10ms apart. - std::chrono::steady_clock::time_point last_vibration; - // Motion is initialized without PID values as motion input is not aviable for SDL2 MotionInput motion{0.0f, 0.0f, 0.0f}; }; -- cgit v1.2.3 From 117bdc71e016629b9355b33a6d64655f0245f833 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 27 Oct 2020 13:15:57 -0400 Subject: sdl_impl: Revert to the "old" method of mapping sticks Not all controllers have a SDL_GameController binding. This caused controllers not present in the SDL GameController database to have buttons mapped instead of axes. Furthermore, it was not possible to invert the axes when it could be useful such as emulating a horizontal single joycon or other potential cases. This allows us to invert the axes by reversing the order of mapping (vertical, then horizontal). --- src/input_common/sdl/sdl_impl.cpp | 45 +++++++---------------- src/yuzu/configuration/configure_input_player.cpp | 12 ++++++ src/yuzu/configuration/configure_input_player.h | 5 ++- 3 files changed, 29 insertions(+), 33 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index a9f7e5103..6024ed97a 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1068,7 +1068,6 @@ public: void Start(const std::string& device_id) override { SDLPoller::Start(device_id); - // Load the game controller // Reset stored axes analog_x_axis = -1; analog_y_axis = -1; @@ -1081,40 +1080,21 @@ public: if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { continue; } - // Simplify controller config by testing if game controller support is enabled. if (event.type == SDL_JOYAXISMOTION) { const auto axis = event.jaxis.axis; - if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - auto* const controller = joystick->GetSDLGameController()) { - 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; - } + // In order to return a complete analog param, we need inputs for both axes. + // First we take the x-axis (horizontal) input, then the y-axis (vertical) input. + if (analog_x_axis == -1) { + analog_x_axis = axis; + } else if (analog_y_axis == -1 && analog_x_axis != axis) { + analog_y_axis = axis; + } + } else { + // 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; } - } - - // 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; } } @@ -1127,6 +1107,7 @@ public: return params; } } + return {}; } diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 4ed704793..5abf9f0bf 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -370,6 +370,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } connect(analog_button, &QPushButton::clicked, [=, this] { + if (!map_analog_stick_accepted) { + map_analog_stick_accepted = + QMessageBox::information( + this, tr("Map Analog Stick"), + tr("After pressing OK, first move your joystick horizontally, and then " + "vertically.\nTo invert the axes, first move your joystick " + "vertically, and then horizontally."), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok; + if (!map_analog_stick_accepted) { + return; + } + } HandleClick( analog_map_buttons[analog_id][sub_button_id], [=, this](const Common::ParamPackage& params) { diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 05dee5af5..4895e8850 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -181,9 +181,12 @@ private: std::vector> device_pollers; + /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. + bool map_analog_stick_accepted{}; + /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, /// keyboard events are ignored. - bool want_keyboard_mouse = false; + bool want_keyboard_mouse{}; /// List of physical devices users can map with. If a SDL backed device is selected, then you /// can use this device to get a default mapping. -- cgit v1.2.3 From e7e8a87927899b69bfe9f8e38f26dac08ec37abe Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 15 Nov 2020 23:32:58 -0500 Subject: sdl_impl: Pump SDL Events at 1000 Hz --- src/input_common/sdl/sdl_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 6024ed97a..8c48bb861 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -698,7 +698,7 @@ SDLState::SDLState() { using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); - std::this_thread::sleep_for(5ms); + std::this_thread::sleep_for(1ms); } }); } -- cgit v1.2.3 From 5b6545b1410e2fa907d12f13d37ef710be654c2a Mon Sep 17 00:00:00 2001 From: german77 Date: Thu, 19 Nov 2020 11:30:52 -0600 Subject: Modify rumble amplification --- src/input_common/gcadapter/gc_poller.cpp | 4 ++-- src/input_common/sdl/sdl_impl.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 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 fe57c13a5..d95574bb5 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -302,8 +302,8 @@ public: bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { const auto mean_amplitude = (amp_low + amp_high) * 0.5f; - const auto processed_amplitude = static_cast( - pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8); + const auto processed_amplitude = + static_cast((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); return gcadapter->RumblePlay(port, processed_amplitude); } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 8c48bb861..c395d96cf 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -402,8 +402,7 @@ public: bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { const auto process_amplitude = [](f32 amplitude) { - return static_cast(std::pow(amplitude, 0.5f) * - (3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF); + return static_cast((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF); }; const auto processed_amp_low = process_amplitude(amp_low); -- cgit v1.2.3 From 5c4774e8ce1d3c5391402f4b2244cfb4dfe7d57a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sun, 15 Nov 2020 06:22:01 -0500 Subject: input_common: Treat warnings as errors Migrates over warnings as errors for input common to match how the common library treats warnings as errors. --- src/core/frontend/input.h | 2 +- src/input_common/CMakeLists.txt | 4 ++++ src/input_common/gcadapter/gc_poller.cpp | 1 - src/input_common/sdl/sdl.h | 2 +- src/input_common/sdl/sdl_impl.cpp | 4 +++- src/input_common/touch_from_button.cpp | 3 +-- src/input_common/udp/client.cpp | 6 +++--- src/input_common/udp/protocol.h | 11 ++++++++++- 8 files changed, 23 insertions(+), 10 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 25ac5af46..95c2848eb 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -30,7 +30,7 @@ public: virtual StatusType GetStatus() const { return {}; } - virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { + virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { return {}; } virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const { diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7b39a38c1..1d1b2e08a 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -31,6 +31,9 @@ add_library(input_common STATIC if (MSVC) target_compile_options(input_common PRIVATE + /W4 + /WX + # 'expression' : signed/unsigned mismatch /we4018 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) @@ -46,6 +49,7 @@ if (MSVC) ) else() target_compile_options(input_common PRIVATE + -Werror -Werror=conversion -Werror=ignored-qualifiers -Werror=implicit-fallthrough diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index d95574bb5..4e8c7e8b9 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -96,7 +96,6 @@ std::unique_ptr GCButtonFactory::Create(const Common::Param adapter.get()); } - UNREACHABLE(); return nullptr; } diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index f3554be9a..42bbf14d4 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h @@ -23,7 +23,7 @@ public: /// Unregisters SDL device factories and shut them down. virtual ~State() = default; - virtual Pollers GetPollers(Polling::DeviceType type) { + virtual Pollers GetPollers(Polling::DeviceType) { return {}; } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index c395d96cf..c16928e98 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -864,6 +864,8 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) { switch (binding.bindType) { + case SDL_CONTROLLER_BINDTYPE_NONE: + break; case SDL_CONTROLLER_BINDTYPE_AXIS: return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); case SDL_CONTROLLER_BINDTYPE_BUTTON: @@ -984,7 +986,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller { public: explicit SDLPoller(SDLState& state_) : state(state_) {} - void Start(const std::string& device_id) override { + void Start([[maybe_unused]] const std::string& device_id) override { state.event_queue.Clear(); state.polling = true; } diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index c37716aae..a07124a86 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -44,8 +44,7 @@ private: std::vector, int, int>> map; }; -std::unique_ptr TouchFromButtonFactory::Create( - const Common::ParamPackage& params) { +std::unique_ptr TouchFromButtonFactory::Create(const Common::ParamPackage&) { return std::make_unique(); } diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 3677e79ca..10b07d338 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -63,7 +63,7 @@ public: } private: - void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { + void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) { if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { switch (*type) { case Type::Version: { @@ -90,7 +90,7 @@ private: StartReceive(); } - void HandleSend(const boost::system::error_code& error) { + void HandleSend(const boost::system::error_code&) { boost::system::error_code _ignored{}; // Send a request for getting port info for the pad const Request::PortInfo port_info{1, {static_cast(pad_index), 0, 0, 0}}; @@ -369,7 +369,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( u16 max_y{}; Status current_status{Status::Initialized}; - SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, + SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) { if (current_status == Status::Initialized) { // Receiving data means the communication is ready now diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index 3ba4d1fc8..fc1aea4b9 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -7,7 +7,16 @@ #include #include #include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4701) +#endif #include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #include "common/bit_field.h" #include "common/swap.h" @@ -93,7 +102,7 @@ static_assert(std::is_trivially_copyable_v, /** * Creates a message with the proper header data that can be sent to the server. - * @param T data Request body to send + * @param data Request body to send * @param client_id ID of the udp client (usually not checked on the server) */ template -- cgit v1.2.3 From 7fb7540d69a75c70971324bc36492ee81d72de3b Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 23 Nov 2020 20:50:35 -0800 Subject: input_common: Add more missing [[maybe_unused]] from #4927. --- src/input_common/gcadapter/gc_poller.cpp | 3 ++- src/input_common/sdl/sdl_impl.cpp | 3 ++- src/input_common/udp/client.cpp | 4 ++-- 3 files changed, 6 insertions(+), 4 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 4e8c7e8b9..6d0c333ee 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -299,7 +299,8 @@ public: return gcadapter->RumblePlay(port, 0); } - bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, [[maybe_unused]] f32 amp_high, + f32 freq_high) const override { const auto mean_amplitude = (amp_low + amp_high) * 0.5f; const auto processed_amplitude = static_cast((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index c16928e98..7827e324c 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -400,7 +400,8 @@ public: return joystick->RumblePlay(0, 0); } - bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high, + [[maybe_unused]] f32 freq_high) const override { const auto process_amplitude = [](f32 amplitude) { return static_cast((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF); }; diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 10b07d338..c0bb90048 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -189,11 +189,11 @@ void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_ind StartCommunication(client, host, port, pad_index, client_id); } -void Client::OnVersion(Response::Version data) { +void Client::OnVersion([[maybe_unused]] Response::Version data) { LOG_TRACE(Input, "Version packet received: {}", data.version); } -void Client::OnPortInfo(Response::PortInfo data) { +void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) { LOG_TRACE(Input, "PortInfo packet received: {}", data.model); } -- cgit v1.2.3 From 80fece4e081323353399fe12a315e02925df778a Mon Sep 17 00:00:00 2001 From: german Date: Sat, 26 Dec 2020 12:17:22 -0600 Subject: Allow to invert analog axis with right click --- src/input_common/gcadapter/gc_poller.cpp | 28 ++++++-- src/input_common/mouse/mouse_poller.cpp | 25 +++++-- src/input_common/sdl/sdl_impl.cpp | 26 +++++-- src/yuzu/configuration/configure_input_player.cpp | 85 ++++++++++------------- 4 files changed, 99 insertions(+), 65 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 4d1052414..9670bdeb2 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -139,10 +139,10 @@ void GCButtonFactory::EndConfiguration() { class GCAnalog final : public Input::AnalogDevice { public: - explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, - const GCAdapter::Adapter* adapter, float range_) - : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), - range(range_) {} + explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_, + float deadzone_, float range_, const GCAdapter::Adapter* adapter) + : port(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_), + deadzone(deadzone_), range(range_), gcadapter(adapter) {} float GetAxis(u32 axis) const { if (gcadapter->DeviceConnected(port)) { @@ -157,7 +157,12 @@ public: std::pair GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { float x = GetAxis(analog_axis_x); float y = GetAxis(analog_axis_y); - + if (invert_x) { + x = -x; + } + if (invert_y) { + y = -y; + } // Make sure the coordinates are in the unit circle, // otherwise normalize it. float r = x * x + y * y; @@ -200,9 +205,11 @@ private: const u32 port; const u32 axis_x; const u32 axis_y; + const bool invert_x; + const bool invert_y; const float deadzone; - const GCAdapter::Adapter* gcadapter; const float range; + const GCAdapter::Adapter* gcadapter; mutable std::mutex mutex; }; @@ -223,8 +230,13 @@ std::unique_ptr GCAnalogFactory::Create(const Common::Param const auto axis_y = static_cast(params.Get("axis_y", 1)); const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); + const std::string invert_x_value = params.Get("invert_x", "+"); + const std::string invert_y_value = params.Get("invert_y", "+"); + const bool invert_x = invert_x_value == "-"; + const bool invert_y = invert_y_value == "-"; - return std::make_unique(port, axis_x, axis_y, deadzone, adapter.get(), range); + return std::make_unique(port, axis_x, axis_y, invert_x, invert_y, deadzone, range, + adapter.get()); } void GCAnalogFactory::BeginConfiguration() { @@ -282,6 +294,8 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { params.Set("port", controller_number); params.Set("axis_x", analog_x_axis); params.Set("axis_y", analog_y_axis); + params.Set("invert_x", "+"); + params.Set("invert_y", "+"); analog_x_axis = -1; analog_y_axis = -1; controller_number = -1; diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 7445ad3ad..508eb0c7d 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -62,10 +62,10 @@ void MouseButtonFactory::EndConfiguration() { class MouseAnalog final : public Input::AnalogDevice { public: - explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, float range_, - const MouseInput::Mouse* mouse_input_) - : button(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), range(range_), - mouse_input(mouse_input_) {} + explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_, + float deadzone_, float range_, const MouseInput::Mouse* mouse_input_) + : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_), + deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {} float GetAxis(u32 axis) const { std::lock_guard lock{mutex}; @@ -77,6 +77,12 @@ public: std::pair GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { float x = GetAxis(analog_axis_x); float y = GetAxis(analog_axis_y); + if (invert_x) { + x = -x; + } + if (invert_y) { + y = -y; + } // Make sure the coordinates are in the unit circle, // otherwise normalize it. @@ -104,6 +110,8 @@ private: const u32 button; const u32 axis_x; const u32 axis_y; + const bool invert_x; + const bool invert_y; const float deadzone; const float range; const MouseInput::Mouse* mouse_input; @@ -128,8 +136,13 @@ std::unique_ptr MouseAnalogFactory::Create( const auto axis_y = static_cast(params.Get("axis_y", 1)); const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); + const std::string invert_x_value = params.Get("invert_x", "+"); + const std::string invert_y_value = params.Get("invert_y", "+"); + const bool invert_x = invert_x_value == "-"; + const bool invert_y = invert_y_value == "-"; - return std::make_unique(port, axis_x, axis_y, deadzone, range, mouse_input.get()); + return std::make_unique(port, axis_x, axis_y, invert_x, invert_y, deadzone, range, + mouse_input.get()); } void MouseAnalogFactory::BeginConfiguration() { @@ -153,6 +166,8 @@ Common::ParamPackage MouseAnalogFactory::GetNextInput() const { params.Set("port", static_cast(pad.button)); params.Set("axis_x", 0); params.Set("axis_y", 1); + params.Set("invert_x", "+"); + params.Set("invert_y", "+"); return params; } } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 7827e324c..0b531f698 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -352,13 +352,20 @@ private: class SDLAnalog final : public Input::AnalogDevice { public: explicit SDLAnalog(std::shared_ptr joystick_, int axis_x_, int axis_y_, - float deadzone_, float range_) - : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), - range(range_) {} + bool invert_x_, bool invert_y_, float deadzone_, float range_) + : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), + invert_y(invert_y_), deadzone(deadzone_), range(range_) {} std::tuple GetStatus() const override { - const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); + auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); const float r = std::sqrt((x * x) + (y * y)); + if (invert_x) { + x = -x; + } + if (invert_y) { + y = -y; + } + if (r > deadzone) { return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), y / r * (r - deadzone) / (1 - deadzone)); @@ -386,6 +393,8 @@ private: std::shared_ptr joystick; const int axis_x; const int axis_y; + const bool invert_x; + const bool invert_y; const float deadzone; const float range; }; @@ -572,12 +581,17 @@ public: const int axis_y = params.Get("axis_y", 1); 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); + const std::string invert_x_value = params.Get("invert_x", "+"); + const std::string invert_y_value = params.Get("invert_y", "+"); + const bool invert_x = invert_x_value == "-"; + const bool invert_y = invert_y_value == "-"; auto joystick = state.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(joystick, axis_x, axis_y, deadzone, range); + return std::make_unique(joystick, axis_x, axis_y, invert_x, invert_y, deadzone, + range); } private: @@ -886,6 +900,8 @@ Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& gui params.Set("guid", guid); params.Set("axis_x", axis_x); params.Set("axis_y", axis_y); + params.Set("invert_x", "+"); + params.Set("invert_y", "+"); return params; } } // Anonymous namespace diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f9915fb7a..3c7500ee3 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -173,61 +173,31 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); } - if (param.Get("engine", "") == "sdl") { + const auto engine_str = param.Get("engine", ""); + const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); + const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); + const bool invert_x = param.Get("invert_x", "+") == "-"; + const bool invert_y = param.Get("invert_y", "+") == "-"; + if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse") { if (dir == "modifier") { return QObject::tr("[unused]"); } - if (dir == "left" || dir == "right") { - const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); - - return QObject::tr("Axis %1").arg(axis_x_str); - } - - if (dir == "up" || dir == "down") { - const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); - - return QObject::tr("Axis %1").arg(axis_y_str); + if (dir == "left") { + const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-"); + return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); } - - return {}; - } - - if (param.Get("engine", "") == "gcpad") { - if (dir == "modifier") { - return QObject::tr("[unused]"); + if (dir == "right") { + const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+"); + return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); } - - if (dir == "left" || dir == "right") { - const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); - - return QObject::tr("GC Axis %1").arg(axis_x_str); + if (dir == "up") { + const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+"); + return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); } - - if (dir == "up" || dir == "down") { - const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); - - return QObject::tr("GC Axis %1").arg(axis_y_str); - } - - return {}; - } - - if (param.Get("engine", "") == "mouse") { - if (dir == "modifier") { - return QObject::tr("[unused]"); - } - - if (dir == "left" || dir == "right") { - const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); - - return QObject::tr("Mouse %1").arg(axis_x_str); - } - - if (dir == "up" || dir == "down") { - const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); - - return QObject::tr("Mouse %1").arg(axis_y_str); + if (dir == "down") { + const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-"); + return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); } return {}; @@ -396,6 +366,25 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i analogs_param[analog_id].Clear(); analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); }); + context_menu.addAction(tr("Invert axis"), [&] { + if (sub_button_id == 2 || sub_button_id == 3) { + const bool invert_value = + analogs_param[analog_id].Get("invert_x", "+") == "-"; + const std::string invert_str = invert_value ? "+" : "-"; + analogs_param[analog_id].Set("invert_x", invert_str); + } + if (sub_button_id == 0 || sub_button_id == 1) { + const bool invert_value = + analogs_param[analog_id].Get("invert_y", "+") == "-"; + const std::string invert_str = invert_value ? "+" : "-"; + analogs_param[analog_id].Set("invert_y", invert_str); + } + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; + ++sub_button_id) { + analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( + analogs_param[analog_id], analog_sub_buttons[sub_button_id])); + } + }); context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( menu_location)); }); -- cgit v1.2.3 From aa4c7687eeaf79552b3563cd61172b0d6a7a99e1 Mon Sep 17 00:00:00 2001 From: german Date: Wed, 30 Dec 2020 22:29:20 -0600 Subject: Port citra-emu/citra#5509 --- src/input_common/sdl/sdl_impl.cpp | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) (limited to 'src/input_common/sdl') diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 7827e324c..d56b7587b 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1014,11 +1014,44 @@ public: } return {}; } - [[nodiscard]] std::optional FromEvent(const SDL_Event& event) const { + [[nodiscard]] std::optional FromEvent(SDL_Event& event) { switch (event.type) { case SDL_JOYAXISMOTION: - if (std::abs(event.jaxis.value / 32767.0) < 0.5) { + if (!axis_memory.count(event.jaxis.which) || + !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { + axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; + axis_event_count[event.jaxis.which][event.jaxis.axis] = 1; break; + } else { + axis_event_count[event.jaxis.which][event.jaxis.axis]++; + // The joystick and axis exist in our map if we take this branch, so no checks + // needed + if (std::abs( + (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / + 32767.0) < 0.5) { + break; + } else { + if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && + IsAxisAtPole(event.jaxis.value) && + IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { + // If we have exactly two events and both are near a pole, this is + // likely a digital input masquerading as an analog axis; Instead of + // trying to look at the direction the axis travelled, assume the first + // event was press and the second was release; This should handle most + // digital axes while deferring to the direction of travel for analog + // axes + event.jaxis.value = static_cast( + std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); + } else { + // There are more than two events, so this is likely a true analog axis, + // check the direction it travelled + event.jaxis.value = static_cast(std::copysign( + 32767, + event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); + } + axis_memory.clear(); + axis_event_count.clear(); + } } [[fallthrough]]; case SDL_JOYBUTTONUP: @@ -1027,6 +1060,16 @@ public: } return std::nullopt; } + +private: + // Determine whether an axis value is close to an extreme or center + // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per + // axis, which is why the center must be considered a pole + bool IsAxisAtPole(int16_t value) const { + return std::abs(value) >= 32767 || std::abs(value) < 327; + } + std::unordered_map> axis_memory; + std::unordered_map> axis_event_count; }; class SDLMotionPoller final : public SDLPoller { -- cgit v1.2.3