Compare commits

..

5 commits

Author SHA1 Message Date
36fae75437
add MMPX filter
All checks were successful
eden-license / license-header (pull_request) Successful in 22s
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-29 17:56:14 +00:00
e8d45d294a
better logic
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-29 17:55:52 +00:00
d8d1ccbe62
fix vk
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-29 17:55:52 +00:00
1290f7dca3
fix ogl
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-29 17:55:52 +00:00
4a1e0e27ab
[vk, ogl] VK_QCOM ZTC, Bspline, Mitchell filter weights
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-29 17:55:51 +00:00
31 changed files with 227 additions and 1093 deletions

View file

@ -139,50 +139,65 @@ endif()
# Set bundled sdl2/qt as dependent options.
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
cmake_dependent_option(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
if (ENABLE_SDL2)
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "NOT MSVC" OFF)
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
endif()
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
option(ENABLE_QT_UPDATE_CHECKER "Enable update checker for the Qt frontend" OFF)
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
CMAKE_DEPENDENT_OPTION(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
set(EXT_DEFAULT OFF)
if (MSVC OR ANDROID)
set(EXT_DEFAULT ON)
endif()
option(YUZU_USE_CPM "Use CPM to fetch system dependencies (fmt, boost, etc) if needed. Externals will still be fetched." ${EXT_DEFAULT})
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from source" OFF "NOT WIN32 AND NOT ANDROID" OFF)
if (ENABLE_SDL2)
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "NOT MSVC" OFF)
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
endif()
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT WIN32 OR NOT ARCHITECTURE_arm64" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
option(ENABLE_QT_UPDATE_CHECKER "Enable update checker for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(YUZU_USE_CPM "Use CPM to fetch system dependencies (fmt, boost, etc) if needed. Externals will still be fetched." ${EXT_DEFAULT})
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(ENABLE_WIFI_SCAN "Enable WiFi scanning" OFF)
cmake_dependent_option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF "ENABLE_QT" OFF)
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from source" OFF "NOT WIN32 AND NOT ANDROID" OFF)
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
CMAKE_DEPENDENT_OPTION(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF "ENABLE_QT" OFF)
option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF)
if (YUZU_USE_PRECOMPILED_HEADERS)
message(STATUS "Using Precompiled Headers.")
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
endif()
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ${EXT_DEFAULT})
# TODO(crueter): CI this?
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CMD "Compile the eden-cli executable" ON "ENABLE_SDL2;NOT ANDROID" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
if(YUZU_ENABLE_LTO)
include(CheckIPOSupported)
@ -190,42 +205,17 @@ if(YUZU_ENABLE_LTO)
if(NOT COMPILER_SUPPORTS_LTO)
message(FATAL_ERROR "Your compiler does not support interprocedural optimization (IPO). Re-run CMake with -DYUZU_ENABLE_LTO=OFF.")
endif()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${COMPILER_SUPPORTS_LTO})
endif()
option(USE_CCACHE "Use ccache for compilation" OFF)
set(CCACHE_PATH "ccache" CACHE STRING "Path to ccache binary")
if(USE_CCACHE)
find_program(CCACHE_BINARY ${CCACHE_PATH})
if(CCACHE_BINARY)
message(STATUS "Found ccache at: ${CCACHE_BINARY}")
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_BINARY})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_BINARY})
if (YUZU_USE_PRECOMPILED_HEADERS)
message(FATAL_ERROR "Precompiled headers are incompatible with ccache. Re-run CMake with -DYUZU_USE_PRECOMPILED_HEADERS=OFF.")
endif()
else()
message(WARNING "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
endif()
endif()
# TODO(crueter): CI this?
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "ENABLE_SDL2;NOT ANDROID" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead of the bundled one)" OFF "APPLE" OFF)
set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database")
cmake_dependent_option(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "LINUX" OFF)
cmake_dependent_option(YUZU_APPLE_USE_BUNDLED_MONTENVK "Download bundled MoltenVK lib" ON "APPLE" OFF)
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
set(DEFAULT_ENABLE_OPENSSL ON)
@ -238,12 +228,15 @@ if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN)
# your own copy of it.
set(DEFAULT_ENABLE_OPENSSL OFF)
endif()
if (ENABLE_WEB_SERVICE)
set(DEFAULT_ENABLE_OPENSSL ON)
endif()
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
if (ENABLE_OPENSSL)
cmake_dependent_option(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" "${MSVC}" "NOT ANDROID" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" "${MSVC}" "NOT ANDROID" ON)
endif()
if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
@ -270,6 +263,21 @@ if (ANDROID)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5) # Workaround for Oboe
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
if (MSVC AND CCACHE)
# buildcache does not properly cache PCH files, leading to compilation errors.
# See https://github.com/mbitsnbites/buildcache/discussions/230
message(WARNING "buildcache does not properly support Precompiled Headers. Disabling PCH")
set(DYNARMIC_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE)
set(YUZU_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE)
endif()
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
message(STATUS "Using Precompiled Headers.")
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
endif()
# Default to a Release build
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
@ -886,47 +894,24 @@ if (MSVC AND CXX_CLANG)
link_libraries(llvm-mingw-runtime)
endif()
#[[
search order:
- gold (GCC only) - the best, generally, but unfortunately not packaged anymore
- mold (GCC only) - generally does well on GCC
- ldd - preferred on clang
- bfd - the final fallback
- If none are found (macOS uses ld.prime, etc) just use the default linker
]]
if (YUZU_USE_FASTER_LD)
find_program(LINKER_BFD bfd)
if (LINKER_BFD)
set(LINKER bfd)
endif()
find_program(LINKER_LLD lld)
if (LINKER_LLD)
# fallback if everything fails (bfd)
set(LINKER bfd)
# clang should always use lld
find_program(LLD lld)
if (LLD)
set(LINKER lld)
endif()
# GNU appears to work better with mold
# TODO: mold has been slow lately, see if better options exist (search for gold?)
if (CXX_GCC)
find_program(LINKER_MOLD mold)
if (LINKER_MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
find_program(MOLD mold)
if (MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
set(LINKER mold)
endif()
find_program(LINKER_GOLD gold)
if (LINKER_GOLD)
set(LINKER gold)
endif()
endif()
if (LINKER)
message(NOTICE "Selecting ${LINKER} as linker")
add_link_options("-fuse-ld=${LINKER}")
else()
message(WARNING "No faster linker found--using default")
endif()
if (LINKER STREQUAL "lld" AND CXX_GCC)
message(WARNING "Using lld on GCC may cause issues with certain LTO settings. If the program fails to compile, disable YUZU_USE_FASTER_LD, or install mold or GNU gold.")
endif()
message(NOTICE "Selecting ${LINKER} as linker")
add_link_options("-fuse-ld=${LINKER}")
endif()
# Set runtime library to MD/MDd for all configurations

View file

@ -10,7 +10,6 @@ function(download_bundled_external remote_path lib_name cpm_key prefix_var versi
set(package_base_url "https://github.com/eden-emulator/")
set(package_repo "no_platform")
set(package_extension "no_platform")
set(CACHE_KEY "")
# TODO(crueter): Need to convert ffmpeg to a CI.
if (WIN32 OR FORCE_WIN_ARCHIVES)
@ -34,9 +33,8 @@ function(download_bundled_external remote_path lib_name cpm_key prefix_var versi
else()
message(FATAL_ERROR "No package available for this platform")
endif()
string(CONCAT package_url "${package_base_url}" "${package_repo}")
string(CONCAT full_url "${package_url}" "${remote_path}" "${lib_name}" "${package_extension}")
message(STATUS "Resolved bundled URL: ${full_url}")
set(package_url "${package_base_url}${package_repo}")
set(full_url ${package_url}${remote_path}${lib_name}${package_extension})
# TODO(crueter): DELETE THIS ENTIRELY, GLORY BE TO THE CI!
AddPackage(
@ -49,12 +47,26 @@ function(download_bundled_external remote_path lib_name cpm_key prefix_var versi
# TODO(crueter): hash
)
if (DEFINED ${cpm_key}_SOURCE_DIR)
set(${prefix_var} "${${cpm_key}_SOURCE_DIR}" PARENT_SCOPE)
message(STATUS "Using bundled binaries at ${${cpm_key}_SOURCE_DIR}")
else()
message(FATAL_ERROR "AddPackage did not set ${cpm_key}_SOURCE_DIR")
set(${prefix_var} "${${cpm_key}_SOURCE_DIR}" PARENT_SCOPE)
message(STATUS "Using bundled binaries at ${${cpm_key}_SOURCE_DIR}")
endfunction()
function(download_moltenvk_external platform version)
set(MOLTENVK_DIR "${CMAKE_BINARY_DIR}/externals/MoltenVK")
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
if (NOT EXISTS ${MOLTENVK_DIR})
if (NOT EXISTS ${MOLTENVK_TAR})
file(DOWNLOAD https://github.com/KhronosGroup/MoltenVK/releases/download/${version}/MoltenVK-${platform}.tar
${MOLTENVK_TAR} SHOW_PROGRESS)
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
# Add the MoltenVK library path to the prefix so find_library can locate it.
list(APPEND CMAKE_PREFIX_PATH "${MOLTENVK_DIR}/MoltenVK/dylib/${platform}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endfunction()
# Determine installation parameters for OS, architecture, and compiler
@ -96,7 +108,7 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
set(host "linux")
set(type "desktop")
set(arch "linux_gcc_64")
set(arch_path "gcc_64")
set(arch_path "linux")
endif()
set(${host_out} "${host}" PARENT_SCOPE)
@ -131,79 +143,56 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba
set(install_args -c "${CURRENT_MODULE_DIR}/aqt_config.ini")
if (tool)
set(prefix "${base_path}/Tools")
list(APPEND install_args install-tool --outputdir "${base_path}" "${host}" desktop "${target}")
set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target})
else()
set(prefix "${base_path}/${target}/${arch_path}")
list(APPEND install_args install-qt --outputdir "${base_path}" "${host}" "${type}" "${target}" "${arch}" -m qt_base)
set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt_base)
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND install_args qtmultimedia)
set(install_args ${install_args} qtmultimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND install_args qtpositioning qtwebchannel qtwebengine)
set(install_args ${install_args} qtpositioning qtwebchannel qtwebengine)
endif()
if (NOT "${YUZU_QT_MIRROR}" STREQUAL "")
if (NOT ${YUZU_QT_MIRROR} STREQUAL "")
message(STATUS "Using Qt mirror ${YUZU_QT_MIRROR}")
list(APPEND install_args -b "${YUZU_QT_MIRROR}")
set(install_args ${install_args} -b ${YUZU_QT_MIRROR})
endif()
endif()
message(STATUS "Install Args: ${install_args}")
message(STATUS "Install Args ${install_args}")
if (NOT EXISTS "${prefix}")
message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0")
if (WIN32)
set(aqt_path "${base_path}/aqt.exe")
if (NOT EXISTS "${aqt_path}")
file(DOWNLOAD "${AQT_PREBUILD_BASE_URL}/aqt.exe" "${aqt_path}" SHOW_PROGRESS)
endif()
execute_process(COMMAND "${aqt_path}" ${install_args}
WORKING_DIRECTORY "${base_path}"
RESULT_VARIABLE aqt_res
OUTPUT_VARIABLE aqt_out
ERROR_VARIABLE aqt_err)
if (NOT aqt_res EQUAL 0)
message(FATAL_ERROR "aqt.exe failed: ${aqt_err}")
file(DOWNLOAD
${AQT_PREBUILD_BASE_URL}/aqt.exe
${aqt_path} SHOW_PROGRESS)
endif()
execute_process(COMMAND ${aqt_path} ${install_args}
WORKING_DIRECTORY ${base_path})
elseif (APPLE)
set(aqt_path "${base_path}/aqt-macos")
if (NOT EXISTS "${aqt_path}")
file(DOWNLOAD "${AQT_PREBUILD_BASE_URL}/aqt-macos" "${aqt_path}" SHOW_PROGRESS)
endif()
execute_process(COMMAND chmod +x "${aqt_path}")
execute_process(COMMAND "${aqt_path}" ${install_args}
WORKING_DIRECTORY "${base_path}"
RESULT_VARIABLE aqt_res
ERROR_VARIABLE aqt_err)
if (NOT aqt_res EQUAL 0)
message(FATAL_ERROR "aqt-macos failed: ${aqt_err}")
file(DOWNLOAD
${AQT_PREBUILD_BASE_URL}/aqt-macos
${aqt_path} SHOW_PROGRESS)
endif()
execute_process(COMMAND chmod +x ${aqt_path})
execute_process(COMMAND ${aqt_path} ${install_args}
WORKING_DIRECTORY ${base_path})
else()
find_program(PYTHON3_EXECUTABLE python3)
if (NOT PYTHON3_EXECUTABLE)
message(FATAL_ERROR "python3 is required to install Qt using aqt (pip mode).")
endif()
set(aqt_install_path "${base_path}/aqt")
file(MAKE_DIRECTORY "${aqt_install_path}")
execute_process(COMMAND "${PYTHON3_EXECUTABLE}" -m pip install --target="${aqt_install_path}" aqtinstall
WORKING_DIRECTORY "${base_path}"
RESULT_VARIABLE pip_res
ERROR_VARIABLE pip_err)
if (NOT pip_res EQUAL 0)
message(FATAL_ERROR "pip install aqtinstall failed: ${pip_err}")
endif()
execute_process(COMMAND "${CMAKE_COMMAND}" -E env PYTHONPATH="${aqt_install_path}" "${PYTHON3_EXECUTABLE}" -m aqt ${install_args}
WORKING_DIRECTORY "${base_path}"
RESULT_VARIABLE aqt_res
ERROR_VARIABLE aqt_err)
if (NOT aqt_res EQUAL 0)
message(FATAL_ERROR "aqt (python) failed: ${aqt_err}")
endif()
execute_process(COMMAND python3 -m pip install --target=${aqt_install_path} aqtinstall
WORKING_DIRECTORY ${base_path})
execute_process(COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${aqt_install_path} python3 -m aqt ${install_args}
WORKING_DIRECTORY ${base_path})
endif()
message(STATUS "Downloaded Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path} to ${prefix}")
@ -221,7 +210,7 @@ endfunction()
function(download_qt target)
determine_qt_parameters("${target}" host type arch arch_path host_type host_arch host_arch_path)
set(base_path "${CMAKE_BINARY_DIR}/externals/qt")
get_external_prefix(qt base_path)
file(MAKE_DIRECTORY "${base_path}")
download_qt_configuration(prefix "${target}" "${host}" "${type}" "${arch}" "${arch_path}" "${base_path}")
@ -238,34 +227,26 @@ function(download_qt target)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endfunction()
function(download_moltenvk version platform)
if(NOT version)
message(FATAL_ERROR "download_moltenvk: version argument is required")
endif()
if(NOT platform)
message(FATAL_ERROR "download_moltenvk: platform argument is required")
endif()
function(download_moltenvk)
set(MOLTENVK_PLATFORM "macOS")
set(MOLTENVK_DIR "${CMAKE_BINARY_DIR}/externals/MoltenVK")
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
set(MOLTENVK_DIR "${CMAKE_BINARY_DIR}/externals/MoltenVK")
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
if (NOT EXISTS ${MOLTENVK_DIR})
if (NOT EXISTS ${MOLTENVK_TAR})
file(DOWNLOAD https://github.com/KhronosGroup/MoltenVK/releases/download/v1.2.10-rc2/MoltenVK-all.tar
${MOLTENVK_TAR} SHOW_PROGRESS)
endif()
if(NOT EXISTS "${MOLTENVK_DIR}")
if(NOT EXISTS "${MOLTENVK_TAR}")
file(DOWNLOAD "https://github.com/KhronosGroup/MoltenVK/releases/download/${version}/MoltenVK-${platform}.tar"
"${MOLTENVK_TAR}" SHOW_PROGRESS)
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals"
RESULT_VARIABLE tar_res
ERROR_VARIABLE tar_err
)
if(NOT tar_res EQUAL 0)
message(FATAL_ERROR "Extracting MoltenVK failed: ${tar_err}")
endif()
endif()
list(APPEND CMAKE_PREFIX_PATH "${MOLTENVK_DIR}/MoltenVK/dylib/${platform}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
# Add the MoltenVK library path to the prefix so find_library can locate it.
list(APPEND CMAKE_PREFIX_PATH "${MOLTENVK_DIR}/MoltenVK/dylib/${MOLTENVK_PLATFORM}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endfunction()
function(get_external_prefix lib_name prefix_var)
set(${prefix_var} "${CMAKE_BINARY_DIR}/externals/${lib_name}" PARENT_SCOPE)
endfunction()

View file

@ -101,7 +101,7 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
<summary>Ubuntu, Debian, Mint Linux</summary>
```sh
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2t64 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libzydis-dev zydis-tools libzycore-dev vulkan-utility-libraries-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libzydis-dev zydis-tools libzycore-dev
```
* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.

View file

@ -31,7 +31,7 @@ Notes:
* Currently, build fails without this
- `YUZU_USE_FASTER_LD` (ON) Check if a faster linker is available
* Only available on UNIX
- `YUZU_APPLE_USE_BUNDLED_MONTENVK` (ON, macOS only) Download bundled MoltenVK lib)
- `USE_SYSTEM_MOLTENVK` (OFF, macOS only) Use the system MoltenVK lib (instead of the bundled one)
- `YUZU_TZDB_PATH` (string) Path to a pre-downloaded timezone database (useful for nixOS)
- `ENABLE_OPENSSL` (ON for Linux and *BSD) Enable OpenSSL backend for the ssl service
* Always enabled if the web service is enabled

View file

@ -68,9 +68,6 @@ 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
@ -492,38 +489,6 @@ 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)
}

