From daa37dfd9949946ce214afedd022e5eba4e145f6 Mon Sep 17 00:00:00 2001 From: David Griswold Date: Sat, 7 Feb 2026 15:50:30 +0300 Subject: [PATCH] add hat support --- .../configure_hotkeys_controller.cpp | 24 ++++++++----- .../controller_sequence_dialog.cpp | 31 ++++++++-------- src/input_common/sdl/sdl_impl.cpp | 36 ++++++++++++++----- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/citra_qt/configuration/configure_hotkeys_controller.cpp b/src/citra_qt/configuration/configure_hotkeys_controller.cpp index 8fa4f478a..6f45c24c0 100644 --- a/src/citra_qt/configuration/configure_hotkeys_controller.cpp +++ b/src/citra_qt/configuration/configure_hotkeys_controller.cpp @@ -141,17 +141,23 @@ QString ConfigureControllerHotkeys::CleanSequence(QString controller_keyseq) { if (controller_keyseq.isEmpty()) return controller_keyseq; QStringList keys = controller_keyseq.split(QStringLiteral("||")); - - QString output = QStringLiteral("Button ").append( - keys.value(0).split(QStringLiteral(",")).value(0).split(QStringLiteral(":")).value(1)); + Common::ParamPackage p1 = Common::ParamPackage(keys.value(0).toStdString()); + QString output; + if (p1.Has("hat")) { + output = QString::fromStdString("Hat " + p1.Get("hat", "") + " " + p1.Get("direction", "")); + } else if (p1.Has("button")) { + output = QString::fromStdString("Button " + p1.Get("button", "")); + } if (keys.length() > 1) { - output.append(QStringLiteral(" + Button ")) - .append(keys.value(1) - .split(QStringLiteral(",")) - .value(0) - .split(QStringLiteral(":")) - .value(1)); + output.append(QStringLiteral(" + ")); + p1 = Common::ParamPackage(keys.value(1).toStdString()); + if (p1.Has("hat")) { + output += + QString::fromStdString("Hat " + p1.Get("hat", "") + " " + p1.Get("direction", "")); + } else if (p1.Has("button")) { + output += QString::fromStdString("Button " + p1.Get("button", "")); + } } return output; } diff --git a/src/citra_qt/util/sequence_dialog/controller_sequence_dialog.cpp b/src/citra_qt/util/sequence_dialog/controller_sequence_dialog.cpp index 1baa10868..d070599d6 100644 --- a/src/citra_qt/util/sequence_dialog/controller_sequence_dialog.cpp +++ b/src/citra_qt/util/sequence_dialog/controller_sequence_dialog.cpp @@ -55,39 +55,36 @@ void ControllerSequenceDialog::LaunchPollers() { Common::ParamPackage params; for (auto& poller : device_pollers) { params = poller->GetNextInput(); - if (params.Has("engine") && params.Has("button")) { // for now, no analog inputs + if (params.Has("engine") && + (params.Has("hat") || params.Has("button"))) { // for now, no analog inputs + std::cerr << "controller hotkey event detected: " + params.Serialize() << std::endl; if (params.Has("down")) { - std::cerr << "button " + params.Get("button", "") + " down" << std::endl; downCount++; - if (downCount > 2) { - // ignore third and fourth and fifth buttons - } else if (downCount == 1) { + if (downCount == 1) { + // either the first press, or the first new press key_sequence = QStringLiteral(""); params1 = params; params2 = Common::ParamPackage(); - textBox->setText(ConfigureControllerHotkeys::CleanSequence( - QString::fromStdString(params1.Serialize())) + + key_sequence = QString::fromStdString(params1.Serialize()); + textBox->setText(ConfigureControllerHotkeys::CleanSequence(key_sequence) + QStringLiteral("...")); - } else if (downCount == 2) { - // this is a second button while the first one is held down, - // go ahead and set the key_sequence as the chord and clear params + } else if (downCount == 2 && !params2.Has("engine")) { + // this is a second button, currently only one button saved, so save it params2 = params; key_sequence = QString::fromStdString(params1.Serialize() + "||" + params2.Serialize()); textBox->setText(ConfigureControllerHotkeys::CleanSequence(key_sequence)); } + // if downCount == 3 or more, just ignore them - we have saved the first two + // presses } else { // button release downCount--; - std::cerr << "button " + params.Get("button", "") + " up" << std::endl; - if (downCount == 0) { - // all released, clear all saved params + if (downCount <= 0) { + // once all buttons are released, clear the params so the user can try again + // if need be params1 = Common::ParamPackage(); params2 = Common::ParamPackage(); } - if (key_sequence.isEmpty()) { - key_sequence = QString::fromStdString(params.Serialize()); - textBox->setText(ConfigureControllerHotkeys::CleanSequence(key_sequence)); - } } } } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 9e6e330fc..729beef23 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -187,7 +187,8 @@ public: state.buttons[button] = value; } - // no longer used, at least as a test + // no longer used - creating state breaks hotkey detection, so + // poll sdl directly below bool GetButton(int button) const { std::lock_guard lock{mutex}; return state.buttons.at(button); @@ -233,11 +234,18 @@ public: state.hats[hat] = direction; } + // no longer used, poll directly instead bool GetHatDirection(int hat, Uint8 direction) const { std::lock_guard lock{mutex}; return (state.hats.at(hat) & direction) != 0; } + bool GetHatDirectionDirect(int hat, Uint8 direction) const { + if (!sdl_joystick) + return false; + return SDL_JoystickGetHat(sdl_joystick.get(), hat) == direction; + } + void SetAccel(const float x, const float y, const float z) { std::lock_guard lock{mutex}; state.accel.x = x; @@ -636,7 +644,7 @@ public: : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {} bool GetStatus() const override { - return joystick->GetHatDirection(hat, direction); + return joystick->GetHatDirectionDirect(hat, direction); } private: @@ -897,6 +905,9 @@ SDLState::~SDLState() { Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event, const bool down = false) { Common::ParamPackage params({{"engine", "sdl"}}); + if (down) { + params.Set("down", "1"); + } auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); switch (event.type) { case SDL_JOYAXISMOTION: { @@ -936,14 +947,16 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve case SDL_HAT_RIGHT: params.Set("direction", "right"); break; + case SDL_HAT_CENTERED: + params.Set("direction", "centered"); + break; default: return {}; } break; } } - if (down) - params.Set("down", 1); + return params; } @@ -1016,11 +1029,18 @@ public: axis_event_count.clear(); } } - case SDL_JOYBUTTONDOWN: - down = true; - case SDL_JOYBUTTONUP: - case SDL_JOYHATMOTION: return SDLEventToButtonParamPackage(state, event, down); + break; + case SDL_JOYBUTTONDOWN: + return SDLEventToButtonParamPackage(state, event, true); + break; + case SDL_JOYBUTTONUP: + return SDLEventToButtonParamPackage(state, event, false); + break; + case SDL_JOYHATMOTION: + return SDLEventToButtonParamPackage(state, event, + event.jhat.value == SDL_HAT_CENTERED); + break; } } return {};