From 75e81885b0bd38ece84a94b2d323b05d788f8741 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 28 Jan 2023 18:19:15 -0600 Subject: input_common: Implement turbo buttons --- src/common/input.h | 2 + src/core/hid/emulated_controller.cpp | 75 ++++++++++++++++++++++- src/core/hid/emulated_controller.h | 6 ++ src/core/hle/service/hid/controllers/npad.cpp | 3 + src/input_common/input_poller.cpp | 30 +++++---- src/yuzu/configuration/configure_input_player.cpp | 17 +++-- 6 files changed, 115 insertions(+), 18 deletions(-) diff --git a/src/common/input.h b/src/common/input.h index d61cd7ca8..b5748a6c8 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -130,6 +130,8 @@ struct ButtonStatus { bool inverted{}; // Press once to activate, press again to release bool toggle{}; + // Spams the button when active + bool turbo{}; // Internal lock for the toggle status bool locked{}; }; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 0e06468da..631aa6ad2 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -12,6 +12,7 @@ namespace Core::HID { constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_TRIGGER_MAX = 0x7fff; +constexpr u32 TURBO_BUTTON_DELAY = 4; // Use a common UUID for TAS and Virtual Gamepad constexpr Common::UUID TAS_UUID = Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; @@ -447,6 +448,7 @@ void EmulatedController::ReloadInput() { }, }); } + turbo_button_state = 0; } void EmulatedController::UnloadInput() { @@ -687,6 +689,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback } current_status.toggle = new_status.toggle; + current_status.turbo = new_status.turbo; current_status.uuid = uuid; // Update button status with current @@ -1548,7 +1551,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const { if (is_configuring) { return {}; } - return controller.npad_button_state; + return {controller.npad_button_state.raw & GetTurboButtonMask()}; } DebugPadButton EmulatedController::GetDebugPadButtons() const { @@ -1656,4 +1659,74 @@ void EmulatedController::DeleteCallback(int key) { } callback_list.erase(iterator); } + +void EmulatedController::TurboButtonUpdate() { + turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2); +} + +NpadButton EmulatedController::GetTurboButtonMask() const { + // Apply no mask when disabled + if (turbo_button_state < TURBO_BUTTON_DELAY) { + return {NpadButton::All}; + } + + NpadButtonState button_mask{}; + for (std::size_t index = 0; index < controller.button_values.size(); ++index) { + if (!controller.button_values[index].turbo) { + continue; + } + + switch (index) { + case Settings::NativeButton::A: + button_mask.a.Assign(1); + break; + case Settings::NativeButton::B: + button_mask.b.Assign(1); + break; + case Settings::NativeButton::X: + button_mask.x.Assign(1); + break; + case Settings::NativeButton::Y: + button_mask.y.Assign(1); + break; + case Settings::NativeButton::L: + button_mask.l.Assign(1); + break; + case Settings::NativeButton::R: + button_mask.r.Assign(1); + break; + case Settings::NativeButton::ZL: + button_mask.zl.Assign(1); + break; + case Settings::NativeButton::ZR: + button_mask.zr.Assign(1); + break; + case Settings::NativeButton::DLeft: + button_mask.left.Assign(1); + break; + case Settings::NativeButton::DUp: + button_mask.up.Assign(1); + break; + case Settings::NativeButton::DRight: + button_mask.right.Assign(1); + break; + case Settings::NativeButton::DDown: + button_mask.down.Assign(1); + break; + case Settings::NativeButton::SL: + button_mask.left_sl.Assign(1); + button_mask.right_sl.Assign(1); + break; + case Settings::NativeButton::SR: + button_mask.left_sr.Assign(1); + button_mask.right_sr.Assign(1); + break; + default: + break; + } + } + + return static_cast(~button_mask.raw); +} + } // namespace Core::HID diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 3ac77b2b5..b02bf35c4 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -411,6 +411,9 @@ public: */ void DeleteCallback(int key); + /// Swaps the state of the turbo buttons + void TurboButtonUpdate(); + private: /// creates input devices from params void LoadDevices(); @@ -511,6 +514,8 @@ private: */ void TriggerOnChange(ControllerTriggerType type, bool is_service_update); + NpadButton GetTurboButtonMask() const; + const NpadIdType npad_id_type; NpadStyleIndex npad_type{NpadStyleIndex::None}; NpadStyleIndex original_npad_type{NpadStyleIndex::None}; @@ -520,6 +525,7 @@ private: bool system_buttons_enabled{true}; f32 motion_sensitivity{0.01f}; bool force_update_motion{false}; + u32 turbo_button_state{0}; // Temporary values to avoid doing changes while the controller is in configuring mode NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 5713f1288..3afda9e3f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -428,6 +428,9 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { return; } + // This function is unique to yuzu for the turbo buttons to work properly + controller.device->TurboButtonUpdate(); + auto& pad_entry = controller.npad_pad_state; auto& trigger_entry = controller.npad_trigger_state; const auto button_state = controller.device->GetNpadButtons(); diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 15cbf7e5f..8c6a6521a 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -16,10 +16,10 @@ public: class InputFromButton final : public Common::Input::InputDevice { public: - explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, - InputEngine* input_engine_) - : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), - input_engine(input_engine_) { + explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_, + bool inverted_, InputEngine* input_engine_) + : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_), + inverted(inverted_), input_engine(input_engine_) { UpdateCallback engine_callback{[this]() { OnChange(); }}; const InputIdentifier input_identifier{ .identifier = identifier, @@ -40,6 +40,7 @@ public: .value = input_engine->GetButton(identifier, button), .inverted = inverted, .toggle = toggle, + .turbo = turbo, }; } @@ -68,6 +69,7 @@ public: private: const PadIdentifier identifier; const int button; + const bool turbo; const bool toggle; const bool inverted; int callback_key; @@ -77,10 +79,10 @@ private: class InputFromHatButton final : public Common::Input::InputDevice { public: - explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, - bool inverted_, InputEngine* input_engine_) - : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), - inverted(inverted_), input_engine(input_engine_) { + explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_, + bool toggle_, bool inverted_, InputEngine* input_engine_) + : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_), + toggle(toggle_), inverted(inverted_), input_engine(input_engine_) { UpdateCallback engine_callback{[this]() { OnChange(); }}; const InputIdentifier input_identifier{ .identifier = identifier, @@ -101,6 +103,7 @@ public: .value = input_engine->GetHatButton(identifier, button, direction), .inverted = inverted, .toggle = toggle, + .turbo = turbo, }; } @@ -130,6 +133,7 @@ private: const PadIdentifier identifier; const int button; const u8 direction; + const bool turbo; const bool toggle; const bool inverted; int callback_key; @@ -853,14 +857,15 @@ std::unique_ptr InputFactory::CreateButtonDevice( const auto keyboard_key = params.Get("code", 0); const auto toggle = params.Get("toggle", false) != 0; const auto inverted = params.Get("inverted", false) != 0; + const auto turbo = params.Get("turbo", false) != 0; input_engine->PreSetController(identifier); input_engine->PreSetButton(identifier, button_id); input_engine->PreSetButton(identifier, keyboard_key); if (keyboard_key != 0) { - return std::make_unique(identifier, keyboard_key, toggle, inverted, + return std::make_unique(identifier, keyboard_key, turbo, toggle, inverted, input_engine.get()); } - return std::make_unique(identifier, button_id, toggle, inverted, + return std::make_unique(identifier, button_id, turbo, toggle, inverted, input_engine.get()); } @@ -876,11 +881,12 @@ std::unique_ptr InputFactory::CreateHatButtonDevice( const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); const auto toggle = params.Get("toggle", false) != 0; const auto inverted = params.Get("inverted", false) != 0; + const auto turbo = params.Get("turbo", false) != 0; input_engine->PreSetController(identifier); input_engine->PreSetHatButton(identifier, button_id); - return std::make_unique(identifier, button_id, direction, toggle, inverted, - input_engine.get()); + return std::make_unique(identifier, button_id, direction, turbo, toggle, + inverted, input_engine.get()); } std::unique_ptr InputFactory::CreateStickDevice( diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 4b7e3b01b..723690e71 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -182,12 +182,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : ""); + const QString turbo = QString::fromStdString(param.Get("turbo", false) ? "$" : ""); const auto common_button_name = input_subsystem->GetButtonName(param); // Retrieve the names from Qt if (param.Get("engine", "") == "keyboard") { const QString button_str = GetKeyName(param.Get("code", 0)); - return QObject::tr("%1%2%3").arg(toggle, inverted, button_str); + return QObject::tr("%1%2%3%4").arg(turbo, toggle, inverted, button_str); } if (common_button_name == Common::Input::ButtonNames::Invalid) { @@ -201,7 +202,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { if (common_button_name == Common::Input::ButtonNames::Value) { if (param.Has("hat")) { const QString hat = GetDirectionName(param.Get("direction", "")); - return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); + return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, hat); } if (param.Has("axis")) { const QString axis = QString::fromStdString(param.Get("axis", "")); @@ -219,13 +220,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { } if (param.Has("button")) { const QString button = QString::fromStdString(param.Get("button", "")); - return QObject::tr("%1%2Button %3").arg(toggle, inverted, button); + return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button); } } QString button_name = GetButtonName(common_button_name); if (param.Has("hat")) { - return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name); + return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name); } if (param.Has("axis")) { return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); @@ -234,7 +235,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); } if (param.Has("button")) { - return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name); + return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button_name); } return QObject::tr("[unknown]"); @@ -395,6 +396,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i button_map[button_id]->setText(ButtonToText(param)); emulated_controller->SetButtonParam(button_id, param); }); + context_menu.addAction(tr("Turbo button"), [&] { + const bool turbo_value = !param.Get("turbo", false); + param.Set("turbo", turbo_value); + button_map[button_id]->setText(ButtonToText(param)); + emulated_controller->SetButtonParam(button_id, param); + }); } if (param.Has("axis")) { context_menu.addAction(tr("Invert axis"), [&] { -- cgit v1.2.3 From ce1895497dd6a1ab87a6ab56d344b6f5e1e36ee7 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 29 Jan 2023 12:23:30 -0600 Subject: yuzu: config: Draw turbo buttons with a different color --- .../configure_input_player_widget.cpp | 35 +++++++++++++--------- .../configuration/configure_input_player_widget.h | 2 ++ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 68af6c20c..c287220fc 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -81,7 +81,6 @@ void PlayerControlPreview::UpdateColors() { colors.outline = QColor(0, 0, 0); colors.primary = QColor(225, 225, 225); colors.button = QColor(109, 111, 114); - colors.button2 = QColor(109, 111, 114); colors.button2 = QColor(77, 80, 84); colors.slider_arrow = QColor(65, 68, 73); colors.font2 = QColor(0, 0, 0); @@ -100,6 +99,7 @@ void PlayerControlPreview::UpdateColors() { colors.led_off = QColor(170, 238, 255); colors.indicator2 = QColor(59, 165, 93); colors.charging = QColor(250, 168, 26); + colors.button_turbo = QColor(217, 158, 4); colors.left = colors.primary; colors.right = colors.primary; @@ -2469,7 +2469,6 @@ void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed, float width, float height, Direction direction, float radius) { - p.setBrush(button_color); if (pressed.value) { switch (direction) { case Direction::Left: @@ -2487,16 +2486,16 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, case Direction::None: break; } - p.setBrush(colors.highlight); } QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; + p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo)); p.drawRoundedRect(rect, radius, radius); } void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, const Common::Input::ButtonStatus& pressed, int button_size) { p.setPen(colors.outline); - p.setBrush(pressed.value ? colors.highlight : colors.button); + p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo)); DrawRectangle(p, center, button_size, button_size / 3.0f); } void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, @@ -2504,7 +2503,7 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, int button_size) { // Draw outer line p.setPen(colors.outline); - p.setBrush(pressed.value ? colors.highlight : colors.button); + p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo)); DrawRectangle(p, center, button_size, button_size / 3.0f); DrawRectangle(p, center, button_size / 3.0f, button_size); @@ -2526,7 +2525,7 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, } p.setPen(colors.outline); - p.setBrush(pressed.value ? colors.highlight : colors.button); + p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo)); DrawPolygon(p, button_x); } @@ -2539,7 +2538,7 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, } p.setPen(colors.outline); - p.setBrush(pressed.value ? colors.highlight : colors.button); + p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo)); DrawPolygon(p, button_x); } @@ -2553,17 +2552,15 @@ void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, } p.setPen(colors.outline); - p.setBrush(pressed.value ? colors.highlight : colors.button2); + p.setBrush(GetButtonColor(colors.button2, pressed.value, pressed.turbo)); DrawPolygon(p, button_x); } void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, const Common::Input::ButtonStatus& pressed, float button_size) { - p.setBrush(button_color); - if (pressed.value) { - p.setBrush(colors.highlight); - } + + p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo)); p.drawEllipse(center, button_size, button_size); } @@ -2620,7 +2617,7 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, // Draw arrow button p.setPen(pressed.value ? colors.highlight : colors.button); - p.setBrush(pressed.value ? colors.highlight : colors.button); + p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo)); DrawPolygon(p, arrow_button); switch (direction) { @@ -2672,10 +2669,20 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, // Draw arrow button p.setPen(colors.outline); - p.setBrush(pressed.value ? colors.highlight : colors.button); + p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo)); DrawPolygon(p, qtrigger_button); } +QColor PlayerControlPreview::GetButtonColor(QColor default_color, bool is_pressed, bool turbo) { + if (is_pressed && turbo) { + return colors.button_turbo; + } + if (is_pressed) { + return colors.highlight; + } + return default_color; +} + void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery) { if (battery == Common::Input::BatteryLevel::None) { diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h index b258c6d77..0e9e95e85 100644 --- a/src/yuzu/configuration/configure_input_player_widget.h +++ b/src/yuzu/configuration/configure_input_player_widget.h @@ -81,6 +81,7 @@ private: QColor right{}; QColor button{}; QColor button2{}; + QColor button_turbo{}; QColor font{}; QColor font2{}; QColor highlight{}; @@ -183,6 +184,7 @@ private: const Common::Input::ButtonStatus& pressed, float size = 1.0f); void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, const Common::Input::ButtonStatus& pressed); + QColor GetButtonColor(QColor default_color, bool is_pressed, bool turbo); // Draw battery functions void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery); -- cgit v1.2.3