View file

@ -55,8 +55,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
FRAME_INTERPOLATION("frame_interpolation"),
// FRAME_SKIPPING("frame_skipping"),
ENABLE_INPUT_OVERLAY_AUTO_HIDE("enable_input_overlay_auto_hide"),
PERF_OVERLAY_BACKGROUND("perf_overlay_background"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),

View file

@ -59,8 +59,7 @@ 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"),
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide")
MY_PAGE_APPLET("my_page_applet_mode")
;
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View file

@ -12,7 +12,6 @@ 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),

View file

@ -96,7 +96,6 @@ abstract class SettingsItem(
const val TYPE_INT_SINGLE_CHOICE = 9
const val TYPE_INPUT_PROFILE = 10
const val TYPE_STRING_INPUT = 11
const val TYPE_SPINBOX = 12
const val FASTMEM_COMBINED = "fastmem_combined"
@ -386,22 +385,6 @@ abstract class SettingsItem(
warningMessage = R.string.warning_resolution
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE,
titleId = R.string.enable_input_overlay_auto_hide,
)
)
put(
SpinBoxSetting(
IntSetting.INPUT_OVERLAY_AUTO_HIDE,
titleId = R.string.overlay_auto_hide,
descriptionId = R.string.overlay_auto_hide_description,
min = 1,
max = 999,
valueHint = R.string.seconds
)
)
put(
SwitchSetting(

View file

@ -1,41 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
class SpinBoxSetting(
setting: AbstractSetting,
@StringRes titleId: Int = 0,
titleString: String = "",
@StringRes descriptionId: Int = 0,
descriptionString: String = "",
val valueHint: Int,
val min: Int,
val max: Int
) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_SPINBOX
fun getSelectedValue(needsGlobal: Boolean = false) =
when (setting) {
is AbstractByteSetting -> setting.getByte(needsGlobal).toInt()
is AbstractShortSetting -> setting.getShort(needsGlobal).toInt()
is AbstractIntSetting -> setting.getInt(needsGlobal)
is AbstractFloatSetting -> setting.getFloat(needsGlobal).toInt()
else -> 0
}
fun setSelectedValue(value: Int) =
when (setting) {
is AbstractByteSetting -> setting.setByte(value.toByte())
is AbstractShortSetting -> setting.setShort(value.toShort())
is AbstractFloatSetting -> setting.setFloat(value.toFloat())
else -> (setting as AbstractIntSetting).setInt(value)
}
}

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
@ -61,10 +61,6 @@ class SettingsAdapter(
SliderViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SPINBOX -> {
SpinBoxViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SUBMENU -> {
SubmenuViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
@ -195,14 +191,6 @@ class SettingsAdapter(
position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
}
fun onSpinBoxClick(item: SpinBoxSetting, position: Int) {
SettingsDialogFragment.newInstance(
settingsViewModel,
item,
SettingsItem.TYPE_SPINBOX,
position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
}
fun onSubmenuClick(item: SubmenuSetting) {
val action = SettingsNavigationDirections.actionGlobalSettingsFragment(item.menuKey, null)

View file

@ -14,7 +14,6 @@ import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
@ -23,7 +22,6 @@ import com.google.android.material.slider.Slider
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.databinding.DialogSpinboxBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
@ -32,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SpinBoxSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.utils.ParamPackage
@ -49,7 +46,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
private lateinit var sliderBinding: DialogSliderBinding
private lateinit var stringInputBinding: DialogEditTextBinding
private lateinit var spinboxBinding: DialogSpinboxBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -146,76 +142,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.create()
}
SettingsItem.TYPE_SPINBOX -> {
spinboxBinding = DialogSpinboxBinding.inflate(layoutInflater)
val item = settingsViewModel.clickedItem as SpinBoxSetting
val currentValue = item.getSelectedValue()
spinboxBinding.editValue.setText(currentValue.toString())
spinboxBinding.textInputLayout.hint = getString(item.valueHint)
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(item.title)
.setView(spinboxBinding.root)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
.create()
val updateButtonState = { enabled: Boolean ->
dialog.setOnShowListener { dialogInterface ->
(dialogInterface as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = enabled
}
if (dialog.isShowing) {
dialog.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = enabled
}
}
val updateValidity = { value: Int ->
val isValid = value in item.min..item.max
if (isValid) {
spinboxBinding.textInputLayout.error = null
} else {
spinboxBinding.textInputLayout.error = getString(
if (value < item.min) R.string.value_too_low else R.string.value_too_high,
if (value < item.min) item.min else item.max
)
}
updateButtonState(isValid)
}
spinboxBinding.buttonDecrement.setOnClickListener {
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
val newValue = current - 1
spinboxBinding.editValue.setText(newValue.toString())
updateValidity(newValue)
}
spinboxBinding.buttonIncrement.setOnClickListener {
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
val newValue = current + 1
spinboxBinding.editValue.setText(newValue.toString())
updateValidity(newValue)
}
spinboxBinding.editValue.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
val value = s.toString().toIntOrNull()
if (value != null) {
updateValidity(value)
} else {
spinboxBinding.textInputLayout.error = getString(R.string.invalid_value)
updateButtonState(false)
}
}
})
updateValidity(currentValue)
dialog
}
SettingsItem.TYPE_STRING_INPUT -> {
stringInputBinding = DialogEditTextBinding.inflate(layoutInflater)
val item = settingsViewModel.clickedItem as StringInputSetting
@ -355,14 +281,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
}
is SpinBoxSetting -> {
val spinBoxSetting = settingsViewModel.clickedItem as SpinBoxSetting
val value = spinboxBinding.editValue.text.toString().toIntOrNull()
if (value != null && value in spinBoxSetting.min..spinBoxSetting.max) {
spinBoxSetting.setSelectedValue(value)
}
}
is StringInputSetting -> {
val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting
stringInputSetting.setSelectedValue(

View file

@ -97,7 +97,6 @@ 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)
@ -157,14 +156,6 @@ 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(
@ -273,13 +264,6 @@ class SettingsFragmentPresenter(
}
}
private fun addInputOverlaySettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE.key)
add(IntSetting.INPUT_OVERLAY_AUTO_HIDE.key)
}
}
private fun addSocOverlaySettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(HeaderSetting(R.string.stats_overlay_customization))

View file

@ -1,44 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SpinBoxSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SpinBoxViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: SpinBoxSetting
override fun bind(item: SettingsItem) {
setting = item as SpinBoxSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
binding.textSettingValue.setVisible(true)
binding.textSettingValue.text = setting.getSelectedValue().toString()
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
setStyle(setting.isEditable, binding)
}
override fun onClick(clicked: View) {
if (setting.isEditable) {
adapter.onSpinBoxClick(setting, bindingAdapterPosition)
}
}
override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) {
return adapter.onLongClick(setting, bindingAdapterPosition)
}
return false
}
}

