Compare commits

..

100 Commits

Author SHA1 Message Date
german77
e4c63d432d wall_clock: use standard wall clock if rtsc frequency is too low 2022-01-27 17:07:52 -06:00
bunnei
2136ebccd6 Merge pull request #7761 from v1993/patch-8
Improve FFmpeg cmake file
2022-01-22 23:43:14 -08:00
bunnei
5b57ee66ce Merge pull request #7716 from german77/volume
yuzu: Add volume hotkeys
2022-01-22 02:25:59 -08:00
Valeri
f68be36159 Improve FFmpeg cmake file
* Fix compilation if CUDA_INCLUDE_DIRS is empty/contains multiple paths
* Pass CMAKE_{C,CXX}_COMPILER_LAUNCHER to ffmpeg compiler
2022-01-22 12:56:25 +03:00
bunnei
8433edacb3 Merge pull request #7735 from german77/udp_battery
input_common: Report battery for UDP controllers
2022-01-22 01:28:14 -08:00
bunnei
68c8a1b170 Merge pull request #7737 from bunnei/fix-dummy-thread-leak
Various fixes to HLE service thread management
2022-01-21 22:34:47 -08:00
bunnei
cef7649bed Merge pull request #7752 from Morph1984/SetCpuOverclockEnabled
service: apm: Stub ISession SetCpuOverclockEnabled
2022-01-21 17:13:52 -08:00
bunnei
615fb40416 hle: kernel: KThread: Ensure host (dummy) threads block on locking.
- But do not enter the priority queue, as otherwise they will be scheduled.
- Allows dummy threads to use guest synchronization primitives.
2022-01-21 17:12:06 -08:00
bunnei
d990f043bd Merge pull request #7756 from lioncash/service
service: Update function tables
2022-01-21 14:26:05 -08:00
Lioncash
12bf8ca929 service/wlan: Update function tables 2022-01-21 16:35:51 -05:00
Lioncash
8dd6dc9a29 service/usb: Update function tables 2022-01-21 16:31:57 -05:00
Lioncash
43039d95ac service/set: Update function tables 2022-01-21 16:26:12 -05:00
Lioncash
7ba6f68a8d service/ns: Update function tables 2022-01-21 16:21:35 -05:00
Lioncash
19c11e2fd2 service/nim: Update unknown function table entries 2022-01-21 16:17:40 -05:00
Lioncash
888bc10c17 service/friend: Update unknown function table entries 2022-01-21 16:08:46 -05:00
Lioncash
54150f5afa service/filsystem: Update fsp-srv function table 2022-01-21 16:07:01 -05:00
Lioncash
3da43ad2a9 service/btm: Update function tables 2022-01-21 15:59:41 -05:00
Lioncash
0f7337c522 service/audio: Update audctl unknown function names 2022-01-21 15:50:38 -05:00
Lioncash
29e3636e0a service/am: Update omm function tables 2022-01-21 15:40:08 -05:00
Lioncash
a271cf89cf service/acc: Update unknown function names
Switchbrew has the function names now.
2022-01-21 15:33:22 -05:00
bunnei
88d9b55b14 Merge pull request #7755 from v1993/someone-in-here-lacks-system-wide-theming
Use Default Colorful theme by default outside of Windows
2022-01-21 11:51:29 -08:00
bunnei
03cf308c16 Merge pull request #7731 from v1993/xfb-varying-check-fix
shader_recompiler: fix potential OOB access
2022-01-21 10:45:56 -08:00
v1993
d63d8bf7f9 Use Default Colorful theme by default outside of Windows
On OSes with system-wide theming this allows yuzu to follow system style, regardless of its exact coloration, working well with both light and dark system themes. Dark /Colorful, on the other hand, forces dark theme regardless of user preferences set in system settings, making for a poor default.

Use Colorful variation to keep in line with icon style of patron-voted Dark Colorful.
2022-01-21 20:57:00 +03:00
Morph
a396473201 service: apm: Stub ISession SetCpuOverclockEnabled
Since we don't currently support CPU overclocking within the emulated system, this can be stubbed for now, like APM IsCpuOverclockEnabled.

- Used by Gravity Rider Zero
2022-01-20 21:07:26 -05:00
bunnei
ef7c50b276 Merge pull request #7695 from Morph1984/is-pow2
common: bit_util: Add IsPow2 helper function
2022-01-20 18:06:11 -08:00
bunnei
f6815086a1 hle: kernel: Remove redundant tracking of dummy threads.
- These are already tracked by kernel's registered_objects member.
2022-01-20 17:08:00 -08:00
bunnei
91ff6d4cb3 hle: kernel: KThread: DummyThread can be waited, ensure wait_queue is not nullptr. 2022-01-20 17:08:00 -08:00
bunnei
46a620f9d7 hle: kernel: KThread: Decrease DummyThread priority to ensure it is never scheduled. 2022-01-20 17:08:00 -08:00
bunnei
0b37e7cb39 hle: kernel: service_thread: Ensure dummy thread is closed & destroyed on thread exit. 2022-01-20 17:08:00 -08:00
bunnei
384e24d3e9 hle: kernel: KServerSession: Remove hack for CompleteSyncRequest.
- This does not appear to be necessary anymore.
2022-01-20 17:08:00 -08:00
bunnei
ad53dc22fd hle: kernel: KServerSession: Simplify CompleteSyncRequest EndWait.
- Considering is_thread_waiting is never set, so we can remove IsThreadWaiting.
- KThread::EndWait will take the scheduler lock, so we can remove the redundant lock.
2022-01-20 17:08:00 -08:00
bunnei
5ffec69dc7 hle: kernel: KThread: Ensure dummy threads never call EndWait.
- These are only used by host threads for locking and will never have a wait_queue.
2022-01-20 17:08:00 -08:00
bunnei
11a380c3da hle: kernel: KScheduler: Ensure dummy threads are never scheduled.
- These are only used by host threads for locking.
2022-01-20 17:08:00 -08:00
bunnei
f6cbb14dce hle: kernel: KThread: Rename thread_type_for_debugging -> thread_type.
- This will be used to ensure that we do not schedule dummy threads.
2022-01-20 17:08:00 -08:00
bunnei
e781f6e767 Merge pull request #7710 from german77/just-shake-it
core/hid: Increment shake force
2022-01-20 16:53:22 -08:00
Mai M
55ef89a9c7 Merge pull request #7749 from jbeich/ffmpeg5
video_core: unbreak build with FFmpeg 5.0
2022-01-20 13:56:32 -05:00
Jan Beich
d24a4b79d4 video_core: constify AVCodec for ffmpeg >= 5.0
src/video_core/command_classes/codecs/codec.cpp:177:16: error: assigning to 'AVCodec *' from 'const AVCodec *' discards qualifiers
    av_codec = avcodec_find_decoder(codec);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~
2022-01-20 11:09:21 +00:00
Morph
dc471700ca Merge pull request #7726 from german77/clamp
service/hid: Initialize applet_resource on SetNpadAnalogStickUseCenterClamp
2022-01-19 14:31:15 -08:00
Fernando S
490df43f34 Merge pull request #7747 from DadSchoorse/fix-anv-64bit-atomic-stype
vulkan_device: Fix sType for VkPhysicalDeviceShaderAtomicInt64Features
2022-01-19 23:01:53 +01:00
german77
8fa0416fee service/hid: Initialize applet_resource on SetNpadAnalogStickUseCenterClamp 2022-01-19 14:20:31 -06:00
Georg Lehmann
d15814d39f vulkan_device: Fix sType for VkPhysicalDeviceShaderAtomicInt64Features 2022-01-19 19:48:46 +01:00
bunnei
b5e83bcc7b Merge pull request #7701 from bunnei/clear-mem-pages
Kernel Memory Updates (Part 3): Clear KMemoryManager pages & other fixes
2022-01-18 21:20:42 -08:00
bunnei
fc3eb72359 Merge pull request #7715 from gidoly/patch-4
Xbox controller default name nit pick
2022-01-18 21:14:30 -08:00
bunnei
eceee8e5f4 Merge pull request #7725 from german77/mouse_in_motion
input_common: Reintroduce motion from mouse and use button names
2022-01-18 18:16:27 -08:00
bunnei
101d86897b Merge pull request #7712 from bunnei/fix-thread-exit
Accurately implement thread exit
2022-01-17 18:08:24 -08:00
bunnei
1dbb9e353e Merge pull request #7724 from ameerj/astc_new_nv
astc_decoder: Combine FastReplicate functions to work around new NV driver bug
2022-01-17 16:48:04 -08:00
bunnei
5eb0b65590 Merge pull request #7732 from v1993/patch-7
kernel: remove no-op code
2022-01-17 15:37:16 -08:00
Narr the Reg
36144a5690 input_common: Report battery for UDP controllers 2022-01-17 13:30:15 -06:00
v1993
a943600019 shader_recompiler: fix potential OOB access
Found by static analysis with PVS-Studio. Original check wasn't actually checking for OOB and would segfault in case of it.
2022-01-17 21:50:51 +03:00
Mai M
45ac3f414e Merge pull request #7730 from v1993/patch-6
input_common: nitpick about SetHatButton usage
2022-01-17 13:43:34 -05:00
Mai M
b92198b7f8 Merge pull request #7729 from v1993/patch-5
input_common: fix copy-paste error
2022-01-17 13:43:21 -05:00
Mai M
cf4893410b Merge pull request #7728 from v1993/patch-4
hid: fix std::transform call
2022-01-17 13:43:11 -05:00
Mai M
345bcd4cdf Merge pull request #7727 from v1993/patch-3
hid: Correct assignment source for rotations
2022-01-17 13:42:57 -05:00
Valeri
84786dde00 hle: remove no-op code
Found by static analysis with PVS-Studio. Nobody seems to really know what was it doing there.
2022-01-17 13:51:12 +03:00
bunnei
ca2d904770 Merge pull request #7719 from gidoly/patch-6
Change default theme to Dark colorful
2022-01-17 01:04:47 -08:00
Valeri
3431e0acf5 input_common: nitpick about SetHatButton usage 2022-01-17 12:04:38 +03:00
Valeri
a99f84f4b2 input_common: fix copy-paste error
Found by static analysis with PVS-Studio.
2022-01-17 11:31:38 +03:00
Valeri
8eddafd9d0 hid: fix std::transform call
Found by static analysis with PVS-Studio.
2022-01-17 10:47:38 +03:00
Morph
78e233c460 uisettings: Add enumeration type for themes
Eliminates the usage of a magic number to indicate the default index of the themes array,
2022-01-17 02:46:30 -05:00
gidoly
789af19c60 config: Change default theme to Dark Colorful 2022-01-17 02:41:53 -05:00
Valeri
c624edceba Correct assignment source for rotations
Found by static analysis with PVS-Studio
2022-01-17 10:40:41 +03:00
german77
9eab07f863 input_common: Reintroduce motion from mouse and use button names 2022-01-16 20:37:34 -06:00
ameerj
a5bff8e9b3 astc_decoder: Combine FastReplicate functions to work around new NV driver bug
The new Nvidia drivers have a bug where the FastReplicateTo6 function produces a lookup into the REPLICATE_TO_8 table rather than the REPLICATE_TO_6 table.

This seems to be an optimization gone wrong. Combining the logic of the FastReplicate functions seems to address the bug.
2022-01-16 16:13:20 -05:00
german77
850896a52b audio/stream: Adjust volume scale factor 2022-01-15 20:28:37 -06:00
german77
c8b3a12856 yuzu: Add volume up/down hotkeys 2022-01-15 20:28:37 -06:00
gidoly
92d676d788 Xbox controller default name nit pick
Discord User moon lacer pointed us that official name is 'Xbox' not 'XBox'
2022-01-16 05:59:31 +09:00
german77
419f427a01 yuzu: Remove speed limit hotkeys 2022-01-15 13:44:45 -06:00
bunnei
480b03b645 Merge pull request #7713 from gidoly/patch-3
Change default name for playstation controllers
2022-01-15 02:39:58 -08:00
bunnei
5eda606952 Merge pull request #7711 from bunnei/fix-service-thread-race-v2
hle: kernel: Fix service_threads access to be thread safe V2.
2022-01-14 22:22:39 -08:00
bunnei
b54cbc985e hle: kernel: k_memory_manager: Clear pages on allocation & free.
- Heap pages should be zero'd.
- Also explicitly passed along heap allocation option.
2022-01-14 21:16:33 -08:00
gidoly
7978ea4e8c Change default name for ps controllers
Minor nitpick
Code is from narr
2022-01-15 10:29:42 +09:00
bunnei
4064e03568 Merge pull request #7707 from german77/slow-update
service/hid: Decrease motion update rate
2022-01-14 17:13:30 -08:00
bunnei
f499c8177e core: hle: kernel: KThread: Integrate with KWorkerTask and implement DoWorkerTaskImpl.
- This is used to terminate a thread asynchronously after it has been exited.
- This fixes a crash that can occur in Pokemon Sword/Shield because a thread is incorrectly closed on svcExitThread, then, the thread is destroyed on svcCloseHandle while it is still scheduled.
- Instead, we now wait for the thread to no longer be scheduled on all cores before destroying it from KWorkerTaskManager, which is accurate to HOS behavior.
2022-01-14 16:44:14 -08:00
bunnei
d8b3f665db core: hle: kernel: KProcess: Integrate with KWorkerTask and add unimplemented DoWorkerTaskImpl. 2022-01-14 16:44:14 -08:00
bunnei
03884b7ea6 core: hle: kernel: KThread: Replace Suspend with UpdateState & various updates.
- This makes our implementations of these more closely match HOS.
2022-01-14 16:44:14 -08:00
bunnei
c905044e1b core: hle: kernel: Instantiate a kernel instance of KWorkerTaskManager. 2022-01-14 16:44:14 -08:00
bunnei
bf32fcc817 core: hle: kernel: Add KWorkerTask and KWorkerTaskManager.
- These primitives are used to dispatch asynchronous kernel tasks from KThread and KProcess.
2022-01-14 16:43:59 -08:00
bunnei
f6de57c1a5 common: fiber: YieldTo: Avoid hard crash on nullptr previous_fiber.
- When the emulator crashes to desktop below, we don't even get this captured in a log, making such issues harder to debug.
2022-01-14 16:17:19 -08:00
bunnei
cc112f971e hle: kernel: Fix service_threads access to be thread safe V2.
- PR #7699 attempted to fix CreateServiceThread and ReleaseServiceThread to be thread safe, but inadvertently introduced a possible dead-lock.
- With this PR, we use a worker thread to manage the service thread list, allowing it only to be accessed by a single thread, and guaranteeing threads will not destroy themselves.
- Fixes a rare crash in Pokemon Sword/Shield, I've now run this game for ~12 hours non-stop and am quite confident this is a good solution for this issue.
2022-01-14 16:02:57 -08:00
Narr the Reg
5ce5432830 core/hid: Increment shake force
With the current settings 2p mode in pokemon let's go wasn't showing up.  By making the shake more violent we can make it appear without any effort using the keyboard
2022-01-14 11:11:20 -06:00
Mai M
b2d45a4072 Merge pull request #7699 from bunnei/fix-service-thread-race
hle: kernel: Fix service_threads access to be thread safe.
2022-01-14 00:46:16 -05:00
Mai M
07e477f891 Merge pull request #7698 from bunnei/mem-code-memory-updates
Kernel Memory Updates (Part 2): SetProcessMemoryPermission, update permissions, and other minor changes.
2022-01-14 00:41:17 -05:00
bunnei
2147240e47 hle: kernel: Fix service_threads access to be thread safe.
- CreateServiceThread and ReleaseServiceThread can be accessed by different threads, uses a lock to make this thread safe.
- Fixes a rare crash in Pokemon Sword/Shield that can occur when a new service thread is being created while an old one is being destroyed.
2022-01-13 21:26:10 -08:00
bunnei
f1aa7ff893 Merge pull request #7690 from Morph1984/increase-file-limit-win
yuzu: main: Increase the open file limit on Windows to 8192
2022-01-13 21:25:06 -08:00
bunnei
b3bcf0fa88 Merge pull request #7700 from german77/no-gyro
core/hid: Reduce gyro threshold even more
2022-01-13 21:24:41 -08:00
Narr the Reg
8185509683 service/hid: Decrease motion update rate
Motion stops working in Mario Tennis in swing mode if the update rate is too fast even when HW it updates at the same speed. 10ms it's the minimum period that the game needs to start working again.
2022-01-12 22:55:33 -06:00
Narr the Reg
d348070226 core/hid: Reduce gyro threshold even more 2022-01-11 23:15:39 -06:00
bunnei
eb7197eb47 Merge pull request #7697 from abouvier/opt-tests
cmake: make tests optional
2022-01-11 20:58:16 -08:00
bunnei
49a0e4330e hle: kernel: k_page_table: Update SetProcessMemoryPermission. 2022-01-11 16:28:11 -08:00
bunnei
6ac44f3bdc hle: service: ldr: UnmapCodeMemory BSS only when set. 2022-01-11 16:28:11 -08:00
bunnei
6123b6ea45 hle: kernel: k_page_table: ReadAndWrite -> UserReadWrite. 2022-01-11 16:28:11 -08:00
bunnei
081669c334 hle: kernel: k_page_table: Rename *ProcessCodeMemory -> *CodeMemory. 2022-01-11 16:28:11 -08:00
bunnei
599c0763e5 Merge pull request #7684 from bunnei/set-mem-perm-attr
Kernel Memory Updates (Part 1): SetMemoryAttribute, and other minor changes.
2022-01-11 16:26:17 -08:00
Alexandre Bouvier
c8b358dba2 cmake: make tests optional 2022-01-12 00:36:20 +01:00
bunnei
c65c651b6f Merge pull request #7633 from german77/hotkeys
yuzu: Add controller hotkeys
2022-01-11 10:49:23 -08:00
Morph
36da4d1121 yuzu: main: Increase the open file limit on Windows to 8192
This is a temporary solution for now to accommodate for mods containing more than 4096 files.
2022-01-09 21:33:58 -05:00
bunnei
af4696657c core: hle: kernel: svc: Updates to SetMemoryAttribute and SetMemoryPermission. 2022-01-08 12:18:14 -08:00
bunnei
b9a313057e core: hle: kernel: k_page_table: Update CheckMemoryState. 2022-01-08 03:20:57 -08:00
german77
72c8a94a6c yuzu: Add controller hotkeys 2022-01-06 21:26:05 -06:00
german77
b94e947793 core/hid: Add home and screenshot button support 2022-01-06 21:11:27 -06:00
79 changed files with 1640 additions and 581 deletions

