input_common: Implement turbo buttons
This commit is contained in:
		| @@ -130,6 +130,8 @@ struct ButtonStatus { | |||||||
|     bool inverted{}; |     bool inverted{}; | ||||||
|     // Press once to activate, press again to release |     // Press once to activate, press again to release | ||||||
|     bool toggle{}; |     bool toggle{}; | ||||||
|  |     // Spams the button when active | ||||||
|  |     bool turbo{}; | ||||||
|     // Internal lock for the toggle status |     // Internal lock for the toggle status | ||||||
|     bool locked{}; |     bool locked{}; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
| namespace Core::HID { | namespace Core::HID { | ||||||
| constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | ||||||
| constexpr s32 HID_TRIGGER_MAX = 0x7fff; | constexpr s32 HID_TRIGGER_MAX = 0x7fff; | ||||||
|  | constexpr u32 TURBO_BUTTON_DELAY = 4; | ||||||
| // Use a common UUID for TAS and Virtual Gamepad | // Use a common UUID for TAS and Virtual Gamepad | ||||||
| constexpr Common::UUID TAS_UUID = | constexpr Common::UUID TAS_UUID = | ||||||
|     Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; |     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() { | void EmulatedController::UnloadInput() { | ||||||
| @@ -687,6 +689,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     current_status.toggle = new_status.toggle; |     current_status.toggle = new_status.toggle; | ||||||
|  |     current_status.turbo = new_status.turbo; | ||||||
|     current_status.uuid = uuid; |     current_status.uuid = uuid; | ||||||
|  |  | ||||||
|     // Update button status with current |     // Update button status with current | ||||||
| @@ -1548,7 +1551,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const { | |||||||
|     if (is_configuring) { |     if (is_configuring) { | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
|     return controller.npad_button_state; |     return {controller.npad_button_state.raw & GetTurboButtonMask()}; | ||||||
| } | } | ||||||
|  |  | ||||||
| DebugPadButton EmulatedController::GetDebugPadButtons() const { | DebugPadButton EmulatedController::GetDebugPadButtons() const { | ||||||
| @@ -1656,4 +1659,74 @@ void EmulatedController::DeleteCallback(int key) { | |||||||
|     } |     } | ||||||
|     callback_list.erase(iterator); |     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 | } // namespace Core::HID | ||||||
|   | |||||||
| @@ -411,6 +411,9 @@ public: | |||||||
|      */ |      */ | ||||||
|     void DeleteCallback(int key); |     void DeleteCallback(int key); | ||||||
|  |  | ||||||
|  |     /// Swaps the state of the turbo buttons | ||||||
|  |     void TurboButtonUpdate(); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     /// creates input devices from params |     /// creates input devices from params | ||||||
|     void LoadDevices(); |     void LoadDevices(); | ||||||
| @@ -511,6 +514,8 @@ private: | |||||||
|      */ |      */ | ||||||
|     void TriggerOnChange(ControllerTriggerType type, bool is_service_update); |     void TriggerOnChange(ControllerTriggerType type, bool is_service_update); | ||||||
|  |  | ||||||
|  |     NpadButton GetTurboButtonMask() const; | ||||||
|  |  | ||||||
|     const NpadIdType npad_id_type; |     const NpadIdType npad_id_type; | ||||||
|     NpadStyleIndex npad_type{NpadStyleIndex::None}; |     NpadStyleIndex npad_type{NpadStyleIndex::None}; | ||||||
|     NpadStyleIndex original_npad_type{NpadStyleIndex::None}; |     NpadStyleIndex original_npad_type{NpadStyleIndex::None}; | ||||||
| @@ -520,6 +525,7 @@ private: | |||||||
|     bool system_buttons_enabled{true}; |     bool system_buttons_enabled{true}; | ||||||
|     f32 motion_sensitivity{0.01f}; |     f32 motion_sensitivity{0.01f}; | ||||||
|     bool force_update_motion{false}; |     bool force_update_motion{false}; | ||||||
|  |     u32 turbo_button_state{0}; | ||||||
|  |  | ||||||
|     // Temporary values to avoid doing changes while the controller is in configuring mode |     // Temporary values to avoid doing changes while the controller is in configuring mode | ||||||
|     NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; |     NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; | ||||||
|   | |||||||
| @@ -428,6 +428,9 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { | |||||||
|         return; |         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& pad_entry = controller.npad_pad_state; | ||||||
|     auto& trigger_entry = controller.npad_trigger_state; |     auto& trigger_entry = controller.npad_trigger_state; | ||||||
|     const auto button_state = controller.device->GetNpadButtons(); |     const auto button_state = controller.device->GetNpadButtons(); | ||||||
|   | |||||||
| @@ -16,10 +16,10 @@ public: | |||||||
|  |  | ||||||
| class InputFromButton final : public Common::Input::InputDevice { | class InputFromButton final : public Common::Input::InputDevice { | ||||||
| public: | public: | ||||||
|     explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, |     explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_, | ||||||
|                              InputEngine* input_engine_) |                              bool inverted_, InputEngine* input_engine_) | ||||||
|         : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), |         : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_), | ||||||
|           input_engine(input_engine_) { |           inverted(inverted_), input_engine(input_engine_) { | ||||||
|         UpdateCallback engine_callback{[this]() { OnChange(); }}; |         UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||||||
|         const InputIdentifier input_identifier{ |         const InputIdentifier input_identifier{ | ||||||
|             .identifier = identifier, |             .identifier = identifier, | ||||||
| @@ -40,6 +40,7 @@ public: | |||||||
|             .value = input_engine->GetButton(identifier, button), |             .value = input_engine->GetButton(identifier, button), | ||||||
|             .inverted = inverted, |             .inverted = inverted, | ||||||
|             .toggle = toggle, |             .toggle = toggle, | ||||||
|  |             .turbo = turbo, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -68,6 +69,7 @@ public: | |||||||
| private: | private: | ||||||
|     const PadIdentifier identifier; |     const PadIdentifier identifier; | ||||||
|     const int button; |     const int button; | ||||||
|  |     const bool turbo; | ||||||
|     const bool toggle; |     const bool toggle; | ||||||
|     const bool inverted; |     const bool inverted; | ||||||
|     int callback_key; |     int callback_key; | ||||||
| @@ -77,10 +79,10 @@ private: | |||||||
|  |  | ||||||
| class InputFromHatButton final : public Common::Input::InputDevice { | class InputFromHatButton final : public Common::Input::InputDevice { | ||||||
| public: | public: | ||||||
|     explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, |     explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_, | ||||||
|                                 bool inverted_, InputEngine* input_engine_) |                                 bool toggle_, bool inverted_, InputEngine* input_engine_) | ||||||
|         : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), |         : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_), | ||||||
|           inverted(inverted_), input_engine(input_engine_) { |           toggle(toggle_), inverted(inverted_), input_engine(input_engine_) { | ||||||
|         UpdateCallback engine_callback{[this]() { OnChange(); }}; |         UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||||||
|         const InputIdentifier input_identifier{ |         const InputIdentifier input_identifier{ | ||||||
|             .identifier = identifier, |             .identifier = identifier, | ||||||
| @@ -101,6 +103,7 @@ public: | |||||||
|             .value = input_engine->GetHatButton(identifier, button, direction), |             .value = input_engine->GetHatButton(identifier, button, direction), | ||||||
|             .inverted = inverted, |             .inverted = inverted, | ||||||
|             .toggle = toggle, |             .toggle = toggle, | ||||||
|  |             .turbo = turbo, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -130,6 +133,7 @@ private: | |||||||
|     const PadIdentifier identifier; |     const PadIdentifier identifier; | ||||||
|     const int button; |     const int button; | ||||||
|     const u8 direction; |     const u8 direction; | ||||||
|  |     const bool turbo; | ||||||
|     const bool toggle; |     const bool toggle; | ||||||
|     const bool inverted; |     const bool inverted; | ||||||
|     int callback_key; |     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 keyboard_key = params.Get("code", 0); | ||||||
|     const auto toggle = params.Get("toggle", false) != 0; |     const auto toggle = params.Get("toggle", false) != 0; | ||||||
|     const auto inverted = params.Get("inverted", false) != 0; |     const auto inverted = params.Get("inverted", false) != 0; | ||||||
|  |     const auto turbo = params.Get("turbo", false) != 0; | ||||||
|     input_engine->PreSetController(identifier); |     input_engine->PreSetController(identifier); | ||||||
|     input_engine->PreSetButton(identifier, button_id); |     input_engine->PreSetButton(identifier, button_id); | ||||||
|     input_engine->PreSetButton(identifier, keyboard_key); |     input_engine->PreSetButton(identifier, keyboard_key); | ||||||
|     if (keyboard_key != 0) { |     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()); |                                                  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()); |                                              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 direction = input_engine->GetHatButtonId(params.Get("direction", "")); | ||||||
|     const auto toggle = params.Get("toggle", false) != 0; |     const auto toggle = params.Get("toggle", false) != 0; | ||||||
|     const auto inverted = params.Get("inverted", false) != 0; |     const auto inverted = params.Get("inverted", false) != 0; | ||||||
|  |     const auto turbo = params.Get("turbo", false) != 0; | ||||||
|  |  | ||||||
|     input_engine->PreSetController(identifier); |     input_engine->PreSetController(identifier); | ||||||
|     input_engine->PreSetHatButton(identifier, button_id); |     input_engine->PreSetHatButton(identifier, button_id); | ||||||
|     return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, |     return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle, | ||||||
|                                                 input_engine.get()); |                                                 inverted, input_engine.get()); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( | 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 toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); | ||||||
|     const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); |     const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); | ||||||
|     const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : ""); |     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); |     const auto common_button_name = input_subsystem->GetButtonName(param); | ||||||
|  |  | ||||||
|     // Retrieve the names from Qt |     // Retrieve the names from Qt | ||||||
|     if (param.Get("engine", "") == "keyboard") { |     if (param.Get("engine", "") == "keyboard") { | ||||||
|         const QString button_str = GetKeyName(param.Get("code", 0)); |         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) { |     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 (common_button_name == Common::Input::ButtonNames::Value) { | ||||||
|         if (param.Has("hat")) { |         if (param.Has("hat")) { | ||||||
|             const QString hat = GetDirectionName(param.Get("direction", "")); |             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")) { |         if (param.Has("axis")) { | ||||||
|             const QString axis = QString::fromStdString(param.Get("axis", "")); |             const QString axis = QString::fromStdString(param.Get("axis", "")); | ||||||
| @@ -219,13 +220,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { | |||||||
|         } |         } | ||||||
|         if (param.Has("button")) { |         if (param.Has("button")) { | ||||||
|             const QString button = QString::fromStdString(param.Get("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); |     QString button_name = GetButtonName(common_button_name); | ||||||
|     if (param.Has("hat")) { |     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")) { |     if (param.Has("axis")) { | ||||||
|         return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); |         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); |         return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); | ||||||
|     } |     } | ||||||
|     if (param.Has("button")) { |     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]"); |     return QObject::tr("[unknown]"); | ||||||
| @@ -395,6 +396,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||||||
|                             button_map[button_id]->setText(ButtonToText(param)); |                             button_map[button_id]->setText(ButtonToText(param)); | ||||||
|                             emulated_controller->SetButtonParam(button_id, 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")) { |                     if (param.Has("axis")) { | ||||||
|                         context_menu.addAction(tr("Invert axis"), [&] { |                         context_menu.addAction(tr("Invert axis"), [&] { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user