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 583e59863..4815f9e2c 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 @@ -186,6 +186,17 @@ object NativeLibrary { external fun unlinkConsole() + + /** + * 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 1ab511ad6..c91f63d40 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 @@ -75,7 +77,8 @@ class EmulationActivity : AppCompatActivity() { binding = ActivityEmulationBinding.inflate(layoutInflater) screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings) - hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this) + hotkeyFunctions = HotkeyFunctions(settingsViewModel.settings) + hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, hotkeyFunctions, this) 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..bbc9fd2b0 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -10,5 +10,6 @@ enum class Hotkey(val button: Int) { CLOSE_GAME(10003), PAUSE_OR_RESUME(10004), QUICKSAVE(10005), - QUICKLOAD(10006); + 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..e9e49293b --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyFunctions.kt @@ -0,0 +1,57 @@ +// Copyright Citra Emulator Project / Azahar 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..9a99937ee 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -11,9 +11,12 @@ import org.citra.citra_emu.R import org.citra.citra_emu.utils.EmulationLifecycleUtil import org.citra.citra_emu.display.ScreenAdjustmentUtil -class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil, private val context: Context) { +class HotkeyUtility( + private val screenAdjustmentUtil: ScreenAdjustmentUtil, + private val hotkeyFunctions: HotkeyFunctions, + private val context: Context) { - val hotkeyButtons = Hotkey.entries.map { it.button } + private val hotkeyButtons = Hotkey.entries.map { it.button } fun handleHotkey(bindedButton: Int): Boolean { if(hotkeyButtons.contains(bindedButton)) { @@ -22,6 +25,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 ad63443f8..c3ae61941 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,7 +65,9 @@ enum class IntSetting( DELAY_RENDER_THREAD_US("delay_game_render_thread_us", Settings.SECTION_RENDERER, 0), 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); + 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 @@ -95,6 +97,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..d8c141ce3 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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..386ab7b4a 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 66a29b187..267d18f42 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 @@ -237,6 +237,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 3fd3e4f0f..902ba9bb5 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 02cc329af..45655fc8e 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -773,6 +773,22 @@ 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_org_citra_citra_1emu_NativeLibrary_toggleTurboSpeed([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jobject obj, + jboolean enabled) { + Settings::values.turbo_speed = enabled ? true : false; +} + +jint JNICALL Java_org_citra_citra_1emu_NativeLibrary_getTurboSpeedSlider( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) { + return static_cast(Settings::values.turbo_speed); +} + +void JNICALL Java_org_citra_citra_1emu_NativeLibrary_setTurboSpeedSlider( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jint value) { + Settings::values.turbo_speed = value; +} + jboolean Java_org_citra_citra_1emu_NativeLibrary_isFullConsoleLinked(JNIEnv* env, jobject obj) { return HW::UniqueData::IsFullConsoleLinked(); } diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 70582074d..1579c8e21 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -242,11 +242,14 @@ Enable asynchronous GPU emulation Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved. Limit Speed - Expand to Cutout Area - Expands the display area to include the cutout (or notch) area. - 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. + Expand to Cutout Area + Expands the display area to include the cutout (or notch) area. + Turbo Speed + Turbo Speed + Emulation speed limit used while the turbo hotkey is active. 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. Auto (Screen Size) diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index 5939d4c20..c54f7f318 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -785,6 +785,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(); }); @@ -792,31 +797,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); @@ -2363,6 +2349,7 @@ void GMainWindow::OnMenuRecentFile() { } void GMainWindow::OnStartGame() { + GetInitialFrameLimit(); qt_cameras->ResumeCameras(); PreventOSSleep(); @@ -2422,6 +2409,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); @@ -2573,6 +2566,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 1c5184928..df16ffeac 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(); @@ -260,6 +261,8 @@ private slots: void ToggleSecondaryFullscreen(); void ChangeScreenLayout(); void ChangeSmallScreenPosition(); + void ToggleEmulationSpeed(); + void AdjustSpeedLimit(bool increase); void UpdateSecondaryWindowVisibility(); void ToggleScreenLayout(); void OnSwapScreens(); @@ -348,6 +351,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; @@ -405,6 +411,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 c72810b37..07057421d 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}}, @@ -807,6 +808,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); @@ -1316,6 +1318,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..de7c92f99 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 f80b00760..21b625e56 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); @@ -75,6 +80,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()); @@ -103,7 +112,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 { @@ -182,7 +194,11 @@ void ConfigureGeneral::RetranslateUI() { void ConfigureGeneral::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { - 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; } @@ -198,6 +214,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..2d401f55d 100644 --- a/src/citra_qt/configuration/configure_general.h +++ b/src/citra_qt/configuration/configure_general.h @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 aebd12464..84e243667 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -147,6 +147,82 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Turbo Speed: + + + + + + + 0 + + + 198 + + + 5 + + + 15 + + + 19 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 32 + 20 + + + + + + + + + + + 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..d3b919ea6 100644 --- a/src/citra_qt/configuration/configure_hotkeys.cpp +++ b/src/citra_qt/configuration/configure_hotkeys.cpp @@ -1,4 +1,4 @@ -// Copyright 2017 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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..130ae02db 100644 --- a/src/citra_qt/configuration/configure_per_game.cpp +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -1,5 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. #include #include @@ -35,7 +40,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 c09021e60..527c16655 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -497,6 +497,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"};