View File

@@ -35,6 +35,8 @@ 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)
@@ -168,7 +170,6 @@ 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
"Catch2 2.13.7 catch2/2.13.7"
"fmt 8.0.1 fmt/8.1.1"
"lz4 1.8 lz4/1.9.2"
"nlohmann_json 3.8 nlohmann_json/3.8.0"
@@ -177,6 +178,11 @@ 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})

View File

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

View File

@@ -95,7 +95,8 @@ if (NOT WIN32)
# 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})
# This line causes build error if CUDA_INCLUDE_DIRS is anything but a single non-empty value
#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})
@@ -119,6 +120,8 @@ if (NOT WIN32)
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER})
set(FFmpeg_CXX ${CMAKE_CXX_COMPILER_LAUNCHER} ${CMAKE_CXX_COMPILER})
add_custom_command(
OUTPUT
${FFmpeg_MAKEFILE}
@@ -137,12 +140,14 @@ if (NOT WIN32)
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
--cc="${CMAKE_C_COMPILER}"
--cxx="${CMAKE_CXX_COMPILER}"
--cc="${FFmpeg_CC}"
--cxx="${FFmpeg_CXX}"
${FFmpeg_HWACCEL_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_CC)
unset(FFmpeg_CXX)
unset(FFmpeg_HWACCEL_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child

View File

@@ -151,7 +151,10 @@ add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(input_common)
add_subdirectory(shader_recompiler)
add_subdirectory(tests)
if (YUZU_TESTS)
add_subdirectory(tests)
endif()
if (ENABLE_SDL2)
add_subdirectory(yuzu_cmd)

View File

@@ -79,8 +79,8 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
return;
}
// Implementation of a volume slider with a dynamic range of 60 dB
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
// Perceived volume is not the same as the volume level
const float volume_scale_factor = (0.85f * ((volume * volume) - volume)) + volume;
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}

View File

@@ -124,7 +124,10 @@ 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()) {
ASSERT(from->impl->previous_fiber != nullptr);
if (from->impl->previous_fiber == nullptr) {
ASSERT_MSG(false, "previous_fiber is nullptr!");
return;
}
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();

View File

@@ -209,6 +209,13 @@ enum class ButtonNames {
Triangle,
Share,
Options,
// Mouse buttons
ButtonMouseWheel,
ButtonBackward,
ButtonForward,
ButtonTask,
ButtonExtra,
};
// Callback data consisting of an input type and the equivalent data status

View File

@@ -72,7 +72,9 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
if (caps.invariant_tsc) {
rtsc_frequency = EstimateRDTSCFrequency();
}
if (rtsc_frequency == 0) {
// Fallback to StandardWallClock if rtsc period is higher than a nano second
if (rtsc_frequency <= 1000000000) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
emulated_clock_frequency);
} else {

View File

@@ -247,6 +247,9 @@ add_library(core STATIC
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

View File

@@ -158,7 +158,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
auto& motion = console.motion_state;
motion.accel = emulated.GetAcceleration();
motion.gyro = emulated.GetGyroscope();
motion.rotation = emulated.GetGyroscope();
motion.rotation = emulated.GetRotations();
motion.orientation = emulated.GetOrientation();
motion.quaternion = emulated.GetQuaternion();
motion.gyro_bias = emulated.GetGyroBias();

View File

@@ -145,7 +145,7 @@ void EmulatedController::LoadDevices() {
motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
@@ -351,6 +351,19 @@ void EmulatedController::DisableConfiguration() {
}
}
void EmulatedController::EnableSystemButtons() {
system_buttons_enabled = true;
}
void EmulatedController::DisableSystemButtons() {
system_buttons_enabled = false;
}
void EmulatedController::ResetSystemButtons() {
controller.home_button_state.home.Assign(false);
controller.capture_button_state.capture.Assign(false);
}
bool EmulatedController::IsConfiguring() const {
return is_configuring;
}
@@ -600,7 +613,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
controller.npad_button_state.right_sr.Assign(current_status.value);
break;
case Settings::NativeButton::Home:
if (!system_buttons_enabled) {
break;
}
controller.home_button_state.home.Assign(current_status.value);
break;
case Settings::NativeButton::Screenshot:
if (!system_buttons_enabled) {
break;
}
controller.capture_button_state.capture.Assign(current_status.value);
break;
}
}
@@ -1081,6 +1103,20 @@ BatteryValues EmulatedController::GetBatteryValues() const {
return controller.battery_values;
}
HomeButtonState EmulatedController::GetHomeButtons() const {
if (is_configuring) {
return {};
}
return controller.home_button_state;
}
CaptureButtonState EmulatedController::GetCaptureButtons() const {
if (is_configuring) {
return {};
}
return controller.capture_button_state;
}
NpadButtonState EmulatedController::GetNpadButtons() const {
if (is_configuring) {
return {};

View File

@@ -101,6 +101,8 @@ struct ControllerStatus {
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{};
@@ -198,6 +200,15 @@ public:
/// 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;
@@ -261,7 +272,13 @@ public:
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
/// Returns the latest status of button input for the npad service
/// 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
@@ -383,6 +400,7 @@ private:
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};

View File

@@ -378,6 +378,26 @@ struct LedPattern {
};
};
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{};

View File

@@ -114,7 +114,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
if (TransformToButton(callback).value) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<s16> distribution(-1000, 1000);
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;

View File

@@ -10,7 +10,7 @@ namespace Core::HID {
MotionInput::MotionInput() {
// Initialize PID constants with default values
SetPID(0.3f, 0.005f, 0.0f);
SetGyroThreshold(0.001f);
SetGyroThreshold(0.00005f);
}
void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {

View File

@@ -341,10 +341,6 @@ public:
return *thread;
}
bool IsThreadWaiting() const {
return is_thread_waiting;
}
private:
friend class IPC::ResponseBuilder;
@@ -379,7 +375,6 @@ private:
u32 domain_offset{};
std::shared_ptr<SessionRequestManager> manager;
bool is_thread_waiting{};
KernelCore& kernel;
Core::Memory::Memory& memory;

View File

@@ -70,12 +70,12 @@ enum class KMemoryState : u32 {
ThreadLocal =
static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@@ -93,6 +93,8 @@ enum class KMemoryState : u32 {
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
FlagReferenceCounted | FlagCanDebug,
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
@@ -108,8 +110,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
@@ -117,6 +119,7 @@ static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
enum class KMemoryPermission : u8 {
None = 0,
@@ -155,7 +158,13 @@ enum class KMemoryPermission : u8 {
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) {
return static_cast<KMemoryPermission>(perm);
return static_cast<KMemoryPermission>(
(static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) |
KMemoryPermission::KernelRead |
((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite)
<< KMemoryPermission::KernelShift) |
(perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped
: KMemoryPermission::None));
}
enum class KMemoryAttribute : u8 {
@@ -169,6 +178,8 @@ enum class KMemoryAttribute : u8 {
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
SetMask = Uncached,
IpcAndDeviceMapped = IpcLocked | DeviceShared,
LockedAndIpcLocked = Locked | IpcLocked,
DeviceSharedAndUncached = DeviceShared | Uncached
@@ -215,6 +226,15 @@ struct KMemoryInfo {
constexpr VAddr GetLastAddress() const {
return GetEndAddress() - 1;
}
constexpr KMemoryState GetState() const {
return state;
}
constexpr KMemoryAttribute GetAttribute() const {
return attribute;
}
constexpr KMemoryPermission GetPermission() const {
return perm;
}
};
class KMemoryBlock final {

View File

@@ -8,12 +8,16 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {}
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
const auto size{end_address - start_address};
@@ -81,7 +85,7 @@ VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size
}
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
Direction dir, u32 heap_fill_value) {
ASSERT(page_list.GetNumPages() == 0);
// Early return if we're allocating no pages
@@ -139,6 +143,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
}
}
// Clear allocated memory.
for (const auto& it : page_list.Nodes()) {
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
it.GetSize());
}
// Only succeed if we allocated as many pages as we wanted
if (num_pages) {
return ResultOutOfMemory;
@@ -146,11 +156,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
// We succeeded!
group_guard.Cancel();
return ResultSuccess;
}
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
Direction dir, u32 heap_fill_value) {
// Early return if we're freeing no pages
if (!num_pages) {
return ResultSuccess;

View File

@@ -12,6 +12,10 @@
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KPageLinkedList;
@@ -42,7 +46,7 @@ public:
Mask = (0xF << Shift),
};
KMemoryManager() = default;
explicit KMemoryManager(Core::System& system_);
constexpr std::size_t GetSize(Pool pool) const {
return managers[static_cast<std::size_t>(pool)].GetSize();
@@ -51,10 +55,10 @@ public:
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir = Direction::FromFront);
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir = Direction::FromFront);
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
u32 heap_fill_value = 0);
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
u32 heap_fill_value = 0);
static constexpr std::size_t MaxManagerCount = 10;
@@ -129,6 +133,7 @@ private:
};
private:
Core::System& system;
std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
std::array<Impl, MaxManagerCount> managers;
};

View File

