summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt74
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt96
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt984
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt188
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt175
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt50
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt38
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.cpp9
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.h3
-rw-r--r--src/android/app/src/main/jni/android_config.cpp67
-rw-r--r--src/android/app/src/main/jni/android_config.h2
-rw-r--r--src/android/app/src/main/jni/android_settings.h30
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp79
-rw-r--r--src/android/app/src/main/jni/id_cache.h12
-rw-r--r--src/android/app/src/main/jni/native_config.cpp70
-rw-r--r--src/android/app/src/main/res/menu/menu_overlay_options.xml5
-rw-r--r--src/android/app/src/main/res/values/arrays.xml10
-rw-r--r--src/android/app/src/main/res/values/integers.xml204
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/android/build.gradle.kts2
-rw-r--r--src/audio_core/device/device_session.cpp4
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/heap_tracker.cpp281
-rw-r--r--src/common/heap_tracker.h98
-rw-r--r--src/common/host_memory.cpp10
-rw-r--r--src/common/host_memory.h11
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/core/CMakeLists.txt19
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp49
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h20
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp5
-rw-r--r--src/core/core_timing.cpp48
-rw-r--r--src/core/core_timing.h27
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp19
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp26
-rw-r--r--src/core/hle/kernel/k_page_table_base.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp6
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.h94
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.h (renamed from src/core/hle/service/hid/controllers/stubbed.h)8
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp15
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h6
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h8
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.cpp63
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.h34
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h6
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.h27
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp41
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h5
-rw-r--r--src/core/hle/service/hid/controllers/home_button.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/home_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h6
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp15
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h5
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp51
-rw-r--r--src/core/hle/service/hid/controllers/npad.h11
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp24
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp31
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h4
-rw-r--r--src/core/hle/service/hid/controllers/types/shared_memory_format.h (renamed from src/core/hle/service/hid/controllers/shared_memory_format.h)0
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.h27
-rw-r--r--src/core/hle/service/hid/hidbus.cpp8
-rw-r--r--src/core/hle/service/hid/hidbus.h2
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp149
-rw-r--r--src/core/hle/service/hid/resource_manager.h26
-rw-r--r--src/core/hle/service/jit/jit.cpp59
-rw-r--r--src/core/hle/service/jit/jit_code_memory.cpp54
-rw-r--r--src/core/hle/service/jit/jit_code_memory.h49
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp8
-rw-r--r--src/core/memory.cpp86
-rw-r--r--src/core/memory.h10
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/memory/cheat_engine.h2
-rw-r--r--src/core/tools/freezer.cpp17
-rw-r--r--src/core/tools/freezer.h2
-rw-r--r--src/frontend_common/config.cpp194
-rw-r--r--src/frontend_common/config.h33
-rw-r--r--src/tests/common/host_memory.cpp99
-rw-r--r--src/tests/core/core_timing.cpp14
-rw-r--r--src/video_core/engines/maxwell_dma.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp2
-rw-r--r--src/yuzu/configuration/qt_config.cpp69
-rw-r--r--src/yuzu_cmd/sdl_config.cpp26
109 files changed, 3131 insertions, 1499 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index f763c657e..53aafa08c 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -10,7 +10,7 @@ plugins {
10 id("com.android.application") 10 id("com.android.application")
11 id("org.jetbrains.kotlin.android") 11 id("org.jetbrains.kotlin.android")
12 id("kotlin-parcelize") 12 id("kotlin-parcelize")
13 kotlin("plugin.serialization") version "1.8.21" 13 kotlin("plugin.serialization") version "1.9.20"
14 id("androidx.navigation.safeargs.kotlin") 14 id("androidx.navigation.safeargs.kotlin")
15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0" 15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
16} 16}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 9b08f008d..93c8ce922 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -49,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
49import org.yuzu.yuzu_emu.utils.InputHandler 49import org.yuzu.yuzu_emu.utils.InputHandler
50import org.yuzu.yuzu_emu.utils.Log 50import org.yuzu.yuzu_emu.utils.Log
51import org.yuzu.yuzu_emu.utils.MemoryUtil 51import org.yuzu.yuzu_emu.utils.MemoryUtil
52import org.yuzu.yuzu_emu.utils.NativeConfig
52import org.yuzu.yuzu_emu.utils.NfcReader 53import org.yuzu.yuzu_emu.utils.NfcReader
53import org.yuzu.yuzu_emu.utils.ThemeHelper 54import org.yuzu.yuzu_emu.utils.ThemeHelper
54import java.text.NumberFormat 55import java.text.NumberFormat
@@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
170 stopMotionSensorListener() 171 stopMotionSensorListener()
171 } 172 }
172 173
174 override fun onStop() {
175 super.onStop()
176 NativeConfig.saveGlobalConfig()
177 }
178
173 override fun onUserLeaveHint() { 179 override fun onUserLeaveHint() {
174 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 180 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
175 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { 181 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 16f06cd0a..86bd33672 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -18,7 +18,14 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
18 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), 18 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
19 RENDERER_DEBUG("debug"), 19 RENDERER_DEBUG("debug"),
20 PICTURE_IN_PICTURE("picture_in_picture"), 20 PICTURE_IN_PICTURE("picture_in_picture"),
21 USE_CUSTOM_RTC("custom_rtc_enabled"); 21 USE_CUSTOM_RTC("custom_rtc_enabled"),
22 BLACK_BACKGROUNDS("black_backgrounds"),
23 JOYSTICK_REL_CENTER("joystick_rel_center"),
24 DPAD_SLIDE("dpad_slide"),
25 HAPTIC_FEEDBACK("haptic_feedback"),
26 SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
27 SHOW_INPUT_OVERLAY("show_input_overlay"),
28 TOUCHSCREEN("touchscreen");
22 29
23 override fun getBoolean(needsGlobal: Boolean): Boolean = 30 override fun getBoolean(needsGlobal: Boolean): Boolean =
24 NativeConfig.getBoolean(key, needsGlobal) 31 NativeConfig.getBoolean(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index df760440f..16fb87614 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -19,7 +19,11 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
19 RENDERER_SCREEN_LAYOUT("screen_layout"), 19 RENDERER_SCREEN_LAYOUT("screen_layout"),
20 RENDERER_ASPECT_RATIO("aspect_ratio"), 20 RENDERER_ASPECT_RATIO("aspect_ratio"),
21 AUDIO_OUTPUT_ENGINE("output_engine"), 21 AUDIO_OUTPUT_ENGINE("output_engine"),
22 MAX_ANISOTROPY("max_anisotropy"); 22 MAX_ANISOTROPY("max_anisotropy"),
23 THEME("theme"),
24 THEME_MODE("theme_mode"),
25 OVERLAY_SCALE("control_scale"),
26 OVERLAY_OPACITY("control_opacity");
23 27
24 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) 28 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
25 29
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 9551fc05e..43caac989 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -15,18 +15,10 @@ object Settings {
15 SECTION_DEBUG(R.string.preferences_debug); 15 SECTION_DEBUG(R.string.preferences_debug);
16 } 16 }
17 17
18 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
18 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 19 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
19 20
20 const val PREF_OVERLAY_VERSION = "OverlayVersion" 21 // Deprecated input overlay preference keys
21 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
22 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
23 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
24 val overlayLayoutPrefs = listOf(
25 PREF_LANDSCAPE_OVERLAY_VERSION,
26 PREF_PORTRAIT_OVERLAY_VERSION,
27 PREF_FOLDABLE_OVERLAY_VERSION
28 )
29
30 const val PREF_CONTROL_SCALE = "controlScale" 22 const val PREF_CONTROL_SCALE = "controlScale"
31 const val PREF_CONTROL_OPACITY = "controlOpacity" 23 const val PREF_CONTROL_OPACITY = "controlOpacity"
32 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 24 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@@ -47,23 +39,12 @@ object Settings {
47 const val PREF_BUTTON_STICK_R = "buttonToggle14" 39 const val PREF_BUTTON_STICK_R = "buttonToggle14"
48 const val PREF_BUTTON_HOME = "buttonToggle15" 40 const val PREF_BUTTON_HOME = "buttonToggle15"
49 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" 41 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
50
51 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" 42 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
52 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" 43 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
53 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" 44 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
54 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" 45 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
55 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" 46 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
56
57 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
58 const val PREF_THEME = "Theme"
59 const val PREF_THEME_MODE = "ThemeMode"
60 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
61
62 val overlayPreferences = listOf( 47 val overlayPreferences = listOf(
63 PREF_OVERLAY_VERSION,
64 PREF_CONTROL_SCALE,
65 PREF_CONTROL_OPACITY,
66 PREF_TOUCH_ENABLED,
67 PREF_BUTTON_A, 48 PREF_BUTTON_A,
68 PREF_BUTTON_B, 49 PREF_BUTTON_B,
69 PREF_BUTTON_X, 50 PREF_BUTTON_X,
@@ -83,6 +64,21 @@ object Settings {
83 PREF_BUTTON_STICK_R 64 PREF_BUTTON_STICK_R
84 ) 65 )
85 66
67 // Deprecated layout preference keys
68 const val PREF_LANDSCAPE_SUFFIX = "_Landscape"
69 const val PREF_PORTRAIT_SUFFIX = "_Portrait"
70 const val PREF_FOLDABLE_SUFFIX = "_Foldable"
71 val overlayLayoutSuffixes = listOf(
72 PREF_LANDSCAPE_SUFFIX,
73 PREF_PORTRAIT_SUFFIX,
74 PREF_FOLDABLE_SUFFIX
75 )
76
77 // Deprecated theme preference keys
78 const val PREF_THEME = "Theme"
79 const val PREF_THEME_MODE = "ThemeMode"
80 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
81
86 const val LayoutOption_Unspecified = 0 82 const val LayoutOption_Unspecified = 0
87 const val LayoutOption_MobilePortrait = 4 83 const val LayoutOption_MobilePortrait = 4
88 const val LayoutOption_MobileLandscape = 5 84 const val LayoutOption_MobileLandscape = 5
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index db1a1076c..2ad2f4966 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -3,10 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.SharedPreferences
7import android.os.Build 6import android.os.Build
8import android.widget.Toast 7import android.widget.Toast
9import androidx.preference.PreferenceManager
10import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
11import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
12import org.yuzu.yuzu_emu.YuzuApplication 10import org.yuzu.yuzu_emu.YuzuApplication
@@ -29,9 +27,6 @@ class SettingsFragmentPresenter(
29) { 27) {
30 private var settingsList = ArrayList<SettingsItem>() 28 private var settingsList = ArrayList<SettingsItem>()
31 29
32 private val preferences: SharedPreferences
33 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
34
35 // Extension for altering settings list based on each setting's properties 30 // Extension for altering settings list based on each setting's properties
36 fun ArrayList<SettingsItem>.add(key: String) { 31 fun ArrayList<SettingsItem>.add(key: String) {
37 val item = SettingsItem.settingsItems[key]!! 32 val item = SettingsItem.settingsItems[key]!!
@@ -170,25 +165,19 @@ class SettingsFragmentPresenter(
170 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 165 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
171 sl.apply { 166 sl.apply {
172 val theme: AbstractIntSetting = object : AbstractIntSetting { 167 val theme: AbstractIntSetting = object : AbstractIntSetting {
173 override fun getInt(needsGlobal: Boolean): Int = 168 override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME.getInt()
174 preferences.getInt(Settings.PREF_THEME, 0)
175
176 override fun setInt(value: Int) { 169 override fun setInt(value: Int) {
177 preferences.edit() 170 IntSetting.THEME.setInt(value)
178 .putInt(Settings.PREF_THEME, value)
179 .apply()
180 settingsViewModel.setShouldRecreate(true) 171 settingsViewModel.setShouldRecreate(true)
181 } 172 }
182 173
183 override val key: String = Settings.PREF_THEME 174 override val key: String = IntSetting.THEME.key
184 override val isRuntimeModifiable: Boolean = false 175 override val isRuntimeModifiable: Boolean = IntSetting.THEME.isRuntimeModifiable
185 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() 176 override fun getValueAsString(needsGlobal: Boolean): String =
186 override val defaultValue: Int = 0 177 IntSetting.THEME.getValueAsString()
187 override fun reset() { 178
188 preferences.edit() 179 override val defaultValue: Int = IntSetting.THEME.defaultValue
189 .putInt(Settings.PREF_THEME, defaultValue) 180 override fun reset() = IntSetting.THEME.setInt(defaultValue)
190 .apply()
191 }
192 } 181 }
193 182
194 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 183 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -214,24 +203,22 @@ class SettingsFragmentPresenter(
214 } 203 }
215 204
216 val themeMode: AbstractIntSetting = object : AbstractIntSetting { 205 val themeMode: AbstractIntSetting = object : AbstractIntSetting {
217 override fun getInt(needsGlobal: Boolean): Int = 206 override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
218 preferences.getInt(Settings.PREF_THEME_MODE, -1)
219
220 override fun setInt(value: Int) { 207 override fun setInt(value: Int) {
221 preferences.edit() 208 IntSetting.THEME_MODE.setInt(value)
222 .putInt(Settings.PREF_THEME_MODE, value)
223 .apply()
224 settingsViewModel.setShouldRecreate(true) 209 settingsViewModel.setShouldRecreate(true)
225 } 210 }
226 211
227 override val key: String = Settings.PREF_THEME_MODE 212 override val key: String = IntSetting.THEME_MODE.key
228 override val isRuntimeModifiable: Boolean = false 213 override val isRuntimeModifiable: Boolean =
229 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() 214 IntSetting.THEME_MODE.isRuntimeModifiable
230 override val defaultValue: Int = -1 215
216 override fun getValueAsString(needsGlobal: Boolean): String =
217 IntSetting.THEME_MODE.getValueAsString()
218
219 override val defaultValue: Int = IntSetting.THEME_MODE.defaultValue
231 override fun reset() { 220 override fun reset() {
232 preferences.edit() 221 IntSetting.THEME_MODE.setInt(defaultValue)
233 .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
234 .apply()
235 settingsViewModel.setShouldRecreate(true) 222 settingsViewModel.setShouldRecreate(true)
236 } 223 }
237 } 224 }
@@ -248,25 +235,24 @@ class SettingsFragmentPresenter(
248 235
249 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { 236 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
250 override fun getBoolean(needsGlobal: Boolean): Boolean = 237 override fun getBoolean(needsGlobal: Boolean): Boolean =
251 preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 238 BooleanSetting.BLACK_BACKGROUNDS.getBoolean()
252 239
253 override fun setBoolean(value: Boolean) { 240 override fun setBoolean(value: Boolean) {
254 preferences.edit() 241 BooleanSetting.BLACK_BACKGROUNDS.setBoolean(value)
255 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
256 .apply()
257 settingsViewModel.setShouldRecreate(true) 242 settingsViewModel.setShouldRecreate(true)
258 } 243 }
259 244
260 override val key: String = Settings.PREF_BLACK_BACKGROUNDS 245 override val key: String = BooleanSetting.BLACK_BACKGROUNDS.key
261 override val isRuntimeModifiable: Boolean = false 246 override val isRuntimeModifiable: Boolean =
247 BooleanSetting.BLACK_BACKGROUNDS.isRuntimeModifiable
248
262 override fun getValueAsString(needsGlobal: Boolean): String = 249 override fun getValueAsString(needsGlobal: Boolean): String =
263 getBoolean().toString() 250 BooleanSetting.BLACK_BACKGROUNDS.getValueAsString()
264 251
265 override val defaultValue: Boolean = false 252 override val defaultValue: Boolean = BooleanSetting.BLACK_BACKGROUNDS.defaultValue
266 override fun reset() { 253 override fun reset() {
267 preferences.edit() 254 BooleanSetting.BLACK_BACKGROUNDS
268 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue) 255 .setBoolean(BooleanSetting.BLACK_BACKGROUNDS.defaultValue)
269 .apply()
270 settingsViewModel.setShouldRecreate(true) 256 settingsViewModel.setShouldRecreate(true)
271 } 257 }
272 } 258 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index d7b38f62d..510b2b5eb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -7,7 +7,6 @@ import android.annotation.SuppressLint
7import android.app.AlertDialog 7import android.app.AlertDialog
8import android.content.Context 8import android.content.Context
9import android.content.DialogInterface 9import android.content.DialogInterface
10import android.content.SharedPreferences
11import android.content.pm.ActivityInfo 10import android.content.pm.ActivityInfo
12import android.content.res.Configuration 11import android.content.res.Configuration
13import android.net.Uri 12import android.net.Uri
@@ -33,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
33import androidx.lifecycle.repeatOnLifecycle 32import androidx.lifecycle.repeatOnLifecycle
34import androidx.navigation.findNavController 33import androidx.navigation.findNavController
35import androidx.navigation.fragment.navArgs 34import androidx.navigation.fragment.navArgs
36import androidx.preference.PreferenceManager
37import androidx.window.layout.FoldingFeature 35import androidx.window.layout.FoldingFeature
38import androidx.window.layout.WindowInfoTracker 36import androidx.window.layout.WindowInfoTracker
39import androidx.window.layout.WindowLayoutInfo 37import androidx.window.layout.WindowLayoutInfo
@@ -46,22 +44,22 @@ import kotlinx.coroutines.launch
46import org.yuzu.yuzu_emu.HomeNavigationDirections 44import org.yuzu.yuzu_emu.HomeNavigationDirections
47import org.yuzu.yuzu_emu.NativeLibrary 45import org.yuzu.yuzu_emu.NativeLibrary
48import org.yuzu.yuzu_emu.R 46import org.yuzu.yuzu_emu.R
49import org.yuzu.yuzu_emu.YuzuApplication
50import org.yuzu.yuzu_emu.activities.EmulationActivity 47import org.yuzu.yuzu_emu.activities.EmulationActivity
51import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding 48import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
52import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding 49import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
50import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
53import org.yuzu.yuzu_emu.features.settings.model.IntSetting 51import org.yuzu.yuzu_emu.features.settings.model.IntSetting
54import org.yuzu.yuzu_emu.features.settings.model.Settings 52import org.yuzu.yuzu_emu.features.settings.model.Settings
55import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 53import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
56import org.yuzu.yuzu_emu.model.DriverViewModel 54import org.yuzu.yuzu_emu.model.DriverViewModel
57import org.yuzu.yuzu_emu.model.Game 55import org.yuzu.yuzu_emu.model.Game
58import org.yuzu.yuzu_emu.model.EmulationViewModel 56import org.yuzu.yuzu_emu.model.EmulationViewModel
59import org.yuzu.yuzu_emu.overlay.InputOverlay 57import org.yuzu.yuzu_emu.overlay.model.OverlayControl
58import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
60import org.yuzu.yuzu_emu.utils.* 59import org.yuzu.yuzu_emu.utils.*
61import java.lang.NullPointerException 60import java.lang.NullPointerException
62 61
63class EmulationFragment : Fragment(), SurfaceHolder.Callback { 62class EmulationFragment : Fragment(), SurfaceHolder.Callback {
64 private lateinit var preferences: SharedPreferences
65 private lateinit var emulationState: EmulationState 63 private lateinit var emulationState: EmulationState
66 private var emulationActivity: EmulationActivity? = null 64 private var emulationActivity: EmulationActivity? = null
67 private var perfStatsUpdater: (() -> Unit)? = null 65 private var perfStatsUpdater: (() -> Unit)? = null
@@ -141,7 +139,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
141 139
142 // So this fragment doesn't restart on configuration changes; i.e. rotation. 140 // So this fragment doesn't restart on configuration changes; i.e. rotation.
143 retainInstance = true 141 retainInstance = true
144 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
145 emulationState = EmulationState(game.path) 142 emulationState = EmulationState(game.path)
146 } 143 }
147 144
@@ -382,24 +379,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
382 } 379 }
383 380
384 updateScreenLayout() 381 updateScreenLayout()
382 val showInputOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
385 if (emulationActivity?.isInPictureInPictureMode == true) { 383 if (emulationActivity?.isInPictureInPictureMode == true) {
386 if (binding.drawerLayout.isOpen) { 384 if (binding.drawerLayout.isOpen) {
387 binding.drawerLayout.close() 385 binding.drawerLayout.close()
388 } 386 }
389 if (EmulationMenuSettings.showOverlay) { 387 if (showInputOverlay) {
390 binding.surfaceInputOverlay.visibility = View.INVISIBLE 388 binding.surfaceInputOverlay.visibility = View.INVISIBLE
391 } 389 }
392 } else { 390 } else {
393 if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) { 391 if (showInputOverlay && emulationViewModel.emulationStarted.value) {
394 binding.surfaceInputOverlay.visibility = View.VISIBLE 392 binding.surfaceInputOverlay.visibility = View.VISIBLE
395 } else { 393 } else {
396 binding.surfaceInputOverlay.visibility = View.INVISIBLE 394 binding.surfaceInputOverlay.visibility = View.INVISIBLE
397 } 395 }
398 if (!isInFoldableLayout) { 396 if (!isInFoldableLayout) {
399 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 397 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
400 binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT 398 binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
401 } else { 399 } else {
402 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE 400 binding.surfaceInputOverlay.layout = OverlayLayout.Landscape
403 } 401 }
404 } 402 }
405 } 403 }
@@ -423,17 +421,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
423 } 421 }
424 422
425 private fun resetInputOverlay() { 423 private fun resetInputOverlay() {
426 preferences.edit() 424 IntSetting.OVERLAY_SCALE.reset()
427 .remove(Settings.PREF_CONTROL_SCALE) 425 IntSetting.OVERLAY_OPACITY.reset()
428 .remove(Settings.PREF_CONTROL_OPACITY)
429 .apply()
430 binding.surfaceInputOverlay.post { 426 binding.surfaceInputOverlay.post {
431 binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement() 427 binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
432 } 428 }
433 } 429 }
434 430
435 private fun updateShowFpsOverlay() { 431 private fun updateShowFpsOverlay() {
436 if (EmulationMenuSettings.showFps) { 432 if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
437 val SYSTEM_FPS = 0 433 val SYSTEM_FPS = 0
438 val FPS = 1 434 val FPS = 1
439 val FRAMETIME = 2 435 val FRAMETIME = 2
@@ -496,7 +492,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
496 binding.inGameMenu.layoutParams.height = it.bounds.bottom 492 binding.inGameMenu.layoutParams.height = it.bounds.bottom
497 493
498 isInFoldableLayout = true 494 isInFoldableLayout = true
499 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE 495 binding.surfaceInputOverlay.layout = OverlayLayout.Foldable
500 } 496 }
501 } 497 }
502 it.isSeparating 498 it.isSeparating
@@ -535,18 +531,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
535 popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu) 531 popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
536 532
537 popup.menu.apply { 533 popup.menu.apply {
538 findItem(R.id.menu_toggle_fps).isChecked = EmulationMenuSettings.showFps 534 findItem(R.id.menu_toggle_fps).isChecked =
539 findItem(R.id.menu_rel_stick_center).isChecked = EmulationMenuSettings.joystickRelCenter 535 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
540 findItem(R.id.menu_dpad_slide).isChecked = EmulationMenuSettings.dpadSlide 536 findItem(R.id.menu_rel_stick_center).isChecked =
541 findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay 537 BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
542 findItem(R.id.menu_haptics).isChecked = EmulationMenuSettings.hapticFeedback 538 findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
539 findItem(R.id.menu_show_overlay).isChecked =
540 BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
541 findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
542 findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
543 } 543 }
544 544
545 popup.setOnMenuItemClickListener { 545 popup.setOnMenuItemClickListener {
546 when (it.itemId) { 546 when (it.itemId) {
547 R.id.menu_toggle_fps -> { 547 R.id.menu_toggle_fps -> {
548 it.isChecked = !it.isChecked 548 it.isChecked = !it.isChecked
549 EmulationMenuSettings.showFps = it.isChecked 549 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
550 updateShowFpsOverlay() 550 updateShowFpsOverlay()
551 true 551 true
552 } 552 }
@@ -564,11 +564,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
564 } 564 }
565 565
566 R.id.menu_toggle_controls -> { 566 R.id.menu_toggle_controls -> {
567 val preferences = 567 val overlayControlData = NativeConfig.getOverlayControlData()
568 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 568 val optionsArray = BooleanArray(overlayControlData.size)
569 val optionsArray = BooleanArray(Settings.overlayPreferences.size) 569 overlayControlData.forEachIndexed { i, _ ->
570 Settings.overlayPreferences.forEachIndexed { i, _ -> 570 optionsArray[i] = overlayControlData.firstOrNull { data ->
571 optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15) 571 OverlayControl.entries[i].id == data.id
572 }?.enabled == true
572 } 573 }
573 574
574 val dialog = MaterialAlertDialogBuilder(requireContext()) 575 val dialog = MaterialAlertDialogBuilder(requireContext())
@@ -577,11 +578,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
577 R.array.gamepadButtons, 578 R.array.gamepadButtons,
578 optionsArray 579 optionsArray
579 ) { _, indexSelected, isChecked -> 580 ) { _, indexSelected, isChecked ->
580 preferences.edit() 581 overlayControlData.firstOrNull { data ->
581 .putBoolean("buttonToggle$indexSelected", isChecked) 582 OverlayControl.entries[indexSelected].id == data.id
582 .apply() 583 }?.enabled = isChecked
583 } 584 }
584 .setPositiveButton(android.R.string.ok) { _, _ -> 585 .setPositiveButton(android.R.string.ok) { _, _ ->
586 NativeConfig.setOverlayControlData(overlayControlData)
587 NativeConfig.saveGlobalConfig()
585 binding.surfaceInputOverlay.refreshControls() 588 binding.surfaceInputOverlay.refreshControls()
586 } 589 }
587 .setNegativeButton(android.R.string.cancel, null) 590 .setNegativeButton(android.R.string.cancel, null)
@@ -592,12 +595,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
592 dialog.getButton(AlertDialog.BUTTON_NEUTRAL) 595 dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
593 .setOnClickListener { 596 .setOnClickListener {
594 val isChecked = !optionsArray[0] 597 val isChecked = !optionsArray[0]
595 Settings.overlayPreferences.forEachIndexed { i, _ -> 598 overlayControlData.forEachIndexed { i, _ ->
596 optionsArray[i] = isChecked 599 optionsArray[i] = isChecked
597 dialog.listView.setItemChecked(i, isChecked) 600 dialog.listView.setItemChecked(i, isChecked)
598 preferences.edit() 601 overlayControlData[i].enabled = isChecked
599 .putBoolean("buttonToggle$i", isChecked)
600 .apply()
601 } 602 }
602 } 603 }
603 true 604 true
@@ -605,26 +606,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
605 606
606 R.id.menu_show_overlay -> { 607 R.id.menu_show_overlay -> {
607 it.isChecked = !it.isChecked 608 it.isChecked = !it.isChecked
608 EmulationMenuSettings.showOverlay = it.isChecked 609 BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
609 binding.surfaceInputOverlay.refreshControls() 610 binding.surfaceInputOverlay.refreshControls()
610 true 611 true
611 } 612 }
612 613
613 R.id.menu_rel_stick_center -> { 614 R.id.menu_rel_stick_center -> {
614 it.isChecked = !it.isChecked 615 it.isChecked = !it.isChecked
615 EmulationMenuSettings.joystickRelCenter = it.isChecked 616 BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(it.isChecked)
616 true 617 true
617 } 618 }
618 619
619 R.id.menu_dpad_slide -> { 620 R.id.menu_dpad_slide -> {
620 it.isChecked = !it.isChecked 621 it.isChecked = !it.isChecked
621 EmulationMenuSettings.dpadSlide = it.isChecked 622 BooleanSetting.DPAD_SLIDE.setBoolean(it.isChecked)
622 true 623 true
623 } 624 }
624 625
625 R.id.menu_haptics -> { 626 R.id.menu_haptics -> {
626 it.isChecked = !it.isChecked 627 it.isChecked = !it.isChecked
627 EmulationMenuSettings.hapticFeedback = it.isChecked 628 BooleanSetting.HAPTIC_FEEDBACK.setBoolean(it.isChecked)
629 true
630 }
631
632 R.id.menu_touchscreen -> {
633 it.isChecked = !it.isChecked
634 BooleanSetting.TOUCHSCREEN.setBoolean(it.isChecked)
628 true 635 true
629 } 636 }
630 637
@@ -667,6 +674,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
667 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 674 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
668 } 675 }
669 } 676 }
677 NativeConfig.saveGlobalConfig()
670 } 678 }
671 679
672 @SuppressLint("SetTextI18n") 680 @SuppressLint("SetTextI18n")
@@ -675,7 +683,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
675 adjustBinding.apply { 683 adjustBinding.apply {
676 inputScaleSlider.apply { 684 inputScaleSlider.apply {
677 valueTo = 150F 685 valueTo = 150F
678 value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() 686 value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
679 addOnChangeListener( 687 addOnChangeListener(
680 Slider.OnChangeListener { _, value, _ -> 688 Slider.OnChangeListener { _, value, _ ->
681 inputScaleValue.text = "${value.toInt()}%" 689 inputScaleValue.text = "${value.toInt()}%"
@@ -685,7 +693,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
685 } 693 }
686 inputOpacitySlider.apply { 694 inputOpacitySlider.apply {
687 valueTo = 100F 695 valueTo = 100F
688 value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat() 696 value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
689 addOnChangeListener( 697 addOnChangeListener(
690 Slider.OnChangeListener { _, value, _ -> 698 Slider.OnChangeListener { _, value, _ ->
691 inputOpacityValue.text = "${value.toInt()}%" 699 inputOpacityValue.text = "${value.toInt()}%"
@@ -709,16 +717,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
709 } 717 }
710 718
711 private fun setControlScale(scale: Int) { 719 private fun setControlScale(scale: Int) {
712 preferences.edit() 720 IntSetting.OVERLAY_SCALE.setInt(scale)
713 .putInt(Settings.PREF_CONTROL_SCALE, scale)
714 .apply()
715 binding.surfaceInputOverlay.refreshControls() 721 binding.surfaceInputOverlay.refreshControls()
716 } 722 }
717 723
718 private fun setControlOpacity(opacity: Int) { 724 private fun setControlOpacity(opacity: Int) {
719 preferences.edit() 725 IntSetting.OVERLAY_OPACITY.setInt(opacity)
720 .putInt(Settings.PREF_CONTROL_OPACITY, opacity)
721 .apply()
722 binding.surfaceInputOverlay.refreshControls() 726 binding.surfaceInputOverlay.refreshControls()
723 } 727 }
724 728
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index a13faf3c7..bb69b8bd5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -21,7 +21,6 @@ import android.view.View
21import android.view.View.OnTouchListener 21import android.view.View.OnTouchListener
22import android.view.WindowInsets 22import android.view.WindowInsets
23import androidx.core.content.ContextCompat 23import androidx.core.content.ContextCompat
24import androidx.preference.PreferenceManager
25import androidx.window.layout.WindowMetricsCalculator 24import androidx.window.layout.WindowMetricsCalculator
26import kotlin.math.max 25import kotlin.math.max
27import kotlin.math.min 26import kotlin.math.min
@@ -29,9 +28,13 @@ import org.yuzu.yuzu_emu.NativeLibrary
29import org.yuzu.yuzu_emu.NativeLibrary.ButtonType 28import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
30import org.yuzu.yuzu_emu.NativeLibrary.StickType 29import org.yuzu.yuzu_emu.NativeLibrary.StickType
31import org.yuzu.yuzu_emu.R 30import org.yuzu.yuzu_emu.R
32import org.yuzu.yuzu_emu.YuzuApplication 31import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
32import org.yuzu.yuzu_emu.features.settings.model.IntSetting
33import org.yuzu.yuzu_emu.features.settings.model.Settings 33import org.yuzu.yuzu_emu.features.settings.model.Settings
34import org.yuzu.yuzu_emu.utils.EmulationMenuSettings 34import org.yuzu.yuzu_emu.overlay.model.OverlayControl
35import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
36import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
37import org.yuzu.yuzu_emu.utils.NativeConfig
35 38
36/** 39/**
37 * Draws the interactive input overlay on top of the 40 * Draws the interactive input overlay on top of the
@@ -51,23 +54,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
51 54
52 private lateinit var windowInsets: WindowInsets 55 private lateinit var windowInsets: WindowInsets
53 56
54 var layout = LANDSCAPE 57 var layout = OverlayLayout.Landscape
55 58
56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 59 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
57 super.onLayout(changed, left, top, right, bottom) 60 super.onLayout(changed, left, top, right, bottom)
58 61
59 windowInsets = rootWindowInsets 62 windowInsets = rootWindowInsets
60 63
61 val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0) 64 val overlayControlData = NativeConfig.getOverlayControlData()
62 if (overlayVersion != OVERLAY_VERSION) { 65 if (overlayControlData.isEmpty()) {
63 resetAllLayouts() 66 populateDefaultConfig()
64 } else { 67 } else {
65 val layoutIndex = overlayLayouts.indexOf(layout) 68 checkForNewControls(overlayControlData)
66 val currentLayoutVersion =
67 preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
68 if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
69 resetCurrentLayout()
70 }
71 } 69 }
72 70
73 // Load the controls. 71 // Load the controls.
@@ -123,7 +121,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
123 } 121 }
124 122
125 for (dpad in overlayDpads) { 123 for (dpad in overlayDpads) {
126 if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide)) { 124 if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) {
127 continue 125 continue
128 } 126 }
129 NativeLibrary.onGamePadButtonEvent( 127 NativeLibrary.onGamePadButtonEvent(
@@ -174,7 +172,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
174 invalidate() 172 invalidate()
175 } 173 }
176 174
177 if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) { 175 if (!BooleanSetting.TOUCHSCREEN.getBoolean()) {
178 return true 176 return true
179 } 177 }
180 178
@@ -211,7 +209,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
211 } 209 }
212 210
213 private fun playHaptics(event: MotionEvent) { 211 private fun playHaptics(event: MotionEvent) {
214 if (EmulationMenuSettings.hapticFeedback) { 212 if (BooleanSetting.HAPTIC_FEEDBACK.getBoolean()) {
215 when (event.actionMasked) { 213 when (event.actionMasked) {
216 MotionEvent.ACTION_DOWN, 214 MotionEvent.ACTION_DOWN,
217 MotionEvent.ACTION_POINTER_DOWN -> 215 MotionEvent.ACTION_POINTER_DOWN ->
@@ -255,10 +253,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
255 MotionEvent.ACTION_POINTER_DOWN -> 253 MotionEvent.ACTION_POINTER_DOWN ->
256 // If no button is being moved now, remember the currently touched button to move. 254 // If no button is being moved now, remember the currently touched button to move.
257 if (buttonBeingConfigured == null && 255 if (buttonBeingConfigured == null &&
258 button.bounds.contains( 256 button.bounds.contains(fingerPositionX, fingerPositionY)
259 fingerPositionX,
260 fingerPositionY
261 )
262 ) { 257 ) {
263 buttonBeingConfigured = button 258 buttonBeingConfigured = button
264 buttonBeingConfigured!!.onConfigureTouch(event) 259 buttonBeingConfigured!!.onConfigureTouch(event)
@@ -274,7 +269,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
274 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { 269 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
275 // Persist button position by saving new place. 270 // Persist button position by saving new place.
276 saveControlPosition( 271 saveControlPosition(
277 buttonBeingConfigured!!.prefId, 272 buttonBeingConfigured!!.overlayControlData.id,
278 buttonBeingConfigured!!.bounds.centerX(), 273 buttonBeingConfigured!!.bounds.centerX(),
279 buttonBeingConfigured!!.bounds.centerY(), 274 buttonBeingConfigured!!.bounds.centerY(),
280 layout 275 layout
@@ -321,10 +316,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
321 when (event.action) { 316 when (event.action) {
322 MotionEvent.ACTION_DOWN, 317 MotionEvent.ACTION_DOWN,
323 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null && 318 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
324 joystick.bounds.contains( 319 joystick.bounds.contains(fingerPositionX, fingerPositionY)
325 fingerPositionX,
326 fingerPositionY
327 )
328 ) { 320 ) {
329 joystickBeingConfigured = joystick 321 joystickBeingConfigured = joystick
330 joystickBeingConfigured!!.onConfigureTouch(event) 322 joystickBeingConfigured!!.onConfigureTouch(event)
@@ -351,231 +343,257 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
351 return true 343 return true
352 } 344 }
353 345
354 private fun addOverlayControls(layout: String) { 346 private fun addOverlayControls(layout: OverlayLayout) {
355 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight)) 347 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
356 if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) { 348 val overlayControlData = NativeConfig.getOverlayControlData()
357 overlayButtons.add( 349 for (data in overlayControlData) {
358 initializeOverlayButton( 350 if (!data.enabled) {
359 context, 351 continue
360 windowSize, 352 }
361 R.drawable.facebutton_a, 353
362 R.drawable.facebutton_a_depressed, 354 val position = data.positionFromLayout(layout)
363 ButtonType.BUTTON_A, 355 when (data.id) {
364 Settings.PREF_BUTTON_A, 356 OverlayControl.BUTTON_A.id -> {
365 layout 357 overlayButtons.add(
366 ) 358 initializeOverlayButton(
367 ) 359 context,
368 } 360 windowSize,
369 if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) { 361 R.drawable.facebutton_a,
370 overlayButtons.add( 362 R.drawable.facebutton_a_depressed,
371 initializeOverlayButton( 363 ButtonType.BUTTON_A,
372 context, 364 data,
373 windowSize, 365 position
374 R.drawable.facebutton_b, 366 )
375 R.drawable.facebutton_b_depressed, 367 )
376 ButtonType.BUTTON_B, 368 }
377 Settings.PREF_BUTTON_B, 369
378 layout 370 OverlayControl.BUTTON_B.id -> {
379 ) 371 overlayButtons.add(
380 ) 372 initializeOverlayButton(
381 } 373 context,
382 if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) { 374 windowSize,
383 overlayButtons.add( 375 R.drawable.facebutton_b,
384 initializeOverlayButton( 376 R.drawable.facebutton_b_depressed,
385 context, 377 ButtonType.BUTTON_B,
386 windowSize, 378 data,
387 R.drawable.facebutton_x, 379 position
388 R.drawable.facebutton_x_depressed, 380 )
389 ButtonType.BUTTON_X, 381 )
390 Settings.PREF_BUTTON_X, 382 }
391 layout 383
392 ) 384 OverlayControl.BUTTON_X.id -> {
393 ) 385 overlayButtons.add(
394 } 386 initializeOverlayButton(
395 if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) { 387 context,
396 overlayButtons.add( 388 windowSize,
397 initializeOverlayButton( 389 R.drawable.facebutton_x,
398 context, 390 R.drawable.facebutton_x_depressed,
399 windowSize, 391 ButtonType.BUTTON_X,
400 R.drawable.facebutton_y, 392 data,
401 R.drawable.facebutton_y_depressed, 393 position
402 ButtonType.BUTTON_Y, 394 )
403 Settings.PREF_BUTTON_Y, 395 )
404 layout 396 }
405 ) 397
406 ) 398 OverlayControl.BUTTON_Y.id -> {
407 } 399 overlayButtons.add(
408 if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) { 400 initializeOverlayButton(
409 overlayButtons.add( 401 context,
410 initializeOverlayButton( 402 windowSize,
411 context, 403 R.drawable.facebutton_y,
412 windowSize, 404 R.drawable.facebutton_y_depressed,
413 R.drawable.l_shoulder, 405 ButtonType.BUTTON_Y,
414 R.drawable.l_shoulder_depressed, 406 data,
415 ButtonType.TRIGGER_L, 407 position
416 Settings.PREF_BUTTON_L, 408 )
417 layout 409 )
418 ) 410 }
419 ) 411
420 } 412 OverlayControl.BUTTON_PLUS.id -> {
421 if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) { 413 overlayButtons.add(
422 overlayButtons.add( 414 initializeOverlayButton(
423 initializeOverlayButton( 415 context,
424 context, 416 windowSize,
425 windowSize, 417 R.drawable.facebutton_plus,
426 R.drawable.r_shoulder, 418 R.drawable.facebutton_plus_depressed,
427 R.drawable.r_shoulder_depressed, 419 ButtonType.BUTTON_PLUS,
428 ButtonType.TRIGGER_R, 420 data,
429 Settings.PREF_BUTTON_R, 421 position
430 layout 422 )
431 ) 423 )
432 ) 424 }
433 } 425
434 if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) { 426 OverlayControl.BUTTON_MINUS.id -> {
435 overlayButtons.add( 427 overlayButtons.add(
436 initializeOverlayButton( 428 initializeOverlayButton(
437 context, 429 context,
438 windowSize, 430 windowSize,
439 R.drawable.zl_trigger, 431 R.drawable.facebutton_minus,
440 R.drawable.zl_trigger_depressed, 432 R.drawable.facebutton_minus_depressed,
441 ButtonType.TRIGGER_ZL, 433 ButtonType.BUTTON_MINUS,
442 Settings.PREF_BUTTON_ZL, 434 data,
443 layout 435 position
444 ) 436 )
445 ) 437 )
446 } 438 }
447 if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) { 439
448 overlayButtons.add( 440 OverlayControl.BUTTON_HOME.id -> {
449 initializeOverlayButton( 441 overlayButtons.add(
450 context, 442 initializeOverlayButton(
451 windowSize, 443 context,
452 R.drawable.zr_trigger, 444 windowSize,
453 R.drawable.zr_trigger_depressed, 445 R.drawable.facebutton_home,
454 ButtonType.TRIGGER_ZR, 446 R.drawable.facebutton_home_depressed,
455 Settings.PREF_BUTTON_ZR, 447 ButtonType.BUTTON_HOME,
456 layout 448 data,
457 ) 449 position
458 ) 450 )
459 } 451 )
460 if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) { 452 }
461 overlayButtons.add( 453
462 initializeOverlayButton( 454 OverlayControl.BUTTON_CAPTURE.id -> {
463 context, 455 overlayButtons.add(
464 windowSize, 456 initializeOverlayButton(
465 R.drawable.facebutton_plus, 457 context,
466 R.drawable.facebutton_plus_depressed, 458 windowSize,
467 ButtonType.BUTTON_PLUS, 459 R.drawable.facebutton_screenshot,
468 Settings.PREF_BUTTON_PLUS, 460 R.drawable.facebutton_screenshot_depressed,
469 layout 461 ButtonType.BUTTON_CAPTURE,
470 ) 462 data,
471 ) 463 position
472 } 464 )
473 if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) { 465 )
474 overlayButtons.add( 466 }
475 initializeOverlayButton( 467
476 context, 468 OverlayControl.BUTTON_L.id -> {
477 windowSize, 469 overlayButtons.add(
478 R.drawable.facebutton_minus, 470 initializeOverlayButton(
479 R.drawable.facebutton_minus_depressed, 471 context,
480 ButtonType.BUTTON_MINUS, 472 windowSize,
481 Settings.PREF_BUTTON_MINUS, 473 R.drawable.l_shoulder,
482 layout 474 R.drawable.l_shoulder_depressed,
483 ) 475 ButtonType.TRIGGER_L,
484 ) 476 data,
485 } 477 position
486 if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) { 478 )
487 overlayDpads.add( 479 )
488 initializeOverlayDpad( 480 }
489 context, 481
490 windowSize, 482 OverlayControl.BUTTON_R.id -> {
491 R.drawable.dpad_standard, 483 overlayButtons.add(
492 R.drawable.dpad_standard_cardinal_depressed, 484 initializeOverlayButton(
493 R.drawable.dpad_standard_diagonal_depressed, 485 context,
494 layout 486 windowSize,
495 ) 487 R.drawable.r_shoulder,
496 ) 488 R.drawable.r_shoulder_depressed,
497 } 489 ButtonType.TRIGGER_R,
498 if (preferences.getBoolean(Settings.PREF_STICK_L, true)) { 490 data,
499 overlayJoysticks.add( 491 position
500 initializeOverlayJoystick( 492 )
501 context, 493 )
502 windowSize, 494 }
503 R.drawable.joystick_range, 495
504 R.drawable.joystick, 496 OverlayControl.BUTTON_ZL.id -> {
505 R.drawable.joystick_depressed, 497 overlayButtons.add(
506 StickType.STICK_L, 498 initializeOverlayButton(
507 ButtonType.STICK_L, 499 context,
508 Settings.PREF_STICK_L, 500 windowSize,
509 layout 501 R.drawable.zl_trigger,
510 ) 502 R.drawable.zl_trigger_depressed,
511 ) 503 ButtonType.TRIGGER_ZL,
512 } 504 data,
513 if (preferences.getBoolean(Settings.PREF_STICK_R, true)) { 505 position
514 overlayJoysticks.add( 506 )
515 initializeOverlayJoystick( 507 )
516 context, 508 }
517 windowSize, 509
518 R.drawable.joystick_range, 510 OverlayControl.BUTTON_ZR.id -> {
519 R.drawable.joystick, 511 overlayButtons.add(
520 R.drawable.joystick_depressed, 512 initializeOverlayButton(
521 StickType.STICK_R, 513 context,
522 ButtonType.STICK_R, 514 windowSize,
523 Settings.PREF_STICK_R, 515 R.drawable.zr_trigger,
524 layout 516 R.drawable.zr_trigger_depressed,
525 ) 517 ButtonType.TRIGGER_ZR,
526 ) 518 data,
527 } 519 position
528 if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) { 520 )
529 overlayButtons.add( 521 )
530 initializeOverlayButton( 522 }
531 context, 523
532 windowSize, 524 OverlayControl.BUTTON_STICK_L.id -> {
533 R.drawable.facebutton_home, 525 overlayButtons.add(
534 R.drawable.facebutton_home_depressed, 526 initializeOverlayButton(
535 ButtonType.BUTTON_HOME, 527 context,
536 Settings.PREF_BUTTON_HOME, 528 windowSize,
537 layout 529 R.drawable.button_l3,
538 ) 530 R.drawable.button_l3_depressed,
539 ) 531 ButtonType.STICK_L,
540 } 532 data,
541 if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) { 533 position
542 overlayButtons.add( 534 )
543 initializeOverlayButton( 535 )
544 context, 536 }
545 windowSize, 537
546 R.drawable.facebutton_screenshot, 538 OverlayControl.BUTTON_STICK_R.id -> {
547 R.drawable.facebutton_screenshot_depressed, 539 overlayButtons.add(
548 ButtonType.BUTTON_CAPTURE, 540 initializeOverlayButton(
549 Settings.PREF_BUTTON_SCREENSHOT, 541 context,
550 layout 542 windowSize,
551 ) 543 R.drawable.button_r3,
552 ) 544 R.drawable.button_r3_depressed,
553 } 545 ButtonType.STICK_R,
554 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) { 546 data,
555 overlayButtons.add( 547 position
556 initializeOverlayButton( 548 )
557 context, 549 )
558 windowSize, 550 }
559 R.drawable.button_l3, 551
560 R.drawable.button_l3_depressed, 552 OverlayControl.STICK_L.id -> {
561 ButtonType.STICK_L, 553 overlayJoysticks.add(
562 Settings.PREF_BUTTON_STICK_L, 554 initializeOverlayJoystick(
563 layout 555 context,
564 ) 556 windowSize,
565 ) 557 R.drawable.joystick_range,
566 } 558 R.drawable.joystick,
567 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) { 559 R.drawable.joystick_depressed,
568 overlayButtons.add( 560 StickType.STICK_L,
569 initializeOverlayButton( 561 ButtonType.STICK_L,
570 context, 562 data,
571 windowSize, 563 position
572 R.drawable.button_r3, 564 )
573 R.drawable.button_r3_depressed, 565 )
574 ButtonType.STICK_R, 566 }
575 Settings.PREF_BUTTON_STICK_R, 567
576 layout 568 OverlayControl.STICK_R.id -> {
577 ) 569 overlayJoysticks.add(
578 ) 570 initializeOverlayJoystick(
571 context,
572 windowSize,
573 R.drawable.joystick_range,
574 R.drawable.joystick,
575 R.drawable.joystick_depressed,
576 StickType.STICK_R,
577 ButtonType.STICK_R,
578 data,
579 position
580 )
581 )
582 }
583
584 OverlayControl.COMBINED_DPAD.id -> {
585 overlayDpads.add(
586 initializeOverlayDpad(
587 context,
588 windowSize,
589 R.drawable.dpad_standard,
590 R.drawable.dpad_standard_cardinal_depressed,
591 R.drawable.dpad_standard_diagonal_depressed,
592 position
593 )
594 )
595 }
596 }
579 } 597 }
580 } 598 }
581 599
@@ -586,313 +604,87 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
586 overlayJoysticks.clear() 604 overlayJoysticks.clear()
587 605
588 // Add all the enabled overlay items back to the HashSet. 606 // Add all the enabled overlay items back to the HashSet.
589 if (EmulationMenuSettings.showOverlay) { 607 if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
590 addOverlayControls(layout) 608 addOverlayControls(layout)
591 } 609 }
592 invalidate() 610 invalidate()
593 } 611 }
594 612
595 private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) { 613 private fun saveControlPosition(id: String, x: Int, y: Int, layout: OverlayLayout) {
596 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight)) 614 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
597 val min = windowSize.first 615 val min = windowSize.first
598 val max = windowSize.second 616 val max = windowSize.second
599 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 617 val overlayControlData = NativeConfig.getOverlayControlData()
600 .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x) 618 val data = overlayControlData.firstOrNull { it.id == id }
601 .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y) 619 val newPosition = Pair((x - min.x).toDouble() / max.x, (y - min.y).toDouble() / max.y)
602 .apply() 620 when (layout) {
621 OverlayLayout.Landscape -> data?.landscapePosition = newPosition
622 OverlayLayout.Portrait -> data?.portraitPosition = newPosition
623 OverlayLayout.Foldable -> data?.foldablePosition = newPosition
624 }
625 NativeConfig.setOverlayControlData(overlayControlData)
603 } 626 }
604 627
605 fun setIsInEditMode(editMode: Boolean) { 628 fun setIsInEditMode(editMode: Boolean) {
606 inEditMode = editMode 629 inEditMode = editMode
607 } 630 }
608 631
609 private fun resetCurrentLayout() { 632 /**
610 defaultOverlayByLayout(layout) 633 * Applies and saves all default values for the overlay
611 val layoutIndex = overlayLayouts.indexOf(layout) 634 */
612 preferences.edit() 635 private fun populateDefaultConfig() {
613 .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex]) 636 val newConfig = OverlayControl.entries.map { it.toOverlayControlData() }
614 .apply() 637 NativeConfig.setOverlayControlData(newConfig.toTypedArray())
638 NativeConfig.saveGlobalConfig()
615 } 639 }
616 640
617 private fun resetAllLayouts() { 641 /**
618 val editor = preferences.edit() 642 * Checks if any new controls were added to OverlayControl that do not exist within deserialized
619 overlayLayouts.forEachIndexed { i, layout -> 643 * config and adds / saves them if necessary
620 defaultOverlayByLayout(layout) 644 *
621 editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i]) 645 * @param overlayControlData Overlay control data from [NativeConfig.getOverlayControlData]
646 */
647 private fun checkForNewControls(overlayControlData: Array<OverlayControlData>) {
648 val missingControls = mutableListOf<OverlayControlData>()
649 OverlayControl.entries.forEach { defaultControl ->
650 val controlData = overlayControlData.firstOrNull { it.id == defaultControl.id }
651 if (controlData == null) {
652 missingControls.add(defaultControl.toOverlayControlData())
653 }
654 }
655
656 if (missingControls.isNotEmpty()) {
657 NativeConfig.setOverlayControlData(
658 arrayOf(*overlayControlData, *(missingControls.toTypedArray()))
659 )
660 NativeConfig.saveGlobalConfig()
622 } 661 }
623 editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
624 editor.apply()
625 } 662 }
626 663
627 fun resetLayoutVisibilityAndPlacement() { 664 fun resetLayoutVisibilityAndPlacement() {
628 defaultOverlayByLayout(layout) 665 defaultOverlayPositionByLayout(layout)
629 val editor = preferences.edit() 666
630 Settings.overlayPreferences.forEachIndexed { _, pref -> 667 val overlayControlData = NativeConfig.getOverlayControlData()
631 editor.remove(pref) 668 overlayControlData.forEach {
669 it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
632 } 670 }
633 editor.apply() 671 NativeConfig.setOverlayControlData(overlayControlData)
672
634 refreshControls() 673 refreshControls()
635 } 674 }
636 675
637 private val landscapeResources = arrayOf( 676 private fun defaultOverlayPositionByLayout(layout: OverlayLayout) {
638 R.integer.SWITCH_BUTTON_A_X, 677 val overlayControlData = NativeConfig.getOverlayControlData()
639 R.integer.SWITCH_BUTTON_A_Y, 678 for (data in overlayControlData) {
640 R.integer.SWITCH_BUTTON_B_X, 679 val defaultControlData = OverlayControl.from(data.id) ?: continue
641 R.integer.SWITCH_BUTTON_B_Y, 680 val position = defaultControlData.getDefaultPositionForLayout(layout)
642 R.integer.SWITCH_BUTTON_X_X, 681 when (layout) {
643 R.integer.SWITCH_BUTTON_X_Y, 682 OverlayLayout.Landscape -> data.landscapePosition = position
644 R.integer.SWITCH_BUTTON_Y_X, 683 OverlayLayout.Portrait -> data.portraitPosition = position
645 R.integer.SWITCH_BUTTON_Y_Y, 684 OverlayLayout.Foldable -> data.foldablePosition = position
646 R.integer.SWITCH_TRIGGER_ZL_X, 685 }
647 R.integer.SWITCH_TRIGGER_ZL_Y,
648 R.integer.SWITCH_TRIGGER_ZR_X,
649 R.integer.SWITCH_TRIGGER_ZR_Y,
650 R.integer.SWITCH_BUTTON_DPAD_X,
651 R.integer.SWITCH_BUTTON_DPAD_Y,
652 R.integer.SWITCH_TRIGGER_L_X,
653 R.integer.SWITCH_TRIGGER_L_Y,
654 R.integer.SWITCH_TRIGGER_R_X,
655 R.integer.SWITCH_TRIGGER_R_Y,
656 R.integer.SWITCH_BUTTON_PLUS_X,
657 R.integer.SWITCH_BUTTON_PLUS_Y,
658 R.integer.SWITCH_BUTTON_MINUS_X,
659 R.integer.SWITCH_BUTTON_MINUS_Y,
660 R.integer.SWITCH_BUTTON_HOME_X,
661 R.integer.SWITCH_BUTTON_HOME_Y,
662 R.integer.SWITCH_BUTTON_CAPTURE_X,
663 R.integer.SWITCH_BUTTON_CAPTURE_Y,
664 R.integer.SWITCH_STICK_R_X,
665 R.integer.SWITCH_STICK_R_Y,
666 R.integer.SWITCH_STICK_L_X,
667 R.integer.SWITCH_STICK_L_Y,
668 R.integer.SWITCH_BUTTON_STICK_L_X,
669 R.integer.SWITCH_BUTTON_STICK_L_Y,
670 R.integer.SWITCH_BUTTON_STICK_R_X,
671 R.integer.SWITCH_BUTTON_STICK_R_Y
672 )
673
674 private val portraitResources = arrayOf(
675 R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
676 R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
677 R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
678 R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
679 R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
680 R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
681 R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
682 R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
683 R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
684 R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
685 R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
686 R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
687 R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
688 R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
689 R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
690 R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
691 R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
692 R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
693 R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
694 R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
695 R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
696 R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
697 R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
698 R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
699 R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
700 R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
701 R.integer.SWITCH_STICK_R_X_PORTRAIT,
702 R.integer.SWITCH_STICK_R_Y_PORTRAIT,
703 R.integer.SWITCH_STICK_L_X_PORTRAIT,
704 R.integer.SWITCH_STICK_L_Y_PORTRAIT,
705 R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT,
706 R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT,
707 R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT,
708 R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT
709 )
710
711 private val foldableResources = arrayOf(
712 R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
713 R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
714 R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
715 R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
716 R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
717 R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
718 R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
719 R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
720 R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
721 R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
722 R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
723 R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
724 R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
725 R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
726 R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
727 R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
728 R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
729 R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
730 R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
731 R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
732 R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
733 R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
734 R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
735 R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
736 R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
737 R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
738 R.integer.SWITCH_STICK_R_X_FOLDABLE,
739 R.integer.SWITCH_STICK_R_Y_FOLDABLE,
740 R.integer.SWITCH_STICK_L_X_FOLDABLE,
741 R.integer.SWITCH_STICK_L_Y_FOLDABLE,
742 R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE,
743 R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE,
744 R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE,
745 R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE
746 )
747
748 private fun getResourceValue(layout: String, position: Int): Float {
749 return when (layout) {
750 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
751 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
752 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
753 } 686 }
754 } 687 NativeConfig.setOverlayControlData(overlayControlData)
755
756 private fun defaultOverlayByLayout(layout: String) {
757 // Each value represents the position of the button in relation to the screen size without insets.
758 preferences.edit()
759 .putFloat(
760 "${Settings.PREF_BUTTON_A}-X$layout",
761 getResourceValue(layout, 0)
762 )
763 .putFloat(
764 "${Settings.PREF_BUTTON_A}-Y$layout",
765 getResourceValue(layout, 1)
766 )
767 .putFloat(
768 "${Settings.PREF_BUTTON_B}-X$layout",
769 getResourceValue(layout, 2)
770 )
771 .putFloat(
772 "${Settings.PREF_BUTTON_B}-Y$layout",
773 getResourceValue(layout, 3)
774 )
775 .putFloat(
776 "${Settings.PREF_BUTTON_X}-X$layout",
777 getResourceValue(layout, 4)
778 )
779 .putFloat(
780 "${Settings.PREF_BUTTON_X}-Y$layout",
781 getResourceValue(layout, 5)
782 )
783 .putFloat(
784 "${Settings.PREF_BUTTON_Y}-X$layout",
785 getResourceValue(layout, 6)
786 )
787 .putFloat(
788 "${Settings.PREF_BUTTON_Y}-Y$layout",
789 getResourceValue(layout, 7)
790 )
791 .putFloat(
792 "${Settings.PREF_BUTTON_ZL}-X$layout",
793 getResourceValue(layout, 8)
794 )
795 .putFloat(
796 "${Settings.PREF_BUTTON_ZL}-Y$layout",
797 getResourceValue(layout, 9)
798 )
799 .putFloat(
800 "${Settings.PREF_BUTTON_ZR}-X$layout",
801 getResourceValue(layout, 10)
802 )
803 .putFloat(
804 "${Settings.PREF_BUTTON_ZR}-Y$layout",
805 getResourceValue(layout, 11)
806 )
807 .putFloat(
808 "${Settings.PREF_BUTTON_DPAD}-X$layout",
809 getResourceValue(layout, 12)
810 )
811 .putFloat(
812 "${Settings.PREF_BUTTON_DPAD}-Y$layout",
813 getResourceValue(layout, 13)
814 )
815 .putFloat(
816 "${Settings.PREF_BUTTON_L}-X$layout",
817 getResourceValue(layout, 14)
818 )
819 .putFloat(
820 "${Settings.PREF_BUTTON_L}-Y$layout",
821 getResourceValue(layout, 15)
822 )
823 .putFloat(
824 "${Settings.PREF_BUTTON_R}-X$layout",
825 getResourceValue(layout, 16)
826 )
827 .putFloat(
828 "${Settings.PREF_BUTTON_R}-Y$layout",
829 getResourceValue(layout, 17)
830 )
831 .putFloat(
832 "${Settings.PREF_BUTTON_PLUS}-X$layout",
833 getResourceValue(layout, 18)
834 )
835 .putFloat(
836 "${Settings.PREF_BUTTON_PLUS}-Y$layout",
837 getResourceValue(layout, 19)
838 )
839 .putFloat(
840 "${Settings.PREF_BUTTON_MINUS}-X$layout",
841 getResourceValue(layout, 20)
842 )
843 .putFloat(
844 "${Settings.PREF_BUTTON_MINUS}-Y$layout",
845 getResourceValue(layout, 21)
846 )
847 .putFloat(
848 "${Settings.PREF_BUTTON_HOME}-X$layout",
849 getResourceValue(layout, 22)
850 )
851 .putFloat(
852 "${Settings.PREF_BUTTON_HOME}-Y$layout",
853 getResourceValue(layout, 23)
854 )
855 .putFloat(
856 "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
857 getResourceValue(layout, 24)
858 )
859 .putFloat(
860 "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
861 getResourceValue(layout, 25)
862 )
863 .putFloat(
864 "${Settings.PREF_STICK_R}-X$layout",
865 getResourceValue(layout, 26)
866 )
867 .putFloat(
868 "${Settings.PREF_STICK_R}-Y$layout",
869 getResourceValue(layout, 27)
870 )
871 .putFloat(
872 "${Settings.PREF_STICK_L}-X$layout",
873 getResourceValue(layout, 28)
874 )
875 .putFloat(
876 "${Settings.PREF_STICK_L}-Y$layout",
877 getResourceValue(layout, 29)
878 )
879 .putFloat(
880 "${Settings.PREF_BUTTON_STICK_L}-X$layout",
881 getResourceValue(layout, 30)
882 )
883 .putFloat(
884 "${Settings.PREF_BUTTON_STICK_L}-Y$layout",
885 getResourceValue(layout, 31)
886 )
887 .putFloat(
888 "${Settings.PREF_BUTTON_STICK_R}-X$layout",
889 getResourceValue(layout, 32)
890 )
891 .putFloat(
892 "${Settings.PREF_BUTTON_STICK_R}-Y$layout",
893 getResourceValue(layout, 33)
894 )
895 .apply()
896 } 688 }
897 689
898 override fun isInEditMode(): Boolean { 690 override fun isInEditMode(): Boolean {
@@ -913,18 +705,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
913 FOLDABLE_OVERLAY_VERSION 705 FOLDABLE_OVERLAY_VERSION
914 ) 706 )
915 707
916 private val preferences: SharedPreferences =
917 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
918
919 const val LANDSCAPE = "_Landscape"
920 const val PORTRAIT = "_Portrait"
921 const val FOLDABLE = "_Foldable"
922 val overlayLayouts = listOf(
923 LANDSCAPE,
924 PORTRAIT,
925 FOLDABLE
926 )
927
928 /** 708 /**
929 * Resizes a [Bitmap] by a given scale factor 709 * Resizes a [Bitmap] by a given scale factor
930 * 710 *
@@ -1036,29 +816,19 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1036 * In the input overlay configuration menu, 816 * In the input overlay configuration menu,
1037 * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay). 817 * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay).
1038 * the X and Y coordinates of the button at the END of its touch event 818 * the X and Y coordinates of the button at the END of its touch event
1039 * (when you remove your finger/stylus from the touchscreen) are then stored 819 * (when you remove your finger/stylus from the touchscreen) are then stored in a native .
1040 * within a SharedPreferences instance so that those values can be retrieved here.
1041 *
1042 *
1043 * This has a few benefits over the conventional way of storing the values
1044 * (ie. within the yuzu ini file).
1045 *
1046 * * No native calls
1047 * * Keeps Android-only values inside the Android environment
1048 *
1049 *
1050 * 820 *
1051 * Technically no modifications should need to be performed on the returned 821 * Technically no modifications should need to be performed on the returned
1052 * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait 822 * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait
1053 * for Android to call the onDraw method. 823 * for Android to call the onDraw method.
1054 * 824 *
1055 * @param context The current [Context]. 825 * @param context The current [Context].
1056 * @param windowSize The size of the window to draw the overlay on. 826 * @param windowSize The size of the window to draw the overlay on.
1057 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). 827 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
1058 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). 828 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
1059 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. 829 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
1060 * @param prefId Identifier for determining where a button appears on screen. 830 * @param overlayControlData Identifier for determining where a button appears on screen.
1061 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. 831 * @param position The position on screen as represented by an x and y value between 0 and 1.
1062 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. 832 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
1063 */ 833 */
1064 private fun initializeOverlayButton( 834 private fun initializeOverlayButton(
@@ -1067,33 +837,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1067 defaultResId: Int, 837 defaultResId: Int,
1068 pressedResId: Int, 838 pressedResId: Int,
1069 buttonId: Int, 839 buttonId: Int,
1070 prefId: String, 840 overlayControlData: OverlayControlData,
1071 layout: String 841 position: Pair<Double, Double>
1072 ): InputOverlayDrawableButton { 842 ): InputOverlayDrawableButton {
1073 // Resources handle for fetching the initial Drawable resource. 843 // Resources handle for fetching the initial Drawable resource.
1074 val res = context.resources 844 val res = context.resources
1075 845
1076 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
1077 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
1078
1079 // Decide scale based on button preference ID and user preference 846 // Decide scale based on button preference ID and user preference
1080 var scale: Float = when (prefId) { 847 var scale: Float = when (overlayControlData.id) {
1081 Settings.PREF_BUTTON_HOME, 848 OverlayControl.BUTTON_HOME.id,
1082 Settings.PREF_BUTTON_SCREENSHOT, 849 OverlayControl.BUTTON_CAPTURE.id,
1083 Settings.PREF_BUTTON_PLUS, 850 OverlayControl.BUTTON_PLUS.id,
1084 Settings.PREF_BUTTON_MINUS -> 0.07f 851 OverlayControl.BUTTON_MINUS.id -> 0.07f
1085 852
1086 Settings.PREF_BUTTON_L, 853 OverlayControl.BUTTON_L.id,
1087 Settings.PREF_BUTTON_R, 854 OverlayControl.BUTTON_R.id,
1088 Settings.PREF_BUTTON_ZL, 855 OverlayControl.BUTTON_ZL.id,
1089 Settings.PREF_BUTTON_ZR -> 0.26f 856 OverlayControl.BUTTON_ZR.id -> 0.26f
1090 857
1091 Settings.PREF_BUTTON_STICK_L, 858 OverlayControl.BUTTON_STICK_L.id,
1092 Settings.PREF_BUTTON_STICK_R -> 0.155f 859 OverlayControl.BUTTON_STICK_R.id -> 0.155f
1093 860
1094 else -> 0.11f 861 else -> 0.11f
1095 } 862 }
1096 scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() 863 scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
1097 scale /= 100f 864 scale /= 100f
1098 865
1099 // Initialize the InputOverlayDrawableButton. 866 // Initialize the InputOverlayDrawableButton.
@@ -1104,7 +871,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1104 defaultStateBitmap, 871 defaultStateBitmap,
1105 pressedStateBitmap, 872 pressedStateBitmap,
1106 buttonId, 873 buttonId,
1107 prefId 874 overlayControlData
1108 ) 875 )
1109 876
1110 // Get the minimum and maximum coordinates of the screen where the button can be placed. 877 // Get the minimum and maximum coordinates of the screen where the button can be placed.
@@ -1113,12 +880,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1113 880
1114 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 881 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1115 // These were set in the input overlay configuration menu. 882 // These were set in the input overlay configuration menu.
1116 val xKey = "$prefId-X$layout" 883 val drawableX = (position.first * max.x + min.x).toInt()
1117 val yKey = "$prefId-Y$layout" 884 val drawableY = (position.second * max.y + min.y).toInt()
1118 val drawableXPercent = sPrefs.getFloat(xKey, 0f)
1119 val drawableYPercent = sPrefs.getFloat(yKey, 0f)
1120 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1121 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1122 val width = overlayDrawable.width 885 val width = overlayDrawable.width
1123 val height = overlayDrawable.height 886 val height = overlayDrawable.height
1124 887
@@ -1136,8 +899,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1136 drawableX - (width / 2), 899 drawableX - (width / 2),
1137 drawableY - (height / 2) 900 drawableY - (height / 2)
1138 ) 901 )
1139 val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100) 902 overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
1140 overlayDrawable.setOpacity(savedOpacity * 255 / 100)
1141 return overlayDrawable 903 return overlayDrawable
1142 } 904 }
1143 905
@@ -1149,7 +911,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1149 * @param defaultResId The [Bitmap] resource ID of the default state. 911 * @param defaultResId The [Bitmap] resource ID of the default state.
1150 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. 912 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction.
1151 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. 913 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
1152 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. 914 * @param position The position on screen as represented by an x and y value between 0 and 1.
1153 * @return The initialized [InputOverlayDrawableDpad] 915 * @return The initialized [InputOverlayDrawableDpad]
1154 */ 916 */
1155 private fun initializeOverlayDpad( 917 private fun initializeOverlayDpad(
@@ -1158,17 +920,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1158 defaultResId: Int, 920 defaultResId: Int,
1159 pressedOneDirectionResId: Int, 921 pressedOneDirectionResId: Int,
1160 pressedTwoDirectionsResId: Int, 922 pressedTwoDirectionsResId: Int,
1161 layout: String 923 position: Pair<Double, Double>
1162 ): InputOverlayDrawableDpad { 924 ): InputOverlayDrawableDpad {
1163 // Resources handle for fetching the initial Drawable resource. 925 // Resources handle for fetching the initial Drawable resource.
1164 val res = context.resources 926 val res = context.resources
1165 927
1166 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad.
1167 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
1168
1169 // Decide scale based on button ID and user preference 928 // Decide scale based on button ID and user preference
1170 var scale = 0.25f 929 var scale = 0.25f
1171 scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() 930 scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
1172 scale /= 100f 931 scale /= 100f
1173 932
1174 // Initialize the InputOverlayDrawableDpad. 933 // Initialize the InputOverlayDrawableDpad.
@@ -1195,10 +954,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1195 954
1196 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. 955 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
1197 // These were set in the input overlay configuration menu. 956 // These were set in the input overlay configuration menu.
1198 val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f) 957 val drawableX = (position.first * max.x + min.x).toInt()
1199 val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f) 958 val drawableY = (position.second * max.y + min.y).toInt()
1200 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1201 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1202 val width = overlayDrawable.width 959 val width = overlayDrawable.width
1203 val height = overlayDrawable.height 960 val height = overlayDrawable.height
1204 961
@@ -1213,8 +970,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1213 970
1214 // Need to set the image's position 971 // Need to set the image's position
1215 overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2)) 972 overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2))
1216 val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100) 973 overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
1217 overlayDrawable.setOpacity(savedOpacity * 255 / 100)
1218 return overlayDrawable 974 return overlayDrawable
1219 } 975 }
1220 976
@@ -1227,9 +983,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1227 * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). 983 * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around).
1228 * @param pressedResInner Resource ID for the pressed inner image of the joystick. 984 * @param pressedResInner Resource ID for the pressed inner image of the joystick.
1229 * @param joystick Identifier for which joystick this is. 985 * @param joystick Identifier for which joystick this is.
1230 * @param button Identifier for which joystick button this is. 986 * @param buttonId Identifier for which joystick button this is.
1231 * @param prefId Identifier for determining where a button appears on screen. 987 * @param overlayControlData Identifier for determining where a button appears on screen.
1232 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. 988 * @param position The position on screen as represented by an x and y value between 0 and 1.
1233 * @return The initialized [InputOverlayDrawableJoystick]. 989 * @return The initialized [InputOverlayDrawableJoystick].
1234 */ 990 */
1235 private fun initializeOverlayJoystick( 991 private fun initializeOverlayJoystick(
@@ -1239,19 +995,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1239 defaultResInner: Int, 995 defaultResInner: Int,
1240 pressedResInner: Int, 996 pressedResInner: Int,
1241 joystick: Int, 997 joystick: Int,
1242 button: Int, 998 buttonId: Int,
1243 prefId: String, 999 overlayControlData: OverlayControlData,
1244 layout: String 1000 position: Pair<Double, Double>
1245 ): InputOverlayDrawableJoystick { 1001 ): InputOverlayDrawableJoystick {
1246 // Resources handle for fetching the initial Drawable resource. 1002 // Resources handle for fetching the initial Drawable resource.
1247 val res = context.resources 1003 val res = context.resources
1248 1004
1249 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick.
1250 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
1251
1252 // Decide scale based on user preference 1005 // Decide scale based on user preference
1253 var scale = 0.3f 1006 var scale = 0.3f
1254 scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() 1007 scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
1255 scale /= 100f 1008 scale /= 100f
1256 1009
1257 // Initialize the InputOverlayDrawableJoystick. 1010 // Initialize the InputOverlayDrawableJoystick.
@@ -1265,10 +1018,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1265 1018
1266 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1019 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1267 // These were set in the input overlay configuration menu. 1020 // These were set in the input overlay configuration menu.
1268 val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f) 1021 val drawableX = (position.first * max.x + min.x).toInt()
1269 val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f) 1022 val drawableY = (position.second * max.y + min.y).toInt()
1270 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1271 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1272 val outerScale = 1.66f 1023 val outerScale = 1.66f
1273 1024
1274 // Now set the bounds for the InputOverlayDrawableJoystick. 1025 // Now set the bounds for the InputOverlayDrawableJoystick.
@@ -1292,14 +1043,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1292 outerRect, 1043 outerRect,
1293 innerRect, 1044 innerRect,
1294 joystick, 1045 joystick,
1295 button, 1046 buttonId,
1296 prefId 1047 overlayControlData.id
1297 ) 1048 )
1298 1049
1299 // Need to set the image's position 1050 // Need to set the image's position
1300 overlayDrawable.setPosition(drawableX, drawableY) 1051 overlayDrawable.setPosition(drawableX, drawableY)
1301 val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100) 1052 overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
1302 overlayDrawable.setOpacity(savedOpacity * 255 / 100)
1303 return overlayDrawable 1053 return overlayDrawable
1304 } 1054 }
1305 } 1055 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
index 2c28dda88..b14a4f96e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
@@ -10,6 +10,7 @@ import android.graphics.Rect
10import android.graphics.drawable.BitmapDrawable 10import android.graphics.drawable.BitmapDrawable
11import android.view.MotionEvent 11import android.view.MotionEvent
12import org.yuzu.yuzu_emu.NativeLibrary.ButtonState 12import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
13import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
13 14
14/** 15/**
15 * Custom [BitmapDrawable] that is capable 16 * Custom [BitmapDrawable] that is capable
@@ -25,7 +26,7 @@ class InputOverlayDrawableButton(
25 defaultStateBitmap: Bitmap, 26 defaultStateBitmap: Bitmap,
26 pressedStateBitmap: Bitmap, 27 pressedStateBitmap: Bitmap,
27 val buttonId: Int, 28 val buttonId: Int,
28 val prefId: String 29 val overlayControlData: OverlayControlData
29) { 30) {
30 // The ID value what motion event is tracking 31 // The ID value what motion event is tracking
31 var trackId: Int 32 var trackId: Int
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index 518b1e783..113bf7c24 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -14,7 +14,7 @@ import kotlin.math.cos
14import kotlin.math.sin 14import kotlin.math.sin
15import kotlin.math.sqrt 15import kotlin.math.sqrt
16import org.yuzu.yuzu_emu.NativeLibrary 16import org.yuzu.yuzu_emu.NativeLibrary
17import org.yuzu.yuzu_emu.utils.EmulationMenuSettings 17import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
18 18
19/** 19/**
20 * Custom [BitmapDrawable] that is capable 20 * Custom [BitmapDrawable] that is capable
@@ -125,7 +125,7 @@ class InputOverlayDrawableJoystick(
125 pressedState = true 125 pressedState = true
126 outerBitmap.alpha = 0 126 outerBitmap.alpha = 0
127 boundsBoxBitmap.alpha = opacity 127 boundsBoxBitmap.alpha = opacity
128 if (EmulationMenuSettings.joystickRelCenter) { 128 if (BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()) {
129 virtBounds.offset( 129 virtBounds.offset(
130 xPosition - virtBounds.centerX(), 130 xPosition - virtBounds.centerX(),
131 yPosition - virtBounds.centerY() 131 yPosition - virtBounds.centerY()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
new file mode 100644
index 000000000..a0eeadf4b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
@@ -0,0 +1,188 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6import androidx.annotation.IntegerRes
7import org.yuzu.yuzu_emu.R
8import org.yuzu.yuzu_emu.YuzuApplication
9
10enum class OverlayControl(
11 val id: String,
12 val defaultVisibility: Boolean,
13 @IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
14 @IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
15 @IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
16) {
17 BUTTON_A(
18 "button_a",
19 true,
20 Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
21 Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
22 Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
23 ),
24 BUTTON_B(
25 "button_b",
26 true,
27 Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
28 Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
29 Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
30 ),
31 BUTTON_X(
32 "button_x",
33 true,
34 Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
35 Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
36 Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
37 ),
38 BUTTON_Y(
39 "button_y",
40 true,
41 Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
42 Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
43 Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
44 ),
45 BUTTON_PLUS(
46 "button_plus",
47 true,
48 Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
49 Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
50 Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
51 ),
52 BUTTON_MINUS(
53 "button_minus",
54 true,
55 Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
56 Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
57 Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
58 ),
59 BUTTON_HOME(
60 "button_home",
61 false,
62 Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
63 Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
64 Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
65 ),
66 BUTTON_CAPTURE(
67 "button_capture",
68 false,
69 Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
70 Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
71 Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
72 ),
73 BUTTON_L(
74 "button_l",
75 true,
76 Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
77 Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
78 Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
79 ),
80 BUTTON_R(
81 "button_r",
82 true,
83 Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
84 Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
85 Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
86 ),
87 BUTTON_ZL(
88 "button_zl",
89 true,
90 Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
91 Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
92 Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
93 ),
94 BUTTON_ZR(
95 "button_zr",
96 true,
97 Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
98 Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
99 Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
100 ),
101 BUTTON_STICK_L(
102 "button_stick_l",
103 true,
104 Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
105 Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
106 Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
107 ),
108 BUTTON_STICK_R(
109 "button_stick_r",
110 true,
111 Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
112 Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
113 Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
114 ),
115 STICK_L(
116 "stick_l",
117 true,
118 Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
119 Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
120 Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
121 ),
122 STICK_R(
123 "stick_r",
124 true,
125 Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
126 Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
127 Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
128 ),
129 COMBINED_DPAD(
130 "combined_dpad",
131 true,
132 Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
133 Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
134 Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
135 );
136
137 fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
138 val rawResourcePair: Pair<Int, Int>
139 YuzuApplication.appContext.resources.apply {
140 rawResourcePair = when (layout) {
141 OverlayLayout.Landscape -> {
142 Pair(
143 getInteger(this@OverlayControl.defaultLandscapePositionResources.first),
144 getInteger(this@OverlayControl.defaultLandscapePositionResources.second)
145 )
146 }
147
148 OverlayLayout.Portrait -> {
149 Pair(
150 getInteger(this@OverlayControl.defaultPortraitPositionResources.first),
151 getInteger(this@OverlayControl.defaultPortraitPositionResources.second)
152 )
153 }
154
155 OverlayLayout.Foldable -> {
156 Pair(
157 getInteger(this@OverlayControl.defaultFoldablePositionResources.first),
158 getInteger(this@OverlayControl.defaultFoldablePositionResources.second)
159 )
160 }
161 }
162 }
163
164 return Pair(
165 rawResourcePair.first.toDouble() / 1000,
166 rawResourcePair.second.toDouble() / 1000
167 )
168 }
169
170 fun toOverlayControlData(): OverlayControlData =
171 OverlayControlData(
172 id,
173 defaultVisibility,
174 getDefaultPositionForLayout(OverlayLayout.Landscape),
175 getDefaultPositionForLayout(OverlayLayout.Portrait),
176 getDefaultPositionForLayout(OverlayLayout.Foldable)
177 )
178
179 companion object {
180 val map: HashMap<String, OverlayControl> by lazy {
181 val hashMap = hashMapOf<String, OverlayControl>()
182 entries.forEach { hashMap[it.id] = it }
183 hashMap
184 }
185
186 fun from(id: String): OverlayControl? = map[id]
187 }
188}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
new file mode 100644
index 000000000..26cfeb1db
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6data class OverlayControlData(
7 val id: String,
8 var enabled: Boolean,
9 var landscapePosition: Pair<Double, Double>,
10 var portraitPosition: Pair<Double, Double>,
11 var foldablePosition: Pair<Double, Double>
12) {
13 fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
14 when (layout) {
15 OverlayLayout.Landscape -> landscapePosition
16 OverlayLayout.Portrait -> portraitPosition
17 OverlayLayout.Foldable -> foldablePosition
18 }
19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
new file mode 100644
index 000000000..6bd74c82f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6import androidx.annotation.IntegerRes
7
8data class OverlayControlDefault(
9 val buttonId: String,
10 @IntegerRes val landscapePositionResource: Pair<Int, Int>,
11 @IntegerRes val portraitPositionResource: Pair<Int, Int>,
12 @IntegerRes val foldablePositionResource: Pair<Int, Int>
13)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
new file mode 100644
index 000000000..d728164e5
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6enum class OverlayLayout(val id: String) {
7 Landscape("Landscape"),
8 Portrait("Portrait"),
9 Foldable("Foldable")
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 0197fd712..de0794a17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -3,9 +3,17 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import androidx.preference.PreferenceManager
6import java.io.IOException 7import java.io.IOException
7import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
11import org.yuzu.yuzu_emu.features.settings.model.IntSetting
12import org.yuzu.yuzu_emu.features.settings.model.Settings
13import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
14import org.yuzu.yuzu_emu.overlay.model.OverlayControl
15import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
16import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
9 17
10object DirectoryInitialization { 18object DirectoryInitialization {
11 private var userPath: String? = null 19 private var userPath: String? = null
@@ -17,6 +25,7 @@ object DirectoryInitialization {
17 initializeInternalStorage() 25 initializeInternalStorage()
18 NativeLibrary.initializeSystem(false) 26 NativeLibrary.initializeSystem(false)
19 NativeConfig.initializeGlobalConfig() 27 NativeConfig.initializeGlobalConfig()
28 migrateSettings()
20 areDirectoriesReady = true 29 areDirectoriesReady = true
21 } 30 }
22 } 31 }
@@ -35,4 +44,170 @@ object DirectoryInitialization {
35 e.printStackTrace() 44 e.printStackTrace()
36 } 45 }
37 } 46 }
47
48 private fun migrateSettings() {
49 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
50 var saveConfig = false
51 val theme = preferences.migratePreference<Int>(Settings.PREF_THEME)
52 if (theme != null) {
53 IntSetting.THEME.setInt(theme)
54 saveConfig = true
55 }
56
57 val themeMode = preferences.migratePreference<Int>(Settings.PREF_THEME_MODE)
58 if (themeMode != null) {
59 IntSetting.THEME_MODE.setInt(themeMode)
60 saveConfig = true
61 }
62
63 val blackBackgrounds =
64 preferences.migratePreference<Boolean>(Settings.PREF_BLACK_BACKGROUNDS)
65 if (blackBackgrounds != null) {
66 BooleanSetting.BLACK_BACKGROUNDS.setBoolean(blackBackgrounds)
67 saveConfig = true
68 }
69
70 val joystickRelCenter =
71 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER)
72 if (joystickRelCenter != null) {
73 BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(joystickRelCenter)
74 saveConfig = true
75 }
76
77 val dpadSlide =
78 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE)
79 if (dpadSlide != null) {
80 BooleanSetting.DPAD_SLIDE.setBoolean(dpadSlide)
81 saveConfig = true
82 }
83
84 val hapticFeedback =
85 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_HAPTICS)
86 if (hapticFeedback != null) {
87 BooleanSetting.HAPTIC_FEEDBACK.setBoolean(hapticFeedback)
88 saveConfig = true
89 }
90
91 val showPerformanceOverlay =
92 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_FPS)
93 if (showPerformanceOverlay != null) {
94 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(showPerformanceOverlay)
95 saveConfig = true
96 }
97
98 val showInputOverlay =
99 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY)
100 if (showInputOverlay != null) {
101 BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(showInputOverlay)
102 saveConfig = true
103 }
104
105 val overlayOpacity = preferences.migratePreference<Int>(Settings.PREF_CONTROL_OPACITY)
106 if (overlayOpacity != null) {
107 IntSetting.OVERLAY_OPACITY.setInt(overlayOpacity)
108 saveConfig = true
109 }
110
111 val overlayScale = preferences.migratePreference<Int>(Settings.PREF_CONTROL_SCALE)
112 if (overlayScale != null) {
113 IntSetting.OVERLAY_SCALE.setInt(overlayScale)
114 saveConfig = true
115 }
116
117 var setOverlayData = false
118 val overlayControlData = NativeConfig.getOverlayControlData()
119 if (overlayControlData.isEmpty()) {
120 val overlayControlDataMap =
121 NativeConfig.getOverlayControlData().associateBy { it.id }.toMutableMap()
122 for (button in Settings.overlayPreferences) {
123 val buttonId = convertButtonId(button)
124 var buttonEnabled = preferences.migratePreference<Boolean>(button)
125 if (buttonEnabled == null) {
126 buttonEnabled = OverlayControl.map[buttonId]?.defaultVisibility == true
127 }
128
129 var landscapeXPosition = preferences.migratePreference<Float>(
130 "$button-X${Settings.PREF_LANDSCAPE_SUFFIX}"
131 )?.toDouble()
132 var landscapeYPosition = preferences.migratePreference<Float>(
133 "$button-Y${Settings.PREF_LANDSCAPE_SUFFIX}"
134 )?.toDouble()
135 if (landscapeXPosition == null || landscapeYPosition == null) {
136 val landscapePosition = OverlayControl.map[buttonId]
137 ?.getDefaultPositionForLayout(OverlayLayout.Landscape) ?: Pair(0.0, 0.0)
138 landscapeXPosition = landscapePosition.first
139 landscapeYPosition = landscapePosition.second
140 }
141
142 var portraitXPosition = preferences.migratePreference<Float>(
143 "$button-X${Settings.PREF_PORTRAIT_SUFFIX}"
144 )?.toDouble()
145 var portraitYPosition = preferences.migratePreference<Float>(
146 "$button-Y${Settings.PREF_PORTRAIT_SUFFIX}"
147 )?.toDouble()
148 if (portraitXPosition == null || portraitYPosition == null) {
149 val portraitPosition = OverlayControl.map[buttonId]
150 ?.getDefaultPositionForLayout(OverlayLayout.Portrait) ?: Pair(0.0, 0.0)
151 portraitXPosition = portraitPosition.first
152 portraitYPosition = portraitPosition.second
153 }
154
155 var foldableXPosition = preferences.migratePreference<Float>(
156 "$button-X${Settings.PREF_FOLDABLE_SUFFIX}"
157 )?.toDouble()
158 var foldableYPosition = preferences.migratePreference<Float>(
159 "$button-Y${Settings.PREF_FOLDABLE_SUFFIX}"
160 )?.toDouble()
161 if (foldableXPosition == null || foldableYPosition == null) {
162 val foldablePosition = OverlayControl.map[buttonId]
163 ?.getDefaultPositionForLayout(OverlayLayout.Foldable) ?: Pair(0.0, 0.0)
164 foldableXPosition = foldablePosition.first
165 foldableYPosition = foldablePosition.second
166 }
167
168 val controlData = OverlayControlData(
169 buttonId,
170 buttonEnabled,
171 Pair(landscapeXPosition, landscapeYPosition),
172 Pair(portraitXPosition, portraitYPosition),
173 Pair(foldableXPosition, foldableYPosition)
174 )
175 overlayControlDataMap[buttonId] = controlData
176 setOverlayData = true
177 }
178
179 if (setOverlayData) {
180 NativeConfig.setOverlayControlData(
181 overlayControlDataMap.map { it.value }.toTypedArray()
182 )
183 saveConfig = true
184 }
185 }
186
187 if (saveConfig) {
188 NativeConfig.saveGlobalConfig()
189 }
190 }
191
192 private fun convertButtonId(buttonId: String): String =
193 when (buttonId) {
194 Settings.PREF_BUTTON_A -> OverlayControl.BUTTON_A.id
195 Settings.PREF_BUTTON_B -> OverlayControl.BUTTON_B.id
196 Settings.PREF_BUTTON_X -> OverlayControl.BUTTON_X.id
197 Settings.PREF_BUTTON_Y -> OverlayControl.BUTTON_Y.id
198 Settings.PREF_BUTTON_L -> OverlayControl.BUTTON_L.id
199 Settings.PREF_BUTTON_R -> OverlayControl.BUTTON_R.id
200 Settings.PREF_BUTTON_ZL -> OverlayControl.BUTTON_ZL.id
201 Settings.PREF_BUTTON_ZR -> OverlayControl.BUTTON_ZR.id
202 Settings.PREF_BUTTON_PLUS -> OverlayControl.BUTTON_PLUS.id
203 Settings.PREF_BUTTON_MINUS -> OverlayControl.BUTTON_MINUS.id
204 Settings.PREF_BUTTON_DPAD -> OverlayControl.COMBINED_DPAD.id
205 Settings.PREF_STICK_L -> OverlayControl.STICK_L.id
206 Settings.PREF_STICK_R -> OverlayControl.STICK_R.id
207 Settings.PREF_BUTTON_HOME -> OverlayControl.BUTTON_HOME.id
208 Settings.PREF_BUTTON_SCREENSHOT -> OverlayControl.BUTTON_CAPTURE.id
209 Settings.PREF_BUTTON_STICK_L -> OverlayControl.BUTTON_STICK_L.id
210 Settings.PREF_BUTTON_STICK_R -> OverlayControl.BUTTON_STICK_R.id
211 else -> ""
212 }
38} 213}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
deleted file mode 100644
index 7e8f058c1..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
+++ /dev/null
@@ -1,50 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import androidx.preference.PreferenceManager
7import org.yuzu.yuzu_emu.YuzuApplication
8import org.yuzu.yuzu_emu.features.settings.model.Settings
9
10object EmulationMenuSettings {
11 private val preferences =
12 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
13
14 var joystickRelCenter: Boolean
15 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
16 set(value) {
17 preferences.edit()
18 .putBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, value)
19 .apply()
20 }
21 var dpadSlide: Boolean
22 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, true)
23 set(value) {
24 preferences.edit()
25 .putBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, value)
26 .apply()
27 }
28 var hapticFeedback: Boolean
29 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, false)
30 set(value) {
31 preferences.edit()
32 .putBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, value)
33 .apply()
34 }
35
36 var showFps: Boolean
37 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
38 set(value) {
39 preferences.edit()
40 .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, value)
41 .apply()
42 }
43 var showOverlay: Boolean
44 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, true)
45 set(value) {
46 preferences.edit()
47 .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, value)
48 .apply()
49 }
50}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
index 7512d5eed..a4c14b3a7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import org.yuzu.yuzu_emu.model.GameDir 6import org.yuzu.yuzu_emu.model.GameDir
7import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
7 8
8object NativeConfig { 9object NativeConfig {
9 /** 10 /**
@@ -150,4 +151,21 @@ object NativeConfig {
150 */ 151 */
151 @Synchronized 152 @Synchronized
152 external fun setDisabledAddons(programId: String, disabledAddons: Array<String>) 153 external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
154
155 /**
156 * Gets an array of [OverlayControlData] from settings
157 *
158 * @return An array of [OverlayControlData]
159 */
160 @Synchronized
161 external fun getOverlayControlData(): Array<OverlayControlData>
162
163 /**
164 * Clears the AndroidSettings::values.overlay_control_data array and replaces its values
165 * with [overlayControlData]
166 *
167 * @param overlayControlData Replacement array of [OverlayControlData]
168 */
169 @Synchronized
170 external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
153} 171}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt
new file mode 100644
index 000000000..a233ba25c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.content.SharedPreferences
7
8object PreferenceUtil {
9 /**
10 * Retrieves a shared preference value and then deletes the value in storage.
11 * @param key Associated key for the value in this preferences instance
12 * @return Typed value associated with [key]. Null if no such key exists.
13 */
14 inline fun <reified T> SharedPreferences.migratePreference(key: String): T? {
15 if (!this.contains(key)) {
16 return null
17 }
18
19 val value: Any = when (T::class) {
20 String::class -> this.getString(key, "")!!
21
22 Boolean::class -> this.getBoolean(key, false)
23
24 Int::class -> this.getInt(key, 0)
25
26 Float::class -> this.getFloat(key, 0f)
27
28 Long::class -> this.getLong(key, 0)
29
30 else -> throw IllegalStateException("Tried to migrate preference with invalid type!")
31 }
32 deletePreference(key)
33 return value as T
34 }
35
36 fun SharedPreferences.deletePreference(key: String) = this.edit().remove(key).apply()
37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index f312e24cf..6f7f40e43 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -5,38 +5,38 @@ package org.yuzu.yuzu_emu.utils
5 5
6import android.content.res.Configuration 6import android.content.res.Configuration
7import android.graphics.Color 7import android.graphics.Color
8import android.os.Build
8import androidx.annotation.ColorInt 9import androidx.annotation.ColorInt
9import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
10import androidx.appcompat.app.AppCompatDelegate 11import androidx.appcompat.app.AppCompatDelegate
11import androidx.core.view.WindowCompat 12import androidx.core.view.WindowCompat
12import androidx.core.view.WindowInsetsControllerCompat 13import androidx.core.view.WindowInsetsControllerCompat
13import androidx.preference.PreferenceManager
14import kotlin.math.roundToInt 14import kotlin.math.roundToInt
15import org.yuzu.yuzu_emu.R 15import org.yuzu.yuzu_emu.R
16import org.yuzu.yuzu_emu.YuzuApplication 16import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
17import org.yuzu.yuzu_emu.features.settings.model.Settings 17import org.yuzu.yuzu_emu.features.settings.model.IntSetting
18import org.yuzu.yuzu_emu.ui.main.ThemeProvider 18import org.yuzu.yuzu_emu.ui.main.ThemeProvider
19 19
20object ThemeHelper { 20object ThemeHelper {
21 const val SYSTEM_BAR_ALPHA = 0.9f 21 const val SYSTEM_BAR_ALPHA = 0.9f
22 22
23 private const val DEFAULT = 0
24 private const val MATERIAL_YOU = 1
25
26 fun setTheme(activity: AppCompatActivity) { 23 fun setTheme(activity: AppCompatActivity) {
27 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
28 setThemeMode(activity) 24 setThemeMode(activity)
29 when (preferences.getInt(Settings.PREF_THEME, 0)) { 25 when (Theme.from(IntSetting.THEME.getInt())) {
30 DEFAULT -> activity.setTheme(R.style.Theme_Yuzu_Main) 26 Theme.Default -> activity.setTheme(R.style.Theme_Yuzu_Main)
31 MATERIAL_YOU -> activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou) 27 Theme.MaterialYou -> {
28 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
29 activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
30 } else {
31 activity.setTheme(R.style.Theme_Yuzu_Main)
32 }
33 }
32 } 34 }
33 35
34 // Using a specific night mode check because this could apply incorrectly when using the 36 // Using a specific night mode check because this could apply incorrectly when using the
35 // light app mode, dark system mode, and black backgrounds. Launching the settings activity 37 // light app mode, dark system mode, and black backgrounds. Launching the settings activity
36 // will then show light mode colors/navigation bars but with black backgrounds. 38 // will then show light mode colors/navigation bars but with black backgrounds.
37 if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) && 39 if (BooleanSetting.BLACK_BACKGROUNDS.getBoolean() && isNightMode(activity)) {
38 isNightMode(activity)
39 ) {
40 activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark) 40 activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
41 } 41 }
42 } 42 }
@@ -60,8 +60,7 @@ object ThemeHelper {
60 } 60 }
61 61
62 fun setThemeMode(activity: AppCompatActivity) { 62 fun setThemeMode(activity: AppCompatActivity) {
63 val themeMode = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) 63 val themeMode = IntSetting.THEME_MODE.getInt()
64 .getInt(Settings.PREF_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
65 activity.delegate.localNightMode = themeMode 64 activity.delegate.localNightMode = themeMode
66 val windowController = WindowCompat.getInsetsController( 65 val windowController = WindowCompat.getInsetsController(
67 activity.window, 66 activity.window,
@@ -95,3 +94,12 @@ object ThemeHelper {
95 windowController.isAppearanceLightNavigationBars = false 94 windowController.isAppearanceLightNavigationBars = false
96 } 95 }
97} 96}
97
98enum class Theme(val int: Int) {
99 Default(0),
100 MaterialYou(1);
101
102 companion object {
103 fun from(int: Int): Theme = entries.firstOrNull { it.int == int } ?: Default
104 }
105}
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp
index 52d8ecfeb..1e884ffdd 100644
--- a/src/android/app/src/main/jni/android_common/android_common.cpp
+++ b/src/android/app/src/main/jni/android_common/android_common.cpp
@@ -9,6 +9,7 @@
9#include <jni.h> 9#include <jni.h>
10 10
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "jni/id_cache.h"
12 13
13std::string GetJString(JNIEnv* env, jstring jstr) { 14std::string GetJString(JNIEnv* env, jstring jstr) {
14 if (!jstr) { 15 if (!jstr) {
@@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
33jstring ToJString(JNIEnv* env, std::u16string_view str) { 34jstring ToJString(JNIEnv* env, std::u16string_view str) {
34 return ToJString(env, Common::UTF16ToUTF8(str)); 35 return ToJString(env, Common::UTF16ToUTF8(str));
35} 36}
37
38double GetJDouble(JNIEnv* env, jobject jdouble) {
39 return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
40}
41
42jobject ToJDouble(JNIEnv* env, double value) {
43 return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
44}
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h
index ccb0c06f7..8eb803e1b 100644
--- a/src/android/app/src/main/jni/android_common/android_common.h
+++ b/src/android/app/src/main/jni/android_common/android_common.h
@@ -10,3 +10,6 @@
10std::string GetJString(JNIEnv* env, jstring jstr); 10std::string GetJString(JNIEnv* env, jstring jstr);
11jstring ToJString(JNIEnv* env, std::string_view str); 11jstring ToJString(JNIEnv* env, std::string_view str);
12jstring ToJString(JNIEnv* env, std::u16string_view str); 12jstring ToJString(JNIEnv* env, std::u16string_view str);
13
14double GetJDouble(JNIEnv* env, jobject jdouble);
15jobject ToJDouble(JNIEnv* env, double value);
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index 9c3a5a9b2..c86aa1c39 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -35,6 +35,7 @@ void AndroidConfig::ReadAndroidValues() {
35 if (global) { 35 if (global) {
36 ReadAndroidUIValues(); 36 ReadAndroidUIValues();
37 ReadUIValues(); 37 ReadUIValues();
38 ReadOverlayValues();
38 } 39 }
39 ReadDriverValues(); 40 ReadDriverValues();
40} 41}
@@ -81,10 +82,42 @@ void AndroidConfig::ReadDriverValues() {
81 EndGroup(); 82 EndGroup();
82} 83}
83 84
85void AndroidConfig::ReadOverlayValues() {
86 BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
87
88 ReadCategory(Settings::Category::Overlay);
89
90 AndroidSettings::values.overlay_control_data.clear();
91 const int control_data_size = BeginArray("control_data");
92 for (int i = 0; i < control_data_size; ++i) {
93 SetArrayIndex(i);
94 AndroidSettings::OverlayControlData control_data;
95 control_data.id = ReadStringSetting(std::string("id"));
96 control_data.enabled = ReadBooleanSetting(std::string("enabled"));
97 control_data.landscape_position.first =
98 ReadDoubleSetting(std::string("landscape\\x_position"));
99 control_data.landscape_position.second =
100 ReadDoubleSetting(std::string("landscape\\y_position"));
101 control_data.portrait_position.first =
102 ReadDoubleSetting(std::string("portrait\\x_position"));
103 control_data.portrait_position.second =
104 ReadDoubleSetting(std::string("portrait\\y_position"));
105 control_data.foldable_position.first =
106 ReadDoubleSetting(std::string("foldable\\x_position"));
107 control_data.foldable_position.second =
108 ReadDoubleSetting(std::string("foldable\\y_position"));
109 AndroidSettings::values.overlay_control_data.push_back(control_data);
110 }
111 EndArray();
112
113 EndGroup();
114}
115
84void AndroidConfig::SaveAndroidValues() { 116void AndroidConfig::SaveAndroidValues() {
85 if (global) { 117 if (global) {
86 SaveAndroidUIValues(); 118 SaveAndroidUIValues();
87 SaveUIValues(); 119 SaveUIValues();
120 SaveOverlayValues();
88 } 121 }
89 SaveDriverValues(); 122 SaveDriverValues();
90 123
@@ -114,8 +147,9 @@ void AndroidConfig::SavePathValues() {
114 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { 147 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
115 SetArrayIndex(i); 148 SetArrayIndex(i);
116 const auto& game_dir = AndroidSettings::values.game_dirs[i]; 149 const auto& game_dir = AndroidSettings::values.game_dirs[i];
117 WriteSetting(std::string("path"), game_dir.path); 150 WriteStringSetting(std::string("path"), game_dir.path);
118 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); 151 WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
152 std::make_optional(false));
119 } 153 }
120 EndArray(); 154 EndArray();
121 155
@@ -130,6 +164,35 @@ void AndroidConfig::SaveDriverValues() {
130 EndGroup(); 164 EndGroup();
131} 165}
132 166
167void AndroidConfig::SaveOverlayValues() {
168 BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
169
170 WriteCategory(Settings::Category::Overlay);
171
172 BeginArray("control_data");
173 for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
174 SetArrayIndex(i);
175 const auto& control_data = AndroidSettings::values.overlay_control_data[i];
176 WriteStringSetting(std::string("id"), control_data.id);
177 WriteBooleanSetting(std::string("enabled"), control_data.enabled);
178 WriteDoubleSetting(std::string("landscape\\x_position"),
179 control_data.landscape_position.first);
180 WriteDoubleSetting(std::string("landscape\\y_position"),
181 control_data.landscape_position.second);
182 WriteDoubleSetting(std::string("portrait\\x_position"),
183 control_data.portrait_position.first);
184 WriteDoubleSetting(std::string("portrait\\y_position"),
185 control_data.portrait_position.second);
186 WriteDoubleSetting(std::string("foldable\\x_position"),
187 control_data.foldable_position.first);
188 WriteDoubleSetting(std::string("foldable\\y_position"),
189 control_data.foldable_position.second);
190 }
191 EndArray();
192
193 EndGroup();
194}
195
133std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { 196std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
134 auto& map = Settings::values.linkage.by_category; 197 auto& map = Settings::values.linkage.by_category;
135 if (map.contains(category)) { 198 if (map.contains(category)) {
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index 2c12874e1..d83852de9 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -18,6 +18,7 @@ protected:
18 void ReadAndroidValues(); 18 void ReadAndroidValues();
19 void ReadAndroidUIValues(); 19 void ReadAndroidUIValues();
20 void ReadDriverValues(); 20 void ReadDriverValues();
21 void ReadOverlayValues();
21 void ReadHidbusValues() override {} 22 void ReadHidbusValues() override {}
22 void ReadDebugControlValues() override {} 23 void ReadDebugControlValues() override {}
23 void ReadPathValues() override; 24 void ReadPathValues() override;
@@ -30,6 +31,7 @@ protected:
30 void SaveAndroidValues(); 31 void SaveAndroidValues();
31 void SaveAndroidUIValues(); 32 void SaveAndroidUIValues();
32 void SaveDriverValues(); 33 void SaveDriverValues();
34 void SaveOverlayValues();
33 void SaveHidbusValues() override {} 35 void SaveHidbusValues() override {}
34 void SaveDebugControlValues() override {} 36 void SaveDebugControlValues() override {}
35 void SavePathValues() override; 37 void SavePathValues() override;
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 3733f5a3c..559ae83eb 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -14,6 +14,14 @@ struct GameDir {
14 bool deep_scan = false; 14 bool deep_scan = false;
15}; 15};
16 16
17struct OverlayControlData {
18 std::string id;
19 bool enabled;
20 std::pair<double, double> landscape_position;
21 std::pair<double, double> portrait_position;
22 std::pair<double, double> foldable_position;
23};
24
17struct Values { 25struct Values {
18 Settings::Linkage linkage; 26 Settings::Linkage linkage;
19 27
@@ -33,6 +41,28 @@ struct Values {
33 41
34 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path", 42 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
35 Settings::Category::GpuDriver}; 43 Settings::Category::GpuDriver};
44
45 Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
46 Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
47 Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
48 Settings::Category::Android};
49
50 // Input/performance overlay settings
51 std::vector<OverlayControlData> overlay_control_data;
52 Settings::Setting<s32> overlay_scale{linkage, 50, "control_scale", Settings::Category::Overlay};
53 Settings::Setting<s32> overlay_opacity{linkage, 100, "control_opacity",
54 Settings::Category::Overlay};
55
56 Settings::Setting<bool> joystick_rel_center{linkage, true, "joystick_rel_center",
57 Settings::Category::Overlay};
58 Settings::Setting<bool> dpad_slide{linkage, true, "dpad_slide", Settings::Category::Overlay};
59 Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
60 Settings::Category::Overlay};
61 Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
62 Settings::Category::Overlay};
63 Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
64 Settings::Category::Overlay};
65 Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
36}; 66};
37 67
38extern Values values; 68extern Values values;
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index e7a86d3fd..c79ad7d76 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
35static jfieldID s_pair_first_field; 35static jfieldID s_pair_first_field;
36static jfieldID s_pair_second_field; 36static jfieldID s_pair_second_field;
37 37
38static jclass s_overlay_control_data_class;
39static jmethodID s_overlay_control_data_constructor;
40static jfieldID s_overlay_control_data_id_field;
41static jfieldID s_overlay_control_data_enabled_field;
42static jfieldID s_overlay_control_data_landscape_position_field;
43static jfieldID s_overlay_control_data_portrait_position_field;
44static jfieldID s_overlay_control_data_foldable_position_field;
45
46static jclass s_double_class;
47static jmethodID s_double_constructor;
48static jfieldID s_double_value_field;
49
38static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 50static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
39 51
40namespace IDCache { 52namespace IDCache {
@@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
146 return s_pair_second_field; 158 return s_pair_second_field;
147} 159}
148 160
161jclass GetOverlayControlDataClass() {
162 return s_overlay_control_data_class;
163}
164
165jmethodID GetOverlayControlDataConstructor() {
166 return s_overlay_control_data_constructor;
167}
168
169jfieldID GetOverlayControlDataIdField() {
170 return s_overlay_control_data_id_field;
171}
172
173jfieldID GetOverlayControlDataEnabledField() {
174 return s_overlay_control_data_enabled_field;
175}
176
177jfieldID GetOverlayControlDataLandscapePositionField() {
178 return s_overlay_control_data_landscape_position_field;
179}
180
181jfieldID GetOverlayControlDataPortraitPositionField() {
182 return s_overlay_control_data_portrait_position_field;
183}
184
185jfieldID GetOverlayControlDataFoldablePositionField() {
186 return s_overlay_control_data_foldable_position_field;
187}
188
189jclass GetDoubleClass() {
190 return s_double_class;
191}
192
193jmethodID GetDoubleConstructor() {
194 return s_double_constructor;
195}
196
197jfieldID GetDoubleValueField() {
198 return s_double_value_field;
199}
200
149} // namespace IDCache 201} // namespace IDCache
150 202
151#ifdef __cplusplus 203#ifdef __cplusplus
@@ -207,6 +259,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
207 s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); 259 s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
208 env->DeleteLocalRef(pair_class); 260 env->DeleteLocalRef(pair_class);
209 261
262 const jclass overlay_control_data_class =
263 env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
264 s_overlay_control_data_class =
265 reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
266 s_overlay_control_data_constructor =
267 env->GetMethodID(overlay_control_data_class, "<init>",
268 "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
269 s_overlay_control_data_id_field =
270 env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
271 s_overlay_control_data_enabled_field =
272 env->GetFieldID(overlay_control_data_class, "enabled", "Z");
273 s_overlay_control_data_landscape_position_field =
274 env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
275 s_overlay_control_data_portrait_position_field =
276 env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
277 s_overlay_control_data_foldable_position_field =
278 env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
279 env->DeleteLocalRef(overlay_control_data_class);
280
281 const jclass double_class = env->FindClass("java/lang/Double");
282 s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
283 s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
284 s_double_value_field = env->GetFieldID(double_class, "value", "D");
285 env->DeleteLocalRef(double_class);
286
210 // Initialize Android Storage 287 // Initialize Android Storage
211 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 288 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
212 289
@@ -231,6 +308,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
231 env->DeleteGlobalRef(s_game_class); 308 env->DeleteGlobalRef(s_game_class);
232 env->DeleteGlobalRef(s_string_class); 309 env->DeleteGlobalRef(s_string_class);
233 env->DeleteGlobalRef(s_pair_class); 310 env->DeleteGlobalRef(s_pair_class);
311 env->DeleteGlobalRef(s_overlay_control_data_class);
312 env->DeleteGlobalRef(s_double_class);
234 313
235 // UnInitialize applets 314 // UnInitialize applets
236 SoftwareKeyboard::CleanupJNI(env); 315 SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 24030be42..784d1412f 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
35jfieldID GetPairFirstField(); 35jfieldID GetPairFirstField();
36jfieldID GetPairSecondField(); 36jfieldID GetPairSecondField();
37 37
38jclass GetOverlayControlDataClass();
39jmethodID GetOverlayControlDataConstructor();
40jfieldID GetOverlayControlDataIdField();
41jfieldID GetOverlayControlDataEnabledField();
42jfieldID GetOverlayControlDataLandscapePositionField();
43jfieldID GetOverlayControlDataPortraitPositionField();
44jfieldID GetOverlayControlDataFoldablePositionField();
45
46jclass GetDoubleClass();
47jmethodID GetDoubleConstructor();
48jfieldID GetDoubleValueField();
49
38} // namespace IDCache 50} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 324d9e9cd..535902483 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -344,4 +344,74 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
344 Settings::values.disabled_addons[program_id] = disabled_addons; 344 Settings::values.disabled_addons[program_id] = disabled_addons;
345} 345}
346 346
347jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JNIEnv* env,
348 jobject obj) {
349 jobjectArray joverlayControlDataArray =
350 env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
351 IDCache::GetOverlayControlDataClass(), nullptr);
352 for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
353 const auto& control_data = AndroidSettings::values.overlay_control_data[i];
354 jobject jlandscapePosition =
355 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
356 ToJDouble(env, control_data.landscape_position.first),
357 ToJDouble(env, control_data.landscape_position.second));
358 jobject jportraitPosition =
359 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
360 ToJDouble(env, control_data.portrait_position.first),
361 ToJDouble(env, control_data.portrait_position.second));
362 jobject jfoldablePosition =
363 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
364 ToJDouble(env, control_data.foldable_position.first),
365 ToJDouble(env, control_data.foldable_position.second));
366
367 jobject jcontrolData = env->NewObject(
368 IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
369 ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
370 jportraitPosition, jfoldablePosition);
371 env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
372 }
373 return joverlayControlDataArray;
374}
375
376void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
377 JNIEnv* env, jobject obj, jobjectArray joverlayControlDataArray) {
378 AndroidSettings::values.overlay_control_data.clear();
379 int size = env->GetArrayLength(joverlayControlDataArray);
380
381 if (size == 0) {
382 return;
383 }
384
385 for (int i = 0; i < size; ++i) {
386 jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
387 jstring jidString = static_cast<jstring>(
388 env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
389 bool enabled = static_cast<bool>(env->GetBooleanField(
390 joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
391
392 jobject jlandscapePosition = env->GetObjectField(
393 joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
394 std::pair<double, double> landscape_position = std::make_pair(
395 GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
396 GetJDouble(env,
397 env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
398
399 jobject jportraitPosition = env->GetObjectField(
400 joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
401 std::pair<double, double> portrait_position = std::make_pair(
402 GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
403 GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
404
405 jobject jfoldablePosition = env->GetObjectField(
406 joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
407 std::pair<double, double> foldable_position = std::make_pair(
408 GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
409 GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
410
411 AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
412 GetJString(env, jidString), enabled, landscape_position, portrait_position,
413 foldable_position});
414 }
415}
416
347} // extern "C" 417} // extern "C"
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml
index 4885b4f6f..363781652 100644
--- a/src/android/app/src/main/res/menu/menu_overlay_options.xml
+++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml
@@ -39,6 +39,11 @@
39 android:checkable="true" /> 39 android:checkable="true" />
40 40
41 <item 41 <item
42 android:id="@+id/menu_touchscreen"
43 android:title="@string/touchscreen"
44 android:checkable="true" />
45
46 <item
42 android:id="@+id/menu_reset_overlay" 47 android:id="@+id/menu_reset_overlay"
43 android:title="@string/emulation_touch_overlay_reset" /> 48 android:title="@string/emulation_touch_overlay_reset" />
44 49
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index c882a8e62..45d57c3ea 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -212,19 +212,19 @@
212 <item>B</item> 212 <item>B</item>
213 <item>X</item> 213 <item>X</item>
214 <item>Y</item> 214 <item>Y</item>
215 <item>+</item>
216 <item>-</item>
217 <item>@string/gamepad_home</item>
218 <item>@string/gamepad_screenshot</item>
215 <item>L</item> 219 <item>L</item>
216 <item>R</item> 220 <item>R</item>
217 <item>ZL</item> 221 <item>ZL</item>
218 <item>ZR</item> 222 <item>ZR</item>
219 <item>+</item>
220 <item>-</item>
221 <item>@string/gamepad_d_pad</item>
222 <item>@string/gamepad_left_stick</item> 223 <item>@string/gamepad_left_stick</item>
223 <item>@string/gamepad_right_stick</item> 224 <item>@string/gamepad_right_stick</item>
224 <item>L3</item> 225 <item>L3</item>
225 <item>R3</item> 226 <item>R3</item>
226 <item>@string/gamepad_home</item> 227 <item>@string/gamepad_d_pad</item>
227 <item>@string/gamepad_screenshot</item>
228 </string-array> 228 </string-array>
229 229
230 <string-array name="themeEntries"> 230 <string-array name="themeEntries">
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index dc527965c..1c6f5db93 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -3,111 +3,111 @@
3 <integer name="grid_columns">1</integer> 3 <integer name="grid_columns">1</integer>
4 4
5 <!-- Default SWITCH landscape layout --> 5 <!-- Default SWITCH landscape layout -->
6 <integer name="SWITCH_BUTTON_A_X">760</integer> 6 <integer name="BUTTON_A_X">760</integer>
7 <integer name="SWITCH_BUTTON_A_Y">790</integer> 7 <integer name="BUTTON_A_Y">790</integer>
8 <integer name="SWITCH_BUTTON_B_X">710</integer> 8 <integer name="BUTTON_B_X">710</integer>
9 <integer name="SWITCH_BUTTON_B_Y">900</integer> 9 <integer name="BUTTON_B_Y">900</integer>
10 <integer name="SWITCH_BUTTON_X_X">710</integer> 10 <integer name="BUTTON_X_X">710</integer>
11 <integer name="SWITCH_BUTTON_X_Y">680</integer> 11 <integer name="BUTTON_X_Y">680</integer>
12 <integer name="SWITCH_BUTTON_Y_X">660</integer> 12 <integer name="BUTTON_Y_X">660</integer>
13 <integer name="SWITCH_BUTTON_Y_Y">790</integer> 13 <integer name="BUTTON_Y_Y">790</integer>
14 <integer name="SWITCH_STICK_L_X">100</integer> 14 <integer name="BUTTON_PLUS_X">540</integer>
15 <integer name="SWITCH_STICK_L_Y">670</integer> 15 <integer name="BUTTON_PLUS_Y">950</integer>
16 <integer name="SWITCH_STICK_R_X">900</integer> 16 <integer name="BUTTON_MINUS_X">460</integer>
17 <integer name="SWITCH_STICK_R_Y">670</integer> 17 <integer name="BUTTON_MINUS_Y">950</integer>
18 <integer name="SWITCH_TRIGGER_L_X">70</integer> 18 <integer name="BUTTON_HOME_X">600</integer>
19 <integer name="SWITCH_TRIGGER_L_Y">220</integer> 19 <integer name="BUTTON_HOME_Y">950</integer>
20 <integer name="SWITCH_TRIGGER_R_X">930</integer> 20 <integer name="BUTTON_CAPTURE_X">400</integer>
21 <integer name="SWITCH_TRIGGER_R_Y">220</integer> 21 <integer name="BUTTON_CAPTURE_Y">950</integer>
22 <integer name="SWITCH_TRIGGER_ZL_X">70</integer> 22 <integer name="BUTTON_L_X">70</integer>
23 <integer name="SWITCH_TRIGGER_ZL_Y">90</integer> 23 <integer name="BUTTON_L_Y">220</integer>
24 <integer name="SWITCH_TRIGGER_ZR_X">930</integer> 24 <integer name="BUTTON_R_X">930</integer>
25 <integer name="SWITCH_TRIGGER_ZR_Y">90</integer> 25 <integer name="BUTTON_R_Y">220</integer>
26 <integer name="SWITCH_BUTTON_MINUS_X">460</integer> 26 <integer name="BUTTON_ZL_X">70</integer>
27 <integer name="SWITCH_BUTTON_MINUS_Y">950</integer> 27 <integer name="BUTTON_ZL_Y">90</integer>
28 <integer name="SWITCH_BUTTON_PLUS_X">540</integer> 28 <integer name="BUTTON_ZR_X">930</integer>
29 <integer name="SWITCH_BUTTON_PLUS_Y">950</integer> 29 <integer name="BUTTON_ZR_Y">90</integer>
30 <integer name="SWITCH_BUTTON_HOME_X">600</integer> 30 <integer name="BUTTON_STICK_L_X">870</integer>
31 <integer name="SWITCH_BUTTON_HOME_Y">950</integer> 31 <integer name="BUTTON_STICK_L_Y">400</integer>
32 <integer name="SWITCH_BUTTON_CAPTURE_X">400</integer> 32 <integer name="BUTTON_STICK_R_X">960</integer>
33 <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> 33 <integer name="BUTTON_STICK_R_Y">430</integer>
34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer> 34 <integer name="STICK_L_X">100</integer>
35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> 35 <integer name="STICK_L_Y">670</integer>
36 <integer name="SWITCH_BUTTON_STICK_L_X">870</integer> 36 <integer name="STICK_R_X">900</integer>
37 <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer> 37 <integer name="STICK_R_Y">670</integer>
38 <integer name="SWITCH_BUTTON_STICK_R_X">960</integer> 38 <integer name="COMBINED_DPAD_X">260</integer>
39 <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer> 39 <integer name="COMBINED_DPAD_Y">790</integer>
40 40
41 <!-- Default SWITCH portrait layout --> 41 <!-- Default SWITCH portrait layout -->
42 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> 42 <integer name="BUTTON_A_X_PORTRAIT">840</integer>
43 <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer> 43 <integer name="BUTTON_A_Y_PORTRAIT">840</integer>
44 <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer> 44 <integer name="BUTTON_B_X_PORTRAIT">740</integer>
45 <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer> 45 <integer name="BUTTON_B_Y_PORTRAIT">880</integer>
46 <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer> 46 <integer name="BUTTON_X_X_PORTRAIT">740</integer>
47 <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer> 47 <integer name="BUTTON_X_Y_PORTRAIT">800</integer>
48 <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer> 48 <integer name="BUTTON_Y_X_PORTRAIT">640</integer>
49 <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer> 49 <integer name="BUTTON_Y_Y_PORTRAIT">840</integer>
50 <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer> 50 <integer name="BUTTON_PLUS_Y_PORTRAIT">950</integer>
51 <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer> 51 <integer name="BUTTON_MINUS_X_PORTRAIT">440</integer>
52 <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer> 52 <integer name="BUTTON_MINUS_Y_PORTRAIT">950</integer>
53 <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer> 53 <integer name="BUTTON_HOME_X_PORTRAIT">680</integer>
54 <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer> 54 <integer name="BUTTON_HOME_Y_PORTRAIT">950</integer>
55 <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer> 55 <integer name="BUTTON_CAPTURE_X_PORTRAIT">320</integer>
56 <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer> 56 <integer name="BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
57 <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer> 57 <integer name="BUTTON_L_X_PORTRAIT">140</integer>
58 <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer> 58 <integer name="BUTTON_L_Y_PORTRAIT">260</integer>
59 <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer> 59 <integer name="BUTTON_R_X_PORTRAIT">860</integer>
60 <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer> 60 <integer name="BUTTON_R_Y_PORTRAIT">260</integer>
61 <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer> 61 <integer name="BUTTON_ZL_X_PORTRAIT">140</integer>
62 <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer> 62 <integer name="BUTTON_ZL_Y_PORTRAIT">200</integer>
63 <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer> 63 <integer name="BUTTON_ZR_X_PORTRAIT">860</integer>
64 <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer> 64 <integer name="BUTTON_ZR_Y_PORTRAIT">200</integer>
65 <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer> 65 <integer name="BUTTON_PLUS_X_PORTRAIT">560</integer>
66 <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer> 66 <integer name="BUTTON_STICK_L_X_PORTRAIT">730</integer>
67 <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer> 67 <integer name="BUTTON_STICK_L_Y_PORTRAIT">510</integer>
68 <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer> 68 <integer name="BUTTON_STICK_R_X_PORTRAIT">900</integer>
69 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> 69 <integer name="BUTTON_STICK_R_Y_PORTRAIT">540</integer>
70 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> 70 <integer name="STICK_L_X_PORTRAIT">180</integer>
71 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> 71 <integer name="STICK_L_Y_PORTRAIT">660</integer>
72 <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer> 72 <integer name="STICK_R_X_PORTRAIT">820</integer>
73 <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer> 73 <integer name="STICK_R_Y_PORTRAIT">660</integer>
74 <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer> 74 <integer name="COMBINED_DPAD_X_PORTRAIT">240</integer>
75 <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer> 75 <integer name="COMBINED_DPAD_Y_PORTRAIT">840</integer>
76 76
77 <!-- Default SWITCH foldable layout --> 77 <!-- Default SWITCH foldable layout -->
78 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> 78 <integer name="BUTTON_A_X_FOLDABLE">840</integer>
79 <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer> 79 <integer name="BUTTON_A_Y_FOLDABLE">390</integer>
80 <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer> 80 <integer name="BUTTON_B_X_FOLDABLE">740</integer>
81 <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer> 81 <integer name="BUTTON_B_Y_FOLDABLE">430</integer>
82 <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer> 82 <integer name="BUTTON_X_X_FOLDABLE">740</integer>
83 <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer> 83 <integer name="BUTTON_X_Y_FOLDABLE">350</integer>
84 <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer> 84 <integer name="BUTTON_Y_X_FOLDABLE">640</integer>
85 <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer> 85 <integer name="BUTTON_Y_Y_FOLDABLE">390</integer>
86 <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer> 86 <integer name="BUTTON_PLUS_X_FOLDABLE">560</integer>
87 <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer> 87 <integer name="BUTTON_PLUS_Y_FOLDABLE">470</integer>
88 <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer> 88 <integer name="BUTTON_MINUS_X_FOLDABLE">440</integer>
89 <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer> 89 <integer name="BUTTON_MINUS_Y_FOLDABLE">470</integer>
90 <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer> 90 <integer name="BUTTON_HOME_X_FOLDABLE">680</integer>
91 <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer> 91 <integer name="BUTTON_HOME_Y_FOLDABLE">470</integer>
92 <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer> 92 <integer name="BUTTON_CAPTURE_X_FOLDABLE">320</integer>
93 <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer> 93 <integer name="BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
94 <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer> 94 <integer name="BUTTON_L_X_FOLDABLE">140</integer>
95 <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer> 95 <integer name="BUTTON_L_Y_FOLDABLE">130</integer>
96 <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer> 96 <integer name="BUTTON_R_X_FOLDABLE">860</integer>
97 <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer> 97 <integer name="BUTTON_R_Y_FOLDABLE">130</integer>
98 <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer> 98 <integer name="BUTTON_ZL_X_FOLDABLE">140</integer>
99 <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer> 99 <integer name="BUTTON_ZL_Y_FOLDABLE">70</integer>
100 <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer> 100 <integer name="BUTTON_ZR_X_FOLDABLE">860</integer>
101 <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer> 101 <integer name="BUTTON_ZR_Y_FOLDABLE">70</integer>
102 <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer> 102 <integer name="BUTTON_STICK_L_X_FOLDABLE">550</integer>
103 <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer> 103 <integer name="BUTTON_STICK_L_Y_FOLDABLE">210</integer>
104 <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer> 104 <integer name="BUTTON_STICK_R_X_FOLDABLE">550</integer>
105 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> 105 <integer name="BUTTON_STICK_R_Y_FOLDABLE">280</integer>
106 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> 106 <integer name="STICK_L_X_FOLDABLE">180</integer>
107 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> 107 <integer name="STICK_L_Y_FOLDABLE">250</integer>
108 <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer> 108 <integer name="STICK_R_X_FOLDABLE">820</integer>
109 <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer> 109 <integer name="STICK_R_Y_FOLDABLE">250</integer>
110 <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer> 110 <integer name="COMBINED_DPAD_X_FOLDABLE">240</integer>
111 <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer> 111 <integer name="COMBINED_DPAD_Y_FOLDABLE">390</integer>
112 112
113</resources> 113</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 4d5c268fe..1bedcb1ef 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -366,6 +366,7 @@
366 <string name="emulation_pause">Pause emulation</string> 366 <string name="emulation_pause">Pause emulation</string>
367 <string name="emulation_unpause">Unpause emulation</string> 367 <string name="emulation_unpause">Unpause emulation</string>
368 <string name="emulation_input_overlay">Overlay options</string> 368 <string name="emulation_input_overlay">Overlay options</string>
369 <string name="touchscreen">Touchscreen</string>
369 370
370 <string name="load_settings">Loading settings…</string> 371 <string name="load_settings">Loading settings…</string>
371 372
diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts
index 51e559321..b77906ed6 100644
--- a/src/android/build.gradle.kts
+++ b/src/android/build.gradle.kts
@@ -5,7 +5,7 @@
5plugins { 5plugins {
6 id("com.android.application") version "8.1.2" apply false 6 id("com.android.application") version "8.1.2" apply false
7 id("com.android.library") version "8.1.2" apply false 7 id("com.android.library") version "8.1.2" apply false
8 id("org.jetbrains.kotlin.android") version "1.8.21" apply false 8 id("org.jetbrains.kotlin.android") version "1.9.20" apply false
9} 9}
10 10
11tasks.register("clean").configure { 11tasks.register("clean").configure {
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index c41d9d1ea..ee42ae529 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -18,9 +18,7 @@ constexpr auto INCREMENT_TIME{5ms};
18DeviceSession::DeviceSession(Core::System& system_) 18DeviceSession::DeviceSession(Core::System& system_)
19 : system{system_}, thread_event{Core::Timing::CreateEvent( 19 : system{system_}, thread_event{Core::Timing::CreateEvent(
20 "AudioOutSampleTick", 20 "AudioOutSampleTick",
21 [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { 21 [this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
22 return ThreadFunc();
23 })} {}
24 22
25DeviceSession::~DeviceSession() { 23DeviceSession::~DeviceSession() {
26 Finalize(); 24 Finalize();
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index b58a7073f..8c57d47c6 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,6 +64,8 @@ add_library(common STATIC
64 fs/path_util.cpp 64 fs/path_util.cpp
65 fs/path_util.h 65 fs/path_util.h
66 hash.h 66 hash.h
67 heap_tracker.cpp
68 heap_tracker.h
67 hex_util.cpp 69 hex_util.cpp
68 hex_util.h 70 hex_util.h
69 host_memory.cpp 71 host_memory.cpp
diff --git a/src/common/heap_tracker.cpp b/src/common/heap_tracker.cpp
new file mode 100644
index 000000000..683208795
--- /dev/null
+++ b/src/common/heap_tracker.cpp
@@ -0,0 +1,281 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fstream>
5#include <vector>
6
7#include "common/heap_tracker.h"
8#include "common/logging/log.h"
9
10namespace Common {
11
12namespace {
13
14s64 GetMaxPermissibleResidentMapCount() {
15 // Default value.
16 s64 value = 65530;
17
18 // Try to read how many mappings we can make.
19 std::ifstream s("/proc/sys/vm/max_map_count");
20 s >> value;
21
22 // Print, for debug.
23 LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
24
25 // Allow 20000 maps for other code and to account for split inaccuracy.
26 return std::max<s64>(value - 20000, 0);
27}
28
29} // namespace
30
31HeapTracker::HeapTracker(Common::HostMemory& buffer)
32 : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
33HeapTracker::~HeapTracker() = default;
34
35void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
36 MemoryPermission perm, bool is_separate_heap) {
37 // When mapping other memory, map pages immediately.
38 if (!is_separate_heap) {
39 m_buffer.Map(virtual_offset, host_offset, length, perm, false);
40 return;
41 }
42
43 {
44 // We are mapping part of a separate heap.
45 std::scoped_lock lk{m_lock};
46
47 auto* const map = new SeparateHeapMap{
48 .vaddr = virtual_offset,
49 .paddr = host_offset,
50 .size = length,
51 .tick = m_tick++,
52 .perm = perm,
53 .is_resident = false,
54 };
55
56 // Insert into mappings.
57 m_map_count++;
58 m_mappings.insert(*map);
59 }
60
61 // Finally, map.
62 this->DeferredMapSeparateHeap(virtual_offset);
63}
64
65void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
66 // If this is a separate heap...
67 if (is_separate_heap) {
68 std::scoped_lock lk{m_lock};
69
70 const SeparateHeapMap key{
71 .vaddr = virtual_offset,
72 };
73
74 // Split at the boundaries of the region we are removing.
75 this->SplitHeapMapLocked(virtual_offset);
76 this->SplitHeapMapLocked(virtual_offset + size);
77
78 // Erase all mappings in range.
79 auto it = m_mappings.find(key);
80 while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
81 // Get underlying item.
82 auto* const item = std::addressof(*it);
83
84 // If resident, erase from resident map.
85 if (item->is_resident) {
86 ASSERT(--m_resident_map_count >= 0);
87 m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
88 }
89
90 // Erase from map.
91 ASSERT(--m_map_count >= 0);
92 it = m_mappings.erase(it);
93
94 // Free the item.
95 delete item;
96 }
97 }
98
99 // Unmap pages.
100 m_buffer.Unmap(virtual_offset, size, false);
101}
102
103void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
104 // Ensure no rebuild occurs while reprotecting.
105 std::shared_lock lk{m_rebuild_lock};
106
107 // Split at the boundaries of the region we are reprotecting.
108 this->SplitHeapMap(virtual_offset, size);
109
110 // Declare tracking variables.
111 const VAddr end = virtual_offset + size;
112 VAddr cur = virtual_offset;
113
114 while (cur < end) {
115 VAddr next = cur;
116 bool should_protect = false;
117
118 {
119 std::scoped_lock lk2{m_lock};
120
121 const SeparateHeapMap key{
122 .vaddr = next,
123 };
124
125 // Try to get the next mapping corresponding to this address.
126 const auto it = m_mappings.nfind(key);
127
128 if (it == m_mappings.end()) {
129 // There are no separate heap mappings remaining.
130 next = end;
131 should_protect = true;
132 } else if (it->vaddr == cur) {
133 // We are in range.
134 // Update permission bits.
135 it->perm = perm;
136
137 // Determine next address and whether we should protect.
138 next = cur + it->size;
139 should_protect = it->is_resident;
140 } else /* if (it->vaddr > cur) */ {
141 // We weren't in range, but there is a block coming up that will be.
142 next = it->vaddr;
143 should_protect = true;
144 }
145 }
146
147 // Clamp to end.
148 next = std::min(next, end);
149
150 // Reprotect, if we need to.
151 if (should_protect) {
152 m_buffer.Protect(cur, next - cur, perm);
153 }
154
155 // Advance.
156 cur = next;
157 }
158}
159
160bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
161 if (m_buffer.IsInVirtualRange(fault_address)) {
162 return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
163 }
164
165 return false;
166}
167
168bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
169 bool rebuild_required = false;
170
171 {
172 std::scoped_lock lk{m_lock};
173
174 // Check to ensure this was a non-resident separate heap mapping.
175 const auto it = this->GetNearestHeapMapLocked(virtual_offset);
176 if (it == m_mappings.end() || it->is_resident) {
177 return false;
178 }
179
180 // Update tick before possible rebuild.
181 it->tick = m_tick++;
182
183 // Check if we need to rebuild.
184 if (m_resident_map_count > m_max_resident_map_count) {
185 rebuild_required = true;
186 }
187
188 // Map the area.
189 m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
190
191 // This map is now resident.
192 it->is_resident = true;
193 m_resident_map_count++;
194 m_resident_mappings.insert(*it);
195 }
196
197 if (rebuild_required) {
198 // A rebuild was required, so perform it now.
199 this->RebuildSeparateHeapAddressSpace();
200 }
201
202 return true;
203}
204
205void HeapTracker::RebuildSeparateHeapAddressSpace() {
206 std::scoped_lock lk{m_rebuild_lock, m_lock};
207
208 ASSERT(!m_resident_mappings.empty());
209
210 // Dump half of the mappings.
211 //
212 // Despite being worse in theory, this has proven to be better in practice than more
213 // regularly dumping a smaller amount, because it significantly reduces average case
214 // lock contention.
215 const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
216 const size_t evict_count = m_resident_map_count - desired_count;
217 auto it = m_resident_mappings.begin();
218
219 for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
220 // Unmark and unmap.
221 it->is_resident = false;
222 m_buffer.Unmap(it->vaddr, it->size, false);
223
224 // Advance.
225 ASSERT(--m_resident_map_count >= 0);
226 it = m_resident_mappings.erase(it);
227 }
228}
229
230void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
231 std::scoped_lock lk{m_lock};
232
233 this->SplitHeapMapLocked(offset);
234 this->SplitHeapMapLocked(offset + size);
235}
236
237void HeapTracker::SplitHeapMapLocked(VAddr offset) {
238 const auto it = this->GetNearestHeapMapLocked(offset);
239 if (it == m_mappings.end() || it->vaddr == offset) {
240 // Not contained or no split required.
241 return;
242 }
243
244 // Cache the original values.
245 auto* const left = std::addressof(*it);
246 const size_t orig_size = left->size;
247
248 // Adjust the left map.
249 const size_t left_size = offset - left->vaddr;
250 left->size = left_size;
251
252 // Create the new right map.
253 auto* const right = new SeparateHeapMap{
254 .vaddr = left->vaddr + left_size,
255 .paddr = left->paddr + left_size,
256 .size = orig_size - left_size,
257 .tick = left->tick,
258 .perm = left->perm,
259 .is_resident = left->is_resident,
260 };
261
262 // Insert the new right map.
263 m_map_count++;
264 m_mappings.insert(*right);
265
266 // If resident, also insert into resident map.
267 if (right->is_resident) {
268 m_resident_map_count++;
269 m_resident_mappings.insert(*right);
270 }
271}
272
273HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
274 const SeparateHeapMap key{
275 .vaddr = offset,
276 };
277
278 return m_mappings.find(key);
279}
280
281} // namespace Common
diff --git a/src/common/heap_tracker.h b/src/common/heap_tracker.h
new file mode 100644
index 000000000..ee5b0bf43
--- /dev/null
+++ b/src/common/heap_tracker.h
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <mutex>
8#include <set>
9#include <shared_mutex>
10
11#include "common/host_memory.h"
12#include "common/intrusive_red_black_tree.h"
13
14namespace Common {
15
16struct SeparateHeapMap {
17 Common::IntrusiveRedBlackTreeNode addr_node{};
18 Common::IntrusiveRedBlackTreeNode tick_node{};
19 VAddr vaddr{};
20 PAddr paddr{};
21 size_t size{};
22 size_t tick{};
23 MemoryPermission perm{};
24 bool is_resident{};
25};
26
27struct SeparateHeapMapAddrComparator {
28 static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
29 if (lhs.vaddr < rhs.vaddr) {
30 return -1;
31 } else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
32 return 0;
33 } else {
34 return 1;
35 }
36 }
37};
38
39struct SeparateHeapMapTickComparator {
40 static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
41 if (lhs.tick < rhs.tick) {
42 return -1;
43 } else if (lhs.tick > rhs.tick) {
44 return 1;
45 } else {
46 return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
47 }
48 }
49};
50
51class HeapTracker {
52public:
53 explicit HeapTracker(Common::HostMemory& buffer);
54 ~HeapTracker();
55
56 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
57 bool is_separate_heap);
58 void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
59 void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
60 u8* VirtualBasePointer() {
61 return m_buffer.VirtualBasePointer();
62 }
63
64 bool DeferredMapSeparateHeap(u8* fault_address);
65 bool DeferredMapSeparateHeap(size_t virtual_offset);
66
67private:
68 using AddrTreeTraits =
69 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
70 using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
71
72 using TickTreeTraits =
73 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
74 using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
75
76 AddrTree m_mappings{};
77 TickTree m_resident_mappings{};
78
79private:
80 void SplitHeapMap(VAddr offset, size_t size);
81 void SplitHeapMapLocked(VAddr offset);
82
83 AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
84
85 void RebuildSeparateHeapAddressSpace();
86
87private:
88 Common::HostMemory& m_buffer;
89 const s64 m_max_resident_map_count;
90
91 std::shared_mutex m_rebuild_lock{};
92 std::mutex m_lock{};
93 s64 m_map_count{};
94 s64 m_resident_map_count{};
95 size_t m_tick{};
96};
97
98} // namespace Common
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index e540375b8..860c39e6a 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
679HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; 679HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
680 680
681void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, 681void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
682 MemoryPermission perms) { 682 MemoryPermission perms, bool separate_heap) {
683 ASSERT(virtual_offset % PageAlignment == 0); 683 ASSERT(virtual_offset % PageAlignment == 0);
684 ASSERT(host_offset % PageAlignment == 0); 684 ASSERT(host_offset % PageAlignment == 0);
685 ASSERT(length % PageAlignment == 0); 685 ASSERT(length % PageAlignment == 0);
@@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
691 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); 691 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
692} 692}
693 693
694void HostMemory::Unmap(size_t virtual_offset, size_t length) { 694void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
695 ASSERT(virtual_offset % PageAlignment == 0); 695 ASSERT(virtual_offset % PageAlignment == 0);
696 ASSERT(length % PageAlignment == 0); 696 ASSERT(length % PageAlignment == 0);
697 ASSERT(virtual_offset + length <= virtual_size); 697 ASSERT(virtual_offset + length <= virtual_size);
@@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
701 impl->Unmap(virtual_offset + virtual_base_offset, length); 701 impl->Unmap(virtual_offset + virtual_base_offset, length);
702} 702}
703 703
704void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, 704void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
705 bool execute) {
706 ASSERT(virtual_offset % PageAlignment == 0); 705 ASSERT(virtual_offset % PageAlignment == 0);
707 ASSERT(length % PageAlignment == 0); 706 ASSERT(length % PageAlignment == 0);
708 ASSERT(virtual_offset + length <= virtual_size); 707 ASSERT(virtual_offset + length <= virtual_size);
709 if (length == 0 || !virtual_base || !impl) { 708 if (length == 0 || !virtual_base || !impl) {
710 return; 709 return;
711 } 710 }
711 const bool read = True(perm & MemoryPermission::Read);
712 const bool write = True(perm & MemoryPermission::Write);
713 const bool execute = True(perm & MemoryPermission::Execute);
712 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); 714 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
713} 715}
714 716
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 747c5850c..72fbb05af 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -40,11 +40,12 @@ public:
40 HostMemory(HostMemory&& other) noexcept; 40 HostMemory(HostMemory&& other) noexcept;
41 HostMemory& operator=(HostMemory&& other) noexcept; 41 HostMemory& operator=(HostMemory&& other) noexcept;
42 42
43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); 43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms,
44 bool separate_heap);
44 45
45 void Unmap(size_t virtual_offset, size_t length); 46 void Unmap(size_t virtual_offset, size_t length, bool separate_heap);
46 47
47 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); 48 void Protect(size_t virtual_offset, size_t length, MemoryPermission perms);
48 49
49 void EnableDirectMappedAddress(); 50 void EnableDirectMappedAddress();
50 51
@@ -64,6 +65,10 @@ public:
64 return virtual_base; 65 return virtual_base;
65 } 66 }
66 67
68 bool IsInVirtualRange(void* address) const noexcept {
69 return address >= virtual_base && address < virtual_base + virtual_size;
70 }
71
67private: 72private:
68 size_t backing_size{}; 73 size_t backing_size{};
69 size_t virtual_size{}; 74 size_t virtual_size{};
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index ea52bbfa6..07709d4e5 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -199,6 +199,8 @@ const char* TranslateCategory(Category category) {
199 case Category::CpuDebug: 199 case Category::CpuDebug:
200 case Category::CpuUnsafe: 200 case Category::CpuUnsafe:
201 return "Cpu"; 201 return "Cpu";
202 case Category::Overlay:
203 return "Overlay";
202 case Category::Renderer: 204 case Category::Renderer:
203 case Category::RendererAdvanced: 205 case Category::RendererAdvanced:
204 case Category::RendererDebug: 206 case Category::RendererDebug:
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index c82e17495..1a290ad77 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -18,6 +18,7 @@ enum class Category : u32 {
18 Cpu, 18 Cpu,
19 CpuDebug, 19 CpuDebug,
20 CpuUnsafe, 20 CpuUnsafe,
21 Overlay,
21 Renderer, 22 Renderer,
22 RendererAdvanced, 23 RendererAdvanced,
23 RendererDebug, 24 RendererDebug,
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 96ab39cb8..dfba79267 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -553,17 +553,26 @@ add_library(core STATIC
553 hle/service/hid/controllers/types/keyboard_types.h 553 hle/service/hid/controllers/types/keyboard_types.h
554 hle/service/hid/controllers/types/mouse_types.h 554 hle/service/hid/controllers/types/mouse_types.h
555 hle/service/hid/controllers/types/npad_types.h 555 hle/service/hid/controllers/types/npad_types.h
556 hle/service/hid/controllers/types/shared_memory_format.h
556 hle/service/hid/controllers/types/touch_types.h 557 hle/service/hid/controllers/types/touch_types.h
557 hle/service/hid/controllers/applet_resource.cpp 558 hle/service/hid/controllers/applet_resource.cpp
558 hle/service/hid/controllers/applet_resource.h 559 hle/service/hid/controllers/applet_resource.h
560 hle/service/hid/controllers/capture_button.cpp
561 hle/service/hid/controllers/capture_button.h
559 hle/service/hid/controllers/console_six_axis.cpp 562 hle/service/hid/controllers/console_six_axis.cpp
560 hle/service/hid/controllers/console_six_axis.h 563 hle/service/hid/controllers/console_six_axis.h
561 hle/service/hid/controllers/controller_base.cpp 564 hle/service/hid/controllers/controller_base.cpp
562 hle/service/hid/controllers/controller_base.h 565 hle/service/hid/controllers/controller_base.h
566 hle/service/hid/controllers/debug_mouse.cpp
567 hle/service/hid/controllers/debug_mouse.h
563 hle/service/hid/controllers/debug_pad.cpp 568 hle/service/hid/controllers/debug_pad.cpp
564 hle/service/hid/controllers/debug_pad.h 569 hle/service/hid/controllers/debug_pad.h
570 hle/service/hid/controllers/digitizer.cpp
571 hle/service/hid/controllers/digitizer.h
565 hle/service/hid/controllers/gesture.cpp 572 hle/service/hid/controllers/gesture.cpp
566 hle/service/hid/controllers/gesture.h 573 hle/service/hid/controllers/gesture.h
574 hle/service/hid/controllers/home_button.cpp
575 hle/service/hid/controllers/home_button.h
567 hle/service/hid/controllers/keyboard.cpp 576 hle/service/hid/controllers/keyboard.cpp
568 hle/service/hid/controllers/keyboard.h 577 hle/service/hid/controllers/keyboard.h
569 hle/service/hid/controllers/mouse.cpp 578 hle/service/hid/controllers/mouse.cpp
@@ -574,15 +583,16 @@ add_library(core STATIC
574 hle/service/hid/controllers/palma.h 583 hle/service/hid/controllers/palma.h
575 hle/service/hid/controllers/seven_six_axis.cpp 584 hle/service/hid/controllers/seven_six_axis.cpp
576 hle/service/hid/controllers/seven_six_axis.h 585 hle/service/hid/controllers/seven_six_axis.h
577 hle/service/hid/controllers/shared_memory_format.h
578 hle/service/hid/controllers/shared_memory_holder.cpp 586 hle/service/hid/controllers/shared_memory_holder.cpp
579 hle/service/hid/controllers/shared_memory_holder.h 587 hle/service/hid/controllers/shared_memory_holder.h
580 hle/service/hid/controllers/six_axis.cpp 588 hle/service/hid/controllers/six_axis.cpp
581 hle/service/hid/controllers/six_axis.h 589 hle/service/hid/controllers/six_axis.h
582 hle/service/hid/controllers/stubbed.cpp 590 hle/service/hid/controllers/sleep_button.cpp
583 hle/service/hid/controllers/stubbed.h 591 hle/service/hid/controllers/sleep_button.h
584 hle/service/hid/controllers/touchscreen.cpp 592 hle/service/hid/controllers/touchscreen.cpp
585 hle/service/hid/controllers/touchscreen.h 593 hle/service/hid/controllers/touchscreen.h
594 hle/service/hid/controllers/unique_pad.cpp
595 hle/service/hid/controllers/unique_pad.h
586 hle/service/hid/hidbus/hidbus_base.cpp 596 hle/service/hid/hidbus/hidbus_base.cpp
587 hle/service/hid/hidbus/hidbus_base.h 597 hle/service/hid/hidbus/hidbus_base.h
588 hle/service/hid/hidbus/ringcon.cpp 598 hle/service/hid/hidbus/ringcon.cpp
@@ -978,6 +988,7 @@ endif()
978 988
979if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 989if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
980 target_sources(core PRIVATE 990 target_sources(core PRIVATE
991 arm/dynarmic/arm_dynarmic.cpp
981 arm/dynarmic/arm_dynarmic.h 992 arm/dynarmic/arm_dynarmic.h
982 arm/dynarmic/arm_dynarmic_64.cpp 993 arm/dynarmic/arm_dynarmic_64.cpp
983 arm/dynarmic/arm_dynarmic_64.h 994 arm/dynarmic/arm_dynarmic_64.h
@@ -987,6 +998,8 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
987 arm/dynarmic/dynarmic_cp15.h 998 arm/dynarmic/dynarmic_cp15.h
988 arm/dynarmic/dynarmic_exclusive_monitor.cpp 999 arm/dynarmic/dynarmic_exclusive_monitor.cpp
989 arm/dynarmic/dynarmic_exclusive_monitor.h 1000 arm/dynarmic/dynarmic_exclusive_monitor.h
1001 hle/service/jit/jit_code_memory.cpp
1002 hle/service/jit/jit_code_memory.h
990 hle/service/jit/jit_context.cpp 1003 hle/service/jit/jit_context.cpp
991 hle/service/jit/jit_context.h 1004 hle/service/jit/jit_context.h
992 hle/service/jit/jit.cpp 1005 hle/service/jit/jit.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
new file mode 100644
index 000000000..e6e9fc45b
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#ifdef __linux__
5
6#include "common/signal_chain.h"
7
8#include "core/arm/dynarmic/arm_dynarmic.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/memory.h"
11
12namespace Core {
13
14namespace {
15
16thread_local Core::Memory::Memory* g_current_memory{};
17std::once_flag g_registered{};
18struct sigaction g_old_segv {};
19
20void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
21 if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
22 return;
23 }
24
25 return g_old_segv.sa_sigaction(sig, info, ctx);
26}
27
28} // namespace
29
30ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
31 g_current_memory = std::addressof(process->GetMemory());
32}
33
34ScopedJitExecution::~ScopedJitExecution() {
35 g_current_memory = nullptr;
36}
37
38void ScopedJitExecution::RegisterHandler() {
39 std::call_once(g_registered, [] {
40 struct sigaction sa {};
41 sa.sa_sigaction = &HandleSigSegv;
42 sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
43 Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
44 });
45}
46
47} // namespace Core
48
49#endif
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index eef7c3116..53dd18815 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
26 return static_cast<HaltReason>(hr); 26 return static_cast<HaltReason>(hr);
27} 27}
28 28
29#ifdef __linux__
30
31class ScopedJitExecution {
32public:
33 explicit ScopedJitExecution(Kernel::KProcess* process);
34 ~ScopedJitExecution();
35 static void RegisterHandler();
36};
37
38#else
39
40class ScopedJitExecution {
41public:
42 explicit ScopedJitExecution(Kernel::KProcess* process) {}
43 ~ScopedJitExecution() {}
44 static void RegisterHandler() {}
45};
46
47#endif
48
29} // namespace Core 49} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index c78cfd528..36478f722 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
331} 331}
332 332
333HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { 333HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
334 ScopedJitExecution sj(thread->GetOwnerProcess());
335
334 m_jit->ClearExclusiveState(); 336 m_jit->ClearExclusiveState();
335 return TranslateHaltReason(m_jit->Run()); 337 return TranslateHaltReason(m_jit->Run());
336} 338}
337 339
338HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { 340HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
341 ScopedJitExecution sj(thread->GetOwnerProcess());
342
339 m_jit->ClearExclusiveState(); 343 m_jit->ClearExclusiveState();
340 return TranslateHaltReason(m_jit->Step()); 344 return TranslateHaltReason(m_jit->Step());
341} 345}
@@ -377,6 +381,7 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc
377 m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} { 381 m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
378 auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); 382 auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
379 m_jit = MakeJit(&page_table_impl); 383 m_jit = MakeJit(&page_table_impl);
384 ScopedJitExecution::RegisterHandler();
380} 385}
381 386
382ArmDynarmic32::~ArmDynarmic32() = default; 387ArmDynarmic32::~ArmDynarmic32() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index f351b13d9..c811c8ad5 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
362} 362}
363 363
364HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { 364HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
365 ScopedJitExecution sj(thread->GetOwnerProcess());
366
365 m_jit->ClearExclusiveState(); 367 m_jit->ClearExclusiveState();
366 return TranslateHaltReason(m_jit->Run()); 368 return TranslateHaltReason(m_jit->Run());
367} 369}
368 370
369HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { 371HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
372 ScopedJitExecution sj(thread->GetOwnerProcess());
373
370 m_jit->ClearExclusiveState(); 374 m_jit->ClearExclusiveState();
371 return TranslateHaltReason(m_jit->Step()); 375 return TranslateHaltReason(m_jit->Step());
372} 376}
@@ -406,6 +410,7 @@ ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProc
406 auto& page_table = process->GetPageTable().GetBasePageTable(); 410 auto& page_table = process->GetPageTable().GetBasePageTable();
407 auto& page_table_impl = page_table.GetImpl(); 411 auto& page_table_impl = page_table.GetImpl();
408 m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); 412 m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
413 ScopedJitExecution::RegisterHandler();
409} 414}
410 415
411ArmDynarmic64::~ArmDynarmic64() = default; 416ArmDynarmic64::~ArmDynarmic64() = default;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index d6b5abc68..fc536413b 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -29,7 +29,6 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
29struct CoreTiming::Event { 29struct CoreTiming::Event {
30 s64 time; 30 s64 time;
31 u64 fifo_order; 31 u64 fifo_order;
32 std::uintptr_t user_data;
33 std::weak_ptr<EventType> type; 32 std::weak_ptr<EventType> type;
34 s64 reschedule_time; 33 s64 reschedule_time;
35 heap_t::handle_type handle{}; 34 heap_t::handle_type handle{};
@@ -67,17 +66,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
67 event_fifo_id = 0; 66 event_fifo_id = 0;
68 shutting_down = false; 67 shutting_down = false;
69 cpu_ticks = 0; 68 cpu_ticks = 0;
70 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
71 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
72 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
73 if (is_multicore) { 69 if (is_multicore) {
74 timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this)); 70 timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
75 } 71 }
76} 72}
77 73
78void CoreTiming::ClearPendingEvents() { 74void CoreTiming::ClearPendingEvents() {
79 std::scoped_lock lock{basic_lock}; 75 std::scoped_lock lock{advance_lock, basic_lock};
80 event_queue.clear(); 76 event_queue.clear();
77 event.Set();
81} 78}
82 79
83void CoreTiming::Pause(bool is_paused) { 80void CoreTiming::Pause(bool is_paused) {
@@ -119,14 +116,12 @@ bool CoreTiming::HasPendingEvents() const {
119} 116}
120 117
121void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, 118void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
122 const std::shared_ptr<EventType>& event_type, 119 const std::shared_ptr<EventType>& event_type, bool absolute_time) {
123 std::uintptr_t user_data, bool absolute_time) {
124 { 120 {
125 std::scoped_lock scope{basic_lock}; 121 std::scoped_lock scope{basic_lock};
126 const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; 122 const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
127 123
128 auto h{event_queue.emplace( 124 auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})};
129 Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
130 (*h).handle = h; 125 (*h).handle = h;
131 } 126 }
132 127
@@ -136,13 +131,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
136void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, 131void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
137 std::chrono::nanoseconds resched_time, 132 std::chrono::nanoseconds resched_time,
138 const std::shared_ptr<EventType>& event_type, 133 const std::shared_ptr<EventType>& event_type,
139 std::uintptr_t user_data, bool absolute_time) { 134 bool absolute_time) {
140 { 135 {
141 std::scoped_lock scope{basic_lock}; 136 std::scoped_lock scope{basic_lock};
142 const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; 137 const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
143 138
144 auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type, 139 auto h{event_queue.emplace(
145 resched_time.count()})}; 140 Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
146 (*h).handle = h; 141 (*h).handle = h;
147 } 142 }
148 143
@@ -150,14 +145,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
150} 145}
151 146
152void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, 147void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
153 std::uintptr_t user_data, bool wait) { 148 UnscheduleEventType type) {
154 { 149 {
155 std::scoped_lock lk{basic_lock}; 150 std::scoped_lock lk{basic_lock};
156 151
157 std::vector<heap_t::handle_type> to_remove; 152 std::vector<heap_t::handle_type> to_remove;
158 for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) { 153 for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
159 const Event& e = *itr; 154 const Event& e = *itr;
160 if (e.type.lock().get() == event_type.get() && e.user_data == user_data) { 155 if (e.type.lock().get() == event_type.get()) {
161 to_remove.push_back(itr->handle); 156 to_remove.push_back(itr->handle);
162 } 157 }
163 } 158 }
@@ -165,10 +160,12 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
165 for (auto h : to_remove) { 160 for (auto h : to_remove) {
166 event_queue.erase(h); 161 event_queue.erase(h);
167 } 162 }
163
164 event_type->sequence_number++;
168 } 165 }
169 166
170 // Force any in-progress events to finish 167 // Force any in-progress events to finish
171 if (wait) { 168 if (type == UnscheduleEventType::Wait) {
172 std::scoped_lock lk{advance_lock}; 169 std::scoped_lock lk{advance_lock};
173 } 170 }
174} 171}
@@ -208,28 +205,31 @@ std::optional<s64> CoreTiming::Advance() {
208 const Event& evt = event_queue.top(); 205 const Event& evt = event_queue.top();
209 206
210 if (const auto event_type{evt.type.lock()}) { 207 if (const auto event_type{evt.type.lock()}) {
211 if (evt.reschedule_time == 0) { 208 const auto evt_time = evt.time;
212 const auto evt_user_data = evt.user_data; 209 const auto evt_sequence_num = event_type->sequence_number;
213 const auto evt_time = evt.time;
214 210
211 if (evt.reschedule_time == 0) {
215 event_queue.pop(); 212 event_queue.pop();
216 213
217 basic_lock.unlock(); 214 basic_lock.unlock();
218 215
219 event_type->callback( 216 event_type->callback(
220 evt_user_data, evt_time, 217 evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
221 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
222 218
223 basic_lock.lock(); 219 basic_lock.lock();
224 } else { 220 } else {
225 basic_lock.unlock(); 221 basic_lock.unlock();
226 222
227 const auto new_schedule_time{event_type->callback( 223 const auto new_schedule_time{event_type->callback(
228 evt.user_data, evt.time, 224 evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})};
229 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
230 225
231 basic_lock.lock(); 226 basic_lock.lock();
232 227
228 if (evt_sequence_num != event_type->sequence_number) {
229 // Heap handle is invalidated after external modification.
230 continue;
231 }
232
233 const auto next_schedule_time{new_schedule_time.has_value() 233 const auto next_schedule_time{new_schedule_time.has_value()
234 ? new_schedule_time.value().count() 234 ? new_schedule_time.value().count()
235 : evt.reschedule_time}; 235 : evt.reschedule_time};
@@ -241,8 +241,8 @@ std::optional<s64> CoreTiming::Advance() {
241 next_time = pause_end_time + next_schedule_time; 241 next_time = pause_end_time + next_schedule_time;
242 } 242 }
243 243
244 event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data, 244 event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
245 evt.type, next_schedule_time, evt.handle}); 245 next_schedule_time, evt.handle});
246 } 246 }
247 } 247 }
248 248
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 21548f0a9..7e4dff7f3 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -22,17 +22,25 @@ namespace Core::Timing {
22 22
23/// A callback that may be scheduled for a particular core timing event. 23/// A callback that may be scheduled for a particular core timing event.
24using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( 24using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
25 std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; 25 s64 time, std::chrono::nanoseconds ns_late)>;
26 26
27/// Contains the characteristics of a particular event. 27/// Contains the characteristics of a particular event.
28struct EventType { 28struct EventType {
29 explicit EventType(TimedCallback&& callback_, std::string&& name_) 29 explicit EventType(TimedCallback&& callback_, std::string&& name_)
30 : callback{std::move(callback_)}, name{std::move(name_)} {} 30 : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
31 31
32 /// The event's callback function. 32 /// The event's callback function.
33 TimedCallback callback; 33 TimedCallback callback;
34 /// A pointer to the name of the event. 34 /// A pointer to the name of the event.
35 const std::string name; 35 const std::string name;
36 /// A monotonic sequence number, incremented when this event is
37 /// changed externally.
38 size_t sequence_number;
39};
40
41enum class UnscheduleEventType {
42 Wait,
43 NoWait,
36}; 44};
37 45
38/** 46/**
@@ -89,23 +97,17 @@ public:
89 97
90 /// Schedules an event in core timing 98 /// Schedules an event in core timing
91 void ScheduleEvent(std::chrono::nanoseconds ns_into_future, 99 void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
92 const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, 100 const std::shared_ptr<EventType>& event_type, bool absolute_time = false);
93 bool absolute_time = false);
94 101
95 /// Schedules an event which will automatically re-schedule itself with the given time, until 102 /// Schedules an event which will automatically re-schedule itself with the given time, until
96 /// unscheduled 103 /// unscheduled
97 void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, 104 void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
98 std::chrono::nanoseconds resched_time, 105 std::chrono::nanoseconds resched_time,
99 const std::shared_ptr<EventType>& event_type, 106 const std::shared_ptr<EventType>& event_type,
100 std::uintptr_t user_data = 0, bool absolute_time = false); 107 bool absolute_time = false);
101 108
102 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data, 109 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
103 bool wait = true); 110 UnscheduleEventType type = UnscheduleEventType::Wait);
104
105 void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
106 std::uintptr_t user_data) {
107 UnscheduleEvent(event_type, user_data, false);
108 }
109 111
110 void AddTicks(u64 ticks_to_add); 112 void AddTicks(u64 ticks_to_add);
111 113
@@ -158,7 +160,6 @@ private:
158 heap_t event_queue; 160 heap_t event_queue;
159 u64 event_fifo_id = 0; 161 u64 event_fifo_id = 0;
160 162
161 std::shared_ptr<EventType> ev_lost;
162 Common::Event event{}; 163 Common::Event event{};
163 Common::Event pause_event{}; 164 Common::Event pause_event{};
164 mutable std::mutex basic_lock; 165 mutable std::mutex basic_lock;
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 7be1322cc..31033634c 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -73,6 +73,9 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
73 return nullptr; 73 return nullptr;
74 74
75 auto in_data = in->ReadAllBytes(); 75 auto in_data = in->ReadAllBytes();
76 if (in_data.size() == 0) {
77 return nullptr;
78 }
76 79
77 std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); 80 std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
78 u64 offset = 5; // After header 81 u64 offset = 5; // After header
@@ -88,6 +91,10 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
88 else 91 else
89 real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2]; 92 real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
90 93
94 if (real_offset > in_data.size()) {
95 return nullptr;
96 }
97
91 u16 data_size{}; 98 u16 data_size{};
92 if (ips->ReadObject(&data_size, offset) != sizeof(u16)) 99 if (ips->ReadObject(&data_size, offset) != sizeof(u16))
93 return nullptr; 100 return nullptr;
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 8e2e40307..4e947dd6b 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -10,15 +10,15 @@ namespace Kernel {
10 10
11void KHardwareTimer::Initialize() { 11void KHardwareTimer::Initialize() {
12 // Create the timing callback to register with CoreTiming. 12 // Create the timing callback to register with CoreTiming.
13 m_event_type = Core::Timing::CreateEvent( 13 m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
14 "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { 14 [this](s64, std::chrono::nanoseconds) {
15 reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); 15 this->DoTask();
16 return std::nullopt; 16 return std::nullopt;
17 }); 17 });
18} 18}
19 19
20void KHardwareTimer::Finalize() { 20void KHardwareTimer::Finalize() {
21 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); 21 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type);
22 m_wakeup_time = std::numeric_limits<s64>::max(); 22 m_wakeup_time = std::numeric_limits<s64>::max();
23 m_event_type.reset(); 23 m_event_type.reset();
24} 24}
@@ -57,13 +57,12 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
57 57
58 m_wakeup_time = wakeup_time; 58 m_wakeup_time = wakeup_time;
59 m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, 59 m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
60 m_event_type, reinterpret_cast<uintptr_t>(this), 60 m_event_type, true);
61 true);
62} 61}
63 62
64void KHardwareTimer::DisableInterrupt() { 63void KHardwareTimer::DisableInterrupt() {
65 m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type, 64 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type,
66 reinterpret_cast<uintptr_t>(this)); 65 Core::Timing::UnscheduleEventType::NoWait);
67 m_wakeup_time = std::numeric_limits<s64>::max(); 66 m_wakeup_time = std::numeric_limits<s64>::max();
68} 67}
69 68
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 423289145..8c1549559 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
434void KPageTableBase::Finalize() { 434void KPageTableBase::Finalize() {
435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { 435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
436 if (Settings::IsFastmemEnabled()) { 436 if (Settings::IsFastmemEnabled()) {
437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); 437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
438 } 438 }
439 }; 439 };
440 440
@@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5243 // Unmap. 5243 // Unmap.
5244 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, 5244 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
5245 cur_pages, 0, false, unmap_properties, 5245 cur_pages, 0, false, unmap_properties,
5246 OperationType::Unmap, true)); 5246 OperationType::UnmapPhysical, true));
5247 } 5247 }
5248 5248
5249 // Check if we're done. 5249 // Check if we're done.
@@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5326 // Map the papges. 5326 // Map the papges.
5327 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages, 5327 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
5328 cur_pg, map_properties, 5328 cur_pg, map_properties,
5329 OperationType::MapFirstGroup, false)); 5329 OperationType::MapFirstGroupPhysical, false));
5330 } 5330 }
5331 } 5331 }
5332 5332
@@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
5480 5480
5481 // Unmap. 5481 // Unmap.
5482 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, 5482 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
5483 unmap_properties, OperationType::Unmap, false)); 5483 unmap_properties, OperationType::UnmapPhysical, false));
5484 } 5484 }
5485 5485
5486 // Check if we're done. 5486 // Check if we're done.
@@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5655 // or free them to the page list, and so it goes unused (along with page properties). 5655 // or free them to the page list, and so it goes unused (along with page properties).
5656 5656
5657 switch (operation) { 5657 switch (operation) {
5658 case OperationType::Unmap: { 5658 case OperationType::Unmap:
5659 case OperationType::UnmapPhysical: {
5660 const bool separate_heap = operation == OperationType::UnmapPhysical;
5661
5659 // Ensure that any pages we track are closed on exit. 5662 // Ensure that any pages we track are closed on exit.
5660 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); 5663 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
5661 SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); 5664 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
@@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5664 this->MakePageGroup(pages_to_close, virt_addr, num_pages); 5667 this->MakePageGroup(pages_to_close, virt_addr, num_pages);
5665 5668
5666 // Unmap. 5669 // Unmap.
5667 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize); 5670 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
5668 5671
5669 R_SUCCEED(); 5672 R_SUCCEED();
5670 } 5673 }
@@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5672 ASSERT(virt_addr != 0); 5675 ASSERT(virt_addr != 0);
5673 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); 5676 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5674 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, 5677 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
5675 ConvertToMemoryPermission(properties.perm)); 5678 ConvertToMemoryPermission(properties.perm), false);
5676 5679
5677 // Open references to pages, if we should. 5680 // Open references to pages, if we should.
5678 if (this->IsHeapPhysicalAddress(phys_addr)) { 5681 if (this->IsHeapPhysicalAddress(phys_addr)) {
@@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5711 5714
5712 switch (operation) { 5715 switch (operation) {
5713 case OperationType::MapGroup: 5716 case OperationType::MapGroup:
5714 case OperationType::MapFirstGroup: { 5717 case OperationType::MapFirstGroup:
5718 case OperationType::MapFirstGroupPhysical: {
5719 const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
5720
5715 // We want to maintain a new reference to every page in the group. 5721 // We want to maintain a new reference to every page in the group.
5716 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); 5722 KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
5717 5723
5718 for (const auto& node : page_group) { 5724 for (const auto& node : page_group) {
5719 const size_t size{node.GetNumPages() * PageSize}; 5725 const size_t size{node.GetNumPages() * PageSize};
5720 5726
5721 // Map the pages. 5727 // Map the pages.
5722 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), 5728 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
5723 ConvertToMemoryPermission(properties.perm)); 5729 ConvertToMemoryPermission(properties.perm), separate_heap);
5724 5730
5725 virt_addr += size; 5731 virt_addr += size;
5726 } 5732 }
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 556d230b3..077cafc96 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -104,6 +104,9 @@ protected:
104 ChangePermissionsAndRefresh = 5, 104 ChangePermissionsAndRefresh = 5,
105 ChangePermissionsAndRefreshAndFlush = 6, 105 ChangePermissionsAndRefreshAndFlush = 6,
106 Separate = 7, 106 Separate = 7,
107
108 MapFirstGroupPhysical = 65000,
109 UnmapPhysical = 65001,
107 }; 110 };
108 111
109 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; 112 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index d6869c228..068e71dff 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1237,8 +1237,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1237 auto& buffer = m_kernel.System().DeviceMemory().buffer; 1237 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1238 const auto& code = code_set.CodeSegment(); 1238 const auto& code = code_set.CodeSegment();
1239 const auto& patch = code_set.PatchSegment(); 1239 const auto& patch = code_set.PatchSegment();
1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true); 1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size,
1241 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true); 1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
1243 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None); 1244 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
1243 } 1245 }
1244#endif 1246#endif
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index c14d2d2f3..1030f0c12 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -238,7 +238,7 @@ struct KernelCore::Impl {
238 void InitializePreemption(KernelCore& kernel) { 238 void InitializePreemption(KernelCore& kernel) {
239 preemption_event = Core::Timing::CreateEvent( 239 preemption_event = Core::Timing::CreateEvent(
240 "PreemptionCallback", 240 "PreemptionCallback",
241 [this, &kernel](std::uintptr_t, s64 time, 241 [this, &kernel](s64 time,
242 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { 242 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
243 { 243 {
244 KScopedSchedulerLock lock(kernel); 244 KScopedSchedulerLock lock(kernel);
diff --git a/src/core/hle/service/hid/controllers/applet_resource.cpp b/src/core/hle/service/hid/controllers/applet_resource.cpp
index c8e74c764..b4ff663c2 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.cpp
+++ b/src/core/hle/service/hid/controllers/applet_resource.cpp
@@ -4,7 +4,7 @@
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h" 5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/applet_resource.h" 6#include "core/hle/service/hid/controllers/applet_resource.h"
7#include "core/hle/service/hid/controllers/shared_memory_format.h" 7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8#include "core/hle/service/hid/errors.h" 8#include "core/hle/service/hid/errors.h"
9 9
10namespace Service::HID { 10namespace Service::HID {
@@ -164,6 +164,22 @@ Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_mem
164 return ResultSuccess; 164 return ResultSuccess;
165} 165}
166 166
167AruidData* AppletResource::GetAruidData(u64 aruid) {
168 const u64 aruid_index = GetIndexFromAruid(aruid);
169 if (aruid_index == AruidIndexMax) {
170 return nullptr;
171 }
172 return &data[aruid_index];
173}
174
175AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) {
176 return &data[aruid_index];
177}
178
179bool AppletResource::IsVibrationAruidActive(u64 aruid) const {
180 return aruid == 0 || aruid == active_vibration_aruid;
181}
182
167u64 AppletResource::GetIndexFromAruid(u64 aruid) { 183u64 AppletResource::GetIndexFromAruid(u64 aruid) {
168 for (std::size_t i = 0; i < AruidIndexMax; i++) { 184 for (std::size_t i = 0; i < AruidIndexMax; i++) {
169 if (registration_list.flag[i] == RegistrationStatus::Initialized && 185 if (registration_list.flag[i] == RegistrationStatus::Initialized &&
diff --git a/src/core/hle/service/hid/controllers/applet_resource.h b/src/core/hle/service/hid/controllers/applet_resource.h
index e7991f93a..52cc4cf42 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.h
+++ b/src/core/hle/service/hid/controllers/applet_resource.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <mutex>
7 8
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -20,6 +21,59 @@ class KSharedMemory;
20 21
21namespace Service::HID { 22namespace Service::HID {
22struct SharedMemoryFormat; 23struct SharedMemoryFormat;
24class AppletResource;
25class NPadResource;
26
27static constexpr std::size_t AruidIndexMax = 0x20;
28
29enum class RegistrationStatus : u32 {
30 None,
31 Initialized,
32 PendingDelete,
33};
34
35struct DataStatusFlag {
36 union {
37 u32 raw{};
38
39 BitField<0, 1, u32> is_initialized;
40 BitField<1, 1, u32> is_assigned;
41 BitField<16, 1, u32> enable_pad_input;
42 BitField<17, 1, u32> enable_six_axis_sensor;
43 BitField<18, 1, u32> bit_18;
44 BitField<19, 1, u32> is_palma_connectable;
45 BitField<20, 1, u32> enable_palma_boost_mode;
46 BitField<21, 1, u32> enable_touchscreen;
47 };
48};
49
50struct AruidRegisterList {
51 std::array<RegistrationStatus, AruidIndexMax> flag{};
52 std::array<u64, AruidIndexMax> aruid{};
53};
54static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
55
56struct AruidData {
57 DataStatusFlag flag{};
58 u64 aruid{};
59 SharedMemoryFormat* shared_memory_format{nullptr};
60};
61
62struct HandheldConfig {
63 bool is_handheld_hid_enabled;
64 bool is_force_handheld;
65 bool is_joycon_rail_enabled;
66 bool is_force_handheld_style_vibration;
67};
68static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size");
69
70struct AppletResourceHolder {
71 std::shared_ptr<AppletResource> applet_resource{nullptr};
72 std::recursive_mutex* shared_mutex{nullptr};
73 NPadResource* shared_npad_resource{nullptr};
74 std::shared_ptr<HandheldConfig> handheld_config{nullptr};
75 long* handle_1;
76};
23 77
24class AppletResource { 78class AppletResource {
25public: 79public:
@@ -36,6 +90,10 @@ public:
36 u64 GetActiveAruid(); 90 u64 GetActiveAruid();
37 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid); 91 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
38 Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid); 92 Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
93 AruidData* GetAruidData(u64 aruid);
94 AruidData* GetAruidDataByIndex(std::size_t aruid_index);
95
96 bool IsVibrationAruidActive(u64 aruid) const;
39 97
40 u64 GetIndexFromAruid(u64 aruid); 98 u64 GetIndexFromAruid(u64 aruid);
41 99
@@ -52,46 +110,12 @@ public:
52 Result UnregisterCoreAppletResource(); 110 Result UnregisterCoreAppletResource();
53 111
54private: 112private:
55 static constexpr std::size_t AruidIndexMax = 0x20;
56
57 enum RegistrationStatus : u32 {
58 None,
59 Initialized,
60 PendingDelete,
61 };
62
63 struct DataStatusFlag {
64 union {
65 u32 raw{};
66
67 BitField<0, 1, u32> is_initialized;
68 BitField<1, 1, u32> is_assigned;
69 BitField<16, 1, u32> enable_pad_input;
70 BitField<17, 1, u32> enable_six_axis_sensor;
71 BitField<18, 1, u32> bit_18;
72 BitField<19, 1, u32> is_palma_connectable;
73 BitField<20, 1, u32> enable_palma_boost_mode;
74 BitField<21, 1, u32> enable_touchscreen;
75 };
76 };
77
78 struct AruidRegisterList {
79 std::array<RegistrationStatus, AruidIndexMax> flag{};
80 std::array<u64, AruidIndexMax> aruid{};
81 };
82 static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
83
84 struct AruidData {
85 DataStatusFlag flag{};
86 u64 aruid{};
87 SharedMemoryFormat* shared_memory_format{nullptr};
88 };
89
90 u64 active_aruid{}; 113 u64 active_aruid{};
91 AruidRegisterList registration_list{}; 114 AruidRegisterList registration_list{};
92 std::array<AruidData, AruidIndexMax> data{}; 115 std::array<AruidData, AruidIndexMax> data{};
93 std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{}; 116 std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
94 s32 ref_counter{}; 117 s32 ref_counter{};
118 u64 active_vibration_aruid;
95 119
96 Core::System& system; 120 Core::System& system;
97}; 121};
diff --git a/src/core/hle/service/hid/controllers/capture_button.cpp b/src/core/hle/service/hid/controllers/capture_button.cpp
new file mode 100644
index 000000000..8b486fcb5
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/capture_button.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/capture_button.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13CaptureButton::~CaptureButton() = default;
14
15void CaptureButton::OnInit() {}
16
17void CaptureButton::OnRelease() {}
18
19void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 auto& header = data->shared_memory_format->capture_button.header;
32 header.timestamp = core_timing.GetGlobalTimeNs().count();
33 header.total_entry_count = 17;
34 header.entry_count = 0;
35 header.last_entry_index = 0;
36}
37
38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/capture_button.h
index d2052fb17..dcc4715c5 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/capture_button.h
@@ -6,12 +6,11 @@
6#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
7 7
8namespace Service::HID { 8namespace Service::HID {
9struct CommonHeader;
10 9
11class Controller_Stubbed final : public ControllerBase { 10class CaptureButton final : public ControllerBase {
12public: 11public:
13 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, CommonHeader& ring_lifo_header); 12 explicit CaptureButton(Core::HID::HIDCore& hid_core_);
14 ~Controller_Stubbed() override; 13 ~CaptureButton() override;
15 14
16 // Called when the controller is initialized 15 // Called when the controller is initialized
17 void OnInit() override; 16 void OnInit() override;
@@ -23,7 +22,6 @@ public:
23 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
24 23
25private: 24private:
26 CommonHeader& header;
27 bool smart_update{}; 25 bool smart_update{};
28}; 26};
29} // namespace Service::HID 27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
index 3961d2b5f..8eba2c292 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -5,13 +5,11 @@
5#include "core/hid/emulated_console.h" 5#include "core/hid/emulated_console.h"
6#include "core/hid/hid_core.h" 6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/controllers/console_six_axis.h" 7#include "core/hle/service/hid/controllers/console_six_axis.h"
8#include "core/hle/service/hid/controllers/shared_memory_format.h" 8#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
9 9
10namespace Service::HID { 10namespace Service::HID {
11 11
12ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, 12ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
13 ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory)
14 : ControllerBase{hid_core_}, shared_memory{console_shared_memory} {
15 console = hid_core.GetEmulatedConsole(); 13 console = hid_core.GetEmulatedConsole();
16} 14}
17 15
@@ -22,6 +20,15 @@ void ConsoleSixAxis::OnInit() {}
22void ConsoleSixAxis::OnRelease() {} 20void ConsoleSixAxis::OnRelease() {}
23 21
24void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 22void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
23 const u64 aruid = applet_resource->GetActiveAruid();
24 auto* data = applet_resource->GetAruidData(aruid);
25
26 if (data == nullptr) {
27 return;
28 }
29
30 ConsoleSixAxisSensorSharedMemoryFormat& shared_memory = data->shared_memory_format->console;
31
25 if (!IsControllerActivated()) { 32 if (!IsControllerActivated()) {
26 return; 33 return;
27 } 34 }
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
index 3d1c9ce23..e3351f83c 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.h
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -10,12 +10,9 @@ class EmulatedConsole;
10} // namespace Core::HID 10} // namespace Core::HID
11 11
12namespace Service::HID { 12namespace Service::HID {
13struct ConsoleSixAxisSensorSharedMemoryFormat;
14
15class ConsoleSixAxis final : public ControllerBase { 13class ConsoleSixAxis final : public ControllerBase {
16public: 14public:
17 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, 15 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
18 ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory);
19 ~ConsoleSixAxis() override; 16 ~ConsoleSixAxis() override;
20 17
21 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -28,7 +25,6 @@ public:
28 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 25 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
29 26
30private: 27private:
31 ConsoleSixAxisSensorSharedMemoryFormat& shared_memory;
32 Core::HID::EmulatedConsole* console = nullptr; 28 Core::HID::EmulatedConsole* console = nullptr;
33}; 29};
34} // namespace Service::HID 30} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 0bcd87062..2083ccfad 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -31,4 +31,9 @@ void ControllerBase::DeactivateController() {
31bool ControllerBase::IsControllerActivated() const { 31bool ControllerBase::IsControllerActivated() const {
32 return is_activated; 32 return is_activated;
33} 33}
34
35void ControllerBase::SetAppletResource(std::shared_ptr<AppletResource> resource) {
36 applet_resource = resource;
37}
38
34} // namespace Service::HID 39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 4326c7821..759ae0053 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -3,8 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <memory>
7
6#include "common/common_types.h" 8#include "common/common_types.h"
7#include "core/hle/result.h" 9#include "core/hle/result.h"
10#include "core/hle/service/hid/controllers/applet_resource.h"
8 11
9namespace Core::Timing { 12namespace Core::Timing {
10class CoreTiming; 13class CoreTiming;
@@ -12,7 +15,7 @@ class CoreTiming;
12 15
13namespace Core::HID { 16namespace Core::HID {
14class HIDCore; 17class HIDCore;
15} 18} // namespace Core::HID
16 19
17namespace Service::HID { 20namespace Service::HID {
18class ControllerBase { 21class ControllerBase {
@@ -39,8 +42,11 @@ public:
39 42
40 bool IsControllerActivated() const; 43 bool IsControllerActivated() const;
41 44
45 void SetAppletResource(std::shared_ptr<AppletResource> resource);
46
42protected: 47protected:
43 bool is_activated{false}; 48 bool is_activated{false};
49 std::shared_ptr<AppletResource> applet_resource{nullptr};
44 50
45 Core::HID::HIDCore& hid_core; 51 Core::HID::HIDCore& hid_core;
46}; 52};
diff --git a/src/core/hle/service/hid/controllers/debug_mouse.cpp b/src/core/hle/service/hid/controllers/debug_mouse.cpp
new file mode 100644
index 000000000..f2f1a27f8
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_mouse.cpp
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/applet_resource.h"
9#include "core/hle/service/hid/controllers/debug_mouse.h"
10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11
12namespace Service::HID {
13
14DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 emulated_devices = hid_core.GetEmulatedDevices();
16}
17
18DebugMouse::~DebugMouse() = default;
19
20void DebugMouse::OnInit() {}
21void DebugMouse::OnRelease() {}
22
23void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_mouse;
32
33 if (!IsControllerActivated()) {
34 shared_memory.mouse_lifo.buffer_count = 0;
35 shared_memory.mouse_lifo.buffer_tail = 0;
36 return;
37 }
38
39 next_state = {};
40
41 const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
42 next_state.sampling_number = last_entry.sampling_number + 1;
43
44 if (Settings::values.mouse_enabled) {
45 const auto& mouse_button_state = emulated_devices->GetMouseButtons();
46 const auto& mouse_position_state = emulated_devices->GetMousePosition();
47 const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
48 next_state.attribute.is_connected.Assign(1);
49 next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
50 next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
51 next_state.delta_x = next_state.x - last_entry.x;
52 next_state.delta_y = next_state.y - last_entry.y;
53 next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
54 next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
55
56 last_mouse_wheel_state = mouse_wheel_state;
57 next_state.button = mouse_button_state;
58 }
59
60 shared_memory.mouse_lifo.WriteNextEntry(next_state);
61}
62
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_mouse.h b/src/core/hle/service/hid/controllers/debug_mouse.h
new file mode 100644
index 000000000..ec939fa9f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_mouse.h
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Core::HID {
9class EmulatedDevices;
10struct MouseState;
11struct AnalogStickState;
12} // namespace Core::HID
13
14namespace Service::HID {
15class DebugMouse final : public ControllerBase {
16public:
17 explicit DebugMouse(Core::HID::HIDCore& hid_core_);
18 ~DebugMouse() override;
19
20 // Called when the controller is initialized
21 void OnInit() override;
22
23 // When the controller is released
24 void OnRelease() override;
25
26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
28
29private:
30 Core::HID::MouseState next_state{};
31 Core::HID::AnalogStickState last_mouse_wheel_state{};
32 Core::HID::EmulatedDevices* emulated_devices = nullptr;
33};
34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 7d2370b4f..1811cf620 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,14 +6,13 @@
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/applet_resource.h"
9#include "core/hle/service/hid/controllers/debug_pad.h" 10#include "core/hle/service/hid/controllers/debug_pad.h"
10#include "core/hle/service/hid/controllers/shared_memory_format.h" 11#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
13 14
14DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, 15DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 DebugPadSharedMemoryFormat& debug_pad_shared_memory)
16 : ControllerBase{hid_core_}, shared_memory{debug_pad_shared_memory} {
17 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 16 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
18} 17}
19 18
@@ -24,6 +23,15 @@ void DebugPad::OnInit() {}
24void DebugPad::OnRelease() {} 23void DebugPad::OnRelease() {}
25 24
26void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 25void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
26 const u64 aruid = applet_resource->GetActiveAruid();
27 auto* data = applet_resource->GetAruidData(aruid);
28
29 if (data == nullptr) {
30 return;
31 }
32
33 DebugPadSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_pad;
34
27 if (!IsControllerActivated()) { 35 if (!IsControllerActivated()) {
28 shared_memory.debug_pad_lifo.buffer_count = 0; 36 shared_memory.debug_pad_lifo.buffer_count = 0;
29 shared_memory.debug_pad_lifo.buffer_tail = 0; 37 shared_memory.debug_pad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 8ab29eca8..dd00b2402 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -15,12 +15,9 @@ class CoreTiming;
15} 15}
16 16
17namespace Service::HID { 17namespace Service::HID {
18struct DebugPadSharedMemoryFormat;
19
20class DebugPad final : public ControllerBase { 18class DebugPad final : public ControllerBase {
21public: 19public:
22 explicit DebugPad(Core::HID::HIDCore& hid_core_, 20 explicit DebugPad(Core::HID::HIDCore& hid_core_);
23 DebugPadSharedMemoryFormat& debug_pad_shared_memory);
24 ~DebugPad() override; 21 ~DebugPad() override;
25 22
26 // Called when the controller is initialized 23 // Called when the controller is initialized
@@ -34,7 +31,6 @@ public:
34 31
35private: 32private:
36 DebugPadState next_state{}; 33 DebugPadState next_state{};
37 DebugPadSharedMemoryFormat& shared_memory;
38 Core::HID::EmulatedController* controller = nullptr; 34 Core::HID::EmulatedController* controller = nullptr;
39}; 35};
40} // namespace Service::HID 36} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/digitizer.cpp b/src/core/hle/service/hid/controllers/digitizer.cpp
new file mode 100644
index 000000000..c01580fd6
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/digitizer.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/digitizer.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13Digitizer::~Digitizer() = default;
14
15void Digitizer::OnInit() {}
16
17void Digitizer::OnRelease() {}
18
19void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 auto& header = data->shared_memory_format->digitizer.header;
32 header.timestamp = core_timing.GetGlobalTimeNs().count();
33 header.total_entry_count = 17;
34 header.entry_count = 0;
35 header.last_entry_index = 0;
36}
37
38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/digitizer.h b/src/core/hle/service/hid/controllers/digitizer.h
new file mode 100644
index 000000000..d81f814c3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/digitizer.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class Digitizer final : public ControllerBase {
11public:
12 explicit Digitizer(Core::HID::HIDCore& hid_core_);
13 ~Digitizer() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index f658005f6..6e686fe65 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -6,8 +6,9 @@
6#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
7#include "core/hid/emulated_console.h" 7#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h" 8#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/applet_resource.h"
9#include "core/hle/service/hid/controllers/gesture.h" 10#include "core/hle/service/hid/controllers/gesture.h"
10#include "core/hle/service/hid/controllers/shared_memory_format.h" 11#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
13// HW is around 700, value is set to 400 to make it easier to trigger with mouse 14// HW is around 700, value is set to 400 to make it easier to trigger with mouse
@@ -21,24 +22,40 @@ constexpr f32 Square(s32 num) {
21 return static_cast<f32>(num * num); 22 return static_cast<f32>(num * num);
22} 23}
23 24
24Gesture::Gesture(Core::HID::HIDCore& hid_core_, GestureSharedMemoryFormat& gesture_shared_memory) 25Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
25 : ControllerBase(hid_core_), shared_memory{gesture_shared_memory} {
26 console = hid_core.GetEmulatedConsole(); 26 console = hid_core.GetEmulatedConsole();
27} 27}
28Gesture::~Gesture() = default; 28Gesture::~Gesture() = default;
29 29
30void Gesture::OnInit() { 30void Gesture::OnInit() {
31 shared_memory.gesture_lifo.buffer_count = 0; 31 const u64 aruid = applet_resource->GetActiveAruid();
32 shared_memory.gesture_lifo.buffer_tail = 0; 32 auto* data = applet_resource->GetAruidData(aruid);
33
34 if (data == nullptr) {
35 return;
36 }
37
38 shared_memory = &data->shared_memory_format->gesture;
39 shared_memory->gesture_lifo.buffer_count = 0;
40 shared_memory->gesture_lifo.buffer_tail = 0;
33 force_update = true; 41 force_update = true;
34} 42}
35 43
36void Gesture::OnRelease() {} 44void Gesture::OnRelease() {}
37 45
38void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 46void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
47 const u64 aruid = applet_resource->GetActiveAruid();
48 auto* data = applet_resource->GetAruidData(aruid);
49
50 if (data == nullptr) {
51 return;
52 }
53
54 shared_memory = &data->shared_memory_format->gesture;
55
39 if (!IsControllerActivated()) { 56 if (!IsControllerActivated()) {
40 shared_memory.gesture_lifo.buffer_count = 0; 57 shared_memory->gesture_lifo.buffer_count = 0;
41 shared_memory.gesture_lifo.buffer_tail = 0; 58 shared_memory->gesture_lifo.buffer_tail = 0;
42 return; 59 return;
43 } 60 }
44 61
@@ -46,7 +63,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
46 63
47 GestureProperties gesture = GetGestureProperties(); 64 GestureProperties gesture = GetGestureProperties();
48 f32 time_difference = 65 f32 time_difference =
49 static_cast<f32>(shared_memory.gesture_lifo.timestamp - last_update_timestamp) / 66 static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
50 (1000 * 1000 * 1000); 67 (1000 * 1000 * 1000);
51 68
52 // Only update if necessary 69 // Only update if necessary
@@ -54,7 +71,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
54 return; 71 return;
55 } 72 }
56 73
57 last_update_timestamp = shared_memory.gesture_lifo.timestamp; 74 last_update_timestamp = shared_memory->gesture_lifo.timestamp;
58 UpdateGestureSharedMemory(gesture, time_difference); 75 UpdateGestureSharedMemory(gesture, time_difference);
59} 76}
60 77
@@ -97,7 +114,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
97 GestureType type = GestureType::Idle; 114 GestureType type = GestureType::Idle;
98 GestureAttribute attributes{}; 115 GestureAttribute attributes{};
99 116
100 const auto& last_entry = shared_memory.gesture_lifo.ReadCurrentEntry().state; 117 const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
101 118
102 // Reset next state to default 119 // Reset next state to default
103 next_state.sampling_number = last_entry.sampling_number + 1; 120 next_state.sampling_number = last_entry.sampling_number + 1;
@@ -127,7 +144,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
127 next_state.points = gesture.points; 144 next_state.points = gesture.points;
128 last_gesture = gesture; 145 last_gesture = gesture;
129 146
130 shared_memory.gesture_lifo.WriteNextEntry(next_state); 147 shared_memory->gesture_lifo.WriteNextEntry(next_state);
131} 148}
132 149
133void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, 150void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
@@ -300,7 +317,7 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
300} 317}
301 318
302const GestureState& Gesture::GetLastGestureEntry() const { 319const GestureState& Gesture::GetLastGestureEntry() const {
303 return shared_memory.gesture_lifo.ReadCurrentEntry().state; 320 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
304} 321}
305 322
306GestureProperties Gesture::GetGestureProperties() { 323GestureProperties Gesture::GetGestureProperties() {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 41fdfcd03..78da1552a 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -18,8 +18,7 @@ struct GestureSharedMemoryFormat;
18 18
19class Gesture final : public ControllerBase { 19class Gesture final : public ControllerBase {
20public: 20public:
21 explicit Gesture(Core::HID::HIDCore& hid_core_, 21 explicit Gesture(Core::HID::HIDCore& hid_core_);
22 GestureSharedMemoryFormat& gesture_shared_memory);
23 ~Gesture() override; 22 ~Gesture() override;
24 23
25 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -74,7 +73,7 @@ private:
74 GestureProperties GetGestureProperties(); 73 GestureProperties GetGestureProperties();
75 74
76 GestureState next_state{}; 75 GestureState next_state{};
77 GestureSharedMemoryFormat& shared_memory; 76 GestureSharedMemoryFormat* shared_memory;
78 Core::HID::EmulatedConsole* console = nullptr; 77 Core::HID::EmulatedConsole* console = nullptr;
79 78
80 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; 79 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/home_button.cpp b/src/core/hle/service/hid/controllers/home_button.cpp
new file mode 100644
index 000000000..71dd9bc08
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/home_button.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/home_button.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13HomeButton::~HomeButton() = default;
14
15void HomeButton::OnInit() {}
16
17void HomeButton::OnRelease() {}
18
19void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 auto& header = data->shared_memory_format->home_button.header;
32 header.timestamp = core_timing.GetGlobalTimeNs().count();
33 header.total_entry_count = 17;
34 header.entry_count = 0;
35 header.last_entry_index = 0;
36}
37
38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/home_button.h b/src/core/hle/service/hid/controllers/home_button.h
new file mode 100644
index 000000000..e91c2aa5d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/home_button.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class HomeButton final : public ControllerBase {
11public:
12 explicit HomeButton(Core::HID::HIDCore& hid_core_);
13 ~HomeButton() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 871e5036a..c72b3e5ce 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -5,14 +5,13 @@
5#include "core/core_timing.h" 5#include "core/core_timing.h"
6#include "core/hid/emulated_devices.h" 6#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/applet_resource.h"
8#include "core/hle/service/hid/controllers/keyboard.h" 9#include "core/hle/service/hid/controllers/keyboard.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h" 10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
10 11
11namespace Service::HID { 12namespace Service::HID {
12 13
13Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, 14Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
14 KeyboardSharedMemoryFormat& keyboard_shared_memory)
15 : ControllerBase{hid_core_}, shared_memory{keyboard_shared_memory} {
16 emulated_devices = hid_core.GetEmulatedDevices(); 15 emulated_devices = hid_core.GetEmulatedDevices();
17} 16}
18 17
@@ -23,6 +22,15 @@ void Keyboard::OnInit() {}
23void Keyboard::OnRelease() {} 22void Keyboard::OnRelease() {}
24 23
25void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 24void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr) {
29 return;
30 }
31
32 KeyboardSharedMemoryFormat& shared_memory = data->shared_memory_format->keyboard;
33
26 if (!IsControllerActivated()) { 34 if (!IsControllerActivated()) {
27 shared_memory.keyboard_lifo.buffer_count = 0; 35 shared_memory.keyboard_lifo.buffer_count = 0;
28 shared_memory.keyboard_lifo.buffer_tail = 0; 36 shared_memory.keyboard_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 4d72171b9..e8ca326c6 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -7,12 +7,9 @@
7#include "core/hle/service/hid/controllers/types/keyboard_types.h" 7#include "core/hle/service/hid/controllers/types/keyboard_types.h"
8 8
9namespace Service::HID { 9namespace Service::HID {
10struct KeyboardSharedMemoryFormat;
11
12class Keyboard final : public ControllerBase { 10class Keyboard final : public ControllerBase {
13public: 11public:
14 explicit Keyboard(Core::HID::HIDCore& hid_core_, 12 explicit Keyboard(Core::HID::HIDCore& hid_core_);
15 KeyboardSharedMemoryFormat& keyboard_shared_memory);
16 ~Keyboard() override; 13 ~Keyboard() override;
17 14
18 // Called when the controller is initialized 15 // Called when the controller is initialized
@@ -26,7 +23,6 @@ public:
26 23
27private: 24private:
28 KeyboardState next_state{}; 25 KeyboardState next_state{};
29 KeyboardSharedMemoryFormat& shared_memory;
30 Core::HID::EmulatedDevices* emulated_devices = nullptr; 26 Core::HID::EmulatedDevices* emulated_devices = nullptr;
31}; 27};
32} // namespace Service::HID 28} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index de5b2c804..58deafbc5 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,13 +5,13 @@
5#include "core/frontend/emu_window.h" 5#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_devices.h" 6#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/applet_resource.h"
8#include "core/hle/service/hid/controllers/mouse.h" 9#include "core/hle/service/hid/controllers/mouse.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h" 10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
10 11
11namespace Service::HID { 12namespace Service::HID {
12 13
13Mouse::Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory) 14Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
14 : ControllerBase{hid_core_}, shared_memory{mouse_shared_memory} {
15 emulated_devices = hid_core.GetEmulatedDevices(); 15 emulated_devices = hid_core.GetEmulatedDevices();
16} 16}
17 17
@@ -21,6 +21,15 @@ void Mouse::OnInit() {}
21void Mouse::OnRelease() {} 21void Mouse::OnRelease() {}
22 22
23void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 23void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->mouse;
32
24 if (!IsControllerActivated()) { 33 if (!IsControllerActivated()) {
25 shared_memory.mouse_lifo.buffer_count = 0; 34 shared_memory.mouse_lifo.buffer_count = 0;
26 shared_memory.mouse_lifo.buffer_tail = 0; 35 shared_memory.mouse_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 363f316a5..cefad956c 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -12,11 +12,9 @@ struct AnalogStickState;
12} // namespace Core::HID 12} // namespace Core::HID
13 13
14namespace Service::HID { 14namespace Service::HID {
15struct MouseSharedMemoryFormat;
16
17class Mouse final : public ControllerBase { 15class Mouse final : public ControllerBase {
18public: 16public:
19 explicit Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory); 17 explicit Mouse(Core::HID::HIDCore& hid_core_);
20 ~Mouse() override; 18 ~Mouse() override;
21 19
22 // Called when the controller is initialized 20 // Called when the controller is initialized
@@ -31,7 +29,6 @@ public:
31private: 29private:
32 Core::HID::MouseState next_state{}; 30 Core::HID::MouseState next_state{};
33 Core::HID::AnalogStickState last_mouse_wheel_state{}; 31 Core::HID::AnalogStickState last_mouse_wheel_state{};
34 MouseSharedMemoryFormat& shared_memory;
35 Core::HID::EmulatedDevices* emulated_devices = nullptr; 32 Core::HID::EmulatedDevices* emulated_devices = nullptr;
36}; 33};
37} // namespace Service::HID 34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 53a737cf5..c7aa606bc 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -16,8 +16,9 @@
16#include "core/hid/hid_core.h" 16#include "core/hid/hid_core.h"
17#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
18#include "core/hle/kernel/k_readable_event.h" 18#include "core/hle/kernel/k_readable_event.h"
19#include "core/hle/service/hid/controllers/applet_resource.h"
19#include "core/hle/service/hid/controllers/npad.h" 20#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/controllers/shared_memory_format.h" 21#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
21#include "core/hle/service/hid/errors.h" 22#include "core/hle/service/hid/errors.h"
22#include "core/hle/service/hid/hid_util.h" 23#include "core/hle/service/hid/hid_util.h"
23#include "core/hle/service/kernel_helpers.h" 24#include "core/hle/service/kernel_helpers.h"
@@ -30,12 +31,10 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
30 Core::HID::NpadIdType::Handheld, 31 Core::HID::NpadIdType::Handheld,
31}; 32};
32 33
33NPad::NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format, 34NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
34 KernelHelpers::ServiceContext& service_context_)
35 : ControllerBase{hid_core_}, service_context{service_context_} { 35 : ControllerBase{hid_core_}, service_context{service_context_} {
36 for (std::size_t i = 0; i < controller_data.size(); ++i) { 36 for (std::size_t i = 0; i < controller_data.size(); ++i) {
37 auto& controller = controller_data[i]; 37 auto& controller = controller_data[i];
38 controller.shared_memory = &npad_shared_memory_format.npad_entry[i].internal_state;
39 controller.device = hid_core.GetEmulatedControllerByIndex(i); 38 controller.device = hid_core.GetEmulatedControllerByIndex(i);
40 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = 39 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
41 Core::HID::DEFAULT_VIBRATION_VALUE; 40 Core::HID::DEFAULT_VIBRATION_VALUE;
@@ -297,12 +296,20 @@ void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
297} 296}
298 297
299void NPad::OnInit() { 298void NPad::OnInit() {
299 const u64 aruid = applet_resource->GetActiveAruid();
300 auto* data = applet_resource->GetAruidData(aruid);
301
302 if (data == nullptr) {
303 return;
304 }
305
300 if (!IsControllerActivated()) { 306 if (!IsControllerActivated()) {
301 return; 307 return;
302 } 308 }
303 309
304 for (std::size_t i = 0; i < controller_data.size(); ++i) { 310 for (std::size_t i = 0; i < controller_data.size(); ++i) {
305 auto& controller = controller_data[i]; 311 auto& controller = controller_data[i];
312 controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
306 controller.styleset_changed_event = 313 controller.styleset_changed_event =
307 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); 314 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
308 } 315 }
@@ -355,7 +362,9 @@ void NPad::OnRelease() {
355 is_controller_initialized = false; 362 is_controller_initialized = false;
356 for (std::size_t i = 0; i < controller_data.size(); ++i) { 363 for (std::size_t i = 0; i < controller_data.size(); ++i) {
357 auto& controller = controller_data[i]; 364 auto& controller = controller_data[i];
358 service_context.CloseEvent(controller.styleset_changed_event); 365 if (controller.styleset_changed_event) {
366 service_context.CloseEvent(controller.styleset_changed_event);
367 }
359 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { 368 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
360 VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {}); 369 VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
361 } 370 }
@@ -432,12 +441,20 @@ void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
432} 441}
433 442
434void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 443void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
444 const u64 aruid = applet_resource->GetActiveAruid();
445 auto* data = applet_resource->GetAruidData(aruid);
446
447 if (data == nullptr) {
448 return;
449 }
450
435 if (!IsControllerActivated()) { 451 if (!IsControllerActivated()) {
436 return; 452 return;
437 } 453 }
438 454
439 for (std::size_t i = 0; i < controller_data.size(); ++i) { 455 for (std::size_t i = 0; i < controller_data.size(); ++i) {
440 auto& controller = controller_data[i]; 456 auto& controller = controller_data[i];
457 controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
441 auto* npad = controller.shared_memory; 458 auto* npad = controller.shared_memory;
442 459
443 const auto& controller_type = controller.device->GetNpadStyleIndex(); 460 const auto& controller_type = controller.device->GetNpadStyleIndex();
@@ -976,30 +993,6 @@ Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
976 return ResultSuccess; 993 return ResultSuccess;
977} 994}
978 995
979NpadSixAxisSensorLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
980 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
981}
982
983NpadSixAxisSensorLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
984 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
985}
986
987NpadSixAxisSensorLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
988 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
989}
990
991NpadSixAxisSensorLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
992 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
993}
994
995NpadSixAxisSensorLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
996 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
997}
998
999NpadSixAxisSensorLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1000 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1001}
1002
1003Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 996Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1004 Core::HID::NpadIdType npad_id_2) { 997 Core::HID::NpadIdType npad_id_2) {
1005 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 998 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 4e2412356..80cfcb2bb 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -30,14 +30,14 @@ class ServiceContext;
30union Result; 30union Result;
31 31
32namespace Service::HID { 32namespace Service::HID {
33class AppletResource;
33struct NpadInternalState; 34struct NpadInternalState;
34struct NpadSixAxisSensorLifo; 35struct NpadSixAxisSensorLifo;
35struct NpadSharedMemoryFormat; 36struct NpadSharedMemoryFormat;
36 37
37class NPad final : public ControllerBase { 38class NPad final : public ControllerBase {
38public: 39public:
39 explicit NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format, 40 explicit NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
40 KernelHelpers::ServiceContext& service_context_);
41 ~NPad() override; 41 ~NPad() override;
42 42
43 // Called when the controller is initialized 43 // Called when the controller is initialized
@@ -106,13 +106,6 @@ public:
106 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 106 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
107 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 107 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
108 108
109 NpadSixAxisSensorLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
110 NpadSixAxisSensorLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
111 NpadSixAxisSensorLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
112 NpadSixAxisSensorLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
113 NpadSixAxisSensorLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
114 NpadSixAxisSensorLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
115
116 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; 109 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
117 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 110 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
118 bool& is_enabled) const; 111 bool& is_enabled) const;
diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
index 51581188e..0bc5169c6 100644
--- a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
+++ b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
@@ -3,8 +3,9 @@
3 3
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h" 5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/shared_memory_format.h" 6#include "core/hle/service/hid/controllers/applet_resource.h"
7#include "core/hle/service/hid/controllers/shared_memory_holder.h" 7#include "core/hle/service/hid/controllers/shared_memory_holder.h"
8#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8#include "core/hle/service/hid/errors.h" 9#include "core/hle/service/hid/errors.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
index 36b72f9ea..a5a67dea6 100644
--- a/src/core/hle/service/hid/controllers/six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -6,8 +6,8 @@
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h"
10#include "core/hle/service/hid/controllers/six_axis.h" 9#include "core/hle/service/hid/controllers/six_axis.h"
10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11#include "core/hle/service/hid/errors.h" 11#include "core/hle/service/hid/errors.h"
12#include "core/hle/service/hid/hid_util.h" 12#include "core/hle/service/hid/hid_util.h"
13 13
@@ -27,14 +27,20 @@ void SixAxis::OnInit() {}
27void SixAxis::OnRelease() {} 27void SixAxis::OnRelease() {}
28 28
29void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 29void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 const u64 aruid = applet_resource->GetActiveAruid();
31 auto* data = applet_resource->GetAruidData(aruid);
32
33 if (data == nullptr) {
34 return;
35 }
36
30 if (!IsControllerActivated()) { 37 if (!IsControllerActivated()) {
31 return; 38 return;
32 } 39 }
33 40
34 for (std::size_t i = 0; i < controller_data.size(); ++i) { 41 for (std::size_t i = 0; i < controller_data.size(); ++i) {
42 NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
35 auto& controller = controller_data[i]; 43 auto& controller = controller_data[i];
36
37 const auto npad_id = IndexToNpadIdType(i);
38 const auto& controller_type = controller.device->GetNpadStyleIndex(); 44 const auto& controller_type = controller.device->GetNpadStyleIndex();
39 45
40 if (controller_type == Core::HID::NpadStyleIndex::None || 46 if (controller_type == Core::HID::NpadStyleIndex::None ||
@@ -50,12 +56,12 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
50 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; 56 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
51 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; 57 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
52 58
53 auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id); 59 auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
54 auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id); 60 auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
55 auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id); 61 auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
56 auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id); 62 auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
57 auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id); 63 auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
58 auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id); 64 auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
59 65
60 // Clear previous state 66 // Clear previous state
61 sixaxis_fullkey_state = {}; 67 sixaxis_fullkey_state = {};
diff --git a/src/core/hle/service/hid/controllers/sleep_button.cpp b/src/core/hle/service/hid/controllers/sleep_button.cpp
new file mode 100644
index 000000000..978dc4c1f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/sleep_button.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/sleep_button.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13SleepButton::~SleepButton() = default;
14
15void SleepButton::OnInit() {}
16
17void SleepButton::OnRelease() {}
18
19void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 auto& header = data->shared_memory_format->capture_button.header;
32 header.timestamp = core_timing.GetGlobalTimeNs().count();
33 header.total_entry_count = 17;
34 header.entry_count = 0;
35 header.last_entry_index = 0;
36}
37
38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/sleep_button.h b/src/core/hle/service/hid/controllers/sleep_button.h
new file mode 100644
index 000000000..59964bf63
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/sleep_button.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class SleepButton final : public ControllerBase {
11public:
12 explicit SleepButton(Core::HID::HIDCore& hid_core_);
13 ~SleepButton() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
deleted file mode 100644
index e2a5f5d79..000000000
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/shared_memory_format.h"
6#include "core/hle/service/hid/controllers/stubbed.h"
7
8namespace Service::HID {
9
10Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_,
11 CommonHeader& ring_lifo_header)
12 : ControllerBase{hid_core_}, header{ring_lifo_header} {}
13
14Controller_Stubbed::~Controller_Stubbed() = default;
15
16void Controller_Stubbed::OnInit() {}
17
18void Controller_Stubbed::OnRelease() {}
19
20void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
21 if (!smart_update) {
22 return;
23 }
24
25 header.timestamp = core_timing.GetGlobalTimeNs().count();
26 header.total_entry_count = 17;
27 header.entry_count = 0;
28 header.last_entry_index = 0;
29}
30
31} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 469750006..291dc707e 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -8,15 +8,14 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "core/hid/emulated_console.h" 9#include "core/hid/emulated_console.h"
10#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
11#include "core/hle/service/hid/controllers/shared_memory_format.h" 11#include "core/hle/service/hid/controllers/applet_resource.h"
12#include "core/hle/service/hid/controllers/touchscreen.h" 12#include "core/hle/service/hid/controllers/touchscreen.h"
13#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
13 14
14namespace Service::HID { 15namespace Service::HID {
15 16
16TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, 17TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_)
17 TouchScreenSharedMemoryFormat& touch_shared_memory) 18 : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
18 : ControllerBase{hid_core_}, shared_memory{touch_shared_memory},
19 touchscreen_width(Layout::ScreenUndocked::Width),
20 touchscreen_height(Layout::ScreenUndocked::Height) { 19 touchscreen_height(Layout::ScreenUndocked::Height) {
21 console = hid_core.GetEmulatedConsole(); 20 console = hid_core.GetEmulatedConsole();
22} 21}
@@ -28,6 +27,14 @@ void TouchScreen::OnInit() {}
28void TouchScreen::OnRelease() {} 27void TouchScreen::OnRelease() {}
29 28
30void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 29void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 const u64 aruid = applet_resource->GetActiveAruid();
31 auto* data = applet_resource->GetAruidData(aruid);
32
33 if (data == nullptr) {
34 return;
35 }
36
37 TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen;
31 shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 38 shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
32 39
33 if (!IsControllerActivated()) { 40 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 5b6305bfc..945d359be 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -18,8 +18,7 @@ struct TouchScreenSharedMemoryFormat;
18 18
19class TouchScreen final : public ControllerBase { 19class TouchScreen final : public ControllerBase {
20public: 20public:
21 explicit TouchScreen(Core::HID::HIDCore& hid_core_, 21 explicit TouchScreen(Core::HID::HIDCore& hid_core_);
22 TouchScreenSharedMemoryFormat& touch_shared_memory);
23 ~TouchScreen() override; 22 ~TouchScreen() override;
24 23
25 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -35,7 +34,6 @@ public:
35 34
36private: 35private:
37 TouchScreenState next_state{}; 36 TouchScreenState next_state{};
38 TouchScreenSharedMemoryFormat& shared_memory;
39 Core::HID::EmulatedConsole* console = nullptr; 37 Core::HID::EmulatedConsole* console = nullptr;
40 38
41 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; 39 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/shared_memory_format.h b/src/core/hle/service/hid/controllers/types/shared_memory_format.h
index 2986c113e..2986c113e 100644
--- a/src/core/hle/service/hid/controllers/shared_memory_format.h
+++ b/src/core/hle/service/hid/controllers/types/shared_memory_format.h
diff --git a/src/core/hle/service/hid/controllers/unique_pad.cpp b/src/core/hle/service/hid/controllers/unique_pad.cpp
new file mode 100644
index 000000000..8230501a5
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/unique_pad.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
7#include "core/hle/service/hid/controllers/unique_pad.h"
8
9namespace Service::HID {
10
11UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13UniquePad::~UniquePad() = default;
14
15void UniquePad::OnInit() {}
16
17void UniquePad::OnRelease() {}
18
19void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr) {
28 return;
29 }
30
31 auto& header = data->shared_memory_format->capture_button.header;
32 header.timestamp = core_timing.GetGlobalTimeNs().count();
33 header.total_entry_count = 17;
34 header.entry_count = 0;
35 header.last_entry_index = 0;
36}
37
38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/unique_pad.h b/src/core/hle/service/hid/controllers/unique_pad.h
new file mode 100644
index 000000000..966368264
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/unique_pad.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class UniquePad final : public ControllerBase {
11public:
12 explicit UniquePad(Core::HID::HIDCore& hid_core_);
13 ~UniquePad() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index d12f9beb0..ffa7e144d 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_)
49 // Register update callbacks 49 // Register update callbacks
50 hidbus_update_event = Core::Timing::CreateEvent( 50 hidbus_update_event = Core::Timing::CreateEvent(
51 "Hidbus::UpdateCallback", 51 "Hidbus::UpdateCallback",
52 [this](std::uintptr_t user_data, s64 time, 52 [this](s64 time,
53 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 53 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
54 const auto guard = LockService(); 54 const auto guard = LockService();
55 UpdateHidbus(user_data, ns_late); 55 UpdateHidbus(ns_late);
56 return std::nullopt; 56 return std::nullopt;
57 }); 57 });
58 58
@@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_)
61} 61}
62 62
63HidBus::~HidBus() { 63HidBus::~HidBus() {
64 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); 64 system.CoreTiming().UnscheduleEvent(hidbus_update_event);
65} 65}
66 66
67void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 67void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
68 if (is_hidbus_enabled) { 68 if (is_hidbus_enabled) {
69 for (std::size_t i = 0; i < devices.size(); ++i) { 69 for (std::size_t i = 0; i < devices.size(); ++i) {
70 if (!devices[i].is_device_initializated) { 70 if (!devices[i].is_device_initializated) {
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
index c29b5e882..85a1df133 100644
--- a/src/core/hle/service/hid/hidbus.h
+++ b/src/core/hle/service/hid/hidbus.h
@@ -108,7 +108,7 @@ private:
108 void DisableJoyPollingReceiveMode(HLERequestContext& ctx); 108 void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
109 void SetStatusManagerType(HLERequestContext& ctx); 109 void SetStatusManagerType(HLERequestContext& ctx);
110 110
111 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 111 void UpdateHidbus(std::chrono::nanoseconds ns_late);
112 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; 112 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
113 113
114 template <typename T> 114 template <typename T>
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index 6c6cbd802..84b4be3ed 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -10,18 +10,23 @@
10#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
11 11
12#include "core/hle/service/hid/controllers/applet_resource.h" 12#include "core/hle/service/hid/controllers/applet_resource.h"
13#include "core/hle/service/hid/controllers/capture_button.h"
13#include "core/hle/service/hid/controllers/console_six_axis.h" 14#include "core/hle/service/hid/controllers/console_six_axis.h"
15#include "core/hle/service/hid/controllers/debug_mouse.h"
14#include "core/hle/service/hid/controllers/debug_pad.h" 16#include "core/hle/service/hid/controllers/debug_pad.h"
17#include "core/hle/service/hid/controllers/digitizer.h"
15#include "core/hle/service/hid/controllers/gesture.h" 18#include "core/hle/service/hid/controllers/gesture.h"
19#include "core/hle/service/hid/controllers/home_button.h"
16#include "core/hle/service/hid/controllers/keyboard.h" 20#include "core/hle/service/hid/controllers/keyboard.h"
17#include "core/hle/service/hid/controllers/mouse.h" 21#include "core/hle/service/hid/controllers/mouse.h"
18#include "core/hle/service/hid/controllers/npad.h" 22#include "core/hle/service/hid/controllers/npad.h"
19#include "core/hle/service/hid/controllers/palma.h" 23#include "core/hle/service/hid/controllers/palma.h"
20#include "core/hle/service/hid/controllers/seven_six_axis.h" 24#include "core/hle/service/hid/controllers/seven_six_axis.h"
21#include "core/hle/service/hid/controllers/shared_memory_format.h"
22#include "core/hle/service/hid/controllers/six_axis.h" 25#include "core/hle/service/hid/controllers/six_axis.h"
23#include "core/hle/service/hid/controllers/stubbed.h" 26#include "core/hle/service/hid/controllers/sleep_button.h"
24#include "core/hle/service/hid/controllers/touchscreen.h" 27#include "core/hle/service/hid/controllers/touchscreen.h"
28#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
29#include "core/hle/service/hid/controllers/unique_pad.h"
25 30
26namespace Service::HID { 31namespace Service::HID {
27 32
@@ -46,42 +51,13 @@ void ResourceManager::Initialize() {
46 } 51 }
47 52
48 system.HIDCore().ReloadInputDevices(); 53 system.HIDCore().ReloadInputDevices();
49 is_initialized = true;
50}
51
52void ResourceManager::InitializeController(u64 aruid) {
53 SharedMemoryFormat* shared_memory = nullptr;
54 const auto result = applet_resource->GetSharedMemoryFormat(&shared_memory, aruid);
55 if (result.IsError()) {
56 return;
57 }
58
59 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory->debug_pad);
60 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory->mouse);
61 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory->debug_mouse);
62 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory->keyboard);
63 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory->unique_pad.header);
64 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory->npad, service_context);
65 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory->gesture);
66 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory->touch_screen);
67 54
68 palma = std::make_shared<Palma>(system.HIDCore(), service_context); 55 InitializeHidCommonSampler();
56 InitializeTouchScreenSampler();
57 InitializeConsoleSixAxisSampler();
58 InitializeAHidSampler();
69 59
70 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory->home_button.header); 60 is_initialized = true;
71 sleep_button =
72 std::make_shared<SleepButton>(system.HIDCore(), shared_memory->sleep_button.header);
73 capture_button =
74 std::make_shared<CaptureButton>(system.HIDCore(), shared_memory->capture_button.header);
75 digitizer = std::make_shared<Digitizer>(system.HIDCore(), shared_memory->digitizer.header);
76
77 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
78 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory->console);
79 seven_six_axis = std::make_shared<SevenSixAxis>(system);
80
81 // Homebrew doesn't try to activate some controllers, so we activate them by default
82 npad->Activate();
83 six_axis->Activate();
84 touch_screen->Activate();
85} 61}
86 62
87std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const { 63std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const {
@@ -165,16 +141,65 @@ Result ResourceManager::CreateAppletResource(u64 aruid) {
165 if (result.IsError()) { 141 if (result.IsError()) {
166 return result; 142 return result;
167 } 143 }
144
145 // Homebrew doesn't try to activate some controllers, so we activate them by default
146 npad->Activate();
147 six_axis->Activate();
148 touch_screen->Activate();
149
168 return GetNpad()->Activate(aruid); 150 return GetNpad()->Activate(aruid);
169} 151}
170 152
171Result ResourceManager::CreateAppletResourceImpl(u64 aruid) { 153Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
172 std::scoped_lock lock{shared_mutex}; 154 std::scoped_lock lock{shared_mutex};
173 const auto result = applet_resource->CreateAppletResource(aruid); 155 return applet_resource->CreateAppletResource(aruid);
174 if (result.IsSuccess()) { 156}
175 InitializeController(aruid); 157
176 } 158void ResourceManager::InitializeHidCommonSampler() {
177 return result; 159 debug_pad = std::make_shared<DebugPad>(system.HIDCore());
160 mouse = std::make_shared<Mouse>(system.HIDCore());
161 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore());
162 keyboard = std::make_shared<Keyboard>(system.HIDCore());
163 unique_pad = std::make_shared<UniquePad>(system.HIDCore());
164 npad = std::make_shared<NPad>(system.HIDCore(), service_context);
165 gesture = std::make_shared<Gesture>(system.HIDCore());
166 home_button = std::make_shared<HomeButton>(system.HIDCore());
167 sleep_button = std::make_shared<SleepButton>(system.HIDCore());
168 capture_button = std::make_shared<CaptureButton>(system.HIDCore());
169 digitizer = std::make_shared<Digitizer>(system.HIDCore());
170
171 palma = std::make_shared<Palma>(system.HIDCore(), service_context);
172 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
173
174 debug_pad->SetAppletResource(applet_resource);
175 digitizer->SetAppletResource(applet_resource);
176 keyboard->SetAppletResource(applet_resource);
177 npad->SetAppletResource(applet_resource);
178 six_axis->SetAppletResource(applet_resource);
179 mouse->SetAppletResource(applet_resource);
180 debug_mouse->SetAppletResource(applet_resource);
181 home_button->SetAppletResource(applet_resource);
182 sleep_button->SetAppletResource(applet_resource);
183 capture_button->SetAppletResource(applet_resource);
184}
185
186void ResourceManager::InitializeTouchScreenSampler() {
187 gesture = std::make_shared<Gesture>(system.HIDCore());
188 touch_screen = std::make_shared<TouchScreen>(system.HIDCore());
189
190 touch_screen->SetAppletResource(applet_resource);
191 gesture->SetAppletResource(applet_resource);
192}
193
194void ResourceManager::InitializeConsoleSixAxisSampler() {
195 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore());
196 seven_six_axis = std::make_shared<SevenSixAxis>(system);
197
198 console_six_axis->SetAppletResource(applet_resource);
199}
200
201void ResourceManager::InitializeAHidSampler() {
202 // TODO
178} 203}
179 204
180Result ResourceManager::RegisterCoreAppletResource() { 205Result ResourceManager::RegisterCoreAppletResource() {
@@ -227,8 +252,7 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
227 applet_resource->EnableTouchScreen(aruid, is_enabled); 252 applet_resource->EnableTouchScreen(aruid, is_enabled);
228} 253}
229 254
230void ResourceManager::UpdateControllers(std::uintptr_t user_data, 255void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
231 std::chrono::nanoseconds ns_late) {
232 auto& core_timing = system.CoreTiming(); 256 auto& core_timing = system.CoreTiming();
233 debug_pad->OnUpdate(core_timing); 257 debug_pad->OnUpdate(core_timing);
234 digitizer->OnUpdate(core_timing); 258 digitizer->OnUpdate(core_timing);
@@ -241,20 +265,19 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
241 capture_button->OnUpdate(core_timing); 265 capture_button->OnUpdate(core_timing);
242} 266}
243 267
244void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 268void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) {
245 auto& core_timing = system.CoreTiming(); 269 auto& core_timing = system.CoreTiming();
246 npad->OnUpdate(core_timing); 270 npad->OnUpdate(core_timing);
247} 271}
248 272
249void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, 273void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) {
250 std::chrono::nanoseconds ns_late) {
251 auto& core_timing = system.CoreTiming(); 274 auto& core_timing = system.CoreTiming();
252 mouse->OnUpdate(core_timing); 275 mouse->OnUpdate(core_timing);
253 debug_mouse->OnUpdate(core_timing); 276 debug_mouse->OnUpdate(core_timing);
254 keyboard->OnUpdate(core_timing); 277 keyboard->OnUpdate(core_timing);
255} 278}
256 279
257void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 280void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) {
258 auto& core_timing = system.CoreTiming(); 281 auto& core_timing = system.CoreTiming();
259 six_axis->OnUpdate(core_timing); 282 six_axis->OnUpdate(core_timing);
260 seven_six_axis->OnUpdate(core_timing); 283 seven_six_axis->OnUpdate(core_timing);
@@ -273,34 +296,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
273 // Register update callbacks 296 // Register update callbacks
274 npad_update_event = Core::Timing::CreateEvent( 297 npad_update_event = Core::Timing::CreateEvent(
275 "HID::UpdatePadCallback", 298 "HID::UpdatePadCallback",
276 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 299 [this, resource](
277 -> std::optional<std::chrono::nanoseconds> { 300 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
278 const auto guard = LockService(); 301 const auto guard = LockService();
279 resource->UpdateNpad(user_data, ns_late); 302 resource->UpdateNpad(ns_late);
280 return std::nullopt; 303 return std::nullopt;
281 }); 304 });
282 default_update_event = Core::Timing::CreateEvent( 305 default_update_event = Core::Timing::CreateEvent(
283 "HID::UpdateDefaultCallback", 306 "HID::UpdateDefaultCallback",
284 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 307 [this, resource](
285 -> std::optional<std::chrono::nanoseconds> { 308 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
286 const auto guard = LockService(); 309 const auto guard = LockService();
287 resource->UpdateControllers(user_data, ns_late); 310 resource->UpdateControllers(ns_late);
288 return std::nullopt; 311 return std::nullopt;
289 }); 312 });
290 mouse_keyboard_update_event = Core::Timing::CreateEvent( 313 mouse_keyboard_update_event = Core::Timing::CreateEvent(
291 "HID::UpdateMouseKeyboardCallback", 314 "HID::UpdateMouseKeyboardCallback",
292 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 315 [this, resource](
293 -> std::optional<std::chrono::nanoseconds> { 316 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
294 const auto guard = LockService(); 317 const auto guard = LockService();
295 resource->UpdateMouseKeyboard(user_data, ns_late); 318 resource->UpdateMouseKeyboard(ns_late);
296 return std::nullopt; 319 return std::nullopt;
297 }); 320 });
298 motion_update_event = Core::Timing::CreateEvent( 321 motion_update_event = Core::Timing::CreateEvent(
299 "HID::UpdateMotionCallback", 322 "HID::UpdateMotionCallback",
300 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 323 [this, resource](
301 -> std::optional<std::chrono::nanoseconds> { 324 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
302 const auto guard = LockService(); 325 const auto guard = LockService();
303 resource->UpdateMotion(user_data, ns_late); 326 resource->UpdateMotion(ns_late);
304 return std::nullopt; 327 return std::nullopt;
305 }); 328 });
306 329
@@ -314,10 +337,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
314} 337}
315 338
316IAppletResource::~IAppletResource() { 339IAppletResource::~IAppletResource() {
317 system.CoreTiming().UnscheduleEvent(npad_update_event, 0); 340 system.CoreTiming().UnscheduleEvent(npad_update_event);
318 system.CoreTiming().UnscheduleEvent(default_update_event, 0); 341 system.CoreTiming().UnscheduleEvent(default_update_event);
319 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); 342 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
320 system.CoreTiming().UnscheduleEvent(motion_update_event, 0); 343 system.CoreTiming().UnscheduleEvent(motion_update_event);
321 resource_manager->FreeAppletResourceId(aruid); 344 resource_manager->FreeAppletResourceId(aruid);
322} 345}
323 346
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 5ad7cb564..70d9b6550 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -20,24 +20,23 @@ class KSharedMemory;
20 20
21namespace Service::HID { 21namespace Service::HID {
22class AppletResource; 22class AppletResource;
23class CaptureButton;
23class Controller_Stubbed; 24class Controller_Stubbed;
24class ConsoleSixAxis; 25class ConsoleSixAxis;
26class DebugMouse;
25class DebugPad; 27class DebugPad;
28class Digitizer;
26class Gesture; 29class Gesture;
30class HomeButton;
27class Keyboard; 31class Keyboard;
28class Mouse; 32class Mouse;
29class NPad; 33class NPad;
30class Palma; 34class Palma;
31class SevenSixAxis; 35class SevenSixAxis;
32class SixAxis; 36class SixAxis;
37class SleepButton;
33class TouchScreen; 38class TouchScreen;
34 39class UniquePad;
35using CaptureButton = Controller_Stubbed;
36using DebugMouse = Mouse;
37using Digitizer = Controller_Stubbed;
38using HomeButton = Controller_Stubbed;
39using SleepButton = Controller_Stubbed;
40using UniquePad = Controller_Stubbed;
41 40
42class ResourceManager { 41class ResourceManager {
43 42
@@ -46,7 +45,6 @@ public:
46 ~ResourceManager(); 45 ~ResourceManager();
47 46
48 void Initialize(); 47 void Initialize();
49 void InitializeController(u64 aruid);
50 48
51 std::shared_ptr<AppletResource> GetAppletResource() const; 49 std::shared_ptr<AppletResource> GetAppletResource() const;
52 std::shared_ptr<CaptureButton> GetCaptureButton() const; 50 std::shared_ptr<CaptureButton> GetCaptureButton() const;
@@ -81,13 +79,17 @@ public:
81 void EnablePadInput(u64 aruid, bool is_enabled); 79 void EnablePadInput(u64 aruid, bool is_enabled);
82 void EnableTouchScreen(u64 aruid, bool is_enabled); 80 void EnableTouchScreen(u64 aruid, bool is_enabled);
83 81
84 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 82 void UpdateControllers(std::chrono::nanoseconds ns_late);
85 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 83 void UpdateNpad(std::chrono::nanoseconds ns_late);
86 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 84 void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
87 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 85 void UpdateMotion(std::chrono::nanoseconds ns_late);
88 86
89private: 87private:
90 Result CreateAppletResourceImpl(u64 aruid); 88 Result CreateAppletResourceImpl(u64 aruid);
89 void InitializeHidCommonSampler();
90 void InitializeTouchScreenSampler();
91 void InitializeConsoleSixAxisSampler();
92 void InitializeAHidSampler();
91 93
92 bool is_initialized{false}; 94 bool is_initialized{false};
93 95
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index a94d05e19..77aa6d7d1 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -4,11 +4,11 @@
4#include "core/arm/debug.h" 4#include "core/arm/debug.h"
5#include "core/arm/symbols.h" 5#include "core/arm/symbols.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/kernel/k_code_memory.h"
8#include "core/hle/kernel/k_transfer_memory.h" 7#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/result.h" 8#include "core/hle/result.h"
10#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
11#include "core/hle/service/jit/jit.h" 10#include "core/hle/service/jit/jit.h"
11#include "core/hle/service/jit/jit_code_memory.h"
12#include "core/hle/service/jit/jit_context.h" 12#include "core/hle/service/jit/jit_context.h"
13#include "core/hle/service/server_manager.h" 13#include "core/hle/service/server_manager.h"
14#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
@@ -23,10 +23,12 @@ struct CodeRange {
23 23
24class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { 24class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
25public: 25public:
26 explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, 26 explicit IJitEnvironment(Core::System& system_,
27 CodeRange user_ro) 27 Kernel::KScopedAutoObject<Kernel::KProcess>&& process_,
28 : ServiceFramework{system_, "IJitEnvironment"}, process{&process_}, 28 CodeMemory&& user_rx_, CodeMemory&& user_ro_)
29 context{process->GetMemory()} { 29 : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
30 user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
31 context{system_.ApplicationMemory()} {
30 // clang-format off 32 // clang-format off
31 static const FunctionInfo functions[] = { 33 static const FunctionInfo functions[] = {
32 {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, 34 {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
@@ -39,10 +41,13 @@ public:
39 RegisterHandlers(functions); 41 RegisterHandlers(functions);
40 42
41 // Identity map user code range into sysmodule context 43 // Identity map user code range into sysmodule context
42 configuration.user_ro_memory = user_ro; 44 configuration.user_rx_memory.size = user_rx.GetSize();
43 configuration.user_rx_memory = user_rx; 45 configuration.user_rx_memory.offset = user_rx.GetAddress();
44 configuration.sys_ro_memory = user_ro; 46 configuration.user_ro_memory.size = user_ro.GetSize();
45 configuration.sys_rx_memory = user_rx; 47 configuration.user_ro_memory.offset = user_ro.GetAddress();
48
49 configuration.sys_rx_memory = configuration.user_rx_memory;
50 configuration.sys_ro_memory = configuration.user_ro_memory;
46 } 51 }
47 52
48 void GenerateCode(HLERequestContext& ctx) { 53 void GenerateCode(HLERequestContext& ctx) {
@@ -318,6 +323,8 @@ private:
318 } 323 }
319 324
320 Kernel::KScopedAutoObject<Kernel::KProcess> process; 325 Kernel::KScopedAutoObject<Kernel::KProcess> process;
326 CodeMemory user_rx;
327 CodeMemory user_ro;
321 GuestCallbacks callbacks; 328 GuestCallbacks callbacks;
322 JITConfiguration configuration; 329 JITConfiguration configuration;
323 JITContext context; 330 JITContext context;
@@ -335,6 +342,7 @@ public:
335 RegisterHandlers(functions); 342 RegisterHandlers(functions);
336 } 343 }
337 344
345private:
338 void CreateJitEnvironment(HLERequestContext& ctx) { 346 void CreateJitEnvironment(HLERequestContext& ctx) {
339 LOG_DEBUG(Service_JIT, "called"); 347 LOG_DEBUG(Service_JIT, "called");
340 348
@@ -380,20 +388,35 @@ public:
380 return; 388 return;
381 } 389 }
382 390
383 const CodeRange user_rx{ 391 CodeMemory rx, ro;
384 .offset = GetInteger(rx_mem->GetSourceAddress()), 392 Result res;
385 .size = parameters.rx_size,
386 };
387 393
388 const CodeRange user_ro{ 394 res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
389 .offset = GetInteger(ro_mem->GetSourceAddress()), 395 Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
390 .size = parameters.ro_size, 396 if (R_FAILED(res)) {
391 }; 397 LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
398 IPC::ResponseBuilder rb{ctx, 2};
399 rb.Push(res);
400 return;
401 }
402
403 res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
404 Kernel::Svc::MemoryPermission::Read, generate_random);
405 if (R_FAILED(res)) {
406 LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
407 IPC::ResponseBuilder rb{ctx, 2};
408 rb.Push(res);
409 return;
410 }
392 411
393 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 412 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
394 rb.Push(ResultSuccess); 413 rb.Push(ResultSuccess);
395 rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro); 414 rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
415 std::move(ro));
396 } 416 }
417
418private:
419 std::mt19937_64 generate_random{};
397}; 420};
398 421
399void LoopProcess(Core::System& system) { 422void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/jit/jit_code_memory.cpp b/src/core/hle/service/jit/jit_code_memory.cpp
new file mode 100644
index 000000000..2b480488a
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.cpp
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/jit/jit_code_memory.h"
5
6namespace Service::JIT {
7
8Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory,
9 size_t size, Kernel::Svc::MemoryPermission perm,
10 std::mt19937_64& generate_random) {
11 auto& page_table = process.GetPageTable();
12 const u64 alias_code_start =
13 GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize;
14 const u64 alias_code_size = page_table.GetAliasCodeRegionSize() / Kernel::PageSize;
15
16 // NOTE: This will retry indefinitely until mapping the code memory succeeds.
17 while (true) {
18 // Generate a new trial address.
19 const u64 mapped_address =
20 (alias_code_start + (generate_random() % alias_code_size)) * Kernel::PageSize;
21
22 // Try to map the address
23 R_TRY_CATCH(code_memory.MapToOwner(mapped_address, size, perm)) {
24 R_CATCH(Kernel::ResultInvalidMemoryRegion) {
25 // If we could not map here, retry.
26 continue;
27 }
28 }
29 R_END_TRY_CATCH;
30
31 // Set members.
32 m_code_memory = std::addressof(code_memory);
33 m_size = size;
34 m_address = mapped_address;
35 m_perm = perm;
36
37 // Open a new reference to the code memory.
38 m_code_memory->Open();
39
40 // We succeeded.
41 R_SUCCEED();
42 }
43}
44
45void CodeMemory::Finalize() {
46 if (m_code_memory) {
47 R_ASSERT(m_code_memory->UnmapFromOwner(m_address, m_size));
48 m_code_memory->Close();
49 }
50
51 m_code_memory = nullptr;
52}
53
54} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_code_memory.h b/src/core/hle/service/jit/jit_code_memory.h
new file mode 100644
index 000000000..6376d4c4e
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <random>
7
8#include "core/hle/kernel/k_code_memory.h"
9
10namespace Service::JIT {
11
12class CodeMemory {
13public:
14 YUZU_NON_COPYABLE(CodeMemory);
15
16 explicit CodeMemory() = default;
17
18 CodeMemory(CodeMemory&& rhs) {
19 std::swap(m_code_memory, rhs.m_code_memory);
20 std::swap(m_size, rhs.m_size);
21 std::swap(m_address, rhs.m_address);
22 std::swap(m_perm, rhs.m_perm);
23 }
24
25 ~CodeMemory() {
26 this->Finalize();
27 }
28
29public:
30 Result Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size,
31 Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random);
32 void Finalize();
33
34 size_t GetSize() const {
35 return m_size;
36 }
37
38 u64 GetAddress() const {
39 return m_address;
40 }
41
42private:
43 Kernel::KCodeMemory* m_code_memory{};
44 size_t m_size{};
45 u64 m_address{};
46 Kernel::Svc::MemoryPermission m_perm{};
47};
48
49} // namespace Service::JIT
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 6352b09a9..aa8aaa2d9 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -67,7 +67,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
67 // Schedule the screen composition events 67 // Schedule the screen composition events
68 multi_composition_event = Core::Timing::CreateEvent( 68 multi_composition_event = Core::Timing::CreateEvent(
69 "ScreenComposition", 69 "ScreenComposition",
70 [this](std::uintptr_t, s64 time, 70 [this](s64 time,
71 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 71 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
72 vsync_signal.Set(); 72 vsync_signal.Set();
73 return std::chrono::nanoseconds(GetNextTicks()); 73 return std::chrono::nanoseconds(GetNextTicks());
@@ -75,7 +75,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
75 75
76 single_composition_event = Core::Timing::CreateEvent( 76 single_composition_event = Core::Timing::CreateEvent(
77 "ScreenComposition", 77 "ScreenComposition",
78 [this](std::uintptr_t, s64 time, 78 [this](s64 time,
79 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 79 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
80 const auto lock_guard = Lock(); 80 const auto lock_guard = Lock();
81 Compose(); 81 Compose();
@@ -93,11 +93,11 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
93 93
94Nvnflinger::~Nvnflinger() { 94Nvnflinger::~Nvnflinger() {
95 if (system.IsMulticore()) { 95 if (system.IsMulticore()) {
96 system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); 96 system.CoreTiming().UnscheduleEvent(multi_composition_event);
97 vsync_thread.request_stop(); 97 vsync_thread.request_stop();
98 vsync_signal.Set(); 98 vsync_signal.Set();
99 } else { 99 } else {
100 system.CoreTiming().UnscheduleEvent(single_composition_event, {}); 100 system.CoreTiming().UnscheduleEvent(single_composition_event);
101 } 101 }
102 102
103 ShutdownLayers(); 103 ShutdownLayers();
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index c7eb32c19..8176a41be 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/atomic_ops.h" 11#include "common/atomic_ops.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/heap_tracker.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/page_table.h" 15#include "common/page_table.h"
15#include "common/scope_exit.h" 16#include "common/scope_exit.h"
@@ -52,10 +53,18 @@ struct Memory::Impl {
52 } else { 53 } else {
53 current_page_table->fastmem_arena = nullptr; 54 current_page_table->fastmem_arena = nullptr;
54 } 55 }
56
57#ifdef __linux__
58 heap_tracker.emplace(system.DeviceMemory().buffer);
59 buffer = std::addressof(*heap_tracker);
60#else
61 buffer = std::addressof(system.DeviceMemory().buffer);
62#endif
55 } 63 }
56 64
57 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 65 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
58 Common::PhysicalAddress target, Common::MemoryPermission perms) { 66 Common::PhysicalAddress target, Common::MemoryPermission perms,
67 bool separate_heap) {
59 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 68 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
60 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 69 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
61 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", 70 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
@@ -64,19 +73,20 @@ struct Memory::Impl {
64 Common::PageType::Memory); 73 Common::PageType::Memory);
65 74
66 if (current_page_table->fastmem_arena) { 75 if (current_page_table->fastmem_arena) {
67 system.DeviceMemory().buffer.Map(GetInteger(base), 76 buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
68 GetInteger(target) - DramMemoryMap::Base, size, perms); 77 separate_heap);
69 } 78 }
70 } 79 }
71 80
72 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 81 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
82 bool separate_heap) {
73 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 83 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
74 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 84 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
75 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, 85 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
76 Common::PageType::Unmapped); 86 Common::PageType::Unmapped);
77 87
78 if (current_page_table->fastmem_arena) { 88 if (current_page_table->fastmem_arena) {
79 system.DeviceMemory().buffer.Unmap(GetInteger(base), size); 89 buffer->Unmap(GetInteger(base), size, separate_heap);
80 } 90 }
81 } 91 }
82 92
@@ -89,11 +99,6 @@ struct Memory::Impl {
89 return; 99 return;
90 } 100 }
91 101
92 const bool is_r = True(perms & Common::MemoryPermission::Read);
93 const bool is_w = True(perms & Common::MemoryPermission::Write);
94 const bool is_x =
95 True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
96
97 u64 protect_bytes{}; 102 u64 protect_bytes{};
98 u64 protect_begin{}; 103 u64 protect_begin{};
99 for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) { 104 for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
@@ -102,8 +107,7 @@ struct Memory::Impl {
102 switch (page_type) { 107 switch (page_type) {
103 case Common::PageType::RasterizerCachedMemory: 108 case Common::PageType::RasterizerCachedMemory:
104 if (protect_bytes > 0) { 109 if (protect_bytes > 0) {
105 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, 110 buffer->Protect(protect_begin, protect_bytes, perms);
106 is_x);
107 protect_bytes = 0; 111 protect_bytes = 0;
108 } 112 }
109 break; 113 break;
@@ -116,7 +120,7 @@ struct Memory::Impl {
116 } 120 }
117 121
118 if (protect_bytes > 0) { 122 if (protect_bytes > 0) {
119 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x); 123 buffer->Protect(protect_begin, protect_bytes, perms);
120 } 124 }
121 } 125 }
122 126
@@ -486,7 +490,9 @@ struct Memory::Impl {
486 } 490 }
487 491
488 if (current_page_table->fastmem_arena) { 492 if (current_page_table->fastmem_arena) {
489 system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug); 493 const auto perm{debug ? Common::MemoryPermission{}
494 : Common::MemoryPermission::ReadWrite};
495 buffer->Protect(vaddr, size, perm);
490 } 496 }
491 497
492 // Iterate over a contiguous CPU address space, marking/unmarking the region. 498 // Iterate over a contiguous CPU address space, marking/unmarking the region.
@@ -543,9 +549,14 @@ struct Memory::Impl {
543 } 549 }
544 550
545 if (current_page_table->fastmem_arena) { 551 if (current_page_table->fastmem_arena) {
546 const bool is_read_enable = 552 Common::MemoryPermission perm{};
547 !Settings::values.use_reactive_flushing.GetValue() || !cached; 553 if (!Settings::values.use_reactive_flushing.GetValue() || !cached) {
548 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 554 perm |= Common::MemoryPermission::Read;
555 }
556 if (!cached) {
557 perm |= Common::MemoryPermission::Write;
558 }
559 buffer->Protect(vaddr, size, perm);
549 } 560 }
550 561
551 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU 562 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
@@ -856,6 +867,13 @@ struct Memory::Impl {
856 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; 867 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
857 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; 868 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
858 std::mutex sys_core_guard; 869 std::mutex sys_core_guard;
870
871 std::optional<Common::HeapTracker> heap_tracker;
872#ifdef __linux__
873 Common::HeapTracker* buffer{};
874#else
875 Common::HostMemory* buffer{};
876#endif
859}; 877};
860 878
861Memory::Memory(Core::System& system_) : system{system_} { 879Memory::Memory(Core::System& system_) : system{system_} {
@@ -873,12 +891,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) {
873} 891}
874 892
875void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 893void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
876 Common::PhysicalAddress target, Common::MemoryPermission perms) { 894 Common::PhysicalAddress target, Common::MemoryPermission perms,
877 impl->MapMemoryRegion(page_table, base, size, target, perms); 895 bool separate_heap) {
896 impl->MapMemoryRegion(page_table, base, size, target, perms, separate_heap);
878} 897}
879 898
880void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 899void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
881 impl->UnmapRegion(page_table, base, size); 900 bool separate_heap) {
901 impl->UnmapRegion(page_table, base, size, separate_heap);
882} 902}
883 903
884void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size, 904void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
@@ -1048,7 +1068,9 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
1048} 1068}
1049 1069
1050bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { 1070bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1051 bool mapped = true; 1071 [[maybe_unused]] bool mapped = true;
1072 [[maybe_unused]] bool rasterizer = false;
1073
1052 u8* const ptr = impl->GetPointerImpl( 1074 u8* const ptr = impl->GetPointerImpl(
1053 GetInteger(vaddr), 1075 GetInteger(vaddr),
1054 [&] { 1076 [&] {
@@ -1056,8 +1078,26 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1056 GetInteger(vaddr)); 1078 GetInteger(vaddr));
1057 mapped = false; 1079 mapped = false;
1058 }, 1080 },
1059 [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); }); 1081 [&] {
1082 impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size);
1083 rasterizer = true;
1084 });
1085
1086#ifdef __linux__
1087 if (!rasterizer && mapped) {
1088 impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
1089 }
1090#endif
1091
1060 return mapped && ptr != nullptr; 1092 return mapped && ptr != nullptr;
1061} 1093}
1062 1094
1095bool Memory::InvalidateSeparateHeap(void* fault_address) {
1096#ifdef __linux__
1097 return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
1098#else
1099 return false;
1100#endif
1101}
1102
1063} // namespace Core::Memory 1103} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c1879e78f..dddfaf4a4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -86,7 +86,8 @@ public:
86 * @param perms The permissions to map the memory with. 86 * @param perms The permissions to map the memory with.
87 */ 87 */
88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
89 Common::PhysicalAddress target, Common::MemoryPermission perms); 89 Common::PhysicalAddress target, Common::MemoryPermission perms,
90 bool separate_heap);
90 91
91 /** 92 /**
92 * Unmaps a region of the emulated process address space. 93 * Unmaps a region of the emulated process address space.
@@ -95,7 +96,8 @@ public:
95 * @param base The address to begin unmapping at. 96 * @param base The address to begin unmapping at.
96 * @param size The amount of bytes to unmap. 97 * @param size The amount of bytes to unmap.
97 */ 98 */
98 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); 99 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
100 bool separate_heap);
99 101
100 /** 102 /**
101 * Protects a region of the emulated process address space with the new permissions. 103 * Protects a region of the emulated process address space with the new permissions.
@@ -486,6 +488,7 @@ public:
486 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); 488 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
487 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); 489 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
488 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); 490 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
491 bool InvalidateSeparateHeap(void* fault_address);
489 void FlushRegion(Common::ProcessAddress dest_addr, size_t size); 492 void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
490 493
491private: 494private:
@@ -683,7 +686,8 @@ public:
683 } else { 686 } else {
684 this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); 687 this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
685 } 688 }
686 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { 689 } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
690 (FLAGS & GuestMemoryFlags::Cached)) {
687 this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); 691 this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
688 } 692 }
689 } 693 }
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 3fc4024dc..7bc5b5ae5 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -190,15 +190,15 @@ CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
190} 190}
191 191
192CheatEngine::~CheatEngine() { 192CheatEngine::~CheatEngine() {
193 core_timing.UnscheduleEvent(event, 0); 193 core_timing.UnscheduleEvent(event);
194} 194}
195 195
196void CheatEngine::Initialize() { 196void CheatEngine::Initialize() {
197 event = Core::Timing::CreateEvent( 197 event = Core::Timing::CreateEvent(
198 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 198 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
199 [this](std::uintptr_t user_data, s64 time, 199 [this](s64 time,
200 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 200 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
201 FrameCallback(user_data, ns_late); 201 FrameCallback(ns_late);
202 return std::nullopt; 202 return std::nullopt;
203 }); 203 });
204 core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); 204 core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
@@ -239,7 +239,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
239 239
240MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); 240MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
241 241
242void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 242void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
243 if (is_pending_reload.exchange(false)) { 243 if (is_pending_reload.exchange(false)) {
244 vm.LoadProgram(cheats); 244 vm.LoadProgram(cheats);
245 } 245 }
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 284abdd28..ced2168d1 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -70,7 +70,7 @@ public:
70 void Reload(std::vector<CheatEntry> reload_cheats); 70 void Reload(std::vector<CheatEntry> reload_cheats);
71 71
72private: 72private:
73 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 73 void FrameCallback(std::chrono::nanoseconds ns_late);
74 74
75 DmntCheatVm vm; 75 DmntCheatVm vm;
76 CheatProcessMetadata metadata; 76 CheatProcessMetadata metadata;
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 98ebbbf32..9d42c726e 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -51,18 +51,17 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
51 51
52Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) 52Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
53 : core_timing{core_timing_}, memory{memory_} { 53 : core_timing{core_timing_}, memory{memory_} {
54 event = Core::Timing::CreateEvent( 54 event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
55 "MemoryFreezer::FrameCallback", 55 [this](s64 time, std::chrono::nanoseconds ns_late)
56 [this](std::uintptr_t user_data, s64 time, 56 -> std::optional<std::chrono::nanoseconds> {
57 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 57 FrameCallback(ns_late);
58 FrameCallback(user_data, ns_late); 58 return std::nullopt;
59 return std::nullopt; 59 });
60 });
61 core_timing.ScheduleEvent(memory_freezer_ns, event); 60 core_timing.ScheduleEvent(memory_freezer_ns, event);
62} 61}
63 62
64Freezer::~Freezer() { 63Freezer::~Freezer() {
65 core_timing.UnscheduleEvent(event, 0); 64 core_timing.UnscheduleEvent(event);
66} 65}
67 66
68void Freezer::SetActive(bool is_active) { 67void Freezer::SetActive(bool is_active) {
@@ -159,7 +158,7 @@ Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
159 [address](const Entry& entry) { return entry.address == address; }); 158 [address](const Entry& entry) { return entry.address == address; });
160} 159}
161 160
162void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 161void Freezer::FrameCallback(std::chrono::nanoseconds ns_late) {
163 if (!IsActive()) { 162 if (!IsActive()) {
164 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
165 return; 164 return;
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 0d6df5217..2efbc11f3 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -77,7 +77,7 @@ private:
77 Entries::iterator FindEntry(VAddr address); 77 Entries::iterator FindEntry(VAddr address);
78 Entries::const_iterator FindEntry(VAddr address) const; 78 Entries::const_iterator FindEntry(VAddr address) const;
79 79
80 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 80 void FrameCallback(std::chrono::nanoseconds ns_late);
81 void FillEntryReads(); 81 void FillEntryReads();
82 82
83 std::atomic_bool active{false}; 83 std::atomic_bool active{false};
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index d9f99148b..51576b4ee 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -403,59 +403,63 @@ void Config::SavePlayerValues(const std::size_t player_index) {
403 // No custom profile selected 403 // No custom profile selected
404 return; 404 return;
405 } 405 }
406 WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name, 406 WriteStringSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
407 std::make_optional(std::string(""))); 407 std::make_optional(std::string("")));
408 } 408 }
409 409
410 WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type), 410 WriteIntegerSetting(
411 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController))); 411 std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
412 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
412 413
413 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { 414 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
414 WriteSetting(std::string(player_prefix).append("connected"), player.connected, 415 WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected,
415 std::make_optional(player_index == 0)); 416 std::make_optional(player_index == 0));
416 WriteSetting(std::string(player_prefix).append("vibration_enabled"), 417 WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"),
417 player.vibration_enabled, std::make_optional(true)); 418 player.vibration_enabled, std::make_optional(true));
418 WriteSetting(std::string(player_prefix).append("vibration_strength"), 419 WriteIntegerSetting(std::string(player_prefix).append("vibration_strength"),
419 player.vibration_strength, std::make_optional(100)); 420 player.vibration_strength, std::make_optional(100));
420 WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left, 421 WriteIntegerSetting(std::string(player_prefix).append("body_color_left"),
421 std::make_optional(Settings::JOYCON_BODY_NEON_BLUE)); 422 player.body_color_left,
422 WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right, 423 std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
423 std::make_optional(Settings::JOYCON_BODY_NEON_RED)); 424 WriteIntegerSetting(std::string(player_prefix).append("body_color_right"),
424 WriteSetting(std::string(player_prefix).append("button_color_left"), 425 player.body_color_right,
425 player.button_color_left, 426 std::make_optional(Settings::JOYCON_BODY_NEON_RED));
426 std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE)); 427 WriteIntegerSetting(std::string(player_prefix).append("button_color_left"),
427 WriteSetting(std::string(player_prefix).append("button_color_right"), 428 player.button_color_left,
428 player.button_color_right, 429 std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
429 std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED)); 430 WriteIntegerSetting(std::string(player_prefix).append("button_color_right"),
431 player.button_color_right,
432 std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
430 } 433 }
431} 434}
432 435
433void Config::SaveTouchscreenValues() { 436void Config::SaveTouchscreenValues() {
434 const auto& touchscreen = Settings::values.touchscreen; 437 const auto& touchscreen = Settings::values.touchscreen;
435 438
436 WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true)); 439 WriteBooleanSetting(std::string("touchscreen_enabled"), touchscreen.enabled,
440 std::make_optional(true));
437 441
438 WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, 442 WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
439 std::make_optional(static_cast<u32>(0))); 443 std::make_optional(static_cast<u32>(0)));
440 WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, 444 WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
441 std::make_optional(static_cast<u32>(15))); 445 std::make_optional(static_cast<u32>(15)));
442 WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, 446 WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
443 std::make_optional(static_cast<u32>(15))); 447 std::make_optional(static_cast<u32>(15)));
444} 448}
445 449
446void Config::SaveMotionTouchValues() { 450void Config::SaveMotionTouchValues() {
447 BeginArray(std::string("touch_from_button_maps")); 451 BeginArray(std::string("touch_from_button_maps"));
448 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { 452 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
449 SetArrayIndex(static_cast<int>(p)); 453 SetArrayIndex(static_cast<int>(p));
450 WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, 454 WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
451 std::make_optional(std::string("default"))); 455 std::make_optional(std::string("default")));
452 456
453 BeginArray(std::string("entries")); 457 BeginArray(std::string("entries"));
454 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); 458 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
455 ++q) { 459 ++q) {
456 SetArrayIndex(static_cast<int>(q)); 460 SetArrayIndex(static_cast<int>(q));
457 WriteSetting(std::string("bind"), 461 WriteStringSetting(std::string("bind"),
458 Settings::values.touch_from_button_maps[p].buttons[q]); 462 Settings::values.touch_from_button_maps[p].buttons[q]);
459 } 463 }
460 EndArray(); // entries 464 EndArray(); // entries
461 } 465 }
@@ -520,16 +524,16 @@ void Config::SaveCoreValues() {
520void Config::SaveDataStorageValues() { 524void Config::SaveDataStorageValues() {
521 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); 525 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
522 526
523 WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir), 527 WriteStringSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
524 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); 528 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
525 WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir), 529 WriteStringSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
526 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); 530 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
527 WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir), 531 WriteStringSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
528 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); 532 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
529 WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir), 533 WriteStringSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
530 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); 534 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
531 WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir), 535 WriteStringSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
532 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); 536 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
533 537
534 WriteCategory(Settings::Category::DataStorage); 538 WriteCategory(Settings::Category::DataStorage);
535 539
@@ -540,7 +544,7 @@ void Config::SaveDebuggingValues() {
540 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); 544 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
541 545
542 // Intentionally not using the QT default setting as this is intended to be changed in the ini 546 // Intentionally not using the QT default setting as this is intended to be changed in the ini
543 WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times); 547 WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
544 548
545 WriteCategory(Settings::Category::Debugging); 549 WriteCategory(Settings::Category::Debugging);
546 WriteCategory(Settings::Category::DebuggingGraphics); 550 WriteCategory(Settings::Category::DebuggingGraphics);
@@ -564,11 +568,13 @@ void Config::SaveDisabledAddOnValues() {
564 BeginArray(std::string("")); 568 BeginArray(std::string(""));
565 for (const auto& elem : Settings::values.disabled_addons) { 569 for (const auto& elem : Settings::values.disabled_addons) {
566 SetArrayIndex(i); 570 SetArrayIndex(i);
567 WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0))); 571 WriteIntegerSetting(std::string("title_id"), elem.first,
572 std::make_optional(static_cast<u64>(0)));
568 BeginArray(std::string("disabled")); 573 BeginArray(std::string("disabled"));
569 for (std::size_t j = 0; j < elem.second.size(); ++j) { 574 for (std::size_t j = 0; j < elem.second.size(); ++j) {
570 SetArrayIndex(static_cast<int>(j)); 575 SetArrayIndex(static_cast<int>(j));
571 WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string(""))); 576 WriteStringSetting(std::string("d"), elem.second[j],
577 std::make_optional(std::string("")));
572 } 578 }
573 EndArray(); // disabled 579 EndArray(); // disabled
574 ++i; 580 ++i;
@@ -609,8 +615,8 @@ void Config::SaveRendererValues() {
609void Config::SaveScreenshotValues() { 615void Config::SaveScreenshotValues() {
610 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); 616 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
611 617
612 WriteSetting(std::string("screenshot_path"), 618 WriteStringSetting(std::string("screenshot_path"),
613 FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)); 619 FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
614 WriteCategory(Settings::Category::Screenshots); 620 WriteCategory(Settings::Category::Screenshots);
615 621
616 EndGroup(); 622 EndGroup();
@@ -746,46 +752,70 @@ bool Config::Exists(const std::string& section, const std::string& key) const {
746 return !value.empty(); 752 return !value.empty();
747} 753}
748 754
749template <typename Type> 755void Config::WriteBooleanSetting(const std::string& key, const bool& value,
750void Config::WriteSetting(const std::string& key, const Type& value, 756 const std::optional<bool>& default_value,
751 const std::optional<Type>& default_value, 757 const std::optional<bool>& use_global) {
752 const std::optional<bool>& use_global) { 758 std::optional<std::string> string_default = std::nullopt;
753 std::string full_key = GetFullKey(key, false); 759 if (default_value.has_value()) {
760 string_default = std::make_optional(ToString(default_value.value()));
761 }
762 WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
763}
754 764
755 std::string saved_value; 765template <typename T>
756 std::string string_default; 766std::enable_if_t<std::is_integral_v<T>> Config::WriteIntegerSetting(
757 if constexpr (std::is_same_v<Type, std::string>) { 767 const std::string& key, const T& value, const std::optional<T>& default_value,
758 saved_value.append(AdjustOutputString(value)); 768 const std::optional<bool>& use_global) {
759 if (default_value.has_value()) { 769 std::optional<std::string> string_default = std::nullopt;
760 string_default.append(AdjustOutputString(default_value.value())); 770 if (default_value.has_value()) {
761 } 771 string_default = std::make_optional(ToString(default_value.value()));
762 } else { 772 }
763 saved_value.append(AdjustOutputString(ToString(value))); 773 WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
764 if (default_value.has_value()) { 774}
765 string_default.append(ToString(default_value.value())); 775
766 } 776void Config::WriteDoubleSetting(const std::string& key, const double& value,
777 const std::optional<double>& default_value,
778 const std::optional<bool>& use_global) {
779 std::optional<std::string> string_default = std::nullopt;
780 if (default_value.has_value()) {
781 string_default = std::make_optional(ToString(default_value.value()));
767 } 782 }
783 WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
784}
768 785
769 if (default_value.has_value() && use_global.has_value()) { 786void Config::WriteStringSetting(const std::string& key, const std::string& value,
787 const std::optional<std::string>& default_value,
788 const std::optional<bool>& use_global) {
789 std::optional string_default = default_value;
790 if (default_value.has_value()) {
791 string_default.value().append(AdjustOutputString(default_value.value()));
792 }
793 WritePreparedSetting(key, AdjustOutputString(value), string_default, use_global);
794}
795
796void Config::WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
797 const std::optional<std::string>& adjusted_default_value,
798 const std::optional<bool>& use_global) {
799 std::string full_key = GetFullKey(key, false);
800 if (adjusted_default_value.has_value() && use_global.has_value()) {
770 if (!global) { 801 if (!global) {
771 WriteSettingInternal(std::string(full_key).append("\\global"), 802 WriteString(std::string(full_key).append("\\global"), ToString(use_global.value()));
772 ToString(use_global.value()));
773 } 803 }
774 if (global || use_global.value() == false) { 804 if (global || use_global.value() == false) {
775 WriteSettingInternal(std::string(full_key).append("\\default"), 805 WriteString(std::string(full_key).append("\\default"),
776 ToString(string_default == saved_value)); 806 ToString(adjusted_default_value == adjusted_value));
777 WriteSettingInternal(full_key, saved_value); 807 WriteString(full_key, adjusted_value);
778 } 808 }
779 } else if (default_value.has_value() && !use_global.has_value()) { 809 } else if (adjusted_default_value.has_value() && !use_global.has_value()) {
780 WriteSettingInternal(std::string(full_key).append("\\default"), 810 WriteString(std::string(full_key).append("\\default"),
781 ToString(string_default == saved_value)); 811 ToString(adjusted_default_value == adjusted_value));
782 WriteSettingInternal(full_key, saved_value); 812 WriteString(full_key, adjusted_value);
783 } else { 813 } else {
784 WriteSettingInternal(full_key, saved_value); 814 WriteString(full_key, adjusted_value);
785 } 815 }
786} 816}
787 817
788void Config::WriteSettingInternal(const std::string& key, const std::string& value) { 818void Config::WriteString(const std::string& key, const std::string& value) {
789 config->SetValue(GetSection().c_str(), key.c_str(), value.c_str()); 819 config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
790} 820}
791 821
@@ -861,17 +891,17 @@ void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
861 std::string key = AdjustKey(setting->GetLabel()); 891 std::string key = AdjustKey(setting->GetLabel());
862 if (setting->Switchable()) { 892 if (setting->Switchable()) {
863 if (!global) { 893 if (!global) {
864 WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); 894 WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
865 } 895 }
866 if (global || !setting->UsingGlobal()) { 896 if (global || !setting->UsingGlobal()) {
867 WriteSetting(std::string(key).append("\\default"), 897 WriteBooleanSetting(std::string(key).append("\\default"),
868 setting->ToString() == setting->DefaultToString()); 898 setting->ToString() == setting->DefaultToString());
869 WriteSetting(key, setting->ToString()); 899 WriteStringSetting(key, setting->ToString());
870 } 900 }
871 } else if (global) { 901 } else if (global) {
872 WriteSetting(std::string(key).append("\\default"), 902 WriteBooleanSetting(std::string(key).append("\\default"),
873 setting->ToString() == setting->DefaultToString()); 903 setting->ToString() == setting->DefaultToString());
874 WriteSetting(key, setting->ToString()); 904 WriteStringSetting(key, setting->ToString());
875 } 905 }
876} 906}
877 907
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
index b3812af17..0c4d505b8 100644
--- a/src/frontend_common/config.h
+++ b/src/frontend_common/config.h
@@ -154,11 +154,20 @@ protected:
154 * @param use_global Specifies if the custom or global config should be in use, for custom 154 * @param use_global Specifies if the custom or global config should be in use, for custom
155 * configs 155 * configs
156 */ 156 */
157 template <typename Type = int> 157 void WriteBooleanSetting(const std::string& key, const bool& value,
158 void WriteSetting(const std::string& key, const Type& value, 158 const std::optional<bool>& default_value = std::nullopt,
159 const std::optional<Type>& default_value = std::nullopt, 159 const std::optional<bool>& use_global = std::nullopt);
160 const std::optional<bool>& use_global = std::nullopt); 160 template <typename T>
161 void WriteSettingInternal(const std::string& key, const std::string& value); 161 std::enable_if_t<std::is_integral_v<T>> WriteIntegerSetting(
162 const std::string& key, const T& value,
163 const std::optional<T>& default_value = std::nullopt,
164 const std::optional<bool>& use_global = std::nullopt);
165 void WriteDoubleSetting(const std::string& key, const double& value,
166 const std::optional<double>& default_value = std::nullopt,
167 const std::optional<bool>& use_global = std::nullopt);
168 void WriteStringSetting(const std::string& key, const std::string& value,
169 const std::optional<std::string>& default_value = std::nullopt,
170 const std::optional<bool>& use_global = std::nullopt);
162 171
163 void ReadCategory(Settings::Category category); 172 void ReadCategory(Settings::Category category);
164 void WriteCategory(Settings::Category category); 173 void WriteCategory(Settings::Category category);
@@ -175,8 +184,10 @@ protected:
175 return value_ ? "true" : "false"; 184 return value_ ? "true" : "false";
176 } else if constexpr (std::is_same_v<T, u64>) { 185 } else if constexpr (std::is_same_v<T, u64>) {
177 return std::to_string(static_cast<u64>(value_)); 186 return std::to_string(static_cast<u64>(value_));
178 } else { 187 } else if constexpr (std::is_same_v<T, s64>) {
179 return std::to_string(static_cast<s64>(value_)); 188 return std::to_string(static_cast<s64>(value_));
189 } else {
190 return std::to_string(value_);
180 } 191 }
181 } 192 }
182 193
@@ -197,9 +208,13 @@ protected:
197 const bool global; 208 const bool global;
198 209
199private: 210private:
200 inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*', 211 void WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
201 '|', ';', '\'', '\"', ',', '<', '.', 212 const std::optional<std::string>& adjusted_default_value,
202 '>', '?', '`', '~', '='}; 213 const std::optional<bool>& use_global);
214 void WriteString(const std::string& key, const std::string& value);
215
216 inline static std::array<char, 18> special_characters = {
217 '!', '#', '$', '%', '^', '&', '*', '|', ';', '\'', '\"', ',', '<', '>', '?', '`', '~', '='};
203 218
204 struct ConfigArray { 219 struct ConfigArray {
205 std::string name; 220 std::string name;
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index 1a28e862b..cb040c942 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -12,6 +12,7 @@ using namespace Common::Literals;
12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; 12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
13static constexpr size_t BACKING_SIZE = 4_GiB; 13static constexpr size_t BACKING_SIZE = 4_GiB;
14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; 14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
15static constexpr auto HEAP = false;
15 16
16TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { 17TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
17 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } 18 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
@@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
20 21
21TEST_CASE("HostMemory: Simple map", "[common]") { 22TEST_CASE("HostMemory: Simple map", "[common]") {
22 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 23 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
23 mem.Map(0x5000, 0x8000, 0x1000, PERMS); 24 mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
24 25
25 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 26 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
26 data[0] = 50; 27 data[0] = 50;
@@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
29 30
30TEST_CASE("HostMemory: Simple mirror map", "[common]") { 31TEST_CASE("HostMemory: Simple mirror map", "[common]") {
31 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 32 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
32 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 33 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
33 mem.Map(0x8000, 0x4000, 0x1000, PERMS); 34 mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
34 35
35 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; 36 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
36 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; 37 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
@@ -40,116 +41,116 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
40 41
41TEST_CASE("HostMemory: Simple unmap", "[common]") { 42TEST_CASE("HostMemory: Simple unmap", "[common]") {
42 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 43 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
43 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 44 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
44 45
45 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 46 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
46 data[75] = 50; 47 data[75] = 50;
47 REQUIRE(data[75] == 50); 48 REQUIRE(data[75] == 50);
48 49
49 mem.Unmap(0x5000, 0x2000); 50 mem.Unmap(0x5000, 0x2000, HEAP);
50} 51}
51 52
52TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { 53TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
53 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 54 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
54 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 55 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
55 56
56 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 57 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
57 data[0] = 50; 58 data[0] = 50;
58 REQUIRE(data[0] == 50); 59 REQUIRE(data[0] == 50);
59 60
60 mem.Unmap(0x5000, 0x2000); 61 mem.Unmap(0x5000, 0x2000, HEAP);
61 62
62 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 63 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
63 REQUIRE(data[0] == 50); 64 REQUIRE(data[0] == 50);
64 65
65 mem.Map(0x7000, 0x2000, 0x5000, PERMS); 66 mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP);
66 REQUIRE(data[0x3000] == 50); 67 REQUIRE(data[0x3000] == 50);
67} 68}
68 69
69TEST_CASE("HostMemory: Nieche allocation", "[common]") { 70TEST_CASE("HostMemory: Nieche allocation", "[common]") {
70 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 71 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
71 mem.Map(0x0000, 0, 0x20000, PERMS); 72 mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
72 mem.Unmap(0x0000, 0x4000); 73 mem.Unmap(0x0000, 0x4000, HEAP);
73 mem.Map(0x1000, 0, 0x2000, PERMS); 74 mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
74 mem.Map(0x3000, 0, 0x1000, PERMS); 75 mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
75 mem.Map(0, 0, 0x1000, PERMS); 76 mem.Map(0, 0, 0x1000, PERMS, HEAP);
76} 77}
77 78
78TEST_CASE("HostMemory: Full unmap", "[common]") { 79TEST_CASE("HostMemory: Full unmap", "[common]") {
79 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 80 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
80 mem.Map(0x8000, 0, 0x4000, PERMS); 81 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
81 mem.Unmap(0x8000, 0x4000); 82 mem.Unmap(0x8000, 0x4000, HEAP);
82 mem.Map(0x6000, 0, 0x16000, PERMS); 83 mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
83} 84}
84 85
85TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { 86TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
86 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 87 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
87 mem.Map(0x0000, 0, 0x4000, PERMS); 88 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
88 mem.Unmap(0x2000, 0x4000); 89 mem.Unmap(0x2000, 0x4000, HEAP);
89 mem.Map(0x2000, 0x80000, 0x4000, PERMS); 90 mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
90} 91}
91 92
92TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { 93TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
93 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 94 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
94 mem.Map(0x8000, 0, 0x4000, PERMS); 95 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
95 mem.Unmap(0x6000, 0x4000); 96 mem.Unmap(0x6000, 0x4000, HEAP);
96 mem.Map(0x8000, 0, 0x2000, PERMS); 97 mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
97} 98}
98 99
99TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { 100TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
100 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 101 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
101 mem.Map(0x0000, 0, 0x4000, PERMS); 102 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
102 mem.Map(0x4000, 0, 0x1b000, PERMS); 103 mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
103 mem.Unmap(0x3000, 0x1c000); 104 mem.Unmap(0x3000, 0x1c000, HEAP);
104 mem.Map(0x3000, 0, 0x20000, PERMS); 105 mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
105} 106}
106 107
107TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { 108TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
108 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 109 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
109 mem.Map(0x0000, 0, 0x4000, PERMS); 110 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
110 mem.Map(0x4000, 0, 0x4000, PERMS); 111 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
111 mem.Unmap(0x2000, 0x4000); 112 mem.Unmap(0x2000, 0x4000, HEAP);
112 mem.Map(0x2000, 0, 0x4000, PERMS); 113 mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
113} 114}
114 115
115TEST_CASE("HostMemory: Unmap to origin", "[common]") { 116TEST_CASE("HostMemory: Unmap to origin", "[common]") {
116 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 117 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
117 mem.Map(0x4000, 0, 0x4000, PERMS); 118 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
118 mem.Map(0x8000, 0, 0x4000, PERMS); 119 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
119 mem.Unmap(0x4000, 0x4000); 120 mem.Unmap(0x4000, 0x4000, HEAP);
120 mem.Map(0, 0, 0x4000, PERMS); 121 mem.Map(0, 0, 0x4000, PERMS, HEAP);
121 mem.Map(0x4000, 0, 0x4000, PERMS); 122 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
122} 123}
123 124
124TEST_CASE("HostMemory: Unmap to right", "[common]") { 125TEST_CASE("HostMemory: Unmap to right", "[common]") {
125 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 126 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
126 mem.Map(0x4000, 0, 0x4000, PERMS); 127 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
127 mem.Map(0x8000, 0, 0x4000, PERMS); 128 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
128 mem.Unmap(0x8000, 0x4000); 129 mem.Unmap(0x8000, 0x4000, HEAP);
129 mem.Map(0x8000, 0, 0x4000, PERMS); 130 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
130} 131}
131 132
132TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { 133TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
133 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 134 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
134 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 135 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
135 136
136 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 137 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
137 ptr[0x1000] = 17; 138 ptr[0x1000] = 17;
138 139
139 mem.Unmap(0x6000, 0x2000); 140 mem.Unmap(0x6000, 0x2000, HEAP);
140 141
141 REQUIRE(ptr[0x1000] == 17); 142 REQUIRE(ptr[0x1000] == 17);
142} 143}
143 144
144TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { 145TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
145 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 146 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
146 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 147 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
147 148
148 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 149 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
149 ptr[0x3000] = 19; 150 ptr[0x3000] = 19;
150 ptr[0x3fff] = 12; 151 ptr[0x3fff] = 12;
151 152
152 mem.Unmap(0x4000, 0x2000); 153 mem.Unmap(0x4000, 0x2000, HEAP);
153 154
154 REQUIRE(ptr[0x3000] == 19); 155 REQUIRE(ptr[0x3000] == 19);
155 REQUIRE(ptr[0x3fff] == 12); 156 REQUIRE(ptr[0x3fff] == 12);
@@ -157,13 +158,13 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
157 158
158TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { 159TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
159 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 160 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
160 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 161 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
161 162
162 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 163 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
163 ptr[0x0000] = 19; 164 ptr[0x0000] = 19;
164 ptr[0x3fff] = 12; 165 ptr[0x3fff] = 12;
165 166
166 mem.Unmap(0x1000, 0x2000); 167 mem.Unmap(0x1000, 0x2000, HEAP);
167 168
168 REQUIRE(ptr[0x0000] == 19); 169 REQUIRE(ptr[0x0000] == 19);
169 REQUIRE(ptr[0x3fff] == 12); 170 REQUIRE(ptr[0x3fff] == 12);
@@ -171,14 +172,14 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
171 172
172TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { 173TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
173 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 174 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
174 mem.Map(0x4000, 0x10000, 0x2000, PERMS); 175 mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
175 mem.Map(0x6000, 0x20000, 0x2000, PERMS); 176 mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
176 177
177 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 178 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
178 ptr[0x0000] = 19; 179 ptr[0x0000] = 19;
179 ptr[0x3fff] = 12; 180 ptr[0x3fff] = 12;
180 181
181 mem.Unmap(0x5000, 0x2000); 182 mem.Unmap(0x5000, 0x2000, HEAP);
182 183
183 REQUIRE(ptr[0x0000] == 19); 184 REQUIRE(ptr[0x0000] == 19);
184 REQUIRE(ptr[0x3fff] == 12); 185 REQUIRE(ptr[0x3fff] == 12);
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index f08afbf9a..81898a1d3 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -16,20 +16,16 @@
16 16
17namespace { 17namespace {
18// Numbers are chosen randomly to make sure the correct one is given. 18// Numbers are chosen randomly to make sure the correct one is given.
19constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
20constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; 19constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
21std::array<s64, 5> delays{}; 20std::array<s64, 5> delays{};
22 21std::bitset<5> callbacks_ran_flags;
23std::bitset<CB_IDS.size()> callbacks_ran_flags;
24u64 expected_callback = 0; 22u64 expected_callback = 0;
25 23
26template <unsigned int IDX> 24template <unsigned int IDX>
27std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time, 25std::optional<std::chrono::nanoseconds> HostCallbackTemplate(s64 time,
28 std::chrono::nanoseconds ns_late) { 26 std::chrono::nanoseconds ns_late) {
29 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 27 static_assert(IDX < callbacks_ran_flags.size(), "IDX out of range");
30 callbacks_ran_flags.set(IDX); 28 callbacks_ran_flags.set(IDX);
31 REQUIRE(CB_IDS[IDX] == user_data);
32 REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
33 delays[IDX] = ns_late.count(); 29 delays[IDX] = ns_late.count();
34 ++expected_callback; 30 ++expected_callback;
35 return std::nullopt; 31 return std::nullopt;
@@ -76,7 +72,7 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
76 const u64 order = calls_order[i]; 72 const u64 order = calls_order[i];
77 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)}; 73 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
78 74
79 core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); 75 core_timing.ScheduleEvent(future_ns, events[order]);
80 } 76 }
81 /// test pause 77 /// test pause
82 REQUIRE(callbacks_ran_flags.none()); 78 REQUIRE(callbacks_ran_flags.none());
@@ -118,7 +114,7 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
118 for (std::size_t i = 0; i < events.size(); i++) { 114 for (std::size_t i = 0; i < events.size(); i++) {
119 const u64 order = calls_order[i]; 115 const u64 order = calls_order[i];
120 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)}; 116 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
121 core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); 117 core_timing.ScheduleEvent(future_ns, events[order]);
122 } 118 }
123 119
124 const u64 end = core_timing.GetGlobalTimeNs().count(); 120 const u64 end = core_timing.GetGlobalTimeNs().count();
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 422d4d859..56fbff306 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -228,7 +228,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
228 228
229 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( 229 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
230 memory_manager, src_operand.address, src_size, &read_buffer); 230 memory_manager, src_operand.address, src_size, &read_buffer);
231 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> 231 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
232 tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer); 232 tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer);
233 233
234 UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth, 234 UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
@@ -292,7 +292,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
292 GPUVAddr dst_addr = regs.offset_out; 292 GPUVAddr dst_addr = regs.offset_out;
293 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( 293 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
294 memory_manager, src_addr, src_size, &read_buffer); 294 memory_manager, src_addr, src_size, &read_buffer);
295 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> 295 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
296 tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer); 296 tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer);
297 297
298 // If the input is linear and the output is tiled, swizzle the input and copy it over. 298 // If the input is linear and the output is tiled, swizzle the input and copy it over.
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 5e7518d96..792ed9615 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -329,7 +329,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) {
329 // to account for that. 329 // to account for that.
330 const bool is_suboptimal = swapchain.NeedsRecreation(); 330 const bool is_suboptimal = swapchain.NeedsRecreation();
331 const bool size_changed = 331 const bool size_changed =
332 swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height; 332 swapchain.GetWidth() < frame->width || swapchain.GetHeight() < frame->height;
333 if (is_suboptimal || size_changed) { 333 if (is_suboptimal || size_changed) {
334 RecreateSwapchain(frame); 334 RecreateSwapchain(frame);
335 } 335 }
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index a71000b72..6aca71d7c 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -348,43 +348,45 @@ void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
348 348
349 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 349 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
350 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 350 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
351 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), 351 WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
352 player.buttons[i], std::make_optional(default_param)); 352 player.buttons[i], std::make_optional(default_param));
353 } 353 }
354 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 354 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
355 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 355 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
356 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 356 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
357 default_analogs[i][3], default_stick_mod[i], 0.5f); 357 default_analogs[i][3], default_stick_mod[i], 0.5f);
358 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), 358 WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
359 player.analogs[i], std::make_optional(default_param)); 359 player.analogs[i], std::make_optional(default_param));
360 } 360 }
361 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 361 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
362 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 362 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
363 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), 363 WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
364 player.motions[i], std::make_optional(default_param)); 364 player.motions[i], std::make_optional(default_param));
365 } 365 }
366} 366}
367 367
368void QtConfig::SaveDebugControlValues() { 368void QtConfig::SaveDebugControlValues() {
369 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 369 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
370 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 370 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
371 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), 371 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
372 Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); 372 Settings::values.debug_pad_buttons[i],
373 std::make_optional(default_param));
373 } 374 }
374 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 375 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
375 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 376 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
376 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 377 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
377 default_analogs[i][3], default_stick_mod[i], 0.5f); 378 default_analogs[i][3], default_stick_mod[i], 0.5f);
378 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), 379 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
379 Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); 380 Settings::values.debug_pad_analogs[i],
381 std::make_optional(default_param));
380 } 382 }
381} 383}
382 384
383void QtConfig::SaveHidbusValues() { 385void QtConfig::SaveHidbusValues() {
384 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 386 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
385 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); 387 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
386 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, 388 WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
387 std::make_optional(default_param)); 389 std::make_optional(default_param));
388} 390}
389 391
390void QtConfig::SaveQtControlValues() { 392void QtConfig::SaveQtControlValues() {
@@ -409,19 +411,20 @@ void QtConfig::SavePathValues() {
409 411
410 WriteCategory(Settings::Category::Paths); 412 WriteCategory(Settings::Category::Paths);
411 413
412 WriteSetting(std::string("romsPath"), UISettings::values.roms_path); 414 WriteStringSetting(std::string("romsPath"), UISettings::values.roms_path);
413 BeginArray(std::string("gamedirs")); 415 BeginArray(std::string("gamedirs"));
414 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { 416 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
415 SetArrayIndex(i); 417 SetArrayIndex(i);
416 const auto& game_dir = UISettings::values.game_dirs[i]; 418 const auto& game_dir = UISettings::values.game_dirs[i];
417 WriteSetting(std::string("path"), game_dir.path); 419 WriteStringSetting(std::string("path"), game_dir.path);
418 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); 420 WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
419 WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true)); 421 std::make_optional(false));
422 WriteBooleanSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
420 } 423 }
421 EndArray(); 424 EndArray();
422 425
423 WriteSetting(std::string("recentFiles"), 426 WriteStringSetting(std::string("recentFiles"),
424 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); 427 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
425 428
426 EndGroup(); 429 EndGroup();
427} 430}
@@ -438,14 +441,14 @@ void QtConfig::SaveShortcutValues() {
438 BeginGroup(group); 441 BeginGroup(group);
439 BeginGroup(name); 442 BeginGroup(name);
440 443
441 WriteSetting(std::string("KeySeq"), shortcut.keyseq, 444 WriteStringSetting(std::string("KeySeq"), shortcut.keyseq,
442 std::make_optional(default_hotkey.keyseq)); 445 std::make_optional(default_hotkey.keyseq));
443 WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq, 446 WriteStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
444 std::make_optional(default_hotkey.controller_keyseq)); 447 std::make_optional(default_hotkey.controller_keyseq));
445 WriteSetting(std::string("Context"), shortcut.context, 448 WriteIntegerSetting(std::string("Context"), shortcut.context,
446 std::make_optional(default_hotkey.context)); 449 std::make_optional(default_hotkey.context));
447 WriteSetting(std::string("Repeat"), shortcut.repeat, 450 WriteBooleanSetting(std::string("Repeat"), shortcut.repeat,
448 std::make_optional(default_hotkey.repeat)); 451 std::make_optional(default_hotkey.repeat));
449 452
450 EndGroup(); // name 453 EndGroup(); // name
451 EndGroup(); // group 454 EndGroup(); // group
@@ -460,9 +463,10 @@ void QtConfig::SaveUIValues() {
460 WriteCategory(Settings::Category::Ui); 463 WriteCategory(Settings::Category::Ui);
461 WriteCategory(Settings::Category::UiGeneral); 464 WriteCategory(Settings::Category::UiGeneral);
462 465
463 WriteSetting(std::string("theme"), UISettings::values.theme, 466 WriteStringSetting(
464 std::make_optional(std::string( 467 std::string("theme"), UISettings::values.theme,
465 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second))); 468 std::make_optional(std::string(
469 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
466 470
467 SaveUIGamelistValues(); 471 SaveUIGamelistValues();
468 SaveUILayoutValues(); 472 SaveUILayoutValues();
@@ -482,7 +486,7 @@ void QtConfig::SaveUIGamelistValues() {
482 BeginArray(std::string("favorites")); 486 BeginArray(std::string("favorites"));
483 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { 487 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
484 SetArrayIndex(i); 488 SetArrayIndex(i);
485 WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]); 489 WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
486 } 490 }
487 EndArray(); // favorites 491 EndArray(); // favorites
488 492
@@ -506,14 +510,15 @@ void QtConfig::SaveMultiplayerValues() {
506 BeginArray(std::string("username_ban_list")); 510 BeginArray(std::string("username_ban_list"));
507 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { 511 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
508 SetArrayIndex(static_cast<int>(i)); 512 SetArrayIndex(static_cast<int>(i));
509 WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]); 513 WriteStringSetting(std::string("username"),
514 UISettings::values.multiplayer_ban_list.first[i]);
510 } 515 }
511 EndArray(); // username_ban_list 516 EndArray(); // username_ban_list
512 517
513 BeginArray(std::string("ip_ban_list")); 518 BeginArray(std::string("ip_ban_list"));
514 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { 519 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
515 SetArrayIndex(static_cast<int>(i)); 520 SetArrayIndex(static_cast<int>(i));
516 WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]); 521 WriteStringSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
517 } 522 }
518 EndArray(); // ip_ban_list 523 EndArray(); // ip_ban_list
519 524
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
index 39fd8050c..e81bf5d45 100644
--- a/src/yuzu_cmd/sdl_config.cpp
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -213,43 +213,45 @@ void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
213 213
214 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 214 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
215 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 215 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
216 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), 216 WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
217 player.buttons[i], std::make_optional(default_param)); 217 player.buttons[i], std::make_optional(default_param));
218 } 218 }
219 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 219 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
220 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 220 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
221 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 221 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
222 default_analogs[i][3], default_stick_mod[i], 0.5f); 222 default_analogs[i][3], default_stick_mod[i], 0.5f);
223 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), 223 WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
224 player.analogs[i], std::make_optional(default_param)); 224 player.analogs[i], std::make_optional(default_param));
225 } 225 }
226 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 226 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
227 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 227 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
228 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), 228 WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
229 player.motions[i], std::make_optional(default_param)); 229 player.motions[i], std::make_optional(default_param));
230 } 230 }
231} 231}
232 232
233void SdlConfig::SaveDebugControlValues() { 233void SdlConfig::SaveDebugControlValues() {
234 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 234 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
235 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 235 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
236 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), 236 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
237 Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); 237 Settings::values.debug_pad_buttons[i],
238 std::make_optional(default_param));
238 } 239 }
239 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 240 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
240 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 241 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
241 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 242 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
242 default_analogs[i][3], default_stick_mod[i], 0.5f); 243 default_analogs[i][3], default_stick_mod[i], 0.5f);
243 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), 244 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
244 Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); 245 Settings::values.debug_pad_analogs[i],
246 std::make_optional(default_param));
245 } 247 }
246} 248}
247 249
248void SdlConfig::SaveHidbusValues() { 250void SdlConfig::SaveHidbusValues() {
249 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 251 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
250 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); 252 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
251 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, 253 WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
252 std::make_optional(default_param)); 254 std::make_optional(default_param));
253} 255}
254 256
255std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) { 257std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {