diff options
53 files changed, 1346 insertions, 444 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 47bd282fb..4bf55d664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -35,6 +35,8 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) | |||
| 35 | 35 | ||
| 36 | option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON) | 36 | option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON) |
| 37 | 37 | ||
| 38 | option(YUZU_TESTS "Compile tests" ON) | ||
| 39 | |||
| 38 | # Default to a Release build | 40 | # Default to a Release build |
| 39 | get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) | 41 | get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) |
| 40 | if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) | 42 | if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) |
| @@ -168,7 +170,6 @@ macro(yuzu_find_packages) | |||
| 168 | # Capitalization matters here. We need the naming to match the generated paths from Conan | 170 | # Capitalization matters here. We need the naming to match the generated paths from Conan |
| 169 | set(REQUIRED_LIBS | 171 | set(REQUIRED_LIBS |
| 170 | # Cmake Pkg Prefix Version Conan Pkg | 172 | # Cmake Pkg Prefix Version Conan Pkg |
| 171 | "Catch2 2.13.7 catch2/2.13.7" | ||
| 172 | "fmt 8.0.1 fmt/8.1.1" | 173 | "fmt 8.0.1 fmt/8.1.1" |
| 173 | "lz4 1.8 lz4/1.9.2" | 174 | "lz4 1.8 lz4/1.9.2" |
| 174 | "nlohmann_json 3.8 nlohmann_json/3.8.0" | 175 | "nlohmann_json 3.8 nlohmann_json/3.8.0" |
| @@ -177,6 +178,11 @@ macro(yuzu_find_packages) | |||
| 177 | # can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068 | 178 | # can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068 |
| 178 | #"opus 1.3 opus/1.3.1" | 179 | #"opus 1.3 opus/1.3.1" |
| 179 | ) | 180 | ) |
| 181 | if (YUZU_TESTS) | ||
| 182 | list(APPEND REQUIRED_LIBS | ||
| 183 | "Catch2 2.13.7 catch2/2.13.7" | ||
| 184 | ) | ||
| 185 | endif() | ||
| 180 | 186 | ||
| 181 | foreach(PACKAGE ${REQUIRED_LIBS}) | 187 | foreach(PACKAGE ${REQUIRED_LIBS}) |
| 182 | string(REGEX REPLACE "[ \t\r\n]+" ";" PACKAGE_SPLIT ${PACKAGE}) | 188 | string(REGEX REPLACE "[ \t\r\n]+" ";" PACKAGE_SPLIT ${PACKAGE}) |
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 158113516..82e8ef18c 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -13,10 +13,6 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) | |||
| 13 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) | 13 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) |
| 14 | endif() | 14 | endif() |
| 15 | 15 | ||
| 16 | # Catch | ||
| 17 | add_library(catch-single-include INTERFACE) | ||
| 18 | target_include_directories(catch-single-include INTERFACE catch/single_include) | ||
| 19 | |||
| 20 | # Dynarmic | 16 | # Dynarmic |
| 21 | if (ARCHITECTURE_x86_64) | 17 | if (ARCHITECTURE_x86_64) |
| 22 | set(DYNARMIC_TESTS OFF) | 18 | set(DYNARMIC_TESTS OFF) |
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19d16147d..1bdf70b76 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -151,7 +151,10 @@ add_subdirectory(audio_core) | |||
| 151 | add_subdirectory(video_core) | 151 | add_subdirectory(video_core) |
| 152 | add_subdirectory(input_common) | 152 | add_subdirectory(input_common) |
| 153 | add_subdirectory(shader_recompiler) | 153 | add_subdirectory(shader_recompiler) |
| 154 | add_subdirectory(tests) | 154 | |
| 155 | if (YUZU_TESTS) | ||
| 156 | add_subdirectory(tests) | ||
| 157 | endif() | ||
| 155 | 158 | ||
| 156 | if (ENABLE_SDL2) | 159 | if (ENABLE_SDL2) |
| 157 | add_subdirectory(yuzu_cmd) | 160 | add_subdirectory(yuzu_cmd) |
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 62010d762..81b212e4b 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp | |||
| @@ -124,7 +124,10 @@ void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) { | |||
| 124 | 124 | ||
| 125 | // "from" might no longer be valid if the thread was killed | 125 | // "from" might no longer be valid if the thread was killed |
| 126 | if (auto from = weak_from.lock()) { | 126 | if (auto from = weak_from.lock()) { |
| 127 | ASSERT(from->impl->previous_fiber != nullptr); | 127 | if (from->impl->previous_fiber == nullptr) { |
| 128 | ASSERT_MSG(false, "previous_fiber is nullptr!"); | ||
| 129 | return; | ||
| 130 | } | ||
| 128 | from->impl->previous_fiber->impl->context = transfer.fctx; | 131 | from->impl->previous_fiber->impl->context = transfer.fctx; |
| 129 | from->impl->previous_fiber->impl->guard.unlock(); | 132 | from->impl->previous_fiber->impl->guard.unlock(); |
| 130 | from->impl->previous_fiber.reset(); | 133 | from->impl->previous_fiber.reset(); |
diff --git a/src/common/input.h b/src/common/input.h index f775a4c01..f4f9eb30a 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -209,6 +209,13 @@ enum class ButtonNames { | |||
| 209 | Triangle, | 209 | Triangle, |
| 210 | Share, | 210 | Share, |
| 211 | Options, | 211 | Options, |
| 212 | |||
| 213 | // Mouse buttons | ||
| 214 | ButtonMouseWheel, | ||
| 215 | ButtonBackward, | ||
| 216 | ButtonForward, | ||
| 217 | ButtonTask, | ||
| 218 | ButtonExtra, | ||
| 212 | }; | 219 | }; |
| 213 | 220 | ||
| 214 | // Callback data consisting of an input type and the equivalent data status | 221 | // Callback data consisting of an input type and the equivalent data status |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b1a746727..6e8d11919 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -247,6 +247,9 @@ add_library(core STATIC | |||
| 247 | hle/kernel/k_trace.h | 247 | hle/kernel/k_trace.h |
| 248 | hle/kernel/k_transfer_memory.cpp | 248 | hle/kernel/k_transfer_memory.cpp |
| 249 | hle/kernel/k_transfer_memory.h | 249 | hle/kernel/k_transfer_memory.h |
| 250 | hle/kernel/k_worker_task.h | ||
| 251 | hle/kernel/k_worker_task_manager.cpp | ||
| 252 | hle/kernel/k_worker_task_manager.h | ||
| 250 | hle/kernel/k_writable_event.cpp | 253 | hle/kernel/k_writable_event.cpp |
| 251 | hle/kernel/k_writable_event.h | 254 | hle/kernel/k_writable_event.h |
| 252 | hle/kernel/kernel.cpp | 255 | hle/kernel/kernel.cpp |
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index 08f8af551..eef0ff493 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp | |||
| @@ -158,7 +158,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { | |||
| 158 | auto& motion = console.motion_state; | 158 | auto& motion = console.motion_state; |
| 159 | motion.accel = emulated.GetAcceleration(); | 159 | motion.accel = emulated.GetAcceleration(); |
| 160 | motion.gyro = emulated.GetGyroscope(); | 160 | motion.gyro = emulated.GetGyroscope(); |
| 161 | motion.rotation = emulated.GetGyroscope(); | 161 | motion.rotation = emulated.GetRotations(); |
| 162 | motion.orientation = emulated.GetOrientation(); | 162 | motion.orientation = emulated.GetOrientation(); |
| 163 | motion.quaternion = emulated.GetQuaternion(); | 163 | motion.quaternion = emulated.GetQuaternion(); |
| 164 | motion.gyro_bias = emulated.GetGyroBias(); | 164 | motion.gyro_bias = emulated.GetGyroBias(); |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 52a56ef1a..d12037b11 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -145,7 +145,7 @@ void EmulatedController::LoadDevices() { | |||
| 145 | motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | 145 | motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); |
| 146 | std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(), | 146 | std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(), |
| 147 | Common::Input::CreateDevice<Common::Input::InputDevice>); | 147 | Common::Input::CreateDevice<Common::Input::InputDevice>); |
| 148 | std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(), | 148 | std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), |
| 149 | Common::Input::CreateDevice<Common::Input::InputDevice>); | 149 | Common::Input::CreateDevice<Common::Input::InputDevice>); |
| 150 | std::transform(output_params.begin(), output_params.end(), output_devices.begin(), | 150 | std::transform(output_params.begin(), output_params.end(), output_devices.begin(), |
| 151 | Common::Input::CreateDevice<Common::Input::OutputDevice>); | 151 | Common::Input::CreateDevice<Common::Input::OutputDevice>); |
| @@ -351,6 +351,19 @@ void EmulatedController::DisableConfiguration() { | |||
| 351 | } | 351 | } |
| 352 | } | 352 | } |
| 353 | 353 | ||
| 354 | void EmulatedController::EnableSystemButtons() { | ||
| 355 | system_buttons_enabled = true; | ||
| 356 | } | ||
| 357 | |||
| 358 | void EmulatedController::DisableSystemButtons() { | ||
| 359 | system_buttons_enabled = false; | ||
| 360 | } | ||
| 361 | |||
| 362 | void EmulatedController::ResetSystemButtons() { | ||
| 363 | controller.home_button_state.home.Assign(false); | ||
| 364 | controller.capture_button_state.capture.Assign(false); | ||
| 365 | } | ||
| 366 | |||
| 354 | bool EmulatedController::IsConfiguring() const { | 367 | bool EmulatedController::IsConfiguring() const { |
| 355 | return is_configuring; | 368 | return is_configuring; |
| 356 | } | 369 | } |
| @@ -600,7 +613,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback | |||
| 600 | controller.npad_button_state.right_sr.Assign(current_status.value); | 613 | controller.npad_button_state.right_sr.Assign(current_status.value); |
| 601 | break; | 614 | break; |
| 602 | case Settings::NativeButton::Home: | 615 | case Settings::NativeButton::Home: |
| 616 | if (!system_buttons_enabled) { | ||
| 617 | break; | ||
| 618 | } | ||
| 619 | controller.home_button_state.home.Assign(current_status.value); | ||
| 620 | break; | ||
| 603 | case Settings::NativeButton::Screenshot: | 621 | case Settings::NativeButton::Screenshot: |
| 622 | if (!system_buttons_enabled) { | ||
| 623 | break; | ||
| 624 | } | ||
| 625 | controller.capture_button_state.capture.Assign(current_status.value); | ||
| 604 | break; | 626 | break; |
| 605 | } | 627 | } |
| 606 | } | 628 | } |
| @@ -1081,6 +1103,20 @@ BatteryValues EmulatedController::GetBatteryValues() const { | |||
| 1081 | return controller.battery_values; | 1103 | return controller.battery_values; |
| 1082 | } | 1104 | } |
| 1083 | 1105 | ||
| 1106 | HomeButtonState EmulatedController::GetHomeButtons() const { | ||
| 1107 | if (is_configuring) { | ||
| 1108 | return {}; | ||
| 1109 | } | ||
| 1110 | return controller.home_button_state; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | CaptureButtonState EmulatedController::GetCaptureButtons() const { | ||
| 1114 | if (is_configuring) { | ||
| 1115 | return {}; | ||
| 1116 | } | ||
| 1117 | return controller.capture_button_state; | ||
| 1118 | } | ||
| 1119 | |||
| 1084 | NpadButtonState EmulatedController::GetNpadButtons() const { | 1120 | NpadButtonState EmulatedController::GetNpadButtons() const { |
| 1085 | if (is_configuring) { | 1121 | if (is_configuring) { |
| 1086 | return {}; | 1122 | return {}; |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index c0994ab4d..a63a83cce 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -101,6 +101,8 @@ struct ControllerStatus { | |||
| 101 | VibrationValues vibration_values{}; | 101 | VibrationValues vibration_values{}; |
| 102 | 102 | ||
| 103 | // Data for HID serices | 103 | // Data for HID serices |
| 104 | HomeButtonState home_button_state{}; | ||
| 105 | CaptureButtonState capture_button_state{}; | ||
| 104 | NpadButtonState npad_button_state{}; | 106 | NpadButtonState npad_button_state{}; |
| 105 | DebugPadButton debug_pad_button_state{}; | 107 | DebugPadButton debug_pad_button_state{}; |
| 106 | AnalogSticks analog_stick_state{}; | 108 | AnalogSticks analog_stick_state{}; |
| @@ -198,6 +200,15 @@ public: | |||
| 198 | /// Returns the emulated controller into normal mode, allowing the modification of the HID state | 200 | /// Returns the emulated controller into normal mode, allowing the modification of the HID state |
| 199 | void DisableConfiguration(); | 201 | void DisableConfiguration(); |
| 200 | 202 | ||
| 203 | /// Enables Home and Screenshot buttons | ||
| 204 | void EnableSystemButtons(); | ||
| 205 | |||
| 206 | /// Disables Home and Screenshot buttons | ||
| 207 | void DisableSystemButtons(); | ||
| 208 | |||
| 209 | /// Sets Home and Screenshot buttons to false | ||
| 210 | void ResetSystemButtons(); | ||
| 211 | |||
| 201 | /// Returns true if the emulated controller is in configuring mode | 212 | /// Returns true if the emulated controller is in configuring mode |
| 202 | bool IsConfiguring() const; | 213 | bool IsConfiguring() const; |
| 203 | 214 | ||
| @@ -261,7 +272,13 @@ public: | |||
| 261 | /// Returns the latest battery status from the controller with parameters | 272 | /// Returns the latest battery status from the controller with parameters |
| 262 | BatteryValues GetBatteryValues() const; | 273 | BatteryValues GetBatteryValues() const; |
| 263 | 274 | ||
| 264 | /// Returns the latest status of button input for the npad service | 275 | /// Returns the latest status of button input for the hid::HomeButton service |
| 276 | HomeButtonState GetHomeButtons() const; | ||
| 277 | |||
| 278 | /// Returns the latest status of button input for the hid::CaptureButton service | ||
| 279 | CaptureButtonState GetCaptureButtons() const; | ||
| 280 | |||
| 281 | /// Returns the latest status of button input for the hid::Npad service | ||
| 265 | NpadButtonState GetNpadButtons() const; | 282 | NpadButtonState GetNpadButtons() const; |
| 266 | 283 | ||
| 267 | /// Returns the latest status of button input for the debug pad service | 284 | /// Returns the latest status of button input for the debug pad service |
| @@ -383,6 +400,7 @@ private: | |||
| 383 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; | 400 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; |
| 384 | bool is_connected{false}; | 401 | bool is_connected{false}; |
| 385 | bool is_configuring{false}; | 402 | bool is_configuring{false}; |
| 403 | bool system_buttons_enabled{true}; | ||
| 386 | f32 motion_sensitivity{0.01f}; | 404 | f32 motion_sensitivity{0.01f}; |
| 387 | bool force_update_motion{false}; | 405 | bool force_update_motion{false}; |
| 388 | 406 | ||
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 4eca68533..778b328b9 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h | |||
| @@ -378,6 +378,26 @@ struct LedPattern { | |||
| 378 | }; | 378 | }; |
| 379 | }; | 379 | }; |
| 380 | 380 | ||
| 381 | struct HomeButtonState { | ||
| 382 | union { | ||
| 383 | u64 raw{}; | ||
| 384 | |||
| 385 | // Buttons | ||
| 386 | BitField<0, 1, u64> home; | ||
| 387 | }; | ||
| 388 | }; | ||
| 389 | static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size."); | ||
| 390 | |||
| 391 | struct CaptureButtonState { | ||
| 392 | union { | ||
| 393 | u64 raw{}; | ||
| 394 | |||
| 395 | // Buttons | ||
| 396 | BitField<0, 1, u64> capture; | ||
| 397 | }; | ||
| 398 | }; | ||
| 399 | static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size."); | ||
| 400 | |||
| 381 | struct NpadButtonState { | 401 | struct NpadButtonState { |
| 382 | union { | 402 | union { |
| 383 | NpadButton raw{}; | 403 | NpadButton raw{}; |
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index f5acff6e0..860aab400 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp | |||
| @@ -114,7 +114,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu | |||
| 114 | if (TransformToButton(callback).value) { | 114 | if (TransformToButton(callback).value) { |
| 115 | std::random_device device; | 115 | std::random_device device; |
| 116 | std::mt19937 gen(device()); | 116 | std::mt19937 gen(device()); |
| 117 | std::uniform_int_distribution<s16> distribution(-1000, 1000); | 117 | std::uniform_int_distribution<s16> distribution(-5000, 5000); |
| 118 | status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | 118 | status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; |
| 119 | status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | 119 | status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; |
| 120 | status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | 120 | status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; |
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp index 43152492e..6e126be19 100644 --- a/src/core/hid/motion_input.cpp +++ b/src/core/hid/motion_input.cpp | |||
| @@ -10,7 +10,7 @@ namespace Core::HID { | |||
| 10 | MotionInput::MotionInput() { | 10 | MotionInput::MotionInput() { |
| 11 | // Initialize PID constants with default values | 11 | // Initialize PID constants with default values |
| 12 | SetPID(0.3f, 0.005f, 0.0f); | 12 | SetPID(0.3f, 0.005f, 0.0f); |
| 13 | SetGyroThreshold(0.001f); | 13 | SetGyroThreshold(0.00005f); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { | 16 | void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { |
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 9e51c33ce..dcca47f1b 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h | |||
| @@ -70,12 +70,12 @@ enum class KMemoryState : u32 { | |||
| 70 | ThreadLocal = | 70 | ThreadLocal = |
| 71 | static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, | 71 | static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, |
| 72 | 72 | ||
| 73 | Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | | 73 | Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | |
| 74 | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | | 74 | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | |
| 75 | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 75 | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| 76 | 76 | ||
| 77 | SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | | 77 | SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | |
| 78 | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 78 | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| 79 | 79 | ||
| 80 | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | | 80 | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | |
| 81 | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 81 | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| @@ -93,6 +93,8 @@ enum class KMemoryState : u32 { | |||
| 93 | GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | | 93 | GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | |
| 94 | FlagReferenceCounted | FlagCanDebug, | 94 | FlagReferenceCounted | FlagCanDebug, |
| 95 | CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, | 95 | CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, |
| 96 | |||
| 97 | Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped, | ||
| 96 | }; | 98 | }; |
| 97 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryState); | 99 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryState); |
| 98 | 100 | ||
| @@ -108,8 +110,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09); | |||
| 108 | static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A); | 110 | static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A); |
| 109 | static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B); | 111 | static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B); |
| 110 | static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C); | 112 | static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C); |
| 111 | static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D); | 113 | static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D); |
| 112 | static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E); | 114 | static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E); |
| 113 | static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F); | 115 | static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F); |
| 114 | static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); | 116 | static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); |
| 115 | static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811); | 117 | static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811); |
| @@ -117,6 +119,7 @@ static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812); | |||
| 117 | static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013); | 119 | static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013); |
| 118 | static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214); | 120 | static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214); |
| 119 | static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015); | 121 | static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015); |
| 122 | static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016); | ||
| 120 | 123 | ||
| 121 | enum class KMemoryPermission : u8 { | 124 | enum class KMemoryPermission : u8 { |
| 122 | None = 0, | 125 | None = 0, |
| @@ -155,7 +158,13 @@ enum class KMemoryPermission : u8 { | |||
| 155 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); | 158 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); |
| 156 | 159 | ||
| 157 | constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) { | 160 | constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) { |
| 158 | return static_cast<KMemoryPermission>(perm); | 161 | return static_cast<KMemoryPermission>( |
| 162 | (static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) | | ||
| 163 | KMemoryPermission::KernelRead | | ||
| 164 | ((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite) | ||
| 165 | << KMemoryPermission::KernelShift) | | ||
| 166 | (perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped | ||
| 167 | : KMemoryPermission::None)); | ||
| 159 | } | 168 | } |
| 160 | 169 | ||
| 161 | enum class KMemoryAttribute : u8 { | 170 | enum class KMemoryAttribute : u8 { |
| @@ -169,6 +178,8 @@ enum class KMemoryAttribute : u8 { | |||
| 169 | DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared), | 178 | DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared), |
| 170 | Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), | 179 | Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), |
| 171 | 180 | ||
| 181 | SetMask = Uncached, | ||
| 182 | |||
| 172 | IpcAndDeviceMapped = IpcLocked | DeviceShared, | 183 | IpcAndDeviceMapped = IpcLocked | DeviceShared, |
| 173 | LockedAndIpcLocked = Locked | IpcLocked, | 184 | LockedAndIpcLocked = Locked | IpcLocked, |
| 174 | DeviceSharedAndUncached = DeviceShared | Uncached | 185 | DeviceSharedAndUncached = DeviceShared | Uncached |
| @@ -215,6 +226,15 @@ struct KMemoryInfo { | |||
| 215 | constexpr VAddr GetLastAddress() const { | 226 | constexpr VAddr GetLastAddress() const { |
| 216 | return GetEndAddress() - 1; | 227 | return GetEndAddress() - 1; |
| 217 | } | 228 | } |
| 229 | constexpr KMemoryState GetState() const { | ||
| 230 | return state; | ||
| 231 | } | ||
| 232 | constexpr KMemoryAttribute GetAttribute() const { | ||
| 233 | return attribute; | ||
| 234 | } | ||
| 235 | constexpr KMemoryPermission GetPermission() const { | ||
| 236 | return perm; | ||
| 237 | } | ||
| 218 | }; | 238 | }; |
| 219 | 239 | ||
| 220 | class KMemoryBlock final { | 240 | class KMemoryBlock final { |
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 0166df0a5..1b44541b1 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp | |||
| @@ -8,12 +8,16 @@ | |||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/scope_exit.h" | 10 | #include "common/scope_exit.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "core/device_memory.h" | ||
| 11 | #include "core/hle/kernel/k_memory_manager.h" | 13 | #include "core/hle/kernel/k_memory_manager.h" |
| 12 | #include "core/hle/kernel/k_page_linked_list.h" | 14 | #include "core/hle/kernel/k_page_linked_list.h" |
| 13 | #include "core/hle/kernel/svc_results.h" | 15 | #include "core/hle/kernel/svc_results.h" |
| 14 | 16 | ||
| 15 | namespace Kernel { | 17 | namespace Kernel { |
| 16 | 18 | ||
| 19 | KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {} | ||
| 20 | |||
| 17 | std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { | 21 | std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { |
| 18 | const auto size{end_address - start_address}; | 22 | const auto size{end_address - start_address}; |
| 19 | 23 | ||
| @@ -81,7 +85,7 @@ VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size | |||
| 81 | } | 85 | } |
| 82 | 86 | ||
| 83 | ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, | 87 | ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, |
| 84 | Direction dir) { | 88 | Direction dir, u32 heap_fill_value) { |
| 85 | ASSERT(page_list.GetNumPages() == 0); | 89 | ASSERT(page_list.GetNumPages() == 0); |
| 86 | 90 | ||
| 87 | // Early return if we're allocating no pages | 91 | // Early return if we're allocating no pages |
| @@ -139,6 +143,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_ | |||
| 139 | } | 143 | } |
| 140 | } | 144 | } |
| 141 | 145 | ||
| 146 | // Clear allocated memory. | ||
| 147 | for (const auto& it : page_list.Nodes()) { | ||
| 148 | std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, | ||
| 149 | it.GetSize()); | ||
| 150 | } | ||
| 151 | |||
| 142 | // Only succeed if we allocated as many pages as we wanted | 152 | // Only succeed if we allocated as many pages as we wanted |
| 143 | if (num_pages) { | 153 | if (num_pages) { |
| 144 | return ResultOutOfMemory; | 154 | return ResultOutOfMemory; |
| @@ -146,11 +156,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_ | |||
| 146 | 156 | ||
| 147 | // We succeeded! | 157 | // We succeeded! |
| 148 | group_guard.Cancel(); | 158 | group_guard.Cancel(); |
| 159 | |||
| 149 | return ResultSuccess; | 160 | return ResultSuccess; |
| 150 | } | 161 | } |
| 151 | 162 | ||
| 152 | ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, | 163 | ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, |
| 153 | Direction dir) { | 164 | Direction dir, u32 heap_fill_value) { |
| 154 | // Early return if we're freeing no pages | 165 | // Early return if we're freeing no pages |
| 155 | if (!num_pages) { | 166 | if (!num_pages) { |
| 156 | return ResultSuccess; | 167 | return ResultSuccess; |
diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index 39badc5f1..abd6c8ace 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | #include "core/hle/kernel/k_page_heap.h" | 12 | #include "core/hle/kernel/k_page_heap.h" |
| 13 | #include "core/hle/result.h" | 13 | #include "core/hle/result.h" |
| 14 | 14 | ||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Kernel { | 19 | namespace Kernel { |
| 16 | 20 | ||
| 17 | class KPageLinkedList; | 21 | class KPageLinkedList; |
| @@ -42,7 +46,7 @@ public: | |||
| 42 | Mask = (0xF << Shift), | 46 | Mask = (0xF << Shift), |
| 43 | }; | 47 | }; |
| 44 | 48 | ||
| 45 | KMemoryManager() = default; | 49 | explicit KMemoryManager(Core::System& system_); |
| 46 | 50 | ||
| 47 | constexpr std::size_t GetSize(Pool pool) const { | 51 | constexpr std::size_t GetSize(Pool pool) const { |
| 48 | return managers[static_cast<std::size_t>(pool)].GetSize(); | 52 | return managers[static_cast<std::size_t>(pool)].GetSize(); |
| @@ -51,10 +55,10 @@ public: | |||
| 51 | void InitializeManager(Pool pool, u64 start_address, u64 end_address); | 55 | void InitializeManager(Pool pool, u64 start_address, u64 end_address); |
| 52 | 56 | ||
| 53 | VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); | 57 | VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); |
| 54 | ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, | 58 | ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, |
| 55 | Direction dir = Direction::FromFront); | 59 | u32 heap_fill_value = 0); |
| 56 | ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, | 60 | ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, |
| 57 | Direction dir = Direction::FromFront); | 61 | u32 heap_fill_value = 0); |
| 58 | 62 | ||
| 59 | static constexpr std::size_t MaxManagerCount = 10; | 63 | static constexpr std::size_t MaxManagerCount = 10; |
| 60 | 64 | ||
| @@ -129,6 +133,7 @@ private: | |||
| 129 | }; | 133 | }; |
| 130 | 134 | ||
| 131 | private: | 135 | private: |
| 136 | Core::System& system; | ||
| 132 | std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; | 137 | std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; |
| 133 | std::array<Impl, MaxManagerCount> managers; | 138 | std::array<Impl, MaxManagerCount> managers; |
| 134 | }; | 139 | }; |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 4da509224..b650ea31d 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -289,8 +289,8 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory | |||
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | KPageLinkedList page_linked_list; | 291 | KPageLinkedList page_linked_list; |
| 292 | CASCADE_CODE( | 292 | CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool, |
| 293 | system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool)); | 293 | allocation_option)); |
| 294 | CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); | 294 | CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); |
| 295 | 295 | ||
| 296 | block_manager->Update(addr, num_pages, state, perm); | 296 | block_manager->Update(addr, num_pages, state, perm); |
| @@ -298,16 +298,16 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory | |||
| 298 | return ResultSuccess; | 298 | return ResultSuccess; |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 301 | ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { |
| 302 | std::lock_guard lock{page_table_lock}; | 302 | std::lock_guard lock{page_table_lock}; |
| 303 | 303 | ||
| 304 | const std::size_t num_pages{size / PageSize}; | 304 | const std::size_t num_pages{size / PageSize}; |
| 305 | 305 | ||
| 306 | KMemoryState state{}; | 306 | KMemoryState state{}; |
| 307 | KMemoryPermission perm{}; | 307 | KMemoryPermission perm{}; |
| 308 | CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All, | 308 | CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, |
| 309 | KMemoryState::Normal, KMemoryPermission::All, | 309 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, |
| 310 | KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, | 310 | KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, |
| 311 | KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | 311 | KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); |
| 312 | 312 | ||
| 313 | if (IsRegionMapped(dst_addr, size)) { | 313 | if (IsRegionMapped(dst_addr, size)) { |
| @@ -335,7 +335,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std: | |||
| 335 | return ResultSuccess; | 335 | return ResultSuccess; |
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 338 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { |
| 339 | std::lock_guard lock{page_table_lock}; | 339 | std::lock_guard lock{page_table_lock}; |
| 340 | 340 | ||
| 341 | if (!size) { | 341 | if (!size) { |
| @@ -344,14 +344,14 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st | |||
| 344 | 344 | ||
| 345 | const std::size_t num_pages{size / PageSize}; | 345 | const std::size_t num_pages{size / PageSize}; |
| 346 | 346 | ||
| 347 | CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, KMemoryState::All, | 347 | CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, |
| 348 | KMemoryState::Normal, KMemoryPermission::None, | 348 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, |
| 349 | KMemoryPermission::None, KMemoryAttribute::Mask, | 349 | KMemoryPermission::None, KMemoryAttribute::Mask, |
| 350 | KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); | 350 | KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); |
| 351 | 351 | ||
| 352 | KMemoryState state{}; | 352 | KMemoryState state{}; |
| 353 | CASCADE_CODE(CheckMemoryState( | 353 | CASCADE_CODE(CheckMemoryState( |
| 354 | &state, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, | 354 | &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, |
| 355 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | 355 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, |
| 356 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | 356 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); |
| 357 | CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, | 357 | CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, |
| @@ -361,7 +361,7 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st | |||
| 361 | 361 | ||
| 362 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free); | 362 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free); |
| 363 | block_manager->Update(src_addr, num_pages, KMemoryState::Normal, | 363 | block_manager->Update(src_addr, num_pages, KMemoryState::Normal, |
| 364 | KMemoryPermission::ReadAndWrite); | 364 | KMemoryPermission::UserReadWrite); |
| 365 | 365 | ||
| 366 | system.InvalidateCpuInstructionCacheRange(dst_addr, size); | 366 | system.InvalidateCpuInstructionCacheRange(dst_addr, size); |
| 367 | 367 | ||
| @@ -416,7 +416,7 @@ void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr star | |||
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; | 418 | const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; |
| 419 | Operate(dst_addr, num_pages, KMemoryPermission::ReadAndWrite, OperationType::Map, | 419 | Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, |
| 420 | map_addr); | 420 | map_addr); |
| 421 | 421 | ||
| 422 | dst_addr += num_pages * PageSize; | 422 | dst_addr += num_pages * PageSize; |
| @@ -457,8 +457,8 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { | |||
| 457 | 457 | ||
| 458 | KPageLinkedList page_linked_list; | 458 | KPageLinkedList page_linked_list; |
| 459 | 459 | ||
| 460 | CASCADE_CODE( | 460 | CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, |
| 461 | system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool)); | 461 | memory_pool, allocation_option)); |
| 462 | 462 | ||
| 463 | // We succeeded, so commit the memory reservation. | 463 | // We succeeded, so commit the memory reservation. |
| 464 | memory_reservation.Commit(); | 464 | memory_reservation.Commit(); |
| @@ -470,7 +470,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { | |||
| 470 | const std::size_t num_pages{size / PageSize}; | 470 | const std::size_t num_pages{size / PageSize}; |
| 471 | block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, | 471 | block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, |
| 472 | KMemoryAttribute::None, KMemoryState::Normal, | 472 | KMemoryAttribute::None, KMemoryState::Normal, |
| 473 | KMemoryPermission::ReadAndWrite, KMemoryAttribute::None); | 473 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None); |
| 474 | 474 | ||
| 475 | return ResultSuccess; | 475 | return ResultSuccess; |
| 476 | } | 476 | } |
| @@ -541,7 +541,8 @@ ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) { | |||
| 541 | } | 541 | } |
| 542 | 542 | ||
| 543 | const std::size_t num_pages{size / PageSize}; | 543 | const std::size_t num_pages{size / PageSize}; |
| 544 | system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool); | 544 | system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool, |
| 545 | allocation_option); | ||
| 545 | 546 | ||
| 546 | block_manager->Update(addr, num_pages, KMemoryState::Free); | 547 | block_manager->Update(addr, num_pages, KMemoryState::Free); |
| 547 | 548 | ||
| @@ -553,8 +554,8 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) { | |||
| 553 | 554 | ||
| 554 | KMemoryState src_state{}; | 555 | KMemoryState src_state{}; |
| 555 | CASCADE_CODE(CheckMemoryState( | 556 | CASCADE_CODE(CheckMemoryState( |
| 556 | &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, | 557 | &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, |
| 557 | KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite, | 558 | KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::UserReadWrite, |
| 558 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | 559 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); |
| 559 | 560 | ||
| 560 | if (IsRegionMapped(dst_addr, size)) { | 561 | if (IsRegionMapped(dst_addr, size)) { |
| @@ -568,13 +569,13 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) { | |||
| 568 | 569 | ||
| 569 | { | 570 | { |
| 570 | auto block_guard = detail::ScopeExit([&] { | 571 | auto block_guard = detail::ScopeExit([&] { |
| 571 | Operate(src_addr, num_pages, KMemoryPermission::ReadAndWrite, | 572 | Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, |
| 572 | OperationType::ChangePermissions); | 573 | OperationType::ChangePermissions); |
| 573 | }); | 574 | }); |
| 574 | 575 | ||
| 575 | CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, | 576 | CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, |
| 576 | OperationType::ChangePermissions)); | 577 | OperationType::ChangePermissions)); |
| 577 | CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::ReadAndWrite)); | 578 | CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::UserReadWrite)); |
| 578 | 579 | ||
| 579 | block_guard.Cancel(); | 580 | block_guard.Cancel(); |
| 580 | } | 581 | } |
| @@ -582,7 +583,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) { | |||
| 582 | block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None, | 583 | block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None, |
| 583 | KMemoryAttribute::Locked); | 584 | KMemoryAttribute::Locked); |
| 584 | block_manager->Update(dst_addr, num_pages, KMemoryState::Stack, | 585 | block_manager->Update(dst_addr, num_pages, KMemoryState::Stack, |
| 585 | KMemoryPermission::ReadAndWrite); | 586 | KMemoryPermission::UserReadWrite); |
| 586 | 587 | ||
| 587 | return ResultSuccess; | 588 | return ResultSuccess; |
| 588 | } | 589 | } |
| @@ -592,13 +593,13 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) { | |||
| 592 | 593 | ||
| 593 | KMemoryState src_state{}; | 594 | KMemoryState src_state{}; |
| 594 | CASCADE_CODE(CheckMemoryState( | 595 | CASCADE_CODE(CheckMemoryState( |
| 595 | &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, | 596 | &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, |
| 596 | KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None, | 597 | KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None, |
| 597 | KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); | 598 | KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); |
| 598 | 599 | ||
| 599 | KMemoryPermission dst_perm{}; | 600 | KMemoryPermission dst_perm{}; |
| 600 | CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, KMemoryState::All, | 601 | CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size, |
| 601 | KMemoryState::Stack, KMemoryPermission::None, | 602 | KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, |
| 602 | KMemoryPermission::None, KMemoryAttribute::Mask, | 603 | KMemoryPermission::None, KMemoryAttribute::Mask, |
| 603 | KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | 604 | KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); |
| 604 | 605 | ||
| @@ -617,13 +618,13 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) { | |||
| 617 | auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); }); | 618 | auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); }); |
| 618 | 619 | ||
| 619 | CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); | 620 | CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); |
| 620 | CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::ReadAndWrite, | 621 | CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, |
| 621 | OperationType::ChangePermissions)); | 622 | OperationType::ChangePermissions)); |
| 622 | 623 | ||
| 623 | block_guard.Cancel(); | 624 | block_guard.Cancel(); |
| 624 | } | 625 | } |
| 625 | 626 | ||
| 626 | block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::ReadAndWrite); | 627 | block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::UserReadWrite); |
| 627 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free); | 628 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free); |
| 628 | 629 | ||
| 629 | return ResultSuccess; | 630 | return ResultSuccess; |
| @@ -713,50 +714,61 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, | |||
| 713 | } | 714 | } |
| 714 | 715 | ||
| 715 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, | 716 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 716 | KMemoryPermission perm) { | 717 | Svc::MemoryPermission svc_perm) { |
| 718 | const size_t num_pages = size / PageSize; | ||
| 717 | 719 | ||
| 720 | // Lock the table. | ||
| 718 | std::lock_guard lock{page_table_lock}; | 721 | std::lock_guard lock{page_table_lock}; |
| 719 | 722 | ||
| 720 | KMemoryState prev_state{}; | 723 | // Verify we can change the memory permission. |
| 721 | KMemoryPermission prev_perm{}; | 724 | KMemoryState old_state; |
| 722 | 725 | KMemoryPermission old_perm; | |
| 723 | CASCADE_CODE(CheckMemoryState( | 726 | size_t num_allocator_blocks; |
| 724 | &prev_state, &prev_perm, nullptr, addr, size, KMemoryState::FlagCode, | 727 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, |
| 725 | KMemoryState::FlagCode, KMemoryPermission::None, KMemoryPermission::None, | 728 | std::addressof(num_allocator_blocks), addr, size, |
| 726 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | 729 | KMemoryState::FlagCode, KMemoryState::FlagCode, |
| 727 | 730 | KMemoryPermission::None, KMemoryPermission::None, | |
| 728 | KMemoryState state{prev_state}; | 731 | KMemoryAttribute::All, KMemoryAttribute::None)); |
| 729 | 732 | ||
| 730 | // Ensure state is mutable if permission allows write | 733 | // Determine new perm/state. |
| 731 | if ((perm & KMemoryPermission::Write) != KMemoryPermission::None) { | 734 | const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); |
| 732 | if (prev_state == KMemoryState::Code) { | 735 | KMemoryState new_state = old_state; |
| 733 | state = KMemoryState::CodeData; | 736 | const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite; |
| 734 | } else if (prev_state == KMemoryState::AliasCode) { | 737 | const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute; |
| 735 | state = KMemoryState::AliasCodeData; | 738 | const bool was_x = |
| 736 | } else { | 739 | (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute; |
| 740 | ASSERT(!(is_w && is_x)); | ||
| 741 | |||
| 742 | if (is_w) { | ||
| 743 | switch (old_state) { | ||
| 744 | case KMemoryState::Code: | ||
| 745 | new_state = KMemoryState::CodeData; | ||
| 746 | break; | ||
| 747 | case KMemoryState::AliasCode: | ||
| 748 | new_state = KMemoryState::AliasCodeData; | ||
| 749 | break; | ||
| 750 | default: | ||
| 737 | UNREACHABLE(); | 751 | UNREACHABLE(); |
| 738 | } | 752 | } |
| 739 | } | 753 | } |
| 740 | 754 | ||
| 741 | // Return early if there is nothing to change | 755 | // Succeed if there's nothing to do. |
| 742 | if (state == prev_state && perm == prev_perm) { | 756 | R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); |
| 743 | return ResultSuccess; | 757 | |
| 744 | } | 758 | // Perform mapping operation. |
| 759 | const auto operation = | ||
| 760 | was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions; | ||
| 761 | R_TRY(Operate(addr, num_pages, new_perm, operation)); | ||
| 745 | 762 | ||
| 746 | if ((prev_perm & KMemoryPermission::Execute) != (perm & KMemoryPermission::Execute)) { | 763 | // Update the blocks. |
| 764 | block_manager->Update(addr, num_pages, new_state, new_perm, KMemoryAttribute::None); | ||
| 765 | |||
| 766 | // Ensure cache coherency, if we're setting pages as executable. | ||
| 767 | if (is_x) { | ||
| 747 | // Memory execution state is changing, invalidate CPU cache range | 768 | // Memory execution state is changing, invalidate CPU cache range |
| 748 | system.InvalidateCpuInstructionCacheRange(addr, size); | 769 | system.InvalidateCpuInstructionCacheRange(addr, size); |
| 749 | } | 770 | } |
| 750 | 771 | ||
| 751 | const std::size_t num_pages{size / PageSize}; | ||
| 752 | const OperationType operation{(perm & KMemoryPermission::Execute) != KMemoryPermission::None | ||
| 753 | ? OperationType::ChangePermissionsAndRefresh | ||
| 754 | : OperationType::ChangePermissions}; | ||
| 755 | |||
| 756 | CASCADE_CODE(Operate(addr, num_pages, perm, operation)); | ||
| 757 | |||
| 758 | block_manager->Update(addr, num_pages, state, perm); | ||
| 759 | |||
| 760 | return ResultSuccess; | 772 | return ResultSuccess; |
| 761 | } | 773 | } |
| 762 | 774 | ||
| @@ -782,10 +794,10 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo | |||
| 782 | KMemoryAttribute attribute{}; | 794 | KMemoryAttribute attribute{}; |
| 783 | 795 | ||
| 784 | CASCADE_CODE(CheckMemoryState( | 796 | CASCADE_CODE(CheckMemoryState( |
| 785 | &state, nullptr, &attribute, addr, size, | 797 | &state, nullptr, &attribute, nullptr, addr, size, |
| 786 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, | 798 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, |
| 787 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All, | 799 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All, |
| 788 | KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None, | 800 | KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, KMemoryAttribute::None, |
| 789 | KMemoryAttribute::IpcAndDeviceMapped)); | 801 | KMemoryAttribute::IpcAndDeviceMapped)); |
| 790 | 802 | ||
| 791 | block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked); | 803 | block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked); |
| @@ -799,13 +811,13 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { | |||
| 799 | KMemoryState state{}; | 811 | KMemoryState state{}; |
| 800 | 812 | ||
| 801 | CASCADE_CODE( | 813 | CASCADE_CODE( |
| 802 | CheckMemoryState(&state, nullptr, nullptr, addr, size, | 814 | CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size, |
| 803 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, | 815 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, |
| 804 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, | 816 | KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, |
| 805 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, | 817 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, |
| 806 | KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); | 818 | KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); |
| 807 | 819 | ||
| 808 | block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite); | 820 | block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite); |
| 809 | return ResultSuccess; | 821 | return ResultSuccess; |
| 810 | } | 822 | } |
| 811 | 823 | ||
| @@ -820,7 +832,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, | |||
| 820 | KMemoryState old_state; | 832 | KMemoryState old_state; |
| 821 | KMemoryPermission old_perm; | 833 | KMemoryPermission old_perm; |
| 822 | R_TRY(this->CheckMemoryState( | 834 | R_TRY(this->CheckMemoryState( |
| 823 | std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size, | 835 | std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size, |
| 824 | KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None, | 836 | KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None, |
| 825 | KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); | 837 | KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); |
| 826 | 838 | ||
| @@ -837,24 +849,36 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, | |||
| 837 | return ResultSuccess; | 849 | return ResultSuccess; |
| 838 | } | 850 | } |
| 839 | 851 | ||
| 840 | ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask, | 852 | ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { |
| 841 | KMemoryAttribute value) { | 853 | const size_t num_pages = size / PageSize; |
| 842 | std::lock_guard lock{page_table_lock}; | 854 | ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) == |
| 855 | KMemoryAttribute::SetMask); | ||
| 843 | 856 | ||
| 844 | KMemoryState state{}; | 857 | // Lock the table. |
| 845 | KMemoryPermission perm{}; | 858 | std::lock_guard lock{page_table_lock}; |
| 846 | KMemoryAttribute attribute{}; | ||
| 847 | 859 | ||
| 848 | CASCADE_CODE(CheckMemoryState( | 860 | // Verify we can change the memory attribute. |
| 849 | &state, &perm, &attribute, addr, size, KMemoryState::FlagCanChangeAttribute, | 861 | KMemoryState old_state; |
| 862 | KMemoryPermission old_perm; | ||
| 863 | KMemoryAttribute old_attr; | ||
| 864 | size_t num_allocator_blocks; | ||
| 865 | constexpr auto AttributeTestMask = | ||
| 866 | ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared); | ||
| 867 | R_TRY(this->CheckMemoryState( | ||
| 868 | std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), | ||
| 869 | std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute, | ||
| 850 | KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, | 870 | KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, |
| 851 | KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, | 871 | AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); |
| 852 | KMemoryAttribute::DeviceSharedAndUncached)); | 872 | |
| 873 | // Determine the new attribute. | ||
| 874 | const auto new_attr = ((old_attr & static_cast<KMemoryAttribute>(~mask)) | | ||
| 875 | static_cast<KMemoryAttribute>(attr & mask)); | ||
| 853 | 876 | ||
| 854 | attribute = attribute & ~mask; | 877 | // Perform operation. |
| 855 | attribute = attribute | (mask & value); | 878 | this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh); |
| 856 | 879 | ||
| 857 | block_manager->Update(addr, size / PageSize, state, perm, attribute); | 880 | // Update the blocks. |
| 881 | block_manager->Update(addr, num_pages, old_state, old_perm, new_attr); | ||
| 858 | 882 | ||
| 859 | return ResultSuccess; | 883 | return ResultSuccess; |
| 860 | } | 884 | } |
| @@ -894,7 +918,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { | |||
| 894 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), | 918 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), |
| 895 | heap_region_start + size, GetHeapSize() - size, | 919 | heap_region_start + size, GetHeapSize() - size, |
| 896 | KMemoryState::All, KMemoryState::Normal, | 920 | KMemoryState::All, KMemoryState::Normal, |
| 897 | KMemoryPermission::All, KMemoryPermission::ReadAndWrite, | 921 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, |
| 898 | KMemoryAttribute::All, KMemoryAttribute::None)); | 922 | KMemoryAttribute::All, KMemoryAttribute::None)); |
| 899 | 923 | ||
| 900 | // Unmap the end of the heap. | 924 | // Unmap the end of the heap. |
| @@ -937,7 +961,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { | |||
| 937 | // Allocate pages for the heap extension. | 961 | // Allocate pages for the heap extension. |
| 938 | KPageLinkedList page_linked_list; | 962 | KPageLinkedList page_linked_list; |
| 939 | R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, | 963 | R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, |
| 940 | memory_pool)); | 964 | memory_pool, allocation_option)); |
| 941 | 965 | ||
| 942 | // Map the pages. | 966 | // Map the pages. |
| 943 | { | 967 | { |
| @@ -969,7 +993,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { | |||
| 969 | 993 | ||
| 970 | // Apply the memory block update. | 994 | // Apply the memory block update. |
| 971 | block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal, | 995 | block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal, |
| 972 | KMemoryPermission::ReadAndWrite, KMemoryAttribute::None); | 996 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None); |
| 973 | 997 | ||
| 974 | // Update the current heap end. | 998 | // Update the current heap end. |
| 975 | current_heap_end = heap_region_start + size; | 999 | current_heap_end = heap_region_start + size; |
| @@ -1004,8 +1028,8 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, | |||
| 1004 | CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); | 1028 | CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); |
| 1005 | } else { | 1029 | } else { |
| 1006 | KPageLinkedList page_group; | 1030 | KPageLinkedList page_group; |
| 1007 | CASCADE_CODE( | 1031 | CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, |
| 1008 | system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool)); | 1032 | memory_pool, allocation_option)); |
| 1009 | CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); | 1033 | CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); |
| 1010 | } | 1034 | } |
| 1011 | 1035 | ||
| @@ -1019,7 +1043,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { | |||
| 1019 | 1043 | ||
| 1020 | KMemoryPermission perm{}; | 1044 | KMemoryPermission perm{}; |
| 1021 | if (const ResultCode result{CheckMemoryState( | 1045 | if (const ResultCode result{CheckMemoryState( |
| 1022 | nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, | 1046 | nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, |
| 1023 | KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, | 1047 | KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, |
| 1024 | KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, | 1048 | KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, |
| 1025 | KMemoryAttribute::DeviceSharedAndUncached)}; | 1049 | KMemoryAttribute::DeviceSharedAndUncached)}; |
| @@ -1042,7 +1066,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 1042 | 1066 | ||
| 1043 | KMemoryPermission perm{}; | 1067 | KMemoryPermission perm{}; |
| 1044 | if (const ResultCode result{CheckMemoryState( | 1068 | if (const ResultCode result{CheckMemoryState( |
| 1045 | nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, | 1069 | nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, |
| 1046 | KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, | 1070 | KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, |
| 1047 | KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, | 1071 | KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, |
| 1048 | KMemoryAttribute::DeviceSharedAndUncached)}; | 1072 | KMemoryAttribute::DeviceSharedAndUncached)}; |
| @@ -1068,7 +1092,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | |||
| 1068 | KMemoryPermission old_perm{}; | 1092 | KMemoryPermission old_perm{}; |
| 1069 | 1093 | ||
| 1070 | if (const ResultCode result{CheckMemoryState( | 1094 | if (const ResultCode result{CheckMemoryState( |
| 1071 | nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | 1095 | nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, |
| 1072 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, | 1096 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, |
| 1073 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; | 1097 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; |
| 1074 | result.IsError()) { | 1098 | result.IsError()) { |
| @@ -1095,7 +1119,7 @@ ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { | |||
| 1095 | KMemoryPermission old_perm{}; | 1119 | KMemoryPermission old_perm{}; |
| 1096 | 1120 | ||
| 1097 | if (const ResultCode result{CheckMemoryState( | 1121 | if (const ResultCode result{CheckMemoryState( |
| 1098 | nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | 1122 | nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, |
| 1099 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, | 1123 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, |
| 1100 | KMemoryAttribute::All, KMemoryAttribute::Locked)}; | 1124 | KMemoryAttribute::All, KMemoryAttribute::Locked)}; |
| 1101 | result.IsError()) { | 1125 | result.IsError()) { |
| @@ -1225,18 +1249,19 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { | |||
| 1225 | return alias_region_start; | 1249 | return alias_region_start; |
| 1226 | case KMemoryState::Stack: | 1250 | case KMemoryState::Stack: |
| 1227 | return stack_region_start; | 1251 | return stack_region_start; |
| 1228 | case KMemoryState::Io: | ||
| 1229 | case KMemoryState::Static: | 1252 | case KMemoryState::Static: |
| 1230 | case KMemoryState::ThreadLocal: | 1253 | case KMemoryState::ThreadLocal: |
| 1231 | return kernel_map_region_start; | 1254 | return kernel_map_region_start; |
| 1255 | case KMemoryState::Io: | ||
| 1232 | case KMemoryState::Shared: | 1256 | case KMemoryState::Shared: |
| 1233 | case KMemoryState::AliasCode: | 1257 | case KMemoryState::AliasCode: |
| 1234 | case KMemoryState::AliasCodeData: | 1258 | case KMemoryState::AliasCodeData: |
| 1235 | case KMemoryState::Transferred: | 1259 | case KMemoryState::Transfered: |
| 1236 | case KMemoryState::SharedTransferred: | 1260 | case KMemoryState::SharedTransfered: |
| 1237 | case KMemoryState::SharedCode: | 1261 | case KMemoryState::SharedCode: |
| 1238 | case KMemoryState::GeneratedCode: | 1262 | case KMemoryState::GeneratedCode: |
| 1239 | case KMemoryState::CodeOut: | 1263 | case KMemoryState::CodeOut: |
| 1264 | case KMemoryState::Coverage: | ||
| 1240 | return alias_code_region_start; | 1265 | return alias_code_region_start; |
| 1241 | case KMemoryState::Code: | 1266 | case KMemoryState::Code: |
| 1242 | case KMemoryState::CodeData: | 1267 | case KMemoryState::CodeData: |
| @@ -1260,18 +1285,19 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { | |||
| 1260 | return alias_region_end - alias_region_start; | 1285 | return alias_region_end - alias_region_start; |
| 1261 | case KMemoryState::Stack: | 1286 | case KMemoryState::Stack: |
| 1262 | return stack_region_end - stack_region_start; | 1287 | return stack_region_end - stack_region_start; |
| 1263 | case KMemoryState::Io: | ||
| 1264 | case KMemoryState::Static: | 1288 | case KMemoryState::Static: |
| 1265 | case KMemoryState::ThreadLocal: | 1289 | case KMemoryState::ThreadLocal: |
| 1266 | return kernel_map_region_end - kernel_map_region_start; | 1290 | return kernel_map_region_end - kernel_map_region_start; |
| 1291 | case KMemoryState::Io: | ||
| 1267 | case KMemoryState::Shared: | 1292 | case KMemoryState::Shared: |
| 1268 | case KMemoryState::AliasCode: | 1293 | case KMemoryState::AliasCode: |
| 1269 | case KMemoryState::AliasCodeData: | 1294 | case KMemoryState::AliasCodeData: |
| 1270 | case KMemoryState::Transferred: | 1295 | case KMemoryState::Transfered: |
| 1271 | case KMemoryState::SharedTransferred: | 1296 | case KMemoryState::SharedTransfered: |
| 1272 | case KMemoryState::SharedCode: | 1297 | case KMemoryState::SharedCode: |
| 1273 | case KMemoryState::GeneratedCode: | 1298 | case KMemoryState::GeneratedCode: |
| 1274 | case KMemoryState::CodeOut: | 1299 | case KMemoryState::CodeOut: |
| 1300 | case KMemoryState::Coverage: | ||
| 1275 | return alias_code_region_end - alias_code_region_start; | 1301 | return alias_code_region_end - alias_code_region_start; |
| 1276 | case KMemoryState::Code: | 1302 | case KMemoryState::Code: |
| 1277 | case KMemoryState::CodeData: | 1303 | case KMemoryState::CodeData: |
| @@ -1283,15 +1309,18 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { | |||
| 1283 | } | 1309 | } |
| 1284 | 1310 | ||
| 1285 | bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const { | 1311 | bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const { |
| 1286 | const VAddr end{addr + size}; | 1312 | const VAddr end = addr + size; |
| 1287 | const VAddr last{end - 1}; | 1313 | const VAddr last = end - 1; |
| 1288 | const VAddr region_start{GetRegionAddress(state)}; | 1314 | |
| 1289 | const std::size_t region_size{GetRegionSize(state)}; | 1315 | const VAddr region_start = this->GetRegionAddress(state); |
| 1290 | const bool is_in_region{region_start <= addr && addr < end && | 1316 | const size_t region_size = this->GetRegionSize(state); |
| 1291 | last <= region_start + region_size - 1}; | 1317 | |
| 1292 | const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)}; | 1318 | const bool is_in_region = |
| 1293 | const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)}; | 1319 | region_start <= addr && addr < end && last <= region_start + region_size - 1; |
| 1294 | 1320 | const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr || | |
| 1321 | heap_region_start == heap_region_end); | ||
| 1322 | const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr || | ||
| 1323 | alias_region_start == alias_region_end); | ||
| 1295 | switch (state) { | 1324 | switch (state) { |
| 1296 | case KMemoryState::Free: | 1325 | case KMemoryState::Free: |
| 1297 | case KMemoryState::Kernel: | 1326 | case KMemoryState::Kernel: |
| @@ -1305,11 +1334,12 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co | |||
| 1305 | case KMemoryState::AliasCodeData: | 1334 | case KMemoryState::AliasCodeData: |
| 1306 | case KMemoryState::Stack: | 1335 | case KMemoryState::Stack: |
| 1307 | case KMemoryState::ThreadLocal: | 1336 | case KMemoryState::ThreadLocal: |
| 1308 | case KMemoryState::Transferred: | 1337 | case KMemoryState::Transfered: |
| 1309 | case KMemoryState::SharedTransferred: | 1338 | case KMemoryState::SharedTransfered: |
| 1310 | case KMemoryState::SharedCode: | 1339 | case KMemoryState::SharedCode: |
| 1311 | case KMemoryState::GeneratedCode: | 1340 | case KMemoryState::GeneratedCode: |
| 1312 | case KMemoryState::CodeOut: | 1341 | case KMemoryState::CodeOut: |
| 1342 | case KMemoryState::Coverage: | ||
| 1313 | return is_in_region && !is_in_heap && !is_in_alias; | 1343 | return is_in_region && !is_in_heap && !is_in_alias; |
| 1314 | case KMemoryState::Normal: | 1344 | case KMemoryState::Normal: |
| 1315 | ASSERT(is_in_heap); | 1345 | ASSERT(is_in_heap); |
| @@ -1324,100 +1354,91 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co | |||
| 1324 | } | 1354 | } |
| 1325 | } | 1355 | } |
| 1326 | 1356 | ||
| 1327 | constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, | 1357 | ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, |
| 1328 | KMemoryState state, KMemoryPermission perm_mask, | 1358 | KMemoryState state, KMemoryPermission perm_mask, |
| 1329 | KMemoryPermission perm, | 1359 | KMemoryPermission perm, KMemoryAttribute attr_mask, |
| 1330 | KMemoryAttribute attr_mask, | 1360 | KMemoryAttribute attr) const { |
| 1331 | KMemoryAttribute attr) const { | 1361 | // Validate the states match expectation. |
| 1332 | // Validate the states match expectation | 1362 | R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory); |
| 1333 | if ((info.state & state_mask) != state) { | 1363 | R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory); |
| 1334 | return ResultInvalidCurrentMemory; | 1364 | R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory); |
| 1335 | } | ||
| 1336 | if ((info.perm & perm_mask) != perm) { | ||
| 1337 | return ResultInvalidCurrentMemory; | ||
| 1338 | } | ||
| 1339 | if ((info.attribute & attr_mask) != attr) { | ||
| 1340 | return ResultInvalidCurrentMemory; | ||
| 1341 | } | ||
| 1342 | 1365 | ||
| 1343 | return ResultSuccess; | 1366 | return ResultSuccess; |
| 1344 | } | 1367 | } |
| 1345 | 1368 | ||
| 1346 | ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | 1369 | ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, |
| 1347 | KMemoryAttribute* out_attr, VAddr addr, std::size_t size, | 1370 | std::size_t size, KMemoryState state_mask, |
| 1348 | KMemoryState state_mask, KMemoryState state, | 1371 | KMemoryState state, KMemoryPermission perm_mask, |
| 1349 | KMemoryPermission perm_mask, KMemoryPermission perm, | 1372 | KMemoryPermission perm, |
| 1350 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | 1373 | KMemoryAttribute attr_mask, |
| 1351 | KMemoryAttribute ignore_attr) { | 1374 | KMemoryAttribute attr) const { |
| 1352 | std::lock_guard lock{page_table_lock}; | 1375 | ASSERT(this->IsLockedByCurrentThread()); |
| 1353 | 1376 | ||
| 1354 | // Get information about the first block | 1377 | // Get information about the first block. |
| 1355 | const VAddr last_addr{addr + size - 1}; | 1378 | const VAddr last_addr = addr + size - 1; |
| 1356 | KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)}; | 1379 | KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); |
| 1357 | KMemoryInfo info{it->GetMemoryInfo()}; | 1380 | KMemoryInfo info = it->GetMemoryInfo(); |
| 1358 | 1381 | ||
| 1359 | // Validate all blocks in the range have correct state | 1382 | // If the start address isn't aligned, we need a block. |
| 1360 | const KMemoryState first_state{info.state}; | 1383 | const size_t blocks_for_start_align = |
| 1361 | const KMemoryPermission first_perm{info.perm}; | 1384 | (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; |
| 1362 | const KMemoryAttribute first_attr{info.attribute}; | ||
| 1363 | 1385 | ||
| 1364 | while (true) { | 1386 | while (true) { |
| 1365 | // Validate the current block | 1387 | // Validate against the provided masks. |
| 1366 | if (!(info.state == first_state)) { | 1388 | R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); |
| 1367 | return ResultInvalidCurrentMemory; | ||
| 1368 | } | ||
| 1369 | if (!(info.perm == first_perm)) { | ||
| 1370 | return ResultInvalidCurrentMemory; | ||
| 1371 | } | ||
| 1372 | if (!((info.attribute | static_cast<KMemoryAttribute>(ignore_attr)) == | ||
| 1373 | (first_attr | static_cast<KMemoryAttribute>(ignore_attr)))) { | ||
| 1374 | return ResultInvalidCurrentMemory; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | // Validate against the provided masks | ||
| 1378 | CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 1379 | 1389 | ||
| 1380 | // Break once we're done | 1390 | // Break once we're done. |
| 1381 | if (last_addr <= info.GetLastAddress()) { | 1391 | if (last_addr <= info.GetLastAddress()) { |
| 1382 | break; | 1392 | break; |
| 1383 | } | 1393 | } |
| 1384 | 1394 | ||
| 1385 | // Advance our iterator | 1395 | // Advance our iterator. |
| 1386 | it++; | 1396 | it++; |
| 1387 | ASSERT(it != block_manager->cend()); | 1397 | ASSERT(it != block_manager->cend()); |
| 1388 | info = it->GetMemoryInfo(); | 1398 | info = it->GetMemoryInfo(); |
| 1389 | } | 1399 | } |
| 1390 | 1400 | ||
| 1391 | // Write output state | 1401 | // If the end address isn't aligned, we need a block. |
| 1392 | if (out_state) { | 1402 | const size_t blocks_for_end_align = |
| 1393 | *out_state = first_state; | 1403 | (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; |
| 1394 | } | 1404 | |
| 1395 | if (out_perm) { | 1405 | if (out_blocks_needed != nullptr) { |
| 1396 | *out_perm = first_perm; | 1406 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; |
| 1397 | } | ||
| 1398 | if (out_attr) { | ||
| 1399 | *out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr); | ||
| 1400 | } | 1407 | } |
| 1401 | 1408 | ||
| 1402 | return ResultSuccess; | 1409 | return ResultSuccess; |
| 1403 | } | 1410 | } |
| 1404 | 1411 | ||
| 1405 | ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, | 1412 | ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, |
| 1406 | KMemoryState state_mask, KMemoryState state, | 1413 | KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, |
| 1407 | KMemoryPermission perm_mask, KMemoryPermission perm, | 1414 | VAddr addr, std::size_t size, KMemoryState state_mask, |
| 1408 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const { | 1415 | KMemoryState state, KMemoryPermission perm_mask, |
| 1416 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 1417 | KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { | ||
| 1418 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1419 | |||
| 1409 | // Get information about the first block. | 1420 | // Get information about the first block. |
| 1410 | const VAddr last_addr = addr + size - 1; | 1421 | const VAddr last_addr = addr + size - 1; |
| 1411 | KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)}; | 1422 | KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); |
| 1412 | KMemoryInfo info = it->GetMemoryInfo(); | 1423 | KMemoryInfo info = it->GetMemoryInfo(); |
| 1413 | 1424 | ||
| 1414 | // If the start address isn't aligned, we need a block. | 1425 | // If the start address isn't aligned, we need a block. |
| 1415 | const size_t blocks_for_start_align = | 1426 | const size_t blocks_for_start_align = |
| 1416 | (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; | 1427 | (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; |
| 1417 | 1428 | ||
| 1429 | // Validate all blocks in the range have correct state. | ||
| 1430 | const KMemoryState first_state = info.state; | ||
| 1431 | const KMemoryPermission first_perm = info.perm; | ||
| 1432 | const KMemoryAttribute first_attr = info.attribute; | ||
| 1418 | while (true) { | 1433 | while (true) { |
| 1434 | // Validate the current block. | ||
| 1435 | R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory); | ||
| 1436 | R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory); | ||
| 1437 | R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr), | ||
| 1438 | ResultInvalidCurrentMemory); | ||
| 1439 | |||
| 1419 | // Validate against the provided masks. | 1440 | // Validate against the provided masks. |
| 1420 | R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | 1441 | R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); |
| 1421 | 1442 | ||
| 1422 | // Break once we're done. | 1443 | // Break once we're done. |
| 1423 | if (last_addr <= info.GetLastAddress()) { | 1444 | if (last_addr <= info.GetLastAddress()) { |
| @@ -1426,6 +1447,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s | |||
| 1426 | 1447 | ||
| 1427 | // Advance our iterator. | 1448 | // Advance our iterator. |
| 1428 | it++; | 1449 | it++; |
| 1450 | ASSERT(it != block_manager->cend()); | ||
| 1429 | info = it->GetMemoryInfo(); | 1451 | info = it->GetMemoryInfo(); |
| 1430 | } | 1452 | } |
| 1431 | 1453 | ||
| @@ -1433,10 +1455,19 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s | |||
| 1433 | const size_t blocks_for_end_align = | 1455 | const size_t blocks_for_end_align = |
| 1434 | (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; | 1456 | (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; |
| 1435 | 1457 | ||
| 1458 | // Write output state. | ||
| 1459 | if (out_state != nullptr) { | ||
| 1460 | *out_state = first_state; | ||
| 1461 | } | ||
| 1462 | if (out_perm != nullptr) { | ||
| 1463 | *out_perm = first_perm; | ||
| 1464 | } | ||
| 1465 | if (out_attr != nullptr) { | ||
| 1466 | *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr); | ||
| 1467 | } | ||
| 1436 | if (out_blocks_needed != nullptr) { | 1468 | if (out_blocks_needed != nullptr) { |
| 1437 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; | 1469 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; |
| 1438 | } | 1470 | } |
| 1439 | |||
| 1440 | return ResultSuccess; | 1471 | return ResultSuccess; |
| 1441 | } | 1472 | } |
| 1442 | 1473 | ||
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 564410dca..f67986e91 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -31,8 +31,8 @@ public: | |||
| 31 | KMemoryManager::Pool pool); | 31 | KMemoryManager::Pool pool); |
| 32 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, | 32 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, |
| 33 | KMemoryPermission perm); | 33 | KMemoryPermission perm); |
| 34 | ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 34 | ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 35 | ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 35 | ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 36 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, | 36 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, |
| 37 | VAddr src_addr); | 37 | VAddr src_addr); |
| 38 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | 38 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); |
| @@ -43,13 +43,13 @@ public: | |||
| 43 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, | 43 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, |
| 44 | KMemoryPermission perm); | 44 | KMemoryPermission perm); |
| 45 | ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); | 45 | ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); |
| 46 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm); | 46 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 47 | Svc::MemoryPermission svc_perm); | ||
| 47 | KMemoryInfo QueryInfo(VAddr addr); | 48 | KMemoryInfo QueryInfo(VAddr addr); |
| 48 | ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); | 49 | ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); |
| 49 | ResultCode ResetTransferMemory(VAddr addr, std::size_t size); | 50 | ResultCode ResetTransferMemory(VAddr addr, std::size_t size); |
| 50 | ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); | 51 | ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); |
| 51 | ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask, | 52 | ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); |
| 52 | KMemoryAttribute value); | ||
| 53 | ResultCode SetMaxHeapSize(std::size_t size); | 53 | ResultCode SetMaxHeapSize(std::size_t size); |
| 54 | ResultCode SetHeapSize(VAddr* out, std::size_t size); | 54 | ResultCode SetHeapSize(VAddr* out, std::size_t size); |
| 55 | ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, | 55 | ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, |
| @@ -102,28 +102,50 @@ private: | |||
| 102 | constexpr VAddr GetRegionAddress(KMemoryState state) const; | 102 | constexpr VAddr GetRegionAddress(KMemoryState state) const; |
| 103 | constexpr std::size_t GetRegionSize(KMemoryState state) const; | 103 | constexpr std::size_t GetRegionSize(KMemoryState state) const; |
| 104 | 104 | ||
| 105 | constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, | 105 | ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, |
| 106 | std::size_t size, KMemoryState state_mask, | ||
| 106 | KMemoryState state, KMemoryPermission perm_mask, | 107 | KMemoryState state, KMemoryPermission perm_mask, |
| 107 | KMemoryPermission perm, KMemoryAttribute attr_mask, | 108 | KMemoryPermission perm, KMemoryAttribute attr_mask, |
| 108 | KMemoryAttribute attr) const; | 109 | KMemoryAttribute attr) const; |
| 110 | ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, | ||
| 111 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 112 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 113 | KMemoryAttribute attr) const { | ||
| 114 | return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, | ||
| 115 | perm, attr_mask, attr); | ||
| 116 | } | ||
| 117 | |||
| 118 | ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, | ||
| 119 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 120 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 121 | KMemoryAttribute attr) const; | ||
| 109 | ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | 122 | ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, |
| 110 | KMemoryAttribute* out_attr, VAddr addr, std::size_t size, | 123 | KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, |
| 124 | VAddr addr, std::size_t size, KMemoryState state_mask, | ||
| 125 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 126 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 127 | KMemoryAttribute attr, | ||
| 128 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; | ||
| 129 | ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, | ||
| 111 | KMemoryState state_mask, KMemoryState state, | 130 | KMemoryState state_mask, KMemoryState state, |
| 112 | KMemoryPermission perm_mask, KMemoryPermission perm, | 131 | KMemoryPermission perm_mask, KMemoryPermission perm, |
| 113 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | 132 | KMemoryAttribute attr_mask, KMemoryAttribute attr, |
| 114 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr); | 133 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { |
| 115 | ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, | 134 | return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, |
| 135 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); | ||
| 136 | } | ||
| 137 | ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, | ||
| 116 | KMemoryState state, KMemoryPermission perm_mask, | 138 | KMemoryState state, KMemoryPermission perm_mask, |
| 117 | KMemoryPermission perm, KMemoryAttribute attr_mask, | 139 | KMemoryPermission perm, KMemoryAttribute attr_mask, |
| 118 | KMemoryAttribute attr, | 140 | KMemoryAttribute attr, |
| 119 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) { | 141 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { |
| 120 | return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, | 142 | return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, |
| 121 | perm, attr_mask, attr, ignore_attr); | 143 | attr_mask, attr, ignore_attr); |
| 144 | } | ||
| 145 | |||
| 146 | bool IsLockedByCurrentThread() const { | ||
| 147 | return true; | ||
| 122 | } | 148 | } |
| 123 | ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, | ||
| 124 | KMemoryState state_mask, KMemoryState state, | ||
| 125 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 126 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 127 | 149 | ||
| 128 | std::recursive_mutex page_table_lock; | 150 | std::recursive_mutex page_table_lock; |
| 129 | std::unique_ptr<KMemoryBlockManager> block_manager; | 151 | std::unique_ptr<KMemoryBlockManager> block_manager; |
| @@ -281,6 +303,7 @@ private: | |||
| 281 | bool is_aslr_enabled{}; | 303 | bool is_aslr_enabled{}; |
| 282 | 304 | ||
| 283 | KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; | 305 | KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; |
| 306 | KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; | ||
| 284 | 307 | ||
| 285 | Common::PageTable page_table_impl; | 308 | Common::PageTable page_table_impl; |
| 286 | 309 | ||
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index bf98a51e2..265ac6fa1 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -149,6 +149,10 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st | |||
| 149 | return ResultSuccess; | 149 | return ResultSuccess; |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | void KProcess::DoWorkerTaskImpl() { | ||
| 153 | UNIMPLEMENTED(); | ||
| 154 | } | ||
| 155 | |||
| 152 | KResourceLimit* KProcess::GetResourceLimit() const { | 156 | KResourceLimit* KProcess::GetResourceLimit() const { |
| 153 | return resource_limit; | 157 | return resource_limit; |
| 154 | } | 158 | } |
| @@ -477,7 +481,7 @@ void KProcess::Finalize() { | |||
| 477 | } | 481 | } |
| 478 | 482 | ||
| 479 | // Perform inherited finalization. | 483 | // Perform inherited finalization. |
| 480 | KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize(); | 484 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); |
| 481 | } | 485 | } |
| 482 | 486 | ||
| 483 | /** | 487 | /** |
| @@ -509,7 +513,7 @@ VAddr KProcess::CreateTLSRegion() { | |||
| 509 | const VAddr tls_page_addr{page_table | 513 | const VAddr tls_page_addr{page_table |
| 510 | ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, | 514 | ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, |
| 511 | KMemoryState::ThreadLocal, | 515 | KMemoryState::ThreadLocal, |
| 512 | KMemoryPermission::ReadAndWrite, | 516 | KMemoryPermission::UserReadWrite, |
| 513 | tls_map_addr) | 517 | tls_map_addr) |
| 514 | .ValueOr(0)}; | 518 | .ValueOr(0)}; |
| 515 | 519 | ||
| @@ -541,16 +545,16 @@ void KProcess::FreeTLSRegion(VAddr tls_address) { | |||
| 541 | 545 | ||
| 542 | void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { | 546 | void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { |
| 543 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, | 547 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, |
| 544 | KMemoryPermission permission) { | 548 | Svc::MemoryPermission permission) { |
| 545 | page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); | 549 | page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); |
| 546 | }; | 550 | }; |
| 547 | 551 | ||
| 548 | kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), | 552 | kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), |
| 549 | code_set.memory.size()); | 553 | code_set.memory.size()); |
| 550 | 554 | ||
| 551 | ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute); | 555 | ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); |
| 552 | ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read); | 556 | ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); |
| 553 | ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite); | 557 | ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); |
| 554 | } | 558 | } |
| 555 | 559 | ||
| 556 | bool KProcess::IsSignaled() const { | 560 | bool KProcess::IsSignaled() const { |
| @@ -587,7 +591,7 @@ ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) { | |||
| 587 | CASCADE_RESULT(main_thread_stack_top, | 591 | CASCADE_RESULT(main_thread_stack_top, |
| 588 | page_table->AllocateAndMapMemory( | 592 | page_table->AllocateAndMapMemory( |
| 589 | main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, | 593 | main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, |
| 590 | KMemoryState::Stack, KMemoryPermission::ReadAndWrite)); | 594 | KMemoryState::Stack, KMemoryPermission::UserReadWrite)); |
| 591 | 595 | ||
| 592 | main_thread_stack_top += main_thread_stack_size; | 596 | main_thread_stack_top += main_thread_stack_size; |
| 593 | 597 | ||
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index e7c8b5838..c2a672021 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/kernel/k_condition_variable.h" | 15 | #include "core/hle/kernel/k_condition_variable.h" |
| 16 | #include "core/hle/kernel/k_handle_table.h" | 16 | #include "core/hle/kernel/k_handle_table.h" |
| 17 | #include "core/hle/kernel/k_synchronization_object.h" | 17 | #include "core/hle/kernel/k_synchronization_object.h" |
| 18 | #include "core/hle/kernel/k_worker_task.h" | ||
| 18 | #include "core/hle/kernel/process_capability.h" | 19 | #include "core/hle/kernel/process_capability.h" |
| 19 | #include "core/hle/kernel/slab_helpers.h" | 20 | #include "core/hle/kernel/slab_helpers.h" |
| 20 | #include "core/hle/result.h" | 21 | #include "core/hle/result.h" |
| @@ -62,8 +63,7 @@ enum class ProcessStatus { | |||
| 62 | DebugBreak, | 63 | DebugBreak, |
| 63 | }; | 64 | }; |
| 64 | 65 | ||
| 65 | class KProcess final | 66 | class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> { |
| 66 | : public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> { | ||
| 67 | KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); | 67 | KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); |
| 68 | 68 | ||
| 69 | public: | 69 | public: |
| @@ -345,6 +345,8 @@ public: | |||
| 345 | 345 | ||
| 346 | bool IsSignaled() const override; | 346 | bool IsSignaled() const override; |
| 347 | 347 | ||
| 348 | void DoWorkerTaskImpl(); | ||
| 349 | |||
| 348 | void PinCurrentThread(s32 core_id); | 350 | void PinCurrentThread(s32 core_id); |
| 349 | void UnpinCurrentThread(s32 core_id); | 351 | void UnpinCurrentThread(s32 core_id); |
| 350 | void UnpinThread(KThread* thread); | 352 | void UnpinThread(KThread* thread); |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 31cec990e..f900b2e7a 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -49,8 +49,6 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul | |||
| 49 | if (!must_context_switch || core != current_core) { | 49 | if (!must_context_switch || core != current_core) { |
| 50 | auto& phys_core = kernel.PhysicalCore(core); | 50 | auto& phys_core = kernel.PhysicalCore(core); |
| 51 | phys_core.Interrupt(); | 51 | phys_core.Interrupt(); |
| 52 | } else { | ||
| 53 | must_context_switch = true; | ||
| 54 | } | 52 | } |
| 55 | cores_pending_reschedule &= ~(1ULL << core); | 53 | cores_pending_reschedule &= ~(1ULL << core); |
| 56 | } | 54 | } |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 71e029a3f..7a5e6fc08 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include "core/hle/kernel/k_system_control.h" | 30 | #include "core/hle/kernel/k_system_control.h" |
| 31 | #include "core/hle/kernel/k_thread.h" | 31 | #include "core/hle/kernel/k_thread.h" |
| 32 | #include "core/hle/kernel/k_thread_queue.h" | 32 | #include "core/hle/kernel/k_thread_queue.h" |
| 33 | #include "core/hle/kernel/k_worker_task_manager.h" | ||
| 33 | #include "core/hle/kernel/kernel.h" | 34 | #include "core/hle/kernel/kernel.h" |
| 34 | #include "core/hle/kernel/svc_results.h" | 35 | #include "core/hle/kernel/svc_results.h" |
| 35 | #include "core/hle/kernel/time_manager.h" | 36 | #include "core/hle/kernel/time_manager.h" |
| @@ -332,7 +333,7 @@ void KThread::Finalize() { | |||
| 332 | } | 333 | } |
| 333 | 334 | ||
| 334 | // Perform inherited finalization. | 335 | // Perform inherited finalization. |
| 335 | KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>::Finalize(); | 336 | KSynchronizationObject::Finalize(); |
| 336 | } | 337 | } |
| 337 | 338 | ||
| 338 | bool KThread::IsSignaled() const { | 339 | bool KThread::IsSignaled() const { |
| @@ -376,11 +377,28 @@ void KThread::StartTermination() { | |||
| 376 | 377 | ||
| 377 | // Register terminated dpc flag. | 378 | // Register terminated dpc flag. |
| 378 | RegisterDpc(DpcFlag::Terminated); | 379 | RegisterDpc(DpcFlag::Terminated); |
| 380 | } | ||
| 381 | |||
| 382 | void KThread::FinishTermination() { | ||
| 383 | // Ensure that the thread is not executing on any core. | ||
| 384 | if (parent != nullptr) { | ||
| 385 | for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) { | ||
| 386 | KThread* core_thread{}; | ||
| 387 | do { | ||
| 388 | core_thread = kernel.Scheduler(i).GetCurrentThread(); | ||
| 389 | } while (core_thread == this); | ||
| 390 | } | ||
| 391 | } | ||
| 379 | 392 | ||
| 380 | // Close the thread. | 393 | // Close the thread. |
| 381 | this->Close(); | 394 | this->Close(); |
| 382 | } | 395 | } |
| 383 | 396 | ||
| 397 | void KThread::DoWorkerTaskImpl() { | ||
| 398 | // Finish the termination that was begun by Exit(). | ||
| 399 | this->FinishTermination(); | ||
| 400 | } | ||
| 401 | |||
| 384 | void KThread::Pin(s32 current_core) { | 402 | void KThread::Pin(s32 current_core) { |
| 385 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 403 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 386 | 404 | ||
| @@ -417,12 +435,7 @@ void KThread::Pin(s32 current_core) { | |||
| 417 | static_cast<u32>(ThreadState::SuspendShift))); | 435 | static_cast<u32>(ThreadState::SuspendShift))); |
| 418 | 436 | ||
| 419 | // Update our state. | 437 | // Update our state. |
| 420 | const ThreadState old_state = thread_state; | 438 | UpdateState(); |
| 421 | thread_state = static_cast<ThreadState>(GetSuspendFlags() | | ||
| 422 | static_cast<u32>(old_state & ThreadState::Mask)); | ||
| 423 | if (thread_state != old_state) { | ||
| 424 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | ||
| 425 | } | ||
| 426 | } | 439 | } |
| 427 | 440 | ||
| 428 | // TODO(bunnei): Update our SVC access permissions. | 441 | // TODO(bunnei): Update our SVC access permissions. |
| @@ -463,20 +476,13 @@ void KThread::Unpin() { | |||
| 463 | } | 476 | } |
| 464 | 477 | ||
| 465 | // Allow performing thread suspension (if termination hasn't been requested). | 478 | // Allow performing thread suspension (if termination hasn't been requested). |
| 466 | { | 479 | if (!IsTerminationRequested()) { |
| 467 | // Update our allow flags. | 480 | // Update our allow flags. |
| 468 | if (!IsTerminationRequested()) { | 481 | suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) + |
| 469 | suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) + | 482 | static_cast<u32>(ThreadState::SuspendShift))); |
| 470 | static_cast<u32>(ThreadState::SuspendShift))); | ||
| 471 | } | ||
| 472 | 483 | ||
| 473 | // Update our state. | 484 | // Update our state. |
| 474 | const ThreadState old_state = thread_state; | 485 | UpdateState(); |
| 475 | thread_state = static_cast<ThreadState>(GetSuspendFlags() | | ||
| 476 | static_cast<u32>(old_state & ThreadState::Mask)); | ||
| 477 | if (thread_state != old_state) { | ||
| 478 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | ||
| 479 | } | ||
| 480 | } | 486 | } |
| 481 | 487 | ||
| 482 | // TODO(bunnei): Update our SVC access permissions. | 488 | // TODO(bunnei): Update our SVC access permissions. |
| @@ -689,12 +695,7 @@ void KThread::Resume(SuspendType type) { | |||
| 689 | ~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type))); | 695 | ~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type))); |
| 690 | 696 | ||
| 691 | // Update our state. | 697 | // Update our state. |
| 692 | const ThreadState old_state = thread_state; | 698 | this->UpdateState(); |
| 693 | thread_state = static_cast<ThreadState>(GetSuspendFlags() | | ||
| 694 | static_cast<u32>(old_state & ThreadState::Mask)); | ||
| 695 | if (thread_state != old_state) { | ||
| 696 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | ||
| 697 | } | ||
| 698 | } | 699 | } |
| 699 | 700 | ||
| 700 | void KThread::WaitCancel() { | 701 | void KThread::WaitCancel() { |
| @@ -721,19 +722,22 @@ void KThread::TrySuspend() { | |||
| 721 | ASSERT(GetNumKernelWaiters() == 0); | 722 | ASSERT(GetNumKernelWaiters() == 0); |
| 722 | 723 | ||
| 723 | // Perform the suspend. | 724 | // Perform the suspend. |
| 724 | Suspend(); | 725 | this->UpdateState(); |
| 725 | } | 726 | } |
| 726 | 727 | ||
| 727 | void KThread::Suspend() { | 728 | void KThread::UpdateState() { |
| 728 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 729 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 729 | ASSERT(IsSuspendRequested()); | ||
| 730 | 730 | ||
| 731 | // Set our suspend flags in state. | 731 | // Set our suspend flags in state. |
| 732 | const auto old_state = thread_state; | 732 | const auto old_state = thread_state; |
| 733 | thread_state = static_cast<ThreadState>(GetSuspendFlags()) | (old_state & ThreadState::Mask); | 733 | const auto new_state = |
| 734 | static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); | ||
| 735 | thread_state = new_state; | ||
| 734 | 736 | ||
| 735 | // Note the state change in scheduler. | 737 | // Note the state change in scheduler. |
| 736 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | 738 | if (new_state != old_state) { |
| 739 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | ||
| 740 | } | ||
| 737 | } | 741 | } |
| 738 | 742 | ||
| 739 | void KThread::Continue() { | 743 | void KThread::Continue() { |
| @@ -998,13 +1002,16 @@ ResultCode KThread::Run() { | |||
| 998 | 1002 | ||
| 999 | // If the current thread has been asked to suspend, suspend it and retry. | 1003 | // If the current thread has been asked to suspend, suspend it and retry. |
| 1000 | if (GetCurrentThread(kernel).IsSuspended()) { | 1004 | if (GetCurrentThread(kernel).IsSuspended()) { |
| 1001 | GetCurrentThread(kernel).Suspend(); | 1005 | GetCurrentThread(kernel).UpdateState(); |
| 1002 | continue; | 1006 | continue; |
| 1003 | } | 1007 | } |
| 1004 | 1008 | ||
| 1005 | // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. | 1009 | // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. |
| 1006 | if (IsUserThread() && IsSuspended()) { | 1010 | if (KProcess* owner = this->GetOwnerProcess(); owner != nullptr) { |
| 1007 | Suspend(); | 1011 | if (IsUserThread() && IsSuspended()) { |
| 1012 | this->UpdateState(); | ||
| 1013 | } | ||
| 1014 | owner->IncrementThreadCount(); | ||
| 1008 | } | 1015 | } |
| 1009 | 1016 | ||
| 1010 | // Set our state and finish. | 1017 | // Set our state and finish. |
| @@ -1031,9 +1038,16 @@ void KThread::Exit() { | |||
| 1031 | 1038 | ||
| 1032 | // Disallow all suspension. | 1039 | // Disallow all suspension. |
| 1033 | suspend_allowed_flags = 0; | 1040 | suspend_allowed_flags = 0; |
| 1041 | this->UpdateState(); | ||
| 1042 | |||
| 1043 | // Disallow all suspension. | ||
| 1044 | suspend_allowed_flags = 0; | ||
| 1034 | 1045 | ||
| 1035 | // Start termination. | 1046 | // Start termination. |
| 1036 | StartTermination(); | 1047 | StartTermination(); |
| 1048 | |||
| 1049 | // Register the thread as a work task. | ||
| 1050 | KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); | ||
| 1037 | } | 1051 | } |
| 1038 | } | 1052 | } |
| 1039 | 1053 | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 83dfde69b..cc427f6cf 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/kernel/k_light_lock.h" | 19 | #include "core/hle/kernel/k_light_lock.h" |
| 20 | #include "core/hle/kernel/k_spin_lock.h" | 20 | #include "core/hle/kernel/k_spin_lock.h" |
| 21 | #include "core/hle/kernel/k_synchronization_object.h" | 21 | #include "core/hle/kernel/k_synchronization_object.h" |
| 22 | #include "core/hle/kernel/k_worker_task.h" | ||
| 22 | #include "core/hle/kernel/slab_helpers.h" | 23 | #include "core/hle/kernel/slab_helpers.h" |
| 23 | #include "core/hle/kernel/svc_common.h" | 24 | #include "core/hle/kernel/svc_common.h" |
| 24 | #include "core/hle/kernel/svc_types.h" | 25 | #include "core/hle/kernel/svc_types.h" |
| @@ -100,7 +101,7 @@ enum class ThreadWaitReasonForDebugging : u32 { | |||
| 100 | [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); | 101 | [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); |
| 101 | [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); | 102 | [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); |
| 102 | 103 | ||
| 103 | class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>, | 104 | class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, |
| 104 | public boost::intrusive::list_base_hook<> { | 105 | public boost::intrusive::list_base_hook<> { |
| 105 | KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); | 106 | KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); |
| 106 | 107 | ||
| @@ -192,9 +193,9 @@ public: | |||
| 192 | 193 | ||
| 193 | void TrySuspend(); | 194 | void TrySuspend(); |
| 194 | 195 | ||
| 195 | void Continue(); | 196 | void UpdateState(); |
| 196 | 197 | ||
| 197 | void Suspend(); | 198 | void Continue(); |
| 198 | 199 | ||
| 199 | constexpr void SetSyncedIndex(s32 index) { | 200 | constexpr void SetSyncedIndex(s32 index) { |
| 200 | synced_index = index; | 201 | synced_index = index; |
| @@ -385,6 +386,8 @@ public: | |||
| 385 | 386 | ||
| 386 | void OnTimer(); | 387 | void OnTimer(); |
| 387 | 388 | ||
| 389 | void DoWorkerTaskImpl(); | ||
| 390 | |||
| 388 | static void PostDestroy(uintptr_t arg); | 391 | static void PostDestroy(uintptr_t arg); |
| 389 | 392 | ||
| 390 | [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); | 393 | [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); |
| @@ -679,6 +682,8 @@ private: | |||
| 679 | 682 | ||
| 680 | void StartTermination(); | 683 | void StartTermination(); |
| 681 | 684 | ||
| 685 | void FinishTermination(); | ||
| 686 | |||
| 682 | [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, | 687 | [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, |
| 683 | s32 prio, s32 virt_core, KProcess* owner, ThreadType type); | 688 | s32 prio, s32 virt_core, KProcess* owner, ThreadType type); |
| 684 | 689 | ||
diff --git a/src/core/hle/kernel/k_worker_task.h b/src/core/hle/kernel/k_worker_task.h new file mode 100644 index 000000000..b7794c6a8 --- /dev/null +++ b/src/core/hle/kernel/k_worker_task.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // Copyright 2022 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 "core/hle/kernel/k_synchronization_object.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | class KWorkerTask : public KSynchronizationObject { | ||
| 12 | public: | ||
| 13 | explicit KWorkerTask(KernelCore& kernel_); | ||
| 14 | |||
| 15 | void DoWorkerTask(); | ||
| 16 | }; | ||
| 17 | |||
| 18 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp new file mode 100644 index 000000000..785e08111 --- /dev/null +++ b/src/core/hle/kernel/k_worker_task_manager.cpp | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_thread.h" | ||
| 8 | #include "core/hle/kernel/k_worker_task.h" | ||
| 9 | #include "core/hle/kernel/k_worker_task_manager.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | KWorkerTask::KWorkerTask(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} | ||
| 15 | |||
| 16 | void KWorkerTask::DoWorkerTask() { | ||
| 17 | if (auto* const thread = this->DynamicCast<KThread*>(); thread != nullptr) { | ||
| 18 | return thread->DoWorkerTaskImpl(); | ||
| 19 | } else { | ||
| 20 | auto* const process = this->DynamicCast<KProcess*>(); | ||
| 21 | ASSERT(process != nullptr); | ||
| 22 | |||
| 23 | return process->DoWorkerTaskImpl(); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} | ||
| 28 | |||
| 29 | void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { | ||
| 30 | ASSERT(type <= WorkerType::Count); | ||
| 31 | kernel.WorkerTaskManager().AddTask(kernel, task); | ||
| 32 | } | ||
| 33 | |||
| 34 | void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { | ||
| 35 | KScopedSchedulerLock sl(kernel); | ||
| 36 | m_waiting_thread.QueueWork([task]() { | ||
| 37 | // Do the task. | ||
| 38 | task->DoWorkerTask(); | ||
| 39 | }); | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_worker_task_manager.h b/src/core/hle/kernel/k_worker_task_manager.h new file mode 100644 index 000000000..43d1bfcec --- /dev/null +++ b/src/core/hle/kernel/k_worker_task_manager.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2022 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 "common/common_types.h" | ||
| 8 | #include "common/thread_worker.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | class KernelCore; | ||
| 13 | class KWorkerTask; | ||
| 14 | |||
| 15 | class KWorkerTaskManager final { | ||
| 16 | public: | ||
| 17 | enum class WorkerType : u32 { | ||
| 18 | Exit, | ||
| 19 | Count, | ||
| 20 | }; | ||
| 21 | |||
| 22 | KWorkerTaskManager(); | ||
| 23 | |||
| 24 | static void AddTask(KernelCore& kernel_, WorkerType type, KWorkerTask* task); | ||
| 25 | |||
| 26 | private: | ||
| 27 | void AddTask(KernelCore& kernel, KWorkerTask* task); | ||
| 28 | |||
| 29 | private: | ||
| 30 | Common::ThreadWorker m_waiting_thread; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1225e1fba..887c1fd27 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | #include "core/hle/kernel/k_shared_memory.h" | 37 | #include "core/hle/kernel/k_shared_memory.h" |
| 38 | #include "core/hle/kernel/k_slab_heap.h" | 38 | #include "core/hle/kernel/k_slab_heap.h" |
| 39 | #include "core/hle/kernel/k_thread.h" | 39 | #include "core/hle/kernel/k_thread.h" |
| 40 | #include "core/hle/kernel/k_worker_task_manager.h" | ||
| 40 | #include "core/hle/kernel/kernel.h" | 41 | #include "core/hle/kernel/kernel.h" |
| 41 | #include "core/hle/kernel/physical_core.h" | 42 | #include "core/hle/kernel/physical_core.h" |
| 42 | #include "core/hle/kernel/service_thread.h" | 43 | #include "core/hle/kernel/service_thread.h" |
| @@ -51,7 +52,8 @@ namespace Kernel { | |||
| 51 | 52 | ||
| 52 | struct KernelCore::Impl { | 53 | struct KernelCore::Impl { |
| 53 | explicit Impl(Core::System& system_, KernelCore& kernel_) | 54 | explicit Impl(Core::System& system_, KernelCore& kernel_) |
| 54 | : time_manager{system_}, object_list_container{kernel_}, system{system_} {} | 55 | : time_manager{system_}, object_list_container{kernel_}, |
| 56 | service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} | ||
| 55 | 57 | ||
| 56 | void SetMulticore(bool is_multi) { | 58 | void SetMulticore(bool is_multi) { |
| 57 | is_multicore = is_multi; | 59 | is_multicore = is_multi; |
| @@ -121,7 +123,7 @@ struct KernelCore::Impl { | |||
| 121 | object_list_container.Finalize(); | 123 | object_list_container.Finalize(); |
| 122 | 124 | ||
| 123 | // Ensures all service threads gracefully shutdown. | 125 | // Ensures all service threads gracefully shutdown. |
| 124 | service_threads.clear(); | 126 | ClearServiceThreads(); |
| 125 | 127 | ||
| 126 | next_object_id = 0; | 128 | next_object_id = 0; |
| 127 | next_kernel_process_id = KProcess::InitialKIPIDMin; | 129 | next_kernel_process_id = KProcess::InitialKIPIDMin; |
| @@ -629,7 +631,7 @@ struct KernelCore::Impl { | |||
| 629 | const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); | 631 | const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); |
| 630 | 632 | ||
| 631 | // Initialize memory managers | 633 | // Initialize memory managers |
| 632 | memory_manager = std::make_unique<KMemoryManager>(); | 634 | memory_manager = std::make_unique<KMemoryManager>(system); |
| 633 | memory_manager->InitializeManager(KMemoryManager::Pool::Application, | 635 | memory_manager->InitializeManager(KMemoryManager::Pool::Application, |
| 634 | application_pool.GetAddress(), | 636 | application_pool.GetAddress(), |
| 635 | application_pool.GetEndAddress()); | 637 | application_pool.GetEndAddress()); |
| @@ -704,6 +706,27 @@ struct KernelCore::Impl { | |||
| 704 | return port; | 706 | return port; |
| 705 | } | 707 | } |
| 706 | 708 | ||
| 709 | std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, | ||
| 710 | const std::string& name) { | ||
| 711 | auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); | ||
| 712 | |||
| 713 | service_threads_manager.QueueWork( | ||
| 714 | [this, service_thread]() { service_threads.emplace(service_thread); }); | ||
| 715 | |||
| 716 | return service_thread; | ||
| 717 | } | ||
| 718 | |||
| 719 | void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { | ||
| 720 | if (auto strong_ptr = service_thread.lock()) { | ||
| 721 | service_threads_manager.QueueWork( | ||
| 722 | [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | void ClearServiceThreads() { | ||
| 727 | service_threads_manager.QueueWork([this]() { service_threads.clear(); }); | ||
| 728 | } | ||
| 729 | |||
| 707 | std::mutex server_ports_lock; | 730 | std::mutex server_ports_lock; |
| 708 | std::mutex server_sessions_lock; | 731 | std::mutex server_sessions_lock; |
| 709 | std::mutex registered_objects_lock; | 732 | std::mutex registered_objects_lock; |
| @@ -759,6 +782,7 @@ struct KernelCore::Impl { | |||
| 759 | 782 | ||
| 760 | // Threads used for services | 783 | // Threads used for services |
| 761 | std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; | 784 | std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; |
| 785 | Common::ThreadWorker service_threads_manager; | ||
| 762 | 786 | ||
| 763 | std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; | 787 | std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; |
| 764 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; | 788 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; |
| @@ -774,6 +798,8 @@ struct KernelCore::Impl { | |||
| 774 | 798 | ||
| 775 | std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; | 799 | std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; |
| 776 | 800 | ||
| 801 | KWorkerTaskManager worker_task_manager; | ||
| 802 | |||
| 777 | // System context | 803 | // System context |
| 778 | Core::System& system; | 804 | Core::System& system; |
| 779 | }; | 805 | }; |
| @@ -1099,15 +1125,11 @@ void KernelCore::ExitSVCProfile() { | |||
| 1099 | } | 1125 | } |
| 1100 | 1126 | ||
| 1101 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { | 1127 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { |
| 1102 | auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name); | 1128 | return impl->CreateServiceThread(*this, name); |
| 1103 | impl->service_threads.emplace(service_thread); | ||
| 1104 | return service_thread; | ||
| 1105 | } | 1129 | } |
| 1106 | 1130 | ||
| 1107 | void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { | 1131 | void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { |
| 1108 | if (auto strong_ptr = service_thread.lock()) { | 1132 | impl->ReleaseServiceThread(service_thread); |
| 1109 | impl->service_threads.erase(strong_ptr); | ||
| 1110 | } | ||
| 1111 | } | 1133 | } |
| 1112 | 1134 | ||
| 1113 | Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() { | 1135 | Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() { |
| @@ -1118,6 +1140,14 @@ const Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() const { | |||
| 1118 | return impl->slab_resource_counts; | 1140 | return impl->slab_resource_counts; |
| 1119 | } | 1141 | } |
| 1120 | 1142 | ||
| 1143 | KWorkerTaskManager& KernelCore::WorkerTaskManager() { | ||
| 1144 | return impl->worker_task_manager; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | const KWorkerTaskManager& KernelCore::WorkerTaskManager() const { | ||
| 1148 | return impl->worker_task_manager; | ||
| 1149 | } | ||
| 1150 | |||
| 1121 | bool KernelCore::IsPhantomModeForSingleCore() const { | 1151 | bool KernelCore::IsPhantomModeForSingleCore() const { |
| 1122 | return impl->IsPhantomModeForSingleCore(); | 1152 | return impl->IsPhantomModeForSingleCore(); |
| 1123 | } | 1153 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index b9b423908..0e04fc3bb 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -52,6 +52,7 @@ class KSharedMemory; | |||
| 52 | class KSharedMemoryInfo; | 52 | class KSharedMemoryInfo; |
| 53 | class KThread; | 53 | class KThread; |
| 54 | class KTransferMemory; | 54 | class KTransferMemory; |
| 55 | class KWorkerTaskManager; | ||
| 55 | class KWritableEvent; | 56 | class KWritableEvent; |
| 56 | class KCodeMemory; | 57 | class KCodeMemory; |
| 57 | class PhysicalCore; | 58 | class PhysicalCore; |
| @@ -343,6 +344,12 @@ public: | |||
| 343 | /// Gets the current slab resource counts. | 344 | /// Gets the current slab resource counts. |
| 344 | const Init::KSlabResourceCounts& SlabResourceCounts() const; | 345 | const Init::KSlabResourceCounts& SlabResourceCounts() const; |
| 345 | 346 | ||
| 347 | /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. | ||
| 348 | KWorkerTaskManager& WorkerTaskManager(); | ||
| 349 | |||
| 350 | /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. | ||
| 351 | const KWorkerTaskManager& WorkerTaskManager() const; | ||
| 352 | |||
| 346 | private: | 353 | private: |
| 347 | friend class KProcess; | 354 | friend class KProcess; |
| 348 | friend class KThread; | 355 | friend class KThread; |
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 7f02d9471..7477668e4 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -16,17 +16,25 @@ namespace Kernel { | |||
| 16 | PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, | 16 | PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, |
| 17 | Core::CPUInterrupts& interrupts_) | 17 | Core::CPUInterrupts& interrupts_) |
| 18 | : core_index{core_index_}, system{system_}, scheduler{scheduler_}, | 18 | : core_index{core_index_}, system{system_}, scheduler{scheduler_}, |
| 19 | interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {} | 19 | interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { |
| 20 | #ifdef ARCHITECTURE_x86_64 | ||
| 21 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with | ||
| 22 | // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. | ||
| 23 | auto& kernel = system.Kernel(); | ||
| 24 | arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( | ||
| 25 | system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); | ||
| 26 | #else | ||
| 27 | #error Platform not supported yet. | ||
| 28 | #endif | ||
| 29 | } | ||
| 20 | 30 | ||
| 21 | PhysicalCore::~PhysicalCore() = default; | 31 | PhysicalCore::~PhysicalCore() = default; |
| 22 | 32 | ||
| 23 | void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { | 33 | void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { |
| 24 | #ifdef ARCHITECTURE_x86_64 | 34 | #ifdef ARCHITECTURE_x86_64 |
| 25 | auto& kernel = system.Kernel(); | 35 | auto& kernel = system.Kernel(); |
| 26 | if (is_64_bit) { | 36 | if (!is_64_bit) { |
| 27 | arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( | 37 | // We already initialized a 64-bit core, replace with a 32-bit one. |
| 28 | system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); | ||
| 29 | } else { | ||
| 30 | arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( | 38 | arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( |
| 31 | system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); | 39 | system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); |
| 32 | } | 40 | } |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 250ef9042..c7f5140f4 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -168,6 +168,9 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { | |||
| 168 | 168 | ||
| 169 | static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size, | 169 | static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size, |
| 170 | MemoryPermission perm) { | 170 | MemoryPermission perm) { |
| 171 | LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, | ||
| 172 | perm); | ||
| 173 | |||
| 171 | // Validate address / size. | 174 | // Validate address / size. |
| 172 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | 175 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); |
| 173 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | 176 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); |
| @@ -186,46 +189,33 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s | |||
| 186 | } | 189 | } |
| 187 | 190 | ||
| 188 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, | 191 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, |
| 189 | u32 attribute) { | 192 | u32 attr) { |
| 190 | LOG_DEBUG(Kernel_SVC, | 193 | LOG_DEBUG(Kernel_SVC, |
| 191 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | 194 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, |
| 192 | size, mask, attribute); | 195 | size, mask, attr); |
| 193 | |||
| 194 | if (!Common::Is4KBAligned(address)) { | ||
| 195 | LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address); | ||
| 196 | return ResultInvalidAddress; | ||
| 197 | } | ||
| 198 | 196 | ||
| 199 | if (size == 0 || !Common::Is4KBAligned(size)) { | 197 | // Validate address / size. |
| 200 | LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.", | 198 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); |
| 201 | size); | 199 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); |
| 202 | return ResultInvalidAddress; | 200 | R_UNLESS(size > 0, ResultInvalidSize); |
| 203 | } | 201 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); |
| 204 | |||
| 205 | if (!IsValidAddressRange(address, size)) { | ||
| 206 | LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})", | ||
| 207 | address, size); | ||
| 208 | return ResultInvalidCurrentMemory; | ||
| 209 | } | ||
| 210 | 202 | ||
| 211 | const auto attributes{static_cast<MemoryAttribute>(mask | attribute)}; | 203 | // Validate the attribute and mask. |
| 212 | if (attributes != static_cast<MemoryAttribute>(mask) || | 204 | constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); |
| 213 | (attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) { | 205 | R_UNLESS((mask | attr) == mask, ResultInvalidCombination); |
| 214 | LOG_ERROR(Kernel_SVC, | 206 | R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); |
| 215 | "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}", | ||
| 216 | attribute, mask); | ||
| 217 | return ResultInvalidCombination; | ||
| 218 | } | ||
| 219 | 207 | ||
| 208 | // Validate that the region is in range for the current process. | ||
| 220 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | 209 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; |
| 210 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 221 | 211 | ||
| 222 | return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask), | 212 | // Set the memory attribute. |
| 223 | static_cast<KMemoryAttribute>(attribute)); | 213 | return page_table.SetMemoryAttribute(address, size, mask, attr); |
| 224 | } | 214 | } |
| 225 | 215 | ||
| 226 | static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, | 216 | static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, |
| 227 | u32 attribute) { | 217 | u32 attr) { |
| 228 | return SetMemoryAttribute(system, address, size, mask, attribute); | 218 | return SetMemoryAttribute(system, address, size, mask, attr); |
| 229 | } | 219 | } |
| 230 | 220 | ||
| 231 | /// Maps a memory range into a different range. | 221 | /// Maps a memory range into a different range. |
| @@ -1319,6 +1309,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces | |||
| 1319 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | 1309 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); |
| 1320 | R_UNLESS(size > 0, ResultInvalidSize); | 1310 | R_UNLESS(size > 0, ResultInvalidSize); |
| 1321 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | 1311 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); |
| 1312 | R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); | ||
| 1313 | R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); | ||
| 1322 | 1314 | ||
| 1323 | // Validate the memory permission. | 1315 | // Validate the memory permission. |
| 1324 | R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); | 1316 | R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); |
| @@ -1333,7 +1325,7 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces | |||
| 1333 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | 1325 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); |
| 1334 | 1326 | ||
| 1335 | // Set the memory permission. | 1327 | // Set the memory permission. |
| 1336 | return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm)); | 1328 | return page_table.SetProcessMemoryPermission(address, size, perm); |
| 1337 | } | 1329 | } |
| 1338 | 1330 | ||
| 1339 | static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | 1331 | static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, |
| @@ -1636,7 +1628,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand | |||
| 1636 | return ResultInvalidMemoryRegion; | 1628 | return ResultInvalidMemoryRegion; |
| 1637 | } | 1629 | } |
| 1638 | 1630 | ||
| 1639 | return page_table.MapProcessCodeMemory(dst_address, src_address, size); | 1631 | return page_table.MapCodeMemory(dst_address, src_address, size); |
| 1640 | } | 1632 | } |
| 1641 | 1633 | ||
| 1642 | static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, | 1634 | static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, |
| @@ -1704,7 +1696,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha | |||
| 1704 | return ResultInvalidMemoryRegion; | 1696 | return ResultInvalidMemoryRegion; |
| 1705 | } | 1697 | } |
| 1706 | 1698 | ||
| 1707 | return page_table.UnmapProcessCodeMemory(dst_address, src_address, size); | 1699 | return page_table.UnmapCodeMemory(dst_address, src_address, size); |
| 1708 | } | 1700 | } |
| 1709 | 1701 | ||
| 1710 | /// Exits the current process | 1702 | /// Exits the current process |
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index ec463b97c..365e22e4e 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h | |||
| @@ -32,6 +32,7 @@ enum class MemoryState : u32 { | |||
| 32 | Kernel = 0x13, | 32 | Kernel = 0x13, |
| 33 | GeneratedCode = 0x14, | 33 | GeneratedCode = 0x14, |
| 34 | CodeOut = 0x15, | 34 | CodeOut = 0x15, |
| 35 | Coverage = 0x16, | ||
| 35 | }; | 36 | }; |
| 36 | DECLARE_ENUM_FLAG_OPERATORS(MemoryState); | 37 | DECLARE_ENUM_FLAG_OPERATORS(MemoryState); |
| 37 | 38 | ||
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 6e12381fb..a2bf7defb 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -37,7 +37,8 @@ namespace Service::HID { | |||
| 37 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew | 37 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew |
| 38 | constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) | 38 | constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) |
| 39 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) | 39 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) |
| 40 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) | 40 | // TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed |
| 41 | constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz) | ||
| 41 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 42 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 42 | 43 | ||
| 43 | IAppletResource::IAppletResource(Core::System& system_, | 44 | IAppletResource::IAppletResource(Core::System& system_, |
| @@ -1175,7 +1176,8 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { | |||
| 1175 | 1176 | ||
| 1176 | const auto parameters{rp.PopRaw<Parameters>()}; | 1177 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1177 | 1178 | ||
| 1178 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 1179 | GetAppletResource() |
| 1180 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1179 | .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); | 1181 | .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); |
| 1180 | 1182 | ||
| 1181 | LOG_WARNING(Service_HID, | 1183 | LOG_WARNING(Service_HID, |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 3782703d2..9fc7bb1b1 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/hle/kernel/k_page_table.h" | 14 | #include "core/hle/kernel/k_page_table.h" |
| 15 | #include "core/hle/kernel/k_system_control.h" | 15 | #include "core/hle/kernel/k_system_control.h" |
| 16 | #include "core/hle/kernel/svc_results.h" | 16 | #include "core/hle/kernel/svc_results.h" |
| 17 | #include "core/hle/kernel/svc_types.h" | ||
| 17 | #include "core/hle/service/ldr/ldr.h" | 18 | #include "core/hle/service/ldr/ldr.h" |
| 18 | #include "core/hle/service/service.h" | 19 | #include "core/hle/service/service.h" |
| 19 | #include "core/loader/nro.h" | 20 | #include "core/loader/nro.h" |
| @@ -325,7 +326,7 @@ public: | |||
| 325 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { | 326 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { |
| 326 | auto& page_table{process->PageTable()}; | 327 | auto& page_table{process->PageTable()}; |
| 327 | const VAddr addr{GetRandomMapRegion(page_table, size)}; | 328 | const VAddr addr{GetRandomMapRegion(page_table, size)}; |
| 328 | const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; | 329 | const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)}; |
| 329 | 330 | ||
| 330 | if (result == Kernel::ResultInvalidCurrentMemory) { | 331 | if (result == Kernel::ResultInvalidCurrentMemory) { |
| 331 | continue; | 332 | continue; |
| @@ -351,12 +352,12 @@ public: | |||
| 351 | 352 | ||
| 352 | if (bss_size) { | 353 | if (bss_size) { |
| 353 | auto block_guard = detail::ScopeExit([&] { | 354 | auto block_guard = detail::ScopeExit([&] { |
| 354 | page_table.UnmapProcessCodeMemory(addr + nro_size, bss_addr, bss_size); | 355 | page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); |
| 355 | page_table.UnmapProcessCodeMemory(addr, nro_addr, nro_size); | 356 | page_table.UnmapCodeMemory(addr, nro_addr, nro_size); |
| 356 | }); | 357 | }); |
| 357 | 358 | ||
| 358 | const ResultCode result{ | 359 | const ResultCode result{ |
| 359 | page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)}; | 360 | page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; |
| 360 | 361 | ||
| 361 | if (result == Kernel::ResultInvalidCurrentMemory) { | 362 | if (result == Kernel::ResultInvalidCurrentMemory) { |
| 362 | continue; | 363 | continue; |
| @@ -397,12 +398,12 @@ public: | |||
| 397 | nro_header.segment_headers[DATA_INDEX].memory_size); | 398 | nro_header.segment_headers[DATA_INDEX].memory_size); |
| 398 | 399 | ||
| 399 | CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( | 400 | CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( |
| 400 | text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute)); | 401 | text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute)); |
| 401 | CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( | 402 | CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( |
| 402 | ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read)); | 403 | ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read)); |
| 403 | 404 | ||
| 404 | return process->PageTable().SetProcessMemoryPermission( | 405 | return process->PageTable().SetProcessMemoryPermission( |
| 405 | data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); | 406 | data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite); |
| 406 | } | 407 | } |
| 407 | 408 | ||
| 408 | void LoadModule(Kernel::HLERequestContext& ctx) { | 409 | void LoadModule(Kernel::HLERequestContext& ctx) { |
| @@ -530,16 +531,19 @@ public: | |||
| 530 | ResultCode UnmapNro(const NROInfo& info) { | 531 | ResultCode UnmapNro(const NROInfo& info) { |
| 531 | // Each region must be unmapped separately to validate memory state | 532 | // Each region must be unmapped separately to validate memory state |
| 532 | auto& page_table{system.CurrentProcess()->PageTable()}; | 533 | auto& page_table{system.CurrentProcess()->PageTable()}; |
| 533 | CASCADE_CODE(page_table.UnmapProcessCodeMemory(info.nro_address + info.text_size + | 534 | |
| 534 | info.ro_size + info.data_size, | 535 | if (info.bss_size != 0) { |
| 535 | info.bss_address, info.bss_size)); | 536 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + |
| 536 | CASCADE_CODE(page_table.UnmapProcessCodeMemory( | 537 | info.ro_size + info.data_size, |
| 537 | info.nro_address + info.text_size + info.ro_size, | 538 | info.bss_address, info.bss_size)); |
| 538 | info.src_addr + info.text_size + info.ro_size, info.data_size)); | 539 | } |
| 539 | CASCADE_CODE(page_table.UnmapProcessCodeMemory( | 540 | |
| 540 | info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size)); | 541 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, |
| 541 | CASCADE_CODE( | 542 | info.src_addr + info.text_size + info.ro_size, |
| 542 | page_table.UnmapProcessCodeMemory(info.nro_address, info.src_addr, info.text_size)); | 543 | info.data_size)); |
| 544 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, | ||
| 545 | info.src_addr + info.text_size, info.ro_size)); | ||
| 546 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); | ||
| 543 | return ResultSuccess; | 547 | return ResultSuccess; |
| 544 | } | 548 | } |
| 545 | 549 | ||
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index aa69216c8..d8ae7f0c1 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp | |||
| @@ -16,6 +16,7 @@ constexpr int mouse_axis_x = 0; | |||
| 16 | constexpr int mouse_axis_y = 1; | 16 | constexpr int mouse_axis_y = 1; |
| 17 | constexpr int wheel_axis_x = 2; | 17 | constexpr int wheel_axis_x = 2; |
| 18 | constexpr int wheel_axis_y = 3; | 18 | constexpr int wheel_axis_y = 3; |
| 19 | constexpr int motion_wheel_y = 4; | ||
| 19 | constexpr int touch_axis_x = 10; | 20 | constexpr int touch_axis_x = 10; |
| 20 | constexpr int touch_axis_y = 11; | 21 | constexpr int touch_axis_y = 11; |
| 21 | constexpr PadIdentifier identifier = { | 22 | constexpr PadIdentifier identifier = { |
| @@ -30,8 +31,9 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) | |||
| 30 | PreSetAxis(identifier, mouse_axis_y); | 31 | PreSetAxis(identifier, mouse_axis_y); |
| 31 | PreSetAxis(identifier, wheel_axis_x); | 32 | PreSetAxis(identifier, wheel_axis_x); |
| 32 | PreSetAxis(identifier, wheel_axis_y); | 33 | PreSetAxis(identifier, wheel_axis_y); |
| 34 | PreSetAxis(identifier, motion_wheel_y); | ||
| 33 | PreSetAxis(identifier, touch_axis_x); | 35 | PreSetAxis(identifier, touch_axis_x); |
| 34 | PreSetAxis(identifier, touch_axis_x); | 36 | PreSetAxis(identifier, touch_axis_y); |
| 35 | update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); }); | 37 | update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); }); |
| 36 | } | 38 | } |
| 37 | 39 | ||
| @@ -48,6 +50,8 @@ void Mouse::UpdateThread(std::stop_token stop_token) { | |||
| 48 | SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); | 50 | SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); |
| 49 | } | 51 | } |
| 50 | 52 | ||
| 53 | SetAxis(identifier, motion_wheel_y, 0.0f); | ||
| 54 | |||
| 51 | if (mouse_panning_timout++ > 20) { | 55 | if (mouse_panning_timout++ > 20) { |
| 52 | StopPanning(); | 56 | StopPanning(); |
| 53 | } | 57 | } |
| @@ -136,6 +140,7 @@ void Mouse::MouseWheelChange(int x, int y) { | |||
| 136 | wheel_position.y += y; | 140 | wheel_position.y += y; |
| 137 | SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); | 141 | SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); |
| 138 | SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); | 142 | SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); |
| 143 | SetAxis(identifier, motion_wheel_y, static_cast<f32>(y) / 100.0f); | ||
| 139 | } | 144 | } |
| 140 | 145 | ||
| 141 | void Mouse::ReleaseAllButtons() { | 146 | void Mouse::ReleaseAllButtons() { |
| @@ -171,13 +176,39 @@ AnalogMapping Mouse::GetAnalogMappingForDevice( | |||
| 171 | return mapping; | 176 | return mapping; |
| 172 | } | 177 | } |
| 173 | 178 | ||
| 179 | Common::Input::ButtonNames Mouse::GetUIButtonName(const Common::ParamPackage& params) const { | ||
| 180 | const auto button = static_cast<MouseButton>(params.Get("button", 0)); | ||
| 181 | switch (button) { | ||
| 182 | case MouseButton::Left: | ||
| 183 | return Common::Input::ButtonNames::ButtonLeft; | ||
| 184 | case MouseButton::Right: | ||
| 185 | return Common::Input::ButtonNames::ButtonRight; | ||
| 186 | case MouseButton::Wheel: | ||
| 187 | return Common::Input::ButtonNames::ButtonMouseWheel; | ||
| 188 | case MouseButton::Backward: | ||
| 189 | return Common::Input::ButtonNames::ButtonBackward; | ||
| 190 | case MouseButton::Forward: | ||
| 191 | return Common::Input::ButtonNames::ButtonForward; | ||
| 192 | case MouseButton::Task: | ||
| 193 | return Common::Input::ButtonNames::ButtonTask; | ||
| 194 | case MouseButton::Extra: | ||
| 195 | return Common::Input::ButtonNames::ButtonExtra; | ||
| 196 | case MouseButton::Undefined: | ||
| 197 | default: | ||
| 198 | return Common::Input::ButtonNames::Undefined; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 174 | Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const { | 202 | Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const { |
| 175 | if (params.Has("button")) { | 203 | if (params.Has("button")) { |
| 176 | return Common::Input::ButtonNames::Value; | 204 | return GetUIButtonName(params); |
| 177 | } | 205 | } |
| 178 | if (params.Has("axis")) { | 206 | if (params.Has("axis")) { |
| 179 | return Common::Input::ButtonNames::Value; | 207 | return Common::Input::ButtonNames::Value; |
| 180 | } | 208 | } |
| 209 | if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) { | ||
| 210 | return Common::Input::ButtonNames::Engine; | ||
| 211 | } | ||
| 181 | 212 | ||
| 182 | return Common::Input::ButtonNames::Invalid; | 213 | return Common::Input::ButtonNames::Invalid; |
| 183 | } | 214 | } |
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index 040446178..c5833b8ed 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h | |||
| @@ -69,6 +69,8 @@ private: | |||
| 69 | void UpdateThread(std::stop_token stop_token); | 69 | void UpdateThread(std::stop_token stop_token); |
| 70 | void StopPanning(); | 70 | void StopPanning(); |
| 71 | 71 | ||
| 72 | Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; | ||
| 73 | |||
| 72 | Common::Vec2<int> mouse_origin; | 74 | Common::Vec2<int> mouse_origin; |
| 73 | Common::Vec2<int> last_mouse_position; | 75 | Common::Vec2<int> last_mouse_position; |
| 74 | Common::Vec2<float> last_mouse_change; | 76 | Common::Vec2<float> last_mouse_change; |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 0cda9df62..ed6281772 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -198,9 +198,15 @@ public: | |||
| 198 | if (sdl_controller) { | 198 | if (sdl_controller) { |
| 199 | switch (SDL_GameControllerGetType(sdl_controller.get())) { | 199 | switch (SDL_GameControllerGetType(sdl_controller.get())) { |
| 200 | case SDL_CONTROLLER_TYPE_XBOX360: | 200 | case SDL_CONTROLLER_TYPE_XBOX360: |
| 201 | return "XBox 360 Controller"; | 201 | return "Xbox 360 Controller"; |
| 202 | case SDL_CONTROLLER_TYPE_XBOXONE: | 202 | case SDL_CONTROLLER_TYPE_XBOXONE: |
| 203 | return "XBox One Controller"; | 203 | return "Xbox One Controller"; |
| 204 | case SDL_CONTROLLER_TYPE_PS3: | ||
| 205 | return "DualShock 3 Controller"; | ||
| 206 | case SDL_CONTROLLER_TYPE_PS4: | ||
| 207 | return "DualShock 4 Controller"; | ||
| 208 | case SDL_CONTROLLER_TYPE_PS5: | ||
| 209 | return "DualSense Controller"; | ||
| 204 | default: | 210 | default: |
| 205 | break; | 211 | break; |
| 206 | } | 212 | } |
| @@ -663,6 +669,7 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding() const { | |||
| 663 | {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | 669 | {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, |
| 664 | {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | 670 | {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, |
| 665 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | 671 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, |
| 672 | {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, | ||
| 666 | }; | 673 | }; |
| 667 | } | 674 | } |
| 668 | 675 | ||
| @@ -699,6 +706,7 @@ ButtonBindings SDLDriver::GetNintendoButtonBinding( | |||
| 699 | {Settings::NativeButton::SL, sl_button}, | 706 | {Settings::NativeButton::SL, sl_button}, |
| 700 | {Settings::NativeButton::SR, sr_button}, | 707 | {Settings::NativeButton::SR, sr_button}, |
| 701 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | 708 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, |
| 709 | {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, | ||
| 702 | }; | 710 | }; |
| 703 | } | 711 | } |
| 704 | 712 | ||
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index e9a5d2e26..4cde3606f 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -24,7 +24,7 @@ namespace InputCommon { | |||
| 24 | class SDLJoystick; | 24 | class SDLJoystick; |
| 25 | 25 | ||
| 26 | using ButtonBindings = | 26 | using ButtonBindings = |
| 27 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; | 27 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>; |
| 28 | using ZButtonBindings = | 28 | using ZButtonBindings = |
| 29 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; | 29 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; |
| 30 | 30 | ||
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp index d78228b50..944e141bf 100644 --- a/src/input_common/drivers/tas_input.cpp +++ b/src/input_common/drivers/tas_input.cpp | |||
| @@ -23,7 +23,7 @@ enum class Tas::TasAxis : u8 { | |||
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | // Supported keywords and buttons from a TAS file | 25 | // Supported keywords and buttons from a TAS file |
| 26 | constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = { | 26 | constexpr std::array<std::pair<std::string_view, TasButton>, 18> text_to_tas_button = { |
| 27 | std::pair{"KEY_A", TasButton::BUTTON_A}, | 27 | std::pair{"KEY_A", TasButton::BUTTON_A}, |
| 28 | {"KEY_B", TasButton::BUTTON_B}, | 28 | {"KEY_B", TasButton::BUTTON_B}, |
| 29 | {"KEY_X", TasButton::BUTTON_X}, | 29 | {"KEY_X", TasButton::BUTTON_X}, |
| @@ -40,8 +40,9 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but | |||
| 40 | {"KEY_DDOWN", TasButton::BUTTON_DOWN}, | 40 | {"KEY_DDOWN", TasButton::BUTTON_DOWN}, |
| 41 | {"KEY_SL", TasButton::BUTTON_SL}, | 41 | {"KEY_SL", TasButton::BUTTON_SL}, |
| 42 | {"KEY_SR", TasButton::BUTTON_SR}, | 42 | {"KEY_SR", TasButton::BUTTON_SR}, |
| 43 | {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE}, | 43 | // These buttons are disabled to avoid TAS input from activating hotkeys |
| 44 | {"KEY_HOME", TasButton::BUTTON_HOME}, | 44 | // {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE}, |
| 45 | // {"KEY_HOME", TasButton::BUTTON_HOME}, | ||
| 45 | {"KEY_ZL", TasButton::TRIGGER_ZL}, | 46 | {"KEY_ZL", TasButton::TRIGGER_ZL}, |
| 46 | {"KEY_ZR", TasButton::TRIGGER_ZR}, | 47 | {"KEY_ZR", TasButton::TRIGGER_ZR}, |
| 47 | }; | 48 | }; |
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index b57330e51..0508b408d 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp | |||
| @@ -173,7 +173,7 @@ void InputEngine::ResetButtonState() { | |||
| 173 | SetButton(controller.first, button.first, false); | 173 | SetButton(controller.first, button.first, false); |
| 174 | } | 174 | } |
| 175 | for (const auto& button : controller.second.hat_buttons) { | 175 | for (const auto& button : controller.second.hat_buttons) { |
| 176 | SetHatButton(controller.first, button.first, false); | 176 | SetHatButton(controller.first, button.first, 0); |
| 177 | } | 177 | } |
| 178 | } | 178 | } |
| 179 | } | 179 | } |
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index 6e0024b2d..475257f42 100644 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp | |||
| @@ -143,6 +143,19 @@ void MappingFactory::RegisterMotion(const MappingData& data) { | |||
| 143 | } | 143 | } |
| 144 | new_input.Set("port", static_cast<int>(data.pad.port)); | 144 | new_input.Set("port", static_cast<int>(data.pad.port)); |
| 145 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | 145 | new_input.Set("pad", static_cast<int>(data.pad.pad)); |
| 146 | |||
| 147 | // If engine is mouse map the mouse position as 3 axis motion | ||
| 148 | if (data.engine == "mouse") { | ||
| 149 | new_input.Set("axis_x", 1); | ||
| 150 | new_input.Set("invert_x", "-"); | ||
| 151 | new_input.Set("axis_y", 0); | ||
| 152 | new_input.Set("axis_z", 4); | ||
| 153 | new_input.Set("range", 1.0f); | ||
| 154 | new_input.Set("deadzone", 0.0f); | ||
| 155 | input_queue.Push(new_input); | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | |||
| 146 | switch (data.type) { | 159 | switch (data.type) { |
| 147 | case EngineInputType::Button: | 160 | case EngineInputType::Button: |
| 148 | case EngineInputType::HatButton: | 161 | case EngineInputType::HatButton: |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4a20c0768..a69ccb264 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -16,6 +16,6 @@ add_executable(tests | |||
| 16 | create_target_directory_groups(tests) | 16 | create_target_directory_groups(tests) |
| 17 | 17 | ||
| 18 | target_link_libraries(tests PRIVATE common core input_common) | 18 | target_link_libraries(tests PRIVATE common core input_common) |
| 19 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) | 19 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) |
| 20 | 20 | ||
| 21 | add_test(NAME tests COMMAND tests) | 21 | add_test(NAME tests COMMAND tests) |
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h index 13ed88382..de5672155 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/command_classes/codecs/codec.h | |||
| @@ -66,7 +66,7 @@ private: | |||
| 66 | bool initialized{}; | 66 | bool initialized{}; |
| 67 | NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; | 67 | NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; |
| 68 | 68 | ||
| 69 | AVCodec* av_codec{nullptr}; | 69 | const AVCodec* av_codec{nullptr}; |
| 70 | AVCodecContext* av_codec_ctx{nullptr}; | 70 | AVCodecContext* av_codec_ctx{nullptr}; |
| 71 | AVBufferRef* av_gpu_decoder{nullptr}; | 71 | AVBufferRef* av_gpu_decoder{nullptr}; |
| 72 | 72 | ||
diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index f34c5f5d9..3a10578cb 100644 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp | |||
| @@ -155,9 +155,6 @@ uint SwizzleOffset(uvec2 pos) { | |||
| 155 | // Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)] | 155 | // Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)] |
| 156 | // is the same as [(num_bits - 1):0] and repeats all the way down. | 156 | // is the same as [(num_bits - 1):0] and repeats all the way down. |
| 157 | uint Replicate(uint val, uint num_bits, uint to_bit) { | 157 | uint Replicate(uint val, uint num_bits, uint to_bit) { |
| 158 | if (num_bits == 0 || to_bit == 0) { | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | const uint v = val & uint((1 << num_bits) - 1); | 158 | const uint v = val & uint((1 << num_bits) - 1); |
| 162 | uint res = v; | 159 | uint res = v; |
| 163 | uint reslen = num_bits; | 160 | uint reslen = num_bits; |
| @@ -187,42 +184,57 @@ uint ReplicateBitTo9(uint value) { | |||
| 187 | return REPLICATE_1_BIT_TO_9_TABLE[value]; | 184 | return REPLICATE_1_BIT_TO_9_TABLE[value]; |
| 188 | } | 185 | } |
| 189 | 186 | ||
| 190 | uint FastReplicateTo8(uint value, uint num_bits) { | 187 | uint FastReplicate(uint value, uint num_bits, uint to_bit) { |
| 191 | switch (num_bits) { | 188 | if (num_bits == 0) { |
| 192 | case 1: | 189 | return 0; |
| 193 | return REPLICATE_1_BIT_TO_8_TABLE[value]; | 190 | } |
| 194 | case 2: | 191 | if (num_bits == to_bit) { |
| 195 | return REPLICATE_2_BIT_TO_8_TABLE[value]; | ||
| 196 | case 3: | ||
| 197 | return REPLICATE_3_BIT_TO_8_TABLE[value]; | ||
| 198 | case 4: | ||
| 199 | return REPLICATE_4_BIT_TO_8_TABLE[value]; | ||
| 200 | case 5: | ||
| 201 | return REPLICATE_5_BIT_TO_8_TABLE[value]; | ||
| 202 | case 6: | ||
| 203 | return REPLICATE_6_BIT_TO_8_TABLE[value]; | ||
| 204 | case 7: | ||
| 205 | return REPLICATE_7_BIT_TO_8_TABLE[value]; | ||
| 206 | case 8: | ||
| 207 | return value; | 192 | return value; |
| 208 | } | 193 | } |
| 209 | return Replicate(value, num_bits, 8); | 194 | if (to_bit == 6) { |
| 195 | switch (num_bits) { | ||
| 196 | case 1: | ||
| 197 | return REPLICATE_1_BIT_TO_6_TABLE[value]; | ||
| 198 | case 2: | ||
| 199 | return REPLICATE_2_BIT_TO_6_TABLE[value]; | ||
| 200 | case 3: | ||
| 201 | return REPLICATE_3_BIT_TO_6_TABLE[value]; | ||
| 202 | case 4: | ||
| 203 | return REPLICATE_4_BIT_TO_6_TABLE[value]; | ||
| 204 | case 5: | ||
| 205 | return REPLICATE_5_BIT_TO_6_TABLE[value]; | ||
| 206 | default: | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | } else { /* if (to_bit == 8) */ | ||
| 210 | switch (num_bits) { | ||
| 211 | case 1: | ||
| 212 | return REPLICATE_1_BIT_TO_8_TABLE[value]; | ||
| 213 | case 2: | ||
| 214 | return REPLICATE_2_BIT_TO_8_TABLE[value]; | ||
| 215 | case 3: | ||
| 216 | return REPLICATE_3_BIT_TO_8_TABLE[value]; | ||
| 217 | case 4: | ||
| 218 | return REPLICATE_4_BIT_TO_8_TABLE[value]; | ||
| 219 | case 5: | ||
| 220 | return REPLICATE_5_BIT_TO_8_TABLE[value]; | ||
| 221 | case 6: | ||
| 222 | return REPLICATE_6_BIT_TO_8_TABLE[value]; | ||
| 223 | case 7: | ||
| 224 | return REPLICATE_7_BIT_TO_8_TABLE[value]; | ||
| 225 | default: | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | return Replicate(value, num_bits, to_bit); | ||
| 230 | } | ||
| 231 | |||
| 232 | uint FastReplicateTo8(uint value, uint num_bits) { | ||
| 233 | return FastReplicate(value, num_bits, 8); | ||
| 210 | } | 234 | } |
| 211 | 235 | ||
| 212 | uint FastReplicateTo6(uint value, uint num_bits) { | 236 | uint FastReplicateTo6(uint value, uint num_bits) { |
| 213 | switch (num_bits) { | 237 | return FastReplicate(value, num_bits, 6); |
| 214 | case 1: | ||
| 215 | return REPLICATE_1_BIT_TO_6_TABLE[value]; | ||
| 216 | case 2: | ||
| 217 | return REPLICATE_2_BIT_TO_6_TABLE[value]; | ||
| 218 | case 3: | ||
| 219 | return REPLICATE_3_BIT_TO_6_TABLE[value]; | ||
| 220 | case 4: | ||
| 221 | return REPLICATE_4_BIT_TO_6_TABLE[value]; | ||
| 222 | case 5: | ||
| 223 | return REPLICATE_5_BIT_TO_6_TABLE[value]; | ||
| 224 | } | ||
| 225 | return Replicate(value, num_bits, 6); | ||
| 226 | } | 238 | } |
| 227 | 239 | ||
| 228 | uint Div3Floor(uint v) { | 240 | uint Div3Floor(uint v) { |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 3d78efddc..153702c0b 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -1038,7 +1038,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 1038 | } | 1038 | } |
| 1039 | if (has_ext_shader_atomic_int64) { | 1039 | if (has_ext_shader_atomic_int64) { |
| 1040 | VkPhysicalDeviceShaderAtomicInt64Features atomic_int64; | 1040 | VkPhysicalDeviceShaderAtomicInt64Features atomic_int64; |
| 1041 | atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | 1041 | atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES; |
| 1042 | atomic_int64.pNext = nullptr; | 1042 | atomic_int64.pNext = nullptr; |
| 1043 | features.pNext = &atomic_int64; | 1043 | features.pNext = &atomic_int64; |
| 1044 | physical.GetFeatures2KHR(features); | 1044 | physical.GetFeatures2KHR(features); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 0f679c37e..33d50667a 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -66,27 +66,27 @@ const std::array<int, 2> Config::default_stick_mod = { | |||
| 66 | // UISetting::values.shortcuts, which is alphabetically ordered. | 66 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 67 | // clang-format off | 67 | // clang-format off |
| 68 | const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{ | 68 | const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{ |
| 69 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, | 69 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, |
| 70 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | 70 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, |
| 71 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, | 71 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, |
| 72 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, | 72 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 73 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, | 73 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 74 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, | 74 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, |
| 75 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, | 75 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, |
| 76 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, | 76 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 77 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, | 77 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, |
| 78 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, | 78 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, |
| 79 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, | 79 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 80 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, | 80 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 81 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, | 81 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 82 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), Qt::ApplicationShortcut}}, | 82 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 83 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), Qt::ApplicationShortcut}}, | 83 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 84 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), Qt::ApplicationShortcut}}, | 84 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 85 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, | 85 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 86 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}}, | 86 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, |
| 87 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}}, | 87 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 88 | {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, | 88 | {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 89 | {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, | 89 | {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 90 | }}; | 90 | }}; |
| 91 | // clang-format on | 91 | // clang-format on |
| 92 | 92 | ||
| @@ -679,7 +679,6 @@ void Config::ReadShortcutValues() { | |||
| 679 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | 679 | qt_config->beginGroup(QStringLiteral("Shortcuts")); |
| 680 | 680 | ||
| 681 | for (const auto& [name, group, shortcut] : default_hotkeys) { | 681 | for (const auto& [name, group, shortcut] : default_hotkeys) { |
| 682 | const auto& [keyseq, context] = shortcut; | ||
| 683 | qt_config->beginGroup(group); | 682 | qt_config->beginGroup(group); |
| 684 | qt_config->beginGroup(name); | 683 | qt_config->beginGroup(name); |
| 685 | // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 | 684 | // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 |
| @@ -688,7 +687,10 @@ void Config::ReadShortcutValues() { | |||
| 688 | UISettings::values.shortcuts.push_back( | 687 | UISettings::values.shortcuts.push_back( |
| 689 | {name, | 688 | {name, |
| 690 | group, | 689 | group, |
| 691 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}}); | 690 | {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), |
| 691 | ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) | ||
| 692 | .toString(), | ||
| 693 | shortcut.context}}); | ||
| 692 | qt_config->endGroup(); | 694 | qt_config->endGroup(); |
| 693 | qt_config->endGroup(); | 695 | qt_config->endGroup(); |
| 694 | } | 696 | } |
| @@ -741,7 +743,10 @@ void Config::ReadUIValues() { | |||
| 741 | qt_config->beginGroup(QStringLiteral("UI")); | 743 | qt_config->beginGroup(QStringLiteral("UI")); |
| 742 | 744 | ||
| 743 | UISettings::values.theme = | 745 | UISettings::values.theme = |
| 744 | ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second)) | 746 | ReadSetting( |
| 747 | QStringLiteral("theme"), | ||
| 748 | QString::fromUtf8( | ||
| 749 | UISettings::themes[static_cast<size_t>(UISettings::Theme::DarkColorful)].second)) | ||
| 745 | .toString(); | 750 | .toString(); |
| 746 | ReadBasicSetting(UISettings::values.enable_discord_presence); | 751 | ReadBasicSetting(UISettings::values.enable_discord_presence); |
| 747 | ReadBasicSetting(UISettings::values.select_user_on_boot); | 752 | ReadBasicSetting(UISettings::values.select_user_on_boot); |
| @@ -1227,8 +1232,10 @@ void Config::SaveShortcutValues() { | |||
| 1227 | 1232 | ||
| 1228 | qt_config->beginGroup(group); | 1233 | qt_config->beginGroup(group); |
| 1229 | qt_config->beginGroup(name); | 1234 | qt_config->beginGroup(name); |
| 1230 | WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkey.first); | 1235 | WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq); |
| 1231 | WriteSetting(QStringLiteral("Context"), shortcut.second, default_hotkey.second); | 1236 | WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, |
| 1237 | default_hotkey.controller_keyseq); | ||
| 1238 | WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); | ||
| 1232 | qt_config->endGroup(); | 1239 | qt_config->endGroup(); |
| 1233 | qt_config->endGroup(); | 1240 | qt_config->endGroup(); |
| 1234 | } | 1241 | } |
| @@ -1266,8 +1273,10 @@ void Config::SaveSystemValues() { | |||
| 1266 | void Config::SaveUIValues() { | 1273 | void Config::SaveUIValues() { |
| 1267 | qt_config->beginGroup(QStringLiteral("UI")); | 1274 | qt_config->beginGroup(QStringLiteral("UI")); |
| 1268 | 1275 | ||
| 1269 | WriteSetting(QStringLiteral("theme"), UISettings::values.theme, | 1276 | WriteSetting( |
| 1270 | QString::fromUtf8(UISettings::themes[0].second)); | 1277 | QStringLiteral("theme"), UISettings::values.theme, |
| 1278 | QString::fromUtf8( | ||
| 1279 | UISettings::themes[static_cast<size_t>(UISettings::Theme::DarkColorful)].second)); | ||
| 1271 | WriteBasicSetting(UISettings::values.enable_discord_presence); | 1280 | WriteBasicSetting(UISettings::values.enable_discord_presence); |
| 1272 | WriteBasicSetting(UISettings::values.select_user_on_boot); | 1281 | WriteBasicSetting(UISettings::values.select_user_on_boot); |
| 1273 | 1282 | ||
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 642a5f966..464e7a489 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -45,7 +45,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, | |||
| 45 | general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, | 45 | general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, |
| 46 | graphics_tab{std::make_unique<ConfigureGraphics>(system_, this)}, | 46 | graphics_tab{std::make_unique<ConfigureGraphics>(system_, this)}, |
| 47 | graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, | 47 | graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, |
| 48 | hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)}, | 48 | hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, |
| 49 | input_tab{std::make_unique<ConfigureInput>(system_, this)}, | 49 | input_tab{std::make_unique<ConfigureInput>(system_, this)}, |
| 50 | network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, | 50 | network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, |
| 51 | profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)}, | 51 | profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)}, |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index ed76fe18e..be10e0a31 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -5,15 +5,24 @@ | |||
| 5 | #include <QMenu> | 5 | #include <QMenu> |
| 6 | #include <QMessageBox> | 6 | #include <QMessageBox> |
| 7 | #include <QStandardItemModel> | 7 | #include <QStandardItemModel> |
| 8 | #include "common/settings.h" | 8 | #include <QTimer> |
| 9 | |||
| 10 | #include "core/hid/emulated_controller.h" | ||
| 11 | #include "core/hid/hid_core.h" | ||
| 12 | |||
| 9 | #include "ui_configure_hotkeys.h" | 13 | #include "ui_configure_hotkeys.h" |
| 10 | #include "yuzu/configuration/config.h" | 14 | #include "yuzu/configuration/config.h" |
| 11 | #include "yuzu/configuration/configure_hotkeys.h" | 15 | #include "yuzu/configuration/configure_hotkeys.h" |
| 12 | #include "yuzu/hotkeys.h" | 16 | #include "yuzu/hotkeys.h" |
| 13 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" | 17 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" |
| 14 | 18 | ||
| 15 | ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) | 19 | constexpr int name_column = 0; |
| 16 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) { | 20 | constexpr int hotkey_column = 1; |
| 21 | constexpr int controller_column = 2; | ||
| 22 | |||
| 23 | ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent) | ||
| 24 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()), | ||
| 25 | timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { | ||
| 17 | ui->setupUi(this); | 26 | ui->setupUi(this); |
| 18 | setFocusPolicy(Qt::ClickFocus); | 27 | setFocusPolicy(Qt::ClickFocus); |
| 19 | 28 | ||
| @@ -26,16 +35,24 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) | |||
| 26 | ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); | 35 | ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); |
| 27 | ui->hotkey_list->setModel(model); | 36 | ui->hotkey_list->setModel(model); |
| 28 | 37 | ||
| 29 | // TODO(Kloen): Make context configurable as well (hiding the column for now) | 38 | ui->hotkey_list->setColumnWidth(name_column, 200); |
| 30 | ui->hotkey_list->hideColumn(2); | 39 | ui->hotkey_list->resizeColumnToContents(hotkey_column); |
| 31 | |||
| 32 | ui->hotkey_list->setColumnWidth(0, 200); | ||
| 33 | ui->hotkey_list->resizeColumnToContents(1); | ||
| 34 | 40 | ||
| 35 | connect(ui->button_restore_defaults, &QPushButton::clicked, this, | 41 | connect(ui->button_restore_defaults, &QPushButton::clicked, this, |
| 36 | &ConfigureHotkeys::RestoreDefaults); | 42 | &ConfigureHotkeys::RestoreDefaults); |
| 37 | connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll); | 43 | connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll); |
| 38 | 44 | ||
| 45 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 46 | |||
| 47 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); | ||
| 48 | |||
| 49 | connect(poll_timer.get(), &QTimer::timeout, [this] { | ||
| 50 | const auto buttons = controller->GetNpadButtons(); | ||
| 51 | if (buttons.raw != Core::HID::NpadButton::None) { | ||
| 52 | SetPollingResult(buttons.raw, false); | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | }); | ||
| 39 | RetranslateUI(); | 56 | RetranslateUI(); |
| 40 | } | 57 | } |
| 41 | 58 | ||
| @@ -49,15 +66,18 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | |||
| 49 | auto* action = new QStandardItem(hotkey.first); | 66 | auto* action = new QStandardItem(hotkey.first); |
| 50 | auto* keyseq = | 67 | auto* keyseq = |
| 51 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); | 68 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); |
| 69 | auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); | ||
| 52 | action->setEditable(false); | 70 | action->setEditable(false); |
| 53 | keyseq->setEditable(false); | 71 | keyseq->setEditable(false); |
| 54 | parent_item->appendRow({action, keyseq}); | 72 | controller_keyseq->setEditable(false); |
| 73 | parent_item->appendRow({action, keyseq, controller_keyseq}); | ||
| 55 | } | 74 | } |
| 56 | model->appendRow(parent_item); | 75 | model->appendRow(parent_item); |
| 57 | } | 76 | } |
| 58 | 77 | ||
| 59 | ui->hotkey_list->expandAll(); | 78 | ui->hotkey_list->expandAll(); |
| 60 | ui->hotkey_list->resizeColumnToContents(0); | 79 | ui->hotkey_list->resizeColumnToContents(name_column); |
| 80 | ui->hotkey_list->resizeColumnToContents(hotkey_column); | ||
| 61 | } | 81 | } |
| 62 | 82 | ||
| 63 | void ConfigureHotkeys::changeEvent(QEvent* event) { | 83 | void ConfigureHotkeys::changeEvent(QEvent* event) { |
| @@ -71,7 +91,7 @@ void ConfigureHotkeys::changeEvent(QEvent* event) { | |||
| 71 | void ConfigureHotkeys::RetranslateUI() { | 91 | void ConfigureHotkeys::RetranslateUI() { |
| 72 | ui->retranslateUi(this); | 92 | ui->retranslateUi(this); |
| 73 | 93 | ||
| 74 | model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")}); | 94 | model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); |
| 75 | } | 95 | } |
| 76 | 96 | ||
| 77 | void ConfigureHotkeys::Configure(QModelIndex index) { | 97 | void ConfigureHotkeys::Configure(QModelIndex index) { |
| @@ -79,7 +99,15 @@ void ConfigureHotkeys::Configure(QModelIndex index) { | |||
| 79 | return; | 99 | return; |
| 80 | } | 100 | } |
| 81 | 101 | ||
| 82 | index = index.sibling(index.row(), 1); | 102 | // Controller configuration is selected |
| 103 | if (index.column() == controller_column) { | ||
| 104 | ConfigureController(index); | ||
| 105 | return; | ||
| 106 | } | ||
| 107 | |||
| 108 | // Swap to the hotkey column | ||
| 109 | index = index.sibling(index.row(), hotkey_column); | ||
| 110 | |||
| 83 | const auto previous_key = model->data(index); | 111 | const auto previous_key = model->data(index); |
| 84 | 112 | ||
| 85 | SequenceDialog hotkey_dialog{this}; | 113 | SequenceDialog hotkey_dialog{this}; |
| @@ -99,13 +127,113 @@ void ConfigureHotkeys::Configure(QModelIndex index) { | |||
| 99 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); | 127 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); |
| 100 | } | 128 | } |
| 101 | } | 129 | } |
| 130 | void ConfigureHotkeys::ConfigureController(QModelIndex index) { | ||
| 131 | if (timeout_timer->isActive()) { | ||
| 132 | return; | ||
| 133 | } | ||
| 134 | |||
| 135 | const auto previous_key = model->data(index); | ||
| 136 | |||
| 137 | input_setter = [this, index, previous_key](const Core::HID::NpadButton button, | ||
| 138 | const bool cancel) { | ||
| 139 | if (cancel) { | ||
| 140 | model->setData(index, previous_key); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 144 | const QString button_string = tr("Home+%1").arg(GetButtonName(button)); | ||
| 145 | |||
| 146 | const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string); | ||
| 147 | |||
| 148 | if (key_sequence_used) { | ||
| 149 | QMessageBox::warning( | ||
| 150 | this, tr("Conflicting Key Sequence"), | ||
| 151 | tr("The entered key sequence is already assigned to: %1").arg(used_action)); | ||
| 152 | model->setData(index, previous_key); | ||
| 153 | } else { | ||
| 154 | model->setData(index, button_string); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | |||
| 158 | model->setData(index, tr("[waiting]")); | ||
| 159 | timeout_timer->start(2500); // Cancel after 2.5 seconds | ||
| 160 | poll_timer->start(200); // Check for new inputs every 200ms | ||
| 161 | // We need to disable configuration to be able to read npad buttons | ||
| 162 | controller->DisableConfiguration(); | ||
| 163 | controller->DisableSystemButtons(); | ||
| 164 | } | ||
| 165 | |||
| 166 | void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) { | ||
| 167 | timeout_timer->stop(); | ||
| 168 | poll_timer->stop(); | ||
| 169 | // Re-Enable configuration | ||
| 170 | controller->EnableConfiguration(); | ||
| 171 | controller->EnableSystemButtons(); | ||
| 172 | |||
| 173 | (*input_setter)(button, cancel); | ||
| 174 | |||
| 175 | input_setter = std::nullopt; | ||
| 176 | } | ||
| 177 | |||
| 178 | QString ConfigureHotkeys::GetButtonName(Core::HID::NpadButton button) const { | ||
| 179 | Core::HID::NpadButtonState state{button}; | ||
| 180 | if (state.a) { | ||
| 181 | return tr("A"); | ||
| 182 | } | ||
| 183 | if (state.b) { | ||
| 184 | return tr("B"); | ||
| 185 | } | ||
| 186 | if (state.x) { | ||
| 187 | return tr("X"); | ||
| 188 | } | ||
| 189 | if (state.y) { | ||
| 190 | return tr("Y"); | ||
| 191 | } | ||
| 192 | if (state.l || state.right_sl || state.left_sl) { | ||
| 193 | return tr("L"); | ||
| 194 | } | ||
| 195 | if (state.r || state.right_sr || state.left_sr) { | ||
| 196 | return tr("R"); | ||
| 197 | } | ||
| 198 | if (state.zl) { | ||
| 199 | return tr("ZL"); | ||
| 200 | } | ||
| 201 | if (state.zr) { | ||
| 202 | return tr("ZR"); | ||
| 203 | } | ||
| 204 | if (state.left) { | ||
| 205 | return tr("Dpad_Left"); | ||
| 206 | } | ||
| 207 | if (state.right) { | ||
| 208 | return tr("Dpad_Right"); | ||
| 209 | } | ||
| 210 | if (state.up) { | ||
| 211 | return tr("Dpad_Up"); | ||
| 212 | } | ||
| 213 | if (state.down) { | ||
| 214 | return tr("Dpad_Down"); | ||
| 215 | } | ||
| 216 | if (state.stick_l) { | ||
| 217 | return tr("Left_Stick"); | ||
| 218 | } | ||
| 219 | if (state.stick_r) { | ||
| 220 | return tr("Right_Stick"); | ||
| 221 | } | ||
| 222 | if (state.minus) { | ||
| 223 | return tr("Minus"); | ||
| 224 | } | ||
| 225 | if (state.plus) { | ||
| 226 | return tr("Plus"); | ||
| 227 | } | ||
| 228 | return tr("Invalid"); | ||
| 229 | } | ||
| 102 | 230 | ||
| 103 | std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { | 231 | std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { |
| 104 | for (int r = 0; r < model->rowCount(); ++r) { | 232 | for (int r = 0; r < model->rowCount(); ++r) { |
| 105 | const QStandardItem* const parent = model->item(r, 0); | 233 | const QStandardItem* const parent = model->item(r, 0); |
| 106 | 234 | ||
| 107 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | 235 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { |
| 108 | const QStandardItem* const key_seq_item = parent->child(r2, 1); | 236 | const QStandardItem* const key_seq_item = parent->child(r2, hotkey_column); |
| 109 | const auto key_seq_str = key_seq_item->text(); | 237 | const auto key_seq_str = key_seq_item->text(); |
| 110 | const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); | 238 | const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); |
| 111 | 239 | ||
| @@ -118,12 +246,31 @@ std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) | |||
| 118 | return std::make_pair(false, QString()); | 246 | return std::make_pair(false, QString()); |
| 119 | } | 247 | } |
| 120 | 248 | ||
| 249 | std::pair<bool, QString> ConfigureHotkeys::IsUsedControllerKey(const QString& key_sequence) const { | ||
| 250 | for (int r = 0; r < model->rowCount(); ++r) { | ||
| 251 | const QStandardItem* const parent = model->item(r, 0); | ||
| 252 | |||
| 253 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | ||
| 254 | const QStandardItem* const key_seq_item = parent->child(r2, controller_column); | ||
| 255 | const auto key_seq_str = key_seq_item->text(); | ||
| 256 | |||
| 257 | if (key_sequence == key_seq_str) { | ||
| 258 | return std::make_pair(true, parent->child(r2, 0)->text()); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | return std::make_pair(false, QString()); | ||
| 264 | } | ||
| 265 | |||
| 121 | void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | 266 | void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { |
| 122 | for (int key_id = 0; key_id < model->rowCount(); key_id++) { | 267 | for (int key_id = 0; key_id < model->rowCount(); key_id++) { |
| 123 | const QStandardItem* parent = model->item(key_id, 0); | 268 | const QStandardItem* parent = model->item(key_id, 0); |
| 124 | for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { | 269 | for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { |
| 125 | const QStandardItem* action = parent->child(key_column_id, 0); | 270 | const QStandardItem* action = parent->child(key_column_id, name_column); |
| 126 | const QStandardItem* keyseq = parent->child(key_column_id, 1); | 271 | const QStandardItem* keyseq = parent->child(key_column_id, hotkey_column); |
| 272 | const QStandardItem* controller_keyseq = | ||
| 273 | parent->child(key_column_id, controller_column); | ||
| 127 | for (auto& [group, sub_actions] : registry.hotkey_groups) { | 274 | for (auto& [group, sub_actions] : registry.hotkey_groups) { |
| 128 | if (group != parent->text()) | 275 | if (group != parent->text()) |
| 129 | continue; | 276 | continue; |
| @@ -131,6 +278,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | |||
| 131 | if (action_name != action->text()) | 278 | if (action_name != action->text()) |
| 132 | continue; | 279 | continue; |
| 133 | hotkey.keyseq = QKeySequence(keyseq->text()); | 280 | hotkey.keyseq = QKeySequence(keyseq->text()); |
| 281 | hotkey.controller_keyseq = controller_keyseq->text(); | ||
| 134 | } | 282 | } |
| 135 | } | 283 | } |
| 136 | } | 284 | } |
| @@ -144,7 +292,12 @@ void ConfigureHotkeys::RestoreDefaults() { | |||
| 144 | const QStandardItem* parent = model->item(r, 0); | 292 | const QStandardItem* parent = model->item(r, 0); |
| 145 | 293 | ||
| 146 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | 294 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { |
| 147 | model->item(r, 0)->child(r2, 1)->setText(Config::default_hotkeys[r2].shortcut.first); | 295 | model->item(r, 0) |
| 296 | ->child(r2, hotkey_column) | ||
| 297 | ->setText(Config::default_hotkeys[r2].shortcut.keyseq); | ||
| 298 | model->item(r, 0) | ||
| 299 | ->child(r2, controller_column) | ||
| 300 | ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); | ||
| 148 | } | 301 | } |
| 149 | } | 302 | } |
| 150 | } | 303 | } |
| @@ -154,7 +307,8 @@ void ConfigureHotkeys::ClearAll() { | |||
| 154 | const QStandardItem* parent = model->item(r, 0); | 307 | const QStandardItem* parent = model->item(r, 0); |
| 155 | 308 | ||
| 156 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | 309 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { |
| 157 | model->item(r, 0)->child(r2, 1)->setText(QString{}); | 310 | model->item(r, 0)->child(r2, hotkey_column)->setText(QString{}); |
| 311 | model->item(r, 0)->child(r2, controller_column)->setText(QString{}); | ||
| 158 | } | 312 | } |
| 159 | } | 313 | } |
| 160 | } | 314 | } |
| @@ -165,28 +319,52 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { | |||
| 165 | return; | 319 | return; |
| 166 | } | 320 | } |
| 167 | 321 | ||
| 168 | const auto selected = index.sibling(index.row(), 1); | 322 | // Swap to the hotkey column if the controller hotkey column is not selected |
| 323 | if (index.column() != controller_column) { | ||
| 324 | index = index.sibling(index.row(), hotkey_column); | ||
| 325 | } | ||
| 326 | |||
| 169 | QMenu context_menu; | 327 | QMenu context_menu; |
| 170 | 328 | ||
| 171 | QAction* restore_default = context_menu.addAction(tr("Restore Default")); | 329 | QAction* restore_default = context_menu.addAction(tr("Restore Default")); |
| 172 | QAction* clear = context_menu.addAction(tr("Clear")); | 330 | QAction* clear = context_menu.addAction(tr("Clear")); |
| 173 | 331 | ||
| 174 | connect(restore_default, &QAction::triggered, [this, selected] { | 332 | connect(restore_default, &QAction::triggered, [this, index] { |
| 175 | const QKeySequence& default_key_sequence = QKeySequence::fromString( | 333 | if (index.column() == controller_column) { |
| 176 | Config::default_hotkeys[selected.row()].shortcut.first, QKeySequence::NativeText); | 334 | RestoreControllerHotkey(index); |
| 177 | const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); | 335 | return; |
| 178 | |||
| 179 | if (key_sequence_used && | ||
| 180 | default_key_sequence != QKeySequence(model->data(selected).toString())) { | ||
| 181 | |||
| 182 | QMessageBox::warning( | ||
| 183 | this, tr("Conflicting Key Sequence"), | ||
| 184 | tr("The default key sequence is already assigned to: %1").arg(used_action)); | ||
| 185 | } else { | ||
| 186 | model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText)); | ||
| 187 | } | 336 | } |
| 337 | RestoreHotkey(index); | ||
| 188 | }); | 338 | }); |
| 189 | connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); }); | 339 | connect(clear, &QAction::triggered, [this, index] { model->setData(index, QString{}); }); |
| 190 | 340 | ||
| 191 | context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); | 341 | context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); |
| 192 | } | 342 | } |
| 343 | |||
| 344 | void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { | ||
| 345 | const QString& default_key_sequence = | ||
| 346 | Config::default_hotkeys[index.row()].shortcut.controller_keyseq; | ||
| 347 | const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); | ||
| 348 | |||
| 349 | if (key_sequence_used && default_key_sequence != model->data(index).toString()) { | ||
| 350 | QMessageBox::warning( | ||
| 351 | this, tr("Conflicting Button Sequence"), | ||
| 352 | tr("The default button sequence is already assigned to: %1").arg(used_action)); | ||
| 353 | } else { | ||
| 354 | model->setData(index, default_key_sequence); | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { | ||
| 359 | const QKeySequence& default_key_sequence = QKeySequence::fromString( | ||
| 360 | Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); | ||
| 361 | const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); | ||
| 362 | |||
| 363 | if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { | ||
| 364 | QMessageBox::warning( | ||
| 365 | this, tr("Conflicting Key Sequence"), | ||
| 366 | tr("The default key sequence is already assigned to: %1").arg(used_action)); | ||
| 367 | } else { | ||
| 368 | model->setData(index, default_key_sequence.toString(QKeySequence::NativeText)); | ||
| 369 | } | ||
| 370 | } | ||
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h index a2ec3323e..f943ec538 100644 --- a/src/yuzu/configuration/configure_hotkeys.h +++ b/src/yuzu/configuration/configure_hotkeys.h | |||
| @@ -7,6 +7,16 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <QWidget> | 8 | #include <QWidget> |
| 9 | 9 | ||
| 10 | namespace Common { | ||
| 11 | class ParamPackage; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Core::HID { | ||
| 15 | class HIDCore; | ||
| 16 | class EmulatedController; | ||
| 17 | enum class NpadButton : u64; | ||
| 18 | } // namespace Core::HID | ||
| 19 | |||
| 10 | namespace Ui { | 20 | namespace Ui { |
| 11 | class ConfigureHotkeys; | 21 | class ConfigureHotkeys; |
| 12 | } | 22 | } |
| @@ -18,7 +28,7 @@ class ConfigureHotkeys : public QWidget { | |||
| 18 | Q_OBJECT | 28 | Q_OBJECT |
| 19 | 29 | ||
| 20 | public: | 30 | public: |
| 21 | explicit ConfigureHotkeys(QWidget* parent = nullptr); | 31 | explicit ConfigureHotkeys(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr); |
| 22 | ~ConfigureHotkeys() override; | 32 | ~ConfigureHotkeys() override; |
| 23 | 33 | ||
| 24 | void ApplyConfiguration(HotkeyRegistry& registry); | 34 | void ApplyConfiguration(HotkeyRegistry& registry); |
| @@ -35,13 +45,24 @@ private: | |||
| 35 | void RetranslateUI(); | 45 | void RetranslateUI(); |
| 36 | 46 | ||
| 37 | void Configure(QModelIndex index); | 47 | void Configure(QModelIndex index); |
| 48 | void ConfigureController(QModelIndex index); | ||
| 38 | std::pair<bool, QString> IsUsedKey(QKeySequence key_sequence) const; | 49 | std::pair<bool, QString> IsUsedKey(QKeySequence key_sequence) const; |
| 50 | std::pair<bool, QString> IsUsedControllerKey(const QString& key_sequence) const; | ||
| 39 | 51 | ||
| 40 | void RestoreDefaults(); | 52 | void RestoreDefaults(); |
| 41 | void ClearAll(); | 53 | void ClearAll(); |
| 42 | void PopupContextMenu(const QPoint& menu_location); | 54 | void PopupContextMenu(const QPoint& menu_location); |
| 55 | void RestoreControllerHotkey(QModelIndex index); | ||
| 56 | void RestoreHotkey(QModelIndex index); | ||
| 43 | 57 | ||
| 44 | std::unique_ptr<Ui::ConfigureHotkeys> ui; | 58 | std::unique_ptr<Ui::ConfigureHotkeys> ui; |
| 45 | 59 | ||
| 46 | QStandardItemModel* model; | 60 | QStandardItemModel* model; |
| 61 | |||
| 62 | void SetPollingResult(Core::HID::NpadButton button, bool cancel); | ||
| 63 | QString GetButtonName(Core::HID::NpadButton button) const; | ||
| 64 | Core::HID::EmulatedController* controller; | ||
| 65 | std::unique_ptr<QTimer> timeout_timer; | ||
| 66 | std::unique_ptr<QTimer> poll_timer; | ||
| 67 | std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter; | ||
| 47 | }; | 68 | }; |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index b9342466e..d2132b408 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -102,6 +102,16 @@ QString GetButtonName(Common::Input::ButtonNames button_name) { | |||
| 102 | return QObject::tr("Share"); | 102 | return QObject::tr("Share"); |
| 103 | case Common::Input::ButtonNames::Options: | 103 | case Common::Input::ButtonNames::Options: |
| 104 | return QObject::tr("Options"); | 104 | return QObject::tr("Options"); |
| 105 | case Common::Input::ButtonNames::ButtonMouseWheel: | ||
| 106 | return QObject::tr("Wheel", "Indicates the mouse wheel"); | ||
| 107 | case Common::Input::ButtonNames::ButtonBackward: | ||
| 108 | return QObject::tr("Backward"); | ||
| 109 | case Common::Input::ButtonNames::ButtonForward: | ||
| 110 | return QObject::tr("Forward"); | ||
| 111 | case Common::Input::ButtonNames::ButtonTask: | ||
| 112 | return QObject::tr("Task"); | ||
| 113 | case Common::Input::ButtonNames::ButtonExtra: | ||
| 114 | return QObject::tr("Extra"); | ||
| 105 | default: | 115 | default: |
| 106 | return QObject::tr("[undefined]"); | 116 | return QObject::tr("[undefined]"); |
| 107 | } | 117 | } |
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index e7e58f314..d96497c4e 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp | |||
| @@ -2,10 +2,13 @@ | |||
| 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 <sstream> | ||
| 5 | #include <QKeySequence> | 6 | #include <QKeySequence> |
| 6 | #include <QShortcut> | 7 | #include <QShortcut> |
| 7 | #include <QTreeWidgetItem> | 8 | #include <QTreeWidgetItem> |
| 8 | #include <QtGlobal> | 9 | #include <QtGlobal> |
| 10 | |||
| 11 | #include "core/hid/emulated_controller.h" | ||
| 9 | #include "yuzu/hotkeys.h" | 12 | #include "yuzu/hotkeys.h" |
| 10 | #include "yuzu/uisettings.h" | 13 | #include "yuzu/uisettings.h" |
| 11 | 14 | ||
| @@ -18,8 +21,9 @@ void HotkeyRegistry::SaveHotkeys() { | |||
| 18 | for (const auto& hotkey : group.second) { | 21 | for (const auto& hotkey : group.second) { |
| 19 | UISettings::values.shortcuts.push_back( | 22 | UISettings::values.shortcuts.push_back( |
| 20 | {hotkey.first, group.first, | 23 | {hotkey.first, group.first, |
| 21 | UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), | 24 | UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), |
| 22 | hotkey.second.context)}); | 25 | hotkey.second.controller_keyseq, |
| 26 | hotkey.second.context})}); | ||
| 23 | } | 27 | } |
| 24 | } | 28 | } |
| 25 | } | 29 | } |
| @@ -29,28 +33,49 @@ void HotkeyRegistry::LoadHotkeys() { | |||
| 29 | // beginGroup() | 33 | // beginGroup() |
| 30 | for (auto shortcut : UISettings::values.shortcuts) { | 34 | for (auto shortcut : UISettings::values.shortcuts) { |
| 31 | Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; | 35 | Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; |
| 32 | if (!shortcut.shortcut.first.isEmpty()) { | 36 | if (!shortcut.shortcut.keyseq.isEmpty()) { |
| 33 | hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText); | 37 | hk.keyseq = |
| 34 | hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second); | 38 | QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText); |
| 39 | hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context); | ||
| 40 | } | ||
| 41 | if (!shortcut.shortcut.controller_keyseq.isEmpty()) { | ||
| 42 | hk.controller_keyseq = shortcut.shortcut.controller_keyseq; | ||
| 35 | } | 43 | } |
| 36 | if (hk.shortcut) { | 44 | if (hk.shortcut) { |
| 37 | hk.shortcut->disconnect(); | 45 | hk.shortcut->disconnect(); |
| 38 | hk.shortcut->setKey(hk.keyseq); | 46 | hk.shortcut->setKey(hk.keyseq); |
| 39 | } | 47 | } |
| 48 | if (hk.controller_shortcut) { | ||
| 49 | hk.controller_shortcut->disconnect(); | ||
| 50 | hk.controller_shortcut->SetKey(hk.controller_keyseq); | ||
| 51 | } | ||
| 40 | } | 52 | } |
| 41 | } | 53 | } |
| 42 | 54 | ||
| 43 | QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { | 55 | QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { |
| 44 | Hotkey& hk = hotkey_groups[group][action]; | 56 | Hotkey& hk = hotkey_groups[group][action]; |
| 45 | 57 | ||
| 46 | if (!hk.shortcut) | 58 | if (!hk.shortcut) { |
| 47 | hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); | 59 | hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); |
| 60 | } | ||
| 48 | 61 | ||
| 49 | hk.shortcut->setAutoRepeat(false); | 62 | hk.shortcut->setAutoRepeat(false); |
| 50 | 63 | ||
| 51 | return hk.shortcut; | 64 | return hk.shortcut; |
| 52 | } | 65 | } |
| 53 | 66 | ||
| 67 | ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action, | ||
| 68 | Core::HID::EmulatedController* controller) { | ||
| 69 | Hotkey& hk = hotkey_groups[group][action]; | ||
| 70 | |||
| 71 | if (!hk.controller_shortcut) { | ||
| 72 | hk.controller_shortcut = new ControllerShortcut(controller); | ||
| 73 | hk.controller_shortcut->SetKey(hk.controller_keyseq); | ||
| 74 | } | ||
| 75 | |||
| 76 | return hk.controller_shortcut; | ||
| 77 | } | ||
| 78 | |||
| 54 | QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { | 79 | QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { |
| 55 | return hotkey_groups[group][action].keyseq; | 80 | return hotkey_groups[group][action].keyseq; |
| 56 | } | 81 | } |
| @@ -59,3 +84,131 @@ Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, | |||
| 59 | const QString& action) { | 84 | const QString& action) { |
| 60 | return hotkey_groups[group][action].context; | 85 | return hotkey_groups[group][action].context; |
| 61 | } | 86 | } |
| 87 | |||
| 88 | ControllerShortcut::ControllerShortcut(Core::HID::EmulatedController* controller) { | ||
| 89 | emulated_controller = controller; | ||
| 90 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 91 | .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); }, | ||
| 92 | .is_npad_service = false, | ||
| 93 | }; | ||
| 94 | callback_key = emulated_controller->SetCallback(engine_callback); | ||
| 95 | is_enabled = true; | ||
| 96 | } | ||
| 97 | |||
| 98 | ControllerShortcut::~ControllerShortcut() { | ||
| 99 | emulated_controller->DeleteCallback(callback_key); | ||
| 100 | } | ||
| 101 | |||
| 102 | void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) { | ||
| 103 | button_sequence = buttons; | ||
| 104 | } | ||
| 105 | |||
| 106 | void ControllerShortcut::SetKey(const QString& buttons_shortcut) { | ||
| 107 | ControllerButtonSequence sequence{}; | ||
| 108 | name = buttons_shortcut.toStdString(); | ||
| 109 | std::istringstream command_line(buttons_shortcut.toStdString()); | ||
| 110 | std::string line; | ||
| 111 | while (std::getline(command_line, line, '+')) { | ||
| 112 | if (line.empty()) { | ||
| 113 | continue; | ||
| 114 | } | ||
| 115 | if (line == "A") { | ||
| 116 | sequence.npad.a.Assign(1); | ||
| 117 | } | ||
| 118 | if (line == "B") { | ||
| 119 | sequence.npad.b.Assign(1); | ||
| 120 | } | ||
| 121 | if (line == "X") { | ||
| 122 | sequence.npad.x.Assign(1); | ||
| 123 | } | ||
| 124 | if (line == "Y") { | ||
| 125 | sequence.npad.y.Assign(1); | ||
| 126 | } | ||
| 127 | if (line == "L") { | ||
| 128 | sequence.npad.l.Assign(1); | ||
| 129 | } | ||
| 130 | if (line == "R") { | ||
| 131 | sequence.npad.r.Assign(1); | ||
| 132 | } | ||
| 133 | if (line == "ZL") { | ||
| 134 | sequence.npad.zl.Assign(1); | ||
| 135 | } | ||
| 136 | if (line == "ZR") { | ||
| 137 | sequence.npad.zr.Assign(1); | ||
| 138 | } | ||
| 139 | if (line == "Dpad_Left") { | ||
| 140 | sequence.npad.left.Assign(1); | ||
| 141 | } | ||
| 142 | if (line == "Dpad_Right") { | ||
| 143 | sequence.npad.right.Assign(1); | ||
| 144 | } | ||
| 145 | if (line == "Dpad_Up") { | ||
| 146 | sequence.npad.up.Assign(1); | ||
| 147 | } | ||
| 148 | if (line == "Dpad_Down") { | ||
| 149 | sequence.npad.down.Assign(1); | ||
| 150 | } | ||
| 151 | if (line == "Left_Stick") { | ||
| 152 | sequence.npad.stick_l.Assign(1); | ||
| 153 | } | ||
| 154 | if (line == "Right_Stick") { | ||
| 155 | sequence.npad.stick_r.Assign(1); | ||
| 156 | } | ||
| 157 | if (line == "Minus") { | ||
| 158 | sequence.npad.minus.Assign(1); | ||
| 159 | } | ||
| 160 | if (line == "Plus") { | ||
| 161 | sequence.npad.plus.Assign(1); | ||
| 162 | } | ||
| 163 | if (line == "Home") { | ||
| 164 | sequence.home.home.Assign(1); | ||
| 165 | } | ||
| 166 | if (line == "Screenshot") { | ||
| 167 | sequence.capture.capture.Assign(1); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | button_sequence = sequence; | ||
| 172 | } | ||
| 173 | |||
| 174 | ControllerButtonSequence ControllerShortcut::ButtonSequence() const { | ||
| 175 | return button_sequence; | ||
| 176 | } | ||
| 177 | |||
| 178 | void ControllerShortcut::SetEnabled(bool enable) { | ||
| 179 | is_enabled = enable; | ||
| 180 | } | ||
| 181 | |||
| 182 | bool ControllerShortcut::IsEnabled() const { | ||
| 183 | return is_enabled; | ||
| 184 | } | ||
| 185 | |||
| 186 | void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { | ||
| 187 | if (!is_enabled) { | ||
| 188 | return; | ||
| 189 | } | ||
| 190 | if (type != Core::HID::ControllerTriggerType::Button) { | ||
| 191 | return; | ||
| 192 | } | ||
| 193 | if (button_sequence.npad.raw == Core::HID::NpadButton::None && | ||
| 194 | button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) { | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | |||
| 198 | const auto player_npad_buttons = | ||
| 199 | emulated_controller->GetNpadButtons().raw & button_sequence.npad.raw; | ||
| 200 | const u64 player_capture_buttons = | ||
| 201 | emulated_controller->GetCaptureButtons().raw & button_sequence.capture.raw; | ||
| 202 | const u64 player_home_buttons = | ||
| 203 | emulated_controller->GetHomeButtons().raw & button_sequence.home.raw; | ||
| 204 | |||
| 205 | if (player_npad_buttons == button_sequence.npad.raw && | ||
| 206 | player_capture_buttons == button_sequence.capture.raw && | ||
| 207 | player_home_buttons == button_sequence.home.raw && !active) { | ||
| 208 | // Force user to press the home or capture button again | ||
| 209 | active = true; | ||
| 210 | emit Activated(); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | active = false; | ||
| 214 | } | ||
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 248fadaf3..57a7c7da5 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h | |||
| @@ -5,11 +5,53 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include "core/hid/hid_types.h" | ||
| 8 | 9 | ||
| 9 | class QDialog; | 10 | class QDialog; |
| 10 | class QKeySequence; | 11 | class QKeySequence; |
| 11 | class QSettings; | 12 | class QSettings; |
| 12 | class QShortcut; | 13 | class QShortcut; |
| 14 | class ControllerShortcut; | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | enum class ControllerTriggerType; | ||
| 18 | class EmulatedController; | ||
| 19 | } // namespace Core::HID | ||
| 20 | |||
| 21 | struct ControllerButtonSequence { | ||
| 22 | Core::HID::CaptureButtonState capture{}; | ||
| 23 | Core::HID::HomeButtonState home{}; | ||
| 24 | Core::HID::NpadButtonState npad{}; | ||
| 25 | }; | ||
| 26 | |||
| 27 | class ControllerShortcut : public QObject { | ||
| 28 | Q_OBJECT | ||
| 29 | |||
| 30 | public: | ||
| 31 | explicit ControllerShortcut(Core::HID::EmulatedController* controller); | ||
| 32 | ~ControllerShortcut(); | ||
| 33 | |||
| 34 | void SetKey(const ControllerButtonSequence& buttons); | ||
| 35 | void SetKey(const QString& buttons_shortcut); | ||
| 36 | |||
| 37 | ControllerButtonSequence ButtonSequence() const; | ||
| 38 | |||
| 39 | void SetEnabled(bool enable); | ||
| 40 | bool IsEnabled() const; | ||
| 41 | |||
| 42 | Q_SIGNALS: | ||
| 43 | void Activated(); | ||
| 44 | |||
| 45 | private: | ||
| 46 | void ControllerUpdateEvent(Core::HID::ControllerTriggerType type); | ||
| 47 | |||
| 48 | bool is_enabled{}; | ||
| 49 | bool active{}; | ||
| 50 | int callback_key{}; | ||
| 51 | ControllerButtonSequence button_sequence{}; | ||
| 52 | std::string name{}; | ||
| 53 | Core::HID::EmulatedController* emulated_controller = nullptr; | ||
| 54 | }; | ||
| 13 | 55 | ||
| 14 | class HotkeyRegistry final { | 56 | class HotkeyRegistry final { |
| 15 | public: | 57 | public: |
| @@ -46,6 +88,8 @@ public: | |||
| 46 | * QShortcut's parent. | 88 | * QShortcut's parent. |
| 47 | */ | 89 | */ |
| 48 | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); | 90 | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); |
| 91 | ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action, | ||
| 92 | Core::HID::EmulatedController* controller); | ||
| 49 | 93 | ||
| 50 | /** | 94 | /** |
| 51 | * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut. | 95 | * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut. |
| @@ -68,7 +112,9 @@ public: | |||
| 68 | private: | 112 | private: |
| 69 | struct Hotkey { | 113 | struct Hotkey { |
| 70 | QKeySequence keyseq; | 114 | QKeySequence keyseq; |
| 115 | QString controller_keyseq; | ||
| 71 | QShortcut* shortcut = nullptr; | 116 | QShortcut* shortcut = nullptr; |
| 117 | ControllerShortcut* controller_shortcut = nullptr; | ||
| 72 | Qt::ShortcutContext context = Qt::WindowShortcut; | 118 | Qt::ShortcutContext context = Qt::WindowShortcut; |
| 73 | }; | 119 | }; |
| 74 | 120 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 53f11a9ac..e10eb70a2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include "core/hle/service/am/applet_ae.h" | 32 | #include "core/hle/service/am/applet_ae.h" |
| 33 | #include "core/hle/service/am/applet_oe.h" | 33 | #include "core/hle/service/am/applet_oe.h" |
| 34 | #include "core/hle/service/am/applets/applets.h" | 34 | #include "core/hle/service/am/applets/applets.h" |
| 35 | #include "yuzu/util/controller_navigation.h" | ||
| 35 | 36 | ||
| 36 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows | 37 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows |
| 37 | // defines. | 38 | // defines. |
| @@ -966,6 +967,12 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name | |||
| 966 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); | 967 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); |
| 967 | 968 | ||
| 968 | this->addAction(action); | 969 | this->addAction(action); |
| 970 | |||
| 971 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 972 | const auto* controller_hotkey = | ||
| 973 | hotkey_registry.GetControllerHotkey(main_window, action_name, controller); | ||
| 974 | connect(controller_hotkey, &ControllerShortcut::Activated, this, | ||
| 975 | [action] { action->trigger(); }); | ||
| 969 | } | 976 | } |
| 970 | 977 | ||
| 971 | void GMainWindow::InitializeHotkeys() { | 978 | void GMainWindow::InitializeHotkeys() { |
| @@ -987,8 +994,12 @@ void GMainWindow::InitializeHotkeys() { | |||
| 987 | 994 | ||
| 988 | static const QString main_window = QStringLiteral("Main Window"); | 995 | static const QString main_window = QStringLiteral("Main Window"); |
| 989 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { | 996 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { |
| 990 | const QShortcut* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); | 997 | const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); |
| 998 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 999 | const auto* controller_hotkey = | ||
| 1000 | hotkey_registry.GetControllerHotkey(main_window, action_name, controller); | ||
| 991 | connect(hotkey, &QShortcut::activated, this, function); | 1001 | connect(hotkey, &QShortcut::activated, this, function); |
| 1002 | connect(controller_hotkey, &ControllerShortcut::Activated, this, function); | ||
| 992 | }; | 1003 | }; |
| 993 | 1004 | ||
| 994 | connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] { | 1005 | connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] { |
| @@ -1165,8 +1176,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1165 | connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode); | 1176 | connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode); |
| 1166 | connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars); | 1177 | connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars); |
| 1167 | connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar); | 1178 | connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar); |
| 1168 | 1179 | connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar); | |
| 1169 | connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); | ||
| 1170 | 1180 | ||
| 1171 | connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720); | 1181 | connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720); |
| 1172 | connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900); | 1182 | connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900); |
| @@ -2168,6 +2178,11 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 2168 | } | 2178 | } |
| 2169 | 2179 | ||
| 2170 | void GMainWindow::OnMenuLoadFile() { | 2180 | void GMainWindow::OnMenuLoadFile() { |
| 2181 | if (is_load_file_select_active) { | ||
| 2182 | return; | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | is_load_file_select_active = true; | ||
| 2171 | const QString extensions = | 2186 | const QString extensions = |
| 2172 | QStringLiteral("*.") | 2187 | QStringLiteral("*.") |
| 2173 | .append(GameList::supported_file_extensions.join(QStringLiteral(" *."))) | 2188 | .append(GameList::supported_file_extensions.join(QStringLiteral(" *."))) |
| @@ -2177,6 +2192,7 @@ void GMainWindow::OnMenuLoadFile() { | |||
| 2177 | .arg(extensions); | 2192 | .arg(extensions); |
| 2178 | const QString filename = QFileDialog::getOpenFileName( | 2193 | const QString filename = QFileDialog::getOpenFileName( |
| 2179 | this, tr("Load File"), UISettings::values.roms_path, file_filter); | 2194 | this, tr("Load File"), UISettings::values.roms_path, file_filter); |
| 2195 | is_load_file_select_active = false; | ||
| 2180 | 2196 | ||
| 2181 | if (filename.isEmpty()) { | 2197 | if (filename.isEmpty()) { |
| 2182 | return; | 2198 | return; |
| @@ -2809,6 +2825,11 @@ void GMainWindow::OnTasStartStop() { | |||
| 2809 | if (!emulation_running) { | 2825 | if (!emulation_running) { |
| 2810 | return; | 2826 | return; |
| 2811 | } | 2827 | } |
| 2828 | |||
| 2829 | // Disable system buttons to prevent TAS from executing a hotkey | ||
| 2830 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 2831 | controller->ResetSystemButtons(); | ||
| 2832 | |||
| 2812 | input_subsystem->GetTas()->StartStop(); | 2833 | input_subsystem->GetTas()->StartStop(); |
| 2813 | OnTasStateChanged(); | 2834 | OnTasStateChanged(); |
| 2814 | } | 2835 | } |
| @@ -2817,12 +2838,34 @@ void GMainWindow::OnTasRecord() { | |||
| 2817 | if (!emulation_running) { | 2838 | if (!emulation_running) { |
| 2818 | return; | 2839 | return; |
| 2819 | } | 2840 | } |
| 2841 | if (is_tas_recording_dialog_active) { | ||
| 2842 | return; | ||
| 2843 | } | ||
| 2844 | |||
| 2845 | // Disable system buttons to prevent TAS from recording a hotkey | ||
| 2846 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 2847 | controller->ResetSystemButtons(); | ||
| 2848 | |||
| 2820 | const bool is_recording = input_subsystem->GetTas()->Record(); | 2849 | const bool is_recording = input_subsystem->GetTas()->Record(); |
| 2821 | if (!is_recording) { | 2850 | if (!is_recording) { |
| 2822 | const auto res = | 2851 | is_tas_recording_dialog_active = true; |
| 2823 | QMessageBox::question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"), | 2852 | ControllerNavigation* controller_navigation = |
| 2824 | QMessageBox::Yes | QMessageBox::No); | 2853 | new ControllerNavigation(system->HIDCore(), this); |
| 2854 | // Use QMessageBox instead of question so we can link controller navigation | ||
| 2855 | QMessageBox* box_dialog = new QMessageBox(); | ||
| 2856 | box_dialog->setWindowTitle(tr("TAS Recording")); | ||
| 2857 | box_dialog->setText(tr("Overwrite file of player 1?")); | ||
| 2858 | box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); | ||
| 2859 | box_dialog->setDefaultButton(QMessageBox::Yes); | ||
| 2860 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 2861 | [box_dialog](Qt::Key key) { | ||
| 2862 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 2863 | QCoreApplication::postEvent(box_dialog, event); | ||
| 2864 | }); | ||
| 2865 | int res = box_dialog->exec(); | ||
| 2866 | controller_navigation->UnloadController(); | ||
| 2825 | input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); | 2867 | input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); |
| 2868 | is_tas_recording_dialog_active = false; | ||
| 2826 | } | 2869 | } |
| 2827 | OnTasStateChanged(); | 2870 | OnTasStateChanged(); |
| 2828 | } | 2871 | } |
| @@ -2871,10 +2914,15 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 2871 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | 2914 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { |
| 2872 | return; | 2915 | return; |
| 2873 | } | 2916 | } |
| 2917 | if (is_amiibo_file_select_active) { | ||
| 2918 | return; | ||
| 2919 | } | ||
| 2874 | 2920 | ||
| 2921 | is_amiibo_file_select_active = true; | ||
| 2875 | const QString extensions{QStringLiteral("*.bin")}; | 2922 | const QString extensions{QStringLiteral("*.bin")}; |
| 2876 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | 2923 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |
| 2877 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); | 2924 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); |
| 2925 | is_amiibo_file_select_active = false; | ||
| 2878 | 2926 | ||
| 2879 | if (filename.isEmpty()) { | 2927 | if (filename.isEmpty()) { |
| 2880 | return; | 2928 | return; |
| @@ -2934,6 +2982,10 @@ void GMainWindow::OnToggleFilterBar() { | |||
| 2934 | } | 2982 | } |
| 2935 | } | 2983 | } |
| 2936 | 2984 | ||
| 2985 | void GMainWindow::OnToggleStatusBar() { | ||
| 2986 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); | ||
| 2987 | } | ||
| 2988 | |||
| 2937 | void GMainWindow::OnCaptureScreenshot() { | 2989 | void GMainWindow::OnCaptureScreenshot() { |
| 2938 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | 2990 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { |
| 2939 | return; | 2991 | return; |
| @@ -3626,8 +3678,8 @@ int main(int argc, char* argv[]) { | |||
| 3626 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); | 3678 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); |
| 3627 | 3679 | ||
| 3628 | #ifdef _WIN32 | 3680 | #ifdef _WIN32 |
| 3629 | // Increases the maximum open file limit to 4096 | 3681 | // Increases the maximum open file limit to 8192 |
| 3630 | _setmaxstdio(4096); | 3682 | _setmaxstdio(8192); |
| 3631 | #endif | 3683 | #endif |
| 3632 | 3684 | ||
| 3633 | #ifdef __APPLE__ | 3685 | #ifdef __APPLE__ |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7870bb963..ca4ab9af5 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -186,6 +186,9 @@ public slots: | |||
| 186 | void OnTasStateChanged(); | 186 | void OnTasStateChanged(); |
| 187 | 187 | ||
| 188 | private: | 188 | private: |
| 189 | /// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry. | ||
| 190 | void LinkActionShortcut(QAction* action, const QString& action_name); | ||
| 191 | |||
| 189 | void RegisterMetaTypes(); | 192 | void RegisterMetaTypes(); |
| 190 | 193 | ||
| 191 | void InitializeWidgets(); | 194 | void InitializeWidgets(); |
| @@ -286,6 +289,7 @@ private slots: | |||
| 286 | void OnOpenYuzuFolder(); | 289 | void OnOpenYuzuFolder(); |
| 287 | void OnAbout(); | 290 | void OnAbout(); |
| 288 | void OnToggleFilterBar(); | 291 | void OnToggleFilterBar(); |
| 292 | void OnToggleStatusBar(); | ||
| 289 | void OnDisplayTitleBars(bool); | 293 | void OnDisplayTitleBars(bool); |
| 290 | void InitializeHotkeys(); | 294 | void InitializeHotkeys(); |
| 291 | void ToggleFullscreen(); | 295 | void ToggleFullscreen(); |
| @@ -303,9 +307,6 @@ private slots: | |||
| 303 | void OnMouseActivity(); | 307 | void OnMouseActivity(); |
| 304 | 308 | ||
| 305 | private: | 309 | private: |
| 306 | /// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry. | ||
| 307 | void LinkActionShortcut(QAction* action, const QString& action_name); | ||
| 308 | |||
| 309 | void RemoveBaseContent(u64 program_id, const QString& entry_type); | 310 | void RemoveBaseContent(u64 program_id, const QString& entry_type); |
| 310 | void RemoveUpdateContent(u64 program_id, const QString& entry_type); | 311 | void RemoveUpdateContent(u64 program_id, const QString& entry_type); |
| 311 | void RemoveAddOnContent(u64 program_id, const QString& entry_type); | 312 | void RemoveAddOnContent(u64 program_id, const QString& entry_type); |
| @@ -400,6 +401,16 @@ private: | |||
| 400 | 401 | ||
| 401 | // Applets | 402 | // Applets |
| 402 | QtSoftwareKeyboardDialog* software_keyboard = nullptr; | 403 | QtSoftwareKeyboardDialog* software_keyboard = nullptr; |
| 404 | |||
| 405 | // True if amiibo file select is visible | ||
| 406 | bool is_amiibo_file_select_active{}; | ||
| 407 | |||
| 408 | // True if load file select is visible | ||
| 409 | bool is_load_file_select_active{}; | ||
| 410 | |||
| 411 | // True if TAS recording dialog is visible | ||
| 412 | bool is_tas_recording_dialog_active{}; | ||
| 413 | |||
| 403 | #ifdef __linux__ | 414 | #ifdef __linux__ |
| 404 | QDBusObjectPath wake_lock{}; | 415 | QDBusObjectPath wake_lock{}; |
| 405 | #endif | 416 | #endif |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index a610e7e25..f7298ddad 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -17,7 +17,11 @@ | |||
| 17 | 17 | ||
| 18 | namespace UISettings { | 18 | namespace UISettings { |
| 19 | 19 | ||
| 20 | using ContextualShortcut = std::pair<QString, int>; | 20 | struct ContextualShortcut { |
| 21 | QString keyseq; | ||
| 22 | QString controller_keyseq; | ||
| 23 | int context; | ||
| 24 | }; | ||
| 21 | 25 | ||
| 22 | struct Shortcut { | 26 | struct Shortcut { |
| 23 | QString name; | 27 | QString name; |
| @@ -25,6 +29,15 @@ struct Shortcut { | |||
| 25 | ContextualShortcut shortcut; | 29 | ContextualShortcut shortcut; |
| 26 | }; | 30 | }; |
| 27 | 31 | ||
| 32 | enum class Theme { | ||
| 33 | Default, | ||
| 34 | DefaultColorful, | ||
| 35 | Dark, | ||
| 36 | DarkColorful, | ||
| 37 | MidnightBlue, | ||
| 38 | MidnightBlueColorful, | ||
| 39 | }; | ||
| 40 | |||
| 28 | using Themes = std::array<std::pair<const char*, const char*>, 6>; | 41 | using Themes = std::array<std::pair<const char*, const char*>, 6>; |
| 29 | extern const Themes themes; | 42 | extern const Themes themes; |
| 30 | 43 | ||