summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/fs/file.h11
-rw-r--r--src/common/page_table.cpp1
-rw-r--r--src/common/page_table.h6
-rw-r--r--src/common/point.h57
-rw-r--r--src/core/arm/arm_interface.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp59
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp61
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h2
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h2
-rw-r--r--src/core/hle/kernel/k_class_token.cpp79
-rw-r--r--src/core/hle/kernel/k_client_port.h6
-rw-r--r--src/core/hle/kernel/k_client_session.h4
-rw-r--r--src/core/hle/kernel/k_event.h14
-rw-r--r--src/core/hle/kernel/k_port.h3
-rw-r--r--src/core/hle/kernel/k_process.h8
-rw-r--r--src/core/hle/kernel/k_readable_event.h4
-rw-r--r--src/core/hle/kernel/k_resource_limit.h4
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp1
-rw-r--r--src/core/hle/kernel/k_server_port.h12
-rw-r--r--src/core/hle/kernel/k_server_session.h6
-rw-r--r--src/core/hle/kernel/k_session.h10
-rw-r--r--src/core/hle/kernel/k_shared_memory.h4
-rw-r--r--src/core/hle/kernel/k_slab_heap.h199
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h4
-rw-r--r--src/core/hle/kernel/k_thread.cpp10
-rw-r--r--src/core/hle/kernel/k_thread.h10
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h10
-rw-r--r--src/core/hle/kernel/k_writable_event.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp5
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp92
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h36
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp11
-rw-r--r--src/core/hle/service/ldn/ldn.cpp141
-rw-r--r--src/core/memory.cpp43
-rw-r--r--src/input_common/sdl/sdl_impl.cpp281
-rw-r--r--src/input_common/sdl/sdl_impl.h37
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h4
-rw-r--r--src/video_core/gpu.cpp12
-rw-r--r--src/video_core/gpu.h2
-rw-r--r--src/video_core/rasterizer_accelerated.cpp6
-rw-r--r--src/video_core/rasterizer_accelerated.h12
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp67
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h7
-rw-r--r--src/yuzu/CMakeLists.txt13
-rw-r--r--src/yuzu/applets/software_keyboard.cpp43
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp14
-rw-r--r--src/yuzu/configuration/configure_dialog.h3
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp14
-rw-r--r--src/yuzu/configuration/configure_per_game.h2
-rw-r--r--src/yuzu/main.cpp40
-rw-r--r--src/yuzu/uisettings.h2
57 files changed, 1118 insertions, 433 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eafb96b0b..7a4d9e354 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -154,6 +154,7 @@ add_library(common STATIC
154 param_package.cpp 154 param_package.cpp
155 param_package.h 155 param_package.h
156 parent_of_member.h 156 parent_of_member.h
157 point.h
157 quaternion.h 158 quaternion.h
158 ring_buffer.h 159 ring_buffer.h
159 scm_rev.cpp 160 scm_rev.cpp
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 209f9664b..50e270c5b 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -117,7 +117,7 @@ template <typename Path>
117} 117}
118#endif 118#endif
119 119
120class IOFile final : NonCopyable { 120class IOFile final {
121public: 121public:
122 IOFile(); 122 IOFile();
123 123
@@ -142,7 +142,10 @@ public:
142 FileType type = FileType::BinaryFile, 142 FileType type = FileType::BinaryFile,
143 FileShareFlag flag = FileShareFlag::ShareReadOnly); 143 FileShareFlag flag = FileShareFlag::ShareReadOnly);
144 144
145 virtual ~IOFile(); 145 ~IOFile();
146
147 IOFile(const IOFile&) = delete;
148 IOFile& operator=(const IOFile&) = delete;
146 149
147 IOFile(IOFile&& other) noexcept; 150 IOFile(IOFile&& other) noexcept;
148 IOFile& operator=(IOFile&& other) noexcept; 151 IOFile& operator=(IOFile&& other) noexcept;
@@ -441,8 +444,8 @@ public:
441 444
442private: 445private:
443 std::filesystem::path file_path; 446 std::filesystem::path file_path;
444 FileAccessMode file_access_mode; 447 FileAccessMode file_access_mode{};
445 FileType file_type; 448 FileType file_type{};
446 449
447 std::FILE* file = nullptr; 450 std::FILE* file = nullptr;
448}; 451};
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 8fd8620fd..9fffd816f 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -14,6 +14,7 @@ void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_b
14 const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; 14 const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
15 pointers.resize(num_page_table_entries); 15 pointers.resize(num_page_table_entries);
16 backing_addr.resize(num_page_table_entries); 16 backing_addr.resize(num_page_table_entries);
17 current_address_space_width_in_bits = address_space_width_in_bits;
17} 18}
18 19
19} // namespace Common 20} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 61c5552e0..e92b66b2b 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -98,6 +98,10 @@ struct PageTable {
98 */ 98 */
99 void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); 99 void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
100 100
101 size_t GetAddressSpaceBits() const {
102 return current_address_space_width_in_bits;
103 }
104
101 /** 105 /**
102 * Vector of memory pointers backing each page. An entry can only be non-null if the 106 * Vector of memory pointers backing each page. An entry can only be non-null if the
103 * corresponding attribute element is of type `Memory`. 107 * corresponding attribute element is of type `Memory`.
@@ -105,6 +109,8 @@ struct PageTable {
105 VirtualBuffer<PageInfo> pointers; 109 VirtualBuffer<PageInfo> pointers;
106 110
107 VirtualBuffer<u64> backing_addr; 111 VirtualBuffer<u64> backing_addr;
112
113 size_t current_address_space_width_in_bits;
108}; 114};
109 115
110} // namespace Common 116} // namespace Common
diff --git a/src/common/point.h b/src/common/point.h
new file mode 100644
index 000000000..c0a52ad8d
--- /dev/null
+++ b/src/common/point.h
@@ -0,0 +1,57 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8
9namespace Common {
10
11// Represents a point within a 2D space.
12template <typename T>
13struct Point {
14 static_assert(std::is_arithmetic_v<T>, "T must be an arithmetic type!");
15
16 T x{};
17 T y{};
18
19#define ARITHMETIC_OP(op, compound_op) \
20 friend constexpr Point operator op(const Point& lhs, const Point& rhs) noexcept { \
21 return { \
22 .x = static_cast<T>(lhs.x op rhs.x), \
23 .y = static_cast<T>(lhs.y op rhs.y), \
24 }; \
25 } \
26 friend constexpr Point operator op(const Point& lhs, T value) noexcept { \
27 return { \
28 .x = static_cast<T>(lhs.x op value), \
29 .y = static_cast<T>(lhs.y op value), \
30 }; \
31 } \
32 friend constexpr Point operator op(T value, const Point& rhs) noexcept { \
33 return { \
34 .x = static_cast<T>(value op rhs.x), \
35 .y = static_cast<T>(value op rhs.y), \
36 }; \
37 } \
38 friend constexpr Point& operator compound_op(Point& lhs, const Point& rhs) noexcept { \
39 lhs.x = static_cast<T>(lhs.x op rhs.x); \
40 lhs.y = static_cast<T>(lhs.y op rhs.y); \
41 return lhs; \
42 } \
43 friend constexpr Point& operator compound_op(Point& lhs, T value) noexcept { \
44 lhs.x = static_cast<T>(lhs.x op value); \
45 lhs.y = static_cast<T>(lhs.y op value); \
46 return lhs; \
47 }
48 ARITHMETIC_OP(+, +=)
49 ARITHMETIC_OP(-, -=)
50 ARITHMETIC_OP(*, *=)
51 ARITHMETIC_OP(/, /=)
52#undef ARITHMETIC_OP
53
54 friend constexpr bool operator==(const Point&, const Point&) = default;
55};
56
57} // namespace Common
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 9a0151736..689e3ceb5 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -65,9 +65,6 @@ public:
65 /// Step CPU by one instruction 65 /// Step CPU by one instruction
66 virtual void Step() = 0; 66 virtual void Step() = 0;
67 67
68 /// Exits execution from a callback, the callback must rewind the stack
69 virtual void ExceptionalExit() = 0;
70
71 /// Clear all instruction cache 68 /// Clear all instruction cache
72 virtual void ClearInstructionCache() = 0; 69 virtual void ClearInstructionCache() = 0;
73 70
@@ -159,8 +156,6 @@ public:
159 */ 156 */
160 virtual void SetTPIDR_EL0(u64 value) = 0; 157 virtual void SetTPIDR_EL0(u64 value) = 0;
161 158
162 virtual void ChangeProcessorID(std::size_t new_core_id) = 0;
163
164 virtual void SaveContext(ThreadContext32& ctx) = 0; 159 virtual void SaveContext(ThreadContext32& ctx) = 0;
165 virtual void SaveContext(ThreadContext64& ctx) = 0; 160 virtual void SaveContext(ThreadContext64& ctx) = 0;
166 virtual void LoadContext(const ThreadContext32& ctx) = 0; 161 virtual void LoadContext(const ThreadContext32& ctx) = 0;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 93d43e22e..cea7f0fb1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -4,9 +4,9 @@
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <memory> 6#include <memory>
7#include <dynarmic/A32/a32.h> 7#include <dynarmic/interface/A32/a32.h>
8#include <dynarmic/A32/config.h> 8#include <dynarmic/interface/A32/config.h>
9#include <dynarmic/A32/context.h> 9#include <dynarmic/interface/A32/context.h>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/page_table.h" 12#include "common/page_table.h"
@@ -24,45 +24,46 @@ namespace Core {
24 24
25class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 25class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
26public: 26public:
27 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) : parent{parent_} {} 27 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
28 : parent{parent_}, memory(parent.system.Memory()) {}
28 29
29 u8 MemoryRead8(u32 vaddr) override { 30 u8 MemoryRead8(u32 vaddr) override {
30 return parent.system.Memory().Read8(vaddr); 31 return memory.Read8(vaddr);
31 } 32 }
32 u16 MemoryRead16(u32 vaddr) override { 33 u16 MemoryRead16(u32 vaddr) override {
33 return parent.system.Memory().Read16(vaddr); 34 return memory.Read16(vaddr);
34 } 35 }
35 u32 MemoryRead32(u32 vaddr) override { 36 u32 MemoryRead32(u32 vaddr) override {
36 return parent.system.Memory().Read32(vaddr); 37 return memory.Read32(vaddr);
37 } 38 }
38 u64 MemoryRead64(u32 vaddr) override { 39 u64 MemoryRead64(u32 vaddr) override {
39 return parent.system.Memory().Read64(vaddr); 40 return memory.Read64(vaddr);
40 } 41 }
41 42
42 void MemoryWrite8(u32 vaddr, u8 value) override { 43 void MemoryWrite8(u32 vaddr, u8 value) override {
43 parent.system.Memory().Write8(vaddr, value); 44 memory.Write8(vaddr, value);
44 } 45 }
45 void MemoryWrite16(u32 vaddr, u16 value) override { 46 void MemoryWrite16(u32 vaddr, u16 value) override {
46 parent.system.Memory().Write16(vaddr, value); 47 memory.Write16(vaddr, value);
47 } 48 }
48 void MemoryWrite32(u32 vaddr, u32 value) override { 49 void MemoryWrite32(u32 vaddr, u32 value) override {
49 parent.system.Memory().Write32(vaddr, value); 50 memory.Write32(vaddr, value);
50 } 51 }
51 void MemoryWrite64(u32 vaddr, u64 value) override { 52 void MemoryWrite64(u32 vaddr, u64 value) override {
52 parent.system.Memory().Write64(vaddr, value); 53 memory.Write64(vaddr, value);
53 } 54 }
54 55
55 bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { 56 bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
56 return parent.system.Memory().WriteExclusive8(vaddr, value, expected); 57 return memory.WriteExclusive8(vaddr, value, expected);
57 } 58 }
58 bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { 59 bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
59 return parent.system.Memory().WriteExclusive16(vaddr, value, expected); 60 return memory.WriteExclusive16(vaddr, value, expected);
60 } 61 }
61 bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { 62 bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
62 return parent.system.Memory().WriteExclusive32(vaddr, value, expected); 63 return memory.WriteExclusive32(vaddr, value, expected);
63 } 64 }
64 bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { 65 bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
65 return parent.system.Memory().WriteExclusive64(vaddr, value, expected); 66 return memory.WriteExclusive64(vaddr, value, expected);
66 } 67 }
67 68
68 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 69 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
@@ -78,7 +79,9 @@ public:
78 } 79 }
79 80
80 void CallSVC(u32 swi) override { 81 void CallSVC(u32 swi) override {
81 Kernel::Svc::Call(parent.system, swi); 82 parent.svc_called = true;
83 parent.svc_swi = swi;
84 parent.jit->HaltExecution();
82 } 85 }
83 86
84 void AddTicks(u64 ticks) override { 87 void AddTicks(u64 ticks) override {
@@ -110,6 +113,7 @@ public:
110 } 113 }
111 114
112 ARM_Dynarmic_32& parent; 115 ARM_Dynarmic_32& parent;
116 Core::Memory::Memory& memory;
113 std::size_t num_interpreted_instructions{}; 117 std::size_t num_interpreted_instructions{};
114 static constexpr u64 minimum_run_cycles = 1000U; 118 static constexpr u64 minimum_run_cycles = 1000U;
115}; 119};
@@ -187,11 +191,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
187} 191}
188 192
189void ARM_Dynarmic_32::Run() { 193void ARM_Dynarmic_32::Run() {
190 jit->Run(); 194 while (true) {
191} 195 jit->Run();
192 196 if (!svc_called) {
193void ARM_Dynarmic_32::ExceptionalExit() { 197 break;
194 jit->ExceptionalExit(); 198 }
199 svc_called = false;
200 Kernel::Svc::Call(system, svc_swi);
201 if (shutdown) {
202 break;
203 }
204 }
195} 205}
196 206
197void ARM_Dynarmic_32::Step() { 207void ARM_Dynarmic_32::Step() {
@@ -255,10 +265,6 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
255 cp15->uprw = static_cast<u32>(value); 265 cp15->uprw = static_cast<u32>(value);
256} 266}
257 267
258void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
259 jit->ChangeProcessorID(new_core_id);
260}
261
262void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 268void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
263 Dynarmic::A32::Context context; 269 Dynarmic::A32::Context context;
264 jit->SaveContext(context); 270 jit->SaveContext(context);
@@ -279,6 +285,7 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
279 285
280void ARM_Dynarmic_32::PrepareReschedule() { 286void ARM_Dynarmic_32::PrepareReschedule() {
281 jit->HaltExecution(); 287 jit->HaltExecution();
288 shutdown = true;
282} 289}
283 290
284void ARM_Dynarmic_32::ClearInstructionCache() { 291void ARM_Dynarmic_32::ClearInstructionCache() {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 42778c02c..063605b46 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -7,9 +7,9 @@
7#include <memory> 7#include <memory>
8#include <unordered_map> 8#include <unordered_map>
9 9
10#include <dynarmic/A32/a32.h> 10#include <dynarmic/interface/A32/a32.h>
11#include <dynarmic/A64/a64.h> 11#include <dynarmic/interface/A64/a64.h>
12#include <dynarmic/exclusive_monitor.h> 12#include <dynarmic/interface/exclusive_monitor.h>
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hash.h" 14#include "common/hash.h"
15#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
@@ -42,13 +42,11 @@ public:
42 u32 GetPSTATE() const override; 42 u32 GetPSTATE() const override;
43 void SetPSTATE(u32 pstate) override; 43 void SetPSTATE(u32 pstate) override;
44 void Run() override; 44 void Run() override;
45 void ExceptionalExit() override;
46 void Step() override; 45 void Step() override;
47 VAddr GetTlsAddress() const override; 46 VAddr GetTlsAddress() const override;
48 void SetTlsAddress(VAddr address) override; 47 void SetTlsAddress(VAddr address) override;
49 void SetTPIDR_EL0(u64 value) override; 48 void SetTPIDR_EL0(u64 value) override;
50 u64 GetTPIDR_EL0() const override; 49 u64 GetTPIDR_EL0() const override;
51 void ChangeProcessorID(std::size_t new_core_id) override;
52 50
53 bool IsInThumbMode() const { 51 bool IsInThumbMode() const {
54 return (GetPSTATE() & 0x20) != 0; 52 return (GetPSTATE() & 0x20) != 0;
@@ -83,6 +81,12 @@ private:
83 std::size_t core_index; 81 std::size_t core_index;
84 DynarmicExclusiveMonitor& exclusive_monitor; 82 DynarmicExclusiveMonitor& exclusive_monitor;
85 std::shared_ptr<Dynarmic::A32::Jit> jit; 83 std::shared_ptr<Dynarmic::A32::Jit> jit;
84
85 // SVC callback
86 u32 svc_swi{};
87 bool svc_called{};
88
89 bool shutdown{};
86}; 90};
87 91
88} // namespace Core 92} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 08fa85904..63193dcb1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -4,8 +4,8 @@
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <memory> 6#include <memory>
7#include <dynarmic/A64/a64.h> 7#include <dynarmic/interface/A64/a64.h>
8#include <dynarmic/A64/config.h> 8#include <dynarmic/interface/A64/config.h>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
@@ -27,57 +27,56 @@ using Vector = Dynarmic::A64::Vector;
27 27
28class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 28class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
29public: 29public:
30 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) : parent{parent_} {} 30 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
31 : parent{parent_}, memory(parent.system.Memory()) {}
31 32
32 u8 MemoryRead8(u64 vaddr) override { 33 u8 MemoryRead8(u64 vaddr) override {
33 return parent.system.Memory().Read8(vaddr); 34 return memory.Read8(vaddr);
34 } 35 }
35 u16 MemoryRead16(u64 vaddr) override { 36 u16 MemoryRead16(u64 vaddr) override {
36 return parent.system.Memory().Read16(vaddr); 37 return memory.Read16(vaddr);
37 } 38 }
38 u32 MemoryRead32(u64 vaddr) override { 39 u32 MemoryRead32(u64 vaddr) override {
39 return parent.system.Memory().Read32(vaddr); 40 return memory.Read32(vaddr);
40 } 41 }
41 u64 MemoryRead64(u64 vaddr) override { 42 u64 MemoryRead64(u64 vaddr) override {
42 return parent.system.Memory().Read64(vaddr); 43 return memory.Read64(vaddr);
43 } 44 }
44 Vector MemoryRead128(u64 vaddr) override { 45 Vector MemoryRead128(u64 vaddr) override {
45 auto& memory = parent.system.Memory();
46 return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; 46 return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
47 } 47 }
48 48
49 void MemoryWrite8(u64 vaddr, u8 value) override { 49 void MemoryWrite8(u64 vaddr, u8 value) override {
50 parent.system.Memory().Write8(vaddr, value); 50 memory.Write8(vaddr, value);
51 } 51 }
52 void MemoryWrite16(u64 vaddr, u16 value) override { 52 void MemoryWrite16(u64 vaddr, u16 value) override {
53 parent.system.Memory().Write16(vaddr, value); 53 memory.Write16(vaddr, value);
54 } 54 }
55 void MemoryWrite32(u64 vaddr, u32 value) override { 55 void MemoryWrite32(u64 vaddr, u32 value) override {
56 parent.system.Memory().Write32(vaddr, value); 56 memory.Write32(vaddr, value);
57 } 57 }
58 void MemoryWrite64(u64 vaddr, u64 value) override { 58 void MemoryWrite64(u64 vaddr, u64 value) override {
59 parent.system.Memory().Write64(vaddr, value); 59 memory.Write64(vaddr, value);
60 } 60 }
61 void MemoryWrite128(u64 vaddr, Vector value) override { 61 void MemoryWrite128(u64 vaddr, Vector value) override {
62 auto& memory = parent.system.Memory();
63 memory.Write64(vaddr, value[0]); 62 memory.Write64(vaddr, value[0]);
64 memory.Write64(vaddr + 8, value[1]); 63 memory.Write64(vaddr + 8, value[1]);
65 } 64 }
66 65
67 bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { 66 bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
68 return parent.system.Memory().WriteExclusive8(vaddr, value, expected); 67 return memory.WriteExclusive8(vaddr, value, expected);
69 } 68 }
70 bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { 69 bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
71 return parent.system.Memory().WriteExclusive16(vaddr, value, expected); 70 return memory.WriteExclusive16(vaddr, value, expected);
72 } 71 }
73 bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { 72 bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
74 return parent.system.Memory().WriteExclusive32(vaddr, value, expected); 73 return memory.WriteExclusive32(vaddr, value, expected);
75 } 74 }
76 bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { 75 bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
77 return parent.system.Memory().WriteExclusive64(vaddr, value, expected); 76 return memory.WriteExclusive64(vaddr, value, expected);
78 } 77 }
79 bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { 78 bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
80 return parent.system.Memory().WriteExclusive128(vaddr, value, expected); 79 return memory.WriteExclusive128(vaddr, value, expected);
81 } 80 }
82 81
83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 82 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
@@ -102,7 +101,9 @@ public:
102 } 101 }
103 102
104 void CallSVC(u32 swi) override { 103 void CallSVC(u32 swi) override {
105 Kernel::Svc::Call(parent.system, swi); 104 parent.svc_called = true;
105 parent.svc_swi = swi;
106 parent.jit->HaltExecution();
106 } 107 }
107 108
108 void AddTicks(u64 ticks) override { 109 void AddTicks(u64 ticks) override {
@@ -137,6 +138,7 @@ public:
137 } 138 }
138 139
139 ARM_Dynarmic_64& parent; 140 ARM_Dynarmic_64& parent;
141 Core::Memory::Memory& memory;
140 u64 tpidrro_el0 = 0; 142 u64 tpidrro_el0 = 0;
141 u64 tpidr_el0 = 0; 143 u64 tpidr_el0 = 0;
142 static constexpr u64 minimum_run_cycles = 1000U; 144 static constexpr u64 minimum_run_cycles = 1000U;
@@ -227,11 +229,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
227} 229}
228 230
229void ARM_Dynarmic_64::Run() { 231void ARM_Dynarmic_64::Run() {
230 jit->Run(); 232 while (true) {
231} 233 jit->Run();
232 234 if (!svc_called) {
233void ARM_Dynarmic_64::ExceptionalExit() { 235 break;
234 jit->ExceptionalExit(); 236 }
237 svc_called = false;
238 Kernel::Svc::Call(system, svc_swi);
239 if (shutdown) {
240 break;
241 }
242 }
235} 243}
236 244
237void ARM_Dynarmic_64::Step() { 245void ARM_Dynarmic_64::Step() {
@@ -296,10 +304,6 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
296 cb->tpidr_el0 = value; 304 cb->tpidr_el0 = value;
297} 305}
298 306
299void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
300 jit->ChangeProcessorID(new_core_id);
301}
302
303void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 307void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
304 ctx.cpu_registers = jit->GetRegisters(); 308 ctx.cpu_registers = jit->GetRegisters();
305 ctx.sp = jit->GetSP(); 309 ctx.sp = jit->GetSP();
@@ -324,6 +328,7 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
324 328
325void ARM_Dynarmic_64::PrepareReschedule() { 329void ARM_Dynarmic_64::PrepareReschedule() {
326 jit->HaltExecution(); 330 jit->HaltExecution();
331 shutdown = true;
327} 332}
328 333
329void ARM_Dynarmic_64::ClearInstructionCache() { 334void ARM_Dynarmic_64::ClearInstructionCache() {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index b81fbcc66..0c4e46c64 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <unordered_map> 8#include <unordered_map>
9 9
10#include <dynarmic/A64/a64.h> 10#include <dynarmic/interface/A64/a64.h>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/hash.h" 12#include "common/hash.h"
13#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
@@ -40,12 +40,10 @@ public:
40 void SetPSTATE(u32 pstate) override; 40 void SetPSTATE(u32 pstate) override;
41 void Run() override; 41 void Run() override;
42 void Step() override; 42 void Step() override;
43 void ExceptionalExit() override;
44 VAddr GetTlsAddress() const override; 43 VAddr GetTlsAddress() const override;
45 void SetTlsAddress(VAddr address) override; 44 void SetTlsAddress(VAddr address) override;
46 void SetTPIDR_EL0(u64 value) override; 45 void SetTPIDR_EL0(u64 value) override;
47 u64 GetTPIDR_EL0() const override; 46 u64 GetTPIDR_EL0() const override;
48 void ChangeProcessorID(std::size_t new_core_id) override;
49 47
50 void SaveContext(ThreadContext32& ctx) override {} 48 void SaveContext(ThreadContext32& ctx) override {}
51 void SaveContext(ThreadContext64& ctx) override; 49 void SaveContext(ThreadContext64& ctx) override;
@@ -76,6 +74,12 @@ private:
76 DynarmicExclusiveMonitor& exclusive_monitor; 74 DynarmicExclusiveMonitor& exclusive_monitor;
77 75
78 std::shared_ptr<Dynarmic::A64::Jit> jit; 76 std::shared_ptr<Dynarmic::A64::Jit> jit;
77
78 // SVC callback
79 u32 svc_swi{};
80 bool svc_called{};
81
82 bool shutdown{};
79}; 83};
80 84
81} // namespace Core 85} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index 8597beddf..7c7ede79e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <optional> 8#include <optional>
9 9
10#include <dynarmic/A32/coprocessor.h> 10#include <dynarmic/interface/A32/coprocessor.h>
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace Core { 13namespace Core {
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
index f9f056a59..73d41f223 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <unordered_map> 8#include <unordered_map>
9 9
10#include <dynarmic/exclusive_monitor.h> 10#include <dynarmic/interface/exclusive_monitor.h>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/arm/dynarmic/arm_dynarmic_32.h" 13#include "core/arm/dynarmic/arm_dynarmic_32.h"
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index beb8a2a05..0be0027be 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -84,50 +84,43 @@ static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAu
84// Ensure that the token hierarchy reflects the class hierarchy. 84// Ensure that the token hierarchy reflects the class hierarchy.
85 85
86// Base classes. 86// Base classes.
87static_assert(!std::is_final<KSynchronizationObject>::value && 87static_assert(!std::is_final_v<KSynchronizationObject> &&
88 std::is_base_of<KAutoObject, KSynchronizationObject>::value); 88 std::is_base_of_v<KAutoObject, KSynchronizationObject>);
89static_assert(!std::is_final<KReadableEvent>::value && 89static_assert(!std::is_final_v<KReadableEvent> &&
90 std::is_base_of<KSynchronizationObject, KReadableEvent>::value); 90 std::is_base_of_v<KSynchronizationObject, KReadableEvent>);
91 91
92// Final classes 92// Final classes
93// static_assert(std::is_final<KInterruptEvent>::value && 93// static_assert(std::is_final_v<KInterruptEvent> &&
94// std::is_base_of<KReadableEvent, KInterruptEvent>::value); 94// std::is_base_of_v<KReadableEvent, KInterruptEvent>);
95// static_assert(std::is_final<KDebug>::value && 95// static_assert(std::is_final_v<KDebug> &&
96// std::is_base_of<KSynchronizationObject, KDebug>::value); 96// std::is_base_of_v<KSynchronizationObject, KDebug>);
97static_assert(std::is_final<KThread>::value && 97static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>);
98 std::is_base_of<KSynchronizationObject, KThread>::value); 98static_assert(std::is_final_v<KServerPort> &&
99static_assert(std::is_final<KServerPort>::value && 99 std::is_base_of_v<KSynchronizationObject, KServerPort>);
100 std::is_base_of<KSynchronizationObject, KServerPort>::value); 100static_assert(std::is_final_v<KServerSession> &&
101static_assert(std::is_final<KServerSession>::value && 101 std::is_base_of_v<KSynchronizationObject, KServerSession>);
102 std::is_base_of<KSynchronizationObject, KServerSession>::value); 102static_assert(std::is_final_v<KClientPort> &&
103static_assert(std::is_final<KClientPort>::value && 103 std::is_base_of_v<KSynchronizationObject, KClientPort>);
104 std::is_base_of<KSynchronizationObject, KClientPort>::value); 104static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>);
105static_assert(std::is_final<KClientSession>::value && 105static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>);
106 std::is_base_of<KAutoObject, KClientSession>::value); 106static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>);
107static_assert(std::is_final<KProcess>::value && 107// static_assert(std::is_final_v<KLightSession> &&
108 std::is_base_of<KSynchronizationObject, KProcess>::value); 108// std::is_base_of_v<KAutoObject, KLightSession>);
109static_assert(std::is_final<KResourceLimit>::value && 109static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
110 std::is_base_of<KAutoObject, KResourceLimit>::value); 110static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
111// static_assert(std::is_final<KLightSession>::value && 111static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
112// std::is_base_of<KAutoObject, KLightSession>::value); 112static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
113static_assert(std::is_final<KPort>::value && std::is_base_of<KAutoObject, KPort>::value); 113static_assert(std::is_final_v<KWritableEvent> && std::is_base_of_v<KAutoObject, KWritableEvent>);
114static_assert(std::is_final<KSession>::value && std::is_base_of<KAutoObject, KSession>::value); 114// static_assert(std::is_final_v<KLightClientSession> &&
115static_assert(std::is_final<KSharedMemory>::value && 115// std::is_base_of_v<KAutoObject, KLightClientSession>);
116 std::is_base_of<KAutoObject, KSharedMemory>::value); 116// static_assert(std::is_final_v<KLightServerSession> &&
117static_assert(std::is_final<KEvent>::value && std::is_base_of<KAutoObject, KEvent>::value); 117// std::is_base_of_v<KAutoObject, KLightServerSession>);
118static_assert(std::is_final<KWritableEvent>::value && 118static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>);
119 std::is_base_of<KAutoObject, KWritableEvent>::value); 119// static_assert(std::is_final_v<KDeviceAddressSpace> &&
120// static_assert(std::is_final<KLightClientSession>::value && 120// std::is_base_of_v<KAutoObject, KDeviceAddressSpace>);
121// std::is_base_of<KAutoObject, KLightClientSession>::value); 121// static_assert(std::is_final_v<KSessionRequest> &&
122// static_assert(std::is_final<KLightServerSession>::value && 122// std::is_base_of_v<KAutoObject, KSessionRequest>);
123// std::is_base_of<KAutoObject, KLightServerSession>::value); 123// static_assert(std::is_final_v<KCodeMemory> &&
124static_assert(std::is_final<KTransferMemory>::value && 124// std::is_base_of_v<KAutoObject, KCodeMemory>);
125 std::is_base_of<KAutoObject, KTransferMemory>::value);
126// static_assert(std::is_final<KDeviceAddressSpace>::value &&
127// std::is_base_of<KAutoObject, KDeviceAddressSpace>::value);
128// static_assert(std::is_final<KSessionRequest>::value &&
129// std::is_base_of<KAutoObject, KSessionRequest>::value);
130// static_assert(std::is_final<KCodeMemory>::value &&
131// std::is_base_of<KAutoObject, KCodeMemory>::value);
132 125
133} // namespace Kernel 126} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index 8501156e8..f2fff3b01 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -22,7 +22,7 @@ class KClientPort final : public KSynchronizationObject {
22 22
23public: 23public:
24 explicit KClientPort(KernelCore& kernel_); 24 explicit KClientPort(KernelCore& kernel_);
25 virtual ~KClientPort() override; 25 ~KClientPort() override;
26 26
27 void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_); 27 void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_);
28 void OnSessionFinalized(); 28 void OnSessionFinalized();
@@ -49,8 +49,8 @@ public:
49 bool IsServerClosed() const; 49 bool IsServerClosed() const;
50 50
51 // Overridden virtual functions. 51 // Overridden virtual functions.
52 virtual void Destroy() override; 52 void Destroy() override;
53 virtual bool IsSignaled() const override; 53 bool IsSignaled() const override;
54 54
55 ResultCode CreateSession(KClientSession** out); 55 ResultCode CreateSession(KClientSession** out);
56 56
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 720a8c243..b11d5b4e3 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -34,7 +34,7 @@ class KClientSession final
34 34
35public: 35public:
36 explicit KClientSession(KernelCore& kernel_); 36 explicit KClientSession(KernelCore& kernel_);
37 virtual ~KClientSession(); 37 ~KClientSession() override;
38 38
39 void Initialize(KSession* parent_, std::string&& name_) { 39 void Initialize(KSession* parent_, std::string&& name_) {
40 // Set member variables. 40 // Set member variables.
@@ -42,7 +42,7 @@ public:
42 name = std::move(name_); 42 name = std::move(name_);
43 } 43 }
44 44
45 virtual void Destroy() override; 45 void Destroy() override;
46 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 46 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
47 47
48 KSession* GetParent() const { 48 KSession* GetParent() const {
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
index 9a59ffb70..3d3ec99e2 100644
--- a/src/core/hle/kernel/k_event.h
+++ b/src/core/hle/kernel/k_event.h
@@ -20,23 +20,21 @@ class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObj
20 20
21public: 21public:
22 explicit KEvent(KernelCore& kernel_); 22 explicit KEvent(KernelCore& kernel_);
23 virtual ~KEvent(); 23 ~KEvent() override;
24 24
25 void Initialize(std::string&& name); 25 void Initialize(std::string&& name);
26 26
27 virtual void Finalize() override; 27 void Finalize() override;
28 28
29 virtual bool IsInitialized() const override { 29 bool IsInitialized() const override {
30 return initialized; 30 return initialized;
31 } 31 }
32 32
33 virtual uintptr_t GetPostDestroyArgument() const override { 33 uintptr_t GetPostDestroyArgument() const override {
34 return reinterpret_cast<uintptr_t>(owner); 34 return reinterpret_cast<uintptr_t>(owner);
35 } 35 }
36 36
37 static void PostDestroy(uintptr_t arg); 37 KProcess* GetOwner() const override {
38
39 virtual KProcess* GetOwner() const override {
40 return owner; 38 return owner;
41 } 39 }
42 40
@@ -48,6 +46,8 @@ public:
48 return writable_event; 46 return writable_event;
49 } 47 }
50 48
49 static void PostDestroy(uintptr_t arg);
50
51private: 51private:
52 KReadableEvent readable_event; 52 KReadableEvent readable_event;
53 KWritableEvent writable_event; 53 KWritableEvent writable_event;
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
index 960f1f3a3..4018ea2df 100644
--- a/src/core/hle/kernel/k_port.h
+++ b/src/core/hle/kernel/k_port.h
@@ -22,7 +22,7 @@ class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjec
22 22
23public: 23public:
24 explicit KPort(KernelCore& kernel_); 24 explicit KPort(KernelCore& kernel_);
25 virtual ~KPort(); 25 ~KPort() override;
26 26
27 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 27 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
28 28
@@ -59,7 +59,6 @@ private:
59 ServerClosed = 3, 59 ServerClosed = 3,
60 }; 60 };
61 61
62private:
63 KServerPort server; 62 KServerPort server;
64 KClientPort client; 63 KClientPort client;
65 State state{State::Invalid}; 64 State state{State::Invalid};
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 123d71cd3..c0656b9af 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -331,19 +331,19 @@ public:
331 331
332 void LoadModule(CodeSet code_set, VAddr base_addr); 332 void LoadModule(CodeSet code_set, VAddr base_addr);
333 333
334 virtual bool IsInitialized() const override { 334 bool IsInitialized() const override {
335 return is_initialized; 335 return is_initialized;
336 } 336 }
337 337
338 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 338 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
339 339
340 virtual void Finalize(); 340 void Finalize() override;
341 341
342 virtual u64 GetId() const override final { 342 u64 GetId() const override {
343 return GetProcessID(); 343 return GetProcessID();
344 } 344 }
345 345
346 virtual bool IsSignaled() const override; 346 bool IsSignaled() const override;
347 347
348 void PinCurrentThread(); 348 void PinCurrentThread();
349 void UnpinCurrentThread(); 349 void UnpinCurrentThread();
diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h
index 33cd1dd3e..b2850ac7b 100644
--- a/src/core/hle/kernel/k_readable_event.h
+++ b/src/core/hle/kernel/k_readable_event.h
@@ -31,8 +31,8 @@ public:
31 return parent; 31 return parent;
32 } 32 }
33 33
34 virtual bool IsSignaled() const override; 34 bool IsSignaled() const override;
35 virtual void Destroy() override; 35 void Destroy() override;
36 36
37 ResultCode Signal(); 37 ResultCode Signal();
38 ResultCode Clear(); 38 ResultCode Clear();
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index 0debbbb51..fab6005ff 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -37,10 +37,10 @@ class KResourceLimit final
37 37
38public: 38public:
39 explicit KResourceLimit(KernelCore& kernel_); 39 explicit KResourceLimit(KernelCore& kernel_);
40 virtual ~KResourceLimit(); 40 ~KResourceLimit() override;
41 41
42 void Initialize(const Core::Timing::CoreTiming* core_timing_); 42 void Initialize(const Core::Timing::CoreTiming* core_timing_);
43 virtual void Finalize() override; 43 void Finalize() override;
44 44
45 s64 GetLimitValue(LimitableResource which) const; 45 s64 GetLimitValue(LimitableResource which) const;
46 s64 GetCurrentValue(LimitableResource which) const; 46 s64 GetCurrentValue(LimitableResource which) const;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 2f82fbcd6..6a7d80d03 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -659,7 +659,6 @@ void KScheduler::Unload(KThread* thread) {
659 659
660 if (thread) { 660 if (thread) {
661 if (thread->IsCallingSvc()) { 661 if (thread->IsCallingSvc()) {
662 system.ArmInterface(core_id).ExceptionalExit();
663 thread->ClearIsCallingSvc(); 662 thread->ClearIsCallingSvc();
664 } 663 }
665 if (!thread->IsTerminationRequested()) { 664 if (!thread->IsTerminationRequested()) {
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index d1a757ec3..55481d63f 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -25,12 +25,9 @@ class SessionRequestHandler;
25class KServerPort final : public KSynchronizationObject { 25class KServerPort final : public KSynchronizationObject {
26 KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject); 26 KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
27 27
28private:
29 using SessionList = boost::intrusive::list<KServerSession>;
30
31public: 28public:
32 explicit KServerPort(KernelCore& kernel_); 29 explicit KServerPort(KernelCore& kernel_);
33 virtual ~KServerPort() override; 30 ~KServerPort() override;
34 31
35 void Initialize(KPort* parent_, std::string&& name_); 32 void Initialize(KPort* parent_, std::string&& name_);
36 33
@@ -63,13 +60,14 @@ public:
63 bool IsLight() const; 60 bool IsLight() const;
64 61
65 // Overridden virtual functions. 62 // Overridden virtual functions.
66 virtual void Destroy() override; 63 void Destroy() override;
67 virtual bool IsSignaled() const override; 64 bool IsSignaled() const override;
68 65
69private: 66private:
67 using SessionList = boost::intrusive::list<KServerSession>;
68
70 void CleanupSessions(); 69 void CleanupSessions();
71 70
72private:
73 SessionList session_list; 71 SessionList session_list;
74 SessionRequestHandlerPtr session_handler; 72 SessionRequestHandlerPtr session_handler;
75 KPort* parent{}; 73 KPort* parent{};
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index dd4de2904..27b757ad2 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -42,9 +42,9 @@ class KServerSession final : public KSynchronizationObject,
42 42
43public: 43public:
44 explicit KServerSession(KernelCore& kernel_); 44 explicit KServerSession(KernelCore& kernel_);
45 virtual ~KServerSession() override; 45 ~KServerSession() override;
46 46
47 virtual void Destroy() override; 47 void Destroy() override;
48 48
49 void Initialize(KSession* parent_, std::string&& name_); 49 void Initialize(KSession* parent_, std::string&& name_);
50 50
@@ -56,7 +56,7 @@ public:
56 return parent; 56 return parent;
57 } 57 }
58 58
59 virtual bool IsSignaled() const override; 59 bool IsSignaled() const override;
60 60
61 void OnClientClosed(); 61 void OnClientClosed();
62 62
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index a981fd1f6..4ddd080d2 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -18,17 +18,17 @@ class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAut
18 18
19public: 19public:
20 explicit KSession(KernelCore& kernel_); 20 explicit KSession(KernelCore& kernel_);
21 virtual ~KSession() override; 21 ~KSession() override;
22 22
23 void Initialize(KClientPort* port_, const std::string& name_); 23 void Initialize(KClientPort* port_, const std::string& name_);
24 24
25 virtual void Finalize() override; 25 void Finalize() override;
26 26
27 virtual bool IsInitialized() const override { 27 bool IsInitialized() const override {
28 return initialized; 28 return initialized;
29 } 29 }
30 30
31 virtual uintptr_t GetPostDestroyArgument() const override { 31 uintptr_t GetPostDestroyArgument() const override {
32 return reinterpret_cast<uintptr_t>(process); 32 return reinterpret_cast<uintptr_t>(process);
33 } 33 }
34 34
@@ -78,7 +78,6 @@ private:
78 ServerClosed = 3, 78 ServerClosed = 3,
79 }; 79 };
80 80
81private:
82 void SetState(State state) { 81 void SetState(State state) {
83 atomic_state = static_cast<u8>(state); 82 atomic_state = static_cast<u8>(state);
84 } 83 }
@@ -87,7 +86,6 @@ private:
87 return static_cast<State>(atomic_state.load(std::memory_order_relaxed)); 86 return static_cast<State>(atomic_state.load(std::memory_order_relaxed));
88 } 87 }
89 88
90private:
91 KServerSession server; 89 KServerSession server;
92 KClientSession client; 90 KClientSession client;
93 std::atomic<std::underlying_type_t<State>> atomic_state{ 91 std::atomic<std::underlying_type_t<State>> atomic_state{
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 553a56327..e9815f90b 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -68,9 +68,9 @@ public:
68 return device_memory->GetPointer(physical_address + offset); 68 return device_memory->GetPointer(physical_address + offset);
69 } 69 }
70 70
71 virtual void Finalize() override; 71 void Finalize() override;
72 72
73 virtual bool IsInitialized() const override { 73 bool IsInitialized() const override {
74 return is_initialized; 74 return is_initialized;
75 } 75 }
76 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 76 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index 81d472a3e..0ad74b0a0 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -4,34 +4,213 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
8
9#include "common/assert.h"
10#include "common/common_types.h"
11
7namespace Kernel { 12namespace Kernel {
8 13
9class KernelCore; 14class KernelCore;
10 15
11/// This is a placeholder class to manage slab heaps for kernel objects. For now, we just allocate 16namespace impl {
12/// these with new/delete, but this can be re-implemented later to allocate these in emulated 17
13/// memory. 18class KSlabHeapImpl final : NonCopyable {
19public:
20 struct Node {
21 Node* next{};
22 };
23
24 constexpr KSlabHeapImpl() = default;
25
26 void Initialize(std::size_t size) {
27 ASSERT(head == nullptr);
28 obj_size = size;
29 }
30
31 constexpr std::size_t GetObjectSize() const {
32 return obj_size;
33 }
34
35 Node* GetHead() const {
36 return head;
37 }
38
39 void* Allocate() {
40 Node* ret = head.load();
41
42 do {
43 if (ret == nullptr) {
44 break;
45 }
46 } while (!head.compare_exchange_weak(ret, ret->next));
47
48 return ret;
49 }
50
51 void Free(void* obj) {
52 Node* node = static_cast<Node*>(obj);
53
54 Node* cur_head = head.load();
55 do {
56 node->next = cur_head;
57 } while (!head.compare_exchange_weak(cur_head, node));
58 }
59
60private:
61 std::atomic<Node*> head{};
62 std::size_t obj_size{};
63};
64
65} // namespace impl
66
67class KSlabHeapBase : NonCopyable {
68public:
69 constexpr KSlabHeapBase() = default;
70
71 constexpr bool Contains(uintptr_t addr) const {
72 return start <= addr && addr < end;
73 }
74
75 constexpr std::size_t GetSlabHeapSize() const {
76 return (end - start) / GetObjectSize();
77 }
78
79 constexpr std::size_t GetObjectSize() const {
80 return impl.GetObjectSize();
81 }
82
83 constexpr uintptr_t GetSlabHeapAddress() const {
84 return start;
85 }
86
87 std::size_t GetObjectIndexImpl(const void* obj) const {
88 return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize();
89 }
90
91 std::size_t GetPeakIndex() const {
92 return GetObjectIndexImpl(reinterpret_cast<const void*>(peak));
93 }
94
95 void* AllocateImpl() {
96 return impl.Allocate();
97 }
98
99 void FreeImpl(void* obj) {
100 // Don't allow freeing an object that wasn't allocated from this heap
101 ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
102
103 impl.Free(obj);
104 }
105
106 void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) {
107 // Ensure we don't initialize a slab using null memory
108 ASSERT(memory != nullptr);
109
110 // Initialize the base allocator
111 impl.Initialize(obj_size);
112
113 // Set our tracking variables
114 const std::size_t num_obj = (memory_size / obj_size);
115 start = reinterpret_cast<uintptr_t>(memory);
116 end = start + num_obj * obj_size;
117 peak = start;
118
119 // Free the objects
120 u8* cur = reinterpret_cast<u8*>(end);
121
122 for (std::size_t i{}; i < num_obj; i++) {
123 cur -= obj_size;
124 impl.Free(cur);
125 }
126 }
127
128private:
129 using Impl = impl::KSlabHeapImpl;
130
131 Impl impl;
132 uintptr_t peak{};
133 uintptr_t start{};
134 uintptr_t end{};
135};
14 136
15template <typename T> 137template <typename T>
16class KSlabHeap final : NonCopyable { 138class KSlabHeap final : public KSlabHeapBase {
17public: 139public:
18 KSlabHeap() = default; 140 enum class AllocationType {
141 Host,
142 Guest,
143 };
19 144
20 void Initialize([[maybe_unused]] void* memory, [[maybe_unused]] std::size_t memory_size) { 145 explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host)
21 // Placeholder that should initialize the backing slab heap implementation. 146 : KSlabHeapBase(), allocation_type{allocation_type_} {}
147
148 void Initialize(void* memory, std::size_t memory_size) {
149 if (allocation_type == AllocationType::Guest) {
150 InitializeImpl(sizeof(T), memory, memory_size);
151 }
22 } 152 }
23 153
24 T* Allocate() { 154 T* Allocate() {
25 return new T(); 155 switch (allocation_type) {
156 case AllocationType::Host:
157 // Fallback for cases where we do not yet support allocating guest memory from the slab
158 // heap, such as for kernel memory regions.
159 return new T;
160
161 case AllocationType::Guest:
162 T* obj = static_cast<T*>(AllocateImpl());
163 if (obj != nullptr) {
164 new (obj) T();
165 }
166 return obj;
167 }
168
169 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
170 return nullptr;
26 } 171 }
27 172
28 T* AllocateWithKernel(KernelCore& kernel) { 173 T* AllocateWithKernel(KernelCore& kernel) {
29 return new T(kernel); 174 switch (allocation_type) {
175 case AllocationType::Host:
176 // Fallback for cases where we do not yet support allocating guest memory from the slab
177 // heap, such as for kernel memory regions.
178 return new T(kernel);
179
180 case AllocationType::Guest:
181 T* obj = static_cast<T*>(AllocateImpl());
182 if (obj != nullptr) {
183 new (obj) T(kernel);
184 }
185 return obj;
186 }
187
188 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
189 return nullptr;
30 } 190 }
31 191
32 void Free(T* obj) { 192 void Free(T* obj) {
33 delete obj; 193 switch (allocation_type) {
194 case AllocationType::Host:
195 // Fallback for cases where we do not yet support allocating guest memory from the slab
196 // heap, such as for kernel memory regions.
197 delete obj;
198 return;
199
200 case AllocationType::Guest:
201 FreeImpl(obj);
202 return;
203 }
204
205 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
34 } 206 }
207
208 constexpr std::size_t GetObjectIndex(const T* obj) const {
209 return GetObjectIndexImpl(obj);
210 }
211
212private:
213 const AllocationType allocation_type;
35}; 214};
36 215
37} // namespace Kernel 216} // namespace Kernel
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index a41dd1220..3d4ce1fbc 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -29,7 +29,7 @@ public:
29 KSynchronizationObject** objects, const s32 num_objects, 29 KSynchronizationObject** objects, const s32 num_objects,
30 s64 timeout); 30 s64 timeout);
31 31
32 virtual void Finalize() override; 32 void Finalize() override;
33 33
34 [[nodiscard]] virtual bool IsSignaled() const = 0; 34 [[nodiscard]] virtual bool IsSignaled() const = 0;
35 35
@@ -37,7 +37,7 @@ public:
37 37
38protected: 38protected:
39 explicit KSynchronizationObject(KernelCore& kernel); 39 explicit KSynchronizationObject(KernelCore& kernel);
40 virtual ~KSynchronizationObject(); 40 ~KSynchronizationObject() override;
41 41
42 virtual void OnFinalizeSynchronizationObject() {} 42 virtual void OnFinalizeSynchronizationObject() {}
43 43
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index e3f08f256..3cf43d290 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -168,13 +168,13 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
168 std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, 168 std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
169 sizeof(StackParameters)); 169 sizeof(StackParameters));
170 170
171 // Setup the TLS, if needed.
172 if (type == ThreadType::User) {
173 tls_address = owner->CreateTLSRegion();
174 }
175
176 // Set parent, if relevant. 171 // Set parent, if relevant.
177 if (owner != nullptr) { 172 if (owner != nullptr) {
173 // Setup the TLS, if needed.
174 if (type == ThreadType::User) {
175 tls_address = owner->CreateTLSRegion();
176 }
177
178 parent = owner; 178 parent = owner;
179 parent->Open(); 179 parent->Open();
180 parent->IncrementThreadCount(); 180 parent->IncrementThreadCount();
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 4abfc2b49..01eebb165 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -358,21 +358,21 @@ public:
358 return termination_requested || GetRawState() == ThreadState::Terminated; 358 return termination_requested || GetRawState() == ThreadState::Terminated;
359 } 359 }
360 360
361 [[nodiscard]] virtual u64 GetId() const override final { 361 [[nodiscard]] u64 GetId() const override {
362 return this->GetThreadID(); 362 return this->GetThreadID();
363 } 363 }
364 364
365 [[nodiscard]] virtual bool IsInitialized() const override { 365 [[nodiscard]] bool IsInitialized() const override {
366 return initialized; 366 return initialized;
367 } 367 }
368 368
369 [[nodiscard]] virtual uintptr_t GetPostDestroyArgument() const override { 369 [[nodiscard]] uintptr_t GetPostDestroyArgument() const override {
370 return reinterpret_cast<uintptr_t>(parent) | (resource_limit_release_hint ? 1 : 0); 370 return reinterpret_cast<uintptr_t>(parent) | (resource_limit_release_hint ? 1 : 0);
371 } 371 }
372 372
373 virtual void Finalize() override; 373 void Finalize() override;
374 374
375 [[nodiscard]] virtual bool IsSignaled() const override; 375 [[nodiscard]] bool IsSignaled() const override;
376 376
377 static void PostDestroy(uintptr_t arg); 377 static void PostDestroy(uintptr_t arg);
378 378
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index c2d0f1eaf..31029a5c2 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -27,23 +27,23 @@ class KTransferMemory final
27 27
28public: 28public:
29 explicit KTransferMemory(KernelCore& kernel_); 29 explicit KTransferMemory(KernelCore& kernel_);
30 virtual ~KTransferMemory() override; 30 ~KTransferMemory() override;
31 31
32 ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_); 32 ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_);
33 33
34 virtual void Finalize() override; 34 void Finalize() override;
35 35
36 virtual bool IsInitialized() const override { 36 bool IsInitialized() const override {
37 return is_initialized; 37 return is_initialized;
38 } 38 }
39 39
40 virtual uintptr_t GetPostDestroyArgument() const override { 40 uintptr_t GetPostDestroyArgument() const override {
41 return reinterpret_cast<uintptr_t>(owner); 41 return reinterpret_cast<uintptr_t>(owner);
42 } 42 }
43 43
44 static void PostDestroy(uintptr_t arg); 44 static void PostDestroy(uintptr_t arg);
45 45
46 KProcess* GetOwner() const { 46 KProcess* GetOwner() const override {
47 return owner; 47 return owner;
48 } 48 }
49 49
diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h
index 607b0eadb..858d982c4 100644
--- a/src/core/hle/kernel/k_writable_event.h
+++ b/src/core/hle/kernel/k_writable_event.h
@@ -21,7 +21,7 @@ public:
21 explicit KWritableEvent(KernelCore& kernel_); 21 explicit KWritableEvent(KernelCore& kernel_);
22 ~KWritableEvent() override; 22 ~KWritableEvent() override;
23 23
24 virtual void Destroy() override; 24 void Destroy() override;
25 25
26 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 26 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
27 27
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 8b55df82e..0ffb78d51 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -258,7 +258,7 @@ struct KernelCore::Impl {
258 KAutoObject::Create(thread.get()); 258 KAutoObject::Create(thread.get());
259 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); 259 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
260 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 260 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
261 return std::move(thread); 261 return thread;
262 }; 262 };
263 263
264 thread_local auto thread = make_thread(); 264 thread_local auto thread = make_thread();
@@ -620,7 +620,8 @@ struct KernelCore::Impl {
620 620
621 void InitializePageSlab() { 621 void InitializePageSlab() {
622 // Allocate slab heaps 622 // Allocate slab heaps
623 user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>(); 623 user_slab_heap_pages =
624 std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
624 625
625 // TODO(ameerj): This should be derived, not hardcoded within the kernel 626 // TODO(ameerj): This should be derived, not hardcoded within the kernel
626 constexpr u64 user_slab_heap_size{0x3de000}; 627 constexpr u64 user_slab_heap_size{0x3de000};
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index b05a5da04..b1a81810b 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -273,8 +273,13 @@ void SoftwareKeyboard::ProcessTextCheck() {
273 273
274 std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); 274 std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck));
275 275
276 std::u16string text_check_message = Common::UTF16StringFromFixedZeroTerminatedBuffer( 276 std::u16string text_check_message =
277 swkbd_text_check.text_check_message.data(), swkbd_text_check.text_check_message.size()); 277 swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure ||
278 swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm
279 ? Common::UTF16StringFromFixedZeroTerminatedBuffer(
280 swkbd_text_check.text_check_message.data(),
281 swkbd_text_check.text_check_message.size())
282 : u"";
278 283
279 LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", 284 LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}",
280 GetTextCheckResultName(swkbd_text_check.text_check_result), 285 GetTextCheckResultName(swkbd_text_check.text_check_result),
@@ -285,10 +290,10 @@ void SoftwareKeyboard::ProcessTextCheck() {
285 SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); 290 SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text);
286 break; 291 break;
287 case SwkbdTextCheckResult::Failure: 292 case SwkbdTextCheckResult::Failure:
288 ShowTextCheckDialog(SwkbdTextCheckResult::Failure, text_check_message); 293 ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message));
289 break; 294 break;
290 case SwkbdTextCheckResult::Confirm: 295 case SwkbdTextCheckResult::Confirm:
291 ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, text_check_message); 296 ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message));
292 break; 297 break;
293 case SwkbdTextCheckResult::Silent: 298 case SwkbdTextCheckResult::Silent:
294 default: 299 default:
@@ -482,7 +487,7 @@ void SoftwareKeyboard::InitializeFrontendKeyboard() {
482 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 487 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
483 488
484 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 489 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
485 .ok_text{ok_text}, 490 .ok_text{std::move(ok_text)},
486 .header_text{}, 491 .header_text{},
487 .sub_text{}, 492 .sub_text{},
488 .guide_text{}, 493 .guide_text{},
@@ -558,10 +563,10 @@ void SoftwareKeyboard::InitializeFrontendKeyboard() {
558 : false; 563 : false;
559 564
560 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 565 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
561 .ok_text{ok_text}, 566 .ok_text{std::move(ok_text)},
562 .header_text{header_text}, 567 .header_text{std::move(header_text)},
563 .sub_text{sub_text}, 568 .sub_text{std::move(sub_text)},
564 .guide_text{guide_text}, 569 .guide_text{std::move(guide_text)},
565 .initial_text{initial_text}, 570 .initial_text{initial_text},
566 .max_text_length{max_text_length}, 571 .max_text_length{max_text_length},
567 .min_text_length{min_text_length}, 572 .min_text_length{min_text_length},
@@ -590,7 +595,7 @@ void SoftwareKeyboard::ShowNormalKeyboard() {
590 595
591void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, 596void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result,
592 std::u16string text_check_message) { 597 std::u16string text_check_message) {
593 frontend.ShowTextCheckDialog(text_check_result, text_check_message); 598 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
594} 599}
595 600
596void SoftwareKeyboard::ShowInlineKeyboard() { 601void SoftwareKeyboard::ShowInlineKeyboard() {
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index d311f754b..764abb5b6 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -91,8 +91,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
91 91
92 // Update if coordinates change 92 // Update if coordinates change
93 for (size_t id = 0; id < MAX_POINTS; id++) { 93 for (size_t id = 0; id < MAX_POINTS; id++) {
94 if (gesture.points[id].x != last_gesture.points[id].x || 94 if (gesture.points[id] != last_gesture.points[id]) {
95 gesture.points[id].y != last_gesture.points[id].y) {
96 return true; 95 return true;
97 } 96 }
98 } 97 }
@@ -124,8 +123,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
124 cur_entry.sampling_number2 = cur_entry.sampling_number; 123 cur_entry.sampling_number2 = cur_entry.sampling_number;
125 124
126 // Reset values to default 125 // Reset values to default
127 cur_entry.delta_x = 0; 126 cur_entry.delta = {};
128 cur_entry.delta_y = 0;
129 cur_entry.vel_x = 0; 127 cur_entry.vel_x = 0;
130 cur_entry.vel_y = 0; 128 cur_entry.vel_y = 0;
131 cur_entry.direction = Direction::None; 129 cur_entry.direction = Direction::None;
@@ -146,13 +144,9 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
146 cur_entry.detection_count = gesture.detection_count; 144 cur_entry.detection_count = gesture.detection_count;
147 cur_entry.type = type; 145 cur_entry.type = type;
148 cur_entry.attributes = attributes; 146 cur_entry.attributes = attributes;
149 cur_entry.x = gesture.mid_point.x; 147 cur_entry.pos = gesture.mid_point;
150 cur_entry.y = gesture.mid_point.y;
151 cur_entry.point_count = static_cast<s32>(gesture.active_points); 148 cur_entry.point_count = static_cast<s32>(gesture.active_points);
152 for (size_t id = 0; id < MAX_POINTS; id++) { 149 cur_entry.points = gesture.points;
153 cur_entry.points[id].x = gesture.points[id].x;
154 cur_entry.points[id].y = gesture.points[id].y;
155 }
156 last_gesture = gesture; 150 last_gesture = gesture;
157 151
158 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 152 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
@@ -160,8 +154,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
160 154
161void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, 155void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
162 Attribute& attributes) { 156 Attribute& attributes) {
163 const auto& last_entry = 157 const auto& last_entry = GetLastGestureEntry();
164 shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; 158
165 gesture.detection_count++; 159 gesture.detection_count++;
166 type = TouchType::Touch; 160 type = TouchType::Touch;
167 161
@@ -174,13 +168,11 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
174 168
175void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, 169void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type,
176 f32 time_difference) { 170 f32 time_difference) {
177 const auto& last_entry = 171 const auto& last_entry = GetLastGestureEntry();
178 shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
179 172
180 // Promote to pan type if touch moved 173 // Promote to pan type if touch moved
181 for (size_t id = 0; id < MAX_POINTS; id++) { 174 for (size_t id = 0; id < MAX_POINTS; id++) {
182 if (gesture.points[id].x != last_gesture.points[id].x || 175 if (gesture.points[id] != last_gesture.points[id]) {
183 gesture.points[id].y != last_gesture.points[id].y) {
184 type = TouchType::Pan; 176 type = TouchType::Pan;
185 break; 177 break;
186 } 178 }
@@ -192,10 +184,7 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
192 enable_press_and_tap = false; 184 enable_press_and_tap = false;
193 gesture.active_points = 0; 185 gesture.active_points = 0;
194 gesture.mid_point = {}; 186 gesture.mid_point = {};
195 for (size_t id = 0; id < MAX_POINTS; id++) { 187 gesture.points.fill({});
196 gesture.points[id].x = 0;
197 gesture.points[id].y = 0;
198 }
199 return; 188 return;
200 } 189 }
201 190
@@ -214,8 +203,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
214void Controller_Gesture::EndGesture(GestureProperties& gesture, 203void Controller_Gesture::EndGesture(GestureProperties& gesture,
215 GestureProperties& last_gesture_props, TouchType& type, 204 GestureProperties& last_gesture_props, TouchType& type,
216 Attribute& attributes, f32 time_difference) { 205 Attribute& attributes, f32 time_difference) {
217 const auto& last_entry = 206 const auto& last_entry = GetLastGestureEntry();
218 shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; 207
219 if (last_gesture_props.active_points != 0) { 208 if (last_gesture_props.active_points != 0) {
220 switch (last_entry.type) { 209 switch (last_entry.type) {
221 case TouchType::Touch: 210 case TouchType::Touch:
@@ -265,13 +254,11 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
265 GestureProperties& last_gesture_props, TouchType& type, 254 GestureProperties& last_gesture_props, TouchType& type,
266 f32 time_difference) { 255 f32 time_difference) {
267 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 256 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
268 const auto& last_entry = 257 const auto& last_entry = GetLastGestureEntry();
269 shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
270 cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
271 cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
272 258
273 cur_entry.vel_x = static_cast<f32>(cur_entry.delta_x) / time_difference; 259 cur_entry.delta = gesture.mid_point - last_entry.pos;
274 cur_entry.vel_y = static_cast<f32>(cur_entry.delta_y) / time_difference; 260 cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference;
261 cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference;
275 last_pan_time_difference = time_difference; 262 last_pan_time_difference = time_difference;
276 263
277 // Promote to pinch type 264 // Promote to pinch type
@@ -295,12 +282,11 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
295 GestureProperties& last_gesture_props, TouchType& type, 282 GestureProperties& last_gesture_props, TouchType& type,
296 f32 time_difference) { 283 f32 time_difference) {
297 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 284 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
298 const auto& last_entry = 285 const auto& last_entry = GetLastGestureEntry();
299 shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
300 cur_entry.vel_x = 286 cur_entry.vel_x =
301 static_cast<f32>(last_entry.delta_x) / (last_pan_time_difference + time_difference); 287 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
302 cur_entry.vel_y = 288 cur_entry.vel_y =
303 static_cast<f32>(last_entry.delta_y) / (last_pan_time_difference + time_difference); 289 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
304 const f32 curr_vel = 290 const f32 curr_vel =
305 std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); 291 std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y));
306 292
@@ -320,22 +306,22 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
320void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 306void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
321 GestureProperties& last_gesture_props, TouchType& type) { 307 GestureProperties& last_gesture_props, TouchType& type) {
322 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 308 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
323 const auto& last_entry = 309 const auto& last_entry = GetLastGestureEntry();
324 shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; 310
325 type = TouchType::Swipe; 311 type = TouchType::Swipe;
326 gesture = last_gesture_props; 312 gesture = last_gesture_props;
327 force_update = true; 313 force_update = true;
328 cur_entry.delta_x = last_entry.delta_x; 314 cur_entry.delta = last_entry.delta;
329 cur_entry.delta_y = last_entry.delta_y; 315
330 if (std::abs(cur_entry.delta_x) > std::abs(cur_entry.delta_y)) { 316 if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) {
331 if (cur_entry.delta_x > 0) { 317 if (cur_entry.delta.x > 0) {
332 cur_entry.direction = Direction::Right; 318 cur_entry.direction = Direction::Right;
333 return; 319 return;
334 } 320 }
335 cur_entry.direction = Direction::Left; 321 cur_entry.direction = Direction::Left;
336 return; 322 return;
337 } 323 }
338 if (cur_entry.delta_y > 0) { 324 if (cur_entry.delta.y > 0) {
339 cur_entry.direction = Direction::Down; 325 cur_entry.direction = Direction::Down;
340 return; 326 return;
341 } 327 }
@@ -364,6 +350,14 @@ std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
364 return std::nullopt; 350 return std::nullopt;
365} 351}
366 352
353Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
354 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
355}
356
357const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
358 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
359}
360
367std::size_t Controller_Gesture::UpdateTouchInputEvent( 361std::size_t Controller_Gesture::UpdateTouchInputEvent(
368 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) { 362 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
369 const auto& [x, y, pressed] = touch_input; 363 const auto& [x, y, pressed] = touch_input;
@@ -381,8 +375,7 @@ std::size_t Controller_Gesture::UpdateTouchInputEvent(
381 finger_id = first_free_id.value(); 375 finger_id = first_free_id.value();
382 fingers[finger_id].pressed = true; 376 fingers[finger_id].pressed = true;
383 } 377 }
384 fingers[finger_id].x = x; 378 fingers[finger_id].pos = {x, y};
385 fingers[finger_id].y = y;
386 return finger_id; 379 return finger_id;
387 } 380 }
388 381
@@ -402,17 +395,18 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties()
402 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 395 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
403 396
404 for (size_t id = 0; id < gesture.active_points; ++id) { 397 for (size_t id = 0; id < gesture.active_points; ++id) {
405 gesture.points[id].x = 398 const auto& [active_x, active_y] = active_fingers[id].pos;
406 static_cast<s32>(active_fingers[id].x * Layout::ScreenUndocked::Width); 399 gesture.points[id] = {
407 gesture.points[id].y = 400 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
408 static_cast<s32>(active_fingers[id].y * Layout::ScreenUndocked::Height); 401 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
402 };
409 403
410 // Hack: There is no touch in docked but games still allow it 404 // Hack: There is no touch in docked but games still allow it
411 if (Settings::values.use_docked_mode.GetValue()) { 405 if (Settings::values.use_docked_mode.GetValue()) {
412 gesture.points[id].x = 406 gesture.points[id] = {
413 static_cast<s32>(active_fingers[id].x * Layout::ScreenDocked::Width); 407 .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
414 gesture.points[id].y = 408 .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
415 static_cast<s32>(active_fingers[id].y * Layout::ScreenDocked::Height); 409 };
416 } 410 }
417 411
418 gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); 412 gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index f46e29411..7e7ae6625 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h"
10#include "core/frontend/input.h" 11#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
12 13
@@ -63,29 +64,21 @@ private:
63 }; 64 };
64 static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); 65 static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
65 66
66 struct Points {
67 s32_le x;
68 s32_le y;
69 };
70 static_assert(sizeof(Points) == 8, "Points is an invalid size");
71
72 struct GestureState { 67 struct GestureState {
73 s64_le sampling_number; 68 s64_le sampling_number;
74 s64_le sampling_number2; 69 s64_le sampling_number2;
75 s64_le detection_count; 70 s64_le detection_count;
76 TouchType type; 71 TouchType type;
77 Direction direction; 72 Direction direction;
78 s32_le x; 73 Common::Point<s32_le> pos;
79 s32_le y; 74 Common::Point<s32_le> delta;
80 s32_le delta_x;
81 s32_le delta_y;
82 f32 vel_x; 75 f32 vel_x;
83 f32 vel_y; 76 f32 vel_y;
84 Attribute attributes; 77 Attribute attributes;
85 f32 scale; 78 f32 scale;
86 f32 rotation_angle; 79 f32 rotation_angle;
87 s32_le point_count; 80 s32_le point_count;
88 std::array<Points, 4> points; 81 std::array<Common::Point<s32_le>, 4> points;
89 }; 82 };
90 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size"); 83 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
91 84
@@ -96,15 +89,14 @@ private:
96 static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size"); 89 static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
97 90
98 struct Finger { 91 struct Finger {
99 f32 x{}; 92 Common::Point<f32> pos{};
100 f32 y{};
101 bool pressed{}; 93 bool pressed{};
102 }; 94 };
103 95
104 struct GestureProperties { 96 struct GestureProperties {
105 std::array<Points, MAX_POINTS> points{}; 97 std::array<Common::Point<s32_le>, MAX_POINTS> points{};
106 std::size_t active_points{}; 98 std::size_t active_points{};
107 Points mid_point{}; 99 Common::Point<s32_le> mid_point{};
108 s64_le detection_count{}; 100 s64_le detection_count{};
109 u64_le delta_time{}; 101 u64_le delta_time{};
110 f32 average_distance{}; 102 f32 average_distance{};
@@ -148,7 +140,11 @@ private:
148 TouchType& type); 140 TouchType& type);
149 141
150 // Returns an unused finger id, if there is no fingers available std::nullopt is returned. 142 // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
151 std::optional<size_t> GetUnusedFingerID() const; 143 [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
144
145 // Retrieves the last gesture entry, as indicated by shared memory indices.
146 [[nodiscard]] GestureState& GetLastGestureEntry();
147 [[nodiscard]] const GestureState& GetLastGestureEntry() const;
152 148
153 /** 149 /**
154 * If the touch is new it tries to assign a new finger id, if there is no fingers available no 150 * If the touch is new it tries to assign a new finger id, if there is no fingers available no
@@ -166,10 +162,10 @@ private:
166 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 162 std::unique_ptr<Input::TouchDevice> touch_mouse_device;
167 std::unique_ptr<Input::TouchDevice> touch_udp_device; 163 std::unique_ptr<Input::TouchDevice> touch_udp_device;
168 std::unique_ptr<Input::TouchDevice> touch_btn_device; 164 std::unique_ptr<Input::TouchDevice> touch_btn_device;
169 std::array<size_t, MAX_FINGERS> mouse_finger_id; 165 std::array<size_t, MAX_FINGERS> mouse_finger_id{};
170 std::array<size_t, MAX_FINGERS> keyboard_finger_id; 166 std::array<size_t, MAX_FINGERS> keyboard_finger_id{};
171 std::array<size_t, MAX_FINGERS> udp_finger_id; 167 std::array<size_t, MAX_FINGERS> udp_finger_id{};
172 std::array<Finger, MAX_POINTS> fingers; 168 std::array<Finger, MAX_POINTS> fingers{};
173 GestureProperties last_gesture{}; 169 GestureProperties last_gesture{};
174 s64_le last_update_timestamp{}; 170 s64_le last_update_timestamp{};
175 s64_le last_tap_timestamp{}; 171 s64_le last_tap_timestamp{};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index ac9112c40..6ef17acc5 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -74,8 +74,11 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
74 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 74 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
75 auto& touch_entry = cur_entry.states[id]; 75 auto& touch_entry = cur_entry.states[id];
76 if (id < active_fingers_count) { 76 if (id < active_fingers_count) {
77 touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width); 77 const auto& [active_x, active_y] = active_fingers[id].position;
78 touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::Height); 78 touch_entry.position = {
79 .x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width),
80 .y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height),
81 };
79 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; 82 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
80 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; 83 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
81 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; 84 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
@@ -86,8 +89,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
86 } else { 89 } else {
87 // Clear touch entry 90 // Clear touch entry
88 touch_entry.attribute.raw = 0; 91 touch_entry.attribute.raw = 0;
89 touch_entry.x = 0; 92 touch_entry.position = {};
90 touch_entry.y = 0;
91 touch_entry.diameter_x = 0; 93 touch_entry.diameter_x = 0;
92 touch_entry.diameter_y = 0; 94 touch_entry.diameter_y = 0;
93 touch_entry.rotation_angle = 0; 95 touch_entry.rotation_angle = 0;
@@ -140,8 +142,7 @@ std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
140 fingers[finger_id].id = static_cast<u32_le>(finger_id); 142 fingers[finger_id].id = static_cast<u32_le>(finger_id);
141 attribute.start_touch.Assign(1); 143 attribute.start_touch.Assign(1);
142 } 144 }
143 fingers[finger_id].x = x; 145 fingers[finger_id].position = {x, y};
144 fingers[finger_id].y = y;
145 fingers[finger_id].attribute = attribute; 146 fingers[finger_id].attribute = attribute;
146 return finger_id; 147 return finger_id;
147 } 148 }
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 2869d0cfd..ef2becefd 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -7,6 +7,7 @@
7#include "common/bit_field.h" 7#include "common/bit_field.h"
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h"
10#include "common/swap.h" 11#include "common/swap.h"
11#include "core/frontend/input.h" 12#include "core/frontend/input.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
@@ -55,8 +56,7 @@ private:
55 u64_le delta_time; 56 u64_le delta_time;
56 Attributes attribute; 57 Attributes attribute;
57 u32_le finger; 58 u32_le finger;
58 u32_le x; 59 Common::Point<u32_le> position;
59 u32_le y;
60 u32_le diameter_x; 60 u32_le diameter_x;
61 u32_le diameter_y; 61 u32_le diameter_y;
62 u32_le rotation_angle; 62 u32_le rotation_angle;
@@ -81,8 +81,7 @@ private:
81 81
82 struct Finger { 82 struct Finger {
83 u64_le last_touch{}; 83 u64_le last_touch{};
84 float x{}; 84 Common::Point<float> position;
85 float y{};
86 u32_le id{}; 85 u32_le id{};
87 bool pressed{}; 86 bool pressed{};
88 Attributes attribute; 87 Attributes attribute;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 49c17fd14..df0fe1c8e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1770,7 +1770,7 @@ public:
1770 {232, nullptr, "GetIrSensorState"}, 1770 {232, nullptr, "GetIrSensorState"},
1771 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 1771 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
1772 {301, nullptr, "ActivateNpadSystem"}, 1772 {301, nullptr, "ActivateNpadSystem"},
1773 {303, nullptr, "ApplyNpadSystemCommonPolicy"}, 1773 {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
1774 {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, 1774 {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
1775 {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, 1775 {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
1776 {306, nullptr, "GetLastActiveNpad"}, 1776 {306, nullptr, "GetLastActiveNpad"},
@@ -1949,6 +1949,15 @@ public:
1949 1949
1950 RegisterHandlers(functions); 1950 RegisterHandlers(functions);
1951 } 1951 }
1952
1953private:
1954 void ApplyNpadSystemCommonPolicy(Kernel::HLERequestContext& ctx) {
1955 // We already do this for homebrew so we can just stub it out
1956 LOG_WARNING(Service_HID, "called");
1957
1958 IPC::ResponseBuilder rb{ctx, 2};
1959 rb.Push(RESULT_SUCCESS);
1960 }
1952}; 1961};
1953 1962
1954class HidTmp final : public ServiceFramework<HidTmp> { 1963class HidTmp final : public ServiceFramework<HidTmp> {
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index d160ffe87..c709a8028 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -215,10 +215,151 @@ public:
215 } 215 }
216}; 216};
217 217
218class INetworkService final : public ServiceFramework<INetworkService> {
219public:
220 explicit INetworkService(Core::System& system_) : ServiceFramework{system_, "INetworkService"} {
221 // clang-format off
222 static const FunctionInfo functions[] = {
223 {0, nullptr, "Initialize"},
224 {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
225 {264, nullptr, "GetNetworkInterfaceLastError"},
226 {272, nullptr, "GetRole"},
227 {280, nullptr, "GetAdvertiseData"},
228 {288, nullptr, "GetGroupInfo"},
229 {296, nullptr, "GetGroupInfo2"},
230 {304, nullptr, "GetGroupOwner"},
231 {312, nullptr, "GetIpConfig"},
232 {320, nullptr, "GetLinkLevel"},
233 {512, nullptr, "Scan"},
234 {768, nullptr, "CreateGroup"},
235 {776, nullptr, "DestroyGroup"},
236 {784, nullptr, "SetAdvertiseData"},
237 {1536, nullptr, "SendToOtherGroup"},
238 {1544, nullptr, "RecvFromOtherGroup"},
239 {1552, nullptr, "AddAcceptableGroupId"},
240 {1560, nullptr, "ClearAcceptableGroupId"},
241 };
242 // clang-format on
243
244 RegisterHandlers(functions);
245 }
246};
247
248class INetworkServiceMonitor final : public ServiceFramework<INetworkServiceMonitor> {
249public:
250 explicit INetworkServiceMonitor(Core::System& system_)
251 : ServiceFramework{system_, "INetworkServiceMonitor"} {
252 // clang-format off
253 static const FunctionInfo functions[] = {
254 {0, &INetworkServiceMonitor::Initialize, "Initialize"},
255 {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
256 {264, nullptr, "GetNetworkInterfaceLastError"},
257 {272, nullptr, "GetRole"},
258 {280, nullptr, "GetAdvertiseData"},
259 {281, nullptr, "GetAdvertiseData2"},
260 {288, nullptr, "GetGroupInfo"},
261 {296, nullptr, "GetGroupInfo2"},
262 {304, nullptr, "GetGroupOwner"},
263 {312, nullptr, "GetIpConfig"},
264 {320, nullptr, "GetLinkLevel"},
265 {328, nullptr, "AttachJoinEvent"},
266 {336, nullptr, "GetMembers"},
267 };
268 // clang-format on
269
270 RegisterHandlers(functions);
271 }
272
273 void Initialize(Kernel::HLERequestContext& ctx) {
274 LOG_WARNING(Service_LDN, "(STUBBED) called");
275
276 IPC::ResponseBuilder rb{ctx, 2};
277 rb.Push(ERROR_DISABLED);
278 }
279};
280
281class LP2PAPP final : public ServiceFramework<LP2PAPP> {
282public:
283 explicit LP2PAPP(Core::System& system_) : ServiceFramework{system_, "lp2p:app"} {
284 // clang-format off
285 static const FunctionInfo functions[] = {
286 {0, &LP2PAPP::CreateMonitorService, "CreateNetworkService"},
287 {8, &LP2PAPP::CreateMonitorService, "CreateNetworkServiceMonitor"},
288 };
289 // clang-format on
290
291 RegisterHandlers(functions);
292 }
293
294 void CreateNetworkervice(Kernel::HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 const u64 reserved_input = rp.Pop<u64>();
297 const u32 input = rp.Pop<u32>();
298
299 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
300 input);
301
302 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
303 rb.Push(RESULT_SUCCESS);
304 rb.PushIpcInterface<INetworkService>(system);
305 }
306
307 void CreateMonitorService(Kernel::HLERequestContext& ctx) {
308 IPC::RequestParser rp{ctx};
309 const u64 reserved_input = rp.Pop<u64>();
310
311 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);
312
313 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
314 rb.Push(RESULT_SUCCESS);
315 rb.PushIpcInterface<INetworkServiceMonitor>(system);
316 }
317};
318
319class LP2PSYS final : public ServiceFramework<LP2PSYS> {
320public:
321 explicit LP2PSYS(Core::System& system_) : ServiceFramework{system_, "lp2p:sys"} {
322 // clang-format off
323 static const FunctionInfo functions[] = {
324 {0, &LP2PSYS::CreateMonitorService, "CreateNetworkService"},
325 {8, &LP2PSYS::CreateMonitorService, "CreateNetworkServiceMonitor"},
326 };
327 // clang-format on
328
329 RegisterHandlers(functions);
330 }
331
332 void CreateNetworkervice(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const u64 reserved_input = rp.Pop<u64>();
335 const u32 input = rp.Pop<u32>();
336
337 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
338 input);
339
340 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
341 rb.Push(RESULT_SUCCESS);
342 rb.PushIpcInterface<INetworkService>(system);
343 }
344
345 void CreateMonitorService(Kernel::HLERequestContext& ctx) {
346 IPC::RequestParser rp{ctx};
347 const u64 reserved_input = rp.Pop<u64>();
348
349 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);
350
351 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
352 rb.Push(RESULT_SUCCESS);
353 rb.PushIpcInterface<INetworkServiceMonitor>(system);
354 }
355};
356
218void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { 357void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
219 std::make_shared<LDNM>(system)->InstallAsService(sm); 358 std::make_shared<LDNM>(system)->InstallAsService(sm);
220 std::make_shared<LDNS>(system)->InstallAsService(sm); 359 std::make_shared<LDNS>(system)->InstallAsService(sm);
221 std::make_shared<LDNU>(system)->InstallAsService(sm); 360 std::make_shared<LDNU>(system)->InstallAsService(sm);
361 std::make_shared<LP2PAPP>(system)->InstallAsService(sm);
362 std::make_shared<LP2PSYS>(system)->InstallAsService(sm);
222} 363}
223 364
224} // namespace Service::LDN 365} // namespace Service::LDN
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index bf2ef7816..9857278f6 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -591,7 +591,15 @@ struct Memory::Impl {
591 * @returns The instance of T read from the specified virtual address. 591 * @returns The instance of T read from the specified virtual address.
592 */ 592 */
593 template <typename T> 593 template <typename T>
594 T Read(const VAddr vaddr) { 594 T Read(VAddr vaddr) {
595 // AARCH64 masks the upper 16 bit of all memory accesses
596 vaddr &= 0xffffffffffffLL;
597
598 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
599 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
600 return 0;
601 }
602
595 // Avoid adding any extra logic to this fast-path block 603 // Avoid adding any extra logic to this fast-path block
596 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 604 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
597 if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 605 if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
@@ -629,7 +637,16 @@ struct Memory::Impl {
629 * is undefined. 637 * is undefined.
630 */ 638 */
631 template <typename T> 639 template <typename T>
632 void Write(const VAddr vaddr, const T data) { 640 void Write(VAddr vaddr, const T data) {
641 // AARCH64 masks the upper 16 bit of all memory accesses
642 vaddr &= 0xffffffffffffLL;
643
644 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
645 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
646 static_cast<u32>(data), vaddr);
647 return;
648 }
649
633 // Avoid adding any extra logic to this fast-path block 650 // Avoid adding any extra logic to this fast-path block
634 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 651 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
635 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 652 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
@@ -656,7 +673,16 @@ struct Memory::Impl {
656 } 673 }
657 674
658 template <typename T> 675 template <typename T>
659 bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { 676 bool WriteExclusive(VAddr vaddr, const T data, const T expected) {
677 // AARCH64 masks the upper 16 bit of all memory accesses
678 vaddr &= 0xffffffffffffLL;
679
680 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
681 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
682 static_cast<u32>(data), vaddr);
683 return true;
684 }
685
660 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 686 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
661 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 687 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
662 // NOTE: Avoid adding any extra logic to this fast-path block 688 // NOTE: Avoid adding any extra logic to this fast-path block
@@ -683,7 +709,16 @@ struct Memory::Impl {
683 return true; 709 return true;
684 } 710 }
685 711
686 bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { 712 bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) {
713 // AARCH64 masks the upper 16 bit of all memory accesses
714 vaddr &= 0xffffffffffffLL;
715
716 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
717 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
718 static_cast<u32>(data[0]), vaddr);
719 return true;
720 }
721
687 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 722 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
688 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 723 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
689 // NOTE: Avoid adding any extra logic to this fast-path block 724 // NOTE: Avoid adding any extra logic to this fast-path block
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index b9b584b2a..68672a92b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -18,16 +18,6 @@
18#include <utility> 18#include <utility>
19#include <vector> 19#include <vector>
20 20
21// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
22#ifdef __clang__
23#pragma clang diagnostic push
24#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
25#endif
26#include <SDL.h>
27#ifdef __clang__
28#pragma clang diagnostic pop
29#endif
30
31#include "common/logging/log.h" 21#include "common/logging/log.h"
32#include "common/math_util.h" 22#include "common/math_util.h"
33#include "common/param_package.h" 23#include "common/param_package.h"
@@ -214,6 +204,40 @@ public:
214 sdl_controller.reset(controller); 204 sdl_controller.reset(controller);
215 } 205 }
216 206
207 bool IsJoyconLeft() const {
208 return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr;
209 }
210
211 bool IsJoyconRight() const {
212 return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr;
213 }
214
215 std::string GetControllerName() const {
216 if (sdl_controller) {
217 switch (SDL_GameControllerGetType(sdl_controller.get())) {
218 case SDL_CONTROLLER_TYPE_XBOX360:
219 return "XBox 360 Controller";
220 case SDL_CONTROLLER_TYPE_XBOXONE:
221 return "XBox One Controller";
222 default:
223 break;
224 }
225 const auto name = SDL_GameControllerName(sdl_controller.get());
226 if (name) {
227 return name;
228 }
229 }
230
231 if (sdl_joystick) {
232 const auto name = SDL_JoystickName(sdl_joystick.get());
233 if (name) {
234 return name;
235 }
236 }
237
238 return "Unknown";
239 }
240
217private: 241private:
218 struct State { 242 struct State {
219 std::unordered_map<int, bool> buttons; 243 std::unordered_map<int, bool> buttons;
@@ -858,23 +882,42 @@ SDLState::~SDLState() {
858std::vector<Common::ParamPackage> SDLState::GetInputDevices() { 882std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
859 std::scoped_lock lock(joystick_map_mutex); 883 std::scoped_lock lock(joystick_map_mutex);
860 std::vector<Common::ParamPackage> devices; 884 std::vector<Common::ParamPackage> devices;
885 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
886 for (const auto& [key, value] : joystick_map) {
887 for (const auto& joystick : value) {
888 if (!joystick->GetSDLJoystick()) {
889 continue;
890 }
891 std::string name =
892 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
893 devices.emplace_back(Common::ParamPackage{
894 {"class", "sdl"},
895 {"display", std::move(name)},
896 {"guid", joystick->GetGUID()},
897 {"port", std::to_string(joystick->GetPort())},
898 });
899 if (joystick->IsJoyconLeft()) {
900 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
901 }
902 }
903 }
904
905 // Add dual controllers
861 for (const auto& [key, value] : joystick_map) { 906 for (const auto& [key, value] : joystick_map) {
862 for (const auto& joystick : value) { 907 for (const auto& joystick : value) {
863 if (auto* const controller = joystick->GetSDLGameController()) { 908 if (joystick->IsJoyconRight()) {
909 if (!joycon_pairs.contains(joystick->GetPort())) {
910 continue;
911 }
912 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
913
864 std::string name = 914 std::string name =
865 fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); 915 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
866 devices.emplace_back(Common::ParamPackage{
867 {"class", "sdl"},
868 {"display", std::move(name)},
869 {"guid", joystick->GetGUID()},
870 {"port", std::to_string(joystick->GetPort())},
871 });
872 } else if (auto* const joy = joystick->GetSDLJoystick()) {
873 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
874 devices.emplace_back(Common::ParamPackage{ 916 devices.emplace_back(Common::ParamPackage{
875 {"class", "sdl"}, 917 {"class", "sdl"},
876 {"display", std::move(name)}, 918 {"display", std::move(name)},
877 {"guid", joystick->GetGUID()}, 919 {"guid", joystick->GetGUID()},
920 {"guid2", joystick2->GetGUID()},
878 {"port", std::to_string(joystick->GetPort())}, 921 {"port", std::to_string(joystick->GetPort())},
879 }); 922 });
880 } 923 }
@@ -883,17 +926,6 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
883 return devices; 926 return devices;
884} 927}
885 928
886std::string SDLState::GetControllerName(SDL_GameController* controller) const {
887 switch (SDL_GameControllerGetType(controller)) {
888 case SDL_CONTROLLER_TYPE_XBOX360:
889 return "XBox 360 Controller";
890 case SDL_CONTROLLER_TYPE_XBOXONE:
891 return "XBox One Controller";
892 default:
893 return SDL_GameControllerName(controller);
894 }
895}
896
897namespace { 929namespace {
898Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, 930Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
899 float value = 0.1f) { 931 float value = 0.1f) {
@@ -1073,24 +1105,48 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1073 return {}; 1105 return {};
1074 } 1106 }
1075 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1107 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1108
1076 auto* controller = joystick->GetSDLGameController(); 1109 auto* controller = joystick->GetSDLGameController();
1077 if (controller == nullptr) { 1110 if (controller == nullptr) {
1078 return {}; 1111 return {};
1079 } 1112 }
1080 1113
1081 const bool invert =
1082 SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
1083
1084 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. 1114 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
1085 // We will add those afterwards 1115 // We will add those afterwards
1086 // This list also excludes Screenshot since theres not really a mapping for that 1116 // This list also excludes Screenshot since theres not really a mapping for that
1087 using ButtonBindings = 1117 ButtonBindings switch_to_sdl_button;
1088 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; 1118
1089 const ButtonBindings switch_to_sdl_button{{ 1119 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
1090 {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, 1120 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
1091 {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, 1121 } else {
1092 {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, 1122 switch_to_sdl_button = GetDefaultButtonBinding();
1093 {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, 1123 }
1124
1125 // Add the missing bindings for ZL/ZR
1126 static constexpr ZButtonBindings switch_to_sdl_axis{{
1127 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
1128 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
1129 }};
1130
1131 // Parameters contain two joysticks return dual
1132 if (params.Has("guid2")) {
1133 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1134
1135 if (joystick2->GetSDLGameController() != nullptr) {
1136 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
1137 switch_to_sdl_axis);
1138 }
1139 }
1140
1141 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
1142}
1143
1144ButtonBindings SDLState::GetDefaultButtonBinding() const {
1145 return {
1146 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
1147 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
1148 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
1149 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
1094 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, 1150 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1095 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, 1151 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1096 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 1152 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
@@ -1104,18 +1160,51 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1104 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 1160 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1105 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, 1161 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1106 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, 1162 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1107 }}; 1163 };
1164}
1108 1165
1109 // Add the missing bindings for ZL/ZR 1166ButtonBindings SDLState::GetNintendoButtonBinding(
1110 using ZBindings = 1167 const std::shared_ptr<SDLJoystick>& joystick) const {
1111 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 1168 // Default SL/SR mapping for pro controllers
1112 static constexpr ZBindings switch_to_sdl_axis{{ 1169 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
1113 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, 1170 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
1114 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, 1171
1115 }}; 1172 if (joystick->IsJoyconLeft()) {
1173 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
1174 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
1175 }
1176 if (joystick->IsJoyconRight()) {
1177 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
1178 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
1179 }
1116 1180
1181 return {
1182 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
1183 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
1184 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
1185 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
1186 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1187 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1188 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1189 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1190 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1191 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1192 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1193 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1194 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1195 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1196 {Settings::NativeButton::SL, sl_button},
1197 {Settings::NativeButton::SR, sr_button},
1198 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1199 };
1200}
1201
1202ButtonMapping SDLState::GetSingleControllerMapping(
1203 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
1204 const ZButtonBindings& switch_to_sdl_axis) const {
1117 ButtonMapping mapping; 1205 ButtonMapping mapping;
1118 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); 1206 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1207 auto* controller = joystick->GetSDLGameController();
1119 1208
1120 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { 1209 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1121 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); 1210 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
@@ -1133,11 +1222,68 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1133 return mapping; 1222 return mapping;
1134} 1223}
1135 1224
1225ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
1226 const std::shared_ptr<SDLJoystick>& joystick2,
1227 const ButtonBindings& switch_to_sdl_button,
1228 const ZButtonBindings& switch_to_sdl_axis) const {
1229 ButtonMapping mapping;
1230 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1231 auto* controller = joystick->GetSDLGameController();
1232 auto* controller2 = joystick2->GetSDLGameController();
1233
1234 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1235 if (IsButtonOnLeftSide(switch_button)) {
1236 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
1237 mapping.insert_or_assign(
1238 switch_button,
1239 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1240 continue;
1241 }
1242 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1243 mapping.insert_or_assign(
1244 switch_button,
1245 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1246 }
1247 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1248 if (IsButtonOnLeftSide(switch_button)) {
1249 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
1250 mapping.insert_or_assign(
1251 switch_button,
1252 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1253 continue;
1254 }
1255 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1256 mapping.insert_or_assign(
1257 switch_button,
1258 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1259 }
1260
1261 return mapping;
1262}
1263
1264bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
1265 switch (button) {
1266 case Settings::NativeButton::DDown:
1267 case Settings::NativeButton::DLeft:
1268 case Settings::NativeButton::DRight:
1269 case Settings::NativeButton::DUp:
1270 case Settings::NativeButton::L:
1271 case Settings::NativeButton::LStick:
1272 case Settings::NativeButton::Minus:
1273 case Settings::NativeButton::Screenshot:
1274 case Settings::NativeButton::ZL:
1275 return true;
1276 default:
1277 return false;
1278 }
1279}
1280
1136AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { 1281AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
1137 if (!params.Has("guid") || !params.Has("port")) { 1282 if (!params.Has("guid") || !params.Has("port")) {
1138 return {}; 1283 return {};
1139 } 1284 }
1140 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1285 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1286 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1141 auto* controller = joystick->GetSDLGameController(); 1287 auto* controller = joystick->GetSDLGameController();
1142 if (controller == nullptr) { 1288 if (controller == nullptr) {
1143 return {}; 1289 return {};
@@ -1148,10 +1294,17 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
1148 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 1294 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
1149 const auto& binding_left_y = 1295 const auto& binding_left_y =
1150 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1296 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1151 mapping.insert_or_assign(Settings::NativeAnalog::LStick, 1297 if (params.Has("guid2")) {
1152 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1298 mapping.insert_or_assign(
1153 binding_left_x.value.axis, 1299 Settings::NativeAnalog::LStick,
1154 binding_left_y.value.axis)); 1300 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1301 binding_left_x.value.axis, binding_left_y.value.axis));
1302 } else {
1303 mapping.insert_or_assign(
1304 Settings::NativeAnalog::LStick,
1305 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1306 binding_left_x.value.axis, binding_left_y.value.axis));
1307 }
1155 const auto& binding_right_x = 1308 const auto& binding_right_x =
1156 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1309 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1157 const auto& binding_right_y = 1310 const auto& binding_right_y =
@@ -1168,20 +1321,32 @@ MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& pa
1168 return {}; 1321 return {};
1169 } 1322 }
1170 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1323 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1324 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1171 auto* controller = joystick->GetSDLGameController(); 1325 auto* controller = joystick->GetSDLGameController();
1172 if (controller == nullptr) { 1326 if (controller == nullptr) {
1173 return {}; 1327 return {};
1174 } 1328 }
1175 1329
1330 MotionMapping mapping = {};
1176 joystick->EnableMotion(); 1331 joystick->EnableMotion();
1177 1332
1178 if (!joystick->HasGyro() && !joystick->HasAccel()) { 1333 if (joystick->HasGyro() || joystick->HasAccel()) {
1179 return {}; 1334 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
1335 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1336 }
1337 if (params.Has("guid2")) {
1338 joystick2->EnableMotion();
1339 if (joystick2->HasGyro() || joystick2->HasAccel()) {
1340 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1341 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
1342 }
1343 } else {
1344 if (joystick->HasGyro() || joystick->HasAccel()) {
1345 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1346 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1347 }
1180 } 1348 }
1181 1349
1182 MotionMapping mapping = {};
1183 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1184 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1185 return mapping; 1350 return mapping;
1186} 1351}
1187namespace Polling { 1352namespace Polling {
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 121e01913..b77afcbd8 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -9,6 +9,17 @@
9#include <mutex> 9#include <mutex>
10#include <thread> 10#include <thread>
11#include <unordered_map> 11#include <unordered_map>
12
13// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
14#ifdef __GNUC__
15#pragma GCC diagnostic push
16#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
17#endif
18#include <SDL.h>
19#ifdef __GNUC__
20#pragma GCC diagnostic pop
21#endif
22
12#include "common/common_types.h" 23#include "common/common_types.h"
13#include "common/threadsafe_queue.h" 24#include "common/threadsafe_queue.h"
14#include "input_common/sdl/sdl.h" 25#include "input_common/sdl/sdl.h"
@@ -18,6 +29,11 @@ using SDL_GameController = struct _SDL_GameController;
18using SDL_Joystick = struct _SDL_Joystick; 29using SDL_Joystick = struct _SDL_Joystick;
19using SDL_JoystickID = s32; 30using SDL_JoystickID = s32;
20 31
32using ButtonBindings =
33 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
34using ZButtonBindings =
35 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
36
21namespace InputCommon::SDL { 37namespace InputCommon::SDL {
22 38
23class SDLAnalogFactory; 39class SDLAnalogFactory;
@@ -66,8 +82,25 @@ private:
66 /// Needs to be called before SDL_QuitSubSystem. 82 /// Needs to be called before SDL_QuitSubSystem.
67 void CloseJoysticks(); 83 void CloseJoysticks();
68 84
69 /// Returns a custom name for specific controllers because the default name is not correct 85 /// Returns the default button bindings list for generic controllers
70 std::string GetControllerName(SDL_GameController* controller) const; 86 ButtonBindings GetDefaultButtonBinding() const;
87
88 /// Returns the default button bindings list for nintendo controllers
89 ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
90
91 /// Returns the button mappings from a single controller
92 ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
93 const ButtonBindings& switch_to_sdl_button,
94 const ZButtonBindings& switch_to_sdl_axis) const;
95
96 /// Returns the button mappings from two different controllers
97 ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
98 const std::shared_ptr<SDLJoystick>& joystick2,
99 const ButtonBindings& switch_to_sdl_button,
100 const ZButtonBindings& switch_to_sdl_axis) const;
101
102 /// Returns true if the button is on the left joycon
103 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
71 104
72 // Set to true if SDL supports game controller subsystem 105 // Set to true if SDL supports game controller subsystem
73 bool has_gamecontroller = false; 106 bool has_gamecontroller = false;
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index de971041f..9e6b87960 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -596,7 +596,7 @@ void BufferCache<P>::PopAsyncFlushes() {
596 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies); 596 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies);
597 } 597 }
598 runtime.Finish(); 598 runtime.Finish();
599 for (const auto [copy, buffer_id] : downloads) { 599 for (const auto& [copy, buffer_id] : downloads) {
600 const Buffer& buffer = slot_buffers[buffer_id]; 600 const Buffer& buffer = slot_buffers[buffer_id];
601 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset; 601 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
602 // Undo the modified offset 602 // Undo the modified offset
@@ -606,7 +606,7 @@ void BufferCache<P>::PopAsyncFlushes() {
606 } 606 }
607 } else { 607 } else {
608 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy); 608 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
609 for (const auto [copy, buffer_id] : downloads) { 609 for (const auto& [copy, buffer_id] : downloads) {
610 Buffer& buffer = slot_buffers[buffer_id]; 610 Buffer& buffer = slot_buffers[buffer_id];
611 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size)); 611 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
612 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset; 612 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 37f7b24e1..35cc561be 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -104,7 +104,13 @@ void GPU::WaitFence(u32 syncpoint_id, u32 value) {
104 } 104 }
105 MICROPROFILE_SCOPE(GPU_wait); 105 MICROPROFILE_SCOPE(GPU_wait);
106 std::unique_lock lock{sync_mutex}; 106 std::unique_lock lock{sync_mutex};
107 sync_cv.wait(lock, [=, this] { return syncpoints.at(syncpoint_id).load() >= value; }); 107 sync_cv.wait(lock, [=, this] {
108 if (shutting_down.load(std::memory_order_relaxed)) {
109 // We're shutting down, ensure no threads continue to wait for the next syncpoint
110 return true;
111 }
112 return syncpoints.at(syncpoint_id).load() >= value;
113 });
108} 114}
109 115
110void GPU::IncrementSyncPoint(const u32 syncpoint_id) { 116void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
@@ -523,6 +529,10 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
523} 529}
524 530
525void GPU::ShutDown() { 531void GPU::ShutDown() {
532 // Signal that threads should no longer block on syncpoint fences
533 shutting_down.store(true, std::memory_order_relaxed);
534 sync_cv.notify_all();
535
526 gpu_thread.ShutDown(); 536 gpu_thread.ShutDown();
527} 537}
528 538
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 29a867863..a8e98e51b 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -389,6 +389,8 @@ private:
389 std::unique_ptr<Engines::KeplerMemory> kepler_memory; 389 std::unique_ptr<Engines::KeplerMemory> kepler_memory;
390 /// Shader build notifier 390 /// Shader build notifier
391 std::unique_ptr<VideoCore::ShaderNotify> shader_notify; 391 std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
392 /// When true, we are about to shut down emulation session, so terminate outstanding tasks
393 std::atomic_bool shutting_down{};
392 394
393 std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{}; 395 std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
394 396
diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp
index 62d84c0f8..6decd2546 100644
--- a/src/video_core/rasterizer_accelerated.cpp
+++ b/src/video_core/rasterizer_accelerated.cpp
@@ -18,10 +18,10 @@ RasterizerAccelerated::~RasterizerAccelerated() = default;
18void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { 18void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
19 const auto page_end = Common::DivCeil(addr + size, Core::Memory::PAGE_SIZE); 19 const auto page_end = Common::DivCeil(addr + size, Core::Memory::PAGE_SIZE);
20 for (auto page = addr >> Core::Memory::PAGE_BITS; page != page_end; ++page) { 20 for (auto page = addr >> Core::Memory::PAGE_BITS; page != page_end; ++page) {
21 auto& count = cached_pages.at(page >> 3).Count(page); 21 auto& count = cached_pages.at(page >> 2).Count(page);
22 22
23 if (delta > 0) { 23 if (delta > 0) {
24 ASSERT_MSG(count < UINT8_MAX, "Count may overflow!"); 24 ASSERT_MSG(count < UINT16_MAX, "Count may overflow!");
25 } else if (delta < 0) { 25 } else if (delta < 0) {
26 ASSERT_MSG(count > 0, "Count may underflow!"); 26 ASSERT_MSG(count > 0, "Count may underflow!");
27 } else { 27 } else {
@@ -29,7 +29,7 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del
29 } 29 }
30 30
31 // Adds or subtracts 1, as count is a unsigned 8-bit value 31 // Adds or subtracts 1, as count is a unsigned 8-bit value
32 count += static_cast<u8>(delta); 32 count += static_cast<u16>(delta);
33 33
34 // Assume delta is either -1 or 1 34 // Assume delta is either -1 or 1
35 if (count == 0) { 35 if (count == 0) {
diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h
index 9227a4adc..ea879bfdd 100644
--- a/src/video_core/rasterizer_accelerated.h
+++ b/src/video_core/rasterizer_accelerated.h
@@ -29,20 +29,20 @@ private:
29 public: 29 public:
30 CacheEntry() = default; 30 CacheEntry() = default;
31 31
32 std::atomic_uint8_t& Count(std::size_t page) { 32 std::atomic_uint16_t& Count(std::size_t page) {
33 return values[page & 7]; 33 return values[page & 3];
34 } 34 }
35 35
36 const std::atomic_uint8_t& Count(std::size_t page) const { 36 const std::atomic_uint16_t& Count(std::size_t page) const {
37 return values[page & 7]; 37 return values[page & 3];
38 } 38 }
39 39
40 private: 40 private:
41 std::array<std::atomic_uint8_t, 8> values{}; 41 std::array<std::atomic_uint16_t, 4> values{};
42 }; 42 };
43 static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!"); 43 static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!");
44 44
45 std::array<CacheEntry, 0x800000> cached_pages; 45 std::array<CacheEntry, 0x1000000> cached_pages;
46 Core::Memory::Memory& cpu_memory; 46 Core::Memory::Memory& cpu_memory;
47}; 47};
48 48
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index fa37aa79a..5edd06ebc 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -53,6 +53,18 @@ struct Range {
53 UNREACHABLE_MSG("Invalid memory usage={}", usage); 53 UNREACHABLE_MSG("Invalid memory usage={}", usage);
54 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 54 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
55} 55}
56
57constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{
58 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
59 .pNext = nullptr,
60#ifdef _WIN32
61 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
62#elif __unix__
63 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
64#else
65 .handleTypes = 0,
66#endif
67};
56} // Anonymous namespace 68} // Anonymous namespace
57 69
58class MemoryAllocation { 70class MemoryAllocation {
@@ -131,7 +143,7 @@ public:
131 143
132 /// Returns whether this allocation is compatible with the arguments. 144 /// Returns whether this allocation is compatible with the arguments.
133 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { 145 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
134 return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; 146 return (flags & property_flags) == property_flags && (type_mask & shifted_memory_type) != 0;
135 } 147 }
136 148
137private: 149private:
@@ -217,14 +229,18 @@ MemoryAllocator::~MemoryAllocator() = default;
217 229
218MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { 230MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
219 // Find the fastest memory flags we can afford with the current requirements 231 // Find the fastest memory flags we can afford with the current requirements
220 const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); 232 const u32 type_mask = requirements.memoryTypeBits;
233 const VkMemoryPropertyFlags usage_flags = MemoryUsagePropertyFlags(usage);
234 const VkMemoryPropertyFlags flags = MemoryPropertyFlags(type_mask, usage_flags);
221 if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { 235 if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) {
222 return std::move(*commit); 236 return std::move(*commit);
223 } 237 }
224 // Commit has failed, allocate more memory. 238 // Commit has failed, allocate more memory.
225 // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. 239 const u64 chunk_size = AllocationChunkSize(requirements.size);
226 AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); 240 if (!TryAllocMemory(flags, type_mask, chunk_size)) {
227 241 // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory.
242 throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY);
243 }
228 // Commit again, this time it won't fail since there's a fresh allocation above. 244 // Commit again, this time it won't fail since there's a fresh allocation above.
229 // If it does, there's a bug. 245 // If it does, there's a bug.
230 return TryCommit(requirements, flags).value(); 246 return TryCommit(requirements, flags).value();
@@ -242,26 +258,25 @@ MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage)
242 return commit; 258 return commit;
243} 259}
244 260
245void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { 261bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
246 const u32 type = FindType(flags, type_mask).value(); 262 const u32 type = FindType(flags, type_mask).value();
247 const VkExportMemoryAllocateInfo export_allocate_info{ 263 vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
248 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
249 .pNext = nullptr,
250#ifdef _WIN32
251 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
252#elif __unix__
253 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
254#else
255 .handleTypes = 0,
256#endif
257 };
258 vk::DeviceMemory memory = device.GetLogical().AllocateMemory({
259 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 264 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
260 .pNext = export_allocations ? &export_allocate_info : nullptr, 265 .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr,
261 .allocationSize = size, 266 .allocationSize = size,
262 .memoryTypeIndex = type, 267 .memoryTypeIndex = type,
263 }); 268 });
269 if (!memory) {
270 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
271 // Try to allocate non device local memory
272 return TryAllocMemory(flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, type_mask, size);
273 } else {
274 // RIP
275 return false;
276 }
277 }
264 allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); 278 allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type));
279 return true;
265} 280}
266 281
267std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, 282std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements,
@@ -274,24 +289,24 @@ std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirement
274 return commit; 289 return commit;
275 } 290 }
276 } 291 }
292 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
293 // Look for non device local commits on failure
294 return TryCommit(requirements, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
295 }
277 return std::nullopt; 296 return std::nullopt;
278} 297}
279 298
280VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
281 return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
282}
283
284VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, 299VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
285 VkMemoryPropertyFlags flags) const { 300 VkMemoryPropertyFlags flags) const {
286 if (FindType(flags, type_mask)) { 301 if (FindType(flags, type_mask)) {
287 // Found a memory type with those requirements 302 // Found a memory type with those requirements
288 return flags; 303 return flags;
289 } 304 }
290 if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { 305 if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) {
291 // Remove host cached bit in case it's not supported 306 // Remove host cached bit in case it's not supported
292 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); 307 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
293 } 308 }
294 if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { 309 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
295 // Remove device local, if it's not supported by the requested resource 310 // Remove device local, if it's not supported by the requested resource
296 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); 311 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
297 } 312 }
@@ -302,7 +317,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
302std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { 317std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const {
303 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { 318 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
304 const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; 319 const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags;
305 if ((type_mask & (1U << type_index)) && (type_flags & flags)) { 320 if ((type_mask & (1U << type_index)) != 0 && (type_flags & flags) == flags) {
306 // The type matches in type and in the wanted properties. 321 // The type matches in type and in the wanted properties.
307 return type_index; 322 return type_index;
308 } 323 }
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index d1ce29450..db12d02f4 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -101,16 +101,13 @@ public:
101 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); 101 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage);
102 102
103private: 103private:
104 /// Allocates a chunk of memory. 104 /// Tries to allocate a chunk of memory.
105 void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); 105 bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
106 106
107 /// Tries to allocate a memory commit. 107 /// Tries to allocate a memory commit.
108 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, 108 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
109 VkMemoryPropertyFlags flags); 109 VkMemoryPropertyFlags flags);
110 110
111 /// Returns the fastest compatible memory property flags from a wanted usage.
112 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const;
113
114 /// Returns the fastest compatible memory property flags from the wanted flags. 111 /// Returns the fastest compatible memory property flags from the wanted flags.
115 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; 112 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const;
116 113
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cc0790e07..634fe66a5 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -4,6 +4,12 @@ set(CMAKE_AUTOUIC ON)
4set(CMAKE_INCLUDE_CURRENT_DIR ON) 4set(CMAKE_INCLUDE_CURRENT_DIR ON)
5set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) 5set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
6 6
7# Set the RPATH for Qt Libraries
8# This must be done before the `yuzu` target is created
9if (YUZU_USE_BUNDLED_QT AND (${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
10 set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/")
11endif()
12
7add_executable(yuzu 13add_executable(yuzu
8 Info.plist 14 Info.plist
9 about_dialog.cpp 15 about_dialog.cpp
@@ -278,11 +284,14 @@ if(UNIX AND NOT APPLE)
278 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 284 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
279endif() 285endif()
280 286
281if (MSVC) 287if (YUZU_USE_BUNDLED_QT)
282 include(CopyYuzuQt5Deps) 288 include(CopyYuzuQt5Deps)
289 copy_yuzu_Qt5_deps(yuzu)
290endif()
291
292if (MSVC)
283 include(CopyYuzuSDLDeps) 293 include(CopyYuzuSDLDeps)
284 include(CopyYuzuFFmpegDeps) 294 include(CopyYuzuFFmpegDeps)
285 copy_yuzu_Qt5_deps(yuzu)
286 copy_yuzu_SDL_deps(yuzu) 295 copy_yuzu_SDL_deps(yuzu)
287 copy_yuzu_FFmpeg_deps(yuzu) 296 copy_yuzu_FFmpeg_deps(yuzu)
288endif() 297endif()
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp
index b0f764994..aa453a79f 100644
--- a/src/yuzu/applets/software_keyboard.cpp
+++ b/src/yuzu/applets/software_keyboard.cpp
@@ -720,21 +720,9 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
720 ui->line_edit_osk->setFocus(); 720 ui->line_edit_osk->setFocus();
721 }); 721 });
722 722
723 connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] { 723 connect(
724 switch (bottom_osk_index) { 724 ui->line_edit_osk, &QLineEdit::returnPressed, this,
725 case BottomOSKIndex::LowerCase: 725 [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection);
726 ui->button_ok->click();
727 break;
728 case BottomOSKIndex::UpperCase:
729 ui->button_ok_shift->click();
730 break;
731 case BottomOSKIndex::NumberPad:
732 ui->button_ok_num->click();
733 break;
734 default:
735 break;
736 }
737 });
738 726
739 ui->line_edit_osk->setPlaceholderText( 727 ui->line_edit_osk->setPlaceholderText(
740 QString::fromStdU16String(initialize_parameters.guide_text)); 728 QString::fromStdU16String(initialize_parameters.guide_text));
@@ -1113,12 +1101,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button)
1113 } 1101 }
1114 1102
1115 if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { 1103 if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
1116 if (ui->topOSK->currentIndex() == 1) { 1104 auto text = ui->topOSK->currentIndex() == 1
1117 emit SubmitNormalText(SwkbdResult::Ok, 1105 ? ui->text_edit_osk->toPlainText().toStdU16String()
1118 ui->text_edit_osk->toPlainText().toStdU16String()); 1106 : ui->line_edit_osk->text().toStdU16String();
1119 } else { 1107
1120 emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String()); 1108 emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
1121 }
1122 return; 1109 return;
1123 } 1110 }
1124 1111
@@ -1277,13 +1264,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1277 if (is_inline) { 1264 if (is_inline) {
1278 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); 1265 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
1279 } else { 1266 } else {
1280 if (ui->topOSK->currentIndex() == 1) { 1267 auto text = ui->topOSK->currentIndex() == 1
1281 emit SubmitNormalText(SwkbdResult::Cancel, 1268 ? ui->text_edit_osk->toPlainText().toStdU16String()
1282 ui->text_edit_osk->toPlainText().toStdU16String()); 1269 : ui->line_edit_osk->text().toStdU16String();
1283 } else { 1270
1284 emit SubmitNormalText(SwkbdResult::Cancel, 1271 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
1285 ui->line_edit_osk->text().toStdU16String());
1286 }
1287 } 1272 }
1288 break; 1273 break;
1289 case HIDButton::Y: 1274 case HIDButton::Y:
@@ -1575,7 +1560,7 @@ void QtSoftwareKeyboard::ShowNormalKeyboard() const {
1575void QtSoftwareKeyboard::ShowTextCheckDialog( 1560void QtSoftwareKeyboard::ShowTextCheckDialog(
1576 Service::AM::Applets::SwkbdTextCheckResult text_check_result, 1561 Service::AM::Applets::SwkbdTextCheckResult text_check_result,
1577 std::u16string text_check_message) const { 1562 std::u16string text_check_message) const {
1578 emit MainWindowShowTextCheckDialog(text_check_result, text_check_message); 1563 emit MainWindowShowTextCheckDialog(text_check_result, std::move(text_check_message));
1579} 1564}
1580 1565
1581void QtSoftwareKeyboard::ShowInlineKeyboard( 1566void QtSoftwareKeyboard::ShowInlineKeyboard(
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 3ad40d2b3..6028135c5 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -2,8 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QAbstractButton>
6#include <QDialogButtonBox>
5#include <QHash> 7#include <QHash>
6#include <QListWidgetItem> 8#include <QListWidgetItem>
9#include <QPushButton>
7#include <QSignalBlocker> 10#include <QSignalBlocker>
8#include "common/settings.h" 11#include "common/settings.h"
9#include "core/core.h" 12#include "core/core.h"
@@ -31,6 +34,12 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
31 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, 34 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
32 &ConfigureDialog::UpdateVisibleTabs); 35 &ConfigureDialog::UpdateVisibleTabs);
33 36
37 if (Core::System::GetInstance().IsPoweredOn()) {
38 QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply);
39 connect(apply_button, &QAbstractButton::clicked, this,
40 &ConfigureDialog::HandleApplyButtonClicked);
41 }
42
34 adjustSize(); 43 adjustSize();
35 ui->selectorList->setCurrentRow(0); 44 ui->selectorList->setCurrentRow(0);
36} 45}
@@ -80,6 +89,11 @@ void ConfigureDialog::RetranslateUI() {
80 ui->tabWidget->setCurrentIndex(old_index); 89 ui->tabWidget->setCurrentIndex(old_index);
81} 90}
82 91
92void ConfigureDialog::HandleApplyButtonClicked() {
93 UISettings::values.configuration_applied = true;
94 ApplyConfiguration();
95}
96
83Q_DECLARE_METATYPE(QList<QWidget*>); 97Q_DECLARE_METATYPE(QList<QWidget*>);
84 98
85void ConfigureDialog::PopulateSelectionList() { 99void ConfigureDialog::PopulateSelectionList() {
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 570c3b941..abe019635 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -35,9 +35,10 @@ signals:
35 35
36private: 36private:
37 void changeEvent(QEvent* event) override; 37 void changeEvent(QEvent* event) override;
38
39 void RetranslateUI(); 38 void RetranslateUI();
40 39
40 void HandleApplyButtonClicked();
41
41 void SetConfiguration(); 42 void SetConfiguration();
42 void UpdateVisibleTabs(); 43 void UpdateVisibleTabs();
43 void PopulateSelectionList(); 44 void PopulateSelectionList();
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 007d93c77..7dfcf150c 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -7,9 +7,12 @@
7#include <string> 7#include <string>
8#include <utility> 8#include <utility>
9 9
10#include <QAbstractButton>
10#include <QCheckBox> 11#include <QCheckBox>
12#include <QDialogButtonBox>
11#include <QHeaderView> 13#include <QHeaderView>
12#include <QMenu> 14#include <QMenu>
15#include <QPushButton>
13#include <QStandardItemModel> 16#include <QStandardItemModel>
14#include <QString> 17#include <QString>
15#include <QTimer> 18#include <QTimer>
@@ -45,6 +48,12 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, std::string_vi
45 scene = new QGraphicsScene; 48 scene = new QGraphicsScene;
46 ui->icon_view->setScene(scene); 49 ui->icon_view->setScene(scene);
47 50
51 if (Core::System::GetInstance().IsPoweredOn()) {
52 QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply);
53 connect(apply_button, &QAbstractButton::clicked, this,
54 &ConfigurePerGame::HandleApplyButtonClicked);
55 }
56
48 LoadConfiguration(); 57 LoadConfiguration();
49} 58}
50 59
@@ -77,6 +86,11 @@ void ConfigurePerGame::RetranslateUI() {
77 ui->retranslateUi(this); 86 ui->retranslateUi(this);
78} 87}
79 88
89void ConfigurePerGame::HandleApplyButtonClicked() {
90 UISettings::values.configuration_applied = true;
91 ApplyConfiguration();
92}
93
80void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) { 94void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) {
81 this->file = std::move(file); 95 this->file = std::move(file);
82 LoadConfiguration(); 96 LoadConfiguration();
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index d86749a0e..dc6b68763 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -40,6 +40,8 @@ private:
40 void changeEvent(QEvent* event) override; 40 void changeEvent(QEvent* event) override;
41 void RetranslateUI(); 41 void RetranslateUI();
42 42
43 void HandleApplyButtonClicked();
44
43 void LoadConfiguration(); 45 void LoadConfiguration();
44 46
45 std::unique_ptr<Ui::ConfigurePerGame> ui; 47 std::unique_ptr<Ui::ConfigurePerGame> ui;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 1d63ababb..dd8dd3233 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2594,12 +2594,12 @@ void GMainWindow::OnConfigure() {
2594 &GMainWindow::OnLanguageChanged); 2594 &GMainWindow::OnLanguageChanged);
2595 2595
2596 const auto result = configure_dialog.exec(); 2596 const auto result = configure_dialog.exec();
2597 if (result != QDialog::Accepted) { 2597 if (result != QDialog::Accepted && !UISettings::values.configuration_applied) {
2598 return; 2598 return;
2599 } else if (result == QDialog::Accepted) {
2600 configure_dialog.ApplyConfiguration();
2601 controller_dialog->refreshConfiguration();
2599 } 2602 }
2600
2601 configure_dialog.ApplyConfiguration();
2602 controller_dialog->refreshConfiguration();
2603 InitializeHotkeys(); 2603 InitializeHotkeys();
2604 if (UISettings::values.theme != old_theme) { 2604 if (UISettings::values.theme != old_theme) {
2605 UpdateUITheme(); 2605 UpdateUITheme();
@@ -2614,6 +2614,8 @@ void GMainWindow::OnConfigure() {
2614 game_list->PopulateAsync(UISettings::values.game_dirs); 2614 game_list->PopulateAsync(UISettings::values.game_dirs);
2615 } 2615 }
2616 2616
2617 UISettings::values.configuration_applied = false;
2618
2617 config->Save(); 2619 config->Save();
2618 2620
2619 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { 2621 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
@@ -2643,23 +2645,27 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
2643 ConfigurePerGame dialog(this, title_id, file_name); 2645 ConfigurePerGame dialog(this, title_id, file_name);
2644 dialog.LoadFromFile(v_file); 2646 dialog.LoadFromFile(v_file);
2645 const auto result = dialog.exec(); 2647 const auto result = dialog.exec();
2646 if (result == QDialog::Accepted) { 2648
2649 if (result != QDialog::Accepted && !UISettings::values.configuration_applied) {
2650 Settings::RestoreGlobalState(system.IsPoweredOn());
2651 return;
2652 } else if (result == QDialog::Accepted) {
2647 dialog.ApplyConfiguration(); 2653 dialog.ApplyConfiguration();
2654 }
2648 2655
2649 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); 2656 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
2650 if (reload) { 2657 if (reload) {
2651 game_list->PopulateAsync(UISettings::values.game_dirs); 2658 game_list->PopulateAsync(UISettings::values.game_dirs);
2652 } 2659 }
2653 2660
2654 // Do not cause the global config to write local settings into the config file 2661 // Do not cause the global config to write local settings into the config file
2655 const bool is_powered_on = system.IsPoweredOn(); 2662 const bool is_powered_on = system.IsPoweredOn();
2656 Settings::RestoreGlobalState(is_powered_on); 2663 Settings::RestoreGlobalState(is_powered_on);
2657 2664
2658 if (!is_powered_on) { 2665 UISettings::values.configuration_applied = false;
2659 config->Save(); 2666
2660 } 2667 if (!is_powered_on) {
2661 } else { 2668 config->Save();
2662 Settings::RestoreGlobalState(system.IsPoweredOn());
2663 } 2669 }
2664} 2670}
2665 2671
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 5ba00b8c8..49122ec32 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -95,6 +95,8 @@ struct Values {
95 uint8_t row_2_text_id; 95 uint8_t row_2_text_id;
96 std::atomic_bool is_game_list_reload_pending{false}; 96 std::atomic_bool is_game_list_reload_pending{false};
97 bool cache_game_list; 97 bool cache_game_list;
98
99 bool configuration_applied;
98}; 100};
99 101
100extern Values values; 102extern Values values;