input_common: Implement turbo buttons
This commit is contained in:
		| @@ -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{}; | ||||
| }; | ||||
|   | ||||
| @@ -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<NpadButton>(~button_mask.raw); | ||||
| } | ||||
|  | ||||
| } // namespace Core::HID | ||||
|   | ||||
| @@ -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}; | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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<Common::Input::InputDevice> 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<InputFromButton>(identifier, keyboard_key, toggle, inverted, | ||||
|         return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted, | ||||
|                                                  input_engine.get()); | ||||
|     } | ||||
|     return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted, | ||||
|     return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted, | ||||
|                                              input_engine.get()); | ||||
| } | ||||
|  | ||||
| @@ -876,11 +881,12 @@ std::unique_ptr<Common::Input::InputDevice> 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<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, | ||||
|                                                 input_engine.get()); | ||||
|     return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle, | ||||
|                                                 inverted, input_engine.get()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( | ||||
|   | ||||
| @@ -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"), [&] { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user