From 0c5d2ba557326800b01941ea5ce866c2c656d46a Mon Sep 17 00:00:00 2001 From: David Griswold Date: Tue, 31 Mar 2026 16:26:39 +0300 Subject: [PATCH] Add Turbo (Hold) Hotkey to desktop. Requred expanding the GApplicationFilter class to contain a set of sequences to filter, a helper method to filter custom sequences, and some new signals and slots. The GApplicationFilter is now a local variable of citra_qt so that sequences can be added by the InitializeHotkeys function. --- src/citra_qt/citra_qt.cpp | 53 ++++++++++++++++++++++++++- src/citra_qt/citra_qt.h | 30 +++++++++------ src/citra_qt/configuration/config.cpp | 3 +- src/citra_qt/configuration/config.h | 2 +- 4 files changed, 73 insertions(+), 15 deletions(-) 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);