summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt67
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt410
-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.kt3
-rw-r--r--src/android/app/src/main/res/drawable/button_l3.xml128
-rw-r--r--src/android/app/src/main/res/drawable/button_l3_depressed.xml75
-rw-r--r--src/android/app/src/main/res/drawable/button_r3.xml128
-rw-r--r--src/android/app/src/main/res/drawable/button_r3_depressed.xml75
-rw-r--r--src/android/app/src/main/res/values/arrays.xml2
-rw-r--r--src/android/app/src/main/res/values/integers.xml12
-rw-r--r--src/video_core/texture_cache/texture_cache.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp43
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h8
-rw-r--r--src/yuzu/main.cpp2
15 files changed, 797 insertions, 181 deletions
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 be6e17e65..a6251bafd 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
@@ -112,25 +112,36 @@ class Settings {
112 112
113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
114 114
115 const val PREF_OVERLAY_INIT = "OverlayInit" 115 const val PREF_OVERLAY_VERSION = "OverlayVersion"
116 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
117 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
118 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
119 val overlayLayoutPrefs = listOf(
120 PREF_LANDSCAPE_OVERLAY_VERSION,
121 PREF_PORTRAIT_OVERLAY_VERSION,
122 PREF_FOLDABLE_OVERLAY_VERSION
123 )
124
116 const val PREF_CONTROL_SCALE = "controlScale" 125 const val PREF_CONTROL_SCALE = "controlScale"
117 const val PREF_CONTROL_OPACITY = "controlOpacity" 126 const val PREF_CONTROL_OPACITY = "controlOpacity"
118 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 127 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
119 const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0" 128 const val PREF_BUTTON_A = "buttonToggle0"
120 const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1" 129 const val PREF_BUTTON_B = "buttonToggle1"
121 const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2" 130 const val PREF_BUTTON_X = "buttonToggle2"
122 const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3" 131 const val PREF_BUTTON_Y = "buttonToggle3"
123 const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4" 132 const val PREF_BUTTON_L = "buttonToggle4"
124 const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5" 133 const val PREF_BUTTON_R = "buttonToggle5"
125 const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6" 134 const val PREF_BUTTON_ZL = "buttonToggle6"
126 const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7" 135 const val PREF_BUTTON_ZR = "buttonToggle7"
127 const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8" 136 const val PREF_BUTTON_PLUS = "buttonToggle8"
128 const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9" 137 const val PREF_BUTTON_MINUS = "buttonToggle9"
129 const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10" 138 const val PREF_BUTTON_DPAD = "buttonToggle10"
130 const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11" 139 const val PREF_STICK_L = "buttonToggle11"
131 const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12" 140 const val PREF_STICK_R = "buttonToggle12"
132 const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" 141 const val PREF_BUTTON_STICK_L = "buttonToggle13"
133 const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" 142 const val PREF_BUTTON_STICK_R = "buttonToggle14"
143 const val PREF_BUTTON_HOME = "buttonToggle15"
144 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
134 145
135 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" 146 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
136 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" 147 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
@@ -145,6 +156,30 @@ class Settings {
145 156
146 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() 157 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
147 158
159 val overlayPreferences = listOf(
160 PREF_OVERLAY_VERSION,
161 PREF_CONTROL_SCALE,
162 PREF_CONTROL_OPACITY,
163 PREF_TOUCH_ENABLED,
164 PREF_BUTTON_A,
165 PREF_BUTTON_B,
166 PREF_BUTTON_X,
167 PREF_BUTTON_Y,
168 PREF_BUTTON_L,
169 PREF_BUTTON_R,
170 PREF_BUTTON_ZL,
171 PREF_BUTTON_ZR,
172 PREF_BUTTON_PLUS,
173 PREF_BUTTON_MINUS,
174 PREF_BUTTON_DPAD,
175 PREF_STICK_L,
176 PREF_STICK_R,
177 PREF_BUTTON_HOME,
178 PREF_BUTTON_SCREENSHOT,
179 PREF_BUTTON_STICK_L,
180 PREF_BUTTON_STICK_R
181 )
182
148 const val LayoutOption_Unspecified = 0 183 const val LayoutOption_Unspecified = 0
149 const val LayoutOption_MobilePortrait = 4 184 const val LayoutOption_MobilePortrait = 4
150 const val LayoutOption_MobileLandscape = 5 185 const val LayoutOption_MobileLandscape = 5
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 09976db62..0e7c1ba88 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
@@ -212,9 +212,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
212 } 212 }
213 if (!isInFoldableLayout) { 213 if (!isInFoldableLayout) {
214 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 214 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
215 binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT 215 binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
216 } else { 216 } else {
217 binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE 217 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
218 } 218 }
219 } 219 }
220 if (!binding.surfaceInputOverlay.isInEditMode) { 220 if (!binding.surfaceInputOverlay.isInEditMode) {
@@ -260,7 +260,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
260 .remove(Settings.PREF_CONTROL_SCALE) 260 .remove(Settings.PREF_CONTROL_SCALE)
261 .remove(Settings.PREF_CONTROL_OPACITY) 261 .remove(Settings.PREF_CONTROL_OPACITY)
262 .apply() 262 .apply()
263 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.resetButtonPlacement() } 263 binding.surfaceInputOverlay.post {
264 binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
265 }
264 } 266 }
265 267
266 private fun updateShowFpsOverlay() { 268 private fun updateShowFpsOverlay() {
@@ -337,7 +339,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
337 binding.inGameMenu.layoutParams.height = it.bounds.bottom 339 binding.inGameMenu.layoutParams.height = it.bounds.bottom
338 340
339 isInFoldableLayout = true 341 isInFoldableLayout = true
340 binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE 342 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
341 refreshInputOverlay() 343 refreshInputOverlay()
342 } 344 }
343 } 345 }
@@ -410,9 +412,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
410 R.id.menu_toggle_controls -> { 412 R.id.menu_toggle_controls -> {
411 val preferences = 413 val preferences =
412 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 414 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
413 val optionsArray = BooleanArray(15) 415 val optionsArray = BooleanArray(Settings.overlayPreferences.size)
414 for (i in 0..14) { 416 Settings.overlayPreferences.forEachIndexed { i, _ ->
415 optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 13) 417 optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
416 } 418 }
417 419
418 val dialog = MaterialAlertDialogBuilder(requireContext()) 420 val dialog = MaterialAlertDialogBuilder(requireContext())
@@ -436,7 +438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
436 dialog.getButton(AlertDialog.BUTTON_NEUTRAL) 438 dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
437 .setOnClickListener { 439 .setOnClickListener {
438 val isChecked = !optionsArray[0] 440 val isChecked = !optionsArray[0]
439 for (i in 0..14) { 441 Settings.overlayPreferences.forEachIndexed { i, _ ->
440 optionsArray[i] = isChecked 442 optionsArray[i] = isChecked
441 dialog.listView.setItemChecked(i, isChecked) 443 dialog.listView.setItemChecked(i, isChecked)
442 preferences.edit() 444 preferences.edit()
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 6251ec783..c055c2e35 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
@@ -51,15 +51,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
51 51
52 private lateinit var windowInsets: WindowInsets 52 private lateinit var windowInsets: WindowInsets
53 53
54 var orientation = LANDSCAPE 54 var layout = LANDSCAPE
55 55
56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
57 super.onLayout(changed, left, top, right, bottom) 57 super.onLayout(changed, left, top, right, bottom)
58 58
59 windowInsets = rootWindowInsets 59 windowInsets = rootWindowInsets
60 60
61 if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { 61 val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0)
62 defaultOverlay() 62 if (overlayVersion != OVERLAY_VERSION) {
63 resetAllLayouts()
64 } else {
65 val layoutIndex = overlayLayouts.indexOf(layout)
66 val currentLayoutVersion =
67 preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
68 if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
69 resetCurrentLayout()
70 }
63 } 71 }
64 72
65 // Load the controls. 73 // Load the controls.
@@ -266,10 +274,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
266 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { 274 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
267 // Persist button position by saving new place. 275 // Persist button position by saving new place.
268 saveControlPosition( 276 saveControlPosition(
269 buttonBeingConfigured!!.buttonId, 277 buttonBeingConfigured!!.prefId,
270 buttonBeingConfigured!!.bounds.centerX(), 278 buttonBeingConfigured!!.bounds.centerX(),
271 buttonBeingConfigured!!.bounds.centerY(), 279 buttonBeingConfigured!!.bounds.centerY(),
272 orientation 280 layout
273 ) 281 )
274 buttonBeingConfigured = null 282 buttonBeingConfigured = null
275 } 283 }
@@ -299,10 +307,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
299 MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { 307 MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
300 // Persist button position by saving new place. 308 // Persist button position by saving new place.
301 saveControlPosition( 309 saveControlPosition(
302 dpadBeingConfigured!!.upId, 310 Settings.PREF_BUTTON_DPAD,
303 dpadBeingConfigured!!.bounds.centerX(), 311 dpadBeingConfigured!!.bounds.centerX(),
304 dpadBeingConfigured!!.bounds.centerY(), 312 dpadBeingConfigured!!.bounds.centerY(),
305 orientation 313 layout
306 ) 314 )
307 dpadBeingConfigured = null 315 dpadBeingConfigured = null
308 } 316 }
@@ -330,10 +338,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
330 MotionEvent.ACTION_UP, 338 MotionEvent.ACTION_UP,
331 MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) { 339 MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) {
332 saveControlPosition( 340 saveControlPosition(
333 joystickBeingConfigured!!.buttonId, 341 joystickBeingConfigured!!.prefId,
334 joystickBeingConfigured!!.bounds.centerX(), 342 joystickBeingConfigured!!.bounds.centerX(),
335 joystickBeingConfigured!!.bounds.centerY(), 343 joystickBeingConfigured!!.bounds.centerY(),
336 orientation 344 layout
337 ) 345 )
338 joystickBeingConfigured = null 346 joystickBeingConfigured = null
339 } 347 }
@@ -343,9 +351,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
343 return true 351 return true
344 } 352 }
345 353
346 private fun addOverlayControls(orientation: String) { 354 private fun addOverlayControls(layout: String) {
347 val windowSize = getSafeScreenSize(context) 355 val windowSize = getSafeScreenSize(context)
348 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) { 356 if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
349 overlayButtons.add( 357 overlayButtons.add(
350 initializeOverlayButton( 358 initializeOverlayButton(
351 context, 359 context,
@@ -353,11 +361,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
353 R.drawable.facebutton_a, 361 R.drawable.facebutton_a,
354 R.drawable.facebutton_a_depressed, 362 R.drawable.facebutton_a_depressed,
355 ButtonType.BUTTON_A, 363 ButtonType.BUTTON_A,
356 orientation 364 Settings.PREF_BUTTON_A,
365 layout
357 ) 366 )
358 ) 367 )
359 } 368 }
360 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_1, true)) { 369 if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) {
361 overlayButtons.add( 370 overlayButtons.add(
362 initializeOverlayButton( 371 initializeOverlayButton(
363 context, 372 context,
@@ -365,11 +374,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
365 R.drawable.facebutton_b, 374 R.drawable.facebutton_b,
366 R.drawable.facebutton_b_depressed, 375 R.drawable.facebutton_b_depressed,
367 ButtonType.BUTTON_B, 376 ButtonType.BUTTON_B,
368 orientation 377 Settings.PREF_BUTTON_B,
378 layout
369 ) 379 )
370 ) 380 )
371 } 381 }
372 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_2, true)) { 382 if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) {
373 overlayButtons.add( 383 overlayButtons.add(
374 initializeOverlayButton( 384 initializeOverlayButton(
375 context, 385 context,
@@ -377,11 +387,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
377 R.drawable.facebutton_x, 387 R.drawable.facebutton_x,
378 R.drawable.facebutton_x_depressed, 388 R.drawable.facebutton_x_depressed,
379 ButtonType.BUTTON_X, 389 ButtonType.BUTTON_X,
380 orientation 390 Settings.PREF_BUTTON_X,
391 layout
381 ) 392 )
382 ) 393 )
383 } 394 }
384 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_3, true)) { 395 if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) {
385 overlayButtons.add( 396 overlayButtons.add(
386 initializeOverlayButton( 397 initializeOverlayButton(
387 context, 398 context,
@@ -389,11 +400,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
389 R.drawable.facebutton_y, 400 R.drawable.facebutton_y,
390 R.drawable.facebutton_y_depressed, 401 R.drawable.facebutton_y_depressed,
391 ButtonType.BUTTON_Y, 402 ButtonType.BUTTON_Y,
392 orientation 403 Settings.PREF_BUTTON_Y,
404 layout
393 ) 405 )
394 ) 406 )
395 } 407 }
396 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_4, true)) { 408 if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) {
397 overlayButtons.add( 409 overlayButtons.add(
398 initializeOverlayButton( 410 initializeOverlayButton(
399 context, 411 context,
@@ -401,11 +413,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
401 R.drawable.l_shoulder, 413 R.drawable.l_shoulder,
402 R.drawable.l_shoulder_depressed, 414 R.drawable.l_shoulder_depressed,
403 ButtonType.TRIGGER_L, 415 ButtonType.TRIGGER_L,
404 orientation 416 Settings.PREF_BUTTON_L,
417 layout
405 ) 418 )
406 ) 419 )
407 } 420 }
408 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_5, true)) { 421 if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) {
409 overlayButtons.add( 422 overlayButtons.add(
410 initializeOverlayButton( 423 initializeOverlayButton(
411 context, 424 context,
@@ -413,11 +426,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
413 R.drawable.r_shoulder, 426 R.drawable.r_shoulder,
414 R.drawable.r_shoulder_depressed, 427 R.drawable.r_shoulder_depressed,
415 ButtonType.TRIGGER_R, 428 ButtonType.TRIGGER_R,
416 orientation 429 Settings.PREF_BUTTON_R,
430 layout
417 ) 431 )
418 ) 432 )
419 } 433 }
420 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_6, true)) { 434 if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) {
421 overlayButtons.add( 435 overlayButtons.add(
422 initializeOverlayButton( 436 initializeOverlayButton(
423 context, 437 context,
@@ -425,11 +439,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
425 R.drawable.zl_trigger, 439 R.drawable.zl_trigger,
426 R.drawable.zl_trigger_depressed, 440 R.drawable.zl_trigger_depressed,
427 ButtonType.TRIGGER_ZL, 441 ButtonType.TRIGGER_ZL,
428 orientation 442 Settings.PREF_BUTTON_ZL,
443 layout
429 ) 444 )
430 ) 445 )
431 } 446 }
432 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_7, true)) { 447 if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) {
433 overlayButtons.add( 448 overlayButtons.add(
434 initializeOverlayButton( 449 initializeOverlayButton(
435 context, 450 context,
@@ -437,11 +452,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
437 R.drawable.zr_trigger, 452 R.drawable.zr_trigger,
438 R.drawable.zr_trigger_depressed, 453 R.drawable.zr_trigger_depressed,
439 ButtonType.TRIGGER_ZR, 454 ButtonType.TRIGGER_ZR,
440 orientation 455 Settings.PREF_BUTTON_ZR,
456 layout
441 ) 457 )
442 ) 458 )
443 } 459 }
444 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_8, true)) { 460 if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) {
445 overlayButtons.add( 461 overlayButtons.add(
446 initializeOverlayButton( 462 initializeOverlayButton(
447 context, 463 context,
@@ -449,11 +465,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
449 R.drawable.facebutton_plus, 465 R.drawable.facebutton_plus,
450 R.drawable.facebutton_plus_depressed, 466 R.drawable.facebutton_plus_depressed,
451 ButtonType.BUTTON_PLUS, 467 ButtonType.BUTTON_PLUS,
452 orientation 468 Settings.PREF_BUTTON_PLUS,
469 layout
453 ) 470 )
454 ) 471 )
455 } 472 }
456 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_9, true)) { 473 if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) {
457 overlayButtons.add( 474 overlayButtons.add(
458 initializeOverlayButton( 475 initializeOverlayButton(
459 context, 476 context,
@@ -461,11 +478,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
461 R.drawable.facebutton_minus, 478 R.drawable.facebutton_minus,
462 R.drawable.facebutton_minus_depressed, 479 R.drawable.facebutton_minus_depressed,
463 ButtonType.BUTTON_MINUS, 480 ButtonType.BUTTON_MINUS,
464 orientation 481 Settings.PREF_BUTTON_MINUS,
482 layout
465 ) 483 )
466 ) 484 )
467 } 485 }
468 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_10, true)) { 486 if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) {
469 overlayDpads.add( 487 overlayDpads.add(
470 initializeOverlayDpad( 488 initializeOverlayDpad(
471 context, 489 context,
@@ -473,11 +491,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
473 R.drawable.dpad_standard, 491 R.drawable.dpad_standard,
474 R.drawable.dpad_standard_cardinal_depressed, 492 R.drawable.dpad_standard_cardinal_depressed,
475 R.drawable.dpad_standard_diagonal_depressed, 493 R.drawable.dpad_standard_diagonal_depressed,
476 orientation 494 layout
477 ) 495 )
478 ) 496 )
479 } 497 }
480 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_11, true)) { 498 if (preferences.getBoolean(Settings.PREF_STICK_L, true)) {
481 overlayJoysticks.add( 499 overlayJoysticks.add(
482 initializeOverlayJoystick( 500 initializeOverlayJoystick(
483 context, 501 context,
@@ -487,11 +505,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
487 R.drawable.joystick_depressed, 505 R.drawable.joystick_depressed,
488 StickType.STICK_L, 506 StickType.STICK_L,
489 ButtonType.STICK_L, 507 ButtonType.STICK_L,
490 orientation 508 Settings.PREF_STICK_L,
509 layout
491 ) 510 )
492 ) 511 )
493 } 512 }
494 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_12, true)) { 513 if (preferences.getBoolean(Settings.PREF_STICK_R, true)) {
495 overlayJoysticks.add( 514 overlayJoysticks.add(
496 initializeOverlayJoystick( 515 initializeOverlayJoystick(
497 context, 516 context,
@@ -501,11 +520,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
501 R.drawable.joystick_depressed, 520 R.drawable.joystick_depressed,
502 StickType.STICK_R, 521 StickType.STICK_R,
503 ButtonType.STICK_R, 522 ButtonType.STICK_R,
504 orientation 523 Settings.PREF_STICK_R,
524 layout
505 ) 525 )
506 ) 526 )
507 } 527 }
508 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_13, false)) { 528 if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) {
509 overlayButtons.add( 529 overlayButtons.add(
510 initializeOverlayButton( 530 initializeOverlayButton(
511 context, 531 context,
@@ -513,11 +533,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
513 R.drawable.facebutton_home, 533 R.drawable.facebutton_home,
514 R.drawable.facebutton_home_depressed, 534 R.drawable.facebutton_home_depressed,
515 ButtonType.BUTTON_HOME, 535 ButtonType.BUTTON_HOME,
516 orientation 536 Settings.PREF_BUTTON_HOME,
537 layout
517 ) 538 )
518 ) 539 )
519 } 540 }
520 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_14, false)) { 541 if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) {
521 overlayButtons.add( 542 overlayButtons.add(
522 initializeOverlayButton( 543 initializeOverlayButton(
523 context, 544 context,
@@ -525,7 +546,34 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
525 R.drawable.facebutton_screenshot, 546 R.drawable.facebutton_screenshot,
526 R.drawable.facebutton_screenshot_depressed, 547 R.drawable.facebutton_screenshot_depressed,
527 ButtonType.BUTTON_CAPTURE, 548 ButtonType.BUTTON_CAPTURE,
528 orientation 549 Settings.PREF_BUTTON_SCREENSHOT,
550 layout
551 )
552 )
553 }
554 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) {
555 overlayButtons.add(
556 initializeOverlayButton(
557 context,
558 windowSize,
559 R.drawable.button_l3,
560 R.drawable.button_l3_depressed,
561 ButtonType.STICK_L,
562 Settings.PREF_BUTTON_STICK_L,
563 layout
564 )
565 )
566 }
567 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) {
568 overlayButtons.add(
569 initializeOverlayButton(
570 context,
571 windowSize,
572 R.drawable.button_r3,
573 R.drawable.button_r3_depressed,
574 ButtonType.STICK_R,
575 Settings.PREF_BUTTON_STICK_R,
576 layout
529 ) 577 )
530 ) 578 )
531 } 579 }
@@ -539,18 +587,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
539 587
540 // Add all the enabled overlay items back to the HashSet. 588 // Add all the enabled overlay items back to the HashSet.
541 if (EmulationMenuSettings.showOverlay) { 589 if (EmulationMenuSettings.showOverlay) {
542 addOverlayControls(orientation) 590 addOverlayControls(layout)
543 } 591 }
544 invalidate() 592 invalidate()
545 } 593 }
546 594
547 private fun saveControlPosition(sharedPrefsId: Int, x: Int, y: Int, orientation: String) { 595 private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) {
548 val windowSize = getSafeScreenSize(context) 596 val windowSize = getSafeScreenSize(context)
549 val min = windowSize.first 597 val min = windowSize.first
550 val max = windowSize.second 598 val max = windowSize.second
551 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 599 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
552 .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x) 600 .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x)
553 .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y) 601 .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y)
554 .apply() 602 .apply()
555 } 603 }
556 604
@@ -558,19 +606,31 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
558 inEditMode = editMode 606 inEditMode = editMode
559 } 607 }
560 608
561 private fun defaultOverlay() { 609 private fun resetCurrentLayout() {
562 if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { 610 defaultOverlayByLayout(layout)
563 defaultOverlayByLayout(orientation) 611 val layoutIndex = overlayLayouts.indexOf(layout)
564 }
565
566 resetButtonPlacement()
567 preferences.edit() 612 preferences.edit()
568 .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true) 613 .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex])
569 .apply() 614 .apply()
570 } 615 }
571 616
572 fun resetButtonPlacement() { 617 private fun resetAllLayouts() {
573 defaultOverlayByLayout(orientation) 618 val editor = preferences.edit()
619 overlayLayouts.forEachIndexed { i, layout ->
620 defaultOverlayByLayout(layout)
621 editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i])
622 }
623 editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
624 editor.apply()
625 }
626
627 fun resetLayoutVisibilityAndPlacement() {
628 defaultOverlayByLayout(layout)
629 val editor = preferences.edit()
630 Settings.overlayPreferences.forEachIndexed { _, pref ->
631 editor.remove(pref)
632 }
633 editor.apply()
574 refreshControls() 634 refreshControls()
575 } 635 }
576 636
@@ -604,7 +664,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
604 R.integer.SWITCH_STICK_R_X, 664 R.integer.SWITCH_STICK_R_X,
605 R.integer.SWITCH_STICK_R_Y, 665 R.integer.SWITCH_STICK_R_Y,
606 R.integer.SWITCH_STICK_L_X, 666 R.integer.SWITCH_STICK_L_X,
607 R.integer.SWITCH_STICK_L_Y 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
608 ) 672 )
609 673
610 private val portraitResources = arrayOf( 674 private val portraitResources = arrayOf(
@@ -637,7 +701,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
637 R.integer.SWITCH_STICK_R_X_PORTRAIT, 701 R.integer.SWITCH_STICK_R_X_PORTRAIT,
638 R.integer.SWITCH_STICK_R_Y_PORTRAIT, 702 R.integer.SWITCH_STICK_R_Y_PORTRAIT,
639 R.integer.SWITCH_STICK_L_X_PORTRAIT, 703 R.integer.SWITCH_STICK_L_X_PORTRAIT,
640 R.integer.SWITCH_STICK_L_Y_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
641 ) 709 )
642 710
643 private val foldableResources = arrayOf( 711 private val foldableResources = arrayOf(
@@ -670,139 +738,159 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
670 R.integer.SWITCH_STICK_R_X_FOLDABLE, 738 R.integer.SWITCH_STICK_R_X_FOLDABLE,
671 R.integer.SWITCH_STICK_R_Y_FOLDABLE, 739 R.integer.SWITCH_STICK_R_Y_FOLDABLE,
672 R.integer.SWITCH_STICK_L_X_FOLDABLE, 740 R.integer.SWITCH_STICK_L_X_FOLDABLE,
673 R.integer.SWITCH_STICK_L_Y_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
674 ) 746 )
675 747
676 private fun getResourceValue(orientation: String, position: Int): Float { 748 private fun getResourceValue(layout: String, position: Int): Float {
677 return when (orientation) { 749 return when (layout) {
678 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 750 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
679 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 751 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
680 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000 752 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
681 } 753 }
682 } 754 }
683 755
684 private fun defaultOverlayByLayout(orientation: String) { 756 private fun defaultOverlayByLayout(layout: String) {
685 // Each value represents the position of the button in relation to the screen size without insets. 757 // Each value represents the position of the button in relation to the screen size without insets.
686 preferences.edit() 758 preferences.edit()
687 .putFloat( 759 .putFloat(
688 ButtonType.BUTTON_A.toString() + "-X$orientation", 760 "${Settings.PREF_BUTTON_A}-X$layout",
689 getResourceValue(orientation, 0) 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)
690 ) 778 )
691 .putFloat( 779 .putFloat(
692 ButtonType.BUTTON_A.toString() + "-Y$orientation", 780 "${Settings.PREF_BUTTON_X}-Y$layout",
693 getResourceValue(orientation, 1) 781 getResourceValue(layout, 5)
694 ) 782 )
695 .putFloat( 783 .putFloat(
696 ButtonType.BUTTON_B.toString() + "-X$orientation", 784 "${Settings.PREF_BUTTON_Y}-X$layout",
697 getResourceValue(orientation, 2) 785 getResourceValue(layout, 6)
698 ) 786 )
699 .putFloat( 787 .putFloat(
700 ButtonType.BUTTON_B.toString() + "-Y$orientation", 788 "${Settings.PREF_BUTTON_Y}-Y$layout",
701 getResourceValue(orientation, 3) 789 getResourceValue(layout, 7)
702 ) 790 )
703 .putFloat( 791 .putFloat(
704 ButtonType.BUTTON_X.toString() + "-X$orientation", 792 "${Settings.PREF_BUTTON_ZL}-X$layout",
705 getResourceValue(orientation, 4) 793 getResourceValue(layout, 8)
706 ) 794 )
707 .putFloat( 795 .putFloat(
708 ButtonType.BUTTON_X.toString() + "-Y$orientation", 796 "${Settings.PREF_BUTTON_ZL}-Y$layout",
709 getResourceValue(orientation, 5) 797 getResourceValue(layout, 9)
710 ) 798 )
711 .putFloat( 799 .putFloat(
712 ButtonType.BUTTON_Y.toString() + "-X$orientation", 800 "${Settings.PREF_BUTTON_ZR}-X$layout",
713 getResourceValue(orientation, 6) 801 getResourceValue(layout, 10)
714 ) 802 )
715 .putFloat( 803 .putFloat(
716 ButtonType.BUTTON_Y.toString() + "-Y$orientation", 804 "${Settings.PREF_BUTTON_ZR}-Y$layout",
717 getResourceValue(orientation, 7) 805 getResourceValue(layout, 11)
718 ) 806 )
719 .putFloat( 807 .putFloat(
720 ButtonType.TRIGGER_ZL.toString() + "-X$orientation", 808 "${Settings.PREF_BUTTON_DPAD}-X$layout",
721 getResourceValue(orientation, 8) 809 getResourceValue(layout, 12)
722 ) 810 )
723 .putFloat( 811 .putFloat(
724 ButtonType.TRIGGER_ZL.toString() + "-Y$orientation", 812 "${Settings.PREF_BUTTON_DPAD}-Y$layout",
725 getResourceValue(orientation, 9) 813 getResourceValue(layout, 13)
726 ) 814 )
727 .putFloat( 815 .putFloat(
728 ButtonType.TRIGGER_ZR.toString() + "-X$orientation", 816 "${Settings.PREF_BUTTON_L}-X$layout",
729 getResourceValue(orientation, 10) 817 getResourceValue(layout, 14)
730 ) 818 )
731 .putFloat( 819 .putFloat(
732 ButtonType.TRIGGER_ZR.toString() + "-Y$orientation", 820 "${Settings.PREF_BUTTON_L}-Y$layout",
733 getResourceValue(orientation, 11) 821 getResourceValue(layout, 15)
734 ) 822 )
735 .putFloat( 823 .putFloat(
736 ButtonType.DPAD_UP.toString() + "-X$orientation", 824 "${Settings.PREF_BUTTON_R}-X$layout",
737 getResourceValue(orientation, 12) 825 getResourceValue(layout, 16)
738 ) 826 )
739 .putFloat( 827 .putFloat(
740 ButtonType.DPAD_UP.toString() + "-Y$orientation", 828 "${Settings.PREF_BUTTON_R}-Y$layout",
741 getResourceValue(orientation, 13) 829 getResourceValue(layout, 17)
742 ) 830 )
743 .putFloat( 831 .putFloat(
744 ButtonType.TRIGGER_L.toString() + "-X$orientation", 832 "${Settings.PREF_BUTTON_PLUS}-X$layout",
745 getResourceValue(orientation, 14) 833 getResourceValue(layout, 18)
746 ) 834 )
747 .putFloat( 835 .putFloat(
748 ButtonType.TRIGGER_L.toString() + "-Y$orientation", 836 "${Settings.PREF_BUTTON_PLUS}-Y$layout",
749 getResourceValue(orientation, 15) 837 getResourceValue(layout, 19)
750 ) 838 )
751 .putFloat( 839 .putFloat(
752 ButtonType.TRIGGER_R.toString() + "-X$orientation", 840 "${Settings.PREF_BUTTON_MINUS}-X$layout",
753 getResourceValue(orientation, 16) 841 getResourceValue(layout, 20)
754 ) 842 )
755 .putFloat( 843 .putFloat(
756 ButtonType.TRIGGER_R.toString() + "-Y$orientation", 844 "${Settings.PREF_BUTTON_MINUS}-Y$layout",
757 getResourceValue(orientation, 17) 845 getResourceValue(layout, 21)
758 ) 846 )
759 .putFloat( 847 .putFloat(
760 ButtonType.BUTTON_PLUS.toString() + "-X$orientation", 848 "${Settings.PREF_BUTTON_HOME}-X$layout",
761 getResourceValue(orientation, 18) 849 getResourceValue(layout, 22)
762 ) 850 )
763 .putFloat( 851 .putFloat(
764 ButtonType.BUTTON_PLUS.toString() + "-Y$orientation", 852 "${Settings.PREF_BUTTON_HOME}-Y$layout",
765 getResourceValue(orientation, 19) 853 getResourceValue(layout, 23)
766 ) 854 )
767 .putFloat( 855 .putFloat(
768 ButtonType.BUTTON_MINUS.toString() + "-X$orientation", 856 "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
769 getResourceValue(orientation, 20) 857 getResourceValue(layout, 24)
770 ) 858 )
771 .putFloat( 859 .putFloat(
772 ButtonType.BUTTON_MINUS.toString() + "-Y$orientation", 860 "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
773 getResourceValue(orientation, 21) 861 getResourceValue(layout, 25)
774 ) 862 )
775 .putFloat( 863 .putFloat(
776 ButtonType.BUTTON_HOME.toString() + "-X$orientation", 864 "${Settings.PREF_STICK_R}-X$layout",
777 getResourceValue(orientation, 22) 865 getResourceValue(layout, 26)
778 ) 866 )
779 .putFloat( 867 .putFloat(
780 ButtonType.BUTTON_HOME.toString() + "-Y$orientation", 868 "${Settings.PREF_STICK_R}-Y$layout",
781 getResourceValue(orientation, 23) 869 getResourceValue(layout, 27)
782 ) 870 )
783 .putFloat( 871 .putFloat(
784 ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation", 872 "${Settings.PREF_STICK_L}-X$layout",
785 getResourceValue(orientation, 24) 873 getResourceValue(layout, 28)
786 ) 874 )
787 .putFloat( 875 .putFloat(
788 ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation", 876 "${Settings.PREF_STICK_L}-Y$layout",
789 getResourceValue(orientation, 25) 877 getResourceValue(layout, 29)
790 ) 878 )
791 .putFloat( 879 .putFloat(
792 ButtonType.STICK_R.toString() + "-X$orientation", 880 "${Settings.PREF_BUTTON_STICK_L}-X$layout",
793 getResourceValue(orientation, 26) 881 getResourceValue(layout, 30)
794 ) 882 )
795 .putFloat( 883 .putFloat(
796 ButtonType.STICK_R.toString() + "-Y$orientation", 884 "${Settings.PREF_BUTTON_STICK_L}-Y$layout",
797 getResourceValue(orientation, 27) 885 getResourceValue(layout, 31)
798 ) 886 )
799 .putFloat( 887 .putFloat(
800 ButtonType.STICK_L.toString() + "-X$orientation", 888 "${Settings.PREF_BUTTON_STICK_R}-X$layout",
801 getResourceValue(orientation, 28) 889 getResourceValue(layout, 32)
802 ) 890 )
803 .putFloat( 891 .putFloat(
804 ButtonType.STICK_L.toString() + "-Y$orientation", 892 "${Settings.PREF_BUTTON_STICK_R}-Y$layout",
805 getResourceValue(orientation, 29) 893 getResourceValue(layout, 33)
806 ) 894 )
807 .apply() 895 .apply()
808 } 896 }
@@ -812,12 +900,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
812 } 900 }
813 901
814 companion object { 902 companion object {
903 // Increase this number every time there is a breaking change to every overlay layout
904 const val OVERLAY_VERSION = 1
905
906 // Increase the corresponding layout version number whenever that layout has a breaking change
907 private const val LANDSCAPE_OVERLAY_VERSION = 1
908 private const val PORTRAIT_OVERLAY_VERSION = 1
909 private const val FOLDABLE_OVERLAY_VERSION = 1
910 val overlayLayoutVersions = listOf(
911 LANDSCAPE_OVERLAY_VERSION,
912 PORTRAIT_OVERLAY_VERSION,
913 FOLDABLE_OVERLAY_VERSION
914 )
915
815 private val preferences: SharedPreferences = 916 private val preferences: SharedPreferences =
816 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 917 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
817 918
818 const val LANDSCAPE = "" 919 const val LANDSCAPE = "_Landscape"
819 const val PORTRAIT = "_Portrait" 920 const val PORTRAIT = "_Portrait"
820 const val FOLDABLE = "_Foldable" 921 const val FOLDABLE = "_Foldable"
922 val overlayLayouts = listOf(
923 LANDSCAPE,
924 PORTRAIT,
925 FOLDABLE
926 )
821 927
822 /** 928 /**
823 * Resizes a [Bitmap] by a given scale factor 929 * Resizes a [Bitmap] by a given scale factor
@@ -948,6 +1054,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
948 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). 1054 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
949 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). 1055 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
950 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. 1056 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
1057 * @param prefId Identifier for determining where a button appears on screen.
1058 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
951 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. 1059 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
952 */ 1060 */
953 private fun initializeOverlayButton( 1061 private fun initializeOverlayButton(
@@ -956,7 +1064,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
956 defaultResId: Int, 1064 defaultResId: Int,
957 pressedResId: Int, 1065 pressedResId: Int,
958 buttonId: Int, 1066 buttonId: Int,
959 orientation: String 1067 prefId: String,
1068 layout: String
960 ): InputOverlayDrawableButton { 1069 ): InputOverlayDrawableButton {
961 // Resources handle for fetching the initial Drawable resource. 1070 // Resources handle for fetching the initial Drawable resource.
962 val res = context.resources 1071 val res = context.resources
@@ -964,17 +1073,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
964 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. 1073 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
965 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 1074 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
966 1075
967 // Decide scale based on button ID and user preference 1076 // Decide scale based on button preference ID and user preference
968 var scale: Float = when (buttonId) { 1077 var scale: Float = when (prefId) {
969 ButtonType.BUTTON_HOME, 1078 Settings.PREF_BUTTON_HOME,
970 ButtonType.BUTTON_CAPTURE, 1079 Settings.PREF_BUTTON_SCREENSHOT,
971 ButtonType.BUTTON_PLUS, 1080 Settings.PREF_BUTTON_PLUS,
972 ButtonType.BUTTON_MINUS -> 0.07f 1081 Settings.PREF_BUTTON_MINUS -> 0.07f
973 1082
974 ButtonType.TRIGGER_L, 1083 Settings.PREF_BUTTON_L,
975 ButtonType.TRIGGER_R, 1084 Settings.PREF_BUTTON_R,
976 ButtonType.TRIGGER_ZL, 1085 Settings.PREF_BUTTON_ZL,
977 ButtonType.TRIGGER_ZR -> 0.26f 1086 Settings.PREF_BUTTON_ZR -> 0.26f
1087
1088 Settings.PREF_BUTTON_STICK_L,
1089 Settings.PREF_BUTTON_STICK_R -> 0.155f
978 1090
979 else -> 0.11f 1091 else -> 0.11f
980 } 1092 }
@@ -984,8 +1096,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
984 // Initialize the InputOverlayDrawableButton. 1096 // Initialize the InputOverlayDrawableButton.
985 val defaultStateBitmap = getBitmap(context, defaultResId, scale) 1097 val defaultStateBitmap = getBitmap(context, defaultResId, scale)
986 val pressedStateBitmap = getBitmap(context, pressedResId, scale) 1098 val pressedStateBitmap = getBitmap(context, pressedResId, scale)
987 val overlayDrawable = 1099 val overlayDrawable = InputOverlayDrawableButton(
988 InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId) 1100 res,
1101 defaultStateBitmap,
1102 pressedStateBitmap,
1103 buttonId,
1104 prefId
1105 )
989 1106
990 // Get the minimum and maximum coordinates of the screen where the button can be placed. 1107 // Get the minimum and maximum coordinates of the screen where the button can be placed.
991 val min = windowSize.first 1108 val min = windowSize.first
@@ -993,8 +1110,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
993 1110
994 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1111 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
995 // These were set in the input overlay configuration menu. 1112 // These were set in the input overlay configuration menu.
996 val xKey = "$buttonId-X$orientation" 1113 val xKey = "$prefId-X$layout"
997 val yKey = "$buttonId-Y$orientation" 1114 val yKey = "$prefId-Y$layout"
998 val drawableXPercent = sPrefs.getFloat(xKey, 0f) 1115 val drawableXPercent = sPrefs.getFloat(xKey, 0f)
999 val drawableYPercent = sPrefs.getFloat(yKey, 0f) 1116 val drawableYPercent = sPrefs.getFloat(yKey, 0f)
1000 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1117 val drawableX = (drawableXPercent * max.x + min.x).toInt()
@@ -1029,7 +1146,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1029 * @param defaultResId The [Bitmap] resource ID of the default state. 1146 * @param defaultResId The [Bitmap] resource ID of the default state.
1030 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. 1147 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction.
1031 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. 1148 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
1032 * @return the initialized [InputOverlayDrawableDpad] 1149 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
1150 * @return The initialized [InputOverlayDrawableDpad]
1033 */ 1151 */
1034 private fun initializeOverlayDpad( 1152 private fun initializeOverlayDpad(
1035 context: Context, 1153 context: Context,
@@ -1037,7 +1155,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1037 defaultResId: Int, 1155 defaultResId: Int,
1038 pressedOneDirectionResId: Int, 1156 pressedOneDirectionResId: Int,
1039 pressedTwoDirectionsResId: Int, 1157 pressedTwoDirectionsResId: Int,
1040 orientation: String 1158 layout: String
1041 ): InputOverlayDrawableDpad { 1159 ): InputOverlayDrawableDpad {
1042 // Resources handle for fetching the initial Drawable resource. 1160 // Resources handle for fetching the initial Drawable resource.
1043 val res = context.resources 1161 val res = context.resources
@@ -1074,8 +1192,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1074 1192
1075 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. 1193 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
1076 // These were set in the input overlay configuration menu. 1194 // These were set in the input overlay configuration menu.
1077 val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f) 1195 val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f)
1078 val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f) 1196 val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f)
1079 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1197 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1080 val drawableY = (drawableYPercent * max.y + min.y).toInt() 1198 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1081 val width = overlayDrawable.width 1199 val width = overlayDrawable.width
@@ -1107,7 +1225,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1107 * @param pressedResInner Resource ID for the pressed inner image of the joystick. 1225 * @param pressedResInner Resource ID for the pressed inner image of the joystick.
1108 * @param joystick Identifier for which joystick this is. 1226 * @param joystick Identifier for which joystick this is.
1109 * @param button Identifier for which joystick button this is. 1227 * @param button Identifier for which joystick button this is.
1110 * @return the initialized [InputOverlayDrawableJoystick]. 1228 * @param prefId Identifier for determining where a button appears on screen.
1229 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
1230 * @return The initialized [InputOverlayDrawableJoystick].
1111 */ 1231 */
1112 private fun initializeOverlayJoystick( 1232 private fun initializeOverlayJoystick(
1113 context: Context, 1233 context: Context,
@@ -1117,7 +1237,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1117 pressedResInner: Int, 1237 pressedResInner: Int,
1118 joystick: Int, 1238 joystick: Int,
1119 button: Int, 1239 button: Int,
1120 orientation: String 1240 prefId: String,
1241 layout: String
1121 ): InputOverlayDrawableJoystick { 1242 ): InputOverlayDrawableJoystick {
1122 // Resources handle for fetching the initial Drawable resource. 1243 // Resources handle for fetching the initial Drawable resource.
1123 val res = context.resources 1244 val res = context.resources
@@ -1141,8 +1262,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1141 1262
1142 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1263 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1143 // These were set in the input overlay configuration menu. 1264 // These were set in the input overlay configuration menu.
1144 val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f) 1265 val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f)
1145 val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f) 1266 val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f)
1146 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1267 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1147 val drawableY = (drawableYPercent * max.y + min.y).toInt() 1268 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1148 val outerScale = 1.66f 1269 val outerScale = 1.66f
@@ -1168,7 +1289,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1168 outerRect, 1289 outerRect,
1169 innerRect, 1290 innerRect,
1170 joystick, 1291 joystick,
1171 button 1292 button,
1293 prefId
1172 ) 1294 )
1173 1295
1174 // Need to set the image's position 1296 // Need to set the image's position
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 4a93e0b14..2c28dda88 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
@@ -24,7 +24,8 @@ class InputOverlayDrawableButton(
24 res: Resources, 24 res: Resources,
25 defaultStateBitmap: Bitmap, 25 defaultStateBitmap: Bitmap,
26 pressedStateBitmap: Bitmap, 26 pressedStateBitmap: Bitmap,
27 val buttonId: Int 27 val buttonId: Int,
28 val prefId: String
28) { 29) {
29 // The ID value what motion event is tracking 30 // The ID value what motion event is tracking
30 var trackId: Int 31 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 fb48f584d..518b1e783 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
@@ -37,7 +37,8 @@ class InputOverlayDrawableJoystick(
37 rectOuter: Rect, 37 rectOuter: Rect,
38 rectInner: Rect, 38 rectInner: Rect,
39 val joystickId: Int, 39 val joystickId: Int,
40 val buttonId: Int 40 val buttonId: Int,
41 val prefId: String
41) { 42) {
42 // The ID value what motion event is tracking 43 // The ID value what motion event is tracking
43 var trackId = -1 44 var trackId = -1
diff --git a/src/android/app/src/main/res/drawable/button_l3.xml b/src/android/app/src/main/res/drawable/button_l3.xml
new file mode 100644
index 000000000..0cb28836e
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/button_l3.xml
@@ -0,0 +1,128 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.5"
9 android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
10 android:strokeAlpha="0.6">
11 <aapt:attr name="android:fillColor">
12 <gradient
13 android:endX="21.568"
14 android:endY="33.938"
15 android:startX="21.568"
16 android:startY="16.14"
17 android:type="linear">
18 <item
19 android:color="#FFC3C4C5"
20 android:offset="0" />
21 <item
22 android:color="#FFC5C6C6"
23 android:offset="0.03" />
24 <item
25 android:color="#FFC7C7C7"
26 android:offset="0.19" />
27 <item
28 android:color="#DBB5B5B5"
29 android:offset="0.44" />
30 <item
31 android:color="#7F878787"
32 android:offset="1" />
33 </gradient>
34 </aapt:attr>
35 </path>
36 <path
37 android:fillAlpha="0.5"
38 android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
39 android:strokeAlpha="0.6">
40 <aapt:attr name="android:fillColor">
41 <gradient
42 android:endX="17.395"
43 android:endY="18.74"
44 android:startX="17.395"
45 android:startY="-1.296"
46 android:type="linear">
47 <item
48 android:color="#FFC3C4C5"
49 android:offset="0" />
50 <item
51 android:color="#FFC5C6C6"
52 android:offset="0.03" />
53 <item
54 android:color="#FFC7C7C7"
55 android:offset="0.19" />
56 <item
57 android:color="#DBB5B5B5"
58 android:offset="0.44" />
59 <item
60 android:color="#7F878787"
61 android:offset="1" />
62 </gradient>
63 </aapt:attr>
64 </path>
65 <path
66 android:fillAlpha="0.5"
67 android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
68 android:strokeAlpha="0.6">
69 <aapt:attr name="android:fillColor">
70 <gradient
71 android:centerX="17.477"
72 android:centerY="19.92"
73 android:gradientRadius="17.201"
74 android:type="radial">
75 <item
76 android:color="#FFC3C4C5"
77 android:offset="0.58" />
78 <item
79 android:color="#FFC6C6C6"
80 android:offset="0.84" />
81 <item
82 android:color="#FFC7C7C7"
83 android:offset="0.88" />
84 <item
85 android:color="#FFC2C2C2"
86 android:offset="0.91" />
87 <item
88 android:color="#FFB5B5B5"
89 android:offset="0.94" />
90 <item
91 android:color="#FF9E9E9E"
92 android:offset="0.98" />
93 <item
94 android:color="#FF8F8F8F"
95 android:offset="1" />
96 </gradient>
97 </aapt:attr>
98 </path>
99 <path
100 android:fillAlpha="0.5"
101 android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z"
102 android:strokeAlpha="0.6">
103 <aapt:attr name="android:fillColor">
104 <gradient
105 android:endX="16.829"
106 android:endY="46.882"
107 android:startX="16.829"
108 android:startY="20.479"
109 android:type="linear">
110 <item
111 android:color="#FFC3C4C5"
112 android:offset="0" />
113 <item
114 android:color="#FFC5C6C6"
115 android:offset="0.03" />
116 <item
117 android:color="#FFC7C7C7"
118 android:offset="0.19" />
119 <item
120 android:color="#DBB5B5B5"
121 android:offset="0.44" />
122 <item
123 android:color="#7F878787"
124 android:offset="1" />
125 </gradient>
126 </aapt:attr>
127 </path>
128</vector>
diff --git a/src/android/app/src/main/res/drawable/button_l3_depressed.xml b/src/android/app/src/main/res/drawable/button_l3_depressed.xml
new file mode 100644
index 000000000..b078dedc9
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/button_l3_depressed.xml
@@ -0,0 +1,75 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.3"
9 android:fillColor="#151515"
10 android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
11 android:strokeAlpha="0.3" />
12 <path
13 android:fillAlpha="0.6"
14 android:fillColor="#151515"
15 android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
16 android:strokeAlpha="0.6" />
17 <path
18 android:fillAlpha="0.6"
19 android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
20 android:strokeAlpha="0.6">
21 <aapt:attr name="android:fillColor">
22 <gradient
23 android:endX="21.568"
24 android:endY="33.938"
25 android:startX="21.568"
26 android:startY="16.14"
27 android:type="linear">
28 <item
29 android:color="#FFC3C4C5"
30 android:offset="0" />
31 <item
32 android:color="#FFC5C6C6"
33 android:offset="0.03" />
34 <item
35 android:color="#FFC7C7C7"
36 android:offset="0.19" />
37 <item
38 android:color="#DBB5B5B5"
39 android:offset="0.44" />
40 <item
41 android:color="#7F878787"
42 android:offset="1" />
43 </gradient>
44 </aapt:attr>
45 </path>
46 <path
47 android:fillAlpha="0.6"
48 android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z"
49 android:strokeAlpha="0.6">
50 <aapt:attr name="android:fillColor">
51 <gradient
52 android:endX="16.829"
53 android:endY="46.882"
54 android:startX="16.829"
55 android:startY="20.479"
56 android:type="linear">
57 <item
58 android:color="#FFC3C4C5"
59 android:offset="0" />
60 <item
61 android:color="#FFC5C6C6"
62 android:offset="0.03" />
63 <item
64 android:color="#FFC7C7C7"
65 android:offset="0.19" />
66 <item
67 android:color="#DBB5B5B5"
68 android:offset="0.44" />
69 <item
70 android:color="#7F878787"
71 android:offset="1" />
72 </gradient>
73 </aapt:attr>
74 </path>
75</vector>
diff --git a/src/android/app/src/main/res/drawable/button_r3.xml b/src/android/app/src/main/res/drawable/button_r3.xml
new file mode 100644
index 000000000..5c6864e26
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/button_r3.xml
@@ -0,0 +1,128 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.5"
9 android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z"
10 android:strokeAlpha="0.6">
11 <aapt:attr name="android:fillColor">
12 <gradient
13 android:endX="15.506"
14 android:endY="48.977"
15 android:startX="15.506"
16 android:startY="19.659"
17 android:type="linear">
18 <item
19 android:color="#FFC3C4C5"
20 android:offset="0" />
21 <item
22 android:color="#FFC5C6C6"
23 android:offset="0.03" />
24 <item
25 android:color="#FFC7C7C7"
26 android:offset="0.19" />
27 <item
28 android:color="#DBB5B5B5"
29 android:offset="0.44" />
30 <item
31 android:color="#7F878787"
32 android:offset="1" />
33 </gradient>
34 </aapt:attr>
35 </path>
36 <path
37 android:fillAlpha="0.5"
38 android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
39 android:strokeAlpha="0.6">
40 <aapt:attr name="android:fillColor">
41 <gradient
42 android:endX="17.395"
43 android:endY="18.74"
44 android:startX="17.395"
45 android:startY="-1.296"
46 android:type="linear">
47 <item
48 android:color="#FFC3C4C5"
49 android:offset="0" />
50 <item
51 android:color="#FFC5C6C6"
52 android:offset="0.03" />
53 <item
54 android:color="#FFC7C7C7"
55 android:offset="0.19" />
56 <item
57 android:color="#DBB5B5B5"
58 android:offset="0.44" />
59 <item
60 android:color="#7F878787"
61 android:offset="1" />
62 </gradient>
63 </aapt:attr>
64 </path>
65 <path
66 android:fillAlpha="0.5"
67 android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
68 android:strokeAlpha="0.6">
69 <aapt:attr name="android:fillColor">
70 <gradient
71 android:centerX="17.477"
72 android:centerY="19.92"
73 android:gradientRadius="17.201"
74 android:type="radial">
75 <item
76 android:color="#FFC3C4C5"
77 android:offset="0.58" />
78 <item
79 android:color="#FFC6C6C6"
80 android:offset="0.84" />
81 <item
82 android:color="#FFC7C7C7"
83 android:offset="0.88" />
84 <item
85 android:color="#FFC2C2C2"
86 android:offset="0.91" />
87 <item
88 android:color="#FFB5B5B5"
89 android:offset="0.94" />
90 <item
91 android:color="#FF9E9E9E"
92 android:offset="0.98" />
93 <item
94 android:color="#FF8F8F8F"
95 android:offset="1" />
96 </gradient>
97 </aapt:attr>
98 </path>
99 <path
100 android:fillAlpha="0.5"
101 android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
102 android:strokeAlpha="0.6">
103 <aapt:attr name="android:fillColor">
104 <gradient
105 android:endX="23.949"
106 android:endY="33.938"
107 android:startX="23.949"
108 android:startY="16.14"
109 android:type="linear">
110 <item
111 android:color="#FFC3C4C5"
112 android:offset="0" />
113 <item
114 android:color="#FFC5C6C6"
115 android:offset="0.03" />
116 <item
117 android:color="#FFC7C7C7"
118 android:offset="0.19" />
119 <item
120 android:color="#DBB5B5B5"
121 android:offset="0.44" />
122 <item
123 android:color="#7F878787"
124 android:offset="1" />
125 </gradient>
126 </aapt:attr>
127 </path>
128</vector>
diff --git a/src/android/app/src/main/res/drawable/button_r3_depressed.xml b/src/android/app/src/main/res/drawable/button_r3_depressed.xml
new file mode 100644
index 000000000..20f480179
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/button_r3_depressed.xml
@@ -0,0 +1,75 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.3"
9 android:fillColor="#151515"
10 android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
11 android:strokeAlpha="0.3" />
12 <path
13 android:fillAlpha="0.6"
14 android:fillColor="#151515"
15 android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
16 android:strokeAlpha="0.6" />
17 <path
18 android:fillAlpha="0.6"
19 android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z"
20 android:strokeAlpha="0.6">
21 <aapt:attr name="android:fillColor">
22 <gradient
23 android:endX="15.506"
24 android:endY="48.977"
25 android:startX="15.506"
26 android:startY="19.659"
27 android:type="linear">
28 <item
29 android:color="#FFC3C4C5"
30 android:offset="0" />
31 <item
32 android:color="#FFC5C6C6"
33 android:offset="0.03" />
34 <item
35 android:color="#FFC7C7C7"
36 android:offset="0.19" />
37 <item
38 android:color="#DBB5B5B5"
39 android:offset="0.44" />
40 <item
41 android:color="#7F878787"
42 android:offset="1" />
43 </gradient>
44 </aapt:attr>
45 </path>
46 <path
47 android:fillAlpha="0.6"
48 android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
49 android:strokeAlpha="0.6">
50 <aapt:attr name="android:fillColor">
51 <gradient
52 android:endX="23.949"
53 android:endY="33.938"
54 android:startX="23.949"
55 android:startY="16.14"
56 android:type="linear">
57 <item
58 android:color="#FFC3C4C5"
59 android:offset="0" />
60 <item
61 android:color="#FFC5C6C6"
62 android:offset="0.03" />
63 <item
64 android:color="#FFC7C7C7"
65 android:offset="0.19" />
66 <item
67 android:color="#DBB5B5B5"
68 android:offset="0.44" />
69 <item
70 android:color="#7F878787"
71 android:offset="1" />
72 </gradient>
73 </aapt:attr>
74 </path>
75</vector>
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 6d092f7a9..200b99185 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -205,6 +205,8 @@
205 <item>@string/gamepad_d_pad</item> 205 <item>@string/gamepad_d_pad</item>
206 <item>@string/gamepad_left_stick</item> 206 <item>@string/gamepad_left_stick</item>
207 <item>@string/gamepad_right_stick</item> 207 <item>@string/gamepad_right_stick</item>
208 <item>L3</item>
209 <item>R3</item>
208 <item>@string/gamepad_home</item> 210 <item>@string/gamepad_home</item>
209 <item>@string/gamepad_screenshot</item> 211 <item>@string/gamepad_screenshot</item>
210 </string-array> 212 </string-array>
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index 2e93b408c..5e39bc7d9 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -33,6 +33,10 @@
33 <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> 33 <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer> 34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer>
35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> 35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
36 <integer name="SWITCH_BUTTON_STICK_L_X">870</integer>
37 <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer>
38 <integer name="SWITCH_BUTTON_STICK_R_X">960</integer>
39 <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer>
36 40
37 <!-- Default SWITCH portrait layout --> 41 <!-- Default SWITCH portrait layout -->
38 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> 42 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
@@ -65,6 +69,10 @@
65 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> 69 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
66 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> 70 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
67 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> 71 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
72 <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer>
73 <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer>
74 <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer>
75 <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer>
68 76
69 <!-- Default SWITCH foldable layout --> 77 <!-- Default SWITCH foldable layout -->
70 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> 78 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
@@ -97,5 +105,9 @@
97 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> 105 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
98 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> 106 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
99 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> 107 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
108 <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer>
109 <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer>
110 <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer>
111 <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer>
100 112
101</resources> 113</resources>
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 79f158db4..3a859139c 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -598,6 +598,10 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
598 [&](ImageId id, Image&) { deleted_images.push_back(id); }); 598 [&](ImageId id, Image&) { deleted_images.push_back(id); });
599 for (const ImageId id : deleted_images) { 599 for (const ImageId id : deleted_images) {
600 Image& image = slot_images[id]; 600 Image& image = slot_images[id];
601 if (True(image.flags & ImageFlagBits::CpuModified)) {
602 continue;
603 }
604 image.flags |= ImageFlagBits::CpuModified;
601 if (True(image.flags & ImageFlagBits::Remapped)) { 605 if (True(image.flags & ImageFlagBits::Remapped)) {
602 continue; 606 continue;
603 } 607 }
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 421e71e5a..e04852e01 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -485,7 +485,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
485 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 485 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
486 } 486 }
487 } 487 }
488 if (extensions.extended_dynamic_state2 && (is_radv || is_qualcomm)) { 488 if (extensions.extended_dynamic_state2 && is_radv) {
489 const u32 version = (properties.properties.driverVersion << 3) >> 3; 489 const u32 version = (properties.properties.driverVersion << 3) >> 3;
490 if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) { 490 if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
491 LOG_WARNING( 491 LOG_WARNING(
@@ -498,6 +498,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
498 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); 498 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
499 } 499 }
500 } 500 }
501 if (extensions.extended_dynamic_state2 && is_qualcomm) {
502 const u32 version = (properties.properties.driverVersion << 3) >> 3;
503 if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
504 version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
505 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
506 LOG_WARNING(Render_Vulkan,
507 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
508 features.extended_dynamic_state2.extendedDynamicState2 = false;
509 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
510 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
511 extensions.extended_dynamic_state2 = false;
512 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
513 }
514 }
501 if (extensions.extended_dynamic_state3 && is_radv) { 515 if (extensions.extended_dynamic_state3 && is_radv) {
502 LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation"); 516 LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
503 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; 517 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
@@ -512,8 +526,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
512 dynamic_state3_enables = false; 526 dynamic_state3_enables = false;
513 } 527 }
514 } 528 }
515 if (extensions.vertex_input_dynamic_state && (is_radv || is_qualcomm)) { 529 if (extensions.vertex_input_dynamic_state && is_radv) {
516 // Qualcomm S8gen2 drivers do not properly support vertex_input_dynamic_state.
517 // TODO(ameerj): Blacklist only offending driver versions 530 // TODO(ameerj): Blacklist only offending driver versions
518 // TODO(ameerj): Confirm if RDNA1 is affected 531 // TODO(ameerj): Confirm if RDNA1 is affected
519 const bool is_rdna2 = 532 const bool is_rdna2 =
@@ -526,6 +539,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
526 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 539 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
527 } 540 }
528 } 541 }
542 if (extensions.vertex_input_dynamic_state && is_qualcomm) {
543 const u32 version = (properties.properties.driverVersion << 3) >> 3;
544 if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
545 version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
546 // Qualcomm Adreno 7xx drivers do not properly support vertex_input_dynamic_state.
547 LOG_WARNING(
548 Render_Vulkan,
549 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
550 features.vertex_input_dynamic_state.vertexInputDynamicState = false;
551 extensions.vertex_input_dynamic_state = false;
552 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
553 }
554 }
529 555
530 sets_per_pool = 64; 556 sets_per_pool = 64;
531 if (extensions.extended_dynamic_state3 && is_amd_driver && 557 if (extensions.extended_dynamic_state3 && is_amd_driver &&
@@ -774,6 +800,17 @@ bool Device::ShouldBoostClocks() const {
774 return validated_driver && !is_steam_deck && !is_debugging; 800 return validated_driver && !is_steam_deck && !is_debugging;
775} 801}
776 802
803bool Device::HasTimelineSemaphore() const {
804 if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
805 GetDriverID() == VK_DRIVER_ID_MESA_TURNIP) {
806 // Timeline semaphores do not work properly on all Qualcomm drivers.
807 // They generally work properly with Turnip drivers, but are problematic on some devices
808 // (e.g. ZTE handsets with Snapdragon 870).
809 return false;
810 }
811 return features.timeline_semaphore.timelineSemaphore;
812}
813
777bool Device::GetSuitability(bool requires_swapchain) { 814bool Device::GetSuitability(bool requires_swapchain) {
778 // Assume we will be suitable. 815 // Assume we will be suitable.
779 bool suitable = true; 816 bool suitable = true;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 3ace1fb03..be3ed45ff 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -528,13 +528,7 @@ public:
528 return extensions.shader_atomic_int64; 528 return extensions.shader_atomic_int64;
529 } 529 }
530 530
531 bool HasTimelineSemaphore() const { 531 bool HasTimelineSemaphore() const;
532 if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
533 // Timeline semaphores do not work properly on all Qualcomm drivers.
534 return false;
535 }
536 return features.timeline_semaphore.timelineSemaphore;
537 }
538 532
539 /// Returns the minimum supported version of SPIR-V. 533 /// Returns the minimum supported version of SPIR-V.
540 u32 SupportedSpirvVersion() const { 534 u32 SupportedSpirvVersion() const {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fea5eb614..20532416c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -454,7 +454,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
454 // the user through their desktop environment. 454 // the user through their desktop environment.
455 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the 455 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
456 //: computer from sleeping 456 //: computer from sleeping
457 QByteArray wakelock_reason = tr("Running a game").toLatin1(); 457 QByteArray wakelock_reason = tr("Running a game").toUtf8();
458 SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data()); 458 SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data());
459 459
460 // SDL disables the screen saver by default, and setting the hint 460 // SDL disables the screen saver by default, and setting the hint