yuzu-emu
/
yuzu-android
Archived
1
0
Fork 0

Merge pull request #12518 from t895/theme-settings

android: Migrate remaining settings to ini
This commit is contained in:
liamwhite 2024-01-01 15:02:06 -05:00 committed by GitHub
commit c3c676b7d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1547 additions and 1046 deletions

View File

@ -10,7 +10,7 @@ plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.8.21"
kotlin("plugin.serialization") version "1.9.20"
id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
}

View File

@ -49,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
import java.text.NumberFormat
@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener()
}
override fun onStop() {
super.onStop()
NativeConfig.saveGlobalConfig()
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {

View File

@ -18,7 +18,14 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
RENDERER_DEBUG("debug"),
PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled");
USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"),
JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"),
HAPTIC_FEEDBACK("haptic_feedback"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
SHOW_INPUT_OVERLAY("show_input_overlay"),
TOUCHSCREEN("touchscreen");
override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal)

View File

@ -19,7 +19,11 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_SCREEN_LAYOUT("screen_layout"),
RENDERER_ASPECT_RATIO("aspect_ratio"),
AUDIO_OUTPUT_ENGINE("output_engine"),
MAX_ANISOTROPY("max_anisotropy");
MAX_ANISOTROPY("max_anisotropy"),
THEME("theme"),
THEME_MODE("theme_mode"),
OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View File

@ -15,18 +15,10 @@ object Settings {
SECTION_DEBUG(R.string.preferences_debug);
}
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val PREF_OVERLAY_VERSION = "OverlayVersion"
const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
val overlayLayoutPrefs = listOf(
PREF_LANDSCAPE_OVERLAY_VERSION,
PREF_PORTRAIT_OVERLAY_VERSION,
PREF_FOLDABLE_OVERLAY_VERSION
)
// Deprecated input overlay preference keys
const val PREF_CONTROL_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity"
const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@ -47,23 +39,12 @@ object Settings {
const val PREF_BUTTON_STICK_R = "buttonToggle14"
const val PREF_BUTTON_HOME = "buttonToggle15"
const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_THEME = "Theme"
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
val overlayPreferences = listOf(
PREF_OVERLAY_VERSION,
PREF_CONTROL_SCALE,
PREF_CONTROL_OPACITY,
PREF_TOUCH_ENABLED,
PREF_BUTTON_A,
PREF_BUTTON_B,
PREF_BUTTON_X,
@ -83,6 +64,21 @@ object Settings {
PREF_BUTTON_STICK_R
)
// Deprecated layout preference keys
const val PREF_LANDSCAPE_SUFFIX = "_Landscape"
const val PREF_PORTRAIT_SUFFIX = "_Portrait"
const val PREF_FOLDABLE_SUFFIX = "_Foldable"
val overlayLayoutSuffixes = listOf(
PREF_LANDSCAPE_SUFFIX,
PREF_PORTRAIT_SUFFIX,
PREF_FOLDABLE_SUFFIX
)
// Deprecated theme preference keys
const val PREF_THEME = "Theme"
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
const val LayoutOption_Unspecified = 0
const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5

View File

@ -3,10 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.SharedPreferences
import android.os.Build
import android.widget.Toast
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@ -29,9 +27,6 @@ class SettingsFragmentPresenter(
) {
private var settingsList = ArrayList<SettingsItem>()
private val preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
// Extension for altering settings list based on each setting's properties
fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!!
@ -170,25 +165,19 @@ class SettingsFragmentPresenter(
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
val theme: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
preferences.getInt(Settings.PREF_THEME, 0)
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME.getInt()
override fun setInt(value: Int) {
preferences.edit()
.putInt(Settings.PREF_THEME, value)
.apply()
IntSetting.THEME.setInt(value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_THEME
override val isRuntimeModifiable: Boolean = false
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
override val defaultValue: Int = 0
override fun reset() {
preferences.edit()
.putInt(Settings.PREF_THEME, defaultValue)
.apply()
}
override val key: String = IntSetting.THEME.key
override val isRuntimeModifiable: Boolean = IntSetting.THEME.isRuntimeModifiable
override fun getValueAsString(needsGlobal: Boolean): String =
IntSetting.THEME.getValueAsString()
override val defaultValue: Int = IntSetting.THEME.defaultValue
override fun reset() = IntSetting.THEME.setInt(defaultValue)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@ -214,24 +203,22 @@ class SettingsFragmentPresenter(
}
val themeMode: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
preferences.getInt(Settings.PREF_THEME_MODE, -1)
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
override fun setInt(value: Int) {
preferences.edit()
.putInt(Settings.PREF_THEME_MODE, value)
.apply()
IntSetting.THEME_MODE.setInt(value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_THEME_MODE
override val isRuntimeModifiable: Boolean = false
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
override val defaultValue: Int = -1
override val key: String = IntSetting.THEME_MODE.key
override val isRuntimeModifiable: Boolean =
IntSetting.THEME_MODE.isRuntimeModifiable
override fun getValueAsString(needsGlobal: Boolean): String =
IntSetting.THEME_MODE.getValueAsString()
override val defaultValue: Int = IntSetting.THEME_MODE.defaultValue
override fun reset() {
preferences.edit()
.putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
.apply()
IntSetting.THEME_MODE.setInt(defaultValue)
settingsViewModel.setShouldRecreate(true)
}
}
@ -248,25 +235,24 @@ class SettingsFragmentPresenter(
val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean =
preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
BooleanSetting.BLACK_BACKGROUNDS.getBoolean()
override fun setBoolean(value: Boolean) {
preferences.edit()
.putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
.apply()
BooleanSetting.BLACK_BACKGROUNDS.setBoolean(value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_BLACK_BACKGROUNDS
override val isRuntimeModifiable: Boolean = false
override fun getValueAsString(needsGlobal: Boolean): String =
getBoolean().toString()
override val key: String = BooleanSetting.BLACK_BACKGROUNDS.key
override val isRuntimeModifiable: Boolean =
BooleanSetting.BLACK_BACKGROUNDS.isRuntimeModifiable
override val defaultValue: Boolean = false
override fun getValueAsString(needsGlobal: Boolean): String =
BooleanSetting.BLACK_BACKGROUNDS.getValueAsString()
override val defaultValue: Boolean = BooleanSetting.BLACK_BACKGROUNDS.defaultValue
override fun reset() {
preferences.edit()
.putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
.apply()
BooleanSetting.BLACK_BACKGROUNDS
.setBoolean(BooleanSetting.BLACK_BACKGROUNDS.defaultValue)
settingsViewModel.setShouldRecreate(true)
}
}

View File

@ -7,7 +7,6 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.net.Uri
@ -33,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
@ -46,22 +44,22 @@ import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.*
import java.lang.NullPointerException
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var preferences: SharedPreferences
private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null
@ -141,7 +139,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
emulationState = EmulationState(game.path)
}
@ -382,24 +379,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
updateScreenLayout()
val showInputOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
if (emulationActivity?.isInPictureInPictureMode == true) {
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
}
if (EmulationMenuSettings.showOverlay) {
if (showInputOverlay) {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
} else {
if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
if (showInputOverlay && emulationViewModel.emulationStarted.value) {
binding.surfaceInputOverlay.visibility = View.VISIBLE
} else {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
} else {
binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
binding.surfaceInputOverlay.layout = OverlayLayout.Landscape
}
}
}
@ -423,17 +421,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun resetInputOverlay() {
preferences.edit()
.remove(Settings.PREF_CONTROL_SCALE)
.remove(Settings.PREF_CONTROL_OPACITY)
.apply()
IntSetting.OVERLAY_SCALE.reset()
IntSetting.OVERLAY_OPACITY.reset()
binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
}
}
private fun updateShowFpsOverlay() {
if (EmulationMenuSettings.showFps) {
if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
val SYSTEM_FPS = 0
val FPS = 1
val FRAMETIME = 2
@ -496,7 +492,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.inGameMenu.layoutParams.height = it.bounds.bottom
isInFoldableLayout = true
binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
binding.surfaceInputOverlay.layout = OverlayLayout.Foldable
}
}
it.isSeparating
@ -535,18 +531,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
popup.menu.apply {
findItem(R.id.menu_toggle_fps).isChecked = EmulationMenuSettings.showFps
findItem(R.id.menu_rel_stick_center).isChecked = EmulationMenuSettings.joystickRelCenter
findItem(R.id.menu_dpad_slide).isChecked = EmulationMenuSettings.dpadSlide
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
findItem(R.id.menu_haptics).isChecked = EmulationMenuSettings.hapticFeedback
findItem(R.id.menu_toggle_fps).isChecked =
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.menu_rel_stick_center).isChecked =
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
findItem(R.id.menu_show_overlay).isChecked =
BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
}
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_toggle_fps -> {
it.isChecked = !it.isChecked
EmulationMenuSettings.showFps = it.isChecked
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
updateShowFpsOverlay()
true
}
@ -564,11 +564,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
R.id.menu_toggle_controls -> {
val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
val optionsArray = BooleanArray(Settings.overlayPreferences.size)
Settings.overlayPreferences.forEachIndexed { i, _ ->
optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
val overlayControlData = NativeConfig.getOverlayControlData()
val optionsArray = BooleanArray(overlayControlData.size)
overlayControlData.forEachIndexed { i, _ ->
optionsArray[i] = overlayControlData.firstOrNull { data ->
OverlayControl.entries[i].id == data.id
}?.enabled == true
}
val dialog = MaterialAlertDialogBuilder(requireContext())
@ -577,11 +578,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.array.gamepadButtons,
optionsArray
) { _, indexSelected, isChecked ->
preferences.edit()
.putBoolean("buttonToggle$indexSelected", isChecked)
.apply()
overlayControlData.firstOrNull { data ->
OverlayControl.entries[indexSelected].id == data.id
}?.enabled = isChecked
}
.setPositiveButton(android.R.string.ok) { _, _ ->
NativeConfig.setOverlayControlData(overlayControlData)
NativeConfig.saveGlobalConfig()
binding.surfaceInputOverlay.refreshControls()
}
.setNegativeButton(android.R.string.cancel, null)
@ -592,12 +595,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
.setOnClickListener {
val isChecked = !optionsArray[0]
Settings.overlayPreferences.forEachIndexed { i, _ ->
overlayControlData.forEachIndexed { i, _ ->
optionsArray[i] = isChecked
dialog.listView.setItemChecked(i, isChecked)
preferences.edit()
.putBoolean("buttonToggle$i", isChecked)
.apply()
overlayControlData[i].enabled = isChecked
}
}
true
@ -605,26 +606,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_show_overlay -> {
it.isChecked = !it.isChecked
EmulationMenuSettings.showOverlay = it.isChecked
BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
binding.surfaceInputOverlay.refreshControls()
true
}
R.id.menu_rel_stick_center -> {
it.isChecked = !it.isChecked
EmulationMenuSettings.joystickRelCenter = it.isChecked
BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(it.isChecked)
true
}
R.id.menu_dpad_slide -> {
it.isChecked = !it.isChecked
EmulationMenuSettings.dpadSlide = it.isChecked
BooleanSetting.DPAD_SLIDE.setBoolean(it.isChecked)
true
}
R.id.menu_haptics -> {
it.isChecked = !it.isChecked
EmulationMenuSettings.hapticFeedback = it.isChecked
BooleanSetting.HAPTIC_FEEDBACK.setBoolean(it.isChecked)
true
}
R.id.menu_touchscreen -> {
it.isChecked = !it.isChecked
BooleanSetting.TOUCHSCREEN.setBoolean(it.isChecked)
true
}
@ -667,6 +674,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
}
NativeConfig.saveGlobalConfig()
}
@SuppressLint("SetTextI18n")
@ -675,7 +683,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
adjustBinding.apply {
inputScaleSlider.apply {
valueTo = 150F
value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
addOnChangeListener(
Slider.OnChangeListener { _, value, _ ->
inputScaleValue.text = "${value.toInt()}%"
@ -685,7 +693,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
inputOpacitySlider.apply {
valueTo = 100F
value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
addOnChangeListener(
Slider.OnChangeListener { _, value, _ ->
inputOpacityValue.text = "${value.toInt()}%"
@ -709,16 +717,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun setControlScale(scale: Int) {
preferences.edit()
.putInt(Settings.PREF_CONTROL_SCALE, scale)
.apply()
IntSetting.OVERLAY_SCALE.setInt(scale)
binding.surfaceInputOverlay.refreshControls()
}
private fun setControlOpacity(opacity: Int) {
preferences.edit()
.putInt(Settings.PREF_CONTROL_OPACITY, opacity)
.apply()
IntSetting.OVERLAY_OPACITY.setInt(opacity)
binding.surfaceInputOverlay.refreshControls()
}

View File

@ -10,6 +10,7 @@ import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
/**
* Custom [BitmapDrawable] that is capable
@ -25,7 +26,7 @@ class InputOverlayDrawableButton(
defaultStateBitmap: Bitmap,
pressedStateBitmap: Bitmap,
val buttonId: Int,
val prefId: String
val overlayControlData: OverlayControlData
) {
// The ID value what motion event is tracking
var trackId: Int

View File

@ -14,7 +14,7 @@ import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
/**
* Custom [BitmapDrawable] that is capable
@ -125,7 +125,7 @@ class InputOverlayDrawableJoystick(
pressedState = true
outerBitmap.alpha = 0
boundsBoxBitmap.alpha = opacity
if (EmulationMenuSettings.joystickRelCenter) {
if (BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()) {
virtBounds.offset(
xPosition - virtBounds.centerX(),
yPosition - virtBounds.centerY()

View File

@ -0,0 +1,188 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
import androidx.annotation.IntegerRes
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
enum class OverlayControl(
val id: String,
val defaultVisibility: Boolean,
@IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
@IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
@IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
) {
BUTTON_A(
"button_a",
true,
Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
),
BUTTON_B(
"button_b",
true,
Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
),
BUTTON_X(
"button_x",
true,
Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
),
BUTTON_Y(
"button_y",
true,
Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
),
BUTTON_PLUS(
"button_plus",
true,
Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
),
BUTTON_MINUS(
"button_minus",
true,
Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
),
BUTTON_HOME(
"button_home",
false,
Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
),
BUTTON_CAPTURE(
"button_capture",
false,
Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
),
BUTTON_L(
"button_l",
true,
Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
),
BUTTON_R(
"button_r",
true,
Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
),
BUTTON_ZL(
"button_zl",
true,
Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
),
BUTTON_ZR(
"button_zr",
true,
Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
),
BUTTON_STICK_L(
"button_stick_l",
true,
Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
),
BUTTON_STICK_R(
"button_stick_r",
true,
Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
),
STICK_L(
"stick_l",
true,
Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
),
STICK_R(
"stick_r",
true,
Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
),
COMBINED_DPAD(
"combined_dpad",
true,
Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
);
fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
val rawResourcePair: Pair<Int, Int>
YuzuApplication.appContext.resources.apply {
rawResourcePair = when (layout) {
OverlayLayout.Landscape -> {
Pair(
getInteger(this@OverlayControl.defaultLandscapePositionResources.first),
getInteger(this@OverlayControl.defaultLandscapePositionResources.second)
)
}
OverlayLayout.Portrait -> {
Pair(
getInteger(this@OverlayControl.defaultPortraitPositionResources.first),
getInteger(this@OverlayControl.defaultPortraitPositionResources.second)
)
}
OverlayLayout.Foldable -> {
Pair(
getInteger(this@OverlayControl.defaultFoldablePositionResources.first),
getInteger(this@OverlayControl.defaultFoldablePositionResources.second)
)
}
}
}
return Pair(
rawResourcePair.first.toDouble() / 1000,
rawResourcePair.second.toDouble() / 1000
)
}
fun toOverlayControlData(): OverlayControlData =
OverlayControlData(
id,
defaultVisibility,
getDefaultPositionForLayout(OverlayLayout.Landscape),
getDefaultPositionForLayout(OverlayLayout.Portrait),
getDefaultPositionForLayout(OverlayLayout.Foldable)
)
companion object {
val map: HashMap<String, OverlayControl> by lazy {
val hashMap = hashMapOf<String, OverlayControl>()
entries.forEach { hashMap[it.id] = it }
hashMap
}
fun from(id: String): OverlayControl? = map[id]
}
}

View File

@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
data class OverlayControlData(
val id: String,
var enabled: Boolean,
var landscapePosition: Pair<Double, Double>,
var portraitPosition: Pair<Double, Double>,
var foldablePosition: Pair<Double, Double>
) {
fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
when (layout) {
OverlayLayout.Landscape -> landscapePosition
OverlayLayout.Portrait -> portraitPosition
OverlayLayout.Foldable -> foldablePosition
}
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
import androidx.annotation.IntegerRes
data class OverlayControlDefault(
val buttonId: String,
@IntegerRes val landscapePositionResource: Pair<Int, Int>,
@IntegerRes val portraitPositionResource: Pair<Int, Int>,
@IntegerRes val foldablePositionResource: Pair<Int, Int>
)

View File

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
enum class OverlayLayout(val id: String) {
Landscape("Landscape"),
Portrait("Portrait"),
Foldable("Foldable")
}

View File

@ -3,9 +3,17 @@
package org.yuzu.yuzu_emu.utils
import androidx.preference.PreferenceManager
import java.io.IOException
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
object DirectoryInitialization {
private var userPath: String? = null
@ -17,6 +25,7 @@ object DirectoryInitialization {
initializeInternalStorage()
NativeLibrary.initializeSystem(false)
NativeConfig.initializeGlobalConfig()
migrateSettings()
areDirectoriesReady = true
}
}
@ -35,4 +44,170 @@ object DirectoryInitialization {
e.printStackTrace()
}
}
private fun migrateSettings() {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
var saveConfig = false
val theme = preferences.migratePreference<Int>(Settings.PREF_THEME)
if (theme != null) {
IntSetting.THEME.setInt(theme)
saveConfig = true
}
val themeMode = preferences.migratePreference<Int>(Settings.PREF_THEME_MODE)
if (themeMode != null) {
IntSetting.THEME_MODE.setInt(themeMode)
saveConfig = true
}
val blackBackgrounds =
preferences.migratePreference<Boolean>(Settings.PREF_BLACK_BACKGROUNDS)
if (blackBackgrounds != null) {
BooleanSetting.BLACK_BACKGROUNDS.setBoolean(blackBackgrounds)
saveConfig = true
}
val joystickRelCenter =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER)
if (joystickRelCenter != null) {
BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(joystickRelCenter)
saveConfig = true
}
val dpadSlide =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE)
if (dpadSlide != null) {
BooleanSetting.DPAD_SLIDE.setBoolean(dpadSlide)
saveConfig = true
}
val hapticFeedback =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_HAPTICS)
if (hapticFeedback != null) {
BooleanSetting.HAPTIC_FEEDBACK.setBoolean(hapticFeedback)
saveConfig = true
}
val showPerformanceOverlay =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_FPS)
if (showPerformanceOverlay != null) {
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(showPerformanceOverlay)
saveConfig = true
}
val showInputOverlay =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY)
if (showInputOverlay != null) {
BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(showInputOverlay)
saveConfig = true
}
val overlayOpacity = preferences.migratePreference<Int>(Settings.PREF_CONTROL_OPACITY)
if (overlayOpacity != null) {
IntSetting.OVERLAY_OPACITY.setInt(overlayOpacity)
saveConfig = true
}
val overlayScale = preferences.migratePreference<Int>(Settings.PREF_CONTROL_SCALE)
if (overlayScale != null) {
IntSetting.OVERLAY_SCALE.setInt(overlayScale)
saveConfig = true
}
var setOverlayData = false
val overlayControlData = NativeConfig.getOverlayControlData()
if (overlayControlData.isEmpty()) {
val overlayControlDataMap =
NativeConfig.getOverlayControlData().associateBy { it.id }.toMutableMap()
for (button in Settings.overlayPreferences) {
val buttonId = convertButtonId(button)
var buttonEnabled = preferences.migratePreference<Boolean>(button)
if (buttonEnabled == null) {
buttonEnabled = OverlayControl.map[buttonId]?.defaultVisibility == true
}
var landscapeXPosition = preferences.migratePreference<Float>(
"$button-X${Settings.PREF_LANDSCAPE_SUFFIX}"
)?.toDouble()
var landscapeYPosition = preferences.migratePreference<Float>(
"$button-Y${Settings.PREF_LANDSCAPE_SUFFIX}"
)?.toDouble()
if (landscapeXPosition == null || landscapeYPosition == null) {
val landscapePosition = OverlayControl.map[buttonId]
?.getDefaultPositionForLayout(OverlayLayout.Landscape) ?: Pair(0.0, 0.0)
landscapeXPosition = landscapePosition.first
landscapeYPosition = landscapePosition.second
}
var portraitXPosition = preferences.migratePreference<Float>(
"$button-X${Settings.PREF_PORTRAIT_SUFFIX}"
)?.toDouble()
var portraitYPosition = preferences.migratePreference<Float>(
"$button-Y${Settings.PREF_PORTRAIT_SUFFIX}"
)?.toDouble()
if (portraitXPosition == null || portraitYPosition == null) {
val portraitPosition = OverlayControl.map[buttonId]
?.getDefaultPositionForLayout(OverlayLayout.Portrait) ?: Pair(0.0, 0.0)
portraitXPosition = portraitPosition.first
portraitYPosition = portraitPosition.second
}
var foldableXPosition = preferences.migratePreference<Float>(
"$button-X${Settings.PREF_FOLDABLE_SUFFIX}"
)?.toDouble()
var foldableYPosition = preferences.migratePreference<Float>(
"$button-Y${Settings.PREF_FOLDABLE_SUFFIX}"
)?.toDouble()
if (foldableXPosition == null || foldableYPosition == null) {
val foldablePosition = OverlayControl.map[buttonId]
?.getDefaultPositionForLayout(OverlayLayout.Foldable) ?: Pair(0.0, 0.0)
foldableXPosition = foldablePosition.first
foldableYPosition = foldablePosition.second
}
val controlData = OverlayControlData(
buttonId,
buttonEnabled,
Pair(landscapeXPosition, landscapeYPosition),
Pair(portraitXPosition, portraitYPosition),
Pair(foldableXPosition, foldableYPosition)
)
overlayControlDataMap[buttonId] = controlData
setOverlayData = true
}
if (setOverlayData) {
NativeConfig.setOverlayControlData(
overlayControlDataMap.map { it.value }.toTypedArray()
)
saveConfig = true
}
}
if (saveConfig) {
NativeConfig.saveGlobalConfig()
}
}
private fun convertButtonId(buttonId: String): String =
when (buttonId) {
Settings.PREF_BUTTON_A -> OverlayControl.BUTTON_A.id
Settings.PREF_BUTTON_B -> OverlayControl.BUTTON_B.id
Settings.PREF_BUTTON_X -> OverlayControl.BUTTON_X.id
Settings.PREF_BUTTON_Y -> OverlayControl.BUTTON_Y.id
Settings.PREF_BUTTON_L -> OverlayControl.BUTTON_L.id
Settings.PREF_BUTTON_R -> OverlayControl.BUTTON_R.id
Settings.PREF_BUTTON_ZL -> OverlayControl.BUTTON_ZL.id
Settings.PREF_BUTTON_ZR -> OverlayControl.BUTTON_ZR.id
Settings.PREF_BUTTON_PLUS -> OverlayControl.BUTTON_PLUS.id
Settings.PREF_BUTTON_MINUS -> OverlayControl.BUTTON_MINUS.id
Settings.PREF_BUTTON_DPAD -> OverlayControl.COMBINED_DPAD.id
Settings.PREF_STICK_L -> OverlayControl.STICK_L.id
Settings.PREF_STICK_R -> OverlayControl.STICK_R.id
Settings.PREF_BUTTON_HOME -> OverlayControl.BUTTON_HOME.id
Settings.PREF_BUTTON_SCREENSHOT -> OverlayControl.BUTTON_CAPTURE.id
Settings.PREF_BUTTON_STICK_L -> OverlayControl.BUTTON_STICK_L.id
Settings.PREF_BUTTON_STICK_R -> OverlayControl.BUTTON_STICK_R.id
else -> ""
}
}

View File

@ -1,50 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
object EmulationMenuSettings {
private val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
var joystickRelCenter: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, value)
.apply()
}
var dpadSlide: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, true)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, value)
.apply()
}
var hapticFeedback: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, false)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, value)
.apply()
}
var showFps: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, value)
.apply()
}
var showOverlay: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, true)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, value)
.apply()
}
}

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.utils
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
object NativeConfig {
/**
@ -150,4 +151,21 @@ object NativeConfig {
*/
@Synchronized
external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
/**
* Gets an array of [OverlayControlData] from settings
*
* @return An array of [OverlayControlData]
*/
@Synchronized
external fun getOverlayControlData(): Array<OverlayControlData>
/**
* Clears the AndroidSettings::values.overlay_control_data array and replaces its values
* with [overlayControlData]
*
* @param overlayControlData Replacement array of [OverlayControlData]
*/
@Synchronized
external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
}

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
object PreferenceUtil {
/**
* Retrieves a shared preference value and then deletes the value in storage.
* @param key Associated key for the value in this preferences instance
* @return Typed value associated with [key]. Null if no such key exists.
*/
inline fun <reified T> SharedPreferences.migratePreference(key: String): T? {
if (!this.contains(key)) {
return null
}
val value: Any = when (T::class) {
String::class -> this.getString(key, "")!!
Boolean::class -> this.getBoolean(key, false)
Int::class -> this.getInt(key, 0)
Float::class -> this.getFloat(key, 0f)
Long::class -> this.getLong(key, 0)
else -> throw IllegalStateException("Tried to migrate preference with invalid type!")
}
deletePreference(key)
return value as T
}
fun SharedPreferences.deletePreference(key: String) = this.edit().remove(key).apply()
}

View File

@ -5,38 +5,38 @@ package org.yuzu.yuzu_emu.utils
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.preference.PreferenceManager
import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.ui.main.ThemeProvider
object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f
private const val DEFAULT = 0
private const val MATERIAL_YOU = 1
fun setTheme(activity: AppCompatActivity) {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
setThemeMode(activity)
when (preferences.getInt(Settings.PREF_THEME, 0)) {
DEFAULT -> activity.setTheme(R.style.Theme_Yuzu_Main)
MATERIAL_YOU -> activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
when (Theme.from(IntSetting.THEME.getInt())) {
Theme.Default -> activity.setTheme(R.style.Theme_Yuzu_Main)
Theme.MaterialYou -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
} else {
activity.setTheme(R.style.Theme_Yuzu_Main)
}
}
}
// Using a specific night mode check because this could apply incorrectly when using the
// light app mode, dark system mode, and black backgrounds. Launching the settings activity
// will then show light mode colors/navigation bars but with black backgrounds.
if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
isNightMode(activity)
) {
if (BooleanSetting.BLACK_BACKGROUNDS.getBoolean() && isNightMode(activity)) {
activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
}
}
@ -60,8 +60,7 @@ object ThemeHelper {
}
fun setThemeMode(activity: AppCompatActivity) {
val themeMode = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
.getInt(Settings.PREF_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
val themeMode = IntSetting.THEME_MODE.getInt()
activity.delegate.localNightMode = themeMode
val windowController = WindowCompat.getInsetsController(
activity.window,
@ -95,3 +94,12 @@ object ThemeHelper {
windowController.isAppearanceLightNavigationBars = false
}
}
enum class Theme(val int: Int) {
Default(0),
MaterialYou(1);
companion object {
fun from(int: Int): Theme = entries.firstOrNull { it.int == int } ?: Default
}
}

View File

@ -9,6 +9,7 @@
#include <jni.h>
#include "common/string_util.h"
#include "jni/id_cache.h"
std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) {
@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
jstring ToJString(JNIEnv* env, std::u16string_view str) {
return ToJString(env, Common::UTF16ToUTF8(str));
}
double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
}
jobject ToJDouble(JNIEnv* env, double value) {
return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
}

View File

@ -10,3 +10,6 @@
std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_view str);
double GetJDouble(JNIEnv* env, jobject jdouble);
jobject ToJDouble(JNIEnv* env, double value);

View File

@ -35,6 +35,7 @@ void AndroidConfig::ReadAndroidValues() {
if (global) {
ReadAndroidUIValues();
ReadUIValues();
ReadOverlayValues();
}
ReadDriverValues();
}
@ -81,10 +82,42 @@ void AndroidConfig::ReadDriverValues() {
EndGroup();
}
void AndroidConfig::ReadOverlayValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
ReadCategory(Settings::Category::Overlay);
AndroidSettings::values.overlay_control_data.clear();
const int control_data_size = BeginArray("control_data");
for (int i = 0; i < control_data_size; ++i) {
SetArrayIndex(i);
AndroidSettings::OverlayControlData control_data;
control_data.id = ReadStringSetting(std::string("id"));
control_data.enabled = ReadBooleanSetting(std::string("enabled"));
control_data.landscape_position.first =
ReadDoubleSetting(std::string("landscape\\x_position"));
control_data.landscape_position.second =
ReadDoubleSetting(std::string("landscape\\y_position"));
control_data.portrait_position.first =
ReadDoubleSetting(std::string("portrait\\x_position"));
control_data.portrait_position.second =
ReadDoubleSetting(std::string("portrait\\y_position"));
control_data.foldable_position.first =
ReadDoubleSetting(std::string("foldable\\x_position"));
control_data.foldable_position.second =
ReadDoubleSetting(std::string("foldable\\y_position"));
AndroidSettings::values.overlay_control_data.push_back(control_data);
}
EndArray();
EndGroup();
}
void AndroidConfig::SaveAndroidValues() {
if (global) {
SaveAndroidUIValues();
SaveUIValues();
SaveOverlayValues();
}
SaveDriverValues();
@ -114,8 +147,9 @@ void AndroidConfig::SavePathValues() {
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i);
const auto& game_dir = AndroidSettings::values.game_dirs[i];
WriteSetting(std::string("path"), game_dir.path);
WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
WriteStringSetting(std::string("path"), game_dir.path);
WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
std::make_optional(false));
}
EndArray();
@ -130,6 +164,35 @@ void AndroidConfig::SaveDriverValues() {
EndGroup();
}
void AndroidConfig::SaveOverlayValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
WriteCategory(Settings::Category::Overlay);
BeginArray("control_data");
for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
SetArrayIndex(i);
const auto& control_data = AndroidSettings::values.overlay_control_data[i];
WriteStringSetting(std::string("id"), control_data.id);
WriteBooleanSetting(std::string("enabled"), control_data.enabled);
WriteDoubleSetting(std::string("landscape\\x_position"),
control_data.landscape_position.first);
WriteDoubleSetting(std::string("landscape\\y_position"),
control_data.landscape_position.second);
WriteDoubleSetting(std::string("portrait\\x_position"),
control_data.portrait_position.first);
WriteDoubleSetting(std::string("portrait\\y_position"),
control_data.portrait_position.second);
WriteDoubleSetting(std::string("foldable\\x_position"),
control_data.foldable_position.first);
WriteDoubleSetting(std::string("foldable\\y_position"),
control_data.foldable_position.second);
}
EndArray();
EndGroup();
}
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {

View File

@ -18,6 +18,7 @@ protected:
void ReadAndroidValues();
void ReadAndroidUIValues();
void ReadDriverValues();
void ReadOverlayValues();
void ReadHidbusValues() override {}
void ReadDebugControlValues() override {}
void ReadPathValues() override;
@ -30,6 +31,7 @@ protected:
void SaveAndroidValues();
void SaveAndroidUIValues();
void SaveDriverValues();
void SaveOverlayValues();
void SaveHidbusValues() override {}
void SaveDebugControlValues() override {}
void SavePathValues() override;

View File

@ -14,6 +14,14 @@ struct GameDir {
bool deep_scan = false;
};
struct OverlayControlData {
std::string id;
bool enabled;
std::pair<double, double> landscape_position;
std::pair<double, double> portrait_position;
std::pair<double, double> foldable_position;
};
struct Values {
Settings::Linkage linkage;
@ -33,6 +41,28 @@ struct Values {
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
Settings::Category::GpuDriver};
Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
Settings::Category::Android};
// Input/performance overlay settings
std::vector<OverlayControlData> overlay_control_data;
Settings::Setting<s32> overlay_scale{linkage, 50, "control_scale", Settings::Category::Overlay};
Settings::Setting<s32> overlay_opacity{linkage, 100, "control_opacity",
Settings::Category::Overlay};
Settings::Setting<bool> joystick_rel_center{linkage, true, "joystick_rel_center",
Settings::Category::Overlay};
Settings::Setting<bool> dpad_slide{linkage, true, "dpad_slide", Settings::Category::Overlay};
Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
Settings::Category::Overlay};
Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
};
extern Values values;

View File

@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
static jfieldID s_pair_first_field;
static jfieldID s_pair_second_field;
static jclass s_overlay_control_data_class;
static jmethodID s_overlay_control_data_constructor;
static jfieldID s_overlay_control_data_id_field;
static jfieldID s_overlay_control_data_enabled_field;
static jfieldID s_overlay_control_data_landscape_position_field;
static jfieldID s_overlay_control_data_portrait_position_field;
static jfieldID s_overlay_control_data_foldable_position_field;
static jclass s_double_class;
static jmethodID s_double_constructor;
static jfieldID s_double_value_field;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace IDCache {
@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
return s_pair_second_field;
}
jclass GetOverlayControlDataClass() {
return s_overlay_control_data_class;
}
jmethodID GetOverlayControlDataConstructor() {
return s_overlay_control_data_constructor;
}
jfieldID GetOverlayControlDataIdField() {
return s_overlay_control_data_id_field;
}
jfieldID GetOverlayControlDataEnabledField() {
return s_overlay_control_data_enabled_field;
}
jfieldID GetOverlayControlDataLandscapePositionField() {
return s_overlay_control_data_landscape_position_field;
}
jfieldID GetOverlayControlDataPortraitPositionField() {
return s_overlay_control_data_portrait_position_field;
}
jfieldID GetOverlayControlDataFoldablePositionField() {
return s_overlay_control_data_foldable_position_field;
}
jclass GetDoubleClass() {
return s_double_class;
}
jmethodID GetDoubleConstructor() {
return s_double_constructor;
}
jfieldID GetDoubleValueField() {
return s_double_value_field;
}
} // namespace IDCache
#ifdef __cplusplus
@ -207,6 +259,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
env->DeleteLocalRef(overlay_control_data_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_field = env->GetFieldID(double_class, "value", "D");
env->DeleteLocalRef(double_class);
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
@ -231,6 +308,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
env->DeleteGlobalRef(s_game_class);
env->DeleteGlobalRef(s_string_class);
env->DeleteGlobalRef(s_pair_class);
env->DeleteGlobalRef(s_overlay_control_data_class);
env->DeleteGlobalRef(s_double_class);
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);

View File

@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
jfieldID GetPairFirstField();
jfieldID GetPairSecondField();
jclass GetOverlayControlDataClass();
jmethodID GetOverlayControlDataConstructor();
jfieldID GetOverlayControlDataIdField();
jfieldID GetOverlayControlDataEnabledField();
jfieldID GetOverlayControlDataLandscapePositionField();
jfieldID GetOverlayControlDataPortraitPositionField();
jfieldID GetOverlayControlDataFoldablePositionField();
jclass GetDoubleClass();
jmethodID GetDoubleConstructor();
jfieldID GetDoubleValueField();
} // namespace IDCache

View File

@ -344,4 +344,74 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
Settings::values.disabled_addons[program_id] = disabled_addons;
}
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JNIEnv* env,
jobject obj) {
jobjectArray joverlayControlDataArray =
env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
IDCache::GetOverlayControlDataClass(), nullptr);
for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
const auto& control_data = AndroidSettings::values.overlay_control_data[i];
jobject jlandscapePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
ToJDouble(env, control_data.landscape_position.first),
ToJDouble(env, control_data.landscape_position.second));
jobject jportraitPosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
ToJDouble(env, control_data.portrait_position.first),
ToJDouble(env, control_data.portrait_position.second));
jobject jfoldablePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
ToJDouble(env, control_data.foldable_position.first),
ToJDouble(env, control_data.foldable_position.second));
jobject jcontrolData = env->NewObject(
IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
jportraitPosition, jfoldablePosition);
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
}
return joverlayControlDataArray;
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
JNIEnv* env, jobject obj, jobjectArray joverlayControlDataArray) {
AndroidSettings::values.overlay_control_data.clear();
int size = env->GetArrayLength(joverlayControlDataArray);
if (size == 0) {
return;
}
for (int i = 0; i < size; ++i) {
jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
jstring jidString = static_cast<jstring>(
env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
bool enabled = static_cast<bool>(env->GetBooleanField(
joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
jobject jlandscapePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
std::pair<double, double> landscape_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
GetJDouble(env,
env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
jobject jportraitPosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
std::pair<double, double> portrait_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
jobject jfoldablePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
std::pair<double, double> foldable_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
GetJString(env, jidString), enabled, landscape_position, portrait_position,
foldable_position});
}
}
} // extern "C"

View File

@ -38,6 +38,11 @@
android:title="@string/emulation_haptics"
android:checkable="true" />
<item
android:id="@+id/menu_touchscreen"
android:title="@string/touchscreen"
android:checkable="true" />
<item
android:id="@+id/menu_reset_overlay"
android:title="@string/emulation_touch_overlay_reset" />

View File

@ -212,19 +212,19 @@
<item>B</item>
<item>X</item>
<item>Y</item>
<item>+</item>
<item>-</item>
<item>@string/gamepad_home</item>
<item>@string/gamepad_screenshot</item>
<item>L</item>
<item>R</item>
<item>ZL</item>
<item>ZR</item>
<item>+</item>
<item>-</item>
<item>@string/gamepad_d_pad</item>
<item>@string/gamepad_left_stick</item>
<item>@string/gamepad_right_stick</item>
<item>L3</item>
<item>R3</item>
<item>@string/gamepad_home</item>
<item>@string/gamepad_screenshot</item>
<item>@string/gamepad_d_pad</item>
</string-array>
<string-array name="themeEntries">

View File

@ -3,111 +3,111 @@
<integer name="grid_columns">1</integer>
<!-- Default SWITCH landscape layout -->
<integer name="SWITCH_BUTTON_A_X">760</integer>
<integer name="SWITCH_BUTTON_A_Y">790</integer>
<integer name="SWITCH_BUTTON_B_X">710</integer>
<integer name="SWITCH_BUTTON_B_Y">900</integer>
<integer name="SWITCH_BUTTON_X_X">710</integer>
<integer name="SWITCH_BUTTON_X_Y">680</integer>
<integer name="SWITCH_BUTTON_Y_X">660</integer>
<integer name="SWITCH_BUTTON_Y_Y">790</integer>
<integer name="SWITCH_STICK_L_X">100</integer>
<integer name="SWITCH_STICK_L_Y">670</integer>
<integer name="SWITCH_STICK_R_X">900</integer>
<integer name="SWITCH_STICK_R_Y">670</integer>
<integer name="SWITCH_TRIGGER_L_X">70</integer>
<integer name="SWITCH_TRIGGER_L_Y">220</integer>
<integer name="SWITCH_TRIGGER_R_X">930</integer>
<integer name="SWITCH_TRIGGER_R_Y">220</integer>
<integer name="SWITCH_TRIGGER_ZL_X">70</integer>
<integer name="SWITCH_TRIGGER_ZL_Y">90</integer>
<integer name="SWITCH_TRIGGER_ZR_X">930</integer>
<integer name="SWITCH_TRIGGER_ZR_Y">90</integer>
<integer name="SWITCH_BUTTON_MINUS_X">460</integer>
<integer name="SWITCH_BUTTON_MINUS_Y">950</integer>
<integer name="SWITCH_BUTTON_PLUS_X">540</integer>
<integer name="SWITCH_BUTTON_PLUS_Y">950</integer>
<integer name="SWITCH_BUTTON_HOME_X">600</integer>
<integer name="SWITCH_BUTTON_HOME_Y">950</integer>
<integer name="SWITCH_BUTTON_CAPTURE_X">400</integer>
<integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
<integer name="SWITCH_BUTTON_DPAD_X">260</integer>
<integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
<integer name="SWITCH_BUTTON_STICK_L_X">870</integer>
<integer name="SWITCH_BUTTON_STICK_L_Y">400</integer>
<integer name="SWITCH_BUTTON_STICK_R_X">960</integer>
<integer name="SWITCH_BUTTON_STICK_R_Y">430</integer>
<integer name="BUTTON_A_X">760</integer>
<integer name="BUTTON_A_Y">790</integer>
<integer name="BUTTON_B_X">710</integer>
<integer name="BUTTON_B_Y">900</integer>
<integer name="BUTTON_X_X">710</integer>
<integer name="BUTTON_X_Y">680</integer>
<integer name="BUTTON_Y_X">660</integer>
<integer name="BUTTON_Y_Y">790</integer>
<integer name="BUTTON_PLUS_X">540</integer>
<integer name="BUTTON_PLUS_Y">950</integer>
<integer name="BUTTON_MINUS_X">460</integer>
<integer name="BUTTON_MINUS_Y">950</integer>
<integer name="BUTTON_HOME_X">600</integer>
<integer name="BUTTON_HOME_Y">950</integer>
<integer name="BUTTON_CAPTURE_X">400</integer>
<integer name="BUTTON_CAPTURE_Y">950</integer>
<integer name="BUTTON_L_X">70</integer>
<integer name="BUTTON_L_Y">220</integer>
<integer name="BUTTON_R_X">930</integer>
<integer name="BUTTON_R_Y">220</integer>
<integer name="BUTTON_ZL_X">70</integer>
<integer name="BUTTON_ZL_Y">90</integer>
<integer name="BUTTON_ZR_X">930</integer>
<integer name="BUTTON_ZR_Y">90</integer>
<integer name="BUTTON_STICK_L_X">870</integer>
<integer name="BUTTON_STICK_L_Y">400</integer>
<integer name="BUTTON_STICK_R_X">960</integer>
<integer name="BUTTON_STICK_R_Y">430</integer>
<integer name="STICK_L_X">100</integer>
<integer name="STICK_L_Y">670</integer>
<integer name="STICK_R_X">900</integer>
<integer name="STICK_R_Y">670</integer>
<integer name="COMBINED_DPAD_X">260</integer>
<integer name="COMBINED_DPAD_Y">790</integer>
<!-- Default SWITCH portrait layout -->
<integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
<integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer>
<integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer>
<integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer>
<integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer>
<integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer>
<integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer>
<integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer>
<integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer>
<integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer>
<integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer>
<integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer>
<integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer>
<integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer>
<integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer>
<integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer>
<integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer>
<integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer>
<integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer>
<integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer>
<integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer>
<integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer>
<integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer>
<integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer>
<integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer>
<integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer>
<integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer>
<integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
<integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
<integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
<integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer>
<integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer>
<integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer>
<integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer>
<integer name="BUTTON_A_X_PORTRAIT">840</integer>
<integer name="BUTTON_A_Y_PORTRAIT">840</integer>
<integer name="BUTTON_B_X_PORTRAIT">740</integer>
<integer name="BUTTON_B_Y_PORTRAIT">880</integer>
<integer name="BUTTON_X_X_PORTRAIT">740</integer>
<integer name="BUTTON_X_Y_PORTRAIT">800</integer>
<integer name="BUTTON_Y_X_PORTRAIT">640</integer>
<integer name="BUTTON_Y_Y_PORTRAIT">840</integer>
<integer name="BUTTON_PLUS_Y_PORTRAIT">950</integer>
<integer name="BUTTON_MINUS_X_PORTRAIT">440</integer>
<integer name="BUTTON_MINUS_Y_PORTRAIT">950</integer>
<integer name="BUTTON_HOME_X_PORTRAIT">680</integer>
<integer name="BUTTON_HOME_Y_PORTRAIT">950</integer>
<integer name="BUTTON_CAPTURE_X_PORTRAIT">320</integer>
<integer name="BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
<integer name="BUTTON_L_X_PORTRAIT">140</integer>
<integer name="BUTTON_L_Y_PORTRAIT">260</integer>
<integer name="BUTTON_R_X_PORTRAIT">860</integer>
<integer name="BUTTON_R_Y_PORTRAIT">260</integer>
<integer name="BUTTON_ZL_X_PORTRAIT">140</integer>
<integer name="BUTTON_ZL_Y_PORTRAIT">200</integer>
<integer name="BUTTON_ZR_X_PORTRAIT">860</integer>
<integer name="BUTTON_ZR_Y_PORTRAIT">200</integer>
<integer name="BUTTON_PLUS_X_PORTRAIT">560</integer>
<integer name="BUTTON_STICK_L_X_PORTRAIT">730</integer>
<integer name="BUTTON_STICK_L_Y_PORTRAIT">510</integer>
<integer name="BUTTON_STICK_R_X_PORTRAIT">900</integer>
<integer name="BUTTON_STICK_R_Y_PORTRAIT">540</integer>
<integer name="STICK_L_X_PORTRAIT">180</integer>
<integer name="STICK_L_Y_PORTRAIT">660</integer>
<integer name="STICK_R_X_PORTRAIT">820</integer>
<integer name="STICK_R_Y_PORTRAIT">660</integer>
<integer name="COMBINED_DPAD_X_PORTRAIT">240</integer>
<integer name="COMBINED_DPAD_Y_PORTRAIT">840</integer>
<!-- Default SWITCH foldable layout -->
<integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
<integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer>
<integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer>
<integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer>
<integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer>
<integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer>
<integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer>
<integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer>
<integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer>
<integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer>
<integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer>
<integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer>
<integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer>
<integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer>
<integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer>
<integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer>
<integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer>
<integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer>
<integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer>
<integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer>
<integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer>
<integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer>
<integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer>
<integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer>
<integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer>
<integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer>
<integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer>
<integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
<integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
<integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
<integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer>
<integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer>
<integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer>
<integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer>
<integer name="BUTTON_A_X_FOLDABLE">840</integer>
<integer name="BUTTON_A_Y_FOLDABLE">390</integer>
<integer name="BUTTON_B_X_FOLDABLE">740</integer>
<integer name="BUTTON_B_Y_FOLDABLE">430</integer>
<integer name="BUTTON_X_X_FOLDABLE">740</integer>
<integer name="BUTTON_X_Y_FOLDABLE">350</integer>
<integer name="BUTTON_Y_X_FOLDABLE">640</integer>
<integer name="BUTTON_Y_Y_FOLDABLE">390</integer>
<integer name="BUTTON_PLUS_X_FOLDABLE">560</integer>
<integer name="BUTTON_PLUS_Y_FOLDABLE">470</integer>
<integer name="BUTTON_MINUS_X_FOLDABLE">440</integer>
<integer name="BUTTON_MINUS_Y_FOLDABLE">470</integer>
<integer name="BUTTON_HOME_X_FOLDABLE">680</integer>
<integer name="BUTTON_HOME_Y_FOLDABLE">470</integer>
<integer name="BUTTON_CAPTURE_X_FOLDABLE">320</integer>
<integer name="BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
<integer name="BUTTON_L_X_FOLDABLE">140</integer>
<integer name="BUTTON_L_Y_FOLDABLE">130</integer>
<integer name="BUTTON_R_X_FOLDABLE">860</integer>
<integer name="BUTTON_R_Y_FOLDABLE">130</integer>
<integer name="BUTTON_ZL_X_FOLDABLE">140</integer>
<integer name="BUTTON_ZL_Y_FOLDABLE">70</integer>
<integer name="BUTTON_ZR_X_FOLDABLE">860</integer>
<integer name="BUTTON_ZR_Y_FOLDABLE">70</integer>
<integer name="BUTTON_STICK_L_X_FOLDABLE">550</integer>
<integer name="BUTTON_STICK_L_Y_FOLDABLE">210</integer>
<integer name="BUTTON_STICK_R_X_FOLDABLE">550</integer>
<integer name="BUTTON_STICK_R_Y_FOLDABLE">280</integer>
<integer name="STICK_L_X_FOLDABLE">180</integer>
<integer name="STICK_L_Y_FOLDABLE">250</integer>
<integer name="STICK_R_X_FOLDABLE">820</integer>
<integer name="STICK_R_Y_FOLDABLE">250</integer>
<integer name="COMBINED_DPAD_X_FOLDABLE">240</integer>
<integer name="COMBINED_DPAD_Y_FOLDABLE">390</integer>
</resources>

View File

@ -366,6 +366,7 @@
<string name="emulation_pause">Pause emulation</string>
<string name="emulation_unpause">Unpause emulation</string>
<string name="emulation_input_overlay">Overlay options</string>
<string name="touchscreen">Touchscreen</string>
<string name="load_settings">Loading settings…</string>

View File

@ -5,7 +5,7 @@
plugins {
id("com.android.application") version "8.1.2" apply false
id("com.android.library") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
}
tasks.register("clean").configure {

View File

@ -199,6 +199,8 @@ const char* TranslateCategory(Category category) {
case Category::CpuDebug:
case Category::CpuUnsafe:
return "Cpu";
case Category::Overlay:
return "Overlay";
case Category::Renderer:
case Category::RendererAdvanced:
case Category::RendererDebug:

View File

@ -18,6 +18,7 @@ enum class Category : u32 {
Cpu,
CpuDebug,
CpuUnsafe,
Overlay,
Renderer,
RendererAdvanced,
RendererDebug,

View File

@ -403,59 +403,63 @@ void Config::SavePlayerValues(const std::size_t player_index) {
// No custom profile selected
return;
}
WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
std::make_optional(std::string("")));
WriteStringSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
std::make_optional(std::string("")));
}
WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
WriteIntegerSetting(
std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
WriteSetting(std::string(player_prefix).append("connected"), player.connected,
std::make_optional(player_index == 0));
WriteSetting(std::string(player_prefix).append("vibration_enabled"),
player.vibration_enabled, std::make_optional(true));
WriteSetting(std::string(player_prefix).append("vibration_strength"),
player.vibration_strength, std::make_optional(100));
WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left,
std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right,
std::make_optional(Settings::JOYCON_BODY_NEON_RED));
WriteSetting(std::string(player_prefix).append("button_color_left"),
player.button_color_left,
std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
WriteSetting(std::string(player_prefix).append("button_color_right"),
player.button_color_right,
std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected,
std::make_optional(player_index == 0));
WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"),
player.vibration_enabled, std::make_optional(true));
WriteIntegerSetting(std::string(player_prefix).append("vibration_strength"),
player.vibration_strength, std::make_optional(100));
WriteIntegerSetting(std::string(player_prefix).append("body_color_left"),
player.body_color_left,
std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
WriteIntegerSetting(std::string(player_prefix).append("body_color_right"),
player.body_color_right,
std::make_optional(Settings::JOYCON_BODY_NEON_RED));
WriteIntegerSetting(std::string(player_prefix).append("button_color_left"),
player.button_color_left,
std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
WriteIntegerSetting(std::string(player_prefix).append("button_color_right"),
player.button_color_right,
std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
}
}
void Config::SaveTouchscreenValues() {
const auto& touchscreen = Settings::values.touchscreen;
WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true));
WriteBooleanSetting(std::string("touchscreen_enabled"), touchscreen.enabled,
std::make_optional(true));
WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
std::make_optional(static_cast<u32>(0)));
WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
std::make_optional(static_cast<u32>(15)));
WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
std::make_optional(static_cast<u32>(15)));
WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
std::make_optional(static_cast<u32>(0)));
WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
std::make_optional(static_cast<u32>(15)));
WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
std::make_optional(static_cast<u32>(15)));
}
void Config::SaveMotionTouchValues() {
BeginArray(std::string("touch_from_button_maps"));
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
SetArrayIndex(static_cast<int>(p));
WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
std::make_optional(std::string("default")));
WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
std::make_optional(std::string("default")));
BeginArray(std::string("entries"));
for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
++q) {
SetArrayIndex(static_cast<int>(q));
WriteSetting(std::string("bind"),
Settings::values.touch_from_button_maps[p].buttons[q]);
WriteStringSetting(std::string("bind"),
Settings::values.touch_from_button_maps[p].buttons[q]);
}
EndArray(); // entries
}
@ -520,16 +524,16 @@ void Config::SaveCoreValues() {
void Config::SaveDataStorageValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
WriteStringSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
WriteStringSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
WriteStringSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
WriteStringSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
WriteStringSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
WriteCategory(Settings::Category::DataStorage);
@ -540,7 +544,7 @@ void Config::SaveDebuggingValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
// Intentionally not using the QT default setting as this is intended to be changed in the ini
WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
WriteCategory(Settings::Category::Debugging);
WriteCategory(Settings::Category::DebuggingGraphics);
@ -564,11 +568,13 @@ void Config::SaveDisabledAddOnValues() {
BeginArray(std::string(""));
for (const auto& elem : Settings::values.disabled_addons) {
SetArrayIndex(i);
WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0)));
WriteIntegerSetting(std::string("title_id"), elem.first,
std::make_optional(static_cast<u64>(0)));
BeginArray(std::string("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
SetArrayIndex(static_cast<int>(j));
WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
WriteStringSetting(std::string("d"), elem.second[j],
std::make_optional(std::string("")));
}
EndArray(); // disabled
++i;
@ -609,8 +615,8 @@ void Config::SaveRendererValues() {
void Config::SaveScreenshotValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
WriteSetting(std::string("screenshot_path"),
FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
WriteStringSetting(std::string("screenshot_path"),
FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
WriteCategory(Settings::Category::Screenshots);
EndGroup();
@ -746,46 +752,70 @@ bool Config::Exists(const std::string& section, const std::string& key) const {
return !value.empty();
}
template <typename Type>
void Config::WriteSetting(const std::string& key, const Type& value,
const std::optional<Type>& default_value,
const std::optional<bool>& use_global) {
std::string full_key = GetFullKey(key, false);
std::string saved_value;
std::string string_default;
if constexpr (std::is_same_v<Type, std::string>) {
saved_value.append(AdjustOutputString(value));
if (default_value.has_value()) {
string_default.append(AdjustOutputString(default_value.value()));
}
} else {
saved_value.append(AdjustOutputString(ToString(value)));
if (default_value.has_value()) {
string_default.append(ToString(default_value.value()));
}
void Config::WriteBooleanSetting(const std::string& key, const bool& value,
const std::optional<bool>& default_value,
const std::optional<bool>& use_global) {
std::optional<std::string> string_default = std::nullopt;
if (default_value.has_value()) {
string_default = std::make_optional(ToString(default_value.value()));
}
WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
}
if (default_value.has_value() && use_global.has_value()) {
template <typename T>
std::enable_if_t<std::is_integral_v<T>> Config::WriteIntegerSetting(
const std::string& key, const T& value, const std::optional<T>& default_value,
const std::optional<bool>& use_global) {
std::optional<std::string> string_default = std::nullopt;
if (default_value.has_value()) {
string_default = std::make_optional(ToString(default_value.value()));
}
WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
}
void Config::WriteDoubleSetting(const std::string& key, const double& value,
const std::optional<double>& default_value,
const std::optional<bool>& use_global) {
std::optional<std::string> string_default = std::nullopt;
if (default_value.has_value()) {
string_default = std::make_optional(ToString(default_value.value()));
}
WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
}
void Config::WriteStringSetting(const std::string& key, const std::string& value,
const std::optional<std::string>& default_value,
const std::optional<bool>& use_global) {
std::optional string_default = default_value;
if (default_value.has_value()) {
string_default.value().append(AdjustOutputString(default_value.value()));
}
WritePreparedSetting(key, AdjustOutputString(value), string_default, use_global);
}
void Config::WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
const std::optional<std::string>& adjusted_default_value,
const std::optional<bool>& use_global) {
std::string full_key = GetFullKey(key, false);
if (adjusted_default_value.has_value() && use_global.has_value()) {
if (!global) {
WriteSettingInternal(std::string(full_key).append("\\global"),
ToString(use_global.value()));
WriteString(std::string(full_key).append("\\global"), ToString(use_global.value()));
}
if (global || use_global.value() == false) {
WriteSettingInternal(std::string(full_key).append("\\default"),
ToString(string_default == saved_value));
WriteSettingInternal(full_key, saved_value);
WriteString(std::string(full_key).append("\\default"),
ToString(adjusted_default_value == adjusted_value));
WriteString(full_key, adjusted_value);
}
} else if (default_value.has_value() && !use_global.has_value()) {
WriteSettingInternal(std::string(full_key).append("\\default"),
ToString(string_default == saved_value));
WriteSettingInternal(full_key, saved_value);
} else if (adjusted_default_value.has_value() && !use_global.has_value()) {
WriteString(std::string(full_key).append("\\default"),
ToString(adjusted_default_value == adjusted_value));
WriteString(full_key, adjusted_value);
} else {
WriteSettingInternal(full_key, saved_value);
WriteString(full_key, adjusted_value);
}
}
void Config::WriteSettingInternal(const std::string& key, const std::string& value) {
void Config::WriteString(const std::string& key, const std::string& value) {
config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
}
@ -861,17 +891,17 @@ void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
std::string key = AdjustKey(setting->GetLabel());
if (setting->Switchable()) {
if (!global) {
WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
}
if (global || !setting->UsingGlobal()) {
WriteSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString());
WriteSetting(key, setting->ToString());
WriteBooleanSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString());
WriteStringSetting(key, setting->ToString());
}
} else if (global) {
WriteSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString());
WriteSetting(key, setting->ToString());
WriteBooleanSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString());
WriteStringSetting(key, setting->ToString());
}
}

View File

@ -154,11 +154,20 @@ protected:
* @param use_global Specifies if the custom or global config should be in use, for custom
* configs
*/
template <typename Type = int>
void WriteSetting(const std::string& key, const Type& value,
const std::optional<Type>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
void WriteSettingInternal(const std::string& key, const std::string& value);
void WriteBooleanSetting(const std::string& key, const bool& value,
const std::optional<bool>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
template <typename T>
std::enable_if_t<std::is_integral_v<T>> WriteIntegerSetting(
const std::string& key, const T& value,
const std::optional<T>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
void WriteDoubleSetting(const std::string& key, const double& value,
const std::optional<double>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
void WriteStringSetting(const std::string& key, const std::string& value,
const std::optional<std::string>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
void ReadCategory(Settings::Category category);
void WriteCategory(Settings::Category category);
@ -175,8 +184,10 @@ protected:
return value_ ? "true" : "false";
} else if constexpr (std::is_same_v<T, u64>) {
return std::to_string(static_cast<u64>(value_));
} else {
} else if constexpr (std::is_same_v<T, s64>) {
return std::to_string(static_cast<s64>(value_));
} else {
return std::to_string(value_);
}
}
@ -197,9 +208,13 @@ protected:
const bool global;
private:
inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*',
'|', ';', '\'', '\"', ',', '<', '.',
'>', '?', '`', '~', '='};
void WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
const std::optional<std::string>& adjusted_default_value,
const std::optional<bool>& use_global);
void WriteString(const std::string& key, const std::string& value);
inline static std::array<char, 18> special_characters = {
'!', '#', '$', '%', '^', '&', '*', '|', ';', '\'', '\"', ',', '<', '>', '?', '`', '~', '='};
struct ConfigArray {
std::string name;

View File

@ -348,43 +348,45 @@ void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
player.buttons[i], std::make_optional(default_param));
WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
player.buttons[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
player.analogs[i], std::make_optional(default_param));
WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
player.analogs[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
player.motions[i], std::make_optional(default_param));
WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
player.motions[i], std::make_optional(default_param));
}
}
void QtConfig::SaveDebugControlValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
Settings::values.debug_pad_buttons[i],
std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
Settings::values.debug_pad_analogs[i],
std::make_optional(default_param));
}
}
void QtConfig::SaveHidbusValues() {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
std::make_optional(default_param));
WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
std::make_optional(default_param));
}
void QtConfig::SaveQtControlValues() {
@ -409,19 +411,20 @@ void QtConfig::SavePathValues() {
WriteCategory(Settings::Category::Paths);
WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
WriteStringSetting(std::string("romsPath"), UISettings::values.roms_path);
BeginArray(std::string("gamedirs"));
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i);
const auto& game_dir = UISettings::values.game_dirs[i];
WriteSetting(std::string("path"), game_dir.path);
WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
WriteStringSetting(std::string("path"), game_dir.path);
WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
std::make_optional(false));
WriteBooleanSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
}
EndArray();
WriteSetting(std::string("recentFiles"),
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
WriteStringSetting(std::string("recentFiles"),
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
EndGroup();
}
@ -438,14 +441,14 @@ void QtConfig::SaveShortcutValues() {
BeginGroup(group);
BeginGroup(name);
WriteSetting(std::string("KeySeq"), shortcut.keyseq,
std::make_optional(default_hotkey.keyseq));
WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
std::make_optional(default_hotkey.controller_keyseq));
WriteSetting(std::string("Context"), shortcut.context,
std::make_optional(default_hotkey.context));
WriteSetting(std::string("Repeat"), shortcut.repeat,
std::make_optional(default_hotkey.repeat));
WriteStringSetting(std::string("KeySeq"), shortcut.keyseq,
std::make_optional(default_hotkey.keyseq));
WriteStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
std::make_optional(default_hotkey.controller_keyseq));
WriteIntegerSetting(std::string("Context"), shortcut.context,
std::make_optional(default_hotkey.context));
WriteBooleanSetting(std::string("Repeat"), shortcut.repeat,
std::make_optional(default_hotkey.repeat));
EndGroup(); // name
EndGroup(); // group
@ -460,9 +463,10 @@ void QtConfig::SaveUIValues() {
WriteCategory(Settings::Category::Ui);
WriteCategory(Settings::Category::UiGeneral);
WriteSetting(std::string("theme"), UISettings::values.theme,
std::make_optional(std::string(
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
WriteStringSetting(
std::string("theme"), UISettings::values.theme,
std::make_optional(std::string(
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
SaveUIGamelistValues();
SaveUILayoutValues();
@ -482,7 +486,7 @@ void QtConfig::SaveUIGamelistValues() {
BeginArray(std::string("favorites"));
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
SetArrayIndex(i);
WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
}
EndArray(); // favorites
@ -506,14 +510,15 @@ void QtConfig::SaveMultiplayerValues() {
BeginArray(std::string("username_ban_list"));
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]);
WriteStringSetting(std::string("username"),
UISettings::values.multiplayer_ban_list.first[i]);
}
EndArray(); // username_ban_list
BeginArray(std::string("ip_ban_list"));
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
WriteStringSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
}
EndArray(); // ip_ban_list

View File

@ -213,43 +213,45 @@ void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
player.buttons[i], std::make_optional(default_param));
WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
player.buttons[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
player.analogs[i], std::make_optional(default_param));
WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
player.analogs[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
player.motions[i], std::make_optional(default_param));
WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
player.motions[i], std::make_optional(default_param));
}
}
void SdlConfig::SaveDebugControlValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
Settings::values.debug_pad_buttons[i],
std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
Settings::values.debug_pad_analogs[i],
std::make_optional(default_param));
}
}
void SdlConfig::SaveHidbusValues() {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
std::make_optional(default_param));
WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
std::make_optional(default_param));
}
std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {