mirror of
https://github.com/azahar-emu/azahar.git
synced 2025-04-28 13:47:59 +03:00
Merge branch 'master' into ui-netsettings
This commit is contained in:
commit
e09bf8ee61
49 changed files with 663 additions and 144 deletions
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
|
@ -50,11 +50,11 @@ jobs:
|
|||
- name: Build
|
||||
run: ./.ci/linux.sh
|
||||
- name: Pack
|
||||
if: ${{ matrix.target == 'appimage' }}
|
||||
run: ./.ci/pack.sh
|
||||
if: ${{ matrix.target == 'appimage' }}
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.target == 'appimage' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: artifacts/
|
||||
|
@ -148,19 +148,19 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.target }}-
|
||||
- name: Set up MSVC
|
||||
if: ${{ matrix.target == 'msvc' }}
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
if: ${{ matrix.target == 'msvc' }}
|
||||
- name: Install extra tools (MSVC)
|
||||
run: choco install ccache ninja wget
|
||||
if: ${{ matrix.target == 'msvc' }}
|
||||
run: choco install ccache ninja ptime wget
|
||||
- name: Install vulkan-sdk (MSVC)
|
||||
if: ${{ matrix.target == 'msvc' }}
|
||||
run: |
|
||||
wget https://sdk.lunarg.com/sdk/download/1.3.296.0/windows/VulkanSDK-1.3.296.0-Installer.exe -O D:/a/_temp/vulkan.exe
|
||||
D:/a/_temp/vulkan.exe --accept-licenses --default-answer --confirm-command install
|
||||
if: ${{ matrix.target == 'msvc' }}
|
||||
- name: Set up MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
if: ${{ matrix.target == 'msys2' }}
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: clang64
|
||||
update: true
|
||||
|
@ -168,10 +168,16 @@ jobs:
|
|||
pacboy: >-
|
||||
toolchain:p ccache:p cmake:p ninja:p spirv-tools:p
|
||||
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
|
||||
- name: Install extra tools (MSYS2)
|
||||
if: ${{ matrix.target == 'msys2' }}
|
||||
uses: crazy-max/ghaction-chocolatey@v3
|
||||
with:
|
||||
args: install ptime wget
|
||||
- name: Install NSIS
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
run: |
|
||||
Invoke-WebRequest https://deac-riga.dl.sourceforge.net/project/nsis/NSIS%203/3.10/nsis-3.10-setup.exe?viasf=1 -OutFile C:\WINDOWS\Temp\nsis-3.10-setup.exe
|
||||
Invoke-Expression "& C:\WINDOWS\Temp\nsis-3.10-setup.exe \S"
|
||||
wget https://download.sourceforge.net/project/nsis/NSIS%203/3.11/nsis-3.11-setup.exe -O D:/a/_temp/nsis-setup.exe
|
||||
ptime D:/a/_temp/nsis-setup.exe /S
|
||||
shell: pwsh
|
||||
- name: Disable line ending translation
|
||||
run: git config --global core.autocrlf input
|
||||
|
@ -241,8 +247,8 @@ jobs:
|
|||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: src/android/app/artifacts/
|
||||
ios:
|
||||
runs-on: macos-14
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
runs-on: macos-14
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPILERCHECK: content
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -41,7 +41,8 @@ enum class SmallScreenPosition(val int: Int) {
|
|||
enum class PortraitScreenLayout(val int: Int) {
|
||||
// These must match what is defined in src/common/settings.h
|
||||
TOP_FULL_WIDTH(0),
|
||||
CUSTOM_PORTRAIT_LAYOUT(1);
|
||||
CUSTOM_PORTRAIT_LAYOUT(1),
|
||||
ORIGINAL(2);
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): PortraitScreenLayout {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -140,6 +140,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,
|
||||
|
@ -205,7 +206,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,
|
||||
|
@ -214,6 +216,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"
|
||||
|
|
|
@ -12,7 +12,8 @@ class DateTimeSetting(
|
|||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
val key: String? = null,
|
||||
private val defaultValue: String? = null
|
||||
private val defaultValue: String? = null,
|
||||
override var isEnabled: Boolean = true
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_DATETIME_SETTING
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -28,6 +28,13 @@ abstract class SettingsItem(
|
|||
return setting?.isRuntimeEditable ?: false
|
||||
}
|
||||
|
||||
open var isEnabled: Boolean = true
|
||||
|
||||
val isActive: Boolean
|
||||
get() {
|
||||
return this.isEditable && this.isEnabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_HEADER = 0
|
||||
const val TYPE_SWITCH = 1
|
||||
|
|
|
@ -15,7 +15,8 @@ class SingleChoiceSetting(
|
|||
val choicesId: Int,
|
||||
val valuesId: Int,
|
||||
val key: String? = null,
|
||||
val defaultValue: Int? = null
|
||||
val defaultValue: Int? = null,
|
||||
override var isEnabled: Boolean = true
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_SINGLE_CHOICE
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ class SliderSetting(
|
|||
val max: Int,
|
||||
val units: String,
|
||||
val key: String? = null,
|
||||
val defaultValue: Float? = null
|
||||
val defaultValue: Float? = null,
|
||||
override var isEnabled: Boolean = true
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_SLIDER
|
||||
val selectedFloat: Float
|
||||
|
|
|
@ -14,6 +14,7 @@ class StringInputSetting(
|
|||
val key: String? = null,
|
||||
val defaultValue: String,
|
||||
val characterLimit: Int = 0,
|
||||
override var isEnabled: Boolean = true
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_STRING_INPUT
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ class StringSingleChoiceSetting(
|
|||
val choices: Array<String>,
|
||||
val values: Array<String>?,
|
||||
val key: String? = null,
|
||||
private val defaultValue: String? = null
|
||||
private val defaultValue: String? = null,
|
||||
override var isEnabled: Boolean = true
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_STRING_SINGLE_CHOICE
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ class SwitchSetting(
|
|||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
val key: String? = null,
|
||||
val defaultValue: Any? = null
|
||||
val defaultValue: Any? = null,
|
||||
override var isEnabled: Boolean = true
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_SWITCH
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.citra.citra_emu.features.settings.ui
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.Color
|
||||
import android.icu.util.Calendar
|
||||
import android.icu.util.TimeZone
|
||||
import android.text.Editable
|
||||
|
@ -17,11 +16,11 @@ import android.text.TextWatcher
|
|||
import android.text.format.DateFormat
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
@ -66,7 +65,6 @@ import org.citra.citra_emu.features.settings.ui.viewholder.SwitchSettingViewHold
|
|||
import org.citra.citra_emu.fragments.MessageDialogFragment
|
||||
import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import java.lang.IllegalStateException
|
||||
import java.lang.NumberFormatException
|
||||
import java.text.SimpleDateFormat
|
||||
import kotlin.math.roundToInt
|
||||
|
@ -153,15 +151,71 @@ class SettingsAdapter(
|
|||
return getItem(position)?.type ?: -1
|
||||
}
|
||||
|
||||
fun setSettingsList(settings: ArrayList<SettingsItem>?) {
|
||||
this.settings = settings ?: arrayListOf()
|
||||
notifyDataSetChanged()
|
||||
fun setSettingsList(newSettings: ArrayList<SettingsItem>?) {
|
||||
if (settings == null) {
|
||||
settings = newSettings ?: arrayListOf()
|
||||
notifyDataSetChanged()
|
||||
return
|
||||
}
|
||||
|
||||
val oldSettings = settings
|
||||
val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
|
||||
override fun getOldListSize() = oldSettings?.size ?: 0
|
||||
override fun getNewListSize() = newSettings?.size ?: 0
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = oldSettings?.get(oldItemPosition)?.setting
|
||||
val newItem = newSettings?.get(newItemPosition)?.setting
|
||||
return oldItem?.key == newItem?.key
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = oldSettings?.get(oldItemPosition)
|
||||
val newItem = newSettings?.get(newItemPosition)
|
||||
|
||||
if (oldItem == null || newItem == null || oldItem.type != newItem.type) {
|
||||
return false
|
||||
}
|
||||
|
||||
return when (oldItem.type) {
|
||||
SettingsItem.TYPE_SLIDER -> {
|
||||
(oldItem as SliderSetting).isEnabled == (newItem as SliderSetting).isEnabled
|
||||
}
|
||||
SettingsItem.TYPE_SWITCH -> {
|
||||
(oldItem as SwitchSetting).isEnabled == (newItem as SwitchSetting).isEnabled
|
||||
}
|
||||
SettingsItem.TYPE_SINGLE_CHOICE -> {
|
||||
(oldItem as SingleChoiceSetting).isEnabled == (newItem as SingleChoiceSetting).isEnabled
|
||||
}
|
||||
SettingsItem.TYPE_DATETIME_SETTING -> {
|
||||
(oldItem as DateTimeSetting).isEnabled == (newItem as DateTimeSetting).isEnabled
|
||||
}
|
||||
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
|
||||
(oldItem as StringSingleChoiceSetting).isEnabled == (newItem as StringSingleChoiceSetting).isEnabled
|
||||
}
|
||||
SettingsItem.TYPE_STRING_INPUT -> {
|
||||
(oldItem as StringInputSetting).isEnabled == (newItem as StringInputSetting).isEnabled
|
||||
}
|
||||
else -> {
|
||||
oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
settings = newSettings ?: arrayListOf()
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
|
||||
val setting = item.setChecked(checked)
|
||||
fragmentView.putSetting(setting)
|
||||
fragmentView.onSettingChanged()
|
||||
|
||||
// If statement is required otherwise the app will crash on activity recreate ex. theme settings
|
||||
if (fragmentView.activityView != null)
|
||||
// Reload the settings list to update the UI
|
||||
fragmentView.loadSettingsList()
|
||||
}
|
||||
|
||||
private fun onSingleChoiceClick(item: SingleChoiceSetting) {
|
||||
|
@ -247,6 +301,7 @@ class SettingsAdapter(
|
|||
notifyItemChanged(clickedPosition)
|
||||
val setting = item.setSelectedValue(rtcString)
|
||||
fragmentView.putSetting(setting)
|
||||
fragmentView.loadSettingsList()
|
||||
clickedItem = null
|
||||
}
|
||||
datePicker.show(
|
||||
|
@ -402,6 +457,7 @@ class SettingsAdapter(
|
|||
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
|
||||
}
|
||||
fragmentView?.putSetting(setting)
|
||||
fragmentView.loadSettingsList()
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
|
@ -425,6 +481,7 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
fragmentView?.putSetting(setting)
|
||||
fragmentView.loadSettingsList()
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
|
@ -447,6 +504,7 @@ class SettingsAdapter(
|
|||
fragmentView?.putSetting(setting)
|
||||
}
|
||||
}
|
||||
fragmentView.loadSettingsList()
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
|
@ -459,6 +517,7 @@ class SettingsAdapter(
|
|||
}
|
||||
val setting = it.setSelectedValue(textInputValue ?: "")
|
||||
fragmentView?.putSetting(setting)
|
||||
fragmentView.loadSettingsList()
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
|
@ -488,6 +547,7 @@ class SettingsAdapter(
|
|||
}
|
||||
notifyItemChanged(position)
|
||||
fragmentView.onSettingChanged()
|
||||
fragmentView.loadSettingsList()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
|
@ -495,10 +555,19 @@ class SettingsAdapter(
|
|||
return true
|
||||
}
|
||||
|
||||
fun onClickDisabledSetting() {
|
||||
MessageDialogFragment.newInstance(
|
||||
R.string.setting_not_editable,
|
||||
fun onClickDisabledSetting(isRuntimeDisabled: Boolean) {
|
||||
val titleId = if (isRuntimeDisabled)
|
||||
R.string.setting_not_editable
|
||||
else
|
||||
R.string.setting_disabled
|
||||
val messageId = if (isRuntimeDisabled)
|
||||
R.string.setting_not_editable_description
|
||||
else
|
||||
R.string.setting_disabled_description
|
||||
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId,
|
||||
messageId
|
||||
).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
|
||||
|
|
|
@ -247,6 +247,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()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -47,7 +47,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
|
||||
binding.textSettingValue.text = dateFormatter.format(zonedTime)
|
||||
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
binding.textSettingName.alpha = 1f
|
||||
binding.textSettingDescription.alpha = 1f
|
||||
binding.textSettingValue.alpha = 1f
|
||||
|
@ -59,18 +59,18 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
adapter.onDateTimeClick(setting, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -45,7 +45,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
|
|||
if (setting.isEditable) {
|
||||
adapter.onInputBindingClick(setting, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
|
|||
if (setting.isEditable) {
|
||||
adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -60,7 +60,7 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||
|
||||
override fun onClick(clicked: View) {
|
||||
if (!setting.isRuntimeRunnable && EmulationActivity.isRunning()) {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(true)
|
||||
} else {
|
||||
setting.runnable.invoke()
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -27,7 +27,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
|||
binding.textSettingValue.visibility = View.VISIBLE
|
||||
binding.textSettingValue.text = getTextSetting()
|
||||
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
binding.textSettingName.alpha = 1f
|
||||
binding.textSettingDescription.alpha = 1f
|
||||
binding.textSettingValue.alpha = 1f
|
||||
|
@ -65,8 +65,8 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
|||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (!setting.isEditable) {
|
||||
adapter.onClickDisabledSetting()
|
||||
if (!setting.isEditable || !setting.isEnabled) {
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -84,10 +84,10 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
|||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -35,7 +35,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
|
|||
else -> "${(setting.setting as AbstractIntSetting).int}${setting.units}"
|
||||
}
|
||||
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
binding.textSettingName.alpha = 1f
|
||||
binding.textSettingDescription.alpha = 1f
|
||||
binding.textSettingValue.alpha = 1f
|
||||
|
@ -47,18 +47,18 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
|
|||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
adapter.onSliderClick(setting, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -25,21 +25,31 @@ class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: Settin
|
|||
}
|
||||
binding.textSettingValue.visibility = View.VISIBLE
|
||||
binding.textSettingValue.text = setting.setting?.valueAsString
|
||||
|
||||
if (setting.isActive) {
|
||||
binding.textSettingName.alpha = 1f
|
||||
binding.textSettingDescription.alpha = 1f
|
||||
binding.textSettingValue.alpha = 1f
|
||||
} else {
|
||||
binding.textSettingName.alpha = 0.5f
|
||||
binding.textSettingDescription.alpha = 0.5f
|
||||
binding.textSettingValue.alpha = 0.5f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (!setting.isEditable) {
|
||||
adapter.onClickDisabledSetting()
|
||||
if (!setting.isEditable || !setting.isEnabled) {
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
return
|
||||
}
|
||||
adapter.onStringInputClick((setting as StringInputSetting), bindingAdapterPosition)
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -33,26 +33,26 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
|
|||
adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
|
||||
}
|
||||
|
||||
binding.switchWidget.isEnabled = setting.isEditable
|
||||
binding.switchWidget.isEnabled = setting.isActive
|
||||
|
||||
val textAlpha = if (setting.isEditable) 1f else 0.5f
|
||||
val textAlpha = if (setting.isActive) 1f else 0.5f
|
||||
binding.textSettingName.alpha = textAlpha
|
||||
binding.textSettingDescription.alpha = textAlpha
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
binding.switchWidget.toggle()
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
if (setting.isActive) {
|
||||
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting()
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -455,6 +455,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
Choreographer.getInstance().postFrameCallback(this)
|
||||
if (NativeLibrary.isRunning()) {
|
||||
NativeLibrary.unPauseEmulation()
|
||||
binding.inGameMenu.menu.findItem(R.id.menu_emulation_pause)?.let { menuItem ->
|
||||
menuItem.title = resources.getString(R.string.pause_emulation)
|
||||
menuItem.icon = ResourcesCompat.getDrawable(
|
||||
resources,
|
||||
R.drawable.ic_pause,
|
||||
requireContext().theme
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -894,10 +902,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
val layoutOptionMenuItem = when (IntSetting.PORTRAIT_SCREEN_LAYOUT.int) {
|
||||
PortraitScreenLayout.TOP_FULL_WIDTH.int ->
|
||||
R.id.menu_portrait_layout_top_full
|
||||
|
||||
PortraitScreenLayout.ORIGINAL.int ->
|
||||
R.id.menu_portrait_layout_original
|
||||
PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int ->
|
||||
R.id.menu_portrait_layout_custom
|
||||
|
||||
else ->
|
||||
R.id.menu_portrait_layout_top_full
|
||||
|
||||
|
@ -912,6 +920,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
true
|
||||
}
|
||||
|
||||
R.id.menu_portrait_layout_original -> {
|
||||
screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.ORIGINAL.int)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_portrait_layout_custom -> {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
|
|
|
@ -148,8 +148,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 {
|
||||
|
|
|
@ -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<jint>(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();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
android:id="@+id/menu_portrait_layout_top_full"
|
||||
android:title="@string/emulation_portrait_layout_top_full" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_portrait_layout_original"
|
||||
android:title="@string/emulation_screen_layout_original" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_portrait_layout_custom"
|
||||
android:title="@string/emulation_screen_layout_custom" />
|
||||
|
|
|
@ -31,11 +31,13 @@
|
|||
|
||||
<string-array name="portraitLayouts">
|
||||
<item>@string/emulation_portrait_layout_top_full</item>
|
||||
<item>@string/emulation_screen_layout_original</item>
|
||||
<item>@string/emulation_screen_layout_custom</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="portraitLayoutValues">
|
||||
<item>0</item>
|
||||
<item>2</item>
|
||||
<item>1</item>
|
||||
</integer-array>
|
||||
|
||||
|
@ -309,6 +311,7 @@
|
|||
</integer-array>
|
||||
|
||||
<string-array name="resolutionFactorNames">
|
||||
<item>@string/internal_resolution_setting_auto</item>
|
||||
<item>@string/internal_resolution_setting_1x</item>
|
||||
<item>@string/internal_resolution_setting_2x</item>
|
||||
<item>@string/internal_resolution_setting_3x</item>
|
||||
|
@ -321,6 +324,7 @@
|
|||
<item>@string/internal_resolution_setting_10x</item>
|
||||
</string-array>
|
||||
<integer-array name="resolutionFactorValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
|
|
|
@ -251,13 +251,17 @@
|
|||
<string name="asynchronous_gpu">Enable asynchronous GPU emulation</string>
|
||||
<string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string>
|
||||
<string name="frame_limit_enable">Limit Speed</string>
|
||||
<string name="expand_to_cutout_area">Expand to Cutout Area</string>
|
||||
<string name="expand_to_cutout_area_description">Expands the display area to include the cutout (or notch) area.</string>
|
||||
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed.</string>
|
||||
<string name="frame_limit_enable_description">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.</string>
|
||||
<string name="frame_limit_slider">Limit Speed Percent</string>
|
||||
<string name="frame_limit_slider_description">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.</string>
|
||||
<string name="expand_to_cutout_area">Expand to Cutout Area</string>
|
||||
<string name="expand_to_cutout_area_description">Expands the display area to include the cutout (or notch) area.</string>
|
||||
<string name="emulation_toggle_turbo_speed">Turbo Speed</string>
|
||||
<string name="turbo_speed">Turbo Speed</string>
|
||||
<string name="turbo_speed_description">Emulation speed limit used while the turbo hotkey is active.</string>
|
||||
<string name="internal_resolution">Internal Resolution</string>
|
||||
<string name="internal_resolution_description">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.</string>
|
||||
<string name="internal_resolution_setting_auto">Auto (Screen Size)</string>
|
||||
<string name="internal_resolution_setting_1x">Native (400x240)</string>
|
||||
<string name="internal_resolution_setting_2x">2x Native (800x480)</string>
|
||||
<string name="internal_resolution_setting_3x">3x Native (1200x720)</string>
|
||||
|
@ -337,6 +341,8 @@
|
|||
<string name="select_rtc_time">Select RTC time</string>
|
||||
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
|
||||
<string name="setting_not_editable">You can\'t edit this now</string>
|
||||
<string name="setting_disabled">Setting disabled</string>
|
||||
<string name="setting_disabled_description">This setting is currently disabled due to another setting not being the appropriate value.</string>
|
||||
<string name="setting_not_editable_description">This option can\'t be changed while a game is running.</string>
|
||||
<string name="auto_select">Auto-Select</string>
|
||||
|
||||
|
|
|
@ -697,25 +697,36 @@ void GMainWindow::InitializeRecentFileMenuActions() {
|
|||
void GMainWindow::InitializeSaveStateMenuActions() {
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i] = new QAction(this);
|
||||
actions_load_state[i]->setData(i + 1);
|
||||
actions_load_state[i]->setData(i);
|
||||
connect(actions_load_state[i], &QAction::triggered, this, &GMainWindow::OnLoadState);
|
||||
ui->menu_Load_State->addAction(actions_load_state[i]);
|
||||
|
||||
if (i > 0)
|
||||
ui->menu_Load_State->addAction(actions_load_state[i]);
|
||||
actions_save_state[i] = new QAction(this);
|
||||
actions_save_state[i]->setData(i + 1);
|
||||
actions_save_state[i]->setData(i);
|
||||
connect(actions_save_state[i], &QAction::triggered, this, &GMainWindow::OnSaveState);
|
||||
ui->menu_Save_State->addAction(actions_save_state[i]);
|
||||
if (i > 0)
|
||||
ui->menu_Save_State->addAction(actions_save_state[i]);
|
||||
}
|
||||
|
||||
connect(ui->action_Load_from_Newest_Slot, &QAction::triggered, this, [this] {
|
||||
UpdateSaveStates();
|
||||
if (newest_slot != 0) {
|
||||
actions_load_state[newest_slot - 1]->trigger();
|
||||
actions_load_state[newest_slot]->trigger();
|
||||
}
|
||||
});
|
||||
connect(ui->action_Save_to_Oldest_Slot, &QAction::triggered, this, [this] {
|
||||
UpdateSaveStates();
|
||||
actions_save_state[oldest_slot - 1]->trigger();
|
||||
actions_save_state[oldest_slot]->trigger();
|
||||
});
|
||||
|
||||
// Quick save / load uses slot
|
||||
connect(ui->action_Quick_Save, &QAction::triggered, this, [this] {
|
||||
UpdateSaveStates();
|
||||
actions_save_state[0]->trigger();
|
||||
});
|
||||
connect(ui->action_Quick_Load, &QAction::triggered, this, [this] {
|
||||
UpdateSaveStates();
|
||||
actions_load_state[0]->trigger();
|
||||
});
|
||||
|
||||
connect(ui->menu_Load_State->menuAction(), &QAction::hovered, this,
|
||||
|
@ -758,8 +769,12 @@ void GMainWindow::InitializeHotkeys() {
|
|||
link_action_shortcut(ui->action_Screen_Layout_Upright_Screens,
|
||||
QStringLiteral("Rotate Screens Upright"));
|
||||
link_action_shortcut(ui->action_Advance_Frame, QStringLiteral("Advance Frame"));
|
||||
link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot"));
|
||||
link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot"));
|
||||
link_action_shortcut(ui->action_Load_from_Newest_Slot,
|
||||
QStringLiteral("Load from Newest Non-Quicksave Slot"));
|
||||
link_action_shortcut(ui->action_Save_to_Oldest_Slot,
|
||||
QStringLiteral("Save to Oldest Non-Quicksave Slot"));
|
||||
link_action_shortcut(ui->action_Quick_Save, QStringLiteral("Quick Save"));
|
||||
link_action_shortcut(ui->action_Quick_Load, QStringLiteral("Quick Load"));
|
||||
link_action_shortcut(ui->action_View_Lobby, QStringLiteral("Multiplayer Browse Public Rooms"));
|
||||
link_action_shortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
|
||||
link_action_shortcut(ui->action_Connect_To_Room,
|
||||
|
@ -784,6 +799,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();
|
||||
});
|
||||
|
@ -791,31 +811,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);
|
||||
|
@ -1592,7 +1593,7 @@ void GMainWindow::UpdateSaveStates() {
|
|||
ui->menu_Save_State->setEnabled(true);
|
||||
ui->action_Load_from_Newest_Slot->setEnabled(false);
|
||||
|
||||
oldest_slot = newest_slot = 0;
|
||||
oldest_slot = newest_slot = 1;
|
||||
oldest_slot_time = std::numeric_limits<u64>::max();
|
||||
newest_slot_time = 0;
|
||||
|
||||
|
@ -1603,13 +1604,30 @@ void GMainWindow::UpdateSaveStates() {
|
|||
auto savestates = Core::ListSaveStates(title_id, movie.GetCurrentMovieID());
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i]->setEnabled(false);
|
||||
actions_load_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
if (i == 0) {
|
||||
actions_load_state[i]->setText(tr("Quick Load"));
|
||||
actions_save_state[i]->setText(tr("Quick Save"));
|
||||
} else {
|
||||
actions_load_state[i]->setText(tr("Slot %1").arg(i));
|
||||
actions_save_state[i]->setText(tr("Slot %1").arg(i));
|
||||
}
|
||||
}
|
||||
for (const auto& savestate : savestates) {
|
||||
const bool display_name =
|
||||
savestate.status == Core::SaveStateInfo::ValidationStatus::RevisionDismatch &&
|
||||
!savestate.build_name.empty();
|
||||
actions_load_state[savestate.slot]->setEnabled(true);
|
||||
if (savestate.slot == 0) {
|
||||
const auto text = tr("%2 %3")
|
||||
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
|
||||
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")))
|
||||
.arg(display_name ? QString::fromStdString(savestate.build_name)
|
||||
: QLatin1String())
|
||||
.trimmed();
|
||||
ui->action_Quick_Save->setText(tr("Quick Save - %1").arg(text).trimmed());
|
||||
ui->action_Quick_Load->setText(tr("Quick Load - %1").arg(text).trimmed());
|
||||
continue;
|
||||
}
|
||||
const auto text =
|
||||
tr("Slot %1 - %2 %3")
|
||||
.arg(savestate.slot)
|
||||
|
@ -1618,12 +1636,10 @@ void GMainWindow::UpdateSaveStates() {
|
|||
.arg(display_name ? QString::fromStdString(savestate.build_name) : QLatin1String())
|
||||
.trimmed();
|
||||
|
||||
actions_load_state[savestate.slot - 1]->setEnabled(true);
|
||||
actions_load_state[savestate.slot - 1]->setText(text);
|
||||
actions_save_state[savestate.slot - 1]->setText(text);
|
||||
actions_load_state[savestate.slot]->setText(text);
|
||||
actions_save_state[savestate.slot]->setText(text);
|
||||
|
||||
ui->action_Load_from_Newest_Slot->setEnabled(true);
|
||||
|
||||
if (savestate.time > newest_slot_time) {
|
||||
newest_slot = savestate.slot;
|
||||
newest_slot_time = savestate.time;
|
||||
|
@ -1633,7 +1649,8 @@ void GMainWindow::UpdateSaveStates() {
|
|||
oldest_slot_time = savestate.time;
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
// Value as 1 because quicksave slot is not used for this calculation
|
||||
for (u32 i = 1; i < Core::SaveStateSlotCount; ++i) {
|
||||
if (!actions_load_state[i]->isEnabled()) {
|
||||
// Prefer empty slot
|
||||
oldest_slot = i + 1;
|
||||
|
@ -2362,6 +2379,7 @@ void GMainWindow::OnMenuRecentFile() {
|
|||
}
|
||||
|
||||
void GMainWindow::OnStartGame() {
|
||||
GetInitialFrameLimit();
|
||||
qt_cameras->ResumeCameras();
|
||||
|
||||
PreventOSSleep();
|
||||
|
@ -2421,6 +2439,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);
|
||||
|
@ -2572,6 +2596,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()) {
|
||||
|
|
|
@ -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<QtConfig> config;
|
||||
|
||||
// Hotkeys
|
||||
bool turbo_mode_active = false;
|
||||
|
||||
// Whether emulation is currently running in Citra.
|
||||
bool emulation_running = false;
|
||||
std::unique_ptr<EmuThread> 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;
|
||||
|
|
|
@ -53,7 +53,7 @@ const std::array<std::array<int, 5>, 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<UISettings::Shortcut, 35> QtConfig::default_hotkeys {{
|
||||
const std::array<UISettings::Shortcut, 38> 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}},
|
||||
|
@ -69,23 +69,26 @@ const std::array<UISettings::Shortcut, 35> QtConfig::default_hotkeys {{
|
|||
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load from Newest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Multiplayer Browse Public Lobby"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Save to Oldest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
|
||||
{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}},
|
||||
{QStringLiteral("Toggle Per-Application Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Per-Application Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
|
@ -806,6 +809,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);
|
||||
|
@ -1310,6 +1314,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);
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||
static const std::array<UISettings::Shortcut, 35> default_hotkeys;
|
||||
static const std::array<UISettings::Shortcut, 38> default_hotkeys;
|
||||
|
||||
private:
|
||||
void Initialize(const std::string& config_name);
|
||||
|
|
|
@ -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<Ui::ConfigureDialog>()}, registry{registry_},
|
||||
system{system_}, is_powered_on{system.IsPoweredOn()},
|
||||
general_tab{std::make_unique<ConfigureGeneral>(this)},
|
||||
general_tab{std::make_unique<ConfigureGeneral>(is_powered_on, this)},
|
||||
system_tab{std::make_unique<ConfigureSystem>(system, this)},
|
||||
input_tab{std::make_unique<ConfigureInput>(system, this)},
|
||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
||||
|
|
|
@ -24,11 +24,16 @@ static constexpr int SettingsToSlider(int value) {
|
|||
return (value - 5) / 5;
|
||||
}
|
||||
|
||||
ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGeneral>()) {
|
||||
|
||||
ConfigureGeneral::ConfigureGeneral(bool is_powered_on, QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGeneral>()), 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<int>(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);
|
||||
|
|
|
@ -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::ConfigureGeneral> ui;
|
||||
bool is_powered_on;
|
||||
};
|
||||
|
|
|
@ -147,6 +147,82 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="turbo_speed_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="turbo_speed_layout_inner">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="turbo_speed_label">
|
||||
<property name="text">
|
||||
<string>Turbo Speed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="turbo_speed">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>198</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>19</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="turbo_speed_display_label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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 <utility>
|
||||
#include <vector>
|
||||
|
@ -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<ConfigureAudio>(is_powered_on, this);
|
||||
general_tab = std::make_unique<ConfigureGeneral>(this);
|
||||
general_tab = std::make_unique<ConfigureGeneral>(is_powered_on, this);
|
||||
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
|
||||
layout_tab = std::make_unique<ConfigureLayout>(this);
|
||||
graphics_tab =
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
<string>Save State</string>
|
||||
</property>
|
||||
<addaction name="action_Save_to_Oldest_Slot"/>
|
||||
<addaction name="action_Quick_Save"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Load_State">
|
||||
|
@ -107,6 +108,7 @@
|
|||
<string>Load State</string>
|
||||
</property>
|
||||
<addaction name="action_Load_from_Newest_Slot"/>
|
||||
<addaction name="action_Quick_Load"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="action_Pause"/>
|
||||
|
@ -343,11 +345,21 @@
|
|||
<string>Save to Oldest Slot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Quick_Save">
|
||||
<property name="text">
|
||||
<string>Quick Save</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_from_Newest_Slot">
|
||||
<property name="text">
|
||||
<string>Load from Newest Slot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Quick_Load">
|
||||
<property name="text">
|
||||
<string>Quick Load</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure">
|
||||
<property name="text">
|
||||
<string>Configure...</string>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
// Copyright Dolphin Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
// Copyright Dolphin Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ enum class PortraitLayoutOption : u32 {
|
|||
// formerly mobile portrait
|
||||
PortraitTopFullWidth,
|
||||
PortraitCustomLayout,
|
||||
PortraitOriginal
|
||||
};
|
||||
|
||||
/** Defines where the small screen will appear relative to the large screen
|
||||
|
@ -497,6 +498,7 @@ struct Values {
|
|||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||
SwitchableSetting<double, true> frame_limit{100, 0, 1000, "frame_limit"};
|
||||
SwitchableSetting<int, true> turbo_speed{200, 0, 1000, "turbo_speed"};
|
||||
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::NoFilter, "texture_filter"};
|
||||
SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled,
|
||||
"texture_sampling"};
|
||||
|
|
|
@ -221,6 +221,10 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
|||
layout = Layout::CustomFrameLayout(
|
||||
width, height, Settings::values.swap_screen.GetValue(), is_portrait_mode);
|
||||
break;
|
||||
case Settings::PortraitLayoutOption::PortraitOriginal:
|
||||
layout = Layout::PortraitOriginalLayout(width, height,
|
||||
Settings::values.swap_screen.GetValue());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (layout_option) {
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -51,6 +51,18 @@ FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool swapped
|
|||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout PortraitOriginalLayout(u32 width, u32 height, bool swapped) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
const float scale_factor = 1;
|
||||
FramebufferLayout res = LargeFrameLayout(width, height, swapped, false, scale_factor,
|
||||
Settings::SmallScreenPosition::BelowLarge);
|
||||
const int shiftY = -(int)(swapped ? res.bottom_screen.top : res.top_screen.top);
|
||||
res.top_screen = res.top_screen.TranslateY(shiftY);
|
||||
res.bottom_screen = res.bottom_screen.TranslateY(shiftY);
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
@ -346,7 +358,7 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool
|
|||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary,
|
||||
bool is_portrait) {
|
||||
int width, height;
|
||||
u32 width, height;
|
||||
if (is_portrait) {
|
||||
auto layout_option = Settings::values.portrait_layout_option.GetValue();
|
||||
switch (layout_option) {
|
||||
|
@ -363,9 +375,14 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar
|
|||
Settings::values.swap_screen.GetValue(), is_portrait);
|
||||
case Settings::PortraitLayoutOption::PortraitTopFullWidth:
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
height = static_cast<int>(Core::kScreenTopHeight + Core::kScreenBottomHeight * 1.25) *
|
||||
res_scale;
|
||||
return PortraitTopFullFrameLayout(width, height,
|
||||
Settings::values.swap_screen.GetValue());
|
||||
case Settings::PortraitLayoutOption::PortraitOriginal:
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
return PortraitOriginalLayout(width, height, Settings::values.swap_screen.GetValue());
|
||||
}
|
||||
} else {
|
||||
auto layout_option = Settings::values.layout_option.GetValue();
|
||||
|
@ -443,6 +460,24 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar
|
|||
Settings::values.upright_screen.GetValue(), 1,
|
||||
Settings::SmallScreenPosition::MiddleRight);
|
||||
|
||||
case Settings::LayoutOption::HybridScreen:
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width = Core::kScreenBottomWidth;
|
||||
} else {
|
||||
width = Core::kScreenTopWidth;
|
||||
}
|
||||
// 2.25f comes from HybridScreenLayout's scale_factor value.
|
||||
width = static_cast<int>((width + (Core::kScreenTopWidth / 2.25f)) * res_scale);
|
||||
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
|
||||
return HybridScreenLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue());
|
||||
|
||||
case Settings::LayoutOption::Default:
|
||||
default:
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
|
@ -479,6 +514,7 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) {
|
|||
if (is_portrait) {
|
||||
switch (Settings::values.portrait_layout_option.GetValue()) {
|
||||
case Settings::PortraitLayoutOption::PortraitTopFullWidth:
|
||||
case Settings::PortraitLayoutOption::PortraitOriginal:
|
||||
cardboard_screen_width = top_screen_width;
|
||||
cardboard_screen_height = top_screen_height + bottom_screen_height;
|
||||
bottom_screen_left += (top_screen_width - bottom_screen_width) / 2;
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -62,8 +62,8 @@ FramebufferLayout reverseLayout(FramebufferLayout layout);
|
|||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
|
||||
|
||||
/**
|
||||
* Factory method for constructing the mobile Full Width Top layout
|
||||
* Two screens at top, full width, no gap between them
|
||||
* Factory method for constructing the mobile Full Width (Default) layout
|
||||
* Two screens at top, full width (so different heights)
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be displayed above the top screen
|
||||
|
@ -71,6 +71,16 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped, boo
|
|||
*/
|
||||
FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool is_swapped);
|
||||
|
||||
/**
|
||||
* Factory method for constructing the mobile Original layout
|
||||
* Two screens at top, equal heights
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be displayed above the top screen
|
||||
* @return Newly created FramebufferLayout object with mobile portrait screen regions initialized
|
||||
*/
|
||||
FramebufferLayout PortraitOriginalLayout(u32 width, u32 height, bool is_swapped);
|
||||
|
||||
/**
|
||||
* Factory method for constructing a FramebufferLayout with only the top or bottom screen
|
||||
* @param width Window framebuffer width in pixels
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue