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);