@@ -289,8 +289,8 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
}
KPageLinkedList page_linked_list;
CASCADE_CODE(
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
allocation_option));
CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
block_manager->Update(addr, num_pages, state, perm);
@@ -298,16 +298,16 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
return ResultSuccess;
}
ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
const std::size_t num_pages{size / PageSize};
KMemoryState state{};
KMemoryPermission perm{};
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
KMemoryState::Normal, KMemoryPermission::All,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
@@ -335,7 +335,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
return ResultSuccess;
}
ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
if (!size) {
@@ -344,14 +344,14 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
const std::size_t num_pages{size / PageSize};
CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, KMemoryState::All,
KMemoryState::Normal, KMemoryPermission::None,
CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
KMemoryState state{};
CASCADE_CODE(CheckMemoryState(
&state, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
&state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
@@ -361,7 +361,7 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite);
KMemoryPermission::UserReadWrite);
system.InvalidateCpuInstructionCacheRange(dst_addr, size);
@@ -416,7 +416,7 @@ void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr star
}
const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
Operate(dst_addr, num_pages, KMemoryPermission::ReadAndWrite, OperationType::Map,
Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
map_addr);
dst_addr += num_pages * PageSize;
@@ -457,8 +457,8 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
KPageLinkedList page_linked_list;
CASCADE_CODE(
system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool));
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
memory_pool, allocation_option));
// We succeeded, so commit the memory reservation.
memory_reservation.Commit();
@@ -470,7 +470,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
const std::size_t num_pages{size / PageSize};
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
return ResultSuccess;
}
@@ -541,7 +541,8 @@ ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
}
const std::size_t num_pages{size / PageSize};
system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool);
system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool,
allocation_option);
block_manager->Update(addr, num_pages, KMemoryState::Free);
@@ -553,8 +554,8 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
&src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
@@ -568,13 +569,13 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
{
auto block_guard = detail::ScopeExit([&] {
Operate(src_addr, num_pages, KMemoryPermission::ReadAndWrite,
Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite,
OperationType::ChangePermissions);
});
CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
OperationType::ChangePermissions));
CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::ReadAndWrite));
CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::UserReadWrite));
block_guard.Cancel();
}
@@ -582,7 +583,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None,
KMemoryAttribute::Locked);
block_manager->Update(dst_addr, num_pages, KMemoryState::Stack,
KMemoryPermission::ReadAndWrite);
KMemoryPermission::UserReadWrite);
return ResultSuccess;
}
@@ -592,13 +593,13 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
&src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
KMemoryPermission dst_perm{};
CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, KMemoryState::All,
KMemoryState::Stack, KMemoryPermission::None,
CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size,
KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
@@ -617,13 +618,13 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); });
CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::ReadAndWrite,
CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite,
OperationType::ChangePermissions));
block_guard.Cancel();
}
block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::ReadAndWrite);
block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::UserReadWrite);
block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
return ResultSuccess;
@@ -713,50 +714,61 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
}
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
KMemoryPermission perm) {
Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
// Lock the table.
std::lock_guard lock{page_table_lock};
KMemoryState prev_state{};
KMemoryPermission prev_perm{};
// Verify we can change the memory permission.
KMemoryState old_state;
KMemoryPermission old_perm;
size_t num_allocator_blocks;
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
std::addressof(num_allocator_blocks), addr, size,
KMemoryState::FlagCode, KMemoryState::FlagCode,
KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::None));
CASCADE_CODE(CheckMemoryState(
&prev_state, &prev_perm, nullptr, addr, size, KMemoryState::FlagCode,
KMemoryState::FlagCode, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
// Determine new perm/state.
const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
KMemoryState new_state = old_state;
const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite;
const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
const bool was_x =
(old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
ASSERT(!(is_w && is_x));
KMemoryState state{prev_state};
// Ensure state is mutable if permission allows write
if ((perm & KMemoryPermission::Write) != KMemoryPermission::None) {
if (prev_state == KMemoryState::Code) {
state = KMemoryState::CodeData;
} else if (prev_state == KMemoryState::AliasCode) {
state = KMemoryState::AliasCodeData;
} else {
if (is_w) {
switch (old_state) {
case KMemoryState::Code:
new_state = KMemoryState::CodeData;
break;
case KMemoryState::AliasCode:
new_state = KMemoryState::AliasCodeData;
break;
default:
UNREACHABLE();
}
}
// Return early if there is nothing to change
if (state == prev_state && perm == prev_perm) {
return ResultSuccess;
}
// Succeed if there's nothing to do.
R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
if ((prev_perm & KMemoryPermission::Execute) != (perm & KMemoryPermission::Execute)) {
// Perform mapping operation.
const auto operation =
was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions;
R_TRY(Operate(addr, num_pages, new_perm, operation));
// Update the blocks.
block_manager->Update(addr, num_pages, new_state, new_perm, KMemoryAttribute::None);
// Ensure cache coherency, if we're setting pages as executable.
if (is_x) {
// Memory execution state is changing, invalidate CPU cache range
system.InvalidateCpuInstructionCacheRange(addr, size);
}
const std::size_t num_pages{size / PageSize};
const OperationType operation{(perm & KMemoryPermission::Execute) != KMemoryPermission::None
? OperationType::ChangePermissionsAndRefresh
: OperationType::ChangePermissions};
CASCADE_CODE(Operate(addr, num_pages, perm, operation));
block_manager->Update(addr, num_pages, state, perm);
return ResultSuccess;
}
@@ -782,10 +794,10 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
KMemoryAttribute attribute{};
CASCADE_CODE(CheckMemoryState(
&state, nullptr, &attribute, addr, size,
&state, nullptr, &attribute, nullptr, addr, size,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
KMemoryAttribute::IpcAndDeviceMapped));
block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
@@ -799,13 +811,13 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
KMemoryState state{};
CASCADE_CODE(
CheckMemoryState(&state, nullptr, nullptr, addr, size,
CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite);
return ResultSuccess;
}
@@ -820,7 +832,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
KMemoryState old_state;
KMemoryPermission old_perm;
R_TRY(this->CheckMemoryState(
std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size,
KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
@@ -837,24 +849,36 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
return ResultSuccess;
}
ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
KMemoryAttribute value) {
ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
const size_t num_pages = size / PageSize;
ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
KMemoryAttribute::SetMask);
// Lock the table.
std::lock_guard lock{page_table_lock};
KMemoryState state{};
KMemoryPermission perm{};
KMemoryAttribute attribute{};
CASCADE_CODE(CheckMemoryState(
&state, &perm, &attribute, addr, size, KMemoryState::FlagCanChangeAttribute,
// Verify we can change the memory attribute.
KMemoryState old_state;
KMemoryPermission old_perm;
KMemoryAttribute old_attr;
size_t num_allocator_blocks;
constexpr auto AttributeTestMask =
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
R_TRY(this->CheckMemoryState(
std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
KMemoryAttribute::DeviceSharedAndUncached));
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
attribute = attribute & ~mask;
attribute = attribute | (mask & value);
// Determine the new attribute.
const auto new_attr = ((old_attr & static_cast<KMemoryAttribute>(~mask)) |
static_cast<KMemoryAttribute>(attr & mask));
block_manager->Update(addr, size / PageSize, state, perm, attribute);
// Perform operation.
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
// Update the blocks.
block_manager->Update(addr, num_pages, old_state, old_perm, new_attr);
return ResultSuccess;
}
@@ -894,7 +918,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
heap_region_start + size, GetHeapSize() - size,
KMemoryState::All, KMemoryState::Normal,
KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
KMemoryPermission::All, KMemoryPermission::UserReadWrite,
KMemoryAttribute::All, KMemoryAttribute::None));
// Unmap the end of the heap.
@@ -937,7 +961,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Allocate pages for the heap extension.
KPageLinkedList page_linked_list;
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
memory_pool));
memory_pool, allocation_option));
// Map the pages.
{
@@ -969,7 +993,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Apply the memory block update.
block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
// Update the current heap end.
current_heap_end = heap_region_start + size;
@@ -1004,8 +1028,8 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
} else {
KPageLinkedList page_group;
CASCADE_CODE(
system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool));
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages,
memory_pool, allocation_option));
CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
}
@@ -1019,7 +1043,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
KMemoryAttribute::DeviceSharedAndUncached)};
@@ -1042,7 +1066,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
KMemoryAttribute::DeviceSharedAndUncached)};
@@ -1068,7 +1092,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
result.IsError()) {
@@ -1095,7 +1119,7 @@ ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::Locked)};
result.IsError()) {
@@ -1225,18 +1249,19 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
return alias_region_start;
case KMemoryState::Stack:
return stack_region_start;
case KMemoryState::Io:
case KMemoryState::Static:
case KMemoryState::ThreadLocal:
return kernel_map_region_start;
case KMemoryState::Io:
case KMemoryState::Shared:
case KMemoryState::AliasCode:
case KMemoryState::AliasCodeData:
case KMemoryState::Transferred:
case KMemoryState::SharedTransferred:
case KMemoryState::Transfered:
case KMemoryState::SharedTransfered:
case KMemoryState::SharedCode:
case KMemoryState::GeneratedCode:
case KMemoryState::CodeOut:
case KMemoryState::Coverage:
return alias_code_region_start;
case KMemoryState::Code:
case KMemoryState::CodeData:
@@ -1260,18 +1285,19 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
return alias_region_end - alias_region_start;
case KMemoryState::Stack:
return stack_region_end - stack_region_start;
case KMemoryState::Io:
case KMemoryState::Static:
case KMemoryState::ThreadLocal:
return kernel_map_region_end - kernel_map_region_start;
case KMemoryState::Io:
case KMemoryState::Shared:
case KMemoryState::AliasCode:
case KMemoryState::AliasCodeData:
case KMemoryState::Transferred:
case KMemoryState::SharedTransferred:
case KMemoryState::Transfered:
case KMemoryState::SharedTransfered:
case KMemoryState::SharedCode:
case KMemoryState::GeneratedCode:
case KMemoryState::CodeOut:
case KMemoryState::Coverage:
return alias_code_region_end - alias_code_region_start;
case KMemoryState::Code:
case KMemoryState::CodeData:
@@ -1283,15 +1309,18 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
}
bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
const VAddr end{addr + size};
const VAddr last{end - 1};
const VAddr region_start{GetRegionAddress(state)};
const std::size_t region_size{GetRegionSize(state)};
const bool is_in_region{region_start <= addr && addr < end &&
last <= region_start + region_size - 1};
const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)};
const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)};
const VAddr end = addr + size;
const VAddr last = end - 1;
const VAddr region_start = this->GetRegionAddress(state);
const size_t region_size = this->GetRegionSize(state);
const bool is_in_region =
region_start <= addr && addr < end && last <= region_start + region_size - 1;
const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr ||
heap_region_start == heap_region_end);
const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr ||
alias_region_start == alias_region_end);
switch (state) {
case KMemoryState::Free:
case KMemoryState::Kernel:
@@ -1305,11 +1334,12 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
case KMemoryState::AliasCodeData:
case KMemoryState::Stack:
case KMemoryState::ThreadLocal:
case KMemoryState::Transferred:
case KMemoryState::SharedTransferred:
case KMemoryState::Transfered:
case KMemoryState::SharedTransfered:
case KMemoryState::SharedCode:
case KMemoryState::GeneratedCode:
case KMemoryState::CodeOut:
case KMemoryState::Coverage:
return is_in_region && !is_in_heap && !is_in_alias;
case KMemoryState::Normal:
ASSERT(is_in_heap);
@@ -1324,91 +1354,29 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
}
}
constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
// Validate the states match expectation.
R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory);
R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory);
R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm,
KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
// Validate the states match expectation
if ((info.state & state_mask) != state) {
return ResultInvalidCurrentMemory;
}
if ((info.perm & perm_mask) != perm) {
return ResultInvalidCurrentMemory;
}
if ((info.attribute & attr_mask) != attr) {
return ResultInvalidCurrentMemory;
}
ASSERT(this->IsLockedByCurrentThread());
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr) {
std::lock_guard lock{page_table_lock};
// Get information about the first block
const VAddr last_addr{addr + size - 1};
KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
KMemoryInfo info{it->GetMemoryInfo()};
// Validate all blocks in the range have correct state
const KMemoryState first_state{info.state};
const KMemoryPermission first_perm{info.perm};
const KMemoryAttribute first_attr{info.attribute};
while (true) {
// Validate the current block
if (!(info.state == first_state)) {
return ResultInvalidCurrentMemory;
}
if (!(info.perm == first_perm)) {
return ResultInvalidCurrentMemory;
}
if (!((info.attribute | static_cast<KMemoryAttribute>(ignore_attr)) ==
(first_attr | static_cast<KMemoryAttribute>(ignore_attr)))) {
return ResultInvalidCurrentMemory;
}
// Validate against the provided masks
CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done
if (last_addr <= info.GetLastAddress()) {
break;
}
// Advance our iterator
it++;
ASSERT(it != block_manager->cend());
info = it->GetMemoryInfo();
}
// Write output state
if (out_state) {
*out_state = first_state;
}
if (out_perm) {
*out_perm = first_perm;
}
if (out_attr) {
*out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr);
}
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
// Get information about the first block.
const VAddr last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
KMemoryInfo info = it->GetMemoryInfo();
// If the start address isn't aligned, we need a block.
@@ -1417,7 +1385,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
while (true) {
// Validate against the provided masks.
R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done.
if (last_addr <= info.GetLastAddress()) {
@@ -1426,6 +1394,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
// Advance our iterator.
it++;
ASSERT(it != block_manager->cend());
info = it->GetMemoryInfo();
}
@@ -1440,4 +1409,66 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block.
const VAddr last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
KMemoryInfo info = it->GetMemoryInfo();
// If the start address isn't aligned, we need a block.
const size_t blocks_for_start_align =
(Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
// Validate all blocks in the range have correct state.
const KMemoryState first_state = info.state;
const KMemoryPermission first_perm = info.perm;
const KMemoryAttribute first_attr = info.attribute;
while (true) {
// Validate the current block.
R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory);
R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory);
R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr),
ResultInvalidCurrentMemory);
// Validate against the provided masks.
R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done.
if (last_addr <= info.GetLastAddress()) {
break;
}
// Advance our iterator.
it++;
ASSERT(it != block_manager->cend());
info = it->GetMemoryInfo();
}
// If the end address isn't aligned, we need a block.
const size_t blocks_for_end_align =
(Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
// Write output state.
if (out_state != nullptr) {
*out_state = first_state;
}
if (out_perm != nullptr) {
*out_perm = first_perm;
}
if (out_attr != nullptr) {
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
}
if (out_blocks_needed != nullptr) {
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
}
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -31,8 +31,8 @@ public:
KMemoryManager::Pool pool);
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -43,13 +43,13 @@ public:
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
KMemoryAttribute value);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
ResultCode SetMaxHeapSize(std::size_t size);
ResultCode SetHeapSize(VAddr* out, std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
@@ -102,28 +102,50 @@ private:
constexpr VAddr GetRegionAddress(KMemoryState state) const;
constexpr std::size_t GetRegionSize(KMemoryState state) const;
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const;
ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr);
}
ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const;
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
}
ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr);
}
bool IsLockedByCurrentThread() const {
return true;
}
std::recursive_mutex page_table_lock;
std::unique_ptr<KMemoryBlockManager> block_manager;
@@ -281,6 +303,7 @@ private:
bool is_aslr_enabled{};
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
Common::PageTable page_table_impl;

View File

@@ -45,6 +45,7 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
{ t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
};
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
@@ -349,24 +350,49 @@ public:
// Mutators.
constexpr void PushBack(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->PushBack(member->GetPriority(), member);
}
constexpr void Remove(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->Remove(member->GetPriority(), member);
}
constexpr void MoveToScheduledFront(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
}
constexpr KThread* MoveToScheduledBack(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return {};
}
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member);
}
// First class fancy operations.
constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
ASSERT(IsValidPriority(prev_priority));
// Remove the member from the queues.
@@ -383,6 +409,11 @@ public:
constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,
Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
// Get the new information.
const s32 priority = member->GetPriority();
const AffinityMaskType& new_affinity = member->GetAffinityMask();
@@ -412,6 +443,11 @@ public:
}
constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
// Get the new information.
const s32 new_core = member->GetActiveCore();
const s32 priority = member->GetPriority();

View File

