diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a9e15cfbd..fdf8900775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,8 +159,6 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WIFI_SCAN "Enable WiFi scanning" OFF) option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ${EXT_DEFAULT}) -option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan Utility Headers from externals" ${EXT_DEFAULT}) -option(YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS "Use SPIRV-Tools from externals" ${EXT_DEFAULT}) option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) @@ -172,8 +170,6 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) CMAKE_DEPENDENT_OPTION(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF "ENABLE_QT" OFF) -option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF) - option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}") option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ${EXT_DEFAULT}) @@ -455,6 +451,28 @@ if (YUZU_USE_CPM) ) endif() endif() + + # VulkanUtilityHeaders - pulls in headers and utility libs + AddJsonPackage(vulkan-utility-headers) + + # small hack + if (NOT VulkanUtilityLibraries_ADDED) + find_package(VulkanHeaders 1.3.274 REQUIRED) + endif() + + # SPIRV Headers + AddJsonPackage(spirv-headers) + + # SPIRV Tools + AddJsonPackage(spirv-tools) + + if (SPIRV-Tools_ADDED) + add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools-static) + target_link_libraries(SPIRV-Tools-static PRIVATE SPIRV-Tools-opt SPIRV-Tools-link) + endif() + + # mbedtls + AddJsonPackage(mbedtls) else() # Enforce the search mode of non-required packages for better and shorter failure messages find_package(fmt 8 REQUIRED) @@ -467,7 +485,13 @@ else() find_package(Opus 1.3 MODULE REQUIRED) find_package(ZLIB 1.2 REQUIRED) find_package(zstd 1.5 REQUIRED MODULE) - find_package(Boost 1.79.0 REQUIRED headers context system fiber) + find_package(Boost 1.57.0 REQUIRED context system fiber) + find_package(MbedTLS 3) + + find_package(VulkanUtilityLibraries REQUIRED) + find_package(VulkanHeaders 1.3.274 REQUIRED) + find_package(SPIRV-Tools MODULE REQUIRED) + find_package(SPIRV-Headers 1.3.274 REQUIRED) if (YUZU_TESTS) find_package(Catch2 3.0.1 REQUIRED) @@ -594,10 +618,8 @@ endfunction() add_subdirectory(externals) # pass targets from externals -find_package(VulkanUtilityLibraries) find_package(libusb) find_package(VulkanMemoryAllocator) -find_package(SPIRV-Tools) if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) find_package(xbyak) diff --git a/CMakeModules/Findmbedtls.cmake b/CMakeModules/Findmbedtls.cmake deleted file mode 100644 index f5ebf1abdc..0000000000 --- a/CMakeModules/Findmbedtls.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Eden Emulator Project -# SPDX-License-Identifier: GPL-3.0-or-later - -include(FindPackageHandleStandardArgs) - -find_package(PkgConfig QUIET) -pkg_search_module(mbedtls QUIET IMPORTED_TARGET mbedtls) -find_package_handle_standard_args(mbedtls - REQUIRED_VARS mbedtls_LINK_LIBRARIES - VERSION_VAR mbedtls_VERSION -) - -pkg_search_module(mbedcrypto QUIET IMPORTED_TARGET mbedcrypto) -find_package_handle_standard_args(mbedcrypto - REQUIRED_VARS mbedcrypto_LINK_LIBRARIES - VERSION_VAR mbedcrypto_VERSION -) diff --git a/cpmfile.json b/cpmfile.json index e071e0a8b8..887e958533 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -91,6 +91,42 @@ "OPUS_PRESUME_NEON ON" ] }, + "vulkan-utility-headers": { + "package": "VulkanUtilityLibraries", + "repo": "scripts/VulkanUtilityHeaders", + "tag": "1.4.326", + "artifact": "VulkanUtilityHeaders.tar.zst", + "git_host": "git.crueter.xyz", + "hash": "5924629755cb1605c4aa4eee20ef7957a9dd8d61e4df548be656d98054f2730c4109693c1bd35811f401f4705d2ccff9fc849be32b0d8480bc3f73541a5e0964" + }, + "spirv-tools": { + "package": "SPIRV-Tools", + "repo": "KhronosGroup/SPIRV-Tools", + "sha": "40eb301f32", + "hash": "58d0fb1047d69373cf24c73e6f78c73a72a6cca3b4df1d9f083b9dcc0962745ef154abf3dbe9b3623b835be20c6ec769431cf11733349f45e7568b3525f707aa", + "find_args": "MODULE", + "options": [ + "SPIRV_SKIP_EXECUTABLES ON" + ] + }, + "spirv-headers": { + "package": "SPIRV-Headers", + "repo": "KhronosGroup/SPIRV-Headers", + "sha": "4e209d3d7e", + "hash": "f48bbe18341ed55ea0fe280dbbbc0a44bf222278de6e716e143ca1e95ca320b06d4d23d6583fbf8d03e1428f3dac8fa00e5b82ddcd6b425e6236d85af09550a4", + "options": [ + "SPIRV_WERROR OFF" + ] + }, + "mbedtls": { + "package": "MbedTLS", + "repo": "Mbed-TLS/mbedtls", + "tag": "mbedtls-%VERSION%", + "hash": "6671fb8fcaa832e5b115dfdce8f78baa6a4aea71f5c89a640583634cdee27aefe3bf4be075744da91f7c3ae5ea4e0c765c8fc3937b5cfd9ea73d87ef496524da", + "version": "3", + "git_version": "3.6.4", + "artifact": "%TAG%.tar.bz2" + }, "cubeb": { "repo": "mozilla/cubeb", "sha": "fa02160712", diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9f89cfc1f5..aba5451b6d 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -54,36 +54,27 @@ endif() # Glad add_subdirectory(glad) -# mbedtls -AddJsonPackage(mbedtls) - -if (mbedtls_ADDED) - target_include_directories(mbedtls PUBLIC ${mbedtls_SOURCE_DIR}/include) - - if (NOT MSVC) - target_compile_options(mbedcrypto PRIVATE - -Wno-unused-but-set-variable - -Wno-string-concatenation - ) - elseif(CXX_CLANG) - foreach(TARGET mbedtls mbedcrypto mbedx509) - target_compile_options(${TARGET} PRIVATE - -w - ) - endforeach() - endif() -endif() - # libusb if (ENABLE_LIBUSB) add_subdirectory(libusb) endif() -# Sirit -# TODO(crueter): spirv-tools doesn't work w/ system -set(SPIRV_WERROR OFF) -AddJsonPackage(spirv-headers) +# VMA +AddJsonPackage(vulkan-memory-allocator) +if (VulkanMemoryAllocator_ADDED) + if (CXX_CLANG) + target_compile_options(VulkanMemoryAllocator INTERFACE + -Wno-unused-variable + ) + elseif(MSVC) + target_compile_options(VulkanMemoryAllocator INTERFACE + /wd4189 + ) + endif() +endif() + +# Sirit AddJsonPackage(sirit) if(MSVC AND USE_CCACHE AND sirit_ADDED) @@ -117,46 +108,9 @@ if (YUZU_USE_BUNDLED_FFMPEG) set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE) endif() -# VulkanUtilityHeaders - pulls in headers and utility libs -AddJsonPackage( - NAME vulkan-utility-headers - BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES} -) - -# small hack -if (NOT VulkanUtilityLibraries_ADDED) - find_package(VulkanHeaders 1.3.274 REQUIRED) -endif() - -# SPIRV Tools -AddJsonPackage( - NAME spirv-tools - BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS} -) - -if (SPIRV-Tools_ADDED) - add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools-static) - target_link_libraries(SPIRV-Tools-static PRIVATE SPIRV-Tools-opt SPIRV-Tools-link) -endif() - # TZDB (Time Zone Database) add_subdirectory(nx_tzdb) -# VMA -AddJsonPackage(vulkan-memory-allocator) - -if (VulkanMemoryAllocator_ADDED) - if (CXX_CLANG) - target_compile_options(VulkanMemoryAllocator INTERFACE - -Wno-unused-variable - ) - elseif(MSVC) - target_compile_options(VulkanMemoryAllocator INTERFACE - /wd4189 - ) - endif() -endif() - if (NOT TARGET LLVM::Demangle) add_library(demangle demangle/ItaniumDemangle.cpp) target_include_directories(demangle PUBLIC ./demangle) diff --git a/externals/cpmfile.json b/externals/cpmfile.json index 65f50ffdfc..283da76743 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -1,15 +1,10 @@ { - "mbedtls": { - "repo": "eden-emulator/mbedtls", - "sha": "ce4f81f4a9", - "hash": "f2e7f887651b28745e508149214d409fd7cfdb92cb94b4146b47ff1e0fc09e47143f203ac18e34c2c1814b5bd031d04c74828676c0d4342920a2ddb7fd35e9a5", - "find_args": "MODULE" - }, - "spirv-headers": { - "package": "SPIRV-Headers", - "repo": "KhronosGroup/SPIRV-Headers", - "sha": "4e209d3d7e", - "hash": "f48bbe18341ed55ea0fe280dbbbc0a44bf222278de6e716e143ca1e95ca320b06d4d23d6583fbf8d03e1428f3dac8fa00e5b82ddcd6b425e6236d85af09550a4" + "vulkan-memory-allocator": { + "package": "VulkanMemoryAllocator", + "repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator", + "sha": "1076b348ab", + "hash": "a46b44e4286d08cffda058e856c47f44c7fed3da55fe9555976eb3907fdcc20ead0b1860b0c38319cda01dbf9b1aa5d4b4038c7f1f8fbd97283d837fa9af9772", + "find_args": "CONFIG" }, "sirit": { "repo": "eden-emulator/sirit", @@ -35,31 +30,6 @@ "CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF" ] }, - "vulkan-utility-headers": { - "package": "VulkanUtilityLibraries", - "repo": "scripts/VulkanUtilityHeaders", - "tag": "1.4.326", - "artifact": "VulkanUtilityHeaders.tar.zst", - "git_host": "git.crueter.xyz", - "hash": "5924629755cb1605c4aa4eee20ef7957a9dd8d61e4df548be656d98054f2730c4109693c1bd35811f401f4705d2ccff9fc849be32b0d8480bc3f73541a5e0964" - }, - "vulkan-memory-allocator": { - "package": "VulkanMemoryAllocator", - "repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator", - "sha": "1076b348ab", - "hash": "a46b44e4286d08cffda058e856c47f44c7fed3da55fe9555976eb3907fdcc20ead0b1860b0c38319cda01dbf9b1aa5d4b4038c7f1f8fbd97283d837fa9af9772", - "find_args": "CONFIG" - }, - "spirv-tools": { - "package": "SPIRV-Tools", - "repo": "KhronosGroup/SPIRV-Tools", - "sha": "40eb301f32", - "hash": "58d0fb1047d69373cf24c73e6f78c73a72a6cca3b4df1d9f083b9dcc0962745ef154abf3dbe9b3623b835be20c6ec769431cf11733349f45e7568b3525f707aa", - "find_args": "MODULE", - "options": [ - "SPIRV_SKIP_EXECUTABLES ON" - ] - }, "xbyak_sun": { "package": "xbyak", "repo": "herumi/xbyak", diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt index 0a20ca94b8..cfa9a02a2a 100644 --- a/externals/libusb/CMakeLists.txt +++ b/externals/libusb/CMakeLists.txt @@ -3,7 +3,17 @@ include(CPMUtil) -AddJsonPackage(libusb) +if (PLATFORM_SUN OR PLATFORM_OPENBSD OR PLATFORM_FREEBSD) + set(libusb_bundled ON) +else() + set(libusb_bundled OFF) +endif() + +# TODO(crueter): Fix on *BSD/Solaris +AddJsonPackage( + NAME libusb + BUNDLED_PACKAGE ${libusb_bundled} +) if (NOT libusb_ADDED) return() 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 e01dc754eb..a85932def5 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 @@ -66,6 +66,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { var isActivityRecreated = false private lateinit var nfcReader: NfcReader + private var touchDownTime: Long = 0 + private val maxTapDuration = 500L + private val gyro = FloatArray(3) private val accel = FloatArray(3) private var motionTimestamp: Long = 0 @@ -476,6 +479,38 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } } + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as? NavHostFragment + val emulationFragment = navHostFragment?.childFragmentManager?.fragments?.firstOrNull() as? org.yuzu.yuzu_emu.fragments.EmulationFragment + + emulationFragment?.let { fragment -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + touchDownTime = System.currentTimeMillis() + // show overlay immediately on touch and cancel timer + if (!emulationViewModel.drawerOpen.value) { + fragment.handler.removeCallbacksAndMessages(null) + fragment.showOverlay() + } + } + MotionEvent.ACTION_UP -> { + if (!emulationViewModel.drawerOpen.value) { + val touchDuration = System.currentTimeMillis() - touchDownTime + + if (touchDuration <= maxTapDuration) { + fragment.handleScreenTap(false) + } else { + // just start the auto-hide timer without toggling visibility + fragment.handleScreenTap(true) + } + } + } + } + } + + return super.dispatchTouchEvent(event) + } + fun onEmulationStarted() { emulationViewModel.setEmulationStarted(true) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 98f342c274..0a2cb41745 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -36,17 +36,18 @@ import androidx.core.net.toUri import androidx.core.content.edit import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.databinding.CardGameGridCompactBinding import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting import org.yuzu.yuzu_emu.features.settings.model.Settings -import org.yuzu.yuzu_emu.utils.NativeConfig class GameAdapter(private val activity: AppCompatActivity) : AbstractDiffAdapter(exact = false) { companion object { const val VIEW_TYPE_GRID = 0 - const val VIEW_TYPE_LIST = 1 - const val VIEW_TYPE_CAROUSEL = 2 + const val VIEW_TYPE_GRID_COMPACT = 1 + const val VIEW_TYPE_LIST = 2 + const val VIEW_TYPE_CAROUSEL = 3 } private var viewType = 0 @@ -80,6 +81,7 @@ class GameAdapter(private val activity: AppCompatActivity) : listBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT listBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT } + VIEW_TYPE_GRID -> { val gridBinding = holder.binding as CardGameGridBinding gridBinding.cardGameGrid.scaleX = 1f @@ -89,6 +91,17 @@ class GameAdapter(private val activity: AppCompatActivity) : gridBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT gridBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT } + + VIEW_TYPE_GRID_COMPACT -> { + val gridCompactBinding = holder.binding as CardGameGridCompactBinding + gridCompactBinding.cardGameGridCompact.scaleX = 1f + gridCompactBinding.cardGameGridCompact.scaleY = 1f + gridCompactBinding.cardGameGridCompact.alpha = 1f + // Reset layout params to XML defaults (same as normal grid) + gridCompactBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT + gridCompactBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT + } + VIEW_TYPE_CAROUSEL -> { val carouselBinding = holder.binding as CardGameCarouselBinding // soothens transient flickering @@ -105,16 +118,25 @@ class GameAdapter(private val activity: AppCompatActivity) : parent, false ) + VIEW_TYPE_GRID -> CardGameGridBinding.inflate( LayoutInflater.from(parent.context), parent, false ) + + VIEW_TYPE_GRID_COMPACT -> CardGameGridCompactBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + VIEW_TYPE_CAROUSEL -> CardGameCarouselBinding.inflate( LayoutInflater.from(parent.context), parent, false ) + else -> throw IllegalArgumentException("Invalid view type") } return GameViewHolder(binding, viewType) @@ -130,6 +152,7 @@ class GameAdapter(private val activity: AppCompatActivity) : VIEW_TYPE_LIST -> bindListView(model) VIEW_TYPE_GRID -> bindGridView(model) VIEW_TYPE_CAROUSEL -> bindCarouselView(model) + VIEW_TYPE_GRID_COMPACT -> bindGridCompactView(model) } } @@ -168,6 +191,23 @@ class GameAdapter(private val activity: AppCompatActivity) : gridBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT } + private fun bindGridCompactView(model: Game) { + val gridCompactBinding = binding as CardGameGridCompactBinding + + gridCompactBinding.imageGameScreenCompact.scaleType = ImageView.ScaleType.CENTER_CROP + GameIconUtils.loadGameIcon(model, gridCompactBinding.imageGameScreenCompact) + + gridCompactBinding.textGameTitleCompact.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ") + + gridCompactBinding.textGameTitleCompact.marquee() + gridCompactBinding.cardGameGridCompact.setOnClickListener { onClick(model) } + gridCompactBinding.cardGameGridCompact.setOnLongClickListener { onLongClick(model) } + + // Reset layout params to XML defaults (same as normal grid) + gridCompactBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT + gridCompactBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT + } + private fun bindCarouselView(model: Game) { val carouselBinding = binding as CardGameCarouselBinding 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 21aad8b5d1..d5556a337b 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 @@ -59,7 +59,8 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { OFFLINE_WEB_APPLET("offline_web_applet_mode"), LOGIN_SHARE_APPLET("login_share_applet_mode"), WIFI_WEB_AUTH_APPLET("wifi_web_auth_applet_mode"), - MY_PAGE_APPLET("my_page_applet_mode") + MY_PAGE_APPLET("my_page_applet_mode"), + INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide") ; override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) 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 2564849ef4..480b3dc11d 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 @@ -12,6 +12,7 @@ object Settings { SECTION_SYSTEM(R.string.preferences_system), SECTION_RENDERER(R.string.preferences_graphics), SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options), + SECTION_INPUT_OVERLAY(R.string.input_overlay_options), SECTION_SOC_OVERLAY(R.string.soc_overlay_options), SECTION_AUDIO(R.string.preferences_audio), SECTION_INPUT(R.string.preferences_controls), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 883d8efaef..ec3ce15ad7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -392,6 +392,15 @@ abstract class SettingsItem( warningMessage = R.string.warning_resolution ) ) + put( + SingleChoiceSetting( + IntSetting.INPUT_OVERLAY_AUTO_HIDE, + titleId = R.string.overlay_auto_hide, + descriptionId = R.string.overlay_auto_hide_description, + choicesId = R.array.overlayAutoHideEntries, + valuesId = R.array.overlayAutoHideValues, + ) + ) put( SwitchSetting( 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 630bcb0d74..d437cede77 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 @@ -97,6 +97,7 @@ class SettingsFragmentPresenter( MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl) MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl) + MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl) MenuTag.SECTION_AUDIO -> addAudioSettings(sl) MenuTag.SECTION_INPUT -> addInputSettings(sl) MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0) @@ -156,6 +157,14 @@ class SettingsFragmentPresenter( menuKey = MenuTag.SECTION_SOC_OVERLAY ) ) + add( + SubmenuSetting( + titleId = R.string.input_overlay_options, + iconId = R.drawable.ic_controller, + descriptionId = R.string.input_overlay_options_description, + menuKey = MenuTag.SECTION_INPUT_OVERLAY + ) + ) } add( SubmenuSetting( @@ -265,6 +274,12 @@ class SettingsFragmentPresenter( } } + private fun addInputOverlaySettings(sl: ArrayList) { + sl.apply { + add(IntSetting.INPUT_OVERLAY_AUTO_HIDE.key) + } + } + private fun addSocOverlaySettings(sl: ArrayList) { sl.apply { add(HeaderSetting(R.string.stats_overlay_customization)) 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 5cc912fbbe..041124b12a 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 @@ -96,6 +96,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private var perfStatsUpdater: (() -> Unit)? = null private var socUpdater: (() -> Unit)? = null + val handler = Handler(Looper.getMainLooper()) + private var isOverlayVisible = true + private var _binding: FragmentEmulationBinding? = null private val binding get() = _binding!! @@ -452,7 +455,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { /** * Ask user if they want to launch with default settings when custom settings fail */ - private suspend fun askUserToLaunchWithDefaultSettings(gameTitle: String, errorMessage: String): Boolean { + private suspend fun askUserToLaunchWithDefaultSettings( + gameTitle: String, + errorMessage: String + ): Boolean { return suspendCoroutine { continuation -> requireActivity().runOnUiThread { MaterialAlertDialogBuilder(requireContext()) @@ -728,6 +734,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { updateShowStatsOverlay() updateSocOverlay() + initializeOverlayAutoHide() + // Re update binding when the specs values get initialized properly binding.inGameMenu.getHeaderView(0).apply { val titleView = findViewById(R.id.text_game_title) @@ -910,6 +918,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val position = IntSetting.PERF_OVERLAY_POSITION.getInt() updateStatsPosition(position) + // if the overlay auto-hide setting is changed while paused, + // we need to reinitialize the auto-hide timer + initializeOverlayAutoHide() + val socPosition = IntSetting.SOC_OVERLAY_POSITION.getInt() updateSocPosition(socPosition) @@ -1033,7 +1045,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val status = batteryIntent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) val isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || - status == BatteryManager.BATTERY_STATUS_FULL + status == BatteryManager.BATTERY_STATUS_FULL if (isCharging) { sb.append(" ${getString(R.string.charging)}") @@ -1722,4 +1734,60 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) private val socUpdateHandler = Handler(Looper.myLooper()!!) } + + private fun startOverlayAutoHideTimer(seconds: Int) { + handler.removeCallbacksAndMessages(null) + + handler.postDelayed({ + if (isOverlayVisible) { + hideOverlay() + } + }, seconds * 1000L) + } + + + fun handleScreenTap(isLongTap: Boolean) { + val autoHideSeconds = IntSetting.INPUT_OVERLAY_AUTO_HIDE.getInt() + + if (!BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) { + return + } + + if (autoHideSeconds == 0) { + showOverlay() + return + } + + // Show overlay for quick taps when it's hidden + if (!isOverlayVisible && !isLongTap) { + showOverlay() + } + + startOverlayAutoHideTimer(autoHideSeconds) + } + + private fun initializeOverlayAutoHide() { + val autoHideSeconds = IntSetting.INPUT_OVERLAY_AUTO_HIDE.getInt() + if (autoHideSeconds > 0) { + handler.postDelayed({ + // since the timer starts only after touch input, we need to always force hide it + hideOverlay() + }, autoHideSeconds * 1000L) + } + } + + + fun showOverlay() { + if (!isOverlayVisible) { + isOverlayVisible = true + ViewUtils.showView(binding.surfaceInputOverlay, 500) + } + } + + private fun hideOverlay() { + if (isOverlayVisible) { + isOverlayVisible = false + ViewUtils.hideView(binding.surfaceInputOverlay, 500) + } + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 03fa1a3a2e..80055628e1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -194,6 +194,10 @@ class GamesFragment : Fragment() { val columns = resources.getInteger(R.integer.game_columns_grid) GridLayoutManager(context, columns) } + GameAdapter.VIEW_TYPE_GRID_COMPACT -> { + val columns = resources.getInteger(R.integer.game_columns_grid) + GridLayoutManager(context, columns) + } GameAdapter.VIEW_TYPE_LIST -> { val columns = resources.getInteger(R.integer.game_columns_list) GridLayoutManager(context, columns) @@ -300,6 +304,7 @@ class GamesFragment : Fragment() { val currentViewType = getCurrentViewType() when (currentViewType) { GameAdapter.VIEW_TYPE_LIST -> popup.menu.findItem(R.id.view_list).isChecked = true + GameAdapter.VIEW_TYPE_GRID_COMPACT -> popup.menu.findItem(R.id.view_grid_compact).isChecked = true GameAdapter.VIEW_TYPE_GRID -> popup.menu.findItem(R.id.view_grid).isChecked = true GameAdapter.VIEW_TYPE_CAROUSEL -> popup.menu.findItem(R.id.view_carousel).isChecked = true } @@ -314,6 +319,14 @@ class GamesFragment : Fragment() { true } + R.id.view_grid_compact -> { + if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) onPause() + setCurrentViewType(GameAdapter.VIEW_TYPE_GRID_COMPACT) + applyGridGamesBinding() + item.isChecked = true + true + } + R.id.view_list -> { if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) onPause() setCurrentViewType(GameAdapter.VIEW_TYPE_LIST) diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index cd18f1e5b3..570c9fc7c7 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -79,6 +79,10 @@ namespace AndroidSettings { Settings::Category::Overlay, Settings::Specialization::Paired, true, true}; + + Settings::Setting perf_overlay_border{linkage, 0, "input_overlay_auto_hide", + Settings::Category::Overlay, + Settings::Specialization::Default, true, true,}; Settings::Setting perf_overlay_background{linkage, false, "perf_overlay_background", Settings::Category::Overlay, Settings::Specialization::Default, true, diff --git a/src/android/app/src/main/res/drawable/gradient_overlay_bottom.xml b/src/android/app/src/main/res/drawable/gradient_overlay_bottom.xml new file mode 100644 index 0000000000..f74cfa0d05 --- /dev/null +++ b/src/android/app/src/main/res/drawable/gradient_overlay_bottom.xml @@ -0,0 +1,9 @@ + + + + diff --git a/src/android/app/src/main/res/layout/card_game_grid.xml b/src/android/app/src/main/res/layout/card_game_grid.xml index 3e0bf6005c..f03a77c4f1 100644 --- a/src/android/app/src/main/res/layout/card_game_grid.xml +++ b/src/android/app/src/main/res/layout/card_game_grid.xml @@ -5,27 +5,32 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="false" - android:focusableInTouchMode="false"> + android:focusableInTouchMode="false" + android:padding="4dp"> + app:cardCornerRadius="16dp" + android:foreground="@color/eden_border_gradient_start"> + android:paddingTop="14dp" + android:paddingLeft="6dp" + android:paddingRight="6dp" + android:paddingBottom="6dp"> + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/menu/menu_game_views.xml b/src/android/app/src/main/res/menu/menu_game_views.xml index 241cdcb4b5..11bf3e695e 100644 --- a/src/android/app/src/main/res/menu/menu_game_views.xml +++ b/src/android/app/src/main/res/menu/menu_game_views.xml @@ -4,6 +4,9 @@ + 1 + + @string/overlay_auto_hide_never + @string/overlay_auto_hide_instant + @string/overlay_auto_hide_5s + @string/overlay_auto_hide_10s + @string/overlay_auto_hide_15s + @string/overlay_auto_hide_20s + @string/overlay_auto_hide_25s + @string/overlay_auto_hide_30s + @string/overlay_auto_hide_35s + @string/overlay_auto_hide_40s + @string/overlay_auto_hide_45s + @string/overlay_auto_hide_50s + @string/overlay_auto_hide_55s + @string/overlay_auto_hide_60s + + + + 0 + 1 + 5 + 10 + 15 + 20 + 25 + 30 + 35 + 40 + 45 + 50 + 55 + 60 + + @string/disabled 1 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 2c7923d5a3..5c9bc7e2e4 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -9,6 +9,29 @@ Shows notifications when something goes wrong. Notification permission not granted! + + Overlay Auto Hide + Automatically hide the touch controls overlay after the specified time of inactivity. Select "Never" to keep the overlay always visible. + + Never + Instantly + 5 seconds + 10 seconds + 15 seconds + 20 seconds + 25 seconds + 30 seconds + 35 seconds + 40 seconds + 45 seconds + 50 seconds + 55 seconds + 1 minute + + Input Overlay + Configure on-screen controls + + (Enhanced) Process RAM: %1$d MB @@ -266,6 +289,7 @@ Alphabetical List Grid + Compact Grid Carousel Screenshot for %1$s Folder diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1e8e4ec07a..89c97eb1aa 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1200,7 +1200,7 @@ else() target_link_libraries(core PUBLIC Boost::headers) endif() -target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API mbedtls mbedcrypto) +target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto MbedTLS::mbedtls) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) endif() diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index c2fd587a73..0a2a4e464c 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h @@ -16,7 +16,7 @@ struct CipherContext; enum class Mode { CTR = 11, ECB = 2, - XTS = 70, + XTS = 74, }; enum class Op { diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 04b75d5e8f..6d0b32ba70 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -539,7 +539,7 @@ static std::array MGF1(const std::array& seed) { while (out.size() < target_size) { out.resize(out.size() + 0x20); seed_exp[in_size + 3] = static_cast(i); - mbedtls_sha256_ret(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); + mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); ++i; } diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 4b45e72c43..905b8139ef 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -178,7 +178,7 @@ std::array FindKeyFromHex(const std::vector& binary, std::array temp{}; for (size_t i = 0; i < binary.size() - key_size; ++i) { - mbedtls_sha256_ret(binary.data() + i, key_size, temp.data(), 0); + mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0); if (temp != hash) continue; @@ -206,7 +206,7 @@ static std::array FindEncryptedMasterKeyFromHex(const std::vector< AESCipher cipher(key, Mode::ECB); for (size_t i = 0; i < binary.size() - 0x10; ++i) { cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt); - mbedtls_sha256_ret(dec_temp.data(), dec_temp.size(), temp.data(), 0); + mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0); for (size_t k = 0; k < out.size(); ++k) { if (temp == master_key_hashes[k]) { diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index cb2089e9b3..45390500cc 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -64,7 +64,7 @@ static std::string GetRelativePathFromNcaID(const std::array& nca_id, bo } Core::Crypto::SHA256Hash hash{}; - mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); + mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); const auto format_str = fmt::runtime(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca"); @@ -146,7 +146,7 @@ bool PlaceholderCache::Create(const NcaID& id, u64 size) const { } Core::Crypto::SHA256Hash hash{}; - mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); + mbedtls_sha256(id.data(), id.size(), hash.data(), 0); const auto dirname = fmt::format("000000{:02X}", hash[0]); const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); @@ -170,7 +170,7 @@ bool PlaceholderCache::Delete(const NcaID& id) const { } Core::Crypto::SHA256Hash hash{}; - mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); + mbedtls_sha256(id.data(), id.size(), hash.data(), 0); const auto dirname = fmt::format("000000{:02X}", hash[0]); const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); @@ -665,7 +665,7 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, const OptionalHeader opt_header{0, 0}; ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); - mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); + mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0); std::memcpy(&c_rec.nca_id, &c_rec.hash, 16); const CNMT new_cnmt(header, opt_header, {c_rec}, {}); if (!RawInstallYuzuMeta(new_cnmt)) { @@ -776,7 +776,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti id = *override_id; } else { const auto& data = in->ReadBytes(0x100000); - mbedtls_sha256_ret(data.data(), data.size(), hash.data(), 0); + mbedtls_sha256(data.data(), data.size(), hash.data(), 0); memcpy(id.data(), hash.data(), 16); } diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index fd5342021c..c1912b2bda 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -68,7 +68,7 @@ NAX::NAX(VirtualFile file_, std::array nca_id) : header(std::make_unique()), file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { Core::Crypto::SHA256Hash hash{}; - mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); + mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], Common::HexToString(nca_id, false))); } diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp index fea373a607..31693cfc33 100644 --- a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp +++ b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp @@ -15,7 +15,7 @@ namespace Service::BCAT { static BcatDigest DigestFile(const FileSys::VirtualFile& file) { BcatDigest out{}; const auto bytes = file->ReadAllBytes(); - mbedtls_md5_ret(bytes.data(), bytes.size(), out.data()); + mbedtls_md5(bytes.data(), bytes.size(), out.data()); return out; } diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index 3d3ad2d62c..600d0ec4fd 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -178,7 +178,7 @@ struct ProcessContext { std::vector nro_data(size); m_process->GetMemory().ReadBlock(base_address, nro_data.data(), size); - mbedtls_sha256_ret(nro_data.data(), size, hash.data(), 0); + mbedtls_sha256(nro_data.data(), size, hash.data(), 0); } for (size_t i = 0; i < MaxNrrInfos; i++) { diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 9a82dae144..0a9d9dc6f8 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -150,7 +150,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function // Initialize sha256 verification context. mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); - mbedtls_sha256_starts_ret(&ctx, 0); + mbedtls_sha256_starts(&ctx, 0); // Ensure we maintain a clean state on exit. SCOPE_EXIT { @@ -168,7 +168,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size); // Update the hash function with the buffer contents. - mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size); + mbedtls_sha256_update(&ctx, buffer.data(), read_size); // Update counters. processed_size += read_size; @@ -181,7 +181,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function // Finalize context and compute the output hash. std::array output_hash; - mbedtls_sha256_finish_ret(&ctx, output_hash.data()); + mbedtls_sha256_finish(&ctx, output_hash.data()); // Compare to expected. if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) { diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt index e5934c941a..5166329156 100644 --- a/src/dedicated_room/CMakeLists.txt +++ b/src/dedicated_room/CMakeLists.txt @@ -20,7 +20,7 @@ if (ENABLE_WEB_SERVICE) target_link_libraries(yuzu-room PRIVATE web_service) endif() -target_link_libraries(yuzu-room PRIVATE mbedtls mbedcrypto) +target_link_libraries(yuzu-room PRIVATE MbedTLS::mbedcrypto MbedTLS::mbedtls) if (MSVC) target_link_libraries(yuzu-room PRIVATE getopt) endif()