View file

@ -96,9 +96,6 @@ 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!!
@ -455,10 +452,7 @@ 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())
@ -734,8 +728,6 @@ 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<TextView>(R.id.text_game_title)
@ -925,11 +917,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updatePauseMenuEntry(emulationState.isPaused)
}
}
// if the overlay auto-hide setting is changed while paused,
// we need to reinitialize the auto-hide timer
initializeOverlayAutoHide()
}
private fun resetInputOverlay() {
@ -937,7 +924,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
IntSetting.OVERLAY_OPACITY.reset()
binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
binding.surfaceInputOverlay.resetIndividualControlScale()
}
}
@ -1048,7 +1034,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)}")
@ -1560,7 +1546,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
.setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
setControlScale(50)
setControlOpacity(100)
binding.surfaceInputOverlay.resetIndividualControlScale()
}
.show()
}
@ -1741,61 +1726,4 @@ 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()
val shouldProceed = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean() && BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE.getBoolean()
if (!shouldProceed) {
return
}
// failsafe
if (autoHideSeconds == 0) {
showOverlay()
return
}
if (!isOverlayVisible && !isLongTap) {
showOverlay()
}
startOverlayAutoHideTimer(autoHideSeconds)
}
private fun initializeOverlayAutoHide() {
val autoHideSeconds = IntSetting.INPUT_OVERLAY_AUTO_HIDE.getInt()
val autoHideEnabled = BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE.getBoolean()
val showOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
if (autoHideEnabled && showOverlay) {
showOverlay()
startOverlayAutoHideTimer(autoHideSeconds)
}
}
fun showOverlay() {
if (!isOverlayVisible) {
isOverlayVisible = true
ViewUtils.showView(binding.surfaceInputOverlay, 500)
}
}
private fun hideOverlay() {
if (isOverlayVisible) {
isOverlayVisible = false
ViewUtils.hideView(binding.surfaceInputOverlay, 500)
}
}
}
}

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay
@ -13,8 +13,6 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.VectorDrawable
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
@ -54,12 +52,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private var dpadBeingConfigured: InputOverlayDrawableDpad? = null
private var joystickBeingConfigured: InputOverlayDrawableJoystick? = null
private var scaleDialog: OverlayScaleDialog? = null
private var touchStartX = 0f
private var touchStartY = 0f
private var hasMoved = false
private val moveThreshold = 20f
private lateinit var windowInsets: WindowInsets
var layout = OverlayLayout.Landscape
@ -262,44 +254,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
) {
buttonBeingConfigured = button
buttonBeingConfigured!!.onConfigureTouch(event)
touchStartX = event.getX(pointerIndex)
touchStartY = event.getY(pointerIndex)
hasMoved = false
}
MotionEvent.ACTION_MOVE -> if (buttonBeingConfigured != null) {
val moveDistance = kotlin.math.sqrt(
(event.getX(pointerIndex) - touchStartX).let { it * it } +
(event.getY(pointerIndex) - touchStartY).let { it * it }
)
if (moveDistance > moveThreshold) {
hasMoved = true
buttonBeingConfigured!!.onConfigureTouch(event)
invalidate()
return true
}
buttonBeingConfigured!!.onConfigureTouch(event)
invalidate()
return true
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
if (!hasMoved) {
showScaleDialog(
buttonBeingConfigured,
null,
null,
fingerPositionX,
fingerPositionY
)
} else {
saveControlPosition(
buttonBeingConfigured!!.overlayControlData.id,
buttonBeingConfigured!!.bounds.centerX(),
buttonBeingConfigured!!.bounds.centerY(),
individuaScale = buttonBeingConfigured!!.overlayControlData.individualScale,
layout
)
}
// Persist button position by saving new place.
saveControlPosition(
buttonBeingConfigured!!.overlayControlData.id,
buttonBeingConfigured!!.bounds.centerX(),
buttonBeingConfigured!!.bounds.centerY(),
layout
)
buttonBeingConfigured = null
}
}
@ -316,46 +287,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
) {
dpadBeingConfigured = dpad
dpadBeingConfigured!!.onConfigureTouch(event)
touchStartX = event.getX(pointerIndex)
touchStartY = event.getY(pointerIndex)
hasMoved = false
}
MotionEvent.ACTION_MOVE -> if (dpadBeingConfigured != null) {
val moveDistance = kotlin.math.sqrt(
(event.getX(pointerIndex) - touchStartX).let { it * it } +
(event.getY(pointerIndex) - touchStartY).let { it * it }
)
if (moveDistance > moveThreshold) {
hasMoved = true
dpadBeingConfigured!!.onConfigureTouch(event)
invalidate()
return true
}
dpadBeingConfigured!!.onConfigureTouch(event)
invalidate()
return true
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
if (!hasMoved) {
// This was a click, show scale dialog for dpad
showScaleDialog(
null,
dpadBeingConfigured,
null,
fingerPositionX,
fingerPositionY
)
} else {
// This was a move, save position
saveControlPosition(
OverlayControl.COMBINED_DPAD.id,
dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(),
individuaScale = dpadBeingConfigured!!.individualScale,
layout
)
}
// Persist button position by saving new place.
saveControlPosition(
OverlayControl.COMBINED_DPAD.id,
dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(),
layout
)
dpadBeingConfigured = null
}
}
@ -369,43 +317,21 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
) {
joystickBeingConfigured = joystick
joystickBeingConfigured!!.onConfigureTouch(event)
touchStartX = event.getX(pointerIndex)
touchStartY = event.getY(pointerIndex)
hasMoved = false
}
MotionEvent.ACTION_MOVE -> if (joystickBeingConfigured != null) {
val moveDistance = kotlin.math.sqrt(
(event.getX(pointerIndex) - touchStartX).let { it * it } +
(event.getY(pointerIndex) - touchStartY).let { it * it }
)
if (moveDistance > moveThreshold) {
hasMoved = true
joystickBeingConfigured!!.onConfigureTouch(event)
invalidate()
}
joystickBeingConfigured!!.onConfigureTouch(event)
invalidate()
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) {
if (!hasMoved) {
showScaleDialog(
null,
null,
joystickBeingConfigured,
fingerPositionX,
fingerPositionY
)
} else {
saveControlPosition(
joystickBeingConfigured!!.prefId,
joystickBeingConfigured!!.bounds.centerX(),
joystickBeingConfigured!!.bounds.centerY(),
individuaScale = joystickBeingConfigured!!.individualScale,
layout
)
}
saveControlPosition(
joystickBeingConfigured!!.prefId,
joystickBeingConfigured!!.bounds.centerX(),
joystickBeingConfigured!!.bounds.centerY(),
layout
)
joystickBeingConfigured = null
}
}
@ -681,117 +607,25 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
invalidate()
}
private fun saveControlPosition(
id: String,
x: Int,
y: Int,
individuaScale: Float,
layout: OverlayLayout
) {
private fun saveControlPosition(id: String, x: Int, y: Int, layout: OverlayLayout) {
val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
val min = windowSize.first
val max = windowSize.second
val overlayControlData = NativeConfig.getOverlayControlData()
val data = overlayControlData.firstOrNull { it.id == id }
val newPosition = Pair((x - min.x).toDouble() / max.x, (y - min.y).toDouble() / max.y)
when (layout) {
OverlayLayout.Landscape -> data?.landscapePosition = newPosition
OverlayLayout.Portrait -> data?.portraitPosition = newPosition
OverlayLayout.Foldable -> data?.foldablePosition = newPosition
}
data?.individualScale = individuaScale
NativeConfig.setOverlayControlData(overlayControlData)
}
fun setIsInEditMode(editMode: Boolean) {
inEditMode = editMode
if (!editMode) {
scaleDialog?.dismiss()
scaleDialog = null
}
}
private fun showScaleDialog(
button: InputOverlayDrawableButton?,
dpad: InputOverlayDrawableDpad?,
joystick: InputOverlayDrawableJoystick?,
x: Int, y: Int
) {
val overlayControlData = NativeConfig.getOverlayControlData()
// prevent dialog from being spam opened
scaleDialog?.dismiss()
when {
button != null -> {
val buttonData =
overlayControlData.firstOrNull { it.id == button.overlayControlData.id }
if (buttonData != null) {
scaleDialog =
OverlayScaleDialog(context, button.overlayControlData) { newScale ->
saveControlPosition(
button.overlayControlData.id,
button.bounds.centerX(),
button.bounds.centerY(),
individuaScale = newScale,
layout
)
refreshControls()
}
scaleDialog?.showDialog(x,y, button.bounds.width(), button.bounds.height())
}
}
dpad != null -> {
val dpadData =
overlayControlData.firstOrNull { it.id == OverlayControl.COMBINED_DPAD.id }
if (dpadData != null) {
scaleDialog = OverlayScaleDialog(context, dpadData) { newScale ->
saveControlPosition(
OverlayControl.COMBINED_DPAD.id,
dpad.bounds.centerX(),
dpad.bounds.centerY(),
newScale,
layout
)
refreshControls()
}
scaleDialog?.showDialog(x,y, dpad.bounds.width(), dpad.bounds.height())
}
}
joystick != null -> {
val joystickData = overlayControlData.firstOrNull { it.id == joystick.prefId }
if (joystickData != null) {
scaleDialog = OverlayScaleDialog(context, joystickData) { newScale ->
saveControlPosition(
joystick.prefId,
joystick.bounds.centerX(),
joystick.bounds.centerY(),
individuaScale = newScale,
layout
)
refreshControls()
}
scaleDialog?.showDialog(x,y, joystick.bounds.width(), joystick.bounds.height())
}
}
}
}
/**
* Applies and saves all default values for the overlay
*/
@ -830,24 +664,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
val overlayControlData = NativeConfig.getOverlayControlData()
overlayControlData.forEach {
it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true
it.individualScale = OverlayControl.from(it.id)?.defaultIndividualScaleResource!!
}
NativeConfig.setOverlayControlData(overlayControlData)
refreshControls()
}
fun resetIndividualControlScale() {
val overlayControlData = NativeConfig.getOverlayControlData()
overlayControlData.forEach { data ->
val defaultControlData = OverlayControl.from(data.id) ?: return@forEach
data.individualScale = defaultControlData.defaultIndividualScaleResource
}
NativeConfig.setOverlayControlData(overlayControlData)
NativeConfig.saveGlobalConfig()
refreshControls()
}
private fun defaultOverlayPositionByLayout(layout: OverlayLayout) {
val overlayControlData = NativeConfig.getOverlayControlData()
for (data in overlayControlData) {
@ -1038,9 +860,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
scale /= 100f
// Apply individual scale
scale *= overlayControlData.individualScale
// Initialize the InputOverlayDrawableButton.
val defaultStateBitmap = getBitmap(context, defaultResId, scale)
val pressedStateBitmap = getBitmap(context, pressedResId, scale)
@ -1103,20 +922,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// Resources handle for fetching the initial Drawable resource.
val res = context.resources
// Get the dpad control data for individual scale
val overlayControlData = NativeConfig.getOverlayControlData()
val dpadData = overlayControlData.firstOrNull { it.id == OverlayControl.COMBINED_DPAD.id }
// Decide scale based on button ID and user preference
var scale = 0.25f
scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
scale /= 100f
// Apply individual scale
if (dpadData != null) {
scale *= dpadData.individualScale
}
// Initialize the InputOverlayDrawableDpad.
val defaultStateBitmap =
getBitmap(context, defaultResId, scale)
@ -1190,9 +1000,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
scale /= 100f
// Apply individual scale
scale *= overlayControlData.individualScale
// Initialize the InputOverlayDrawableJoystick.
val bitmapOuter = getBitmap(context, resOuter, scale)
val bitmapInnerDefault = getBitmap(context, defaultResInner, 1.0f)

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay
@ -42,8 +42,6 @@ class InputOverlayDrawableDpad(
val width: Int
val height: Int
var individualScale: Float = 1.0f
private val defaultStateBitmap: BitmapDrawable
private val pressedOneDirectionStateBitmap: BitmapDrawable
private val pressedTwoDirectionsStateBitmap: BitmapDrawable

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay
@ -51,8 +51,6 @@ class InputOverlayDrawableJoystick(
val width: Int
val height: Int
var individualScale: Float = 1.0f
private var opacity: Int = 0
private var virtBounds: Rect

View file

@ -1,124 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.overlay
import android.app.Dialog
import android.content.Context
import android.view.Gravity
import android.view.LayoutInflater
import android.view.WindowManager
import android.widget.TextView
import com.google.android.material.button.MaterialButton
import com.google.android.material.slider.Slider
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
class OverlayScaleDialog(
context: Context,
private val overlayControlData: OverlayControlData,
private val onScaleChanged: (Float) -> Unit
) : Dialog(context) {
private var currentScale = overlayControlData.individualScale
private val originalScale = overlayControlData.individualScale
private lateinit var scaleValueText: TextView
private lateinit var scaleSlider: Slider
init {
setupDialog()
}
private fun setupDialog() {
val view = LayoutInflater.from(context).inflate(R.layout.dialog_overlay_scale, null)
setContentView(view)
window?.setBackgroundDrawable(null)
window?.apply {
attributes = attributes.apply {
flags = flags and WindowManager.LayoutParams.FLAG_DIM_BEHIND.inv()
flags = flags or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
}
}
scaleValueText = view.findViewById(R.id.scaleValueText)
scaleSlider = view.findViewById(R.id.scaleSlider)
val resetButton = view.findViewById<MaterialButton>(R.id.resetButton)
val confirmButton = view.findViewById<MaterialButton>(R.id.confirmButton)
val cancelButton = view.findViewById<MaterialButton>(R.id.cancelButton)
scaleValueText.text = String.format("%.1fx", currentScale)
scaleSlider.value = currentScale
scaleSlider.addOnChangeListener { _, value, input ->
if (input) {
currentScale = value
scaleValueText.text = String.format("%.1fx", currentScale)
}
}
scaleSlider.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) {
// pass
}
override fun onStopTrackingTouch(slider: Slider) {
onScaleChanged(currentScale)
}
})
resetButton.setOnClickListener {
currentScale = 1.0f
scaleSlider.value = 1.0f
scaleValueText.text = String.format("%.1fx", currentScale)
onScaleChanged(currentScale)
}
confirmButton.setOnClickListener {
overlayControlData.individualScale = currentScale
//slider value is already saved on touch dispatch but just to be sure
onScaleChanged(currentScale)
dismiss()
}
// both cancel button and back gesture should revert the scale change
cancelButton.setOnClickListener {
onScaleChanged(originalScale)
dismiss()
}
setOnCancelListener {
onScaleChanged(originalScale)
dismiss()
}
}
fun showDialog(anchorX: Int, anchorY: Int, anchorHeight: Int, anchorWidth: Int) {
show()
show()
// TODO: this calculation is a bit rough, improve it later on
window?.let { window ->
val layoutParams = window.attributes
layoutParams.gravity = Gravity.TOP or Gravity.START
val density = context.resources.displayMetrics.density
val dialogWidthPx = (320 * density).toInt()
val dialogHeightPx = (400 * density).toInt() // set your estimated dialog height
val screenHeight = context.resources.displayMetrics.heightPixels
layoutParams.x = anchorX + anchorWidth / 2 - dialogWidthPx / 2
layoutParams.y = anchorY + anchorHeight / 2 - dialogHeightPx / 2
layoutParams.width = dialogWidthPx
window.attributes = layoutParams
}
}
}

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
@ -12,144 +12,126 @@ enum class OverlayControl(
val defaultVisibility: Boolean,
@IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
@IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
@IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>,
val defaultIndividualScaleResource: Float,
@IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
) {
BUTTON_A(
"button_a",
true,
Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
),
BUTTON_B(
"button_b",
true,
Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
),
BUTTON_X(
"button_x",
true,
Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
),
BUTTON_Y(
"button_y",
true,
Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
),
BUTTON_PLUS(
"button_plus",
true,
Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
),
BUTTON_MINUS(
"button_minus",
true,
Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
),
BUTTON_HOME(
"button_home",
false,
Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
),
BUTTON_CAPTURE(
"button_capture",
false,
Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
),
BUTTON_L(
"button_l",
true,
Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
),
BUTTON_R(
"button_r",
true,
Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
),
BUTTON_ZL(
"button_zl",
true,
Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
),
BUTTON_ZR(
"button_zr",
true,
Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
),
BUTTON_STICK_L(
"button_stick_l",
true,
Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
),
BUTTON_STICK_R(
"button_stick_r",
true,
Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE),
1.0f
Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
),
STICK_L(
"stick_l",
true,
Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE),
1.0f
Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
),
STICK_R(
"stick_r",
true,
Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE),
1.0f
Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
),
COMBINED_DPAD(
"combined_dpad",
true,
Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE),
1.0f
Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
);
fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
@ -191,8 +173,7 @@ enum class OverlayControl(
defaultVisibility,
getDefaultPositionForLayout(OverlayLayout.Landscape),
getDefaultPositionForLayout(OverlayLayout.Portrait),
getDefaultPositionForLayout(OverlayLayout.Foldable),
defaultIndividualScaleResource
getDefaultPositionForLayout(OverlayLayout.Foldable)
)
companion object {

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
@ -8,8 +8,7 @@ data class OverlayControlData(
var enabled: Boolean,
var landscapePosition: Pair<Double, Double>,
var portraitPosition: Pair<Double, Double>,
var foldablePosition: Pair<Double, Double>,
var individualScale: Float
var foldablePosition: Pair<Double, Double>
) {
fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
when (layout) {

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
@ -170,8 +170,7 @@ object DirectoryInitialization {
buttonEnabled,
Pair(landscapeXPosition, landscapeYPosition),
Pair(portraitXPosition, portraitYPosition),
Pair(foldableXPosition, foldableYPosition),
OverlayControl.map[buttonId]?.defaultIndividualScaleResource ?: 1.0f
Pair(foldableXPosition, foldableYPosition)
)
overlayControlDataMap[buttonId] = controlData
setOverlayData = true

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/logging/log.h>
#include <input_common/main.h>
@ -103,7 +103,6 @@ void AndroidConfig::ReadOverlayValues() {
ReadDoubleSetting(std::string("foldable\\x_position"));
control_data.foldable_position.second =
ReadDoubleSetting(std::string("foldable\\y_position"));
control_data.individual_scale = static_cast<float>(ReadDoubleSetting(std::string("individual_scale")));
AndroidSettings::values.overlay_control_data.push_back(control_data);
}
EndArray();
@ -256,7 +255,6 @@ void AndroidConfig::SaveOverlayValues() {
control_data.foldable_position.first);
WriteDoubleSetting(std::string("foldable\\y_position"),
control_data.foldable_position.second);
WriteDoubleSetting(std::string("individual_scale"), static_cast<double>(control_data.individual_scale));
}
EndArray();

View file

@ -24,7 +24,6 @@ namespace AndroidSettings {
std::pair<double, double> landscape_position;
std::pair<double, double> portrait_position;
std::pair<double, double> foldable_position;
float individual_scale;
};
struct Values {
@ -80,15 +79,6 @@ namespace AndroidSettings {
Settings::Category::Overlay,
Settings::Specialization::Paired, true,
true};
Settings::Setting<bool> enable_input_overlay_auto_hide{linkage, false,
"enable_input_overlay_auto_hide",
Settings::Category::Overlay,
Settings::Specialization::Default, true,
true,};
Settings::Setting<u32> input_overlay_auto_hide{linkage, 5, "input_overlay_auto_hide",
Settings::Category::Overlay,
Settings::Specialization::Default, true, true, &enable_input_overlay_auto_hide};
Settings::Setting<bool> perf_overlay_background{linkage, false, "perf_overlay_background",
Settings::Category::Overlay,
Settings::Specialization::Default, true,

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
@ -369,9 +369,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JN
env->NewObject(Common::Android::GetOverlayControlDataClass(),
Common::Android::GetOverlayControlDataConstructor(),
Common::Android::ToJString(env, control_data.id), control_data.enabled,
jlandscapePosition, jportraitPosition, jfoldablePosition,
control_data.individual_scale);
jlandscapePosition, jportraitPosition, jfoldablePosition);
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
}
return joverlayControlDataArray;
@ -420,12 +418,9 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
env,
env->GetObjectField(jfoldablePosition, Common::Android::GetPairSecondField())));
float individual_scale = static_cast<float>(env->GetFloatField(
joverlayControlData, Common::Android::GetOverlayControlDataIndividualScaleField()));
AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
Common::Android::GetJString(env, jidString), enabled, landscape_position,
portrait_position, foldable_position, individual_scale});
portrait_position, foldable_position});
}
}