@@ -149,6 +149,10 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
return ResultSuccess;
}
void KProcess::DoWorkerTaskImpl() {
UNIMPLEMENTED();
}
KResourceLimit* KProcess::GetResourceLimit() const {
return resource_limit;
}
@@ -477,7 +481,7 @@ void KProcess::Finalize() {
}
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
}
/**
@@ -509,7 +513,7 @@ VAddr KProcess::CreateTLSRegion() {
const VAddr tls_page_addr{page_table
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
KMemoryState::ThreadLocal,
KMemoryPermission::ReadAndWrite,
KMemoryPermission::UserReadWrite,
tls_map_addr)
.ValueOr(0)};
@@ -541,16 +545,16 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
KMemoryPermission permission) {
Svc::MemoryPermission permission) {
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
};
kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
code_set.memory.size());
ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute);
ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite);
ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute);
ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
}
bool KProcess::IsSignaled() const {
@@ -587,7 +591,7 @@ ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
CASCADE_RESULT(main_thread_stack_top,
page_table->AllocateAndMapMemory(
main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
KMemoryState::Stack, KMemoryPermission::ReadAndWrite));
KMemoryState::Stack, KMemoryPermission::UserReadWrite));
main_thread_stack_top += main_thread_stack_size;

View File

@@ -15,6 +15,7 @@
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
@@ -62,8 +63,7 @@ enum class ProcessStatus {
DebugBreak,
};
class KProcess final
: public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> {
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
public:
@@ -345,6 +345,8 @@ public:
bool IsSignaled() const override;
void DoWorkerTaskImpl();
void PinCurrentThread(s32 core_id);
void UnpinCurrentThread(s32 core_id);
void UnpinThread(KThread* thread);

View File

@@ -49,8 +49,6 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
if (!must_context_switch || core != current_core) {
auto& phys_core = kernel.PhysicalCore(core);
phys_core.Interrupt();
} else {
must_context_switch = true;
}
cores_pending_reschedule &= ~(1ULL << core);
}
@@ -408,6 +406,9 @@ void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduli
} else {
RescheduleCores(kernel, cores_needing_scheduling);
}
// Special case to ensure dummy threads that are waiting block.
current_thread->IfDummyThreadTryWait();
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -741,6 +742,12 @@ void KScheduler::ScheduleImpl() {
next_thread = idle_thread;
}
// We never want to schedule a dummy thread, as these are only used by host threads for locking.
if (next_thread->GetThreadType() == ThreadType::Dummy) {
ASSERT_MSG(false, "Dummy threads should never be scheduled!");
next_thread = idle_thread;
}
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
previous_thread->EnableDispatch();

View File

@@ -8,7 +8,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -123,20 +122,10 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
// In the event that something fails here, stub a result to prevent the game from crashing.
// This is a work-around in the event that somehow we process a service request after the
// session has been closed by the game. This has been observed to happen rarely in Pokemon
// Sword/Shield and is likely a result of us using host threads/scheduling for services.
// TODO(bunnei): Find a better solution here.
auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); });
// Ensure we have a session request handler
if (manager->HasSessionRequestHandler(*context)) {
if (auto strong_ptr = manager->GetServiceThread().lock()) {
strong_ptr->QueueSyncRequest(*parent, std::move(context));
// We succeeded.
error_guard.Cancel();
} else {
ASSERT_MSG(false, "strong_ptr is nullptr!");
}
@@ -171,13 +160,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
convert_to_domain = false;
}
// Some service requests require the thread to block
{
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
context.GetThread().EndWait(result);
}
}
// The calling thread is waiting for this request to complete, so wake it up.
context.GetThread().EndWait(result);
return result;
}

View File

@@ -30,6 +30,7 @@
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
@@ -105,7 +106,7 @@ KThread::~KThread() = default;
ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
s32 virt_core, KProcess* owner, ThreadType type) {
// Assert parameters are valid.
ASSERT((type == ThreadType::Main) ||
ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) ||
(Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority));
ASSERT((owner != nullptr) || (type != ThreadType::User));
ASSERT(0 <= virt_core && virt_core < static_cast<s32>(Common::BitSize<u64>()));
@@ -139,7 +140,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
break;
}
thread_type_for_debugging = type;
thread_type = type;
// Set the ideal core ID and affinity mask.
virtual_ideal_core_id = virt_core;
@@ -261,7 +262,7 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
}
ResultCode KThread::InitializeDummyThread(KThread* thread) {
return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
}
ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -332,7 +333,7 @@ void KThread::Finalize() {
}
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>::Finalize();
KSynchronizationObject::Finalize();
}
bool KThread::IsSignaled() const {
@@ -376,11 +377,28 @@ void KThread::StartTermination() {
// Register terminated dpc flag.
RegisterDpc(DpcFlag::Terminated);
}
void KThread::FinishTermination() {
// Ensure that the thread is not executing on any core.
if (parent != nullptr) {
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
KThread* core_thread{};
do {
core_thread = kernel.Scheduler(i).GetCurrentThread();
} while (core_thread == this);
}
}
// Close the thread.
this->Close();
}
void KThread::DoWorkerTaskImpl() {
// Finish the termination that was begun by Exit().
this->FinishTermination();
}
void KThread::Pin(s32 current_core) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -417,12 +435,7 @@ void KThread::Pin(s32 current_core) {
static_cast<u32>(ThreadState::SuspendShift)));
// Update our state.
const ThreadState old_state = thread_state;
thread_state = static_cast<ThreadState>(GetSuspendFlags() |
static_cast<u32>(old_state & ThreadState::Mask));
if (thread_state != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
UpdateState();
}
// TODO(bunnei): Update our SVC access permissions.
@@ -463,20 +476,13 @@ void KThread::Unpin() {
}
// Allow performing thread suspension (if termination hasn't been requested).
{
if (!IsTerminationRequested()) {
// Update our allow flags.
if (!IsTerminationRequested()) {
suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) +
static_cast<u32>(ThreadState::SuspendShift)));
}
suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) +
static_cast<u32>(ThreadState::SuspendShift)));
// Update our state.
const ThreadState old_state = thread_state;
thread_state = static_cast<ThreadState>(GetSuspendFlags() |
static_cast<u32>(old_state & ThreadState::Mask));
if (thread_state != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
UpdateState();
}
// TODO(bunnei): Update our SVC access permissions.
@@ -689,12 +695,7 @@ void KThread::Resume(SuspendType type) {
~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)));
// Update our state.
const ThreadState old_state = thread_state;
thread_state = static_cast<ThreadState>(GetSuspendFlags() |
static_cast<u32>(old_state & ThreadState::Mask));
if (thread_state != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
this->UpdateState();
}
void KThread::WaitCancel() {
@@ -721,19 +722,22 @@ void KThread::TrySuspend() {
ASSERT(GetNumKernelWaiters() == 0);
// Perform the suspend.
Suspend();
this->UpdateState();
}
void KThread::Suspend() {
void KThread::UpdateState() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
ASSERT(IsSuspendRequested());
// Set our suspend flags in state.
const auto old_state = thread_state;
thread_state = static_cast<ThreadState>(GetSuspendFlags()) | (old_state & ThreadState::Mask);
const auto new_state =
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
thread_state = new_state;
// Note the state change in scheduler.
KScheduler::OnThreadStateChanged(kernel, this, old_state);
if (new_state != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
}
void KThread::Continue() {
@@ -998,13 +1002,16 @@ ResultCode KThread::Run() {
// If the current thread has been asked to suspend, suspend it and retry.
if (GetCurrentThread(kernel).IsSuspended()) {
GetCurrentThread(kernel).Suspend();
GetCurrentThread(kernel).UpdateState();
continue;
}
// If we're not a kernel thread and we've been asked to suspend, suspend ourselves.
if (IsUserThread() && IsSuspended()) {
Suspend();
if (KProcess* owner = this->GetOwnerProcess(); owner != nullptr) {
if (IsUserThread() && IsSuspended()) {
this->UpdateState();
}
owner->IncrementThreadCount();
}
// Set our state and finish.
@@ -1029,11 +1036,18 @@ void KThread::Exit() {
{
KScopedSchedulerLock sl{kernel};
// Disallow all suspension.
suspend_allowed_flags = 0;
this->UpdateState();
// Disallow all suspension.
suspend_allowed_flags = 0;
// Start termination.
StartTermination();
// Register the thread as a work task.
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
}
}
@@ -1061,12 +1075,46 @@ ResultCode KThread::Sleep(s64 timeout) {
return ResultSuccess;
}
void KThread::IfDummyThreadTryWait() {
if (!IsDummyThread()) {
return;
}
if (GetState() != ThreadState::Waiting) {
return;
}
// Block until we can grab the lock.
KScopedSpinLock lk{dummy_wait_lock};
}
void KThread::IfDummyThreadBeginWait() {
if (!IsDummyThread()) {
return;
}
// Ensure the thread will block when IfDummyThreadTryWait is called.
dummy_wait_lock.Lock();
}
void KThread::IfDummyThreadEndWait() {
if (!IsDummyThread()) {
return;
}
// Ensure the thread will no longer block.
dummy_wait_lock.Unlock();
}
void KThread::BeginWait(KThreadQueue* queue) {
// Set our state as waiting.
SetState(ThreadState::Waiting);
// Set our wait queue.
wait_queue = queue;
// Special case for dummy threads to ensure they block.
IfDummyThreadBeginWait();
}
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
@@ -1085,7 +1133,16 @@ void KThread::EndWait(ResultCode wait_result_) {
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
if (wait_queue == nullptr) {
// This should never happen, but avoid a hard crash below to get this logged.
ASSERT_MSG(false, "wait_queue is nullptr!");
return;
}
wait_queue->EndWait(this, wait_result_);
// Special case for dummy threads to wakeup if necessary.
IfDummyThreadEndWait();
}
}

View File

@@ -19,6 +19,7 @@
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_types.h"
@@ -100,7 +101,7 @@ enum class ThreadWaitReasonForDebugging : u32 {
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>,
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
public boost::intrusive::list_base_hook<> {
KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
@@ -111,6 +112,7 @@ private:
public:
static constexpr s32 DefaultThreadPriority = 44;
static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
static constexpr s32 DummyThreadPriority = Svc::LowestThreadPriority + 2;
explicit KThread(KernelCore& kernel_);
~KThread() override;
@@ -192,9 +194,9 @@ public:
void TrySuspend();
void Continue();
void UpdateState();
void Suspend();
void Continue();
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
@@ -385,6 +387,8 @@ public:
void OnTimer();
void DoWorkerTaskImpl();
static void PostDestroy(uintptr_t arg);
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -550,8 +554,12 @@ public:
return wait_reason_for_debugging;
}
[[nodiscard]] ThreadType GetThreadTypeForDebugging() const {
return thread_type_for_debugging;
[[nodiscard]] ThreadType GetThreadType() const {
return thread_type;
}
[[nodiscard]] bool IsDummyThread() const {
return GetThreadType() == ThreadType::Dummy;
}
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
@@ -628,6 +636,14 @@ public:
return condvar_key;
}
// Dummy threads (used for HLE host threads) cannot wait based on the guest scheduler, and
// therefore will not block on guest kernel synchronization primitives. These methods handle
// blocking as needed.
void IfDummyThreadTryWait();
void IfDummyThreadBeginWait();
void IfDummyThreadEndWait();
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
@@ -679,6 +695,8 @@ private:
void StartTermination();
void FinishTermination();
[[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
@@ -744,16 +762,17 @@ private:
bool resource_limit_release_hint{};
StackParameters stack_parameters{};
KSpinLock context_guard{};
KSpinLock dummy_wait_lock{};
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
ThreadType thread_type{};
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
ThreadType thread_type_for_debugging{};
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;

View File

@@ -0,0 +1,18 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/k_synchronization_object.h"
namespace Kernel {
class KWorkerTask : public KSynchronizationObject {
public:
explicit KWorkerTask(KernelCore& kernel_);
void DoWorkerTask();
};
} // namespace Kernel

View File

@@ -0,0 +1,42 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
KWorkerTask::KWorkerTask(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
void KWorkerTask::DoWorkerTask() {
if (auto* const thread = this->DynamicCast<KThread*>(); thread != nullptr) {
return thread->DoWorkerTaskImpl();
} else {
auto* const process = this->DynamicCast<KProcess*>();
ASSERT(process != nullptr);
return process->DoWorkerTaskImpl();
}
}
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count);
kernel.WorkerTaskManager().AddTask(kernel, task);
}
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
KScopedSchedulerLock sl(kernel);
m_waiting_thread.QueueWork([task]() {
// Do the task.
task->DoWorkerTask();
});
}
} // namespace Kernel

View File

@@ -0,0 +1,33 @@
// Copyright 2022 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 "common/thread_worker.h"
namespace Kernel {
class KernelCore;
class KWorkerTask;
class KWorkerTaskManager final {
public:
enum class WorkerType : u32 {
Exit,
Count,
};
KWorkerTaskManager();
static void AddTask(KernelCore& kernel_, WorkerType type, KWorkerTask* task);
private:
void AddTask(KernelCore& kernel, KWorkerTask* task);
private:
Common::ThreadWorker m_waiting_thread;
};
} // namespace Kernel

View File

@@ -37,6 +37,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/service_thread.h"
@@ -51,7 +52,8 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system_, KernelCore& kernel_)
: time_manager{system_}, object_list_container{kernel_}, system{system_} {}
: time_manager{system_}, object_list_container{kernel_},
service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
void SetMulticore(bool is_multi) {
is_multicore = is_multi;
@@ -121,7 +123,7 @@ struct KernelCore::Impl {
object_list_container.Finalize();
// Ensures all service threads gracefully shutdown.
service_threads.clear();
ClearServiceThreads();
next_object_id = 0;
next_kernel_process_id = KProcess::InitialKIPIDMin;
@@ -299,12 +301,10 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
auto make_thread = [this]() {
std::lock_guard lk(dummy_thread_lock);
auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
KAutoObject::Create(thread.get());
ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
KThread* thread = KThread::Create(system.Kernel());
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread.get();
return thread;
};
thread_local KThread* saved_thread = make_thread();
@@ -629,7 +629,7 @@ struct KernelCore::Impl {
const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
// Initialize memory managers
memory_manager = std::make_unique<KMemoryManager>();
memory_manager = std::make_unique<KMemoryManager>(system);
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
application_pool.GetAddress(),
application_pool.GetEndAddress());
@@ -704,11 +704,31 @@ struct KernelCore::Impl {
return port;
}
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
service_threads_manager.QueueWork(
[this, service_thread]() { service_threads.emplace(service_thread); });
return service_thread;
}
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
if (auto strong_ptr = service_thread.lock()) {
service_threads_manager.QueueWork(
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
}
}
void ClearServiceThreads() {
service_threads_manager.QueueWork([this]() { service_threads.clear(); });
}
std::mutex server_ports_lock;
std::mutex server_sessions_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
std::mutex dummy_thread_lock;
std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
@@ -759,14 +779,12 @@ struct KernelCore::Impl {
// Threads used for services
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
// Specifically tracked to be automatically destroyed with kernel
std::vector<std::unique_ptr<KThread>> dummy_threads;
bool is_multicore{};
std::atomic_bool is_shutting_down{};
bool is_phantom_mode_for_singlecore{};
@@ -774,6 +792,8 @@ struct KernelCore::Impl {
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
KWorkerTaskManager worker_task_manager;
// System context
Core::System& system;
};
@@ -1099,15 +1119,11 @@ void KernelCore::ExitSVCProfile() {
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
impl->service_threads.emplace(service_thread);
return service_thread;
return impl->CreateServiceThread(*this, name);
}
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
if (auto strong_ptr = service_thread.lock()) {
impl->service_threads.erase(strong_ptr);
}
impl->ReleaseServiceThread(service_thread);
}
Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() {
@@ -1118,6 +1134,14 @@ const Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() const {
return impl->slab_resource_counts;
}
KWorkerTaskManager& KernelCore::WorkerTaskManager() {
return impl->worker_task_manager;
}
const KWorkerTaskManager& KernelCore::WorkerTaskManager() const {
return impl->worker_task_manager;
}
bool KernelCore::IsPhantomModeForSingleCore() const {
return impl->IsPhantomModeForSingleCore();
}

View File

@@ -52,6 +52,7 @@ class KSharedMemory;
class KSharedMemoryInfo;
class KThread;
class KTransferMemory;
class KWorkerTaskManager;
class KWritableEvent;
class KCodeMemory;
class PhysicalCore;
@@ -343,6 +344,12 @@ public:
/// Gets the current slab resource counts.
const Init::KSlabResourceCounts& SlabResourceCounts() const;
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
KWorkerTaskManager& WorkerTaskManager();
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
const KWorkerTaskManager& WorkerTaskManager() const;
private:
friend class KProcess;
friend class KThread;

View File

@@ -16,17 +16,25 @@ namespace Kernel {
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
Core::CPUInterrupts& interrupts_)
: core_index{core_index_}, system{system_}, scheduler{scheduler_},
interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {}
interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {
#ifdef ARCHITECTURE_x86_64
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
#else
#error Platform not supported yet.
#endif
}
PhysicalCore::~PhysicalCore() = default;
void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
#ifdef ARCHITECTURE_x86_64
auto& kernel = system.Kernel();
if (is_64_bit) {
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
} else {
if (!is_64_bit) {
// We already initialized a 64-bit core, replace with a 32-bit one.
arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
}

View File

@@ -12,6 +12,7 @@
#include "common/scope_exit.h"
#include "common/thread.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/service_thread.h"
@@ -50,6 +51,10 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
kernel.RegisterHostThread();
// Ensure the dummy thread allocated for this host thread is closed on exit.
auto* dummy_thread = kernel.GetCurrentEmuThread();
SCOPE_EXIT({ dummy_thread->Close(); });
while (true) {
std::function<void()> task;

View File

@@ -168,6 +168,9 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
MemoryPermission perm) {
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
perm);
// Validate address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
@@ -186,46 +189,33 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s
}
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
u32 attribute) {
u32 attr) {
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attribute);
size, mask, attr);
if (!Common::Is4KBAligned(address)) {
LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
return ResultInvalidAddress;
}
// Validate address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
size);
return ResultInvalidAddress;
}
if (!IsValidAddressRange(address, size)) {
LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
address, size);
return ResultInvalidCurrentMemory;
}
const auto attributes{static_cast<MemoryAttribute>(mask | attribute)};
if (attributes != static_cast<MemoryAttribute>(mask) ||
(attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
return ResultInvalidCombination;
}
// Validate the attribute and mask.
constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
// Validate that the region is in range for the current process.
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
static_cast<KMemoryAttribute>(attribute));
// Set the memory attribute.
return page_table.SetMemoryAttribute(address, size, mask, attr);
}
static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
u32 attribute) {
return SetMemoryAttribute(system, address, size, mask, attribute);
u32 attr) {
return SetMemoryAttribute(system, address, size, mask, attr);
}
/// Maps a memory range into a different range.
@@ -1319,6 +1309,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
// Validate the memory permission.
R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
@@ -1333,7 +1325,7 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Set the memory permission.
return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
return page_table.SetProcessMemoryPermission(address, size, perm);
}
static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
@@ -1636,7 +1628,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return ResultInvalidMemoryRegion;
}
return page_table.MapProcessCodeMemory(dst_address, src_address, size);
return page_table.MapCodeMemory(dst_address, src_address, size);
}
static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
@@ -1704,7 +1696,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidMemoryRegion;
}
return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
return page_table.UnmapCodeMemory(dst_address, src_address, size);
}
/// Exits the current process

View File

@@ -32,6 +32,7 @@ enum class MemoryState : u32 {
Kernel = 0x13,
GeneratedCode = 0x14,
CodeOut = 0x15,
Coverage = 0x16,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);

View File

@@ -37,8 +37,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
{130, nullptr, "ActivateOpenContextRetention"},
{140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"},
{150, nullptr, "AuthenticateApplicationAsync"},
{151, nullptr, "Unknown151"},
{152, nullptr, "Unknown152"},
{151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"},
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
{190, nullptr, "GetUserLastOpenedApplication"},
{191, nullptr, "ActivateOpenContextHolder"},
{200, nullptr, "BeginUserRegistration"},

View File

@@ -37,8 +37,8 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
{130, nullptr, "ActivateOpenContextRetention"},
{140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"},
{150, nullptr, "AuthenticateApplicationAsync"},
{151, nullptr, "Unknown151"},
{152, nullptr, "Unknown152"},
{151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"},
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
{190, nullptr, "GetUserLastOpenedApplication"},
{191, nullptr, "ActivateOpenContextHolder"},
{997, nullptr, "DebugInvalidateTokenCacheForUser"},

View File

@@ -37,6 +37,7 @@ OMM::OMM(Core::System& system_) : ServiceFramework{system_, "omm"} {
{25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
{26, nullptr, "GetOperationModeSystemInfo"},
{27, nullptr, "GetAppletFullAwakingSystemEvent"},
{28, nullptr, "CreateCradleFirmwareUpdater"},
};
// clang-format on

View File

@@ -17,7 +17,7 @@ public:
static const FunctionInfo functions[] = {
{0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"},
{1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"},
{2, nullptr, "SetCpuOverclockEnabled"},
{2, &ISession::SetCpuOverclockEnabled, "SetCpuOverclockEnabled"},
};
RegisterHandlers(functions);
}
@@ -47,6 +47,18 @@ private:
rb.PushEnum(controller.GetCurrentPerformanceConfiguration(mode));
}
void SetCpuOverclockEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto cpu_overclock_enabled = rp.Pop<bool>();
LOG_WARNING(Service_APM, "(STUBBED) called, cpu_overclock_enabled={}",
cpu_overclock_enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
Controller& controller;
};

View File

@@ -41,14 +41,14 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
{27, nullptr, "SetVolumeMappingTableForDev"},
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{30, nullptr, "Unknown30"},
{31, nullptr, "Unknown31"},
{32, nullptr, "Unknown32"},
{33, nullptr, "Unknown33"},
{34, nullptr, "Unknown34"},
{10000, nullptr, "Unknown10000"},
{10001, nullptr, "Unknown10001"},
{10002, nullptr, "Unknown10002"},
{30, nullptr, "SetSpeakerAutoMuteEnabled"},
{31, nullptr, "IsSpeakerAutoMuteEnabled"},
{32, nullptr, "GetActiveOutputTarget"},
{33, nullptr, "GetTargetDeviceInfo"},
{34, nullptr, "AcquireTargetNotification"},
{10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
{10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
{10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
};
// clang-format on

View File

@@ -201,6 +201,22 @@ public:
{62, nullptr, "Unknown62"},
{63, nullptr, "Unknown63"},
{64, nullptr, "Unknown64"},
{65, nullptr, "Unknown65"},
{66, nullptr, "Unknown66"},
{67, nullptr, "Unknown67"},
{68, nullptr, "Unknown68"},
{69, nullptr, "Unknown69"},
{70, nullptr, "Unknown70"},
{71, nullptr, "Unknown71"},
{72, nullptr, "Unknown72"},
{73, nullptr, "Unknown73"},
{74, nullptr, "Unknown74"},
{75, nullptr, "Unknown75"},
{76, nullptr, "Unknown76"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
{110, nullptr, "Unknown102"},
{111, nullptr, "Unknown103"},
};
// clang-format on
@@ -249,6 +265,20 @@ public:
{7, nullptr, "AcquireRadioEvent"},
{8, nullptr, "AcquireGamepadPairingEvent"},
{9, nullptr, "IsGamepadPairingStarted"},
{10, nullptr, "StartAudioDeviceDiscovery"},
{11, nullptr, "StopAudioDeviceDiscovery"},
{12, nullptr, "IsDiscoveryingAudioDevice"},
{13, nullptr, "GetDiscoveredAudioDevice"},
{14, nullptr, "AcquireAudioDeviceConnectionEvent"},
{15, nullptr, "ConnectAudioDevice"},
{16, nullptr, "IsConnectingAudioDevice"},
{17, nullptr, "GetConnectedAudioDevices"},
{18, nullptr, "DisconnectAudioDevice"},
{19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
{20, nullptr, "GetPairedAudioDevices"},
{21, nullptr, "RemoveAudioDevicePairing"},
{22, nullptr, "RequestAudioDeviceConnectionRejection"},
{23, nullptr, "CancelAudioDeviceConnectionRejection"}
};
// clang-format on

View File

@@ -744,6 +744,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
{204, nullptr, "OpenDataFileSystemByProgramIndex"},
{205, &FSP_SRV::OpenDataStorageWithProgramIndex, "OpenDataStorageWithProgramIndex"},
{206, nullptr, "OpenDataStorageByPath"},
{400, nullptr, "OpenDeviceOperator"},
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
{501, nullptr, "OpenGameCardDetectionEventNotifier"},
@@ -796,6 +797,8 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{1014, nullptr, "OutputMultiProgramTagAccessLog"},
{1016, nullptr, "FlushAccessLogOnSdCard"},
{1017, nullptr, "OutputApplicationInfoAccessLog"},
{1018, nullptr, "SetDebugOption"},
{1019, nullptr, "UnsetDebugOption"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
{1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},

View File

@@ -27,13 +27,13 @@ public:
{10101, &IFriendService::GetFriendList, "GetFriendList"},
{10102, nullptr, "UpdateFriendInfo"},
{10110, nullptr, "GetFriendProfileImage"},
{10120, nullptr, "Unknown10120"},
{10121, nullptr, "Unknown10121"},
{10120, nullptr, "IsFriendListCacheAvailable"},
{10121, nullptr, "EnsureFriendListAvailable"},
{10200, nullptr, "SendFriendRequestForApplication"},
{10211, nullptr, "AddFacedFriendRequestForApplication"},
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
{10420, nullptr, "Unknown10420"},
{10421, nullptr, "Unknown10421"},
{10420, nullptr, "IsBlockedUserListCacheAvailable"},
{10421, nullptr, "EnsureBlockedUserListAvailable"},
{10500, nullptr, "GetProfileList"},
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
{10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
@@ -103,8 +103,8 @@ public:
{30900, nullptr, "SendFriendInvitation"},
{30910, nullptr, "ReadFriendInvitation"},
{30911, nullptr, "ReadAllFriendInvitations"},
{40100, nullptr, "Unknown40100"},
{40400, nullptr, "Unknown40400"},
{40100, nullptr, "DeleteFriendListCache"},
{40400, nullptr, "DeleteBlockedUserListCache"},
{49900, nullptr, "DeleteNetworkServiceAccountCache"},
};
// clang-format on

View File

@@ -37,7 +37,8 @@ namespace Service::HID {
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed
constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system_,
@@ -1175,7 +1176,8 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
GetAppletResource()
->GetController<Controller_NPad>(HidController::NPad)
.SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp);
LOG_WARNING(Service_HID,

View File

@@ -14,6 +14,7 @@
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/service.h"
#include "core/loader/nro.h"
@@ -325,7 +326,7 @@ public:
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
const VAddr addr{GetRandomMapRegion(page_table, size)};
const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
@@ -351,12 +352,12 @@ public:
if (bss_size) {
auto block_guard = detail::ScopeExit([&] {
page_table.UnmapProcessCodeMemory(addr + nro_size, bss_addr, bss_size);
page_table.UnmapProcessCodeMemory(addr, nro_addr, nro_size);
page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
});
const ResultCode result{
page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)};
page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
@@ -397,12 +398,12 @@ public:
nro_header.segment_headers[DATA_INDEX].memory_size);
CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute));
CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read));
ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read));
return process->PageTable().SetProcessMemoryPermission(
data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite);
}
void LoadModule(Kernel::HLERequestContext& ctx) {
@@ -530,16 +531,19 @@ public:
ResultCode UnmapNro(const NROInfo& info) {
// Each region must be unmapped separately to validate memory state
auto& page_table{system.CurrentProcess()->PageTable()};
CASCADE_CODE(page_table.UnmapProcessCodeMemory(info.nro_address + info.text_size +
info.ro_size + info.data_size,
info.bss_address, info.bss_size));
CASCADE_CODE(page_table.UnmapProcessCodeMemory(
info.nro_address + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size, info.data_size));
CASCADE_CODE(page_table.UnmapProcessCodeMemory(
info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size));
CASCADE_CODE(
page_table.UnmapProcessCodeMemory(info.nro_address, info.src_addr, info.text_size));
if (info.bss_size != 0) {
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size +
info.ro_size + info.data_size,
info.bss_address, info.bss_size));
}
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size,
info.data_size));
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
info.src_addr + info.text_size, info.ro_size));
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
return ResultSuccess;
}

View File

@@ -211,6 +211,11 @@ public:
{127, nullptr, "Unknown127"},
{128, nullptr, "Unknown128"},
{129, nullptr, "Unknown129"},
{130, nullptr, "Unknown130"},
{131, nullptr, "Unknown131"},
{132, nullptr, "Unknown132"},
{133, nullptr, "Unknown133"},
{134, nullptr, "Unknown134"},
};
// clang-format on
@@ -287,6 +292,7 @@ public:
{502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
{503, nullptr, "RequestSyncTicket"},
{504, nullptr, "RequestDownloadTicketForPrepurchasedContents2"},
{505, nullptr, "RequestDownloadTicketForPrepurchasedContentsForAccount"},
};
// clang-format on

View File

@@ -158,6 +158,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
{606, nullptr, "GetContentMetaStorage"},
{607, nullptr, "ListAvailableAddOnContent"},
{609, nullptr, "ListAvailabilityAssuredAddOnContent"},
{700, nullptr, "PushDownloadTaskList"},
{701, nullptr, "ClearTaskStatusList"},
{702, nullptr, "RequestDownloadTaskList"},
@@ -289,6 +290,11 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{2514, nullptr, "ClearTaskOfAsyncTaskManager"},
{2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
{2516, nullptr, "EnsureApplicationCertificate"},
{2517, nullptr, "CreateApplicationInstance"},
{2518, nullptr, "UpdateQualificationForDebug"},
{2519, nullptr, "IsQualificationTransitionSupported"},
{2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
{2521, nullptr, "GetRightsUserChangedEvent"},
{2800, nullptr, "GetApplicationIdOfPreomia"},
{3000, nullptr, "RegisterDeviceLockKey"},
{3001, nullptr, "UnregisterDeviceLockKey"},

View File

@@ -307,6 +307,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
{202, nullptr, "SetFieldTestingFlag"},
{203, nullptr, "GetPanelCrcMode"},
{204, nullptr, "SetPanelCrcMode"},
{205, nullptr, "GetNxControllerSettingsEx"},
{206, nullptr, "SetNxControllerSettingsEx"},
};
// clang-format on

View File

@@ -17,19 +17,19 @@ public:
explicit IDsInterface(Core::System& system_) : ServiceFramework{system_, "IDsInterface"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetDsEndpoint"},
{1, nullptr, "GetSetupEvent"},
{2, nullptr, "Unknown2"},
{3, nullptr, "EnableInterface"},
{4, nullptr, "DisableInterface"},
{5, nullptr, "CtrlInPostBufferAsync"},
{6, nullptr, "CtrlOutPostBufferAsync"},
{7, nullptr, "GetCtrlInCompletionEvent"},
{8, nullptr, "GetCtrlInReportData"},
{9, nullptr, "GetCtrlOutCompletionEvent"},
{10, nullptr, "GetCtrlOutReportData"},
{11, nullptr, "StallCtrl"},
{12, nullptr, "AppendConfigurationData"},
{0, nullptr, "BindDevice"},
{1, nullptr, "BindClientProcess"},
{2, nullptr, "AddInterface"},
{3, nullptr, "GetStateChangeEvent"},
{4, nullptr, "GetState"},
{5, nullptr, "ClearDeviceData"},
{6, nullptr, "AddUsbStringDescriptor"},
{7, nullptr, "DeleteUsbStringDescriptor"},
{8, nullptr, "SetUsbDeviceDescriptor"},
{9, nullptr, "SetBinaryObjectStore"},
{10, nullptr, "Enable"},
{11, nullptr, "Disable"},
{12, nullptr, "Unknown12"},
};
// clang-format on
@@ -42,19 +42,7 @@ public:
explicit USB_DS(Core::System& system_) : ServiceFramework{system_, "usb:ds"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindDevice"},
{1, nullptr, "BindClientProcess"},
{2, nullptr, "GetDsInterface"},
{3, nullptr, "GetStateChangeEvent"},
{4, nullptr, "GetState"},
{5, nullptr, "ClearDeviceData"},
{6, nullptr, "AddUsbStringDescriptor"},
{7, nullptr, "DeleteUsbStringDescriptor"},
{8, nullptr, "SetUsbDeviceDescriptor"},
{9, nullptr, "SetBinaryObjectStore"},
{10, nullptr, "Enable"},
{11, nullptr, "Disable"},
{12, nullptr, "Unknown12"},
{0, nullptr, "OpenDsService"},
};
// clang-format on
@@ -120,7 +108,7 @@ public:
{5, nullptr, "DestroyInterfaceAvailableEvent"},
{6, nullptr, "GetInterfaceStateChangeEvent"},
{7, nullptr, "AcquireUsbIf"},
{8, nullptr, "Unknown8"},
{8, nullptr, "ResetDevice"},
};
// clang-format on

View File

@@ -53,6 +53,7 @@ public:
{35, nullptr, "Unknown35"},
{36, nullptr, "Unknown36"},
{37, nullptr, "Unknown37"},
{38, nullptr, "Unknown38"},
};
// clang-format on
@@ -117,7 +118,6 @@ public:
{49, nullptr, "Unknown49"},
{50, nullptr, "Unknown50"},
{51, nullptr, "Unknown51"},
{52, nullptr, "Unknown52"},
};
// clang-format on

View File

@@ -16,6 +16,7 @@ constexpr int mouse_axis_x = 0;
constexpr int mouse_axis_y = 1;
constexpr int wheel_axis_x = 2;
constexpr int wheel_axis_y = 3;
constexpr int motion_wheel_y = 4;
constexpr int touch_axis_x = 10;
constexpr int touch_axis_y = 11;
constexpr PadIdentifier identifier = {
@@ -30,8 +31,9 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_))
PreSetAxis(identifier, mouse_axis_y);
PreSetAxis(identifier, wheel_axis_x);
PreSetAxis(identifier, wheel_axis_y);
PreSetAxis(identifier, motion_wheel_y);
PreSetAxis(identifier, touch_axis_x);
PreSetAxis(identifier, touch_axis_x);
PreSetAxis(identifier, touch_axis_y);
update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
}
@@ -48,6 +50,8 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity);
}
SetAxis(identifier, motion_wheel_y, 0.0f);
if (mouse_panning_timout++ > 20) {
StopPanning();
}
@@ -136,6 +140,7 @@ void Mouse::MouseWheelChange(int x, int y) {
wheel_position.y += y;
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
SetAxis(identifier, motion_wheel_y, static_cast<f32>(y) / 100.0f);
}
void Mouse::ReleaseAllButtons() {
@@ -171,13 +176,39 @@ AnalogMapping Mouse::GetAnalogMappingForDevice(
return mapping;
}
Common::Input::ButtonNames Mouse::GetUIButtonName(const Common::ParamPackage& params) const {
const auto button = static_cast<MouseButton>(params.Get("button", 0));
switch (button) {
case MouseButton::Left:
return Common::Input::ButtonNames::ButtonLeft;
case MouseButton::Right:
return Common::Input::ButtonNames::ButtonRight;
case MouseButton::Wheel:
return Common::Input::ButtonNames::ButtonMouseWheel;
case MouseButton::Backward:
return Common::Input::ButtonNames::ButtonBackward;
case MouseButton::Forward:
return Common::Input::ButtonNames::ButtonForward;
case MouseButton::Task:
return Common::Input::ButtonNames::ButtonTask;
case MouseButton::Extra:
return Common::Input::ButtonNames::ButtonExtra;
case MouseButton::Undefined:
default:
return Common::Input::ButtonNames::Undefined;
}
}
Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const {
if (params.Has("button")) {
return Common::Input::ButtonNames::Value;
return GetUIButtonName(params);
}
if (params.Has("axis")) {
return Common::Input::ButtonNames::Value;
}
if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
return Common::Input::ButtonNames::Engine;
}
return Common::Input::ButtonNames::Invalid;
}

View File

@@ -69,6 +69,8 @@ private:
void UpdateThread(std::stop_token stop_token);
void StopPanning();
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
Common::Vec2<int> mouse_origin;
Common::Vec2<int> last_mouse_position;
Common::Vec2<float> last_mouse_change;

View File

@@ -198,9 +198,15 @@ public:
if (sdl_controller) {
switch (SDL_GameControllerGetType(sdl_controller.get())) {
case SDL_CONTROLLER_TYPE_XBOX360:
return "XBox 360 Controller";
return "Xbox 360 Controller";
case SDL_CONTROLLER_TYPE_XBOXONE:
return "XBox One Controller";
return "Xbox One Controller";
case SDL_CONTROLLER_TYPE_PS3:
return "DualShock 3 Controller";
case SDL_CONTROLLER_TYPE_PS4:
return "DualShock 4 Controller";
case SDL_CONTROLLER_TYPE_PS5:
return "DualSense Controller";
default:
break;
}
@@ -663,6 +669,7 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding() const {
{Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
{Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
{Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
};
}
@@ -699,6 +706,7 @@ ButtonBindings SDLDriver::GetNintendoButtonBinding(
{Settings::NativeButton::SL, sl_button},
{Settings::NativeButton::SR, sr_button},
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
{Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
};
}

View File

@@ -24,7 +24,7 @@ namespace InputCommon {
class SDLJoystick;
using ButtonBindings =
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>;
using ZButtonBindings =
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;

View File

@@ -23,7 +23,7 @@ enum class Tas::TasAxis : u8 {
};
// Supported keywords and buttons from a TAS file
constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
constexpr std::array<std::pair<std::string_view, TasButton>, 18> text_to_tas_button = {
std::pair{"KEY_A", TasButton::BUTTON_A},
{"KEY_B", TasButton::BUTTON_B},
{"KEY_X", TasButton::BUTTON_X},
@@ -40,8 +40,9 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
{"KEY_SL", TasButton::BUTTON_SL},
{"KEY_SR", TasButton::BUTTON_SR},
{"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
{"KEY_HOME", TasButton::BUTTON_HOME},
// These buttons are disabled to avoid TAS input from activating hotkeys
// {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
// {"KEY_HOME", TasButton::BUTTON_HOME},
{"KEY_ZL", TasButton::TRIGGER_ZL},
{"KEY_ZR", TasButton::TRIGGER_ZR},
};

View File

@@ -192,6 +192,25 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
return MAX_UDP_CLIENTS;
}
BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
switch (battery) {
case Response::Battery::Dying:
return BatteryLevel::Empty;
case Response::Battery::Low:
return BatteryLevel::Critical;
case Response::Battery::Medium:
return BatteryLevel::Low;
case Response::Battery::High:
return BatteryLevel::Medium;
case Response::Battery::Full:
case Response::Battery::Charged:
return BatteryLevel::Full;
case Response::Battery::Charging:
default:
return BatteryLevel::Charging;
}
}
void UDPClient::OnVersion([[maybe_unused]] Response::Version data) {
LOG_TRACE(Input, "Version packet received: {}", data.version);
}
@@ -299,6 +318,8 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
const int button = static_cast<int>(buttons[i]);
SetButton(identifier, button, button_status);
}
SetBattery(identifier, GetBatteryLevel(data.info.battery));
}
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {

View File

@@ -15,6 +15,7 @@ namespace InputCommon::CemuhookUDP {
class Socket;
namespace Response {
enum class Battery : u8;
struct PadData;
struct PortInfo;
struct TouchPad;
@@ -137,6 +138,9 @@ private:
// Translates configuration to client number
std::size_t GetClientNumber(std::string_view host, u16 port) const;
// Translates UDP battery level to input engine battery level
BatteryLevel GetBatteryLevel(Response::Battery battery) const;
void OnVersion(Response::Version);
void OnPortInfo(Response::PortInfo);
void OnPadData(Response::PadData, std::size_t client);

View File

@@ -173,7 +173,7 @@ void InputEngine::ResetButtonState() {
SetButton(controller.first, button.first, false);
}
for (const auto& button : controller.second.hat_buttons) {
SetHatButton(controller.first, button.first, false);
SetHatButton(controller.first, button.first, 0);
}
}
}

View File

@@ -143,6 +143,19 @@ void MappingFactory::RegisterMotion(const MappingData& data) {
}
new_input.Set("port", static_cast<int>(data.pad.port));
new_input.Set("pad", static_cast<int>(data.pad.pad));
// If engine is mouse map the mouse position as 3 axis motion
if (data.engine == "mouse") {
new_input.Set("axis_x", 1);
new_input.Set("invert_x", "-");
new_input.Set("axis_y", 0);
new_input.Set("axis_z", 4);
new_input.Set("range", 1.0f);
new_input.Set("deadzone", 0.0f);
input_queue.Push(new_input);
return;
}
switch (data.type) {
case EngineInputType::Button:
case EngineInputType::HatButton:

View File

@@ -458,9 +458,10 @@ void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {
std::string definition{fmt::format("layout(location={}", index)};
const u32 remainder{4 - element};
const TransformFeedbackVarying* xfb_varying{};
if (!runtime_info.xfb_varyings.empty()) {
xfb_varying = &runtime_info.xfb_varyings[base_index + element];
xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
const size_t xfb_varying_index{base_index + element};
if (xfb_varying_index < runtime_info.xfb_varyings.size()) {
xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index];
xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;
}
const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
if (element > 0) {

View File

@@ -164,9 +164,10 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
while (element < 4) {
const u32 remainder{4 - element};
const TransformFeedbackVarying* xfb_varying{};
if (!ctx.runtime_info.xfb_varyings.empty()) {
xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element];
xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
const size_t xfb_varying_index{base_attr_index + element};
if (xfb_varying_index < ctx.runtime_info.xfb_varyings.size()) {
xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index];
xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;
}
const u32 num_components{xfb_varying ? xfb_varying->components : remainder};

View File

@@ -16,6 +16,6 @@ add_executable(tests
create_target_directory_groups(tests)
target_link_libraries(tests PRIVATE common core input_common)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads)
add_test(NAME tests COMMAND tests)

View File

@@ -66,7 +66,7 @@ private:
bool initialized{};
NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
AVCodec* av_codec{nullptr};
const AVCodec* av_codec{nullptr};
AVCodecContext* av_codec_ctx{nullptr};
AVBufferRef* av_gpu_decoder{nullptr};

View File

@@ -155,9 +155,6 @@ uint SwizzleOffset(uvec2 pos) {
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
// is the same as [(num_bits - 1):0] and repeats all the way down.
uint Replicate(uint val, uint num_bits, uint to_bit) {
if (num_bits == 0 || to_bit == 0) {
return 0;
}
const uint v = val & uint((1 << num_bits) - 1);
uint res = v;
uint reslen = num_bits;
@@ -187,42 +184,57 @@ uint ReplicateBitTo9(uint value) {
return REPLICATE_1_BIT_TO_9_TABLE[value];
}
uint FastReplicateTo8(uint value, uint num_bits) {
switch (num_bits) {
case 1:
return REPLICATE_1_BIT_TO_8_TABLE[value];
case 2:
return REPLICATE_2_BIT_TO_8_TABLE[value];
case 3:
return REPLICATE_3_BIT_TO_8_TABLE[value];
case 4:
return REPLICATE_4_BIT_TO_8_TABLE[value];
case 5:
return REPLICATE_5_BIT_TO_8_TABLE[value];
case 6:
return REPLICATE_6_BIT_TO_8_TABLE[value];
case 7:
return REPLICATE_7_BIT_TO_8_TABLE[value];
case 8:
uint FastReplicate(uint value, uint num_bits, uint to_bit) {
if (num_bits == 0) {
return 0;
}
if (num_bits == to_bit) {
return value;
}
return Replicate(value, num_bits, 8);
if (to_bit == 6) {
switch (num_bits) {
case 1:
return REPLICATE_1_BIT_TO_6_TABLE[value];
case 2:
return REPLICATE_2_BIT_TO_6_TABLE[value];
case 3:
return REPLICATE_3_BIT_TO_6_TABLE[value];
case 4:
return REPLICATE_4_BIT_TO_6_TABLE[value];
case 5:
return REPLICATE_5_BIT_TO_6_TABLE[value];
default:
break;
}
} else { /* if (to_bit == 8) */
switch (num_bits) {
case 1:
return REPLICATE_1_BIT_TO_8_TABLE[value];
case 2:
return REPLICATE_2_BIT_TO_8_TABLE[value];
case 3:
return REPLICATE_3_BIT_TO_8_TABLE[value];
case 4:
return REPLICATE_4_BIT_TO_8_TABLE[value];
case 5:
return REPLICATE_5_BIT_TO_8_TABLE[value];
case 6:
return REPLICATE_6_BIT_TO_8_TABLE[value];
case 7:
return REPLICATE_7_BIT_TO_8_TABLE[value];
default:
break;
}
}
return Replicate(value, num_bits, to_bit);
}
uint FastReplicateTo8(uint value, uint num_bits) {
return FastReplicate(value, num_bits, 8);
}
uint FastReplicateTo6(uint value, uint num_bits) {
switch (num_bits) {
case 1:
return REPLICATE_1_BIT_TO_6_TABLE[value];
case 2:
return REPLICATE_2_BIT_TO_6_TABLE[value];
case 3:
return REPLICATE_3_BIT_TO_6_TABLE[value];
case 4:
return REPLICATE_4_BIT_TO_6_TABLE[value];
case 5:
return REPLICATE_5_BIT_TO_6_TABLE[value];
}
return Replicate(value, num_bits, 6);
return FastReplicate(value, num_bits, 6);
}
uint Div3Floor(uint v) {

View File

@@ -1038,7 +1038,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
}
if (has_ext_shader_atomic_int64) {
VkPhysicalDeviceShaderAtomicInt64Features atomic_int64;
atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES;
atomic_int64.pNext = nullptr;
features.pNext = &atomic_int64;
physical.GetFeatures2KHR(features);

View File

@@ -65,28 +65,27 @@ const std::array<int, 2> Config::default_stick_mod = {
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
const std::array<UISettings::Shortcut, 20> Config::default_hotkeys{{
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
}};
// clang-format on
@@ -679,7 +678,6 @@ void Config::ReadShortcutValues() {
qt_config->beginGroup(QStringLiteral("Shortcuts"));
for (const auto& [name, group, shortcut] : default_hotkeys) {
const auto& [keyseq, context] = shortcut;
qt_config->beginGroup(group);
qt_config->beginGroup(name);
// No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
@@ -688,7 +686,10 @@ void Config::ReadShortcutValues() {
UISettings::values.shortcuts.push_back(
{name,
group,
{ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
{ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
.toString(),
shortcut.context}});
qt_config->endGroup();
qt_config->endGroup();
}
@@ -741,7 +742,9 @@ void Config::ReadUIValues() {
qt_config->beginGroup(QStringLiteral("UI"));
UISettings::values.theme =
ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second))
ReadSetting(
QStringLiteral("theme"),
QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second))
.toString();
ReadBasicSetting(UISettings::values.enable_discord_presence);
ReadBasicSetting(UISettings::values.select_user_on_boot);
@@ -1227,8 +1230,10 @@ void Config::SaveShortcutValues() {
qt_config->beginGroup(group);
qt_config->beginGroup(name);
WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkey.first);
WriteSetting(QStringLiteral("Context"), shortcut.second, default_hotkey.second);
WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq);
WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
default_hotkey.controller_keyseq);
WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
qt_config->endGroup();
qt_config->endGroup();
}
@@ -1267,7 +1272,7 @@ void Config::SaveUIValues() {
qt_config->beginGroup(QStringLiteral("UI"));
WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
QString::fromUtf8(UISettings::themes[0].second));
QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second));
WriteBasicSetting(UISettings::values.enable_discord_presence);
WriteBasicSetting(UISettings::values.select_user_on_boot);

