diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index f176f6b87..3ca8fb7d6 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -182,6 +182,17 @@ object NativeLibrary { external fun uninstallSystemFiles(old3DS: Boolean) + + /** + * Turbo speed. + */ + external fun toggleTurboSpeed(enabled: Boolean) + + external fun getTurboSpeedSlider(): Int + + external fun setTurboSpeedSlider(value: Int) + + private var coreErrorAlertResult = false private val coreErrorAlertLock = Object() diff --git a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt index 8d9bf24e3..c7b6efa0f 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt @@ -33,6 +33,7 @@ import org.citra.citra_emu.contracts.OpenFileResultContract import org.citra.citra_emu.databinding.ActivityEmulationBinding import org.citra.citra_emu.display.ScreenAdjustmentUtil import org.citra.citra_emu.features.hotkeys.HotkeyUtility +import org.citra.citra_emu.features.hotkeys.HotkeyFunctions import org.citra.citra_emu.features.settings.model.BooleanSetting import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.SettingsViewModel @@ -55,6 +56,7 @@ class EmulationActivity : AppCompatActivity() { private lateinit var binding: ActivityEmulationBinding private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil + private lateinit var hotkeyFunctions: HotkeyFunctions private lateinit var hotkeyUtility: HotkeyUtility private val emulationFragment: EmulationFragment @@ -76,6 +78,7 @@ class EmulationActivity : AppCompatActivity() { binding = ActivityEmulationBinding.inflate(layoutInflater) screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings) hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this) + hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, hotkeyFunctions) setContentView(binding.root) val navHostFragment = @@ -138,6 +141,7 @@ class EmulationActivity : AppCompatActivity() { } override fun onDestroy() { + hotkeyFunctions.resetTurboSpeed() EmulationLifecycleUtil.clear() isEmulationRunning = false instance = null 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 db99abf67..1fc905cb3 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,4 +11,5 @@ enum class Hotkey(val button: Int) { PAUSE_OR_RESUME(10004), QUICKSAVE(10005), QUICKLOAD(10006); + TURBO_SPEED(10007); } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyFunctions.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyFunctions.kt new file mode 100644 index 000000000..251f86db5 --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyFunctions.kt @@ -0,0 +1,57 @@ +// Copyright Lime3DS Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package org.citra.citra_emu.features.hotkeys + +import android.widget.Toast +import org.citra.citra_emu.CitraApplication +import org.citra.citra_emu.NativeLibrary +import org.citra.citra_emu.features.settings.model.IntSetting +import org.citra.citra_emu.features.settings.model.Settings +import org.citra.citra_emu.features.settings.utils.SettingsFile + + +class HotkeyFunctions ( + private val settings: Settings +) { + private var normalSpeed = IntSetting.FRAME_LIMIT.int + var isTurboSpeedEnabled = false + + // Turbo Speed + fun setTurboSpeed(enabled: Boolean) { + isTurboSpeedEnabled = enabled + toggleTurboSpeed() + } + + fun toggleTurboSpeed() { + if (isTurboSpeedEnabled) { + normalSpeed = IntSetting.FRAME_LIMIT.int + NativeLibrary.toggleTurboSpeed(true) + NativeLibrary.setTurboSpeedSlider(IntSetting.TURBO_SPEED.int) + IntSetting.FRAME_LIMIT.int = IntSetting.TURBO_SPEED.int + } else { + NativeLibrary.toggleTurboSpeed(false) + NativeLibrary.setTurboSpeedSlider(normalSpeed) + IntSetting.FRAME_LIMIT.int = normalSpeed + } + + settings.saveSetting(IntSetting.FRAME_LIMIT, SettingsFile.FILE_NAME_CONFIG) + NativeLibrary.reloadSettings() + + val context = CitraApplication.appContext + Toast.makeText(context + , "Changed Emulation Speed to: ${IntSetting.FRAME_LIMIT.int}%", Toast.LENGTH_SHORT).show() + } + + fun resetTurboSpeed() { + if (isTurboSpeedEnabled) { + isTurboSpeedEnabled = false + NativeLibrary.toggleTurboSpeed(false) + IntSetting.FRAME_LIMIT.int = normalSpeed + + settings.saveSetting(IntSetting.FRAME_LIMIT, SettingsFile.FILE_NAME_CONFIG) + NativeLibrary.reloadSettings() + } + } +} \ No newline at end of file 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 25f6a493b..42b180ec5 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 @@ -22,6 +22,7 @@ class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil, priv Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts() Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame() Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume() + Hotkey.TURBO_SPEED.button -> hotkeyFunctions.setTurboSpeed(!hotkeyFunctions.isTurboSpeedEnabled) Hotkey.QUICKSAVE.button -> { NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT) Toast.makeText(context, diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt index 0f1ca8d43..a077c8c05 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt @@ -65,6 +65,8 @@ enum class IntSetting( USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, 0), ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2), DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 0); + TURBO_SPEED("turbo_speed", Settings.SECTION_CORE, 200); + override var int: Int = defaultValue override val valueAsString: String @@ -94,6 +96,7 @@ enum class IntSetting( AUDIO_INPUT_TYPE, USE_ARTIC_BASE_CONTROLLER, SHADERS_ACCURATE_MUL, + FRAME_LIMIT ) fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key } 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 5ea358989..f11df55f9 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 @@ -139,6 +139,7 @@ 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_SPEED = "hotkey_turbo_speed" val buttonKeys = listOf( KEY_BUTTON_A, @@ -204,7 +205,8 @@ class Settings { HOTKEY_CLOSE_GAME, HOTKEY_PAUSE_OR_RESUME, HOTKEY_QUICKSAVE, - HOTKEY_QUICKlOAD + HOTKEY_QUICKlOAD, + HOTKEY_TURBO_SPEED ) val hotkeyTitles = listOf( R.string.emulation_swap_screens, @@ -213,6 +215,7 @@ class Settings { R.string.emulation_toggle_pause, R.string.emulation_quicksave, R.string.emulation_quickload, + R.string.emulation_toggle_turbo_speed ) const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" 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 8ecd60684..5a722c07c 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 @@ -133,6 +133,7 @@ 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_SPEED -> Hotkey.TURBO_SPEED.button else -> -1 } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 3dde86f0e..bc04a8ef7 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -236,6 +236,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) IntSetting.FRAME_LIMIT.defaultValue.toFloat() ) ) + add( + SliderSetting( + IntSetting.TURBO_SPEED, + R.string.turbo_speed, + R.string.turbo_speed_description, + 100, + 400, + "%", + IntSetting.TURBO_SPEED.key, + IntSetting.TURBO_SPEED.defaultValue.toFloat() + ) + ) } } diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 19e422324..6ad3a6cd1 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -149,8 +149,8 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.use_vsync_new); ReadSetting("Renderer", Settings::values.texture_filter); ReadSetting("Renderer", Settings::values.texture_sampling); - - // Work around to map Android setting for enabling the frame limiter to the format Citra expects + ReadSetting("Renderer", Settings::values.turbo_speed); + // Workaround to map Android setting for enabling the frame limiter to the format Citra expects if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) { ReadSetting("Renderer", Settings::values.frame_limit); } else { diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index a88a4036c..6b7871f9a 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -772,4 +772,19 @@ void Java_org_citra_citra_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIE LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); } +void JNICALL Java_io_github_lime3ds_android_NativeLibrary_toggleTurboSpeed( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jboolean enabled) { + Settings::values.turbo_speed = enabled ? true : false; +} + +jint JNICALL Java_io_github_lime3ds_android_NativeLibrary_getTurboSpeedSlider( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) { + return static_cast(Settings::values.turbo_speed); +} + +void JNICALL Java_io_github_lime3ds_android_NativeLibrary_setTurboSpeedSlider( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jint value) { + Settings::values.turbo_speed = value; +} + } // extern "C" diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index eb108e1b6..eb0fa6bd0 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -231,9 +231,12 @@ Enable asynchronous GPU emulation Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved. Limit Speed - When enabled, emulation speed will be limited to a specified percentage of normal speed. + When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work. Limit Speed Percent Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit. + Turbo Speed + Turbo Speed + Value used for the turbo hotkey Internal Resolution Specifies the resolution used to render at. A high resolution will improve visual quality a lot but is also quite heavy on performance and might cause glitches in certain applications. Native (400x240) diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index 3f2b06e76..153fb4145 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -782,6 +782,11 @@ void GMainWindow::InitializeHotkeys() { } }); connect_shortcut(QStringLiteral("Toggle Per-Application Speed"), [&] { + if (!hotkey_registry + .GetKeySequence(QStringLiteral("Main Window"), QStringLiteral("Toggle Turbo Mode")) + .isEmpty()) { + return; + } Settings::values.frame_limit.SetGlobal(!Settings::values.frame_limit.UsingGlobal()); UpdateStatusBar(); }); @@ -789,31 +794,12 @@ void GMainWindow::InitializeHotkeys() { [&] { Settings::values.dump_textures = !Settings::values.dump_textures; }); connect_shortcut(QStringLiteral("Toggle Custom Textures"), [&] { Settings::values.custom_textures = !Settings::values.custom_textures; }); - // We use "static" here in order to avoid capturing by lambda due to a MSVC bug, which makes - // the variable hold a garbage value after this function exits - static constexpr u16 SPEED_LIMIT_STEP = 5; - connect_shortcut(QStringLiteral("Increase Speed Limit"), [&] { - if (Settings::values.frame_limit.GetValue() == 0) { - return; - } - if (Settings::values.frame_limit.GetValue() < 995 - SPEED_LIMIT_STEP) { - Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() + - SPEED_LIMIT_STEP); - } else { - Settings::values.frame_limit = 0; - } - UpdateStatusBar(); - }); - connect_shortcut(QStringLiteral("Decrease Speed Limit"), [&] { - if (Settings::values.frame_limit.GetValue() == 0) { - Settings::values.frame_limit = 995; - } else if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) { - Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() - - SPEED_LIMIT_STEP); - UpdateStatusBar(); - } - UpdateStatusBar(); - }); + + connect_shortcut(QStringLiteral("Toggle Turbo Mode"), &GMainWindow::ToggleEmulationSpeed); + + connect_shortcut(QStringLiteral("Increase Speed Limit"), [&] { AdjustSpeedLimit(true); }); + + connect_shortcut(QStringLiteral("Decrease Speed Limit"), [&] { AdjustSpeedLimit(false); }); connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute); connect_shortcut(QStringLiteral("Audio Volume Down"), &GMainWindow::OnDecreaseVolume); @@ -2354,6 +2340,7 @@ void GMainWindow::OnMenuRecentFile() { } void GMainWindow::OnStartGame() { + GetInitialFrameLimit(); qt_cameras->ResumeCameras(); PreventOSSleep(); @@ -2413,6 +2400,12 @@ void GMainWindow::OnPauseContinueGame() { } void GMainWindow::OnStopGame() { + if (turbo_mode_active) { + turbo_mode_active = false; + Settings::values.frame_limit.SetValue(initial_frame_limit); + UpdateStatusBar(); + } + play_time_manager->Stop(); // Update game list to show new play time game_list->PopulateAsync(UISettings::values.game_dirs); @@ -2568,6 +2561,54 @@ void GMainWindow::ChangeSmallScreenPosition() { UpdateSecondaryWindowVisibility(); } +void GMainWindow::GetInitialFrameLimit() { + initial_frame_limit = Settings::values.frame_limit.GetValue(); + turbo_mode_active = false; +} + +void GMainWindow::ToggleEmulationSpeed() { + static bool key_pressed = false; // Prevent spam on hold + + if (!key_pressed) { + key_pressed = true; + turbo_mode_active = !turbo_mode_active; + + if (turbo_mode_active) { + Settings::values.frame_limit.SetValue(Settings::values.turbo_speed.GetValue()); + } else { + Settings::values.frame_limit.SetValue(initial_frame_limit); + } + + UpdateStatusBar(); + QTimer::singleShot(200, [] { key_pressed = false; }); + } +} + +void GMainWindow::AdjustSpeedLimit(bool increase) { + if (!turbo_mode_active) { + return; + } + + const int SPEED_LIMIT_STEP = 5; + int turbo_speed = Settings::values.turbo_speed.GetValue(); + + if (increase) { + if (turbo_speed < 995) { + Settings::values.turbo_speed.SetValue(turbo_speed + SPEED_LIMIT_STEP); + Settings::values.frame_limit.SetValue(turbo_speed + SPEED_LIMIT_STEP); + } + } else { + if (turbo_speed > SPEED_LIMIT_STEP) { + Settings::values.turbo_speed.SetValue(turbo_speed - SPEED_LIMIT_STEP); + Settings::values.frame_limit.SetValue(turbo_speed - SPEED_LIMIT_STEP); + } + } + + if (turbo_mode_active) { + UpdateStatusBar(); + } +} + void GMainWindow::ToggleScreenLayout() { const Settings::LayoutOption new_layout = []() { switch (Settings::values.layout_option.GetValue()) { diff --git a/src/citra_qt/citra_qt.h b/src/citra_qt/citra_qt.h index e352b49a3..d77f059c4 100644 --- a/src/citra_qt/citra_qt.h +++ b/src/citra_qt/citra_qt.h @@ -222,6 +222,7 @@ private: private slots: void OnStartGame(); + void GetInitialFrameLimit(); void OnRestartGame(); void OnPauseGame(); void OnPauseContinueGame(); @@ -261,6 +262,8 @@ private slots: void ToggleSecondaryFullscreen(); void ChangeScreenLayout(); void ChangeSmallScreenPosition(); + void ToggleEmulationSpeed(); + void AdjustSpeedLimit(bool increase); void UpdateSecondaryWindowVisibility(); void ToggleScreenLayout(); void OnSwapScreens(); @@ -349,6 +352,9 @@ private: UserDataMigrator user_data_migrator; std::unique_ptr config; + // Hotkeys + bool turbo_mode_active = false; + // Whether emulation is currently running in Citra. bool emulation_running = false; std::unique_ptr emu_thread; @@ -406,6 +412,8 @@ private: u32 newest_slot; u64 newest_slot_time; + int initial_frame_limit; + // Secondary window actions QAction* action_secondary_fullscreen; QAction* action_secondary_toggle_screen; diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 5e261dca3..2c325f606 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -54,7 +54,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}}, @@ -83,6 +83,7 @@ const std::array QtConfig::default_hotkeys {{ {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, {QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}}, {QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}}, + {QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, {QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}}, @@ -806,6 +807,7 @@ void QtConfig::ReadUIValues() { ReadBasicSetting(UISettings::values.display_titlebar); ReadBasicSetting(UISettings::values.show_filter_bar); ReadBasicSetting(UISettings::values.show_status_bar); + ReadBasicSetting(Settings::values.turbo_speed); ReadBasicSetting(UISettings::values.confirm_before_closing); ReadBasicSetting(UISettings::values.save_state_warning); ReadBasicSetting(UISettings::values.first_start); @@ -1313,6 +1315,7 @@ void QtConfig::SaveUIValues() { WriteBasicSetting(UISettings::values.show_filter_bar); WriteBasicSetting(UISettings::values.show_status_bar); WriteBasicSetting(UISettings::values.confirm_before_closing); + WriteBasicSetting(Settings::values.turbo_speed); WriteBasicSetting(UISettings::values.save_state_warning); WriteBasicSetting(UISettings::values.first_start); WriteBasicSetting(UISettings::values.callout_flags); diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index d1a7abfcf..6df56367e 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); diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index 6c5a78e11..6071d1e18 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -28,7 +28,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor bool enable_web_config) : QDialog(parent), ui{std::make_unique()}, registry{registry_}, system{system_}, is_powered_on{system.IsPoweredOn()}, - general_tab{std::make_unique(this)}, + general_tab{std::make_unique(is_powered_on, this)}, system_tab{std::make_unique(system, this)}, input_tab{std::make_unique(system, this)}, hotkeys_tab{std::make_unique(this)}, diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index cf091ec3c..87a46de67 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -24,11 +24,16 @@ static constexpr int SettingsToSlider(int value) { return (value - 5) / 5; } -ConfigureGeneral::ConfigureGeneral(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { - +ConfigureGeneral::ConfigureGeneral(bool is_powered_on, QWidget* parent) + : QWidget(parent), ui(std::make_unique()), is_powered_on{is_powered_on} { ui->setupUi(this); + connect(ui->turbo_speed, &QSlider::valueChanged, this, [&](int value) { + Settings::values.turbo_speed.SetValue(SliderToSettings(value)); + ui->turbo_speed_display_label->setText( + QStringLiteral("%1%").arg(Settings::values.turbo_speed.GetValue())); + }); + // Set a minimum width for the label to prevent the slider from changing size. // This scales across DPIs, and is acceptable for uncapitalized strings. const auto width = static_cast(tr("unthrottled").size() * 6); @@ -73,6 +78,10 @@ ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::SetConfiguration() { if (Settings::IsConfiguringGlobal()) { + ui->turbo_speed->setValue(SettingsToSlider(Settings::values.turbo_speed.GetValue())); + ui->turbo_speed_display_label->setText( + QStringLiteral("%1%").arg(Settings::values.turbo_speed.GetValue())); + ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); ui->toggle_background_pause->setChecked( UISettings::values.pause_when_in_background.GetValue()); @@ -101,7 +110,10 @@ void ConfigureGeneral::SetConfiguration() { } if (!Settings::IsConfiguringGlobal()) { - if (Settings::values.frame_limit.UsingGlobal()) { + if (is_powered_on) { + ui->emulation_speed_combo->setEnabled(false); + ui->frame_limit->setEnabled(false); + } else if (Settings::values.frame_limit.UsingGlobal()) { ui->emulation_speed_combo->setCurrentIndex(0); ui->frame_limit->setEnabled(false); } else { @@ -192,7 +204,11 @@ void ConfigureGeneral::RetranslateUI() { void ConfigureGeneral::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { ui->region_combobox->setEnabled(Settings::values.region_value.UsingGlobal()); - ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); + if (is_powered_on) { + ui->frame_limit->setEnabled(false); + } else { + ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); + } return; } @@ -208,6 +224,7 @@ void ConfigureGeneral::SetupPerGameUI() { ConfigurationShared::SetHighlight(ui->widget_screenshot, index == 1); }); + ui->turbo_speed->setVisible(false); ui->general_group->setVisible(false); ui->button_reset_defaults->setVisible(false); ui->toggle_gamemode->setVisible(false); diff --git a/src/citra_qt/configuration/configure_general.h b/src/citra_qt/configuration/configure_general.h index 3c6cef219..1d5f6adba 100644 --- a/src/citra_qt/configuration/configure_general.h +++ b/src/citra_qt/configuration/configure_general.h @@ -17,7 +17,7 @@ class ConfigureGeneral : public QWidget { Q_OBJECT public: - explicit ConfigureGeneral(QWidget* parent = nullptr); + explicit ConfigureGeneral(bool is_powered_on, QWidget* parent = nullptr); ~ConfigureGeneral() override; void ResetDefaults(); @@ -29,4 +29,5 @@ public: private: std::unique_ptr ui; + bool is_powered_on; }; diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index 4b6b0adf6..b73f414df 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -216,6 +216,66 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Turbo Speed: + + + + + + + 0 + + + 198 + + + 5 + + + 15 + + + 19 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + diff --git a/src/citra_qt/configuration/configure_hotkeys.cpp b/src/citra_qt/configuration/configure_hotkeys.cpp index 6ac0dcd8a..0364d643a 100644 --- a/src/citra_qt/configuration/configure_hotkeys.cpp +++ b/src/citra_qt/configuration/configure_hotkeys.cpp @@ -98,6 +98,37 @@ void ConfigureHotkeys::Configure(QModelIndex index) { } const auto [key_sequence_used, used_action] = IsUsedKey(key_sequence); + // Check for turbo/per-game speed conflict. Needed to prevent the user from binding both hotkeys + // to the same action. Which cuases problems resetting the frame limit.to the inititla value. + const QString current_action = + model->data(model->index(index.row(), 0, index.parent())).toString(); + const bool is_turbo = current_action == tr("Toggle Turbo Mode"); + const bool is_per_game = current_action == tr("Toggle Per-Game Speed"); + + if (is_turbo || is_per_game) { + QString other_action = is_turbo ? tr("Toggle Per-Game Speed") : tr("Toggle Turbo Mode"); + QKeySequence other_sequence; + + for (int r = 0; r < model->rowCount(); ++r) { + const QStandardItem* const parent = model->item(r, 0); + for (int r2 = 0; r2 < parent->rowCount(); ++r2) { + if (parent->child(r2, 0)->text() == other_action) { + other_sequence = QKeySequence::fromString( + parent->child(r2, hotkey_column)->text(), QKeySequence::NativeText); + break; + } + } + } + + // Show warning if either hotkey is already set + if (!key_sequence.isEmpty() && !other_sequence.isEmpty()) { + QMessageBox::warning( + this, tr("Conflicting Key Sequence"), + tr("The per-game speed and turbo speed hotkeys cannot be bound at the same time.")); + return; + } + } + if (key_sequence_used && key_sequence != QKeySequence(previous_key.toString())) { QMessageBox::warning( this, tr("Conflicting Key Sequence"), diff --git a/src/citra_qt/configuration/configure_per_game.cpp b/src/citra_qt/configuration/configure_per_game.cpp index 88709df16..562eff8f7 100644 --- a/src/citra_qt/configuration/configure_per_game.cpp +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -35,7 +35,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString const bool is_powered_on = system.IsPoweredOn(); audio_tab = std::make_unique(is_powered_on, this); - general_tab = std::make_unique(this); + general_tab = std::make_unique(is_powered_on, this); enhancements_tab = std::make_unique(this); layout_tab = std::make_unique(this); graphics_tab = diff --git a/src/common/settings.h b/src/common/settings.h index b3351085c..8babbd1b1 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -495,6 +495,7 @@ struct Values { Setting use_shader_jit{true, "use_shader_jit"}; SwitchableSetting resolution_factor{1, 0, 10, "resolution_factor"}; SwitchableSetting frame_limit{100, 0, 1000, "frame_limit"}; + SwitchableSetting turbo_speed{200, 0, 1000, "turbo_speed"}; SwitchableSetting texture_filter{TextureFilter::NoFilter, "texture_filter"}; SwitchableSetting texture_sampling{TextureSampling::GameControlled, "texture_sampling"};