diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 1f3d9a22a2..e49f466462 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -203,6 +203,24 @@ object NativeLibrary { external fun getDebugKnobAt(index: Int): Boolean + /** + * Set the current speed limit to the configured turbo speed. + */ + external fun setTurboSpeedLimit(enabled: Boolean) + + /** + * Set the current speed limit to the configured slow speed. + */ + external fun setSlowSpeedLimit(enabled: Boolean) + + /** + * Set the current speed limit to the configured standard speed. + */ + external fun setStandardSpeedLimit(enabled: Boolean) + + external fun isTurboMode(): Boolean + external fun isSlowMode(): Boolean + /** * Returns Vulkan driver version / API version / GPU model */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt index 992f8f2a16..400257e8d9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt @@ -11,6 +11,7 @@ import android.widget.RadioGroup import android.widget.TextView import androidx.drawerlayout.widget.DrawerLayout import com.google.android.material.color.MaterialColors +import com.google.android.material.materialswitch.MaterialSwitch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting @@ -135,6 +136,39 @@ class QuickSettings(val emulationFragment: EmulationFragment) { container.addView(itemView) } + fun addCustomToggle( + name: Int, + isChecked: Boolean, + isEnabled: Boolean, + + container: ViewGroup, + callback: (Boolean) -> Unit + ): MaterialSwitch? { + val inflater = LayoutInflater.from(emulationFragment.requireContext()) + val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) + + val switchContainer = itemView.findViewById(R.id.switch_container) + val titleView = itemView.findViewById(R.id.switch_title) + val switchView = itemView.findViewById(R.id.setting_switch) + + titleView.text = YuzuApplication.appContext.getString(name) + switchContainer.visibility = View.VISIBLE + + switchView.isChecked = isChecked + + switchView.setOnCheckedChangeListener { _, checked -> + callback(checked) + saveSettings() + } + + switchContainer.setOnClickListener { + switchView.toggle() + } + container.addView(itemView) + + return switchView + } + fun addSliderSetting( name: Int, container: ViewGroup, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt index 16eb4ffdd5..b38136ad36 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +9,10 @@ package org.yuzu.yuzu_emu.features.settings.model import org.yuzu.yuzu_emu.utils.NativeConfig enum class ShortSetting(override val key: String) : AbstractShortSetting { - RENDERER_SPEED_LIMIT("speed_limit"); + RENDERER_SPEED_LIMIT("speed_limit"), + RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"), + RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"), + ; override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 762f0da262..02289edeae 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -180,6 +180,26 @@ abstract class SettingsItem( units = "%" ) ) + put( + SliderSetting( + ShortSetting.RENDERER_TURBO_SPEED_LIMIT, + titleId = R.string.turbo_speed_limit, + descriptionId = R.string.turbo_speed_limit_description, + min = 1, + max = 400, + units = "%" + ) + ) + put( + SliderSetting( + ShortSetting.RENDERER_SLOW_SPEED_LIMIT, + titleId = R.string.slow_speed_limit, + descriptionId = R.string.slow_speed_limit_description, + min = 1, + max = 400, + units = "%" + ) + ) put( SingleChoiceSetting( IntSetting.CPU_BACKEND, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 06d4d74176..61b86c70d0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -227,6 +227,8 @@ class SettingsFragmentPresenter( add(StringSetting.DEVICE_NAME.key) add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) add(ShortSetting.RENDERER_SPEED_LIMIT.key) + add(ShortSetting.RENDERER_TURBO_SPEED_LIMIT.key) + add(ShortSetting.RENDERER_SLOW_SPEED_LIMIT.key) add(BooleanSetting.USE_DOCKED_MODE.key) add(IntSetting.REGION_INDEX.key) add(IntSetting.LANGUAGE_INDEX.key) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 0c091fdeb9..e8739e2d23 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -55,6 +55,7 @@ import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowLayoutInfo import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.materialswitch.MaterialSwitch import com.google.android.material.textview.MaterialTextView import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers @@ -1055,11 +1056,47 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { quickSettings.addPerGameConfigStatusIndicator(container) } - quickSettings.addBooleanSetting( + lateinit var slowSpeed: MaterialSwitch + lateinit var turboSpeed: MaterialSwitch + + turboSpeed = quickSettings.addCustomToggle( + R.string.turbo_speed_limit, + NativeLibrary.isTurboMode(), + BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false), + container + ) { enabled -> + if (enabled) + slowSpeed.isChecked = false + NativeLibrary.setTurboSpeedLimit(enabled) + }!! + + slowSpeed = quickSettings.addCustomToggle( + R.string.slow_speed_limit, + NativeLibrary.isSlowMode(), + BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false), + container + ) { enabled -> + if (enabled) + turboSpeed.isChecked = false + NativeLibrary.setSlowSpeedLimit(enabled) + }!! + + quickSettings.addCustomToggle( R.string.frame_limit_enable, - container, - BooleanSetting.RENDERER_USE_SPEED_LIMIT, - ) + BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false), + true, + container + ) { enabled -> + if (!enabled) { + turboSpeed.isChecked = false + slowSpeed.isChecked = false + } + + turboSpeed.isEnabled = enabled + slowSpeed.isEnabled = enabled + + NativeLibrary.setStandardSpeedLimit(enabled) + }!! quickSettings.addSliderSetting( R.string.frame_limit_slider, diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 9cb44bbd3a..2e50bb1069 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -1233,6 +1233,39 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobje return static_cast(Settings::getDebugKnobAt(static_cast(index))); } +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) { + if (enabled) { + Settings::values.use_speed_limit.SetValue(true); + Settings::SetSpeedMode(Settings::SpeedMode::Turbo); + } else { + Settings::SetSpeedMode(Settings::SpeedMode::Standard); + } +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setSlowSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) { + if (enabled) { + Settings::values.use_speed_limit.SetValue(true); + Settings::SetSpeedMode(Settings::SpeedMode::Slow); + } else { + Settings::SetSpeedMode(Settings::SpeedMode::Standard); + } +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setStandardSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) { + Settings::values.use_speed_limit.SetValue(enabled); + if (enabled) { + Settings::SetSpeedMode(Settings::SpeedMode::Standard); + } +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isTurboMode(JNIEnv *env, jobject jobj) { + return Settings::values.current_speed_mode.GetValue() == Settings::SpeedMode::Turbo; +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isSlowMode(JNIEnv *env, jobject jobj) { + return Settings::values.current_speed_mode.GetValue() == Settings::SpeedMode::Slow; +} + void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, jint j_program_index, jboolean j_frontend_initiated) { diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 3d6a27bb3c..e2148eb5ef 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -422,6 +422,10 @@ Limits emulation speed to a specified percentage of normal speed. Limit speed percent Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit. + Turbo speed + When Turbo Mode is enabled, emulation will run at this speed. + Slow speed + When Slow Mode is enabled, emulation will run at this speed. CPU backend CPU accuracy %1$s%2$s diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 5d8a0e41f6..c952567e63 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -379,4 +379,52 @@ void SetConfiguringGlobal(bool is_global) { configuring_global = is_global; } +u16 SpeedLimit() { + switch (SpeedMode(values.current_speed_mode)) { + case SpeedMode::Standard: + return values.speed_limit.GetValue(); + case SpeedMode::Turbo: + return values.turbo_speed_limit.GetValue(); + case SpeedMode::Slow: + return values.slow_speed_limit.GetValue(); + default: + UNIMPLEMENTED(); + } + + return 100; +} + +void SetSpeedMode(const SpeedMode& mode) { + values.current_speed_mode.SetValue(mode); + + switch (mode) { + case SpeedMode::Turbo: + case SpeedMode::Slow: + values.use_speed_limit.SetValue(true); + break; + case SpeedMode::Standard: + default: + break; + } +} + +void ToggleStandardMode() { + values.use_speed_limit.SetValue(!values.use_speed_limit.GetValue()); + SetSpeedMode(SpeedMode::Standard); +} + +void ToggleTurboMode() { + if (values.current_speed_mode.GetValue() != SpeedMode::Turbo) + SetSpeedMode(SpeedMode::Turbo); + else + SetSpeedMode(SpeedMode::Standard); +} + +void ToggleSlowMode() { + if (values.current_speed_mode.GetValue() != SpeedMode::Slow) + SetSpeedMode(SpeedMode::Slow); + else + SetSpeedMode(SpeedMode::Standard); +} + } // namespace Settings diff --git a/src/common/settings.h b/src/common/settings.h index 385ae7e1c9..de7387c4f8 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -204,6 +204,7 @@ struct Values { true}; SwitchableSetting use_speed_limit{ linkage, true, "use_speed_limit", Category::Core, Specialization::Paired, true, true}; + SwitchableSetting speed_limit{linkage, 100, 0, @@ -214,6 +215,30 @@ struct Values { true, true, &use_speed_limit}; + + SwitchableSetting slow_speed_limit{linkage, + 50, + 0, + 9999, + "slow_speed_limit", + Category::Core, + Specialization::Countable | Specialization::Percentage, + true, + true}; + + SwitchableSetting turbo_speed_limit{linkage, + 200, + 0, + 9999, + "turbo_speed_limit", + Category::Core, + Specialization::Countable | Specialization::Percentage, + true, + true}; + + // The currently used speed mode. + Setting current_speed_mode{linkage, SpeedMode::Standard, "current_speed_mode", Category::Core, Specialization::Default, false, true}; + SwitchableSetting sync_core_speed{linkage, false, "sync_core_speed", Category::Core, Specialization::Default}; @@ -824,6 +849,13 @@ bool IsDockedMode(); float Volume(); +// speed limit ops +u16 SpeedLimit(); +void SetSpeedMode(const SpeedMode &mode); +void ToggleStandardMode(); +void ToggleTurboMode(); +void ToggleSlowMode(); + std::string GetTimeZoneString(TimeZone time_zone); void LogSettings(); diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 7e3bef9bea..62bf17c51a 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -157,6 +157,7 @@ ENUM(TemperatureUnits, Celsius, Fahrenheit) ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3); ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All) ENUM(GameListMode, TreeView, GridView); +ENUM(SpeedMode, Standard, Turbo, Slow); template inline std::string_view CanonicalizeEnum(Type id) { diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b0fa7cd503..6f45a270db 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -201,7 +201,7 @@ u64 CoreTiming::GetClockTicks() const { if (Settings::values.sync_core_speed.GetValue()) { const auto ticks = double(fres); - const auto speed_limit = double(Settings::values.speed_limit.GetValue())*0.01; + const auto speed_limit = double(Settings::SpeedLimit())*0.01; return u64(ticks/speed_limit); } else { return fres; diff --git a/src/core/hle/service/vi/conductor.cpp b/src/core/hle/service/vi/conductor.cpp index a271041be6..801a135dd7 100644 --- a/src/core/hle/service/vi/conductor.cpp +++ b/src/core/hle/service/vi/conductor.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -95,7 +95,7 @@ s64 Conductor::GetNextTicks() const { if (settings.use_speed_limit.GetValue()) { // Scales the speed based on speed_limit setting on MC. SC is handled by // SpeedLimiter::DoSpeedLimiting. - speed_scale = 100.f / settings.speed_limit.GetValue(); + speed_scale = 100.f / Settings::SpeedLimit(); } else { // Run at unlocked framerate. speed_scale = 0.01f; diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 35e76624f4..1ca3fe488c 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2017 Citra Emulator Project @@ -143,7 +143,7 @@ void SpeedLimiter::DoSpeedLimiting(microseconds current_system_time_us) { auto now = Clock::now(); - const double sleep_scale = Settings::values.speed_limit.GetValue() / 100.0; + const double sleep_scale = Settings::SpeedLimit() / 100.0; // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current // speed percent or it will clamp too much and prevent this from properly limiting to that diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 225cdcd10e..7d53eb609e 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -69,10 +69,10 @@ std::unique_ptr InitializeTranslations(QObject* parent) Settings, memory_layout_mode, tr("Memory Layout"), - tr("Increases the amount of emulated RAM from 4GB of the board to the " - "devkit 8/6GB.\nDoesn't affect performance/stability but may allow HD texture " + tr("Increases the amount of emulated RAM.\nDoesn't affect performance/stability but may allow HD texture " "mods to load.")); INSERT(Settings, use_speed_limit, QString(), QString()); + INSERT(Settings, current_speed_mode, QString(), QString()); INSERT(Settings, speed_limit, tr("Limit Speed Percent"), @@ -80,6 +80,14 @@ std::unique_ptr InitializeTranslations(QObject* parent) "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a " "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the " "maximum your PC can reach.")); + + INSERT(Settings, turbo_speed_limit, tr("Turbo Speed"), + tr("When the Turbo Speed hotkey is pressed, the speed will be limited to this " + "percentage.")); + INSERT(Settings, slow_speed_limit, tr("Slow Speed"), + tr("When the Slow Speed hotkey is pressed, the speed will be limited to this " + "percentage.")); + INSERT(Settings, sync_core_speed, tr("Synchronize Core Speed"), diff --git a/src/qt_common/config/uisettings.h b/src/qt_common/config/uisettings.h index 3c75268377..b52c673305 100644 --- a/src/qt_common/config/uisettings.h +++ b/src/qt_common/config/uisettings.h @@ -249,7 +249,7 @@ void RestoreWindowState(std::unique_ptr& qtConfig); // 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 default_hotkeys{{ +const std::array default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, @@ -265,11 +265,11 @@ const std::array default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}}, @@ -277,6 +277,8 @@ const std::array default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Turbo Speed")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Z"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Slow Speed")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+X"), std::string(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}}, diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 2e5548c775..0c6f6f04d0 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -3,6 +3,7 @@ // Qt on macOS doesn't define VMA shit #include +#include "common/settings.h" #include "common/settings_enums.h" #include "frontend_common/settings_generator.h" #include "qt_common/qt_string_lookup.h" @@ -1406,12 +1407,12 @@ void MainWindow::InitializeHotkeys() { LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true); LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true); LinkActionShortcut(ui->action_View_Lobby, - QStringLiteral("Multiplayer Browse Public Game Lobby")); - LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room")); + QStringLiteral("Browse Public Game Lobby")); + LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Create Room")); LinkActionShortcut(ui->action_Connect_To_Room, - QStringLiteral("Multiplayer Direct Connect to Room")); - LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room")); - LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room")); + QStringLiteral("Direct Connect to Room")); + LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Show Current Room")); + LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Leave Room")); LinkActionShortcut(ui->action_Configure, QStringLiteral("Configure")); LinkActionShortcut(ui->action_Configure_Current_Game, QStringLiteral("Configure Current Game")); @@ -1440,9 +1441,25 @@ void MainWindow::InitializeHotkeys() { connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &MainWindow::OnMute); connect_shortcut(QStringLiteral("Audio Volume Down"), &MainWindow::OnDecreaseVolume); connect_shortcut(QStringLiteral("Audio Volume Up"), &MainWindow::OnIncreaseVolume); - connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { - Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); + + connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [this] { + Settings::ToggleStandardMode(); + const bool limited = Settings::values.use_speed_limit.GetValue(); + m_fpsSuffix = limited ? QString{} : tr("Unlocked"); + }); + + connect_shortcut(QStringLiteral("Toggle Turbo Speed"), [this] { + Settings::ToggleTurboMode(); + const bool turbo = Settings::values.current_speed_mode.GetValue() == Settings::SpeedMode::Turbo; + m_fpsSuffix = turbo ? tr("Turbo") : QString{}; }); + + connect_shortcut(QStringLiteral("Toggle Slow Speed"), [this] { + Settings::ToggleSlowMode(); + const bool slow = Settings::values.current_speed_mode.GetValue() == Settings::SpeedMode::Slow; + m_fpsSuffix = slow ? tr("Slow") : QString{}; + }); + connect_shortcut(QStringLiteral("Toggle Renderdoc Capture"), [] { if (Settings::values.enable_renderdoc_hotkey) { QtCommon::system->GetRenderdocAPI().ToggleCapture(); @@ -4254,14 +4271,15 @@ void MainWindow::UpdateStatusBar() { if (Settings::values.use_speed_limit.GetValue()) { emu_speed_label->setText(tr("Speed: %1% / %2%") .arg(results.emulation_speed * 100.0, 0, 'f', 0) - .arg(Settings::values.speed_limit.GetValue())); + .arg(Settings::SpeedLimit())); } else { emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); } - game_fps_label->setText( - tr("Game: %1 FPS").arg(std::round(results.average_game_fps), 0, 'f', 0) + - tr(Settings::values.use_speed_limit ? "" : " (Unlocked)")); + QString fpsText = tr("Game: %1 FPS").arg(std::round(results.average_game_fps), 0, 'f', 0); + if (!m_fpsSuffix.isEmpty()) fpsText = fpsText % QStringLiteral(" (%1)").arg(m_fpsSuffix); + + game_fps_label->setText(fpsText); emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h index ddabe21ae0..2b2e3108da 100644 --- a/src/yuzu/main_window.h +++ b/src/yuzu/main_window.h @@ -518,6 +518,9 @@ private: QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; + // Stores what suffix to add to the FPS counter, e.g. Unlocked. + QString m_fpsSuffix{}; + UserDataMigrator user_data_migrator; std::unique_ptr config;