View File

@@ -46,7 +46,15 @@ public:
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
static const std::array<UISettings::Shortcut, 21> default_hotkeys;
static const std::array<UISettings::Shortcut, 20> default_hotkeys;
static constexpr UISettings::Theme default_theme{
#ifdef _WIN32
UISettings::Theme::DarkColorful
#else
UISettings::Theme::DefaultColorful
#endif
};
private:
void Initialize(const std::string& config_name);

View File

@@ -45,7 +45,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
general_tab{std::make_unique<ConfigureGeneral>(system_, this)},
graphics_tab{std::make_unique<ConfigureGraphics>(system_, this)},
graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
input_tab{std::make_unique<ConfigureInput>(system_, this)},
network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)},

View File

@@ -5,15 +5,24 @@
#include <QMenu>
#include <QMessageBox>
#include <QStandardItemModel>
#include "common/settings.h"
#include <QTimer>
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "ui_configure_hotkeys.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_hotkeys.h"
#include "yuzu/hotkeys.h"
#include "yuzu/util/sequence_dialog/sequence_dialog.h"
ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) {
constexpr int name_column = 0;
constexpr int hotkey_column = 1;
constexpr int controller_column = 2;
ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()),
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -26,16 +35,24 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
ui->hotkey_list->setModel(model);
// TODO(Kloen): Make context configurable as well (hiding the column for now)
ui->hotkey_list->hideColumn(2);
ui->hotkey_list->setColumnWidth(0, 200);
ui->hotkey_list->resizeColumnToContents(1);
ui->hotkey_list->setColumnWidth(name_column, 200);
ui->hotkey_list->resizeColumnToContents(hotkey_column);
connect(ui->button_restore_defaults, &QPushButton::clicked, this,
&ConfigureHotkeys::RestoreDefaults);
connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll);
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
connect(poll_timer.get(), &QTimer::timeout, [this] {
const auto buttons = controller->GetNpadButtons();
if (buttons.raw != Core::HID::NpadButton::None) {
SetPollingResult(buttons.raw, false);
return;
}
});
RetranslateUI();
}
@@ -49,15 +66,18 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
auto* action = new QStandardItem(hotkey.first);
auto* keyseq =
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
action->setEditable(false);
keyseq->setEditable(false);
parent_item->appendRow({action, keyseq});
controller_keyseq->setEditable(false);
parent_item->appendRow({action, keyseq, controller_keyseq});
}
model->appendRow(parent_item);
}
ui->hotkey_list->expandAll();
ui->hotkey_list->resizeColumnToContents(0);
ui->hotkey_list->resizeColumnToContents(name_column);
ui->hotkey_list->resizeColumnToContents(hotkey_column);
}
void ConfigureHotkeys::changeEvent(QEvent* event) {
@@ -71,7 +91,7 @@ void ConfigureHotkeys::changeEvent(QEvent* event) {
void ConfigureHotkeys::RetranslateUI() {
ui->retranslateUi(this);
model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")});
model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
}
void ConfigureHotkeys::Configure(QModelIndex index) {
@@ -79,7 +99,15 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
return;
}
index = index.sibling(index.row(), 1);
// Controller configuration is selected
if (index.column() == controller_column) {
ConfigureController(index);
return;
}
// Swap to the hotkey column
index = index.sibling(index.row(), hotkey_column);
const auto previous_key = model->data(index);
SequenceDialog hotkey_dialog{this};
@@ -99,13 +127,113 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
model->setData(index, key_sequence.toString(QKeySequence::NativeText));
}
}
void ConfigureHotkeys::ConfigureController(QModelIndex index) {
if (timeout_timer->isActive()) {
return;
}
const auto previous_key = model->data(index);
input_setter = [this, index, previous_key](const Core::HID::NpadButton button,
const bool cancel) {
if (cancel) {
model->setData(index, previous_key);
return;
}
const QString button_string = tr("Home+%1").arg(GetButtonName(button));
const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string);
if (key_sequence_used) {
QMessageBox::warning(
this, tr("Conflicting Key Sequence"),
tr("The entered key sequence is already assigned to: %1").arg(used_action));
model->setData(index, previous_key);
} else {
model->setData(index, button_string);
}
};
model->setData(index, tr("[waiting]"));
timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(200); // Check for new inputs every 200ms
// We need to disable configuration to be able to read npad buttons
controller->DisableConfiguration();
controller->DisableSystemButtons();
}
void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) {
timeout_timer->stop();
poll_timer->stop();
// Re-Enable configuration
controller->EnableConfiguration();
controller->EnableSystemButtons();
(*input_setter)(button, cancel);
input_setter = std::nullopt;
}
QString ConfigureHotkeys::GetButtonName(Core::HID::NpadButton button) const {
Core::HID::NpadButtonState state{button};
if (state.a) {
return tr("A");
}
if (state.b) {
return tr("B");
}
if (state.x) {
return tr("X");
}
if (state.y) {
return tr("Y");
}
if (state.l || state.right_sl || state.left_sl) {
return tr("L");
}
if (state.r || state.right_sr || state.left_sr) {
return tr("R");
}
if (state.zl) {
return tr("ZL");
}
if (state.zr) {
return tr("ZR");
}
if (state.left) {
return tr("Dpad_Left");
}
if (state.right) {
return tr("Dpad_Right");
}
if (state.up) {
return tr("Dpad_Up");
}
if (state.down) {
return tr("Dpad_Down");
}
if (state.stick_l) {
return tr("Left_Stick");
}
if (state.stick_r) {
return tr("Right_Stick");
}
if (state.minus) {
return tr("Minus");
}
if (state.plus) {
return tr("Plus");
}
return tr("Invalid");
}
std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
for (int r = 0; r < model->rowCount(); ++r) {
const QStandardItem* const parent = model->item(r, 0);
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
const QStandardItem* const key_seq_item = parent->child(r2, 1);
const QStandardItem* const key_seq_item = parent->child(r2, hotkey_column);
const auto key_seq_str = key_seq_item->text();
const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText);
@@ -118,12 +246,31 @@ std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence)
return std::make_pair(false, QString());
}
std::pair<bool, QString> ConfigureHotkeys::IsUsedControllerKey(const QString& key_sequence) const {
for (int r = 0; r < model->rowCount(); ++r) {
const QStandardItem* const parent = model->item(r, 0);
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
const QStandardItem* const key_seq_item = parent->child(r2, controller_column);
const auto key_seq_str = key_seq_item->text();
if (key_sequence == key_seq_str) {
return std::make_pair(true, parent->child(r2, 0)->text());
}
}
}
return std::make_pair(false, QString());
}
void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
for (int key_id = 0; key_id < model->rowCount(); key_id++) {
const QStandardItem* parent = model->item(key_id, 0);
for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
const QStandardItem* action = parent->child(key_column_id, 0);
const QStandardItem* keyseq = parent->child(key_column_id, 1);
const QStandardItem* action = parent->child(key_column_id, name_column);
const QStandardItem* keyseq = parent->child(key_column_id, hotkey_column);
const QStandardItem* controller_keyseq =
parent->child(key_column_id, controller_column);
for (auto& [group, sub_actions] : registry.hotkey_groups) {
if (group != parent->text())
continue;
@@ -131,6 +278,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
if (action_name != action->text())
continue;
hotkey.keyseq = QKeySequence(keyseq->text());
hotkey.controller_keyseq = controller_keyseq->text();
}
}
}
@@ -144,7 +292,12 @@ void ConfigureHotkeys::RestoreDefaults() {
const QStandardItem* parent = model->item(r, 0);
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
model->item(r, 0)->child(r2, 1)->setText(Config::default_hotkeys[r2].shortcut.first);
model->item(r, 0)
->child(r2, hotkey_column)
->setText(Config::default_hotkeys[r2].shortcut.keyseq);
model->item(r, 0)
->child(r2, controller_column)
->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq);
}
}
}
@@ -154,7 +307,8 @@ void ConfigureHotkeys::ClearAll() {
const QStandardItem* parent = model->item(r, 0);
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
model->item(r, 0)->child(r2, 1)->setText(QString{});
model->item(r, 0)->child(r2, hotkey_column)->setText(QString{});
model->item(r, 0)->child(r2, controller_column)->setText(QString{});
}
}
}
@@ -165,28 +319,52 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
return;
}
const auto selected = index.sibling(index.row(), 1);
// Swap to the hotkey column if the controller hotkey column is not selected
if (index.column() != controller_column) {
index = index.sibling(index.row(), hotkey_column);
}
QMenu context_menu;
QAction* restore_default = context_menu.addAction(tr("Restore Default"));
QAction* clear = context_menu.addAction(tr("Clear"));
connect(restore_default, &QAction::triggered, [this, selected] {
const QKeySequence& default_key_sequence = QKeySequence::fromString(
Config::default_hotkeys[selected.row()].shortcut.first, QKeySequence::NativeText);
const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
if (key_sequence_used &&
default_key_sequence != QKeySequence(model->data(selected).toString())) {
QMessageBox::warning(
this, tr("Conflicting Key Sequence"),
tr("The default key sequence is already assigned to: %1").arg(used_action));
} else {
model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText));
connect(restore_default, &QAction::triggered, [this, index] {
if (index.column() == controller_column) {
RestoreControllerHotkey(index);
return;
}
RestoreHotkey(index);
});
connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); });
connect(clear, &QAction::triggered, [this, index] { model->setData(index, QString{}); });
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
}
void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
const QString& default_key_sequence =
Config::default_hotkeys[index.row()].shortcut.controller_keyseq;
const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence);
if (key_sequence_used && default_key_sequence != model->data(index).toString()) {
QMessageBox::warning(
this, tr("Conflicting Button Sequence"),
tr("The default button sequence is already assigned to: %1").arg(used_action));
} else {
model->setData(index, default_key_sequence);
}
}
void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
const QKeySequence& default_key_sequence = QKeySequence::fromString(
Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText);
const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) {
QMessageBox::warning(
this, tr("Conflicting Key Sequence"),
tr("The default key sequence is already assigned to: %1").arg(used_action));
} else {
model->setData(index, default_key_sequence.toString(QKeySequence::NativeText));
}
}