View file

@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="320dp"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:cardBackgroundColor="#CC222222">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/scaleValueText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1.0x"
android:textAppearance="?attr/textAppearanceBody1"
android:gravity="center"
android:layout_marginBottom="4dp"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<com.google.android.material.slider.Slider
android:id="@+id/scaleSlider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="0.5"
android:valueTo="4.0"
android:stepSize="0.1"
android:value="1.0"
android:layout_marginBottom="8dp"
app:trackColorActive="@color/eden_border_gradient_start"
app:trackColorInactive="@color/eden_border_gradient_end"
app:tickColor="@color/eden_border_gradient_start"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
<com.google.android.material.button.MaterialButton
android:id="@+id/resetButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:layout_marginEnd="4dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/cancelButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/cancel"
android:layout_marginEnd="4dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/confirmButton"
style="@style/Widget.Material3.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="@color/eden_button_secondary_bg"
android:textColor="@color/eden_border_gradient_end"
android:text="@string/confirm" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View file

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="32dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/spacing_large"
android:paddingRight="@dimen/spacing_large">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_decrement"
style="@style/Widget.Material3.Button.IconButton.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/decrement"
android:text="-" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_input_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/spacing_medlarge"
android:layout_marginRight="@dimen/spacing_medlarge"
android:layout_weight="1"
android:hint="Value">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textAlignment="textStart"
tools:text="0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_increment"
style="@style/Widget.Material3.Button.IconButton.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/increment"
android:text="+" />
</LinearLayout>
</RelativeLayout>

