Compare commits

..

1 Commits

Author SHA1 Message Date
vonchenplus 5d686986e7 Fix crash when exit app 2021-11-16 19:24:09 +08:00
501 changed files with 32472 additions and 59161 deletions
@@ -25,7 +25,7 @@ def check_individual(repo_id, pr_id):
def merge_pr(pn, ref):
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f", "--no-recurse-submodules"]))
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
+1 -1
View File
@@ -25,7 +25,7 @@ def do_page(page):
if (check_individual(pr["labels"])):
pn = pr["number"]
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f", "--no-recurse-submodules"]))
print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
+3 -2
View File
@@ -41,11 +41,12 @@ for i in package/*.exe; do
done
pip3 install pefile
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/"
python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
# copy FFmpeg libraries
EXTERNALS_PATH="$(pwd)/build/externals"
FFMPEG_DLL_PATH="$(find "${EXTERNALS_PATH}" -maxdepth 1 -type d | grep 'ffmpeg-')/bin"
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
# copy libraries from yuzu.exe path
+3 -3
View File
@@ -34,12 +34,12 @@
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
[submodule "ffmpeg"]
path = externals/ffmpeg
url = https://git.ffmpeg.org/ffmpeg.git
[submodule "SDL"]
path = externals/SDL
url = https://github.com/libsdl-org/SDL.git
[submodule "externals/cpp-httplib"]
path = externals/cpp-httplib
url = https://github.com/yhirose/cpp-httplib.git
[submodule "externals/ffmpeg/ffmpeg"]
path = externals/ffmpeg/ffmpeg
url = https://git.ffmpeg.org/ffmpeg.git
+225 -19
View File
@@ -33,10 +33,6 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
option(YUZU_TESTS "Compile tests" ON)
# 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)
@@ -61,7 +57,7 @@ function(check_submodules_present)
string(REGEX REPLACE "path *= *" "" module ${module})
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
message(FATAL_ERROR "Git submodule ${module} not found. "
"Please run: \ngit submodule update --init --recursive")
"Please run: git submodule update --init --recursive")
endif()
endforeach()
endfunction()
@@ -135,7 +131,7 @@ add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
# boost still makes use of deprecated result_of.
# cubeb and boost still make use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
else()
set(CMAKE_CXX_STANDARD 20)
@@ -170,7 +166,8 @@ macro(yuzu_find_packages)
# Capitalization matters here. We need the naming to match the generated paths from Conan
set(REQUIRED_LIBS
# Cmake Pkg Prefix Version Conan Pkg
"fmt 8.0.1 fmt/8.1.1"
"Catch2 2.13 catch2/2.13.0"
"fmt 8.0 fmt/8.0.0"
"lz4 1.8 lz4/1.9.2"
"nlohmann_json 3.8 nlohmann_json/3.8.0"
"ZLIB 1.2 zlib/1.2.11"
@@ -178,11 +175,6 @@ macro(yuzu_find_packages)
# can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068
#"opus 1.3 opus/1.3.1"
)
if (YUZU_TESTS)
list(APPEND REQUIRED_LIBS
"Catch2 2.13.7 catch2/2.13.7"
)
endif()
foreach(PACKAGE ${REQUIRED_LIBS})
string(REGEX REPLACE "[ \t\r\n]+" ";" PACKAGE_SPLIT ${PACKAGE})
@@ -237,7 +229,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
else()
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
endif()
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
@@ -257,7 +249,7 @@ if(ENABLE_QT)
# Check for system Qt on Linux, fallback to bundled Qt
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets)
endif()
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
# Check for dependencies, then enable bundled Qt download
@@ -378,7 +370,7 @@ if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.18")
set(SDL2_VER "SDL2-2.0.16")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
endif()
@@ -398,7 +390,7 @@ if (ENABLE_SDL2)
elseif (YUZU_USE_EXTERNAL_SDL2)
message(STATUS "Using SDL2 from externals.")
else()
find_package(SDL2 2.0.18 REQUIRED)
find_package(SDL2 2.0.16 REQUIRED)
# Some installations don't set SDL2_LIBRARIES
if("${SDL2_LIBRARIES}" STREQUAL "")
@@ -516,13 +508,13 @@ set(FFmpeg_COMPONENTS
avutil
swscale)
if (UNIX AND NOT APPLE)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
endif()
if (NOT YUZU_USE_BUNDLED_FFMPEG)
# Use system installed FFmpeg
find_package(FFmpeg 4.3 QUIET COMPONENTS ${FFmpeg_COMPONENTS})
find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS})
if (FFmpeg_FOUND)
# Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
@@ -535,11 +527,225 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE)
endforeach()
else()
message(WARNING "FFmpeg not found or too old, falling back to externals")
message(WARNING "FFmpeg not found, falling back to externals")
set(YUZU_USE_BUNDLED_FFMPEG ON)
endif()
endif()
if (YUZU_USE_BUNDLED_FFMPEG)
if (NOT WIN32)
# TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to
# externals/ffmpeg/ffmpeg)
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
# FFmpeg has source that requires one of nasm or yasm to assemble it.
# REQUIRED throws an error if not found here during configuration rather than during compilation.
find_program(ASSEMBLER NAMES nasm yasm)
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
endif()
find_program(AUTOCONF autoconf)
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
message(FATAL_ERROR "Required program `autoconf` not found.")
endif()
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg)
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg)
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
make_directory(${FFmpeg_BUILD_DIR})
# Read version string from external
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
set(FFmpeg_FOUND NO)
if (NOT FFmpeg_VERSION STREQUAL "")
set(FFmpeg_FOUND YES)
endif()
unset(FFmpeg_LIBRARIES CACHE)
foreach(COMPONENT ${FFmpeg_COMPONENTS})
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARIES}
${FFmpeg_${COMPONENT}_LIBRARY}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach()
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS)
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
set(FFmpeg_HWACCEL_LDFLAGS)
if(LIBVA_FOUND)
pkg_check_modules(LIBDRM libdrm REQUIRED)
find_package(X11 REQUIRED)
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES}
${X11_LIBRARIES}
${LIBVA-DRM_LIBRARIES}
${LIBVA-X11_LIBRARIES}
${LIBVA_LIBRARIES})
set(FFmpeg_HWACCEL_FLAGS
--enable-hwaccel=h264_vaapi
--enable-hwaccel=vp8_vaapi
--enable-hwaccel=vp9_vaapi
--enable-libdrm)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS}
${X11_INCLUDE_DIRS}
${LIBVA-DRM_INCLUDE_DIRS}
${LIBVA-X11_INCLUDE_DIRS}
${LIBVA_INCLUDE_DIRS}
)
message(STATUS "VA-API found")
else()
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
endif()
if (FFNVCODEC_FOUND AND CUDA_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-cuvid
--enable-ffnvcodec
--enable-nvdec
--enable-hwaccel=h264_nvdec
--enable-hwaccel=vp8_nvdec
--enable-hwaccel=vp9_nvdec
--extra-cflags=-I${CUDA_INCLUDE_DIRS}
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${FFNVCODEC_LIBRARIES}
${CUDA_LIBRARIES}
)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${FFNVCODEC_INCLUDE_DIRS}
${CUDA_INCLUDE_DIRS}
)
list(APPEND FFmpeg_HWACCEL_LDFLAGS
${FFNVCODEC_LDFLAGS}
${CUDA_LDFLAGS}
)
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
endif()
if (VDPAU_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-vdpau
--enable-hwaccel=h264_vdpau
--enable-hwaccel=vp9_vdpau
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
else()
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
add_custom_command(
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avfilter
--disable-avformat
--disable-doc
--disable-everything
--disable-ffmpeg
--disable-ffprobe
--disable-network
--disable-postproc
--disable-swresample
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
--cc="${CMAKE_C_COMPILER}"
--cxx="${CMAKE_CXX_COMPILER}"
${FFmpeg_HWACCEL_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_HWACCEL_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
set(FFmpeg_INCLUDE_DIR
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LDFLAGS
"${FFmpeg_HWACCEL_LDFLAGS}"
CACHE STRING "FFmpeg linker flags" FORCE)
# ALL makes this custom target build every time
# but it won't actually build if the DEPENDS parameter is up to date
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
unset(FFmpeg_BUILD_LIBRARIES)
unset(FFmpeg_HWACCEL_FLAGS)
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
unset(FFmpeg_HWACCEL_LDFLAGS)
unset(FFmpeg_HWACCEL_LIBRARIES)
if (FFmpeg_FOUND)
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else() # WIN32
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib
${FFmpeg_LIBRARY_DIR}/avutil.lib
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endif()
endif()
unset(FFmpeg_COMPONENTS)
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
+3 -9
View File
@@ -11,15 +11,9 @@ find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
# generate git/build information
include(GetGitRevisionDescription)
if(NOT GIT_REF_SPEC)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
endif()
if(NOT GIT_DESC)
git_describe(GIT_DESC --always --long --dirty)
endif()
if (NOT GIT_BRANCH)
git_branch_name(GIT_BRANCH)
endif()
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE)
# Generate cpp with Git revision from template
+1 -1
View File
@@ -17,7 +17,7 @@ It is written in C++ with portability in mind, and we actively maintain builds f
alt="Azure Mainline CI Build Status">
</a>
<a href="https://discord.com/invite/u77vRWY">
<img src="https://img.shields.io/discord/398318088170242053?color=5865F2&label=yuzu&logo=discord&logoColor=white"
<img src="https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white"
alt="Discord">
</a>
</p>
+1454 -1762
View File
File diff suppressed because it is too large Load Diff
+842 -1117
View File
File diff suppressed because it is too large Load Diff
+829 -1104
View File
File diff suppressed because it is too large Load Diff
+843 -1118
View File
File diff suppressed because it is too large Load Diff
+1472 -1748
View File
File diff suppressed because it is too large Load Diff
+872 -1147
View File
File diff suppressed because it is too large Load Diff
+894 -1177
View File
File diff suppressed because it is too large Load Diff
+849 -1124
View File
File diff suppressed because it is too large Load Diff
+851 -1126
View File
File diff suppressed because it is too large Load Diff
+826 -1101
View File
File diff suppressed because it is too large Load Diff
+834 -1115
View File
File diff suppressed because it is too large Load Diff
+833 -1108
View File
File diff suppressed because it is too large Load Diff
+909 -1190
View File
File diff suppressed because it is too large Load Diff
+833 -1108
View File
File diff suppressed because it is too large Load Diff
+905 -1180
View File
File diff suppressed because it is too large Load Diff
+833 -1108
View File
File diff suppressed because it is too large Load Diff
+909 -1187
View File
File diff suppressed because it is too large Load Diff
-6085
View File
File diff suppressed because it is too large Load Diff
+847 -1122
View File
File diff suppressed because it is too large Load Diff
+930 -1213
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,5 +8,5 @@ Icon=yuzu
TryExec=yuzu
Exec=yuzu %f
Categories=Game;Emulator;Qt;
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
MimeType=application/x-nx-nro;application/x-nx-nso;
Keywords=Switch;Nintendo;
-15
View File
@@ -15,19 +15,4 @@
<glob pattern="*.nso"/>
<magic><match value="NSO" type="string" offset="0"/></magic>
</mime-type>
<mime-type type="application/x-nx-nsp">
<comment>Nintendo Switch Package</comment>
<acronym>NSP</acronym>
<icon name="yuzu"/>
<glob pattern="*.nsp"/>
<magic><match value="PFS" type="string" offset="0"/></magic>
</mime-type>
<mime-type type="application/x-nx-xci">
<comment>Nintendo Switch Card Image</comment>
<acronym>XCI</acronym>
<icon name="yuzu"/>
<glob pattern="*.xci"/>
</mime-type>
</mime-info>
+14 -16
View File
@@ -13,6 +13,10 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif()
# Catch
add_library(catch-single-include INTERFACE)
target_include_directories(catch-single-include INTERFACE catch/single_include)
# Dynarmic
if (ARCHITECTURE_x86_64)
set(DYNARMIC_TESTS OFF)
@@ -40,6 +44,10 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)
# Unicorn
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# libusb
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
add_subdirectory(libusb)
@@ -48,12 +56,11 @@ endif()
# SDL2
if (YUZU_USE_EXTERNAL_SDL2)
if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
set(SDL_UNUSED_SUBSYSTEMS
CPUinfo File Filesystem
Locale Power Render)
Atomic Render Power Threads
File CPUinfo Filesystem Locale)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
option(SDL_${_OPT} "" OFF)
@@ -113,17 +120,8 @@ if (ENABLE_WEB_SERVICE)
endif()
# Opus
if (YUZU_USE_BUNDLED_OPUS)
find_package(opus 1.3)
if (NOT opus_FOUND)
message(STATUS "opus 1.3 or newer not found, falling back to externals")
add_subdirectory(opus EXCLUDE_FROM_ALL)
else()
find_package(opus 1.3 REQUIRED)
endif()
# FFMpeg
if (YUZU_USE_BUNDLED_FFMPEG)
add_subdirectory(ffmpeg)
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
endif()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+1 -1
Vendored Submodule
+1
Submodule externals/ffmpeg added at 79e8d17024
-214
View File
@@ -1,214 +0,0 @@
if (NOT WIN32)
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
# FFmpeg has source that requires one of nasm or yasm to assemble it.
# REQUIRED throws an error if not found here during configuration rather than during compilation.
find_program(ASSEMBLER NAMES nasm yasm)
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
endif()
endif()
find_program(AUTOCONF autoconf)
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
message(FATAL_ERROR "Required program `autoconf` not found.")
endif()
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg/ffmpeg)
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg-build)
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
make_directory(${FFmpeg_BUILD_DIR})
# Read version string from external
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
set(FFmpeg_FOUND NO)
if (NOT FFmpeg_VERSION STREQUAL "")
set(FFmpeg_FOUND YES)
endif()
unset(FFmpeg_LIBRARIES CACHE)
foreach(COMPONENT ${FFmpeg_COMPONENTS})
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARIES}
${FFmpeg_${COMPONENT}_LIBRARY}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach()
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS)
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
set(FFmpeg_HWACCEL_LDFLAGS)
if(LIBVA_FOUND)
pkg_check_modules(LIBDRM libdrm REQUIRED)
find_package(X11 REQUIRED)
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES}
${X11_LIBRARIES}
${LIBVA-DRM_LIBRARIES}
${LIBVA-X11_LIBRARIES}
${LIBVA_LIBRARIES})
set(FFmpeg_HWACCEL_FLAGS
--enable-hwaccel=h264_vaapi
--enable-hwaccel=vp8_vaapi
--enable-hwaccel=vp9_vaapi
--enable-libdrm)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS}
${X11_INCLUDE_DIRS}
${LIBVA-DRM_INCLUDE_DIRS}
${LIBVA-X11_INCLUDE_DIRS}
${LIBVA_INCLUDE_DIRS}
)
message(STATUS "VA-API found")
else()
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
endif()
if (FFNVCODEC_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-cuvid
--enable-ffnvcodec
--enable-nvdec
--enable-hwaccel=h264_nvdec
--enable-hwaccel=vp8_nvdec
--enable-hwaccel=vp9_nvdec
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS})
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
# ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
# here we handle the hard-linking senario where CUDA is linked during compilation
if (CUDA_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
message(STATUS "CUDA libraries found, hard-linking will be performed")
endif(CUDA_FOUND)
endif()
if (VDPAU_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-vdpau
--enable-hwaccel=h264_vdpau
--enable-hwaccel=vp9_vdpau
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
else()
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
add_custom_command(
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avfilter
--disable-avformat
--disable-doc
--disable-everything
--disable-ffmpeg
--disable-ffprobe
--disable-network
--disable-postproc
--disable-swresample
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
--cc="${CMAKE_C_COMPILER}"
--cxx="${CMAKE_CXX_COMPILER}"
${FFmpeg_HWACCEL_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_HWACCEL_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
set(FFmpeg_INCLUDE_DIR
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LDFLAGS
"${FFmpeg_HWACCEL_LDFLAGS}"
CACHE STRING "FFmpeg linker flags" FORCE)
# ALL makes this custom target build every time
# but it won't actually build if the DEPENDS parameter is up to date
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
unset(FFmpeg_BUILD_LIBRARIES)
unset(FFmpeg_HWACCEL_FLAGS)
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
unset(FFmpeg_HWACCEL_LDFLAGS)
unset(FFmpeg_HWACCEL_LIBRARIES)
if (FFmpeg_FOUND)
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else(WIN32)
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib
${FFmpeg_LIBRARY_DIR}/avutil.lib
CACHE PATH "Paths to FFmpeg libraries" FORCE)
# exported variables
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
endif(WIN32)
unset(FFmpeg_COMPONENTS)
+18
View File
@@ -0,0 +1,18 @@
# Exports:
# LIBUNICORN_FOUND
# LIBUNICORN_INCLUDE_DIR
# LIBUNICORN_LIBRARY
find_path(LIBUNICORN_INCLUDE_DIR
unicorn/unicorn.h
HINTS $ENV{UNICORNDIR}
PATH_SUFFIXES include)
find_library(LIBUNICORN_LIBRARY
NAMES unicorn
HINTS $ENV{UNICORNDIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(unicorn DEFAULT_MSG
LIBUNICORN_LIBRARY LIBUNICORN_INCLUDE_DIR)
mark_as_advanced(LIBUNICORN_INCLUDE_DIR LIBUNICORN_LIBRARY)
+1 -6
View File
@@ -24,7 +24,6 @@ if (MSVC)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zm - Specifies the precompiled header memory allocation limit
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
@@ -37,7 +36,6 @@ if (MSVC)
add_compile_options(
/MP
/Zi
/Zm200
/Zo
/permissive-
/EHsc
@@ -151,10 +149,7 @@ add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(input_common)
add_subdirectory(shader_recompiler)
if (YUZU_TESTS)
add_subdirectory(tests)
endif()
add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(yuzu_cmd)
-4
View File
@@ -1,7 +1,3 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "audio_core/delay_line.h"
-4
View File
@@ -1,7 +1,3 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
-7
View File
@@ -22,11 +22,6 @@ add_custom_command(OUTPUT scm_rev.cpp
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
-DBUILD_TAG=${BUILD_TAG}
-DBUILD_ID=${DISPLAY_VERSION}
-DGIT_REF_SPEC=${GIT_REF_SPEC}
-DGIT_REV=${GIT_REV}
-DGIT_DESC=${GIT_DESC}
-DGIT_BRANCH=${GIT_BRANCH}
-DBUILD_FULLNAME=${BUILD_FULLNAME}
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
DEPENDS
@@ -78,14 +73,12 @@ add_library(common STATIC
hex_util.h
host_memory.cpp
host_memory.h
input.h
intrusive_red_black_tree.h
literals.h
logging/backend.cpp
logging/backend.h
logging/filter.cpp
logging/filter.h
logging/formatter.h
logging/log.h
logging/log_entry.h
logging/text_formatter.cpp
-13
View File
@@ -7,7 +7,6 @@
#include <bit>
#include <climits>
#include <cstddef>
#include <type_traits>
#include "common/common_types.h"
@@ -45,16 +44,4 @@ template <typename T>
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
}
template <typename T>
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool IsPow2(T value) {
return std::has_single_bit(value);
}
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] T NextPow2(T value) {
return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
}
} // namespace Common
+1 -4
View File
@@ -124,10 +124,7 @@ void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
// "from" might no longer be valid if the thread was killed
if (auto from = weak_from.lock()) {
if (from->impl->previous_fiber == nullptr) {
ASSERT_MSG(false, "previous_fiber is nullptr!");
return;
}
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
-4
View File
@@ -1,7 +1,3 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#ifdef _WIN32
#include <iterator>
-373
View File
@@ -1,373 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/uuid.h"
namespace Common::Input {
// Type of data that is expected to recieve or send
enum class InputType {
None,
Battery,
Button,
Stick,
Analog,
Trigger,
Motion,
Touch,
Color,
Vibration,
Nfc,
Ir,
};
// Internal battery charge level
enum class BatteryLevel : u32 {
None,
Empty,
Critical,
Low,
Medium,
Full,
Charging,
};
enum class PollingMode {
// Constant polling of buttons, analogs and motion data
Active,
// Only update on button change, digital analogs
Pasive,
// Enable near field communication polling
NFC,
// Enable infrared camera polling
IR,
};
// Vibration reply from the controller
enum class VibrationError {
None,
NotSupported,
Disabled,
Unknown,
};
// Polling mode reply from the controller
enum class PollingError {
None,
NotSupported,
Unknown,
};
// Hint for amplification curve to be used
enum class VibrationAmplificationType {
Linear,
Exponential,
};
// Analog properties for calibration
struct AnalogProperties {
// Anything below this value will be detected as zero
float deadzone{};
// Anyting above this values will be detected as one
float range{1.0f};
// Minimum value to be detected as active
float threshold{0.5f};
// Drift correction applied to the raw data
float offset{};
// Invert direction of the sensor data
bool inverted{};
};
// Single analog sensor data
struct AnalogStatus {
float value{};
float raw_value{};
AnalogProperties properties{};
};
// Button data
struct ButtonStatus {
Common::UUID uuid{};
bool value{};
bool inverted{};
bool toggle{};
bool locked{};
};
// Internal battery data
using BatteryStatus = BatteryLevel;
// Analog and digital joystick data
struct StickStatus {
Common::UUID uuid{};
AnalogStatus x{};
AnalogStatus y{};
bool left{};
bool right{};
bool up{};
bool down{};
};
// Analog and digital trigger data
struct TriggerStatus {
Common::UUID uuid{};
AnalogStatus analog{};
ButtonStatus pressed{};
};
// 3D vector representing motion input
struct MotionSensor {
AnalogStatus x{};
AnalogStatus y{};
AnalogStatus z{};
};
// Motion data used to calculate controller orientation
struct MotionStatus {
// Gyroscope vector measurement in radians/s.
MotionSensor gyro{};
// Acceleration vector measurement in G force
MotionSensor accel{};
// Time since last measurement in microseconds
u64 delta_timestamp{};
// Request to update after reading the value
bool force_update{};
};
// Data of a single point on a touch screen
struct TouchStatus {
ButtonStatus pressed{};
AnalogStatus x{};
AnalogStatus y{};
int id{};
};
// Physical controller color in RGB format
struct BodyColorStatus {
u32 body{};
u32 buttons{};
};
// HD rumble data
struct VibrationStatus {
f32 low_amplitude{};
f32 low_frequency{};
f32 high_amplitude{};
f32 high_frequency{};
VibrationAmplificationType type;
};
// Physical controller LED pattern
struct LedStatus {
bool led_1{};
bool led_2{};
bool led_3{};
bool led_4{};
};
// List of buttons to be passed to Qt that can be translated
enum class ButtonNames {
Undefined,
Invalid,
// This will display the engine name instead of the button name
Engine,
// This will display the button by value instead of the button name
Value,
ButtonLeft,
ButtonRight,
ButtonDown,
ButtonUp,
TriggerZ,
TriggerR,
TriggerL,
ButtonA,
ButtonB,
ButtonX,
ButtonY,
ButtonStart,
// DS4 button names
L1,
L2,
L3,
R1,
R2,
R3,
Circle,
Cross,
Square,
Triangle,
Share,
Options,
// Mouse buttons
ButtonMouseWheel,
ButtonBackward,
ButtonForward,
ButtonTask,
ButtonExtra,
};
// Callback data consisting of an input type and the equivalent data status
struct CallbackStatus {
InputType type{InputType::None};
ButtonStatus button_status{};
StickStatus stick_status{};
AnalogStatus analog_status{};
TriggerStatus trigger_status{};
MotionStatus motion_status{};
TouchStatus touch_status{};
BodyColorStatus color_status{};
BatteryStatus battery_status{};
VibrationStatus vibration_status{};
};
// Triggered once every input change
struct InputCallback {
std::function<void(const CallbackStatus&)> on_change;
};
/// An abstract class template for an input device (a button, an analog input, etc.).
class InputDevice {
public:
virtual ~InputDevice() = default;
// Request input device to update if necessary
virtual void SoftUpdate() {}
// Force input device to update data regardless of the current state
virtual void ForceUpdate() {}
// Sets the function to be triggered when input changes
void SetCallback(InputCallback callback_) {
callback = std::move(callback_);
}
// Triggers the function set in the callback
void TriggerOnChange(const CallbackStatus& status) {
if (callback.on_change) {
callback.on_change(status);
}
}
private:
InputCallback callback;
};
/// An abstract class template for an output device (rumble, LED pattern, polling mode).
class OutputDevice {
public:
virtual ~OutputDevice() = default;
virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {}
virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
return VibrationError::NotSupported;
}
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
return PollingError::NotSupported;
}
};
/// An abstract class template for a factory that can create input devices.
template <typename InputDeviceType>
class Factory {
public:
virtual ~Factory() = default;
virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
};
namespace Impl {
template <typename InputDeviceType>
using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
template <typename InputDeviceType>
struct FactoryList {
static FactoryListType<InputDeviceType> list;
};
template <typename InputDeviceType>
FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
} // namespace Impl
/**
* Registers an input device factory.
* @tparam InputDeviceType the type of input devices the factory can create
* @param name the name of the factory. Will be used to match the "engine" parameter when creating
* a device
* @param factory the factory object to register
*/
template <typename InputDeviceType>
void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
auto pair = std::make_pair(name, std::move(factory));
if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
LOG_ERROR(Input, "Factory '{}' already registered", name);
}
}
/**
* Unregisters an input device factory.
* @tparam InputDeviceType the type of input devices the factory can create
* @param name the name of the factory to unregister
*/
template <typename InputDeviceType>
void UnregisterFactory(const std::string& name) {
if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
LOG_ERROR(Input, "Factory '{}' not registered", name);
}
}
/**
* Create an input device from given paramters.
* @tparam InputDeviceType the type of input devices to create
* @param params a serialized ParamPackage string that contains all parameters for creating the
* device
*/
template <typename InputDeviceType>
std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& params) {
const Common::ParamPackage package(params);
const std::string engine = package.Get("engine", "null");
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
const auto pair = factory_list.find(engine);
if (pair == factory_list.end()) {
if (engine != "null") {
LOG_ERROR(Input, "Unknown engine name: {}", engine);
}
return std::make_unique<InputDeviceType>();
}
return pair->second->Create(package);
}
/**
* Create an input device from given paramters.
* @tparam InputDeviceType the type of input devices to create
* @param A ParamPackage that contains all parameters for creating the device
*/
template <typename InputDeviceType>
std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) {
const std::string engine = package.Get("engine", "null");
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
const auto pair = factory_list.find(engine);
if (pair == factory_list.end()) {
if (engine != "null") {
LOG_ERROR(Input, "Unknown engine name: {}", engine);
}
return std::make_unique<InputDeviceType>();
}
return pair->second->Create(package);
}
} // namespace Common::Input
-1
View File
@@ -114,7 +114,6 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NGCT) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NOTIF) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
-23
View File
@@ -1,23 +0,0 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <type_traits>
#include <fmt/format.h>
// adapted from https://github.com/fmtlib/fmt/issues/2704
// a generic formatter for enum classes
#if FMT_VERSION >= 80100
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
: formatter<std::underlying_type_t<T>> {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
return fmt::formatter<std::underlying_type_t<T>>::format(
static_cast<std::underlying_type_t<T>>(value), ctx);
}
};
#endif
+1 -2
View File
@@ -7,9 +7,8 @@
#include <algorithm>
#include <string_view>
#include <fmt/format.h>
#include <fmt/core.h>
#include "common/logging/formatter.h"
#include "common/logging/types.h"
namespace Common::Log {
-1
View File
@@ -82,7 +82,6 @@ enum class Class : u8 {
Service_NGCT, ///< The NGCT (No Good Content for Terra) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NOTIF, ///< The NOTIF (Notification) service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
+2 -2
View File
@@ -48,8 +48,8 @@ struct Rectangle {
}
[[nodiscard]] Rectangle<T> Scale(const float s) const {
return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
}
};
+1 -53
View File
@@ -47,9 +47,7 @@ void LogSettings() {
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
@@ -107,55 +105,6 @@ float Volume() {
return values.volume.GetValue() / 100.0f;
}
void UpdateRescalingInfo() {
const auto setup = values.resolution_setup.GetValue();
auto& info = values.resolution_info;
info.downscale = false;
switch (setup) {
case ResolutionSetup::Res1_2X:
info.up_scale = 1;
info.down_shift = 1;
info.downscale = true;
break;
case ResolutionSetup::Res3_4X:
info.up_scale = 3;
info.down_shift = 2;
info.downscale = true;
break;
case ResolutionSetup::Res1X:
info.up_scale = 1;
info.down_shift = 0;
break;
case ResolutionSetup::Res2X:
info.up_scale = 2;
info.down_shift = 0;
break;
case ResolutionSetup::Res3X:
info.up_scale = 3;
info.down_shift = 0;
break;
case ResolutionSetup::Res4X:
info.up_scale = 4;
info.down_shift = 0;
break;
case ResolutionSetup::Res5X:
info.up_scale = 5;
info.down_shift = 0;
break;
case ResolutionSetup::Res6X:
info.up_scale = 6;
info.down_shift = 0;
break;
default:
UNREACHABLE();
info.up_scale = 1;
info.down_shift = 0;
}
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
info.active = info.up_scale != 1 || info.down_shift != 0;
}
void RestoreGlobalState(bool is_powered_on) {
// If a game is running, DO NOT restore the global settings state
if (is_powered_on) {
@@ -183,7 +132,6 @@ void RestoreGlobalState(bool is_powered_on) {
values.max_anisotropy.SetGlobal(true);
values.use_speed_limit.SetGlobal(true);
values.speed_limit.SetGlobal(true);
values.fps_cap.SetGlobal(true);
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
+14 -61
View File
@@ -6,6 +6,7 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <map>
#include <optional>
#include <string>
@@ -51,56 +52,6 @@ enum class NvdecEmulation : u32 {
GPU = 2,
};
enum class ResolutionSetup : u32 {
Res1_2X = 0,
Res3_4X = 1,
Res1X = 2,
Res2X = 3,
Res3X = 4,
Res4X = 5,
Res5X = 6,
Res6X = 7,
};
enum class ScalingFilter : u32 {
NearestNeighbor = 0,
Bilinear = 1,
Bicubic = 2,
Gaussian = 3,
ScaleForce = 4,
Fsr = 5,
LastFilter = Fsr,
};
enum class AntiAliasing : u32 {
None = 0,
Fxaa = 1,
LastAA = Fxaa,
};
struct ResolutionScalingInfo {
u32 up_scale{1};
u32 down_shift{0};
f32 up_factor{1.0f};
f32 down_factor{1.0f};
bool active{};
bool downscale{};
s32 ScaleUp(s32 value) const {
if (value == 0) {
return 0;
}
return std::max((value * static_cast<s32>(up_scale)) >> static_cast<s32>(down_shift), 1);
}
u32 ScaleUp(u32 value) const {
if (value == 0U) {
return 0U;
}
return std::max((value * up_scale) >> down_shift, 1U);
}
};
/** The BasicSetting class is a simple resource manager. It defines a label and default value
* alongside the actual value of the setting for simpler and less-error prone use with frontend
* configurations. Setting a default value and label is required, though subclasses may deviate from
@@ -500,10 +451,7 @@ struct Values {
"disable_shader_loop_safety_checks"};
Setting<int> vulkan_device{0, "vulkan_device"};
ResolutionScalingInfo resolution_info{};
Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
Setting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
Setting<u16> resolution_factor{1, "resolution_factor"};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
RangedSetting<FullscreenMode> fullscreen_mode{
@@ -514,7 +462,7 @@ struct Values {
#endif
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
RangedSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"};
RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"};
Setting<bool> use_speed_limit{true, "use_speed_limit"};
RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
@@ -524,7 +472,7 @@ struct Values {
Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
Setting<bool> accelerate_astc{true, "accelerate_astc"};
Setting<bool> use_vsync{true, "use_vsync"};
RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
@@ -559,19 +507,25 @@ struct Values {
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
Setting<bool> motion_enabled{true, "motion_enabled"};
BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
"motion_device"};
BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"};
BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
BasicSetting<bool> tas_enable{false, "tas_enable"};
BasicSetting<bool> tas_loop{false, "tas_loop"};
BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
BasicSetting<bool> mouse_panning{false, "mouse_panning"};
BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"};
KeyboardKeysRaw keyboard_keys;
KeyboardModsRaw keyboard_mods;
BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
ButtonsRaw debug_pad_buttons;
@@ -579,11 +533,14 @@ struct Values {
TouchscreenInput touchscreen;
BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"};
BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850",
"touch_device"};
BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
std::vector<TouchFromButtonMap> touch_from_button_maps;
std::atomic_bool is_device_reload_pending{true};
// Data Storage
BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
@@ -597,7 +554,6 @@ struct Values {
BasicSetting<std::string> program_args{std::string(), "program_args"};
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
BasicSetting<bool> dump_nso{false, "dump_nso"};
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
BasicSetting<bool> reporting_services{false, "reporting_services"};
BasicSetting<bool> quest_flag{false, "quest_flag"};
@@ -605,7 +561,6 @@ struct Values {
BasicSetting<bool> extended_logging{false, "extended_logging"};
BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"};
// Miscellaneous
BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
@@ -640,8 +595,6 @@ std::string GetTimeZoneString();
void LogSettings();
void UpdateRescalingInfo();
// Restore the global state of all applicable settings in the Values struct
void RestoreGlobalState(bool is_powered_on);
+19 -51
View File
@@ -62,22 +62,11 @@ enum Values : int {
constexpr int STICK_HID_BEGIN = LStick;
constexpr int STICK_HID_END = NumAnalogs;
constexpr int NUM_STICKS_HID = NumAnalogs;
extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
namespace NativeTrigger {
enum Values : int {
LTrigger,
RTrigger,
NumTriggers,
};
constexpr int TRIGGER_HID_BEGIN = LTrigger;
constexpr int TRIGGER_HID_END = NumTriggers;
} // namespace NativeTrigger
namespace NativeVibration {
enum Values : int {
LeftVibrationDevice,
@@ -126,20 +115,10 @@ constexpr int NUM_MOUSE_HID = NumMouseButtons;
extern const std::array<const char*, NumMouseButtons> mapping;
} // namespace NativeMouseButton
namespace NativeMouseWheel {
enum Values {
X,
Y,
NumMouseWheels,
};
extern const std::array<const char*, NumMouseWheels> mapping;
} // namespace NativeMouseWheel
namespace NativeKeyboard {
enum Keys {
None,
Error,
A = 4,
B,
@@ -177,22 +156,22 @@ enum Keys {
N8,
N9,
N0,
Return,
Enter,
Escape,
Backspace,
Tab,
Space,
Minus,
Plus,
OpenBracket,
CloseBracket,
Pipe,
Equal,
LeftBrace,
RightBrace,
Backslash,
Tilde,
Semicolon,
Quote,
Backquote,
Apostrophe,
Grave,
Comma,
Period,
Dot,
Slash,
CapsLockKey,
@@ -209,7 +188,7 @@ enum Keys {
F11,
F12,
PrintScreen,
SystemRequest,
ScrollLockKey,
Pause,
Insert,
@@ -278,18 +257,8 @@ enum Keys {
ScrollLockActive,
KPComma,
Ro = 0x87,
KatakanaHiragana,
Yen,
Henkan,
Muhenkan,
NumPadCommaPc98,
HangulEnglish = 0x90,
Hanja,
KatakanaKey,
HiraganaKey,
ZenkakuHankaku,
KPLeftParenthesis,
KPRightParenthesis,
LeftControlKey = 0xE0,
LeftShiftKey,
@@ -338,8 +307,6 @@ enum Modifiers {
CapsLock,
ScrollLock,
NumLock,
Katakana,
Hiragana,
NumKeyboardMods,
};
@@ -357,6 +324,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
@@ -370,11 +342,6 @@ enum class ControllerType {
RightJoycon,
Handheld,
GameCube,
Pokeball,
NES,
SNES,
N64,
SegaGenesis,
};
struct PlayerInput {
@@ -382,6 +349,7 @@ struct PlayerInput {
ControllerType controller_type;
ButtonsRaw buttons;
AnalogsRaw analogs;
VibrationsRaw vibrations;
MotionsRaw motions;
bool vibration_enabled;
+12
View File
@@ -71,6 +71,9 @@ static CPUCaps Detect() {
else
caps.manufacturer = Manufacturer::Unknown;
u32 family = {};
u32 model = {};
__cpuid(cpu_id, 0x80000000);
u32 max_ex_fn = cpu_id[0];
@@ -81,6 +84,15 @@ static CPUCaps Detect() {
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
family = (cpu_id[0] >> 8) & 0xf;
model = (cpu_id[0] >> 4) & 0xf;
if (family == 0xf) {
family += (cpu_id[0] >> 20) & 0xff;
}
if (family >= 6) {
model += ((cpu_id[0] >> 16) & 0xf) << 4;
}
if ((cpu_id[3] >> 25) & 1)
caps.sse = true;
if ((cpu_id[3] >> 26) & 1)
+18 -18
View File
@@ -15,26 +15,26 @@
namespace Common {
u64 EstimateRDTSCFrequency() {
// Discard the first result measuring the rdtsc.
const auto milli_10 = std::chrono::milliseconds{10};
// get current time
_mm_mfence();
__rdtsc();
std::this_thread::sleep_for(std::chrono::milliseconds{1});
const u64 tscStart = __rdtsc();
const auto startTime = std::chrono::high_resolution_clock::now();
// wait roughly 3 seconds
while (true) {
auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - startTime);
if (milli.count() >= 3000)
break;
std::this_thread::sleep_for(milli_10);
}
const auto endTime = std::chrono::high_resolution_clock::now();
_mm_mfence();
__rdtsc();
// Get the current time.
const auto start_time = std::chrono::steady_clock::now();
_mm_mfence();
const u64 tsc_start = __rdtsc();
// Wait for 200 milliseconds.
std::this_thread::sleep_for(std::chrono::milliseconds{200});
const auto end_time = std::chrono::steady_clock::now();
_mm_mfence();
const u64 tsc_end = __rdtsc();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
const u64 tsc_diff = tsc_end - tsc_start;
const u64 tscEnd = __rdtsc();
// calculate difference
const u64 timer_diff =
std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
const u64 tsc_diff = tscEnd - tscStart;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
return tsc_freq;
}
+5 -29
View File
@@ -132,23 +132,11 @@ add_library(core STATIC
frontend/emu_window.h
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/input_interpreter.cpp
frontend/input_interpreter.h
frontend/input.h
hardware_interrupt_manager.cpp
hardware_interrupt_manager.h
hid/emulated_console.cpp
hid/emulated_console.h
hid/emulated_controller.cpp
hid/emulated_controller.h
hid/emulated_devices.cpp
hid/emulated_devices.h
hid/hid_core.cpp
hid/hid_core.h
hid/hid_types.h
hid/input_converter.cpp
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
hle/ipc.h
hle/ipc_helpers.h
@@ -179,17 +167,12 @@ add_library(core STATIC
hle/kernel/k_client_port.h
hle/kernel/k_client_session.cpp
hle/kernel/k_client_session.h
hle/kernel/k_code_memory.cpp
hle/kernel/k_code_memory.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
hle/kernel/k_event.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
hle/kernel/k_interrupt_manager.cpp
hle/kernel/k_interrupt_manager.h
hle/kernel/k_light_condition_variable.cpp
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
@@ -242,14 +225,10 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
hle/kernel/k_transfer_memory.cpp
hle/kernel/k_transfer_memory.h
hle/kernel/k_worker_task.h
hle/kernel/k_worker_task_manager.cpp
hle/kernel/k_worker_task_manager.h
hle/kernel/k_writable_event.cpp
hle/kernel/k_writable_event.h
hle/kernel/kernel.cpp
@@ -270,6 +249,8 @@ add_library(core STATIC
hle/kernel/svc_wrap.h
hle/kernel/time_manager.cpp
hle/kernel/time_manager.h
hle/lock.cpp
hle/lock.h
hle/result.h
hle/service/acc/acc.cpp
hle/service/acc/acc.h
@@ -415,15 +396,12 @@ add_library(core STATIC
hle/service/glue/glue.h
hle/service/glue/glue_manager.cpp
hle/service/glue/glue_manager.h
hle/service/glue/notif.cpp
hle/service/glue/notif.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/errors.h
@@ -488,8 +466,6 @@ add_library(core STATIC
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pdm_qry.cpp
hle/service/ns/pdm_qry.h
hle/service/ns/pl_u.cpp
hle/service/ns/pl_u.h
hle/service/nvdrv/devices/nvdevice.h
-20
View File
@@ -86,26 +86,6 @@ public:
num_instructions, MemoryReadCode(pc));
}
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
VAddr value) override {
switch (op) {
case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
static constexpr u64 ICACHE_LINE_SIZE = 64;
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
break;
}
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
parent.ClearInstructionCache();
break;
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
default:
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
break;
}
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
switch (exception) {
case Dynarmic::A64::Exception::WaitForInterrupt:
+9 -13
View File
@@ -27,7 +27,6 @@
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/hardware_interrupt_manager.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
@@ -127,7 +126,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system}, hid_core{},
: kernel{system}, fs_controller{system}, memory{system},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
SystemResultStatus Run() {
@@ -317,8 +316,6 @@ struct System::Impl {
is_powered_on = false;
exit_lock = false;
gpu_core->NotifyShutdown();
services.reset();
service_manager.reset();
cheat_engine.reset();
@@ -394,7 +391,6 @@ struct System::Impl {
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
std::unique_ptr<Core::DeviceMemory> device_memory;
Core::Memory::Memory memory;
Core::HID::HIDCore hid_core;
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
@@ -523,6 +519,12 @@ const ARM_Interface& System::CurrentArmInterface() const {
return impl->kernel.CurrentPhysicalCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() const {
std::size_t core = impl->kernel.GetCurrentHostThreadID();
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
return core;
}
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
return impl->kernel.CurrentPhysicalCore();
}
@@ -613,14 +615,6 @@ const Kernel::KernelCore& System::Kernel() const {
return impl->kernel;
}
HID::HIDCore& System::HIDCore() {
return impl->hid_core;
}
const HID::HIDCore& System::HIDCore() const {
return impl->hid_core;
}
Timing::CoreTiming& System::CoreTiming() {
return impl->core_timing;
}
@@ -831,6 +825,8 @@ void System::ApplySettings() {
if (IsPoweredOn()) {
Renderer().RefreshBaseSettings();
}
Service::HID::ReloadInputDevices();
}
} // namespace Core
+3 -10
View File
@@ -89,10 +89,6 @@ namespace Core::Hardware {
class InterruptManager;
}
namespace Core::HID {
class HIDCore;
}
namespace Core {
class ARM_Interface;
@@ -208,6 +204,9 @@ public:
/// Gets an ARM interface to the CPU core that is currently running
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
/// Gets the index of the currently running CPU core
[[nodiscard]] std::size_t CurrentCoreIndex() const;
/// Gets the physical core for the CPU core that is currently running
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
@@ -286,12 +285,6 @@ public:
/// Provides a constant reference to the kernel instance.
[[nodiscard]] const Kernel::KernelCore& Kernel() const;
/// Gets a mutable reference to the HID interface.
[[nodiscard]] HID::HIDCore& HIDCore();
/// Gets an immutable reference to the HID interface.
[[nodiscard]] const HID::HIDCore& HIDCore() const;
/// Provides a reference to the internal PerfStats instance.
[[nodiscard]] Core::PerfStats& GetPerfStats();
+13 -10
View File
@@ -117,18 +117,17 @@ void CpuManager::MultiCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
{
Kernel::KScopedDisableDispatch dd(kernel);
physical_core->ArmInterface().ClearExclusiveState();
}
physical_core->ArmInterface().ClearExclusiveState();
kernel.CurrentScheduler()->RescheduleCurrentCore();
}
}
void CpuManager::MultiCoreRunIdleThread() {
auto& kernel = system.Kernel();
while (true) {
Kernel::KScopedDisableDispatch dd(kernel);
kernel.CurrentPhysicalCore().Idle();
auto& physical_core = kernel.CurrentPhysicalCore();
physical_core.Idle();
kernel.CurrentScheduler()->RescheduleCurrentCore();
}
}
@@ -136,12 +135,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto core = kernel.CurrentPhysicalCoreIndex();
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
}
}
@@ -347,11 +346,15 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
sc_sync_first_use = false;
}
// Emulation was stopped
if (stop_token.stop_requested()) {
// Abort if emulation was killed before the session really starts
if (!system.IsPoweredOn()) {
return;
}
if (stop_token.stop_requested()) {
break;
}
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
data.is_running = true;
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
+25 -20
View File
@@ -5,15 +5,16 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/frontend/applets/controller.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace Core::Frontend {
ControllerApplet::~ControllerApplet() = default;
DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {}
DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
: service_manager{service_manager_} {}
DefaultControllerApplet::~DefaultControllerApplet() = default;
@@ -21,20 +22,24 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
const ControllerParameters& parameters) const {
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
auto& npad =
service_manager.GetService<Service::HID::Hid>("hid")
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
auto& players = Settings::values.players.GetValue();
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
// Disconnect Handheld first.
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
handheld->Disconnect();
npad.DisconnectNpadAtIndex(8);
// Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
for (std::size_t index = 0; index < players.size() - 2; ++index) {
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
controller->Disconnect();
npad.DisconnectNpadAtIndex(index);
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
@@ -44,27 +49,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->Connect(true);
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
} else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
controller->Connect(true);
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// Assign left joycons to even player indices and right joycons to odd player indices.
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// a right Joycon for Player 2 in 2 Player Assist mode.
if (index % 2 == 0) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
controller->Connect(true);
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
} else {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
controller->Connect(true);
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode.GetValue()) {
// We should *never* reach here under any normal circumstances.
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect(true);
npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
index);
} else {
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
}
+4 -4
View File
@@ -8,8 +8,8 @@
#include "common/common_types.h"
namespace Core::HID {
class HIDCore;
namespace Service::SM {
class ServiceManager;
}
namespace Core::Frontend {
@@ -44,14 +44,14 @@ public:
class DefaultControllerApplet final : public ControllerApplet {
public:
explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void()> callback,
const ControllerParameters& parameters) const override;
private:
HID::HIDCore& hid_core;
Service::SM::ServiceManager& service_manager;
};
} // namespace Core::Frontend
+89 -11
View File
@@ -3,31 +3,66 @@
// Refer to the license.txt file included.
#include <mutex>
#include "common/settings.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/input.h"
namespace Core::Frontend {
GraphicsContext::~GraphicsContext() = default;
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
public std::enable_shared_from_this<TouchState> {
public:
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
return std::make_unique<Device>(shared_from_this());
}
std::mutex mutex;
Input::TouchStatus status;
private:
class Device : public Input::TouchDevice {
public:
explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {}
Input::TouchStatus GetStatus() const override {
if (auto state = touch_state.lock()) {
std::lock_guard guard{state->mutex};
return state->status;
}
return {};
}
private:
std::weak_ptr<TouchState> touch_state;
};
};
EmuWindow::EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size =
std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
active_config = config;
touch_state = std::make_shared<TouchState>();
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
}
EmuWindow::~EmuWindow() {}
EmuWindow::~EmuWindow() {
Input::UnregisterFactory<Input::TouchDevice>("emu_window");
}
std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
const float x =
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
const float y =
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
return std::make_pair(x, y);
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
* @param framebuffer_x Framebuffer x-coordinate to check
* @param framebuffer_y Framebuffer y-coordinate to check
* @return True if the coordinates are within the touchpad, otherwise false
*/
static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x,
u32 framebuffer_y) {
return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
}
std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
@@ -40,6 +75,49 @@ std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
return std::make_pair(new_x, new_y);
}
void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
return;
}
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex};
const float x =
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
const float y =
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
touch_state->status[id] = std::make_tuple(x, y, true);
}
void EmuWindow::TouchReleased(size_t id) {
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex};
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
}
void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
if (id >= touch_state->status.size()) {
return;
}
if (!std::get<2>(touch_state->status[id])) {
return;
}
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
}
TouchPressed(framebuffer_x, framebuffer_y, id);
}
void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) {
NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height));
}
+25 -5
View File
@@ -112,6 +112,28 @@ public:
/// Returns if window is shown (not minimized)
virtual bool IsShown() const = 0;
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed
* @param framebuffer_y Framebuffer y-coordinate that was pressed
* @param id Touch event ID
*/
void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id);
/**
* Signal that a touch released event has occurred (e.g. mouse click released)
* @param id Touch event ID
*/
void TouchReleased(size_t id);
/**
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
* @param framebuffer_x Framebuffer x-coordinate
* @param framebuffer_y Framebuffer y-coordinate
* @param id Touch event ID
*/
void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id);
/**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
@@ -190,11 +212,6 @@ protected:
client_area_height = size.second;
}
/**
* Converts a screen postion into the equivalent touchscreen position.
*/
std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
WindowSystemInfo window_info;
private:
@@ -220,6 +237,9 @@ private:
WindowConfig config; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
class TouchState;
std::shared_ptr<TouchState> touch_state;
};
} // namespace Core::Frontend
+10 -12
View File
@@ -25,12 +25,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
ASSERT(height > 0);
// The drawing code needs at least somewhat valid values for both screens
// so just calculate them both even if the other isn't showing.
FramebufferLayout res{
.width = width,
.height = height,
.screen = {},
.is_srgb = false,
};
FramebufferLayout res{width, height, false, {}};
const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
const float emulation_aspect_ratio = EmulationAspectRatio(
@@ -49,13 +44,16 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
return res;
}
FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) {
const bool is_docked = Settings::values.use_docked_mode.GetValue();
const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width;
const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height;
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
u32 width, height;
const u32 width = static_cast<u32>(static_cast<f32>(screen_width) * res_scale);
const u32 height = static_cast<u32>(static_cast<f32>(screen_height) * res_scale);
if (Settings::values.use_docked_mode.GetValue()) {
width = ScreenDocked::Width * res_scale;
height = ScreenDocked::Height * res_scale;
} else {
width = ScreenUndocked::Width * res_scale;
height = ScreenUndocked::Height * res_scale;
}
return DefaultFrameLayout(width, height);
}
+11 -2
View File
@@ -35,8 +35,17 @@ enum class AspectRatio {
struct FramebufferLayout {
u32 width{ScreenUndocked::Width};
u32 height{ScreenUndocked::Height};
Common::Rectangle<u32> screen;
bool is_srgb{};
Common::Rectangle<u32> screen;
/**
* Returns the ration of pixel size of the screen, compared to the native size of the undocked
* Switch screen.
*/
float GetScalingRatio() const {
return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
}
};
/**
@@ -51,7 +60,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height);
* Convenience method to get frame layout by resolution scale
* @param res_scale resolution scale factor
*/
FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale);
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
/**
* Convenience method to determine emulation aspect ratio
+217
View File
@@ -0,0 +1,217 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/quaternion.h"
#include "common/vector_math.h"
namespace Input {
enum class AnalogDirection : u8 {
RIGHT,
LEFT,
UP,
DOWN,
};
struct AnalogProperties {
float deadzone;
float range;
float threshold;
};
template <typename StatusType>
struct InputCallback {
std::function<void(StatusType)> on_change;
};
/// An abstract class template for an input device (a button, an analog input, etc.).
template <typename StatusType>
class InputDevice {
public:
virtual ~InputDevice() = default;
virtual StatusType GetStatus() const {
return {};
}
virtual StatusType GetRawStatus() const {
return GetStatus();
}
virtual AnalogProperties GetAnalogProperties() const {
return {};
}
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
return {};
}
virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
[[maybe_unused]] f32 amp_high,
[[maybe_unused]] f32 freq_high) const {
return {};
}
void SetCallback(InputCallback<StatusType> callback_) {
callback = std::move(callback_);
}
void TriggerOnChange() {
if (callback.on_change) {
callback.on_change(GetStatus());
}
}
private:
InputCallback<StatusType> callback;
};
/// An abstract class template for a factory that can create input devices.
template <typename InputDeviceType>
class Factory {
public:
virtual ~Factory() = default;
virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
};
namespace Impl {
template <typename InputDeviceType>
using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
template <typename InputDeviceType>
struct FactoryList {
static FactoryListType<InputDeviceType> list;
};
template <typename InputDeviceType>
FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
} // namespace Impl
/**
* Registers an input device factory.
* @tparam InputDeviceType the type of input devices the factory can create
* @param name the name of the factory. Will be used to match the "engine" parameter when creating
* a device
* @param factory the factory object to register
*/
template <typename InputDeviceType>
void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
auto pair = std::make_pair(name, std::move(factory));
if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
LOG_ERROR(Input, "Factory '{}' already registered", name);
}
}
/**
* Unregisters an input device factory.
* @tparam InputDeviceType the type of input devices the factory can create
* @param name the name of the factory to unregister
*/
template <typename InputDeviceType>
void UnregisterFactory(const std::string& name) {
if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
LOG_ERROR(Input, "Factory '{}' not registered", name);
}
}
/**
* Create an input device from given paramters.
* @tparam InputDeviceType the type of input devices to create
* @param params a serialized ParamPackage string contains all parameters for creating the device
*/
template <typename InputDeviceType>
std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
const Common::ParamPackage package(params);
const std::string engine = package.Get("engine", "null");
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
const auto pair = factory_list.find(engine);
if (pair == factory_list.end()) {
if (engine != "null") {
LOG_ERROR(Input, "Unknown engine name: {}", engine);
}
return std::make_unique<InputDeviceType>();
}
return pair->second->Create(package);
}
/**
* A button device is an input device that returns bool as status.
* true for pressed; false for released.
*/
using ButtonDevice = InputDevice<bool>;
/**
* An analog device is an input device that returns a tuple of x and y coordinates as status. The
* coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
* direction
*/
using AnalogDevice = InputDevice<std::tuple<float, float>>;
/**
* A vibration device is an input device that returns an unsigned byte as status.
* It represents whether the vibration device supports vibration or not.
* If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
*/
using VibrationDevice = InputDevice<u8>;
/**
* A motion status is an object that returns a tuple of accelerometer state vector,
* gyroscope state vector, rotation state vector, orientation state matrix and quaterion state
* vector.
*
* For both 3D vectors:
* x+ is the same direction as RIGHT on D-pad.
* y+ is normal to the touch screen, pointing outward.
* z+ is the same direction as UP on D-pad.
*
* For accelerometer state vector
* Units: g (gravitational acceleration)
*
* For gyroscope state vector:
* Orientation is determined by right-hand rule.
* Units: deg/sec
*
* For rotation state vector
* Units: rotations
*
* For orientation state matrix
* x vector
* y vector
* z vector
*
* For quaternion state vector
* xyz vector
* w float
*/
using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
std::array<Common::Vec3f, 3>, Common::Quaternion<f32>>;
/**
* A motion device is an input device that returns a motion status object
*/
using MotionDevice = InputDevice<MotionStatus>;
/**
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
* pressed.
*/
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
/**
* A touch device is an input device that returns a touch status object
*/
using TouchDevice = InputDevice<TouchStatus>;
/**
* A mouse device is an input device that returns a tuple of two floats and four ints.
* The first two floats are X and Y device coordinates of the mouse (from 0-1).
* The s32s are the mouse wheel.
*/
using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
} // namespace Input
@@ -3,8 +3,7 @@
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hid/input_interpreter.h"
#include "core/frontend/input_interpreter.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
@@ -20,7 +19,7 @@ InputInterpreter::InputInterpreter(Core::System& system)
InputInterpreter::~InputInterpreter() = default;
void InputInterpreter::PollInput() {
const auto button_state = npad.GetAndResetPressState();
const u32 button_state = npad.GetAndResetPressState();
previous_index = current_index;
current_index = (current_index + 1) % button_states.size();
@@ -32,30 +31,32 @@ void InputInterpreter::ResetButtonStates() {
previous_index = 0;
current_index = 0;
button_states[0] = Core::HID::NpadButton::All;
button_states[0] = 0xFFFFFFFF;
for (std::size_t i = 1; i < button_states.size(); ++i) {
button_states[i] = Core::HID::NpadButton::None;
button_states[i] = 0;
}
}
bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
return True(button_states[current_index] & button);
bool InputInterpreter::IsButtonPressed(HIDButton button) const {
return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
}
bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
const bool current_press = True(button_states[current_index] & button);
const bool previous_press = True(button_states[previous_index] & button);
bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
const bool current_press =
(button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
const bool previous_press =
(button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
return current_press && !previous_press;
}
bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
Core::HID::NpadButton held_buttons{button_states[0]};
bool InputInterpreter::IsButtonHeld(HIDButton button) const {
u32 held_buttons{button_states[0]};
for (std::size_t i = 1; i < button_states.size(); ++i) {
held_buttons &= button_states[i];
}
return True(held_buttons & button);
return (held_buttons & (1U << static_cast<u8>(button))) != 0;
}
@@ -12,14 +12,46 @@ namespace Core {
class System;
}
namespace Core::HID {
enum class NpadButton : u64;
}
namespace Service::HID {
class Controller_NPad;
}
enum class HIDButton : u8 {
A,
B,
X,
Y,
LStick,
RStick,
L,
R,
ZL,
ZR,
Plus,
Minus,
DLeft,
DUp,
DRight,
DDown,
LStickLeft,
LStickUp,
LStickRight,
LStickDown,
RStickLeft,
RStickUp,
RStickRight,
RStickDown,
LeftSL,
LeftSR,
RightSL,
RightSR,
};
/**
* The InputInterpreter class interfaces with HID to retrieve button press states.
* Input is intended to be polled every 50ms so that a button is considered to be
@@ -44,7 +76,7 @@ public:
*
* @returns True when the button is pressed.
*/
[[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
[[nodiscard]] bool IsButtonPressed(HIDButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed.
@@ -53,7 +85,7 @@ public:
*
* @returns True when at least one of the buttons is pressed.
*/
template <Core::HID::NpadButton... T>
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressed() {
return (IsButtonPressed(T) || ...);
}
@@ -66,7 +98,7 @@ public:
*
* @returns True when the button is pressed once.
*/
[[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
[[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed once.
@@ -75,7 +107,7 @@ public:
*
* @returns True when at least one of the buttons is pressed once.
*/
template <Core::HID::NpadButton... T>
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() const {
return (IsButtonPressedOnce(T) || ...);
}
@@ -87,7 +119,7 @@ public:
*
* @returns True when the button is held down.
*/
[[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
[[nodiscard]] bool IsButtonHeld(HIDButton button) const;
/**
* Checks whether any of the buttons in the parameter list is held down.
@@ -96,7 +128,7 @@ public:
*
* @returns True when at least one of the buttons is held down.
*/
template <Core::HID::NpadButton... T>
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonHeld() const {
return (IsButtonHeld(T) || ...);
}
@@ -105,7 +137,7 @@ private:
Service::HID::Controller_NPad& npad;
/// Stores 9 consecutive button states polled from HID.
std::array<Core::HID::NpadButton, 9> button_states{};
std::array<u32, 9> button_states{};
std::size_t previous_index{};
std::size_t current_index{};
-235
View File
@@ -1,235 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#include "common/settings.h"
#include "core/hid/emulated_console.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
EmulatedConsole::EmulatedConsole() = default;
EmulatedConsole::~EmulatedConsole() = default;
void EmulatedConsole::ReloadFromSettings() {
// Using first motion device from player 1. No need to assign any unique config at the moment
const auto& player = Settings::values.players.GetValue()[0];
motion_params = Common::ParamPackage(player.motions[0]);
ReloadInput();
}
void EmulatedConsole::SetTouchParams() {
// TODO(german77): Support any number of fingers
std::size_t index = 0;
// Hardcode mouse, touchscreen and cemuhook parameters
if (!Settings::values.mouse_enabled) {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
}
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
// Map the rest of the fingers from touch from button configuration
for (const auto& config_entry : touch_buttons) {
if (index >= touch_params.size()) {
continue;
}
Common::ParamPackage params{config_entry};
Common::ParamPackage touch_button_params;
const int x = params.Get("x", 0);
const int y = params.Get("y", 0);
params.Erase("x");
params.Erase("y");
touch_button_params.Set("engine", "touch_from_button");
touch_button_params.Set("button", params.Serialize());
touch_button_params.Set("x", x);
touch_button_params.Set("y", y);
touch_button_params.Set("touch_id", static_cast<int>(index));
touch_params[index] = touch_button_params;
index++;
}
}
void EmulatedConsole::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
SetTouchParams();
motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
if (motion_devices) {
motion_devices->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
});
}
// Unique index for identifying touch device source
std::size_t index = 0;
for (auto& touch_device : touch_devices) {
touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
if (!touch_device) {
continue;
}
touch_device->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetTouch(callback, index);
},
});
index++;
}
}
void EmulatedConsole::UnloadInput() {
motion_devices.reset();
for (auto& touch : touch_devices) {
touch.reset();
}
}
void EmulatedConsole::EnableConfiguration() {
is_configuring = true;
SaveCurrentConfig();
}
void EmulatedConsole::DisableConfiguration() {
is_configuring = false;
}
bool EmulatedConsole::IsConfiguring() const {
return is_configuring;
}
void EmulatedConsole::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
}
void EmulatedConsole::RestoreConfig() {
if (!is_configuring) {
return;
}
ReloadFromSettings();
}
Common::ParamPackage EmulatedConsole::GetMotionParam() const {
return motion_params;
}
void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
motion_params = param;
ReloadInput();
}
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
std::lock_guard lock{mutex};
auto& raw_status = console.motion_values.raw_status;
auto& emulated = console.motion_values.emulated;
raw_status = TransformToMotion(callback);
emulated.SetAcceleration(Common::Vec3f{
raw_status.accel.x.value,
raw_status.accel.y.value,
raw_status.accel.z.value,
});
emulated.SetGyroscope(Common::Vec3f{
raw_status.gyro.x.value,
raw_status.gyro.y.value,
raw_status.gyro.z.value,
});
emulated.UpdateRotation(raw_status.delta_timestamp);
emulated.UpdateOrientation(raw_status.delta_timestamp);
if (is_configuring) {
TriggerOnChange(ConsoleTriggerType::Motion);
return;
}
auto& motion = console.motion_state;
motion.accel = emulated.GetAcceleration();
motion.gyro = emulated.GetGyroscope();
motion.rotation = emulated.GetRotations();
motion.orientation = emulated.GetOrientation();
motion.quaternion = emulated.GetQuaternion();
motion.gyro_bias = emulated.GetGyroBias();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
// Find what is this value
motion.verticalization_error = 0.0f;
TriggerOnChange(ConsoleTriggerType::Motion);
}
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
if (index >= console.touch_values.size()) {
return;
}
std::lock_guard lock{mutex};
console.touch_values[index] = TransformToTouch(callback);
if (is_configuring) {
TriggerOnChange(ConsoleTriggerType::Touch);
return;
}
// TODO(german77): Remap touch id in sequential order
console.touch_state[index] = {
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
.id = static_cast<u32>(console.touch_values[index].id),
.pressed = console.touch_values[index].pressed.value,
};
TriggerOnChange(ConsoleTriggerType::Touch);
}
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
return console.motion_values;
}
TouchValues EmulatedConsole::GetTouchValues() const {
return console.touch_values;
}
ConsoleMotion EmulatedConsole::GetMotion() const {
return console.motion_state;
}
TouchFingerState EmulatedConsole::GetTouch() const {
return console.touch_state;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
for (const auto& poller_pair : callback_list) {
const ConsoleUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
poller.on_change(type);
}
}
}
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
std::lock_guard lock{mutex};
callback_list.insert_or_assign(last_callback_key, update_callback);
return last_callback_key++;
}
void EmulatedConsole::DeleteCallback(int key) {
std::lock_guard lock{mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
}
} // namespace Core::HID
-192
View File
@@ -1,192 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/point.h"
#include "common/quaternion.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
struct ConsoleMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
};
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
using ConsoleMotionParams = Common::ParamPackage;
using TouchParams = std::array<Common::ParamPackage, 16>;
using ConsoleMotionValues = ConsoleMotionInfo;
using TouchValues = std::array<Common::Input::TouchStatus, 16>;
struct TouchFinger {
u64 last_touch{};
Common::Point<float> position{};
u32 id{};
TouchAttribute attribute{};
bool pressed{};
};
// Contains all motion related data that is used on the services
struct ConsoleMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
Common::Quaternion<f32> quaternion{};
Common::Vec3f gyro_bias{};
f32 verticalization_error{};
bool is_at_rest{};
};
using TouchFingerState = std::array<TouchFinger, 16>;
struct ConsoleStatus {
// Data from input_common
ConsoleMotionValues motion_values{};
TouchValues touch_values{};
// Data for HID services
ConsoleMotion motion_state{};
TouchFingerState touch_state{};
};
enum class ConsoleTriggerType {
Motion,
Touch,
All,
};
struct ConsoleUpdateCallback {
std::function<void(ConsoleTriggerType)> on_change;
};
class EmulatedConsole {
public:
/**
* Contains all input data within the emulated switch console tablet such as touch and motion
*/
explicit EmulatedConsole();
~EmulatedConsole();
YUZU_NON_COPYABLE(EmulatedConsole);
YUZU_NON_MOVEABLE(EmulatedConsole);
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated console into configuring mode
* This prevents the modification of the HID state of the emulated console by input commands
*/
void EnableConfiguration();
/// Returns the emulated console into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Returns true if the emulated console is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
// Returns the current mapped motion device
Common::ParamPackage GetMotionParam() const;
/**
* Updates the current mapped motion device
* @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(Common::ParamPackage param);
/// Returns the latest status of motion input from the console with parameters
ConsoleMotionValues GetMotionValues() const;
/// Returns the latest status of touch input from the console with parameters
TouchValues GetTouchValues() const;
/// Returns the latest status of motion input from the console
ConsoleMotion GetMotion() const;
/// Returns the latest status of touch input from the console
TouchFingerState GetTouch() const;
/**
* Adds a callback to the list of events
* @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ConsoleUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
private:
/// Creates and stores the touch params
void SetTouchParams();
/**
* Updates the motion status of the console
* @param callback A CallbackStatus containing gyro and accelerometer data
*/
void SetMotion(const Common::Input::CallbackStatus& callback);
/**
* Updates the touch status of the console
* @param callback A CallbackStatus containing the touch position
* @param index Finger ID to be updated
*/
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Triggers a callback that something has changed on the console status
* @param type Input type of the event to trigger
*/
void TriggerOnChange(ConsoleTriggerType type);
bool is_configuring{false};
f32 motion_sensitivity{0.01f};
ConsoleMotionParams motion_params;
TouchParams touch_params;
ConsoleMotionDevices motion_devices;
TouchDevices touch_devices;
mutable std::mutex mutex;
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all console input
ConsoleStatus console;
};
} // namespace Core::HID
File diff suppressed because it is too large Load Diff
-439
View File
@@ -1,439 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/point.h"
#include "common/quaternion.h"
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
};
using ButtonDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
using StickDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using OutputDevices =
std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
using TriggerValues =
std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
AnalogStickState left{};
AnalogStickState right{};
};
struct ControllerColors {
NpadControllerColor fullkey{};
NpadControllerColor left{};
NpadControllerColor right{};
};
struct BatteryLevelState {
NpadPowerInfo dual{};
NpadPowerInfo left{};
NpadPowerInfo right{};
};
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
bool is_at_rest{};
};
enum EmulatedDeviceIndex : u8 {
LeftIndex,
RightIndex,
DualIndex,
AllDevices,
};
using MotionState = std::array<ControllerMotion, 2>;
struct ControllerStatus {
// Data from input_common
ButtonValues button_values{};
SticksValues stick_values{};
ControllerMotionValues motion_values{};
TriggerValues trigger_values{};
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
// Data for HID serices
HomeButtonState home_button_state{};
CaptureButtonState capture_button_state{};
NpadButtonState npad_button_state{};
DebugPadButton debug_pad_button_state{};
AnalogSticks analog_stick_state{};
MotionState motion_state{};
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
};
enum class ControllerTriggerType {
Button,
Stick,
Trigger,
Motion,
Color,
Battery,
Vibration,
Connected,
Disconnected,
Type,
All,
};
struct ControllerUpdateCallback {
std::function<void(ControllerTriggerType)> on_change;
bool is_npad_service;
};
class EmulatedController {
public:
/**
* Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
* @param npad_id_type npad id type for this specific controller
*/
explicit EmulatedController(NpadIdType npad_id_type_);
~EmulatedController();
YUZU_NON_COPYABLE(EmulatedController);
YUZU_NON_MOVEABLE(EmulatedController);
/// Converts the controller type from settings to npad type
static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
/// Converts npad type to the equivalent of controller type from settings
static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
/// Gets the NpadIdType for this controller
NpadIdType GetNpadIdType() const;
/// Sets the NpadStyleIndex for this controller
void SetNpadStyleIndex(NpadStyleIndex npad_type_);
/**
* Gets the NpadStyleIndex for this controller
* @param get_temporary_value If true tmp_npad_type will be returned
* @return NpadStyleIndex set on the controller
*/
NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
/**
* Sets the supported controller types. Disconnects the controller if current type is not
* supported
* @param supported_styles bitflag with supported types
*/
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
/**
* Sets the connected status to true
* @param use_temporary_value If true tmp_npad_type will be used
*/
void Connect(bool use_temporary_value = false);
/// Sets the connected status to false
void Disconnect();
/**
* Is the emulated connected
* @param get_temporary_value If true tmp_is_connected will be returned
* @return true if the controller has the connected status
*/
bool IsConnected(bool get_temporary_value = false) const;
/// Returns true if vibration is enabled
bool IsVibrationEnabled() const;
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated controller into configuring mode
* This prevents the modification of the HID state of the emulated controller by input commands
*/
void EnableConfiguration();
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Enables Home and Screenshot buttons
void EnableSystemButtons();
/// Disables Home and Screenshot buttons
void DisableSystemButtons();
/// Sets Home and Screenshot buttons to false
void ResetSystemButtons();
/// Returns true if the emulated controller is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
/// Returns a vector of mapped devices from the mapped button and stick parameters
std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const;
// Returns the current mapped button device
Common::ParamPackage GetButtonParam(std::size_t index) const;
// Returns the current mapped stick device
Common::ParamPackage GetStickParam(std::size_t index) const;
// Returns the current mapped motion device
Common::ParamPackage GetMotionParam(std::size_t index) const;
/**
* Updates the current mapped button device
* @param param ParamPackage with controller data to be mapped
*/
void SetButtonParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped stick device
* @param param ParamPackage with controller data to be mapped
*/
void SetStickParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped motion device
* @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
/// Returns the latest button status from the controller with parameters
ButtonValues GetButtonsValues() const;
/// Returns the latest analog stick status from the controller with parameters
SticksValues GetSticksValues() const;
/// Returns the latest trigger status from the controller with parameters
TriggerValues GetTriggersValues() const;
/// Returns the latest motion status from the controller with parameters
ControllerMotionValues GetMotionValues() const;
/// Returns the latest color status from the controller with parameters
ColorValues GetColorsValues() const;
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
/// Returns the latest status of button input for the hid::CaptureButton service
CaptureButtonState GetCaptureButtons() const;
/// Returns the latest status of button input for the hid::Npad service
NpadButtonState GetNpadButtons() const;
/// Returns the latest status of button input for the debug pad service
DebugPadButton GetDebugPadButtons() const;
/// Returns the latest status of stick input from the mouse
AnalogSticks GetSticks() const;
/// Returns the latest status of trigger input from the mouse
NpadGcTriggerState GetTriggers() const;
/// Returns the latest status of motion input from the mouse
MotionState GetMotions() const;
/// Returns the latest color value from the controller
ControllerColors GetColors() const;
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
/**
* Sends a specific vibration to the output device
* @return returns true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
/**
* Sends a small vibration to the output device
* @return returns true if SetVibration was successfull
*/
bool TestVibration(std::size_t device_index);
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
/// Asks the output device to change the player led pattern
void SetLedPattern();
/**
* Adds a callback to the list of events
* @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ControllerUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
private:
/// creates input devices from params
void LoadDevices();
/// Set the params for TAS devices
void LoadTASParams();
/**
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller style is fullkey
*/
bool IsControllerFullkey(bool use_temporary_value = false) const;
/**
* Checks the current controller type against the supported_style_tag
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller is supported
*/
bool IsControllerSupported(bool use_temporary_value = false) const;
/**
* Updates the button status of the controller
* @param callback A CallbackStatus containing the button status
* @param index Button ID of the to be updated
*/
void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the analog stick status of the controller
* @param callback A CallbackStatus containing the analog stick status
* @param index stick ID of the to be updated
*/
void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the trigger status of the controller
* @param callback A CallbackStatus containing the trigger status
* @param index trigger ID of the to be updated
*/
void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the motion status of the controller
* @param callback A CallbackStatus containing gyro and accelerometer data
* @param index motion ID of the to be updated
*/
void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the battery status of the controller
* @param callback A CallbackStatus containing the battery status
* @param index Button ID of the to be updated
*/
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
bool system_buttons_enabled{true};
f32 motion_sensitivity{0.01f};
bool force_update_motion{false};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
bool tmp_is_connected{false};
ButtonParams button_params;
StickParams stick_params;
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
OutputParams output_params;
ButtonDevices button_devices;
StickDevices stick_devices;
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
OutputDevices output_devices;
// TAS related variables
ButtonParams tas_button_params;
StickParams tas_stick_params;
ButtonDevices tas_button_devices;
StickDevices tas_stick_devices;
mutable std::mutex mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all controller input
ControllerStatus controller;
};
} // namespace Core::HID
-459
View File
@@ -1,459 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#include <algorithm>
#include <fmt/format.h>
#include "core/hid/emulated_devices.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
EmulatedDevices::EmulatedDevices() = default;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
ReloadInput();
}
void EmulatedDevices::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
std::size_t key_index = 0;
for (auto& mouse_device : mouse_button_devices) {
Common::ParamPackage mouse_params;
mouse_params.Set("engine", "mouse");
mouse_params.Set("button", static_cast<int>(key_index));
mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
key_index++;
}
mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
"engine:mouse,axis_x:0,axis_y:1");
// First two axis are reserved for mouse position
key_index = 2;
for (auto& mouse_device : mouse_analog_devices) {
Common::ParamPackage mouse_params;
mouse_params.Set("engine", "mouse");
mouse_params.Set("axis", static_cast<int>(key_index));
mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
key_index++;
}
key_index = 0;
for (auto& keyboard_device : keyboard_devices) {
// Keyboard keys are only mapped on port 1, pad 0
Common::ParamPackage keyboard_params;
keyboard_params.Set("engine", "keyboard");
keyboard_params.Set("button", static_cast<int>(key_index));
keyboard_params.Set("port", 1);
keyboard_params.Set("pad", 0);
keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
key_index++;
}
key_index = 0;
for (auto& keyboard_device : keyboard_modifier_devices) {
// Keyboard moddifiers are only mapped on port 1, pad 1
Common::ParamPackage keyboard_params;
keyboard_params.Set("engine", "keyboard");
keyboard_params.Set("button", static_cast<int>(key_index));
keyboard_params.Set("port", 1);
keyboard_params.Set("pad", 1);
keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
key_index++;
}
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
continue;
}
mouse_button_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetMouseButton(callback, index);
},
});
}
for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
if (!mouse_analog_devices[index]) {
continue;
}
mouse_analog_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetMouseAnalog(callback, index);
},
});
}
if (mouse_stick_device) {
mouse_stick_device->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); },
});
}
for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
if (!keyboard_devices[index]) {
continue;
}
keyboard_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetKeyboardButton(callback, index);
},
});
}
for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
if (!keyboard_modifier_devices[index]) {
continue;
}
keyboard_modifier_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetKeyboardModifier(callback, index);
},
});
}
}
void EmulatedDevices::UnloadInput() {
for (auto& button : mouse_button_devices) {
button.reset();
}
for (auto& analog : mouse_analog_devices) {
analog.reset();
}
mouse_stick_device.reset();
for (auto& button : keyboard_devices) {
button.reset();
}
for (auto& button : keyboard_modifier_devices) {
button.reset();
}
}
void EmulatedDevices::EnableConfiguration() {
is_configuring = true;
SaveCurrentConfig();
}
void EmulatedDevices::DisableConfiguration() {
is_configuring = false;
}
bool EmulatedDevices::IsConfiguring() const {
return is_configuring;
}
void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
}
void EmulatedDevices::RestoreConfig() {
if (!is_configuring) {
return;
}
ReloadFromSettings();
}
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_values.size()) {
return;
}
std::lock_guard lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_values[index];
current_status.toggle = new_status.toggle;
// Update button status with current status
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button, ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
TriggerOnChange(DeviceTriggerType::Keyboard);
return;
}
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
UpdateKey(index, current_status.value);
TriggerOnChange(DeviceTriggerType::Keyboard);
}
void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
constexpr std::size_t KEYS_PER_BYTE = 8;
auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
if (status) {
entry = entry | mask;
} else {
entry = static_cast<u8>(entry & ~mask);
}
}
void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_moddifier_values.size()) {
return;
}
std::lock_guard lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_moddifier_values[index];
current_status.toggle = new_status.toggle;
// Update button status with current
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
return;
}
switch (index) {
case Settings::NativeKeyboard::LeftControl:
case Settings::NativeKeyboard::RightControl:
device_status.keyboard_moddifier_state.control.Assign(current_status.value);
break;
case Settings::NativeKeyboard::LeftShift:
case Settings::NativeKeyboard::RightShift:
device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
break;
case Settings::NativeKeyboard::LeftAlt:
device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
break;
case Settings::NativeKeyboard::RightAlt:
device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
break;
case Settings::NativeKeyboard::CapsLock:
device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
break;
case Settings::NativeKeyboard::ScrollLock:
device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
break;
case Settings::NativeKeyboard::NumLock:
device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
break;
}
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}
void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.mouse_button_values.size()) {
return;
}
std::lock_guard lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.mouse_button_values[index];
current_status.toggle = new_status.toggle;
// Update button status with current
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
switch (index) {
case Settings::NativeMouseButton::Left:
device_status.mouse_button_state.left.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Right:
device_status.mouse_button_state.right.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Middle:
device_status.mouse_button_state.middle.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Forward:
device_status.mouse_button_state.forward.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Back:
device_status.mouse_button_state.back.Assign(current_status.value);
break;
}
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.mouse_analog_values.size()) {
return;
}
std::lock_guard lock{mutex};
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_analog_values[index] = analog_value;
if (is_configuring) {
device_status.mouse_position_state = {};
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
switch (index) {
case Settings::NativeMouseWheel::X:
device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
break;
case Settings::NativeMouseWheel::Y:
device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
break;
}
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
std::lock_guard lock{mutex};
const auto touch_value = TransformToTouch(callback);
device_status.mouse_stick_value = touch_value;
if (is_configuring) {
device_status.mouse_position_state = {};
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
device_status.mouse_position_state.x = touch_value.x.value;
device_status.mouse_position_state.y = touch_value.y.value;
TriggerOnChange(DeviceTriggerType::Mouse);
}
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
return device_status.keyboard_values;
}
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
return device_status.keyboard_moddifier_values;
}
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
return device_status.mouse_button_values;
}
KeyboardKey EmulatedDevices::GetKeyboard() const {
return device_status.keyboard_state;
}
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
return device_status.keyboard_moddifier_state;
}
MouseButton EmulatedDevices::GetMouseButtons() const {
return device_status.mouse_button_state;
}
MousePosition EmulatedDevices::GetMousePosition() const {
return device_status.mouse_position_state;
}
AnalogStickState EmulatedDevices::GetMouseWheel() const {
return device_status.mouse_wheel_state;
}
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
for (const auto& poller_pair : callback_list) {
const InterfaceUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
poller.on_change(type);
}
}
}
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
std::lock_guard lock{mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedDevices::DeleteCallback(int key) {
std::lock_guard lock{mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
}
} // namespace Core::HID
-210
View File
@@ -1,210 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/settings.h"
#include "core/hid/hid_types.h"
namespace Core::HID {
using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeKeyboard::NumKeyboardKeys>;
using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeKeyboard::NumKeyboardMods>;
using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseButton::NumMouseButtons>;
using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
using MouseButtonParams =
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
using KeyboardValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
using KeyboardModifierValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
using MouseButtonValues =
std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
using MouseAnalogValues =
std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;
struct MousePosition {
f32 x;
f32 y;
};
struct DeviceStatus {
// Data from input_common
KeyboardValues keyboard_values{};
KeyboardModifierValues keyboard_moddifier_values{};
MouseButtonValues mouse_button_values{};
MouseAnalogValues mouse_analog_values{};
MouseStickValue mouse_stick_value{};
// Data for HID serices
KeyboardKey keyboard_state{};
KeyboardModifier keyboard_moddifier_state{};
MouseButton mouse_button_state{};
MousePosition mouse_position_state{};
AnalogStickState mouse_wheel_state{};
};
enum class DeviceTriggerType {
Keyboard,
KeyboardModdifier,
Mouse,
};
struct InterfaceUpdateCallback {
std::function<void(DeviceTriggerType)> on_change;
};
class EmulatedDevices {
public:
/**
* Contains all input data related to external devices that aren't necesarily a controller
* This includes devices such as the keyboard or mouse
*/
explicit EmulatedDevices();
~EmulatedDevices();
YUZU_NON_COPYABLE(EmulatedDevices);
YUZU_NON_MOVEABLE(EmulatedDevices);
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated devices into configuring mode
* This prevents the modification of the HID state of the emulated devices by input commands
*/
void EnableConfiguration();
/// Returns the emulated devices into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Returns true if the emulated device is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
/// Returns the latest status of button input from the keyboard with parameters
KeyboardValues GetKeyboardValues() const;
/// Returns the latest status of button input from the keyboard modifiers with parameters
KeyboardModifierValues GetKeyboardModdifierValues() const;
/// Returns the latest status of button input from the mouse with parameters
MouseButtonValues GetMouseButtonsValues() const;
/// Returns the latest status of button input from the keyboard
KeyboardKey GetKeyboard() const;
/// Returns the latest status of button input from the keyboard modifiers
KeyboardModifier GetKeyboardModifier() const;
/// Returns the latest status of button input from the mouse
MouseButton GetMouseButtons() const;
/// Returns the latest mouse coordinates
MousePosition GetMousePosition() const;
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(InterfaceUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
private:
/// Helps assigning a value to keyboard_state
void UpdateKey(std::size_t key_index, bool status);
/**
* Updates the touch status of the keyboard device
* @param callback A CallbackStatus containing the key status
* @param index key ID to be updated
*/
void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the keyboard status of the keyboard device
* @param callback A CallbackStatus containing the modifier key status
* @param index modifier key ID to be updated
*/
void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the mouse button status of the mouse device
* @param callback A CallbackStatus containing the button status
* @param index Button ID to be updated
*/
void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the mouse wheel status of the mouse device
* @param callback A CallbackStatus containing the wheel status
* @param index wheel ID to be updated
*/
void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the mouse position status of the mouse device
* @param callback A CallbackStatus containing the position status
*/
void SetMouseStick(const Common::Input::CallbackStatus& callback);
/**
* Triggers a callback that something has changed on the device status
* @param type Input type of the event to trigger
*/
void TriggerOnChange(DeviceTriggerType type);
bool is_configuring{false};
KeyboardDevices keyboard_devices;
KeyboardModifierDevices keyboard_modifier_devices;
MouseButtonDevices mouse_button_devices;
MouseAnalogDevices mouse_analog_devices;
MouseStickDevice mouse_stick_device;
mutable std::mutex mutex;
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all external device input
DeviceStatus device_status;
};
} // namespace Core::HID
-214
View File
@@ -1,214 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hid/emulated_console.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
namespace Core::HID {
HIDCore::HIDCore()
: player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
other{std::make_unique<EmulatedController>(NpadIdType::Other)},
handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
HIDCore::~HIDCore() = default;
EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return player_1.get();
case NpadIdType::Player2:
return player_2.get();
case NpadIdType::Player3:
return player_3.get();
case NpadIdType::Player4:
return player_4.get();
case NpadIdType::Player5:
return player_5.get();
case NpadIdType::Player6:
return player_6.get();
case NpadIdType::Player7:
return player_7.get();
case NpadIdType::Player8:
return player_8.get();
case NpadIdType::Other:
return other.get();
case NpadIdType::Handheld:
return handheld.get();
case NpadIdType::Invalid:
default:
UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
switch (npad_id_type) {
case NpadIdType::Player1:
return player_1.get();
case NpadIdType::Player2:
return player_2.get();
case NpadIdType::Player3:
return player_3.get();
case NpadIdType::Player4:
return player_4.get();
case NpadIdType::Player5:
return player_5.get();
case NpadIdType::Player6:
return player_6.get();
case NpadIdType::Player7:
return player_7.get();
case NpadIdType::Player8:
return player_8.get();
case NpadIdType::Other:
return other.get();
case NpadIdType::Handheld:
return handheld.get();
case NpadIdType::Invalid:
default:
UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
EmulatedConsole* HIDCore::GetEmulatedConsole() {
return console.get();
}
const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
return console.get();
}
EmulatedDevices* HIDCore::GetEmulatedDevices() {
return devices.get();
}
const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
return devices.get();
}
EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
return GetEmulatedController(IndexToNpadIdType(index));
}
const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
return GetEmulatedController(IndexToNpadIdType(index));
}
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
supported_style_tag.raw = style_tag.raw;
player_1->SetSupportedNpadStyleTag(supported_style_tag);
player_2->SetSupportedNpadStyleTag(supported_style_tag);
player_3->SetSupportedNpadStyleTag(supported_style_tag);
player_4->SetSupportedNpadStyleTag(supported_style_tag);
player_5->SetSupportedNpadStyleTag(supported_style_tag);
player_6->SetSupportedNpadStyleTag(supported_style_tag);
player_7->SetSupportedNpadStyleTag(supported_style_tag);
player_8->SetSupportedNpadStyleTag(supported_style_tag);
other->SetSupportedNpadStyleTag(supported_style_tag);
handheld->SetSupportedNpadStyleTag(supported_style_tag);
}
NpadStyleTag HIDCore::GetSupportedStyleTag() const {
return supported_style_tag;
}
s8 HIDCore::GetPlayerCount() const {
s8 active_players = 0;
for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (controller->IsConnected()) {
active_players++;
}
}
return active_players;
}
NpadIdType HIDCore::GetFirstNpadId() const {
for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (controller->IsConnected()) {
return controller->GetNpadIdType();
}
}
return NpadIdType::Player1;
}
NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (!controller->IsConnected()) {
return controller->GetNpadIdType();
}
}
return NpadIdType::Player1;
}
void HIDCore::EnableAllControllerConfiguration() {
player_1->EnableConfiguration();
player_2->EnableConfiguration();
player_3->EnableConfiguration();
player_4->EnableConfiguration();
player_5->EnableConfiguration();
player_6->EnableConfiguration();
player_7->EnableConfiguration();
player_8->EnableConfiguration();
other->EnableConfiguration();
handheld->EnableConfiguration();
}
void HIDCore::DisableAllControllerConfiguration() {
player_1->DisableConfiguration();
player_2->DisableConfiguration();
player_3->DisableConfiguration();
player_4->DisableConfiguration();
player_5->DisableConfiguration();
player_6->DisableConfiguration();
player_7->DisableConfiguration();
player_8->DisableConfiguration();
other->DisableConfiguration();
handheld->DisableConfiguration();
}
void HIDCore::ReloadInputDevices() {
player_1->ReloadFromSettings();
player_2->ReloadFromSettings();
player_3->ReloadFromSettings();
player_4->ReloadFromSettings();
player_5->ReloadFromSettings();
player_6->ReloadFromSettings();
player_7->ReloadFromSettings();
player_8->ReloadFromSettings();
other->ReloadFromSettings();
handheld->ReloadFromSettings();
console->ReloadFromSettings();
devices->ReloadFromSettings();
}
void HIDCore::UnloadInputDevices() {
player_1->UnloadInput();
player_2->UnloadInput();
player_3->UnloadInput();
player_4->UnloadInput();
player_5->UnloadInput();
player_6->UnloadInput();
player_7->UnloadInput();
player_8->UnloadInput();
other->UnloadInput();
handheld->UnloadInput();
console->UnloadInput();
devices->UnloadInput();
}
} // namespace Core::HID
-82
View File
@@ -1,82 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/hid/hid_types.h"
namespace Core::HID {
class EmulatedConsole;
class EmulatedController;
class EmulatedDevices;
} // namespace Core::HID
namespace Core::HID {
class HIDCore {
public:
explicit HIDCore();
~HIDCore();
YUZU_NON_COPYABLE(HIDCore);
YUZU_NON_MOVEABLE(HIDCore);
EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
EmulatedConsole* GetEmulatedConsole();
const EmulatedConsole* GetEmulatedConsole() const;
EmulatedDevices* GetEmulatedDevices();
const EmulatedDevices* GetEmulatedDevices() const;
void SetSupportedStyleTag(NpadStyleTag style_tag);
NpadStyleTag GetSupportedStyleTag() const;
/// Counts the connected players from P1-P8
s8 GetPlayerCount() const;
/// Returns the first connected npad id
NpadIdType GetFirstNpadId() const;
/// Returns the first disconnected npad id
NpadIdType GetFirstDisconnectedNpadId() const;
/// Sets all emulated controllers into configuring mode.
void EnableAllControllerConfiguration();
/// Sets all emulated controllers into normal mode.
void DisableAllControllerConfiguration();
/// Reloads all input devices from settings
void ReloadInputDevices();
/// Removes all callbacks from input common
void UnloadInputDevices();
/// Number of emulated controllers
static constexpr std::size_t available_controllers{10};
private:
std::unique_ptr<EmulatedController> player_1;
std::unique_ptr<EmulatedController> player_2;
std::unique_ptr<EmulatedController> player_3;
std::unique_ptr<EmulatedController> player_4;
std::unique_ptr<EmulatedController> player_5;
std::unique_ptr<EmulatedController> player_6;
std::unique_ptr<EmulatedController> player_7;
std::unique_ptr<EmulatedController> player_8;
std::unique_ptr<EmulatedController> other;
std::unique_ptr<EmulatedController> handheld;
std::unique_ptr<EmulatedConsole> console;
std::unique_ptr<EmulatedDevices> devices;
NpadStyleTag supported_style_tag{NpadStyleSet::All};
};
} // namespace Core::HID
-662
View File
@@ -1,662 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/point.h"
#include "common/uuid.h"
namespace Core::HID {
enum class DeviceIndex : u8 {
Left = 0,
Right = 1,
None = 2,
MaxDeviceIndex = 3,
};
// This is nn::hid::NpadButton
enum class NpadButton : u64 {
None = 0,
A = 1U << 0,
B = 1U << 1,
X = 1U << 2,
Y = 1U << 3,
StickL = 1U << 4,
StickR = 1U << 5,
L = 1U << 6,
R = 1U << 7,
ZL = 1U << 8,
ZR = 1U << 9,
Plus = 1U << 10,
Minus = 1U << 11,
Left = 1U << 12,
Up = 1U << 13,
Right = 1U << 14,
Down = 1U << 15,
StickLLeft = 1U << 16,
StickLUp = 1U << 17,
StickLRight = 1U << 18,
StickLDown = 1U << 19,
StickRLeft = 1U << 20,
StickRUp = 1U << 21,
StickRRight = 1U << 22,
StickRDown = 1U << 23,
LeftSL = 1U << 24,
LeftSR = 1U << 25,
RightSL = 1U << 26,
RightSR = 1U << 27,
Palma = 1U << 28,
Verification = 1U << 29,
HandheldLeftB = 1U << 30,
LagonCLeft = 1U << 31,
LagonCUp = 1ULL << 32,
LagonCRight = 1ULL << 33,
LagonCDown = 1ULL << 34,
All = 0xFFFFFFFFFFFFFFFFULL,
};
DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
enum class KeyboardKeyIndex : u32 {
A = 4,
B = 5,
C = 6,
D = 7,
E = 8,
F = 9,
G = 10,
H = 11,
I = 12,
J = 13,
K = 14,
L = 15,
M = 16,
N = 17,
O = 18,
P = 19,
Q = 20,
R = 21,
S = 22,
T = 23,
U = 24,
V = 25,
W = 26,
X = 27,
Y = 28,
Z = 29,
D1 = 30,
D2 = 31,
D3 = 32,
D4 = 33,
D5 = 34,
D6 = 35,
D7 = 36,
D8 = 37,
D9 = 38,
D0 = 39,
Return = 40,
Escape = 41,
Backspace = 42,
Tab = 43,
Space = 44,
Minus = 45,
Plus = 46,
OpenBracket = 47,
CloseBracket = 48,
Pipe = 49,
Tilde = 50,
Semicolon = 51,
Quote = 52,
Backquote = 53,
Comma = 54,
Period = 55,
Slash = 56,
CapsLock = 57,
F1 = 58,
F2 = 59,
F3 = 60,
F4 = 61,
F5 = 62,
F6 = 63,
F7 = 64,
F8 = 65,
F9 = 66,
F10 = 67,
F11 = 68,
F12 = 69,
PrintScreen = 70,
ScrollLock = 71,
Pause = 72,
Insert = 73,
Home = 74,
PageUp = 75,
Delete = 76,
End = 77,
PageDown = 78,
RightArrow = 79,
LeftArrow = 80,
DownArrow = 81,
UpArrow = 82,
NumLock = 83,
NumPadDivide = 84,
NumPadMultiply = 85,
NumPadSubtract = 86,
NumPadAdd = 87,
NumPadEnter = 88,
NumPad1 = 89,
NumPad2 = 90,
NumPad3 = 91,
NumPad4 = 92,
NumPad5 = 93,
NumPad6 = 94,
NumPad7 = 95,
NumPad8 = 96,
NumPad9 = 97,
NumPad0 = 98,
NumPadDot = 99,
Backslash = 100,
Application = 101,
Power = 102,
NumPadEquals = 103,
F13 = 104,
F14 = 105,
F15 = 106,
F16 = 107,
F17 = 108,
F18 = 109,
F19 = 110,
F20 = 111,
F21 = 112,
F22 = 113,
F23 = 114,
F24 = 115,
NumPadComma = 133,
Ro = 135,
KatakanaHiragana = 136,
Yen = 137,
Henkan = 138,
Muhenkan = 139,
NumPadCommaPc98 = 140,
HangulEnglish = 144,
Hanja = 145,
Katakana = 146,
Hiragana = 147,
ZenkakuHankaku = 148,
LeftControl = 224,
LeftShift = 225,
LeftAlt = 226,
LeftGui = 227,
RightControl = 228,
RightShift = 229,
RightAlt = 230,
RightGui = 231,
};
// This is nn::hid::NpadIdType
enum class NpadIdType : u32 {
Player1 = 0x0,
Player2 = 0x1,
Player3 = 0x2,
Player4 = 0x3,
Player5 = 0x4,
Player6 = 0x5,
Player7 = 0x6,
Player8 = 0x7,
Other = 0x10,
Handheld = 0x20,
Invalid = 0xFFFFFFFF,
};
// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
None = 0,
ProController = 3,
Handheld = 4,
HandheldNES = 4,
JoyconDual = 5,
JoyconLeft = 6,
JoyconRight = 7,
GameCube = 8,
Pokeball = 9,
NES = 10,
SNES = 12,
N64 = 13,
SegaGenesis = 14,
SystemExt = 32,
System = 33,
MaxNpadType = 34,
};
// This is nn::hid::NpadStyleSet
enum class NpadStyleSet : u32 {
None = 0,
Fullkey = 1U << 0,
Handheld = 1U << 1,
JoyDual = 1U << 2,
JoyLeft = 1U << 3,
JoyRight = 1U << 4,
Gc = 1U << 5,
Palma = 1U << 6,
Lark = 1U << 7,
HandheldLark = 1U << 8,
Lucia = 1U << 9,
Lagoon = 1U << 10,
Lager = 1U << 11,
SystemExt = 1U << 29,
System = 1U << 30,
All = 0xFFFFFFFFU,
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
// This is nn::hid::VibrationDevicePosition
enum class VibrationDevicePosition : u32 {
None = 0,
Left = 1,
Right = 2,
};
// This is nn::hid::VibrationDeviceType
enum class VibrationDeviceType : u32 {
Unknown = 0,
LinearResonantActuator = 1,
GcErm = 2,
};
// This is nn::hid::VibrationGcErmCommand
enum class VibrationGcErmCommand : u64 {
Stop = 0,
Start = 1,
StopHard = 2,
};
// This is nn::hid::NpadStyleTag
struct NpadStyleTag {
union {
NpadStyleSet raw{};
BitField<0, 1, u32> fullkey;
BitField<1, 1, u32> handheld;
BitField<2, 1, u32> joycon_dual;
BitField<3, 1, u32> joycon_left;
BitField<4, 1, u32> joycon_right;
BitField<5, 1, u32> gamecube;
BitField<6, 1, u32> palma;
BitField<7, 1, u32> lark;
BitField<8, 1, u32> handheld_lark;
BitField<9, 1, u32> lucia;
BitField<10, 1, u32> lagoon;
BitField<11, 1, u32> lager;
BitField<29, 1, u32> system_ext;
BitField<30, 1, u32> system;
};
};
static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
// This is nn::hid::TouchAttribute
struct TouchAttribute {
union {
u32 raw{};
BitField<0, 1, u32> start_touch;
BitField<1, 1, u32> end_touch;
};
};
static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
// This is nn::hid::TouchState
struct TouchState {
u64 delta_time;
TouchAttribute attribute;
u32 finger;
Common::Point<u32> position;
u32 diameter_x;
u32 diameter_y;
u32 rotation_angle;
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
u32 body;
u32 button;
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
// This is nn::hid::AnalogStickState
struct AnalogStickState {
s32 x;
s32 y;
};
static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
// This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState {
s64 sampling_number{};
s32 left{};
s32 right{};
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::system::NpadBatteryLevel
using NpadBatteryLevel = u32;
static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
// This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo {
bool is_powered;
bool is_charging;
INSERT_PADDING_BYTES(0x6);
NpadBatteryLevel battery_level;
};
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
position2.Assign(light2);
position3.Assign(light3);
position4.Assign(light4);
}
union {
u64 raw{};
BitField<0, 1, u64> position1;
BitField<1, 1, u64> position2;
BitField<2, 1, u64> position3;
BitField<3, 1, u64> position4;
};
};
struct HomeButtonState {
union {
u64 raw{};
// Buttons
BitField<0, 1, u64> home;
};
};
static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size.");
struct CaptureButtonState {
union {
u64 raw{};
// Buttons
BitField<0, 1, u64> capture;
};
};
static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size.");
struct NpadButtonState {
union {
NpadButton raw{};
// Buttons
BitField<0, 1, u64> a;
BitField<1, 1, u64> b;
BitField<2, 1, u64> x;
BitField<3, 1, u64> y;
BitField<4, 1, u64> stick_l;
BitField<5, 1, u64> stick_r;
BitField<6, 1, u64> l;
BitField<7, 1, u64> r;
BitField<8, 1, u64> zl;
BitField<9, 1, u64> zr;
BitField<10, 1, u64> plus;
BitField<11, 1, u64> minus;
// D-Pad
BitField<12, 1, u64> left;
BitField<13, 1, u64> up;
BitField<14, 1, u64> right;
BitField<15, 1, u64> down;
// Left JoyStick
BitField<16, 1, u64> stick_l_left;
BitField<17, 1, u64> stick_l_up;
BitField<18, 1, u64> stick_l_right;
BitField<19, 1, u64> stick_l_down;
// Right JoyStick
BitField<20, 1, u64> stick_r_left;
BitField<21, 1, u64> stick_r_up;
BitField<22, 1, u64> stick_r_right;
BitField<23, 1, u64> stick_r_down;
BitField<24, 1, u64> left_sl;
BitField<25, 1, u64> left_sr;
BitField<26, 1, u64> right_sl;
BitField<27, 1, u64> right_sr;
BitField<28, 1, u64> palma;
BitField<29, 1, u64> verification;
BitField<30, 1, u64> handheld_left_b;
BitField<31, 1, u64> lagon_c_left;
BitField<32, 1, u64> lagon_c_up;
BitField<33, 1, u64> lagon_c_right;
BitField<34, 1, u64> lagon_c_down;
};
};
static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");
// This is nn::hid::DebugPadButton
struct DebugPadButton {
union {
u32 raw{};
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
BitField<2, 1, u32> x;
BitField<3, 1, u32> y;
BitField<4, 1, u32> l;
BitField<5, 1, u32> r;
BitField<6, 1, u32> zl;
BitField<7, 1, u32> zr;
BitField<8, 1, u32> plus;
BitField<9, 1, u32> minus;
BitField<10, 1, u32> d_left;
BitField<11, 1, u32> d_up;
BitField<12, 1, u32> d_right;
BitField<13, 1, u32> d_down;
};
};
static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
// This is nn::hid::ConsoleSixAxisSensorHandle
struct ConsoleSixAxisSensorHandle {
u8 unknown_1;
u8 unknown_2;
INSERT_PADDING_BYTES_NOINIT(2);
};
static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
"ConsoleSixAxisSensorHandle is an invalid size");
// This is nn::hid::SixAxisSensorHandle
struct SixAxisSensorHandle {
NpadStyleIndex npad_type;
u8 npad_id;
DeviceIndex device_index;
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
struct SixAxisSensorFusionParameters {
f32 parameter1;
f32 parameter2;
};
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
NpadStyleIndex npad_type;
u8 npad_id;
DeviceIndex device_index;
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
// This is nn::hid::VibrationValue
struct VibrationValue {
f32 low_amplitude;
f32 low_frequency;
f32 high_amplitude;
f32 high_frequency;
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
.low_amplitude = 0.0f,
.low_frequency = 160.0f,
.high_amplitude = 0.0f,
.high_frequency = 320.0f,
};
// This is nn::hid::VibrationDeviceInfo
struct VibrationDeviceInfo {
VibrationDeviceType type{};
VibrationDevicePosition position{};
};
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
// This is nn::hid::KeyboardModifier
struct KeyboardModifier {
union {
u32 raw{};
BitField<0, 1, u32> control;
BitField<1, 1, u32> shift;
BitField<2, 1, u32> left_alt;
BitField<3, 1, u32> right_alt;
BitField<4, 1, u32> gui;
BitField<8, 1, u32> caps_lock;
BitField<9, 1, u32> scroll_lock;
BitField<10, 1, u32> num_lock;
BitField<11, 1, u32> katakana;
BitField<12, 1, u32> hiragana;
};
};
static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");
// This is nn::hid::KeyboardAttribute
struct KeyboardAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
};
};
static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");
// This is nn::hid::KeyboardKey
struct KeyboardKey {
// This should be a 256 bit flag
std::array<u8, 32> key;
};
static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
// This is nn::hid::MouseButton
struct MouseButton {
union {
u32_le raw{};
BitField<0, 1, u32> left;
BitField<1, 1, u32> right;
BitField<2, 1, u32> middle;
BitField<3, 1, u32> forward;
BitField<4, 1, u32> back;
};
};
static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");
// This is nn::hid::MouseAttribute
struct MouseAttribute {
union {
u32 raw{};
BitField<0, 1, u32> transferable;
BitField<1, 1, u32> is_connected;
};
};
static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");
// This is nn::hid::detail::MouseState
struct MouseState {
s64 sampling_number;
s32 x;
s32 y;
s32 delta_x;
s32 delta_y;
// Axis Order in HW is switched for the wheel
s32 delta_wheel_y;
s32 delta_wheel_x;
MouseButton button;
MouseAttribute attribute;
};
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
/// Converts a NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return 0;
case NpadIdType::Player2:
return 1;
case NpadIdType::Player3:
return 2;
case NpadIdType::Player4:
return 3;
case NpadIdType::Player5:
return 4;
case NpadIdType::Player6:
return 5;
case NpadIdType::Player7:
return 6;
case NpadIdType::Player8:
return 7;
case NpadIdType::Handheld:
return 8;
case NpadIdType::Other:
return 9;
default:
return 0;
}
}
/// Converts an array index to a NpadIdType
constexpr NpadIdType IndexToNpadIdType(size_t index) {
switch (index) {
case 0:
return NpadIdType::Player1;
case 1:
return NpadIdType::Player2;
case 2:
return NpadIdType::Player3;
case 3:
return NpadIdType::Player4;
case 4:
return NpadIdType::Player5;
case 5:
return NpadIdType::Player6;
case 6:
return NpadIdType::Player7;
case 7:
return NpadIdType::Player8;
case 8:
return NpadIdType::Handheld;
case 9:
return NpadIdType::Other;
default:
return NpadIdType::Invalid;
}
}
} // namespace Core::HID
-383
View File
@@ -1,383 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#include <random>
#include "common/input.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
switch (callback.type) {
case Common::Input::InputType::Analog:
case Common::Input::InputType::Trigger: {
const auto value = TransformToTrigger(callback).analog.value;
battery = Common::Input::BatteryLevel::Empty;
if (value > 0.2f) {
battery = Common::Input::BatteryLevel::Critical;
}
if (value > 0.4f) {
battery = Common::Input::BatteryLevel::Low;
}
if (value > 0.6f) {
battery = Common::Input::BatteryLevel::Medium;
}
if (value > 0.8f) {
battery = Common::Input::BatteryLevel::Full;
}
if (value >= 1.0f) {
battery = Common::Input::BatteryLevel::Charging;
}
break;
}
case Common::Input::InputType::Button:
battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
: Common::Input::BatteryLevel::Critical;
break;
case Common::Input::InputType::Battery:
battery = callback.battery_status;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
break;
}
return battery;
}
Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
Common::Input::ButtonStatus status{};
switch (callback.type) {
case Common::Input::InputType::Analog:
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
break;
case Common::Input::InputType::Button:
status = callback.button_status;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
break;
}
if (status.inverted) {
status.value = !status.value;
}
return status;
}
Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
Common::Input::MotionStatus status{};
switch (callback.type) {
case Common::Input::InputType::Button: {
Common::Input::AnalogProperties properties{
.deadzone = 0.0f,
.range = 1.0f,
.offset = 0.0f,
};
status.delta_timestamp = 5000;
status.force_update = true;
status.accel.x = {
.value = 0.0f,
.raw_value = 0.0f,
.properties = properties,
};
status.accel.y = {
.value = 0.0f,
.raw_value = 0.0f,
.properties = properties,
};
status.accel.z = {
.value = 0.0f,
.raw_value = -1.0f,
.properties = properties,
};
status.gyro.x = {
.value = 0.0f,
.raw_value = 0.0f,
.properties = properties,
};
status.gyro.y = {
.value = 0.0f,
.raw_value = 0.0f,
.properties = properties,
};
status.gyro.z = {
.value = 0.0f,
.raw_value = 0.0f,
.properties = properties,
};
if (TransformToButton(callback).value) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<s16> distribution(-5000, 5000);
status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
}
break;
}
case Common::Input::InputType::Motion:
status = callback.motion_status;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
break;
}
SanitizeAnalog(status.accel.x, false);
SanitizeAnalog(status.accel.y, false);
SanitizeAnalog(status.accel.z, false);
SanitizeAnalog(status.gyro.x, false);
SanitizeAnalog(status.gyro.y, false);
SanitizeAnalog(status.gyro.z, false);
return status;
}
Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
Common::Input::StickStatus status{};
switch (callback.type) {
case Common::Input::InputType::Stick:
status = callback.stick_status;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
break;
}
SanitizeStick(status.x, status.y, true);
const auto& properties_x = status.x.properties;
const auto& properties_y = status.y.properties;
const float x = status.x.value;
const float y = status.y.value;
// Set directional buttons
status.right = x > properties_x.threshold;
status.left = x < -properties_x.threshold;
status.up = y > properties_y.threshold;
status.down = y < -properties_y.threshold;
return status;
}
Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
Common::Input::TouchStatus status{};
switch (callback.type) {
case Common::Input::InputType::Touch:
status = callback.touch_status;
break;
case Common::Input::InputType::Stick:
status.x = callback.stick_status.x;
status.y = callback.stick_status.y;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
break;
}
SanitizeAnalog(status.x, true);
SanitizeAnalog(status.y, true);
float& x = status.x.value;
float& y = status.y.value;
// Adjust if value is inverted
x = status.x.properties.inverted ? 1.0f + x : x;
y = status.y.properties.inverted ? 1.0f + y : y;
// clamp value
x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f);
if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value;
}
return status;
}
Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
Common::Input::TriggerStatus status{};
float& raw_value = status.analog.raw_value;
bool calculate_button_value = true;
switch (callback.type) {
case Common::Input::InputType::Analog:
status.analog.properties = callback.analog_status.properties;
raw_value = callback.analog_status.raw_value;
break;
case Common::Input::InputType::Button:
status.analog.properties.range = 1.0f;
status.analog.properties.inverted = callback.button_status.inverted;
raw_value = callback.button_status.value ? 1.0f : 0.0f;
break;
case Common::Input::InputType::Trigger:
status = callback.trigger_status;
calculate_button_value = false;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
break;
}
SanitizeAnalog(status.analog, true);
const auto& properties = status.analog.properties;
float& value = status.analog.value;
// Set button status
if (calculate_button_value) {
status.pressed.value = value > properties.threshold;
}
// Adjust if value is inverted
value = properties.inverted ? 1.0f + value : value;
// clamp value
value = std::clamp(value, 0.0f, 1.0f);
return status;
}
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
Common::Input::AnalogStatus status{};
switch (callback.type) {
case Common::Input::InputType::Analog:
status.properties = callback.analog_status.properties;
status.raw_value = callback.analog_status.raw_value;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
break;
}
SanitizeAnalog(status, false);
// Adjust if value is inverted
status.value = status.properties.inverted ? -status.value : status.value;
return status;
}
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
float& raw_value = analog.raw_value;
float& value = analog.value;
if (!std::isnormal(raw_value)) {
raw_value = 0;
}
// Apply center offset
raw_value -= properties.offset;
// Set initial values to be formated
value = raw_value;
// Calculate vector size
const float r = std::abs(value);
// Return zero if value is smaller than the deadzone
if (r <= properties.deadzone || properties.deadzone == 1.0f) {
analog.value = 0;
return;
}
// Adjust range of value
const float deadzone_factor =
1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
value = value * deadzone_factor / properties.range;
// Invert direction if needed
if (properties.inverted) {
value = -value;
}
// Clamp value
if (clamp_value) {
value = std::clamp(value, -1.0f, 1.0f);
}
}
void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
bool clamp_value) {
const auto& properties_x = analog_x.properties;
const auto& properties_y = analog_y.properties;
float& raw_x = analog_x.raw_value;
float& raw_y = analog_y.raw_value;
float& x = analog_x.value;
float& y = analog_y.value;
if (!std::isnormal(raw_x)) {
raw_x = 0;
}
if (!std::isnormal(raw_y)) {
raw_y = 0;
}
// Apply center offset
raw_x += properties_x.offset;
raw_y += properties_y.offset;
// Apply X scale correction from offset
if (std::abs(properties_x.offset) < 0.5f) {
if (raw_x > 0) {
raw_x /= 1 + properties_x.offset;
} else {
raw_x /= 1 - properties_x.offset;
}
}
// Apply Y scale correction from offset
if (std::abs(properties_y.offset) < 0.5f) {
if (raw_y > 0) {
raw_y /= 1 + properties_y.offset;
} else {
raw_y /= 1 - properties_y.offset;
}
}
// Invert direction if needed
raw_x = properties_x.inverted ? -raw_x : raw_x;
raw_y = properties_y.inverted ? -raw_y : raw_y;
// Set initial values to be formated
x = raw_x;
y = raw_y;
// Calculate vector size
float r = x * x + y * y;
r = std::sqrt(r);
// TODO(German77): Use deadzone and range of both axis
// Return zero if values are smaller than the deadzone
if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
x = 0;
y = 0;
return;
}
// Adjust range of joystick
const float deadzone_factor =
1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
x = x * deadzone_factor / properties_x.range;
y = y * deadzone_factor / properties_x.range;
r = r * deadzone_factor / properties_x.range;
// Normalize joystick
if (clamp_value && r > 1.0f) {
x /= r;
y /= r;
}
}
} // namespace Core::HID
-96
View File
@@ -1,96 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#pragma once
namespace Common::Input {
struct CallbackStatus;
enum class BatteryLevel : u32;
using BatteryStatus = BatteryLevel;
struct AnalogStatus;
struct ButtonStatus;
struct MotionStatus;
struct StickStatus;
struct TouchStatus;
struct TriggerStatus;
}; // namespace Common::Input
namespace Core::HID {
/**
* Converts raw input data into a valid battery status.
*
* @param callback Supported callbacks: Analog, Battery, Trigger.
* @return A valid BatteryStatus object.
*/
Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid button status. Applies invert properties to the output.
*
* @param callback Supported callbacks: Analog, Button, Trigger.
* @return A valid TouchStatus object.
*/
Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid motion status.
*
* @param callback Supported callbacks: Motion.
* @return A valid TouchStatus object.
*/
Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
* properties to the output.
*
* @param callback Supported callbacks: Stick.
* @return A valid StickStatus object.
*/
Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid touch status.
*
* @param callback Supported callbacks: Touch.
* @return A valid TouchStatus object.
*/
Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
* invert properties to the output. Button status uses the threshold property if necessary.
*
* @param callback Supported callbacks: Analog, Button, Trigger.
* @return A valid TriggerStatus object.
*/
Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid analog status. Applies offset, deadzone, range and
* invert properties to the output.
*
* @param callback Supported callbacks: Analog.
* @return A valid AnalogStatus object.
*/
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties
* @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
*/
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
/**
* Converts raw stick data into a valid stick value
* @param analog_x raw analog data and properties for the x-axis
* @param analog_y raw analog data and properties for the y-axis
* @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
*/
void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
bool clamp_value);
} // namespace Core::HID
@@ -9,7 +9,6 @@
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {
@@ -43,11 +42,6 @@ void GlobalSchedulerContext::PreemptThreads() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
// interrupts that may have occurred.
kernel.PhysicalCore(core_id).Interrupt();
}
}
@@ -9,7 +9,6 @@
#include "core/core.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
@@ -33,7 +32,6 @@ namespace Kernel::Init {
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
+49 -43
View File
@@ -8,7 +8,6 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
@@ -29,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
const auto current_core = system.CurrentCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -59,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
const auto current_core = system.CurrentCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -86,27 +85,6 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
return true;
}
class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearAddressArbiter();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KAddressArbiter::ThreadTree* m_tree;
};
} // namespace
ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
@@ -118,14 +96,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
target_thread->Wakeup();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -151,14 +129,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
target_thread->Wakeup();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -219,14 +197,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
target_thread->Wakeup();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -236,7 +214,6 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -247,6 +224,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
@@ -276,20 +256,31 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
return cur_thread->GetWaitResult();
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
}
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -300,6 +291,9 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
@@ -322,14 +316,26 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
return cur_thread->GetWaitResult();
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
}
} // namespace Kernel
-4
View File
@@ -170,10 +170,6 @@ public:
}
}
const std::string& GetName() const {
return name;
}
private:
void RegisterWithKernel();
void UnregisterWithKernel();
+2 -3
View File
@@ -6,7 +6,6 @@
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -49,7 +48,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
// Ensure that the token hierarchy is correct.
@@ -80,7 +79,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.
-146
View File
@@ -1,146 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
KCodeMemory::KCodeMemory(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
m_owner = kernel.CurrentProcess();
// Get the owner page table.
auto& page_table = m_owner->PageTable();
// Construct the page group.
KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))
// Clear the memory.
for (const auto& block : m_page_group.Nodes()) {
std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
}
// Set remaining tracking members.
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
m_is_mapped = false;
// We succeeded.
return ResultSuccess;
}
void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
m_owner->PageTable().UnlockForCodeMemory(m_address, size);
}
}
ResultCode KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_mapped, ResultInvalidState);
// Map the memory.
R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
// Mark ourselves as mapped.
m_is_mapped = true;
return ResultSuccess;
}
ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
KMemoryState::CodeOut));
// Mark ourselves as unmapped.
m_is_mapped = false;
return ResultSuccess;
}
ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
// Convert the memory permission.
KMemoryPermission k_perm{};
switch (perm) {
case Svc::MemoryPermission::Read:
k_perm = KMemoryPermission::UserRead;
break;
case Svc::MemoryPermission::ReadExecute:
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
break;
}
// Map the memory.
R_TRY(
m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
// Mark ourselves as mapped.
m_is_owner_mapped = true;
return ResultSuccess;
}
ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
// Mark ourselves as unmapped.
m_is_owner_mapped = false;
return ResultSuccess;
}
} // namespace Kernel
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
enum class CodeMemoryOperation : u32 {
Map = 0,
MapToOwner = 1,
Unmap = 2,
UnmapFromOwner = 3,
};
class KCodeMemory final
: public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
public:
explicit KCodeMemory(KernelCore& kernel_);
ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
void Finalize();
ResultCode Map(VAddr address, size_t size);
ResultCode Unmap(VAddr address, size_t size);
ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
ResultCode UnmapFromOwner(VAddr address, size_t size);
bool IsInitialized() const {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KProcess* GetOwner() const {
return m_owner;
}
VAddr GetSourceAddress() const {
return m_address;
}
size_t GetSize() const {
return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
}
private:
KPageLinkedList m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
bool m_is_initialized{};
bool m_is_owner_mapped{};
bool m_is_mapped{};
};
} // namespace Kernel
+127 -112
View File
@@ -11,7 +11,6 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
@@ -34,7 +33,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
const auto current_core = system.CurrentCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);
@@ -58,48 +57,6 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
return true;
}
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
: KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
private:
KConditionVariable::ThreadTree* m_tree;
public:
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// If the thread is waiting on a condvar, remove it from the tree.
if (waiting_thread->IsWaitingForConditionVariable()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearConditionVariable();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
@@ -121,77 +78,84 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Determine the next tag.
u32 next_value{};
if (next_owner_thread != nullptr) {
if (next_owner_thread) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
// Write the value to userspace.
ResultCode result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
if (next_owner_thread) {
next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
}
// Signal the next owner thread.
next_owner_thread->EndWait(result);
return result;
} else {
// Just write the value to userspace.
R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
ResultInvalidCurrentMemory);
return ResultSuccess;
return ResultInvalidCurrentMemory;
}
}
return ResultSuccess;
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(kernel);
KScopedAutoObject<KThread> owner_thread;
ASSERT(owner_thread.IsNull());
{
KScopedSchedulerLock sl(kernel);
cur_thread->SetSyncedObject(nullptr, ResultSuccess);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
{
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// If the tag isn't the handle (with wait mask), we're done.
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Get the lock owner thread.
owner_thread =
kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
handle);
R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
}
ASSERT(owner_thread.IsNotNull());
}
// Close our reference to the owner thread, now that the wait is over.
owner_thread->Close();
// Remove the thread as a waiter from the lock owner.
{
KScopedSchedulerLock sl(kernel);
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
}
// Get the wait result.
return cur_thread->GetWaitResult();
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
}
void KConditionVariable::SignalImpl(KThread* thread) {
KThread* KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -205,16 +169,18 @@ void KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) [[likely]] {
if (can_access) {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
if (can_access) [[likely]] {
KThread* thread_to_close = nullptr;
if (can_access) {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->EndWait(ResultSuccess);
thread->SetSyncedObject(nullptr, ResultSuccess);
thread->Wakeup();
} else {
// Get the previous owner.
KThread* owner_thread = kernel.CurrentProcess()
@@ -223,22 +189,33 @@ void KConditionVariable::SignalImpl(KThread* thread) {
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
if (owner_thread) [[likely]] {
if (owner_thread) {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
owner_thread->Close();
thread_to_close = owner_thread;
} else {
// The lock was tagged with a thread that doesn't exist.
thread->EndWait(ResultInvalidState);
thread->SetSyncedObject(nullptr, ResultInvalidState);
thread->Wakeup();
}
}
} else {
// If the address wasn't accessible, note so.
thread->EndWait(ResultInvalidCurrentMemory);
thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
thread->Wakeup();
}
return thread_to_close;
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Prepare for signaling.
constexpr int MaxThreads = 16;
KLinkedList<KThread> thread_list{kernel};
std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{};
// Perform signaling.
s32 num_waiters{};
{
@@ -249,7 +226,14 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
this->SignalImpl(target_thread);
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
thread_list.push_back(*thread);
}
}
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
@@ -261,16 +245,27 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
// Close threads in the array.
for (auto i = 0; i < num_to_close; ++i) {
thread_array[i]->Close();
}
// Close threads in the list.
for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
(*it).Close();
}
}
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
kernel, std::addressof(thread_tree));
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -295,7 +290,8 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Wake up the next owner.
next_owner_thread->EndWait(ResultSuccess);
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
}
// Write to the cv key.
@@ -312,21 +308,40 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
}
// If timeout is zero, time out.
R_UNLESS(timeout != 0, ResultTimedOut);
// Update condition variable tracking.
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
{
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
}
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
// If the timeout is non-zero, set the thread as waiting.
if (timeout != 0) {
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
}
// Get the wait result.
return cur_thread->GetWaitResult();
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the condition variable.
{
KScopedSchedulerLock sl(kernel);
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread);
}
if (cur_thread->IsWaitingForConditionVariable()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearConditionVariable();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
}
} // namespace Kernel
+1 -1
View File
@@ -34,7 +34,7 @@ public:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
void SignalImpl(KThread* thread);
[[nodiscard]] KThread* SignalImpl(KThread* thread);
ThreadTree thread_tree;
-6
View File
@@ -13,7 +13,6 @@ ResultCode KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
@@ -44,7 +43,6 @@ bool KHandleTable::Remove(Handle handle) {
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
@@ -64,7 +62,6 @@ bool KHandleTable::Remove(Handle handle) {
}
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -87,7 +84,6 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
}
ResultCode KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -98,7 +94,6 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@@ -117,7 +112,6 @@ void KHandleTable::Unreserve(Handle handle) {
}
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
-2
View File
@@ -68,7 +68,6 @@ public:
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -123,7 +122,6 @@ public:
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.
@@ -1,34 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel::KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
auto* process = kernel.CurrentProcess();
if (!process) {
return;
}
auto& scheduler = kernel.Scheduler(core_id);
auto& current_thread = *scheduler.GetCurrentThread();
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
scheduler.GetCurrentThread()->SetInterruptFlag();
}
}
} // namespace Kernel::KInterruptManager
-17
View File
@@ -1,17 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel {
class KernelCore;
namespace KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id);
}
} // namespace Kernel
@@ -1,80 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
public:
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
bool term)
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
}
// Remove the thread from the waiting thread from the light condition variable.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
bool m_allow_terminating_thread;
};
} // namespace
void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
// Create thread queue.
KThread* owner = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
allow_terminating_thread);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Add the thread to the queue.
wait_list.push_back(*owner);
// Begin waiting.
owner->BeginWait(std::addressof(wait_queue));
}
// Re-acquire the lock.
lock->Lock();
}
void KLightConditionVariable::Broadcast() {
KScopedSchedulerLock lk(kernel);
// Signal all threads.
for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
it->EndWait(ResultSuccess);
}
}
} // namespace Kernel
@@ -2,24 +2,72 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class KernelCore;
class KLightLock;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
void Broadcast();
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
WaitImpl(lock, timeout, allow_terminating_thread);
}
void Broadcast() {
KScopedSchedulerLock lk{kernel};
// Signal all threads.
for (auto& thread : wait_list) {
thread.SetState(ThreadState::Runnable);
}
}
private:
void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
KThread* owner = GetCurrentThreadPointer(kernel);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Set the thread as waiting.
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(GetCurrentThread(kernel));
}
// Remove the thread from the wait list.
{
KScopedSchedulerLock sl{kernel};
wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
}
// Cancel the task that the sleep setup.
kernel.TimeManager().UnscheduleTimeEvent(owner);
// Re-acquire the lock.
lock->Lock();
}
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
+33 -39
View File
@@ -5,59 +5,44 @@
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
const uintptr_t cur_thread_tag = (cur_thread | 1);
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
std::memory_order_acquire)) {
if ((old_tag | 1) == cur_thread_tag) {
return;
}
}
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
break;
}
LockSlowPath(old_tag | 1, cur_thread);
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
this->UnlockSlowPath(cur_thread);
}
do {
if (expected != cur_thread) {
return UnlockSlowPath(cur_thread);
}
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
}
bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
ThreadQueueImplForKLightLock wait_queue(kernel);
// Pend the current thread waiting on the owner thread.
{
@@ -65,7 +50,7 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return false;
return;
}
// Add the current thread as a waiter on the owner.
@@ -73,15 +58,22 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Begin waiting to hold the lock.
cur_thread->BeginWait(std::addressof(wait_queue));
// Set thread states.
cur_thread->SetState(ThreadState::Waiting);
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
return true;
// We're no longer waiting on the lock owner.
{
KScopedSchedulerLock sl{kernel};
if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
}
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
@@ -89,20 +81,22 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
// Unlock.
{
KScopedSchedulerLock sl(kernel);
KScopedSchedulerLock sl{kernel};
// Get the next owner.
s32 num_waiters;
s32 num_waiters = 0;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
next_tag =
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
next_owner->EndWait(ResultSuccess);
next_owner->SetState(ThreadState::Runnable);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
@@ -116,7 +110,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
}
// Write the new tag value.
tag.store(next_tag, std::memory_order_release);
tag.store(next_tag);
}
}

Some files were not shown because too many files have changed in this diff Show More