View File

@@ -7,6 +7,16 @@
#include <memory>
#include <QWidget>
namespace Common {
class ParamPackage;
}
namespace Core::HID {
class HIDCore;
class EmulatedController;
enum class NpadButton : u64;
} // namespace Core::HID
namespace Ui {
class ConfigureHotkeys;
}
@@ -18,7 +28,7 @@ class ConfigureHotkeys : public QWidget {
Q_OBJECT
public:
explicit ConfigureHotkeys(QWidget* parent = nullptr);
explicit ConfigureHotkeys(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr);
~ConfigureHotkeys() override;
void ApplyConfiguration(HotkeyRegistry& registry);
@@ -35,13 +45,24 @@ private:
void RetranslateUI();
void Configure(QModelIndex index);
void ConfigureController(QModelIndex index);
std::pair<bool, QString> IsUsedKey(QKeySequence key_sequence) const;
std::pair<bool, QString> IsUsedControllerKey(const QString& key_sequence) const;
void RestoreDefaults();
void ClearAll();
void PopupContextMenu(const QPoint& menu_location);
void RestoreControllerHotkey(QModelIndex index);
void RestoreHotkey(QModelIndex index);
std::unique_ptr<Ui::ConfigureHotkeys> ui;
QStandardItemModel* model;
void SetPollingResult(Core::HID::NpadButton button, bool cancel);
QString GetButtonName(Core::HID::NpadButton button) const;
Core::HID::EmulatedController* controller;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter;
};