View file

@ -12,24 +12,6 @@
<string name="app_notification_channel_id" translatable="false">Eden</string>
<string name="app_notification_channel_description">Eden Switch emulator notifications</string>
<string name="app_notification_running">Eden is Running</string>
<string name="seconds">Seconds</string>
<!-- Spinbox strings -->
<string name="increment">Increment</string>
<string name="decrement">Decrement</string>
<string name="value">Value</string>
<string name="value_too_low">Value must be at least %1$d</string>
<string name="value_too_high">Value must be at most %1$d</string>
<string name="invalid_value">Invalid value</string>
<!-- Input Overlay -->
<string name="overlay_auto_hide">Overlay Auto Hide</string>
<string name="overlay_auto_hide_description">Automatically hide the touch controls overlay after the specified time of inactivity.</string>
<string name="enable_input_overlay_auto_hide">Enable Overlay Auto Hide</string>
<string name="input_overlay_options">Input Overlay</string>
<string name="input_overlay_options_description">Configure on-screen controls</string>
<!-- Stats Overlay settings -->
@ -873,7 +855,6 @@
<string name="touchscreen">Touchscreen</string>
<string name="lock_drawer">Lock drawer</string>
<string name="unlock_drawer">Unlock drawer</string>
<string name="reset">Reset</string>
<string name="load_settings">Loading settings…</string>
@ -889,7 +870,7 @@
<string name="save_load_error">Save/Load Error</string>
<string name="fatal_error">Fatal Error</string>
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes.</string>
<string name="performance_warning">Turning off this setting will significantly degrade performance. It&quot;s recommended that you leave this setting enabled.</string>
<string name="performance_warning">Turning off this setting will significantly degrade performance. It's recommended that you leave this setting enabled.</string>
<string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
<string name="memory_formatted">%1$s %2$s</string>
<string name="no_game_present">No bootable game present!</string>

