android: Migrate in-game overlay settings to ini
This commit is contained in:
		@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,13 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
 | 
			
		||||
    RENDERER_DEBUG("debug"),
 | 
			
		||||
    PICTURE_IN_PICTURE("picture_in_picture"),
 | 
			
		||||
    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 =
 | 
			
		||||
        NativeConfig.getBoolean(key, needsGlobal)
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
 | 
			
		||||
    AUDIO_OUTPUT_ENGINE("output_engine"),
 | 
			
		||||
    MAX_ANISOTROPY("max_anisotropy"),
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,24 +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"
 | 
			
		||||
 | 
			
		||||
    // 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(
 | 
			
		||||
        PREF_OVERLAY_VERSION,
 | 
			
		||||
        PREF_CONTROL_SCALE,
 | 
			
		||||
        PREF_CONTROL_OPACITY,
 | 
			
		||||
        PREF_TOUCH_ENABLED,
 | 
			
		||||
        PREF_BUTTON_A,
 | 
			
		||||
        PREF_BUTTON_B,
 | 
			
		||||
        PREF_BUTTON_X,
 | 
			
		||||
@@ -84,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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,21 @@ 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()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 +563,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 +577,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 +594,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 +605,26 @@ 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
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -667,6 +667,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
                it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        NativeConfig.saveGlobalConfig()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("SetTextI18n")
 | 
			
		||||
@@ -675,7 +676,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 +686,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 +710,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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
@@ -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>
 | 
			
		||||
)
 | 
			
		||||
@@ -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")
 | 
			
		||||
}
 | 
			
		||||
@@ -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.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 {
 | 
			
		||||
@@ -64,8 +67,147 @@ object DirectoryInitialization {
 | 
			
		||||
            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 -> ""
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
@@ -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>)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 | 
			
		||||
@@ -131,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)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
@@ -38,6 +46,23 @@ struct Values {
 | 
			
		||||
    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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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">
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ enum class Category : u32 {
 | 
			
		||||
    Cpu,
 | 
			
		||||
    CpuDebug,
 | 
			
		||||
    CpuUnsafe,
 | 
			
		||||
    Overlay,
 | 
			
		||||
    Renderer,
 | 
			
		||||
    RendererAdvanced,
 | 
			
		||||
    RendererDebug,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user