android: Migrate in-game overlay settings to ini

This commit is contained in:
t895 2023-12-24 15:42:28 -05:00
parent 051afd21e7
commit d163b18208
27 changed files with 1209 additions and 847 deletions

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

View file

@ -19,7 +19,13 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_DEBUG("debug"), RENDERER_DEBUG("debug"),
PICTURE_IN_PICTURE("picture_in_picture"), PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"), USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"); 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 = override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal) NativeConfig.getBoolean(key, needsGlobal)

View file

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

View file

@ -15,18 +15,10 @@ object Settings {
SECTION_DEBUG(R.string.preferences_debug); SECTION_DEBUG(R.string.preferences_debug);
} }
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val PREF_OVERLAY_VERSION = "OverlayVersion" // Deprecated input overlay preference keys
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
)
const val PREF_CONTROL_SCALE = "controlScale" const val PREF_CONTROL_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity" const val PREF_CONTROL_OPACITY = "controlOpacity"
const val PREF_TOUCH_ENABLED = "isTouchEnabled" const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@ -47,24 +39,12 @@ object Settings {
const val PREF_BUTTON_STICK_R = "buttonToggle14" const val PREF_BUTTON_STICK_R = "buttonToggle14"
const val PREF_BUTTON_HOME = "buttonToggle15" const val PREF_BUTTON_HOME = "buttonToggle15"
const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
// Deprecated theme preference keys
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( val overlayPreferences = listOf(
PREF_OVERLAY_VERSION,
PREF_CONTROL_SCALE,
PREF_CONTROL_OPACITY,
PREF_TOUCH_ENABLED,
PREF_BUTTON_A, PREF_BUTTON_A,
PREF_BUTTON_B, PREF_BUTTON_B,
PREF_BUTTON_X, PREF_BUTTON_X,
@ -84,6 +64,21 @@ object Settings {
PREF_BUTTON_STICK_R 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_Unspecified = 0
const val LayoutOption_MobilePortrait = 4 const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5 const val LayoutOption_MobileLandscape = 5

View file

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

View file

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

View file

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

@ -10,6 +10,9 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 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.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings 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 import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
object DirectoryInitialization { object DirectoryInitialization {
@ -64,8 +67,147 @@ object DirectoryInitialization {
saveConfig = true 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) { if (saveConfig) {
NativeConfig.saveGlobalConfig() 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 package org.yuzu.yuzu_emu.utils
import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
object NativeConfig { object NativeConfig {
/** /**
@ -150,4 +151,21 @@ object NativeConfig {
*/ */
@Synchronized @Synchronized
external fun setDisabledAddons(programId: String, disabledAddons: Array<String>) 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

@ -9,6 +9,7 @@
#include <jni.h> #include <jni.h>
#include "common/string_util.h" #include "common/string_util.h"
#include "jni/id_cache.h"
std::string GetJString(JNIEnv* env, jstring jstr) { std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) { if (!jstr) {
@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
jstring ToJString(JNIEnv* env, std::u16string_view str) { jstring ToJString(JNIEnv* env, std::u16string_view str) {
return ToJString(env, Common::UTF16ToUTF8(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); std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str); jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_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) { if (global) {
ReadAndroidUIValues(); ReadAndroidUIValues();
ReadUIValues(); ReadUIValues();
ReadOverlayValues();
} }
ReadDriverValues(); ReadDriverValues();
} }
@ -81,10 +82,42 @@ void AndroidConfig::ReadDriverValues() {
EndGroup(); 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() { void AndroidConfig::SaveAndroidValues() {
if (global) { if (global) {
SaveAndroidUIValues(); SaveAndroidUIValues();
SaveUIValues(); SaveUIValues();
SaveOverlayValues();
} }
SaveDriverValues(); SaveDriverValues();
@ -131,6 +164,35 @@ void AndroidConfig::SaveDriverValues() {
EndGroup(); 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) { std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category; auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) { if (map.contains(category)) {

View file

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

View file

@ -14,6 +14,14 @@ struct GameDir {
bool deep_scan = false; 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 { struct Values {
Settings::Linkage linkage; Settings::Linkage linkage;
@ -38,6 +46,23 @@ struct Values {
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", 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::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
Settings::Category::Android}; 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; extern Values values;

View file

@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
static jfieldID s_pair_first_field; static jfieldID s_pair_first_field;
static jfieldID s_pair_second_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; static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace IDCache { namespace IDCache {
@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
return s_pair_second_field; 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 } // namespace IDCache
#ifdef __cplusplus #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;"); s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class); 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 // Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 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_game_class);
env->DeleteGlobalRef(s_string_class); env->DeleteGlobalRef(s_string_class);
env->DeleteGlobalRef(s_pair_class); env->DeleteGlobalRef(s_pair_class);
env->DeleteGlobalRef(s_overlay_control_data_class);
env->DeleteGlobalRef(s_double_class);
// UnInitialize applets // UnInitialize applets
SoftwareKeyboard::CleanupJNI(env); SoftwareKeyboard::CleanupJNI(env);

View file

@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
jfieldID GetPairFirstField(); jfieldID GetPairFirstField();
jfieldID GetPairSecondField(); jfieldID GetPairSecondField();
jclass GetOverlayControlDataClass();
jmethodID GetOverlayControlDataConstructor();
jfieldID GetOverlayControlDataIdField();
jfieldID GetOverlayControlDataEnabledField();
jfieldID GetOverlayControlDataLandscapePositionField();
jfieldID GetOverlayControlDataPortraitPositionField();
jfieldID GetOverlayControlDataFoldablePositionField();
jclass GetDoubleClass();
jmethodID GetDoubleConstructor();
jfieldID GetDoubleValueField();
} // namespace IDCache } // 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; 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" } // extern "C"

View file

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

View file

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

View file

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

View file

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