View File

@@ -102,6 +102,16 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
return QObject::tr("Share");
case Common::Input::ButtonNames::Options:
return QObject::tr("Options");
case Common::Input::ButtonNames::ButtonMouseWheel:
return QObject::tr("Wheel", "Indicates the mouse wheel");
case Common::Input::ButtonNames::ButtonBackward:
return QObject::tr("Backward");
case Common::Input::ButtonNames::ButtonForward:
return QObject::tr("Forward");
case Common::Input::ButtonNames::ButtonTask:
return QObject::tr("Task");
case Common::Input::ButtonNames::ButtonExtra:
return QObject::tr("Extra");
default:
return QObject::tr("[undefined]");
}

View File

@@ -95,7 +95,7 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList(
std::size_t row = 0;
auto add_threads = [&](const std::vector<Kernel::KThread*>& threads) {
for (std::size_t i = 0; i < threads.size(); ++i) {
if (threads[i]->GetThreadTypeForDebugging() == Kernel::ThreadType::User) {
if (threads[i]->GetThreadType() == Kernel::ThreadType::User) {
item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i], system));
item_list.back()->row = row;
}
@@ -153,7 +153,7 @@ QString WaitTreeCallstack::GetText() const {
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list;
if (thread.GetThreadTypeForDebugging() != Kernel::ThreadType::User) {
if (thread.GetThreadType() != Kernel::ThreadType::User) {
return list;
}

View File

@@ -2,10 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sstream>
#include <QKeySequence>
#include <QShortcut>
#include <QTreeWidgetItem>
#include <QtGlobal>
#include "core/hid/emulated_controller.h"
#include "yuzu/hotkeys.h"
#include "yuzu/uisettings.h"
@@ -18,8 +21,9 @@ void HotkeyRegistry::SaveHotkeys() {
for (const auto& hotkey : group.second) {
UISettings::values.shortcuts.push_back(
{hotkey.first, group.first,
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
hotkey.second.context)});
UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
hotkey.second.controller_keyseq,
hotkey.second.context})});
}
}
}
@@ -29,28 +33,49 @@ void HotkeyRegistry::LoadHotkeys() {
// beginGroup()
for (auto shortcut : UISettings::values.shortcuts) {
Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
if (!shortcut.shortcut.first.isEmpty()) {
hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText);
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second);
if (!shortcut.shortcut.keyseq.isEmpty()) {
hk.keyseq =
QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText);
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
}
if (!shortcut.shortcut.controller_keyseq.isEmpty()) {
hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
}
if (hk.shortcut) {
hk.shortcut->disconnect();
hk.shortcut->setKey(hk.keyseq);
}
if (hk.controller_shortcut) {
hk.controller_shortcut->disconnect();
hk.controller_shortcut->SetKey(hk.controller_keyseq);
}
}
}
QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut)
if (!hk.shortcut) {
hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
}
hk.shortcut->setAutoRepeat(false);
return hk.shortcut;
}
ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action,
Core::HID::EmulatedController* controller) {
Hotkey& hk = hotkey_groups[group][action];
if (!hk.controller_shortcut) {
hk.controller_shortcut = new ControllerShortcut(controller);
hk.controller_shortcut->SetKey(hk.controller_keyseq);
}
return hk.controller_shortcut;
}
QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) {
return hotkey_groups[group][action].keyseq;
}
@@ -59,3 +84,131 @@ Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group,
const QString& action) {
return hotkey_groups[group][action].context;
}
ControllerShortcut::ControllerShortcut(Core::HID::EmulatedController* controller) {
emulated_controller = controller;
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); },
.is_npad_service = false,
};
callback_key = emulated_controller->SetCallback(engine_callback);
is_enabled = true;
}
ControllerShortcut::~ControllerShortcut() {
emulated_controller->DeleteCallback(callback_key);
}
void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) {
button_sequence = buttons;
}
void ControllerShortcut::SetKey(const QString& buttons_shortcut) {
ControllerButtonSequence sequence{};
name = buttons_shortcut.toStdString();
std::istringstream command_line(buttons_shortcut.toStdString());
std::string line;
while (std::getline(command_line, line, '+')) {
if (line.empty()) {
continue;
}
if (line == "A") {
sequence.npad.a.Assign(1);
}
if (line == "B") {
sequence.npad.b.Assign(1);
}
if (line == "X") {
sequence.npad.x.Assign(1);
}
if (line == "Y") {
sequence.npad.y.Assign(1);
}
if (line == "L") {
sequence.npad.l.Assign(1);
}
if (line == "R") {
sequence.npad.r.Assign(1);
}
if (line == "ZL") {
sequence.npad.zl.Assign(1);
}
if (line == "ZR") {
sequence.npad.zr.Assign(1);
}
if (line == "Dpad_Left") {
sequence.npad.left.Assign(1);
}
if (line == "Dpad_Right") {
sequence.npad.right.Assign(1);
}
if (line == "Dpad_Up") {
sequence.npad.up.Assign(1);
}
if (line == "Dpad_Down") {
sequence.npad.down.Assign(1);
}
if (line == "Left_Stick") {
sequence.npad.stick_l.Assign(1);
}
if (line == "Right_Stick") {
sequence.npad.stick_r.Assign(1);
}
if (line == "Minus") {
sequence.npad.minus.Assign(1);
}
if (line == "Plus") {
sequence.npad.plus.Assign(1);
}
if (line == "Home") {
sequence.home.home.Assign(1);
}
if (line == "Screenshot") {
sequence.capture.capture.Assign(1);
}
}
button_sequence = sequence;
}
ControllerButtonSequence ControllerShortcut::ButtonSequence() const {
return button_sequence;
}
void ControllerShortcut::SetEnabled(bool enable) {
is_enabled = enable;
}
bool ControllerShortcut::IsEnabled() const {
return is_enabled;
}
void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
if (!is_enabled) {
return;
}
if (type != Core::HID::ControllerTriggerType::Button) {
return;
}
if (button_sequence.npad.raw == Core::HID::NpadButton::None &&
button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
return;
}
const auto player_npad_buttons =
emulated_controller->GetNpadButtons().raw & button_sequence.npad.raw;
const u64 player_capture_buttons =
emulated_controller->GetCaptureButtons().raw & button_sequence.capture.raw;
const u64 player_home_buttons =
emulated_controller->GetHomeButtons().raw & button_sequence.home.raw;
if (player_npad_buttons == button_sequence.npad.raw &&
player_capture_buttons == button_sequence.capture.raw &&
player_home_buttons == button_sequence.home.raw && !active) {
// Force user to press the home or capture button again
active = true;
emit Activated();
return;
}
active = false;
}

