diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/Hotkey.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/Hotkey.kt index e2319a7e4..9950ebc8d 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/Hotkey.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/Hotkey.kt @@ -11,6 +11,7 @@ enum class Hotkey(val button: Int) { PAUSE_OR_RESUME(10004), QUICKSAVE(10005), QUICKLOAD(10006), - TURBO_LIMIT(10007), - ENABLE(10008); + TURBO_TOGGLE(10007), + ENABLE(10008), + TURBO_HOLD(10009); } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt index d01d5f769..6db6f610a 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt @@ -16,6 +16,7 @@ import org.citra.citra_emu.utils.TurboHelper import org.citra.citra_emu.display.ScreenAdjustmentUtil import org.citra.citra_emu.features.settings.model.view.InputBindingSetting import org.citra.citra_emu.features.settings.model.Settings +import org.citra.citra_emu.utils.Log class HotkeyUtility( private val screenAdjustmentUtil: ScreenAdjustmentUtil, @@ -40,6 +41,7 @@ class HotkeyUtility( // Now process all internal buttons associated with this keypress for (button in buttonSet) { + Log.debug("Button pressed: " + button) currentlyPressedButtons.add(button) //option 1 - this is the enable command, which was already handled if (button == Hotkey.ENABLE.button) { @@ -78,9 +80,11 @@ class HotkeyUtility( } for (button in buttonSet) { + Log.debug("Button released: " + button) // this is a hotkey button if (hotkeyButtons.contains(button)) { currentlyPressedButtons.remove(button) + handleHotkeyRelease(button) if (!currentlyPressedButtons.any { hotkeyButtons.contains(it) }) { // all hotkeys are no longer pressed hotkeyIsPressed = false @@ -106,13 +110,23 @@ class HotkeyUtility( return handled } + fun handleHotkeyRelease(bindedButton: Int): Boolean { + Log.debug("Handling hotkey button release: " + bindedButton) + if (bindedButton == Hotkey.TURBO_HOLD.button) { + TurboHelper.setTurboEnabled(false, false) + } + return true + } + fun handleHotkey(bindedButton: Int): Boolean { + Log.debug("Handling hotkey button press: " + bindedButton) when (bindedButton) { Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen() Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts() Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame() Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume() - Hotkey.TURBO_LIMIT.button -> TurboHelper.toggleTurbo(true) + Hotkey.TURBO_TOGGLE.button -> TurboHelper.toggleTurbo(true) + Hotkey.TURBO_HOLD.button -> TurboHelper.setTurboEnabled(true, false) Hotkey.QUICKSAVE.button -> { NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT) Toast.makeText( diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt index 547a53594..6a46e6e54 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt @@ -143,7 +143,8 @@ class Settings { const val HOTKEY_PAUSE_OR_RESUME = "hotkey_pause_or_resume_game" const val HOTKEY_QUICKSAVE = "hotkey_quickload" const val HOTKEY_QUICKlOAD = "hotkey_quickpause" - const val HOTKEY_TURBO_LIMIT = "hotkey_turbo_limit" + const val HOTKEY_TURBO_TOGGLE = "hotkey_turbo_limit" + const val HOTKEY_TURBO_HOLD = "hotkey_turbo_hold" val buttonKeys = listOf( KEY_BUTTON_A, @@ -211,7 +212,8 @@ class Settings { HOTKEY_PAUSE_OR_RESUME, HOTKEY_QUICKSAVE, HOTKEY_QUICKlOAD, - HOTKEY_TURBO_LIMIT + HOTKEY_TURBO_TOGGLE, + HOTKEY_TURBO_HOLD ) val hotkeyTitles = listOf( R.string.controller_hotkey_enable_button, @@ -221,7 +223,8 @@ class Settings { R.string.emulation_toggle_pause, R.string.emulation_quicksave, R.string.emulation_quickload, - R.string.turbo_limit_hotkey + R.string.turbo_limit_hotkey, + R.string.turbo_hold_hotkey ) // TODO: Move these in with the other setting keys in GenerateSettingKeys.cmake diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt index 6ec851db1..d9cfcf610 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt @@ -136,7 +136,8 @@ class InputBindingSetting( Settings.HOTKEY_PAUSE_OR_RESUME -> Hotkey.PAUSE_OR_RESUME.button Settings.HOTKEY_QUICKSAVE -> Hotkey.QUICKSAVE.button Settings.HOTKEY_QUICKlOAD -> Hotkey.QUICKLOAD.button - Settings.HOTKEY_TURBO_LIMIT -> Hotkey.TURBO_LIMIT.button + Settings.HOTKEY_TURBO_TOGGLE -> Hotkey.TURBO_TOGGLE.button + Settings.HOTKEY_TURBO_HOLD -> Hotkey.TURBO_HOLD.button else -> -1 } diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 750677fd5..010aa4e28 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -164,7 +164,8 @@ ZR This control must be bound to a gamepad analog stick or D-pad axis! This control must be bound to a gamepad button! - Turbo Speed + Turbo (Toggle) + Turbo (Hold) Turbo Speed Enabled Turbo Speed Disabled diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index 9bb0b2e34..a357e67f5 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -902,6 +902,27 @@ void GMainWindow::InitializeHotkeys() { const auto fullscreen_hotkey = hotkey_registry.GetKeySequence(main_window, fullscreen); add_secondary_window_hotkey(action_secondary_fullscreen, fullscreen_hotkey, SLOT(ToggleSecondaryFullscreen())); + + // add Toggle(Hold) to the event filter to be caught manually and processed in the two below + // methods + filter->sequences_to_catch.emplace( + hotkey_registry.GetKeySequence(main_window, QStringLiteral("Turbo Mode (Hold)"))); +} + +void GMainWindow::OnSequencePressed(QKeySequence seq) { + auto turbo_sequence = hotkey_registry.GetKeySequence(QStringLiteral("Main Window"), + QStringLiteral("Turbo Mode (Hold)")); + if (seq == turbo_sequence) { + SetTurboEnabled(true); + } +} + +void GMainWindow::OnSequenceReleased(QKeySequence seq) { + auto turbo_sequence = hotkey_registry.GetKeySequence(QStringLiteral("Main Window"), + QStringLiteral("Turbo Mode (Hold)")); + if (seq == turbo_sequence) { + SetTurboEnabled(false); + } } void GMainWindow::SetDefaultUIGeometry() { @@ -975,19 +996,49 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { } } +// helper method to check if a keyevent matches a key sequence. +bool matchesKeyEvent(const QKeyEvent* event, const QKeySequence& seq) { + if (seq.isEmpty()) + return false; + + int seqKey = seq[0]; + int eventKey = (event->key() | event->modifiers()); + return eventKey == seqKey; +} + bool GApplicationEventFilter::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::FileOpen) { emit FileOpen(static_cast(event)); return true; } + if (event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress) { + QKeyEvent* key = static_cast(event); + for (auto seq : sequences_to_catch) { + if (matchesKeyEvent(key, seq)) { + emit SequencePressCaught(seq); + return true; + } + } + } else if (event->type() == QEvent::KeyRelease) { + QKeyEvent* key = static_cast(event); + for (auto seq : sequences_to_catch) { + if (matchesKeyEvent(key, seq)) { + emit SequenceReleaseCaught(seq); + return true; + } + } + } return false; } void GMainWindow::ConnectAppEvents() { - const auto filter = new GApplicationEventFilter(); QGuiApplication::instance()->installEventFilter(filter); connect(filter, &GApplicationEventFilter::FileOpen, this, &GMainWindow::OnFileOpen); + connect(filter, &GApplicationEventFilter::SequencePressCaught, this, + &GMainWindow::OnSequencePressed); + connect(filter, &GApplicationEventFilter::SequenceReleaseCaught, this, + &GMainWindow::OnSequenceReleased); } void GMainWindow::ConnectWidgetEvents() { diff --git a/src/citra_qt/citra_qt.h b/src/citra_qt/citra_qt.h index cdf9eaff6..651fa3f29 100644 --- a/src/citra_qt/citra_qt.h +++ b/src/citra_qt/citra_qt.h @@ -94,6 +94,21 @@ enum class MediaType : u32; int LaunchQtFrontend(int argc, char* argv[]); +class GApplicationEventFilter : public QObject { + Q_OBJECT + +public: + std::set sequences_to_catch{}; + +signals: + void FileOpen(const QFileOpenEvent* event); + void SequencePressCaught(QKeySequence seq); + void SequenceReleaseCaught(QKeySequence seq); + +protected: + bool eventFilter(QObject* object, QEvent* event) override; +}; + class GMainWindow : public QMainWindow { Q_OBJECT @@ -117,7 +132,8 @@ public: void AcceptDropEvent(QDropEvent* event); void OnFileOpen(const QFileOpenEvent* event); - + void OnSequencePressed(QKeySequence seq); + void OnSequenceReleased(QKeySequence seq); void UninstallTitles( const std::vector>& titles); @@ -339,7 +355,7 @@ private: std::unique_ptr ui; Core::System& system; Core::Movie& movie; - + GApplicationEventFilter* filter = new GApplicationEventFilter(); GRenderWindow* render_window; GRenderWindow* secondary_window; @@ -464,15 +480,5 @@ protected: void showEvent(QShowEvent* event) override; }; -class GApplicationEventFilter : public QObject { - Q_OBJECT - -signals: - void FileOpen(const QFileOpenEvent* event); - -protected: - bool eventFilter(QObject* object, QEvent* event) override; -}; - Q_DECLARE_METATYPE(std::size_t); Q_DECLARE_METATYPE(Service::AM::InstallStatus); diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index a5aa9896b..0f5162ca6 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -57,7 +57,7 @@ const std::array, Settings::NativeAnalog::NumAnalogs> QtConfi // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off -const std::array QtConfig::default_hotkeys {{ +const std::array QtConfig::default_hotkeys {{ {QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, @@ -96,6 +96,7 @@ const std::array QtConfig::default_hotkeys {{ {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, {QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral("Turbo Mode (Hold)"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, }}; // clang-format on diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index 3fba498ed..35279903d 100644 --- a/src/citra_qt/configuration/config.h +++ b/src/citra_qt/configuration/config.h @@ -26,7 +26,7 @@ public: static const std::array default_buttons; static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; - static const std::array default_hotkeys; + static const std::array default_hotkeys; private: void Initialize(const std::string& config_name);