View file

@ -1,4 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <jni.h>
@ -46,7 +49,6 @@ static jclass s_overlay_control_data_class;
static jmethodID s_overlay_control_data_constructor;
static jfieldID s_overlay_control_data_id_field;
static jfieldID s_overlay_control_data_enabled_field;
static jfieldID s_overlay_control_data_individual_scale_field;
static jfieldID s_overlay_control_data_landscape_position_field;
static jfieldID s_overlay_control_data_portrait_position_field;
static jfieldID s_overlay_control_data_foldable_position_field;
@ -242,10 +244,6 @@ namespace Common::Android {
return s_overlay_control_data_enabled_field;
}
jfieldID GetOverlayControlDataIndividualScaleField() {
return s_overlay_control_data_individual_scale_field;
}
jfieldID GetOverlayControlDataLandscapePositionField() {
return s_overlay_control_data_landscape_position_field;
}
@ -496,7 +494,7 @@ namespace Common::Android {
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
@ -507,8 +505,6 @@ namespace Common::Android {
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
s_overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");

View file

@ -1,4 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@ -64,7 +67,6 @@ jclass GetOverlayControlDataClass();
jmethodID GetOverlayControlDataConstructor();
jfieldID GetOverlayControlDataIdField();
jfieldID GetOverlayControlDataEnabledField();
jfieldID GetOverlayControlDataIndividualScaleField();
jfieldID GetOverlayControlDataLandscapePositionField();
jfieldID GetOverlayControlDataPortraitPositionField();
jfieldID GetOverlayControlDataFoldablePositionField();

View file

@ -366,10 +366,10 @@ if (APPLE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
if (YUZU_APPLE_USE_BUNDLED_MONTENVK)
if (NOT USE_SYSTEM_MOLTENVK)
set(MOLTENVK_PLATFORM "macOS")
set(MOLTENVK_VERSION "v1.3.0")
download_moltenvk(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
endif()
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")