View File

@@ -5,11 +5,53 @@
#pragma once
#include <map>
#include "core/hid/hid_types.h"
class QDialog;
class QKeySequence;
class QSettings;
class QShortcut;
class ControllerShortcut;
namespace Core::HID {
enum class ControllerTriggerType;
class EmulatedController;
} // namespace Core::HID
struct ControllerButtonSequence {
Core::HID::CaptureButtonState capture{};
Core::HID::HomeButtonState home{};
Core::HID::NpadButtonState npad{};
};
class ControllerShortcut : public QObject {
Q_OBJECT
public:
explicit ControllerShortcut(Core::HID::EmulatedController* controller);
~ControllerShortcut();
void SetKey(const ControllerButtonSequence& buttons);
void SetKey(const QString& buttons_shortcut);
ControllerButtonSequence ButtonSequence() const;
void SetEnabled(bool enable);
bool IsEnabled() const;
Q_SIGNALS:
void Activated();
private:
void ControllerUpdateEvent(Core::HID::ControllerTriggerType type);
bool is_enabled{};
bool active{};
int callback_key{};
ControllerButtonSequence button_sequence{};
std::string name{};
Core::HID::EmulatedController* emulated_controller = nullptr;
};
class HotkeyRegistry final {
public:
@@ -46,6 +88,8 @@ public:
* QShortcut's parent.
*/
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action,
Core::HID::EmulatedController* controller);
/**
* Returns a QKeySequence object whose signal can be connected to QAction::setShortcut.
@@ -68,7 +112,9 @@ public:
private:
struct Hotkey {
QKeySequence keyseq;
QString controller_keyseq;
QShortcut* shortcut = nullptr;
ControllerShortcut* controller_shortcut = nullptr;
Qt::ShortcutContext context = Qt::WindowShortcut;
};

View File

@@ -32,6 +32,7 @@
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "yuzu/util/controller_navigation.h"
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
// defines.
@@ -966,6 +967,12 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name
action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name));
this->addAction(action);
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
const auto* controller_hotkey =
hotkey_registry.GetControllerHotkey(main_window, action_name, controller);
connect(controller_hotkey, &ControllerShortcut::Activated, this,
[action] { action->trigger(); });
}
void GMainWindow::InitializeHotkeys() {
@@ -987,8 +994,12 @@ void GMainWindow::InitializeHotkeys() {
static const QString main_window = QStringLiteral("Main Window");
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
const QShortcut* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this);
const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this);
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
const auto* controller_hotkey =
hotkey_registry.GetControllerHotkey(main_window, action_name, controller);
connect(hotkey, &QShortcut::activated, this, function);
connect(controller_hotkey, &ControllerShortcut::Activated, this, function);
};
connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] {
@@ -997,33 +1008,24 @@ void GMainWindow::InitializeHotkeys() {
ToggleFullscreen();
}
});
connect_shortcut(QStringLiteral("Toggle Speed Limit"), [&] {
Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
UpdateStatusBar();
});
constexpr u16 SPEED_LIMIT_STEP = 5;
connect_shortcut(QStringLiteral("Increase Speed Limit"), [&] {
if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP +
Settings::values.speed_limit.GetValue());
UpdateStatusBar();
}
});
connect_shortcut(QStringLiteral("Decrease Speed Limit"), [&] {
if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) {
Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() -
SPEED_LIMIT_STEP);
UpdateStatusBar();
}
});
connect_shortcut(QStringLiteral("Change Docked Mode"), [&] {
Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue());
OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
Settings::values.use_docked_mode.GetValue(), *system);
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
});
connect_shortcut(QStringLiteral("Mute Audio"),
connect_shortcut(QStringLiteral("Audio Mute/Unmute"),
[] { Settings::values.audio_muted = !Settings::values.audio_muted; });
connect_shortcut(QStringLiteral("Audio Volume Down"), [] {
const auto current_volume = static_cast<int>(Settings::values.volume.GetValue());
const auto new_volume = std::max(current_volume - 5, 0);
Settings::values.volume.SetValue(static_cast<u8>(new_volume));
});
connect_shortcut(QStringLiteral("Audio Volume Up"), [] {
const auto current_volume = static_cast<int>(Settings::values.volume.GetValue());
const auto new_volume = std::min(current_volume + 5, 100);
Settings::values.volume.SetValue(static_cast<u8>(new_volume));
});
connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
Settings::values.disable_fps_limit.SetValue(!Settings::values.disable_fps_limit.GetValue());
});
@@ -1165,8 +1167,7 @@ void GMainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode);
connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars);
connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar);
connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720);
connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900);
@@ -2168,6 +2169,11 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
}
void GMainWindow::OnMenuLoadFile() {
if (is_load_file_select_active) {
return;
}
is_load_file_select_active = true;
const QString extensions =
QStringLiteral("*.")
.append(GameList::supported_file_extensions.join(QStringLiteral(" *.")))
@@ -2177,6 +2183,7 @@ void GMainWindow::OnMenuLoadFile() {
.arg(extensions);
const QString filename = QFileDialog::getOpenFileName(
this, tr("Load File"), UISettings::values.roms_path, file_filter);
is_load_file_select_active = false;
if (filename.isEmpty()) {
return;
@@ -2809,6 +2816,11 @@ void GMainWindow::OnTasStartStop() {
if (!emulation_running) {
return;
}
// Disable system buttons to prevent TAS from executing a hotkey
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
controller->ResetSystemButtons();
input_subsystem->GetTas()->StartStop();
OnTasStateChanged();
}
@@ -2817,12 +2829,34 @@ void GMainWindow::OnTasRecord() {
if (!emulation_running) {
return;
}
if (is_tas_recording_dialog_active) {
return;
}
// Disable system buttons to prevent TAS from recording a hotkey
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
controller->ResetSystemButtons();
const bool is_recording = input_subsystem->GetTas()->Record();
if (!is_recording) {
const auto res =
QMessageBox::question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
QMessageBox::Yes | QMessageBox::No);
is_tas_recording_dialog_active = true;
ControllerNavigation* controller_navigation =
new ControllerNavigation(system->HIDCore(), this);
// Use QMessageBox instead of question so we can link controller navigation
QMessageBox* box_dialog = new QMessageBox();
box_dialog->setWindowTitle(tr("TAS Recording"));
box_dialog->setText(tr("Overwrite file of player 1?"));
box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
box_dialog->setDefaultButton(QMessageBox::Yes);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
[box_dialog](Qt::Key key) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(box_dialog, event);
});
int res = box_dialog->exec();
controller_navigation->UnloadController();
input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
is_tas_recording_dialog_active = false;
}
OnTasStateChanged();
}
@@ -2871,10 +2905,15 @@ void GMainWindow::OnLoadAmiibo() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
}
if (is_amiibo_file_select_active) {
return;
}
is_amiibo_file_select_active = true;
const QString extensions{QStringLiteral("*.bin")};
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter);
is_amiibo_file_select_active = false;
if (filename.isEmpty()) {
return;
@@ -2934,6 +2973,10 @@ void GMainWindow::OnToggleFilterBar() {
}
}
void GMainWindow::OnToggleStatusBar() {
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
}
void GMainWindow::OnCaptureScreenshot() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
@@ -3626,8 +3669,8 @@ int main(int argc, char* argv[]) {
QCoreApplication::setApplicationName(QStringLiteral("yuzu"));
#ifdef _WIN32
// Increases the maximum open file limit to 4096
_setmaxstdio(4096);
// Increases the maximum open file limit to 8192
_setmaxstdio(8192);
#endif
#ifdef __APPLE__

View File

@@ -186,6 +186,9 @@ public slots:
void OnTasStateChanged();
private:
/// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry.
void LinkActionShortcut(QAction* action, const QString& action_name);
void RegisterMetaTypes();
void InitializeWidgets();
@@ -286,6 +289,7 @@ private slots:
void OnOpenYuzuFolder();
void OnAbout();
void OnToggleFilterBar();
void OnToggleStatusBar();
void OnDisplayTitleBars(bool);
void InitializeHotkeys();
void ToggleFullscreen();
@@ -303,9 +307,6 @@ private slots:
void OnMouseActivity();
private:
/// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry.
void LinkActionShortcut(QAction* action, const QString& action_name);
void RemoveBaseContent(u64 program_id, const QString& entry_type);
void RemoveUpdateContent(u64 program_id, const QString& entry_type);
void RemoveAddOnContent(u64 program_id, const QString& entry_type);
@@ -400,6 +401,16 @@ private:
// Applets
QtSoftwareKeyboardDialog* software_keyboard = nullptr;
// True if amiibo file select is visible
bool is_amiibo_file_select_active{};
// True if load file select is visible
bool is_load_file_select_active{};
// True if TAS recording dialog is visible
bool is_tas_recording_dialog_active{};
#ifdef __linux__
QDBusObjectPath wake_lock{};
#endif

View File

@@ -17,7 +17,11 @@
namespace UISettings {
using ContextualShortcut = std::pair<QString, int>;
struct ContextualShortcut {
QString keyseq;
QString controller_keyseq;
int context;
};
struct Shortcut {
QString name;
@@ -25,6 +29,15 @@ struct Shortcut {
ContextualShortcut shortcut;
};
enum class Theme {
Default,
DefaultColorful,
Dark,
DarkColorful,
MidnightBlue,
MidnightBlueColorful,
};
using Themes = std::array<std::pair<const char*, const char*>, 6>;
extern const Themes themes;