summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/clang/docker.sh1
-rwxr-xr-x.ci/scripts/linux/docker.sh1
-rw-r--r--.ci/templates/build-msvc.yml2
-rw-r--r--CMakeLists.txt2
m---------externals/Vulkan-Headers0
-rw-r--r--src/common/assert.h4
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/hid/emulated_controller.cpp82
-rw-r--r--src/core/hid/emulated_controller.h9
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp1
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp74
-rw-r--r--src/core/hle/kernel/k_hardware_timer.h54
-rw-r--r--src/core/hle/kernel/k_hardware_timer_base.h92
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h4
-rw-r--r--src/core/hle/kernel/k_thread.h8
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp6
-rw-r--r--src/core/hle/kernel/k_timer_task.h40
-rw-r--r--src/core/hle/kernel/kernel.cpp20
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/time_manager.cpp44
-rw-r--r--src/core/hle/kernel/time_manager.h41
-rw-r--r--src/core/hle/service/nfc/nfc_user.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp2
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/drivers/camera.h1
-rw-r--r--src/input_common/drivers/virtual_gamepad.cpp78
-rw-r--r--src/input_common/drivers/virtual_gamepad.h73
-rw-r--r--src/input_common/input_mapping.cpp6
-rw-r--r--src/input_common/main.cpp286
-rw-r--r--src/input_common/main.h7
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp12
-rw-r--r--src/yuzu/bootmanager.cpp18
-rw-r--r--src/yuzu/bootmanager.h8
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/main.cpp3
-rw-r--r--src/yuzu/startup_checks.cpp2
-rw-r--r--src/yuzu/util/overlay_dialog.cpp12
37 files changed, 692 insertions, 325 deletions
diff --git a/.ci/scripts/clang/docker.sh b/.ci/scripts/clang/docker.sh
index 7d3ae4a1a..51769545e 100755
--- a/.ci/scripts/clang/docker.sh
+++ b/.ci/scripts/clang/docker.sh
@@ -11,6 +11,7 @@ ccache -s
11mkdir build || true && cd build 11mkdir build || true && cd build
12cmake .. \ 12cmake .. \
13 -DCMAKE_BUILD_TYPE=Release \ 13 -DCMAKE_BUILD_TYPE=Release \
14 -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
14 -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \ 15 -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \
15 -DCMAKE_C_COMPILER=/usr/lib/ccache/clang \ 16 -DCMAKE_C_COMPILER=/usr/lib/ccache/clang \
16 -DCMAKE_INSTALL_PREFIX="/usr" \ 17 -DCMAKE_INSTALL_PREFIX="/usr" \
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index 35c4a4368..c8bc56c9a 100755
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -12,6 +12,7 @@ mkdir build || true && cd build
12cmake .. \ 12cmake .. \
13 -DBoost_USE_STATIC_LIBS=ON \ 13 -DBoost_USE_STATIC_LIBS=ON \
14 -DCMAKE_BUILD_TYPE=Release \ 14 -DCMAKE_BUILD_TYPE=Release \
15 -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
15 -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ 16 -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
16 -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ 17 -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
17 -DCMAKE_INSTALL_PREFIX="/usr" \ 18 -DCMAKE_INSTALL_PREFIX="/usr" \
diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml
index ea405e5dc..c379dd757 100644
--- a/.ci/templates/build-msvc.yml
+++ b/.ci/templates/build-msvc.yml
@@ -9,7 +9,7 @@ parameters:
9steps: 9steps:
10- script: choco install vulkan-sdk 10- script: choco install vulkan-sdk
11 displayName: 'Install vulkan-sdk' 11 displayName: 'Install vulkan-sdk'
12- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. 12- script: refreshenv && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
13 displayName: 'Configure CMake' 13 displayName: 'Configure CMake'
14- task: MSBuild@1 14- task: MSBuild@1
15 displayName: 'Build' 15 displayName: 'Build'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 47eddf99e..f71a8b3e3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -208,7 +208,7 @@ find_package(libusb 1.0.24)
208find_package(lz4 REQUIRED) 208find_package(lz4 REQUIRED)
209find_package(nlohmann_json 3.8 REQUIRED) 209find_package(nlohmann_json 3.8 REQUIRED)
210find_package(Opus 1.3) 210find_package(Opus 1.3)
211find_package(Vulkan 1.3.213) 211find_package(Vulkan 1.3.238)
212find_package(ZLIB 1.2 REQUIRED) 212find_package(ZLIB 1.2 REQUIRED)
213find_package(zstd 1.5 REQUIRED) 213find_package(zstd 1.5 REQUIRED)
214 214
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
Subproject 2826791bed6a793f164bf534cd859968f13df8a Subproject 00671c64ba5c488ade22ad572a0ef81d5e64c80
diff --git a/src/common/assert.h b/src/common/assert.h
index 8c927fcc0..67e7e9375 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -69,7 +69,7 @@ void assert_fail_impl();
69#define ASSERT_OR_EXECUTE(_a_, _b_) \ 69#define ASSERT_OR_EXECUTE(_a_, _b_) \
70 do { \ 70 do { \
71 ASSERT(_a_); \ 71 ASSERT(_a_); \
72 if (!(_a_)) { \ 72 if (!(_a_)) [[unlikely]] { \
73 _b_ \ 73 _b_ \
74 } \ 74 } \
75 } while (0) 75 } while (0)
@@ -78,7 +78,7 @@ void assert_fail_impl();
78#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ 78#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
79 do { \ 79 do { \
80 ASSERT_MSG(_a_, __VA_ARGS__); \ 80 ASSERT_MSG(_a_, __VA_ARGS__); \
81 if (!(_a_)) { \ 81 if (!(_a_)) [[unlikely]] { \
82 _b_ \ 82 _b_ \
83 } \ 83 } \
84 } while (0) 84 } while (0)
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c6b5ac196..0252c8c31 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -201,6 +201,9 @@ add_library(core STATIC
201 hle/kernel/k_event_info.h 201 hle/kernel/k_event_info.h
202 hle/kernel/k_handle_table.cpp 202 hle/kernel/k_handle_table.cpp
203 hle/kernel/k_handle_table.h 203 hle/kernel/k_handle_table.h
204 hle/kernel/k_hardware_timer_base.h
205 hle/kernel/k_hardware_timer.cpp
206 hle/kernel/k_hardware_timer.h
204 hle/kernel/k_interrupt_manager.cpp 207 hle/kernel/k_interrupt_manager.cpp
205 hle/kernel/k_interrupt_manager.h 208 hle/kernel/k_interrupt_manager.h
206 hle/kernel/k_light_condition_variable.cpp 209 hle/kernel/k_light_condition_variable.cpp
@@ -268,6 +271,7 @@ add_library(core STATIC
268 hle/kernel/k_thread_local_page.h 271 hle/kernel/k_thread_local_page.h
269 hle/kernel/k_thread_queue.cpp 272 hle/kernel/k_thread_queue.cpp
270 hle/kernel/k_thread_queue.h 273 hle/kernel/k_thread_queue.h
274 hle/kernel/k_timer_task.h
271 hle/kernel/k_trace.h 275 hle/kernel/k_trace.h
272 hle/kernel/k_transfer_memory.cpp 276 hle/kernel/k_transfer_memory.cpp
273 hle/kernel/k_transfer_memory.h 277 hle/kernel/k_transfer_memory.h
@@ -290,8 +294,6 @@ add_library(core STATIC
290 hle/kernel/svc_common.h 294 hle/kernel/svc_common.h
291 hle/kernel/svc_types.h 295 hle/kernel/svc_types.h
292 hle/kernel/svc_wrap.h 296 hle/kernel/svc_wrap.h
293 hle/kernel/time_manager.cpp
294 hle/kernel/time_manager.h
295 hle/result.h 297 hle/result.h
296 hle/service/acc/acc.cpp 298 hle/service/acc/acc.cpp
297 hle/service/acc/acc.h 299 hle/service/acc/acc.h
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 67969e938..f238d6ccd 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -145,6 +145,7 @@ void EmulatedController::LoadDevices() {
145 output_params[3].Set("output", true); 145 output_params[3].Set("output", true);
146 146
147 LoadTASParams(); 147 LoadTASParams();
148 LoadVirtualGamepadParams();
148 149
149 std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); 150 std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
150 std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); 151 std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
@@ -163,6 +164,12 @@ void EmulatedController::LoadDevices() {
163 Common::Input::CreateInputDevice); 164 Common::Input::CreateInputDevice);
164 std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), 165 std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
165 Common::Input::CreateInputDevice); 166 Common::Input::CreateInputDevice);
167
168 // Initialize virtual gamepad devices
169 std::ranges::transform(virtual_button_params, virtual_button_devices.begin(),
170 Common::Input::CreateInputDevice);
171 std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(),
172 Common::Input::CreateInputDevice);
166} 173}
167 174
168void EmulatedController::LoadTASParams() { 175void EmulatedController::LoadTASParams() {
@@ -205,6 +212,46 @@ void EmulatedController::LoadTASParams() {
205 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); 212 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
206} 213}
207 214
215void EmulatedController::LoadVirtualGamepadParams() {
216 const auto player_index = NpadIdTypeToIndex(npad_id_type);
217 Common::ParamPackage common_params{};
218 common_params.Set("engine", "virtual_gamepad");
219 common_params.Set("port", static_cast<int>(player_index));
220 for (auto& param : virtual_button_params) {
221 param = common_params;
222 }
223 for (auto& param : virtual_stick_params) {
224 param = common_params;
225 }
226
227 // TODO(german77): Replace this with an input profile or something better
228 virtual_button_params[Settings::NativeButton::A].Set("button", 0);
229 virtual_button_params[Settings::NativeButton::B].Set("button", 1);
230 virtual_button_params[Settings::NativeButton::X].Set("button", 2);
231 virtual_button_params[Settings::NativeButton::Y].Set("button", 3);
232 virtual_button_params[Settings::NativeButton::LStick].Set("button", 4);
233 virtual_button_params[Settings::NativeButton::RStick].Set("button", 5);
234 virtual_button_params[Settings::NativeButton::L].Set("button", 6);
235 virtual_button_params[Settings::NativeButton::R].Set("button", 7);
236 virtual_button_params[Settings::NativeButton::ZL].Set("button", 8);
237 virtual_button_params[Settings::NativeButton::ZR].Set("button", 9);
238 virtual_button_params[Settings::NativeButton::Plus].Set("button", 10);
239 virtual_button_params[Settings::NativeButton::Minus].Set("button", 11);
240 virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12);
241 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
242 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
243 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
244 virtual_button_params[Settings::NativeButton::SL].Set("button", 16);
245 virtual_button_params[Settings::NativeButton::SR].Set("button", 17);
246 virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
247 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
248
249 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
250 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
251 virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
252 virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
253}
254
208void EmulatedController::ReloadInput() { 255void EmulatedController::ReloadInput() {
209 // If you load any device here add the equivalent to the UnloadInput() function 256 // If you load any device here add the equivalent to the UnloadInput() function
210 LoadDevices(); 257 LoadDevices();
@@ -322,6 +369,35 @@ void EmulatedController::ReloadInput() {
322 }, 369 },
323 }); 370 });
324 } 371 }
372
373 // Use a common UUID for Virtual Gamepad
374 static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{
375 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
376
377 // Register virtual devices. No need to force update
378 for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) {
379 if (!virtual_button_devices[index]) {
380 continue;
381 }
382 virtual_button_devices[index]->SetCallback({
383 .on_change =
384 [this, index](const Common::Input::CallbackStatus& callback) {
385 SetButton(callback, index, VIRTUAL_UUID);
386 },
387 });
388 }
389
390 for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) {
391 if (!virtual_stick_devices[index]) {
392 continue;
393 }
394 virtual_stick_devices[index]->SetCallback({
395 .on_change =
396 [this, index](const Common::Input::CallbackStatus& callback) {
397 SetStick(callback, index, VIRTUAL_UUID);
398 },
399 });
400 }
325} 401}
326 402
327void EmulatedController::UnloadInput() { 403void EmulatedController::UnloadInput() {
@@ -349,6 +425,12 @@ void EmulatedController::UnloadInput() {
349 for (auto& stick : tas_stick_devices) { 425 for (auto& stick : tas_stick_devices) {
350 stick.reset(); 426 stick.reset();
351 } 427 }
428 for (auto& button : virtual_button_devices) {
429 button.reset();
430 }
431 for (auto& stick : virtual_stick_devices) {
432 stick.reset();
433 }
352 camera_devices.reset(); 434 camera_devices.reset();
353 nfc_devices.reset(); 435 nfc_devices.reset();
354} 436}
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index fa7a34278..a398543a6 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -385,6 +385,9 @@ private:
385 /// Set the params for TAS devices 385 /// Set the params for TAS devices
386 void LoadTASParams(); 386 void LoadTASParams();
387 387
388 /// Set the params for virtual pad devices
389 void LoadVirtualGamepadParams();
390
388 /** 391 /**
389 * @param use_temporary_value If true tmp_npad_type will be used 392 * @param use_temporary_value If true tmp_npad_type will be used
390 * @return true if the controller style is fullkey 393 * @return true if the controller style is fullkey
@@ -500,6 +503,12 @@ private:
500 ButtonDevices tas_button_devices; 503 ButtonDevices tas_button_devices;
501 StickDevices tas_stick_devices; 504 StickDevices tas_stick_devices;
502 505
506 // Virtual gamepad related variables
507 ButtonParams virtual_button_params;
508 StickParams virtual_stick_params;
509 ButtonDevices virtual_button_devices;
510 StickDevices virtual_stick_devices;
511
503 mutable std::mutex mutex; 512 mutable std::mutex mutex;
504 mutable std::mutex callback_mutex; 513 mutable std::mutex callback_mutex;
505 std::unordered_map<int, ControllerUpdateCallback> callback_list; 514 std::unordered_map<int, ControllerUpdateCallback> callback_list;
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index f85b11557..a442a3b98 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -10,7 +10,6 @@
10#include "core/hle/kernel/k_thread_queue.h" 10#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 12#include "core/hle/kernel/svc_results.h"
13#include "core/hle/kernel/time_manager.h"
14#include "core/memory.h" 13#include "core/memory.h"
15 14
16namespace Kernel { 15namespace Kernel {
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
new file mode 100644
index 000000000..6bba79ea0
--- /dev/null
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -0,0 +1,74 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_hardware_timer.h"
7#include "core/hle/kernel/k_scheduler.h"
8
9namespace Kernel {
10
11void KHardwareTimer::Initialize() {
12 // Create the timing callback to register with CoreTiming.
13 m_event_type = Core::Timing::CreateEvent(
14 "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
15 reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
16 return std::nullopt;
17 });
18}
19
20void KHardwareTimer::Finalize() {
21 this->DisableInterrupt();
22 m_event_type.reset();
23}
24
25void KHardwareTimer::DoTask() {
26 // Handle the interrupt.
27 {
28 KScopedSchedulerLock slk{m_kernel};
29 KScopedSpinLock lk(this->GetLock());
30
31 //! Ignore this event if needed.
32 if (!this->GetInterruptEnabled()) {
33 return;
34 }
35
36 // Disable the timer interrupt while we handle this.
37 this->DisableInterrupt();
38
39 if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
40 0 < next_time && next_time <= m_wakeup_time) {
41 // We have a next time, so we should set the time to interrupt and turn the interrupt
42 // on.
43 this->EnableInterrupt(next_time);
44 }
45 }
46
47 // Clear the timer interrupt.
48 // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer,
49 // GetCurrentCoreId());
50}
51
52void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
53 this->DisableInterrupt();
54
55 m_wakeup_time = wakeup_time;
56 m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
57 m_event_type, reinterpret_cast<uintptr_t>(this),
58 true);
59}
60
61void KHardwareTimer::DisableInterrupt() {
62 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
63 m_wakeup_time = std::numeric_limits<s64>::max();
64}
65
66s64 KHardwareTimer::GetTick() const {
67 return m_kernel.System().CoreTiming().GetGlobalTimeNs().count();
68}
69
70bool KHardwareTimer::GetInterruptEnabled() {
71 return m_wakeup_time != std::numeric_limits<s64>::max();
72}
73
74} // namespace Kernel
diff --git a/src/core/hle/kernel/k_hardware_timer.h b/src/core/hle/kernel/k_hardware_timer.h
new file mode 100644
index 000000000..00bef6ea1
--- /dev/null
+++ b/src/core/hle/kernel/k_hardware_timer.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_hardware_timer_base.h"
7
8namespace Core::Timing {
9struct EventType;
10} // namespace Core::Timing
11
12namespace Kernel {
13
14class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
15public:
16 explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
17
18 // Public API.
19 void Initialize();
20 void Finalize();
21
22 s64 GetCount() const {
23 return GetTick();
24 }
25
26 void RegisterTask(KTimerTask* task, s64 time_from_now) {
27 this->RegisterAbsoluteTask(task, GetTick() + time_from_now);
28 }
29
30 void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
31 KScopedDisableDispatch dd{m_kernel};
32 KScopedSpinLock lk{this->GetLock()};
33
34 if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
35 if (task_time <= m_wakeup_time) {
36 this->EnableInterrupt(task_time);
37 }
38 }
39 }
40
41private:
42 void EnableInterrupt(s64 wakeup_time);
43 void DisableInterrupt();
44 bool GetInterruptEnabled();
45 s64 GetTick() const;
46 void DoTask();
47
48private:
49 // Absolute time in nanoseconds
50 s64 m_wakeup_time{std::numeric_limits<s64>::max()};
51 std::shared_ptr<Core::Timing::EventType> m_event_type{};
52};
53
54} // namespace Kernel
diff --git a/src/core/hle/kernel/k_hardware_timer_base.h b/src/core/hle/kernel/k_hardware_timer_base.h
new file mode 100644
index 000000000..6318b35bd
--- /dev/null
+++ b/src/core/hle/kernel/k_hardware_timer_base.h
@@ -0,0 +1,92 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_spin_lock.h"
7#include "core/hle/kernel/k_thread.h"
8#include "core/hle/kernel/k_timer_task.h"
9
10namespace Kernel {
11
12class KHardwareTimerBase {
13public:
14 explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {}
15
16 void CancelTask(KTimerTask* task) {
17 KScopedDisableDispatch dd{m_kernel};
18 KScopedSpinLock lk{m_lock};
19
20 if (const s64 task_time = task->GetTime(); task_time > 0) {
21 this->RemoveTaskFromTree(task);
22 }
23 }
24
25protected:
26 KSpinLock& GetLock() {
27 return m_lock;
28 }
29
30 s64 DoInterruptTaskImpl(s64 cur_time) {
31 // We want to handle all tasks, returning the next time that a task is scheduled.
32 while (true) {
33 // Get the next task. If there isn't one, return 0.
34 KTimerTask* task = m_next_task;
35 if (task == nullptr) {
36 return 0;
37 }
38
39 // If the task needs to be done in the future, do it in the future and not now.
40 if (const s64 task_time = task->GetTime(); task_time > cur_time) {
41 return task_time;
42 }
43
44 // Remove the task from the tree of tasks, and update our next task.
45 this->RemoveTaskFromTree(task);
46
47 // Handle the task.
48 task->OnTimer();
49 }
50 }
51
52 bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) {
53 ASSERT(task_time > 0);
54
55 // Set the task's time, and insert it into our tree.
56 task->SetTime(task_time);
57 m_task_tree.insert(*task);
58
59 // Update our next task if relevant.
60 if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) {
61 return false;
62 }
63 m_next_task = task;
64 return true;
65 }
66
67private:
68 void RemoveTaskFromTree(KTimerTask* task) {
69 // Erase from the tree.
70 auto it = m_task_tree.erase(m_task_tree.iterator_to(*task));
71
72 // Clear the task's scheduled time.
73 task->SetTime(0);
74
75 // Update our next task if relevant.
76 if (m_next_task == task) {
77 m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr;
78 }
79 }
80
81protected:
82 KernelCore& m_kernel;
83
84private:
85 using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>;
86
87 KSpinLock m_lock{};
88 TimerTaskTree m_task_tree{};
89 KTimerTask* m_next_task{};
90};
91
92} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 76c095e69..76db65a4d 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -5,9 +5,9 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/kernel/global_scheduler_context.h" 7#include "core/hle/kernel/global_scheduler_context.h"
8#include "core/hle/kernel/k_hardware_timer.h"
8#include "core/hle/kernel/k_thread.h" 9#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/time_manager.h"
11 11
12namespace Kernel { 12namespace Kernel {
13 13
@@ -22,7 +22,7 @@ public:
22 ~KScopedSchedulerLockAndSleep() { 22 ~KScopedSchedulerLockAndSleep() {
23 // Register the sleep. 23 // Register the sleep.
24 if (timeout_tick > 0) { 24 if (timeout_tick > 0) {
25 kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick); 25 kernel.HardwareTimer().RegisterTask(thread, timeout_tick);
26 } 26 }
27 27
28 // Unlock the scheduler. 28 // Unlock the scheduler.
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index dc52b4ed3..7cd94a340 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -22,6 +22,7 @@
22#include "core/hle/kernel/k_light_lock.h" 22#include "core/hle/kernel/k_light_lock.h"
23#include "core/hle/kernel/k_spin_lock.h" 23#include "core/hle/kernel/k_spin_lock.h"
24#include "core/hle/kernel/k_synchronization_object.h" 24#include "core/hle/kernel/k_synchronization_object.h"
25#include "core/hle/kernel/k_timer_task.h"
25#include "core/hle/kernel/k_worker_task.h" 26#include "core/hle/kernel/k_worker_task.h"
26#include "core/hle/kernel/slab_helpers.h" 27#include "core/hle/kernel/slab_helpers.h"
27#include "core/hle/kernel/svc_common.h" 28#include "core/hle/kernel/svc_common.h"
@@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread);
112[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); 113[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
113 114
114class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, 115class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
115 public boost::intrusive::list_base_hook<> { 116 public boost::intrusive::list_base_hook<>,
117 public KTimerTask {
116 KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); 118 KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
117 119
118private: 120private:
@@ -840,4 +842,8 @@ private:
840 KernelCore& kernel; 842 KernelCore& kernel;
841}; 843};
842 844
845inline void KTimerTask::OnTimer() {
846 static_cast<KThread*>(this)->OnTimer();
847}
848
843} // namespace Kernel 849} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
index 9f4e081ba..5f1dc97eb 100644
--- a/src/core/hle/kernel/k_thread_queue.cpp
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -1,9 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/kernel/k_hardware_timer.h"
4#include "core/hle/kernel/k_thread_queue.h" 5#include "core/hle/kernel/k_thread_queue.h"
5#include "core/hle/kernel/kernel.h" 6#include "core/hle/kernel/kernel.h"
6#include "core/hle/kernel/time_manager.h"
7 7
8namespace Kernel { 8namespace Kernel {
9 9
@@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {
22 waiting_thread->ClearWaitQueue(); 22 waiting_thread->ClearWaitQueue();
23 23
24 // Cancel the thread task. 24 // Cancel the thread task.
25 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); 25 kernel.HardwareTimer().CancelTask(waiting_thread);
26} 26}
27 27
28void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { 28void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) {
@@ -37,7 +37,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool
37 37
38 // Cancel the thread task. 38 // Cancel the thread task.
39 if (cancel_timer_task) { 39 if (cancel_timer_task) {
40 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); 40 kernel.HardwareTimer().CancelTask(waiting_thread);
41 } 41 }
42} 42}
43 43
diff --git a/src/core/hle/kernel/k_timer_task.h b/src/core/hle/kernel/k_timer_task.h
new file mode 100644
index 000000000..66f0a5a90
--- /dev/null
+++ b/src/core/hle/kernel/k_timer_task.h
@@ -0,0 +1,40 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/intrusive_red_black_tree.h"
7
8namespace Kernel {
9
10class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> {
11public:
12 static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) {
13 if (lhs.GetTime() < rhs.GetTime()) {
14 return -1;
15 } else {
16 return 1;
17 }
18 }
19
20 constexpr explicit KTimerTask() = default;
21
22 constexpr void SetTime(s64 t) {
23 m_time = t;
24 }
25
26 constexpr s64 GetTime() const {
27 return m_time;
28 }
29
30 // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a
31 // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have
32 // devirtualized (see inline declaration for this inside k_thread.h).
33 void OnTimer();
34
35private:
36 // Absolute time in nanoseconds
37 s64 m_time{};
38};
39
40} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 0eb74a422..b75bac5df 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -26,6 +26,7 @@
26#include "core/hle/kernel/k_client_port.h" 26#include "core/hle/kernel/k_client_port.h"
27#include "core/hle/kernel/k_dynamic_resource_manager.h" 27#include "core/hle/kernel/k_dynamic_resource_manager.h"
28#include "core/hle/kernel/k_handle_table.h" 28#include "core/hle/kernel/k_handle_table.h"
29#include "core/hle/kernel/k_hardware_timer.h"
29#include "core/hle/kernel/k_memory_layout.h" 30#include "core/hle/kernel/k_memory_layout.h"
30#include "core/hle/kernel/k_memory_manager.h" 31#include "core/hle/kernel/k_memory_manager.h"
31#include "core/hle/kernel/k_page_buffer.h" 32#include "core/hle/kernel/k_page_buffer.h"
@@ -39,7 +40,6 @@
39#include "core/hle/kernel/kernel.h" 40#include "core/hle/kernel/kernel.h"
40#include "core/hle/kernel/physical_core.h" 41#include "core/hle/kernel/physical_core.h"
41#include "core/hle/kernel/service_thread.h" 42#include "core/hle/kernel/service_thread.h"
42#include "core/hle/kernel/time_manager.h"
43#include "core/hle/result.h" 43#include "core/hle/result.h"
44#include "core/hle/service/sm/sm.h" 44#include "core/hle/service/sm/sm.h"
45#include "core/memory.h" 45#include "core/memory.h"
@@ -55,7 +55,7 @@ struct KernelCore::Impl {
55 static constexpr size_t ReservedDynamicPageCount = 64; 55 static constexpr size_t ReservedDynamicPageCount = 64;
56 56
57 explicit Impl(Core::System& system_, KernelCore& kernel_) 57 explicit Impl(Core::System& system_, KernelCore& kernel_)
58 : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, 58 : service_threads_manager{1, "ServiceThreadsManager"},
59 service_thread_barrier{2}, system{system_} {} 59 service_thread_barrier{2}, system{system_} {}
60 60
61 void SetMulticore(bool is_multi) { 61 void SetMulticore(bool is_multi) {
@@ -63,6 +63,9 @@ struct KernelCore::Impl {
63 } 63 }
64 64
65 void Initialize(KernelCore& kernel) { 65 void Initialize(KernelCore& kernel) {
66 hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel);
67 hardware_timer->Initialize();
68
66 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); 69 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
67 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 70 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
68 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 71 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
@@ -193,6 +196,9 @@ struct KernelCore::Impl {
193 // Ensure that the object list container is finalized and properly shutdown. 196 // Ensure that the object list container is finalized and properly shutdown.
194 global_object_list_container->Finalize(); 197 global_object_list_container->Finalize();
195 global_object_list_container.reset(); 198 global_object_list_container.reset();
199
200 hardware_timer->Finalize();
201 hardware_timer.reset();
196 } 202 }
197 203
198 void CloseServices() { 204 void CloseServices() {
@@ -832,7 +838,7 @@ struct KernelCore::Impl {
832 std::vector<KProcess*> process_list; 838 std::vector<KProcess*> process_list;
833 std::atomic<KProcess*> current_process{}; 839 std::atomic<KProcess*> current_process{};
834 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 840 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
835 Kernel::TimeManager time_manager; 841 std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
836 842
837 Init::KSlabResourceCounts slab_resource_counts{}; 843 Init::KSlabResourceCounts slab_resource_counts{};
838 KResourceLimit* system_resource_limit{}; 844 KResourceLimit* system_resource_limit{};
@@ -1019,12 +1025,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
1019 return impl->schedulers[core_id].get(); 1025 return impl->schedulers[core_id].get();
1020} 1026}
1021 1027
1022Kernel::TimeManager& KernelCore::TimeManager() { 1028Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
1023 return impl->time_manager; 1029 return *impl->hardware_timer;
1024}
1025
1026const Kernel::TimeManager& KernelCore::TimeManager() const {
1027 return impl->time_manager;
1028} 1030}
1029 1031
1030Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { 1032Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 2e22fe0f6..8d22f8d2c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -39,6 +39,7 @@ class KDynamicPageManager;
39class KEvent; 39class KEvent;
40class KEventInfo; 40class KEventInfo;
41class KHandleTable; 41class KHandleTable;
42class KHardwareTimer;
42class KLinkedListNode; 43class KLinkedListNode;
43class KMemoryLayout; 44class KMemoryLayout;
44class KMemoryManager; 45class KMemoryManager;
@@ -63,7 +64,6 @@ class KCodeMemory;
63class PhysicalCore; 64class PhysicalCore;
64class ServiceThread; 65class ServiceThread;
65class Synchronization; 66class Synchronization;
66class TimeManager;
67 67
68using ServiceInterfaceFactory = 68using ServiceInterfaceFactory =
69 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; 69 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
@@ -175,11 +175,8 @@ public:
175 /// Gets the an instance of the current physical CPU core. 175 /// Gets the an instance of the current physical CPU core.
176 const Kernel::PhysicalCore& CurrentPhysicalCore() const; 176 const Kernel::PhysicalCore& CurrentPhysicalCore() const;
177 177
178 /// Gets the an instance of the TimeManager Interface. 178 /// Gets the an instance of the hardware timer.
179 Kernel::TimeManager& TimeManager(); 179 Kernel::KHardwareTimer& HardwareTimer();
180
181 /// Gets the an instance of the TimeManager Interface.
182 const Kernel::TimeManager& TimeManager() const;
183 180
184 /// Stops execution of 'id' core, in order to reschedule a new thread. 181 /// Stops execution of 'id' core, in order to reschedule a new thread.
185 void PrepareReschedule(std::size_t id); 182 void PrepareReschedule(std::size_t id);
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
deleted file mode 100644
index 5ee72c432..000000000
--- a/src/core/hle/kernel/time_manager.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/time_manager.h"
10
11namespace Kernel {
12
13TimeManager::TimeManager(Core::System& system_) : system{system_} {
14 time_manager_event_type = Core::Timing::CreateEvent(
15 "Kernel::TimeManagerCallback",
16 [this](std::uintptr_t thread_handle, s64 time,
17 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
18 KThread* thread = reinterpret_cast<KThread*>(thread_handle);
19 {
20 KScopedSchedulerLock sl(system.Kernel());
21 thread->OnTimer();
22 }
23 return std::nullopt;
24 });
25}
26
27void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
28 std::scoped_lock lock{mutex};
29 if (nanoseconds > 0) {
30 ASSERT(thread);
31 ASSERT(thread->GetState() != ThreadState::Runnable);
32 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
33 time_manager_event_type,
34 reinterpret_cast<uintptr_t>(thread));
35 }
36}
37
38void TimeManager::UnscheduleTimeEvent(KThread* thread) {
39 std::scoped_lock lock{mutex};
40 system.CoreTiming().UnscheduleEvent(time_manager_event_type,
41 reinterpret_cast<uintptr_t>(thread));
42}
43
44} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
deleted file mode 100644
index 94d16b3b4..000000000
--- a/src/core/hle/kernel/time_manager.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8
9namespace Core {
10class System;
11} // namespace Core
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Kernel {
18
19class KThread;
20
21/**
22 * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
23 * method when the event is triggered.
24 */
25class TimeManager {
26public:
27 explicit TimeManager(Core::System& system);
28
29 /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
30 void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
31
32 /// Unschedule an existing time event
33 void UnscheduleTimeEvent(KThread* thread);
34
35private:
36 Core::System& system;
37 std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
38 std::mutex mutex;
39};
40
41} // namespace Kernel
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp
index 4615697e2..89aa6b3f5 100644
--- a/src/core/hle/service/nfc/nfc_user.cpp
+++ b/src/core/hle/service/nfc/nfc_user.cpp
@@ -97,7 +97,7 @@ void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {
97} 97}
98 98
99void IUser::ListDevices(Kernel::HLERequestContext& ctx) { 99void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
100 LOG_INFO(Service_NFC, "called"); 100 LOG_DEBUG(Service_NFC, "called");
101 101
102 if (state == State::NonInitialized) { 102 if (state == State::NonInitialized) {
103 IPC::ResponseBuilder rb{ctx, 2}; 103 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 49816b4c7..a4d3d1bc7 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -83,7 +83,7 @@ void IUser::Finalize(Kernel::HLERequestContext& ctx) {
83} 83}
84 84
85void IUser::ListDevices(Kernel::HLERequestContext& ctx) { 85void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
86 LOG_INFO(Service_NFP, "called"); 86 LOG_DEBUG(Service_NFP, "called");
87 87
88 if (state == State::NonInitialized) { 88 if (state == State::NonInitialized) {
89 IPC::ResponseBuilder rb{ctx, 2}; 89 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 7932aaab0..f24c89b04 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -20,6 +20,8 @@ add_library(input_common STATIC
20 drivers/udp_client.h 20 drivers/udp_client.h
21 drivers/virtual_amiibo.cpp 21 drivers/virtual_amiibo.cpp
22 drivers/virtual_amiibo.h 22 drivers/virtual_amiibo.h
23 drivers/virtual_gamepad.cpp
24 drivers/virtual_gamepad.h
23 helpers/stick_from_buttons.cpp 25 helpers/stick_from_buttons.cpp
24 helpers/stick_from_buttons.h 26 helpers/stick_from_buttons.h
25 helpers/touch_from_buttons.cpp 27 helpers/touch_from_buttons.cpp
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
index 38fb1ae4c..ead3e0fde 100644
--- a/src/input_common/drivers/camera.h
+++ b/src/input_common/drivers/camera.h
@@ -25,6 +25,7 @@ public:
25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, 25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
26 Common::Input::CameraFormat camera_format) override; 26 Common::Input::CameraFormat camera_format) override;
27 27
28private:
28 Common::Input::CameraStatus status{}; 29 Common::Input::CameraStatus status{};
29}; 30};
30 31
diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp
new file mode 100644
index 000000000..7db945aa6
--- /dev/null
+++ b/src/input_common/drivers/virtual_gamepad.cpp
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "input_common/drivers/virtual_gamepad.h"
5
6namespace InputCommon {
7constexpr std::size_t PlayerIndexCount = 10;
8
9VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
10 for (std::size_t i = 0; i < PlayerIndexCount; i++) {
11 PreSetController(GetIdentifier(i));
12 }
13}
14
15void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) {
16 if (player_index > PlayerIndexCount) {
17 return;
18 }
19 const auto identifier = GetIdentifier(player_index);
20 SetButton(identifier, button_id, value);
21}
22
23void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) {
24 SetButtonState(player_index, static_cast<int>(button_id), value);
25}
26
27void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value,
28 float y_value) {
29 if (player_index > PlayerIndexCount) {
30 return;
31 }
32 const auto identifier = GetIdentifier(player_index);
33 SetAxis(identifier, axis_id * 2, x_value);
34 SetAxis(identifier, (axis_id * 2) + 1, y_value);
35}
36
37void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
38 float y_value) {
39 SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value);
40}
41
42void VirtualGamepad::ResetControllers() {
43 for (std::size_t i = 0; i < PlayerIndexCount; i++) {
44 SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f);
45 SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f);
46
47 SetButtonState(i, VirtualButton::ButtonA, false);
48 SetButtonState(i, VirtualButton::ButtonB, false);
49 SetButtonState(i, VirtualButton::ButtonX, false);
50 SetButtonState(i, VirtualButton::ButtonY, false);
51 SetButtonState(i, VirtualButton::StickL, false);
52 SetButtonState(i, VirtualButton::StickR, false);
53 SetButtonState(i, VirtualButton::TriggerL, false);
54 SetButtonState(i, VirtualButton::TriggerR, false);
55 SetButtonState(i, VirtualButton::TriggerZL, false);
56 SetButtonState(i, VirtualButton::TriggerZR, false);
57 SetButtonState(i, VirtualButton::ButtonPlus, false);
58 SetButtonState(i, VirtualButton::ButtonMinus, false);
59 SetButtonState(i, VirtualButton::ButtonLeft, false);
60 SetButtonState(i, VirtualButton::ButtonUp, false);
61 SetButtonState(i, VirtualButton::ButtonRight, false);
62 SetButtonState(i, VirtualButton::ButtonDown, false);
63 SetButtonState(i, VirtualButton::ButtonSL, false);
64 SetButtonState(i, VirtualButton::ButtonSR, false);
65 SetButtonState(i, VirtualButton::ButtonHome, false);
66 SetButtonState(i, VirtualButton::ButtonCapture, false);
67 }
68}
69
70PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const {
71 return {
72 .guid = Common::UUID{},
73 .port = player_index,
74 .pad = 0,
75 };
76}
77
78} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h
new file mode 100644
index 000000000..3df91cc6f
--- /dev/null
+++ b/src/input_common/drivers/virtual_gamepad.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "input_common/input_engine.h"
7
8namespace InputCommon {
9
10/**
11 * A virtual controller that is always assigned to the game input
12 */
13class VirtualGamepad final : public InputEngine {
14public:
15 enum class VirtualButton {
16 ButtonA,
17 ButtonB,
18 ButtonX,
19 ButtonY,
20 StickL,
21 StickR,
22 TriggerL,
23 TriggerR,
24 TriggerZL,
25 TriggerZR,
26 ButtonPlus,
27 ButtonMinus,
28 ButtonLeft,
29 ButtonUp,
30 ButtonRight,
31 ButtonDown,
32 ButtonSL,
33 ButtonSR,
34 ButtonHome,
35 ButtonCapture,
36 };
37
38 enum class VirtualStick {
39 Left = 0,
40 Right = 1,
41 };
42
43 explicit VirtualGamepad(std::string input_engine_);
44
45 /**
46 * Sets the status of all buttons bound with the key to pressed
47 * @param player_index the player number that will take this action
48 * @param button_id the id of the button
49 * @param value indicates if the button is pressed or not
50 */
51 void SetButtonState(std::size_t player_index, int button_id, bool value);
52 void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value);
53
54 /**
55 * Sets the status of all buttons bound with the key to released
56 * @param player_index the player number that will take this action
57 * @param axis_id the id of the axis to move
58 * @param x_value the position of the stick in the x axis
59 * @param y_value the position of the stick in the y axis
60 */
61 void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value);
62 void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
63 float y_value);
64
65 /// Restores all inputs into the neutral position
66 void ResetControllers();
67
68private:
69 /// Returns the correct identifier corresponding to the player index
70 PadIdentifier GetIdentifier(std::size_t player_index) const;
71};
72
73} // namespace InputCommon
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index 0fa4b1ddb..edd5287c1 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -200,12 +200,6 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
200 return false; 200 return false;
201 } 201 }
202 // The following drivers don't need to be mapped 202 // The following drivers don't need to be mapped
203 if (data.engine == "tas") {
204 return false;
205 }
206 if (data.engine == "touch") {
207 return false;
208 }
209 if (data.engine == "touch_from_button") { 203 if (data.engine == "touch_from_button") {
210 return false; 204 return false;
211 } 205 }
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 942a13535..86deb4c7c 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -12,6 +12,7 @@
12#include "input_common/drivers/touch_screen.h" 12#include "input_common/drivers/touch_screen.h"
13#include "input_common/drivers/udp_client.h" 13#include "input_common/drivers/udp_client.h"
14#include "input_common/drivers/virtual_amiibo.h" 14#include "input_common/drivers/virtual_amiibo.h"
15#include "input_common/drivers/virtual_gamepad.h"
15#include "input_common/helpers/stick_from_buttons.h" 16#include "input_common/helpers/stick_from_buttons.h"
16#include "input_common/helpers/touch_from_buttons.h" 17#include "input_common/helpers/touch_from_buttons.h"
17#include "input_common/input_engine.h" 18#include "input_common/input_engine.h"
@@ -25,73 +26,33 @@
25namespace InputCommon { 26namespace InputCommon {
26 27
27struct InputSubsystem::Impl { 28struct InputSubsystem::Impl {
28 void Initialize() { 29 template <typename Engine>
29 mapping_factory = std::make_shared<MappingFactory>(); 30 void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
30 MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }}; 31 MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
31 32
32 keyboard = std::make_shared<Keyboard>("keyboard"); 33 engine = std::make_shared<Engine>(name);
33 keyboard->SetMappingCallback(mapping_callback); 34 engine->SetMappingCallback(mapping_callback);
34 keyboard_factory = std::make_shared<InputFactory>(keyboard); 35
35 keyboard_output_factory = std::make_shared<OutputFactory>(keyboard); 36 std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine);
36 Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory); 37 std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine);
37 Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory); 38 Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory));
38 39 Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory));
39 mouse = std::make_shared<Mouse>("mouse"); 40 }
40 mouse->SetMappingCallback(mapping_callback); 41
41 mouse_factory = std::make_shared<InputFactory>(mouse); 42 void Initialize() {
42 mouse_output_factory = std::make_shared<OutputFactory>(mouse); 43 mapping_factory = std::make_shared<MappingFactory>();
43 Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory);
44 Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory);
45
46 touch_screen = std::make_shared<TouchScreen>("touch");
47 touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
48 Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory);
49
50 gcadapter = std::make_shared<GCAdapter>("gcpad");
51 gcadapter->SetMappingCallback(mapping_callback);
52 gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
53 gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
54 Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory);
55 Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory);
56
57 udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
58 udp_client->SetMappingCallback(mapping_callback);
59 udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
60 udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
61 Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory);
62 Common::Input::RegisterOutputFactory(udp_client->GetEngineName(),
63 udp_client_output_factory);
64
65 tas_input = std::make_shared<TasInput::Tas>("tas");
66 tas_input->SetMappingCallback(mapping_callback);
67 tas_input_factory = std::make_shared<InputFactory>(tas_input);
68 tas_output_factory = std::make_shared<OutputFactory>(tas_input);
69 Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory);
70 Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory);
71
72 camera = std::make_shared<Camera>("camera");
73 camera->SetMappingCallback(mapping_callback);
74 camera_input_factory = std::make_shared<InputFactory>(camera);
75 camera_output_factory = std::make_shared<OutputFactory>(camera);
76 Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory);
77 Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory);
78
79 virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
80 virtual_amiibo->SetMappingCallback(mapping_callback);
81 virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
82 virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
83 Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(),
84 virtual_amiibo_input_factory);
85 Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(),
86 virtual_amiibo_output_factory);
87 44
45 RegisterEngine("keyboard", keyboard);
46 RegisterEngine("mouse", mouse);
47 RegisterEngine("touch", touch_screen);
48 RegisterEngine("gcpad", gcadapter);
49 RegisterEngine("cemuhookudp", udp_client);
50 RegisterEngine("tas", tas_input);
51 RegisterEngine("camera", camera);
52 RegisterEngine("virtual_amiibo", virtual_amiibo);
53 RegisterEngine("virtual_gamepad", virtual_gamepad);
88#ifdef HAVE_SDL2 54#ifdef HAVE_SDL2
89 sdl = std::make_shared<SDLDriver>("sdl"); 55 RegisterEngine("sdl", sdl);
90 sdl->SetMappingCallback(mapping_callback);
91 sdl_input_factory = std::make_shared<InputFactory>(sdl);
92 sdl_output_factory = std::make_shared<OutputFactory>(sdl);
93 Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory);
94 Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory);
95#endif 56#endif
96 57
97 Common::Input::RegisterInputFactory("touch_from_button", 58 Common::Input::RegisterInputFactory("touch_from_button",
@@ -100,42 +61,25 @@ struct InputSubsystem::Impl {
100 std::make_shared<StickFromButton>()); 61 std::make_shared<StickFromButton>());
101 } 62 }
102 63
103 void Shutdown() { 64 template <typename Engine>
104 Common::Input::UnregisterInputFactory(keyboard->GetEngineName()); 65 void UnregisterEngine(std::shared_ptr<Engine>& engine) {
105 Common::Input::UnregisterOutputFactory(keyboard->GetEngineName()); 66 Common::Input::UnregisterInputFactory(engine->GetEngineName());
106 keyboard.reset(); 67 Common::Input::UnregisterOutputFactory(engine->GetEngineName());
107 68 engine.reset();
108 Common::Input::UnregisterInputFactory(mouse->GetEngineName()); 69 }
109 Common::Input::UnregisterOutputFactory(mouse->GetEngineName());
110 mouse.reset();
111
112 Common::Input::UnregisterInputFactory(touch_screen->GetEngineName());
113 touch_screen.reset();
114
115 Common::Input::UnregisterInputFactory(gcadapter->GetEngineName());
116 Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName());
117 gcadapter.reset();
118
119 Common::Input::UnregisterInputFactory(udp_client->GetEngineName());
120 Common::Input::UnregisterOutputFactory(udp_client->GetEngineName());
121 udp_client.reset();
122
123 Common::Input::UnregisterInputFactory(tas_input->GetEngineName());
124 Common::Input::UnregisterOutputFactory(tas_input->GetEngineName());
125 tas_input.reset();
126
127 Common::Input::UnregisterInputFactory(camera->GetEngineName());
128 Common::Input::UnregisterOutputFactory(camera->GetEngineName());
129 camera.reset();
130
131 Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName());
132 Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());
133 virtual_amiibo.reset();
134 70
71 void Shutdown() {
72 UnregisterEngine(keyboard);
73 UnregisterEngine(mouse);
74 UnregisterEngine(touch_screen);
75 UnregisterEngine(gcadapter);
76 UnregisterEngine(udp_client);
77 UnregisterEngine(tas_input);
78 UnregisterEngine(camera);
79 UnregisterEngine(virtual_amiibo);
80 UnregisterEngine(virtual_gamepad);
135#ifdef HAVE_SDL2 81#ifdef HAVE_SDL2
136 Common::Input::UnregisterInputFactory(sdl->GetEngineName()); 82 UnregisterEngine(sdl);
137 Common::Input::UnregisterOutputFactory(sdl->GetEngineName());
138 sdl.reset();
139#endif 83#endif
140 84
141 Common::Input::UnregisterInputFactory("touch_from_button"); 85 Common::Input::UnregisterInputFactory("touch_from_button");
@@ -163,117 +107,86 @@ struct InputSubsystem::Impl {
163 return devices; 107 return devices;
164 } 108 }
165 109
166 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( 110 [[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine(
167 const Common::ParamPackage& params) const { 111 const Common::ParamPackage& params) const {
168 if (!params.Has("engine") || params.Get("engine", "") == "any") { 112 if (!params.Has("engine") || params.Get("engine", "") == "any") {
169 return {}; 113 return nullptr;
170 } 114 }
171 const std::string engine = params.Get("engine", ""); 115 const std::string engine = params.Get("engine", "");
116 if (engine == keyboard->GetEngineName()) {
117 return keyboard;
118 }
172 if (engine == mouse->GetEngineName()) { 119 if (engine == mouse->GetEngineName()) {
173 return mouse->GetAnalogMappingForDevice(params); 120 return mouse;
174 } 121 }
175 if (engine == gcadapter->GetEngineName()) { 122 if (engine == gcadapter->GetEngineName()) {
176 return gcadapter->GetAnalogMappingForDevice(params); 123 return gcadapter;
177 } 124 }
178 if (engine == udp_client->GetEngineName()) { 125 if (engine == udp_client->GetEngineName()) {
179 return udp_client->GetAnalogMappingForDevice(params); 126 return udp_client;
180 }
181 if (engine == tas_input->GetEngineName()) {
182 return tas_input->GetAnalogMappingForDevice(params);
183 } 127 }
184#ifdef HAVE_SDL2 128#ifdef HAVE_SDL2
185 if (engine == sdl->GetEngineName()) { 129 if (engine == sdl->GetEngineName()) {
186 return sdl->GetAnalogMappingForDevice(params); 130 return sdl;
187 } 131 }
188#endif 132#endif
189 return {}; 133 return nullptr;
190 } 134 }
191 135
192 [[nodiscard]] ButtonMapping GetButtonMappingForDevice( 136 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
193 const Common::ParamPackage& params) const { 137 const Common::ParamPackage& params) const {
194 if (!params.Has("engine") || params.Get("engine", "") == "any") { 138 const auto input_engine = GetInputEngine(params);
139
140 if (input_engine == nullptr) {
195 return {}; 141 return {};
196 } 142 }
197 const std::string engine = params.Get("engine", ""); 143
198 if (engine == gcadapter->GetEngineName()) { 144 return input_engine->GetAnalogMappingForDevice(params);
199 return gcadapter->GetButtonMappingForDevice(params); 145 }
200 } 146
201 if (engine == udp_client->GetEngineName()) { 147 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
202 return udp_client->GetButtonMappingForDevice(params); 148 const Common::ParamPackage& params) const {
203 } 149 const auto input_engine = GetInputEngine(params);
204 if (engine == tas_input->GetEngineName()) { 150
205 return tas_input->GetButtonMappingForDevice(params); 151 if (input_engine == nullptr) {
206 } 152 return {};
207#ifdef HAVE_SDL2
208 if (engine == sdl->GetEngineName()) {
209 return sdl->GetButtonMappingForDevice(params);
210 } 153 }
211#endif 154
212 return {}; 155 return input_engine->GetButtonMappingForDevice(params);
213 } 156 }
214 157
215 [[nodiscard]] MotionMapping GetMotionMappingForDevice( 158 [[nodiscard]] MotionMapping GetMotionMappingForDevice(
216 const Common::ParamPackage& params) const { 159 const Common::ParamPackage& params) const {
217 if (!params.Has("engine") || params.Get("engine", "") == "any") { 160 const auto input_engine = GetInputEngine(params);
161
162 if (input_engine == nullptr) {
218 return {}; 163 return {};
219 } 164 }
220 const std::string engine = params.Get("engine", ""); 165
221 if (engine == udp_client->GetEngineName()) { 166 return input_engine->GetMotionMappingForDevice(params);
222 return udp_client->GetMotionMappingForDevice(params);
223 }
224#ifdef HAVE_SDL2
225 if (engine == sdl->GetEngineName()) {
226 return sdl->GetMotionMappingForDevice(params);
227 }
228#endif
229 return {};
230 } 167 }
231 168
232 Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const { 169 Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
233 if (!params.Has("engine") || params.Get("engine", "") == "any") { 170 if (!params.Has("engine") || params.Get("engine", "") == "any") {
234 return Common::Input::ButtonNames::Undefined; 171 return Common::Input::ButtonNames::Undefined;
235 } 172 }
236 const std::string engine = params.Get("engine", ""); 173 const auto input_engine = GetInputEngine(params);
237 if (engine == mouse->GetEngineName()) { 174
238 return mouse->GetUIName(params); 175 if (input_engine == nullptr) {
239 } 176 return Common::Input::ButtonNames::Invalid;
240 if (engine == gcadapter->GetEngineName()) {
241 return gcadapter->GetUIName(params);
242 }
243 if (engine == udp_client->GetEngineName()) {
244 return udp_client->GetUIName(params);
245 }
246 if (engine == tas_input->GetEngineName()) {
247 return tas_input->GetUIName(params);
248 }
249#ifdef HAVE_SDL2
250 if (engine == sdl->GetEngineName()) {
251 return sdl->GetUIName(params);
252 } 177 }
253#endif 178
254 return Common::Input::ButtonNames::Invalid; 179 return input_engine->GetUIName(params);
255 } 180 }
256 181
257 bool IsStickInverted(const Common::ParamPackage& params) { 182 bool IsStickInverted(const Common::ParamPackage& params) {
258 const std::string engine = params.Get("engine", ""); 183 const auto input_engine = GetInputEngine(params);
259 if (engine == mouse->GetEngineName()) { 184
260 return mouse->IsStickInverted(params); 185 if (input_engine == nullptr) {
261 } 186 return false;
262 if (engine == gcadapter->GetEngineName()) {
263 return gcadapter->IsStickInverted(params);
264 }
265 if (engine == udp_client->GetEngineName()) {
266 return udp_client->IsStickInverted(params);
267 }
268 if (engine == tas_input->GetEngineName()) {
269 return tas_input->IsStickInverted(params);
270 }
271#ifdef HAVE_SDL2
272 if (engine == sdl->GetEngineName()) {
273 return sdl->IsStickInverted(params);
274 } 187 }
275#endif 188
276 return false; 189 return input_engine->IsStickInverted(params);
277 } 190 }
278 191
279 bool IsController(const Common::ParamPackage& params) { 192 bool IsController(const Common::ParamPackage& params) {
@@ -290,6 +203,9 @@ struct InputSubsystem::Impl {
290 if (engine == tas_input->GetEngineName()) { 203 if (engine == tas_input->GetEngineName()) {
291 return true; 204 return true;
292 } 205 }
206 if (engine == virtual_gamepad->GetEngineName()) {
207 return true;
208 }
293#ifdef HAVE_SDL2 209#ifdef HAVE_SDL2
294 if (engine == sdl->GetEngineName()) { 210 if (engine == sdl->GetEngineName()) {
295 return true; 211 return true;
@@ -338,28 +254,10 @@ struct InputSubsystem::Impl {
338 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 254 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
339 std::shared_ptr<Camera> camera; 255 std::shared_ptr<Camera> camera;
340 std::shared_ptr<VirtualAmiibo> virtual_amiibo; 256 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
341 257 std::shared_ptr<VirtualGamepad> virtual_gamepad;
342 std::shared_ptr<InputFactory> keyboard_factory;
343 std::shared_ptr<InputFactory> mouse_factory;
344 std::shared_ptr<InputFactory> gcadapter_input_factory;
345 std::shared_ptr<InputFactory> touch_screen_factory;
346 std::shared_ptr<InputFactory> udp_client_input_factory;
347 std::shared_ptr<InputFactory> tas_input_factory;
348 std::shared_ptr<InputFactory> camera_input_factory;
349 std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
350
351 std::shared_ptr<OutputFactory> keyboard_output_factory;
352 std::shared_ptr<OutputFactory> mouse_output_factory;
353 std::shared_ptr<OutputFactory> gcadapter_output_factory;
354 std::shared_ptr<OutputFactory> udp_client_output_factory;
355 std::shared_ptr<OutputFactory> tas_output_factory;
356 std::shared_ptr<OutputFactory> camera_output_factory;
357 std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
358 258
359#ifdef HAVE_SDL2 259#ifdef HAVE_SDL2
360 std::shared_ptr<SDLDriver> sdl; 260 std::shared_ptr<SDLDriver> sdl;
361 std::shared_ptr<InputFactory> sdl_input_factory;
362 std::shared_ptr<OutputFactory> sdl_output_factory;
363#endif 261#endif
364}; 262};
365 263
@@ -423,6 +321,14 @@ const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
423 return impl->virtual_amiibo.get(); 321 return impl->virtual_amiibo.get();
424} 322}
425 323
324VirtualGamepad* InputSubsystem::GetVirtualGamepad() {
325 return impl->virtual_gamepad.get();
326}
327
328const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const {
329 return impl->virtual_gamepad.get();
330}
331
426std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 332std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
427 return impl->GetInputDevices(); 333 return impl->GetInputDevices();
428} 334}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 6218c37f6..1207d786c 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -34,6 +34,7 @@ class Keyboard;
34class Mouse; 34class Mouse;
35class TouchScreen; 35class TouchScreen;
36class VirtualAmiibo; 36class VirtualAmiibo;
37class VirtualGamepad;
37struct MappingData; 38struct MappingData;
38} // namespace InputCommon 39} // namespace InputCommon
39 40
@@ -108,6 +109,12 @@ public:
108 /// Retrieves the underlying virtual amiibo input device. 109 /// Retrieves the underlying virtual amiibo input device.
109 [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; 110 [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
110 111
112 /// Retrieves the underlying virtual gamepad input device.
113 [[nodiscard]] VirtualGamepad* GetVirtualGamepad();
114
115 /// Retrieves the underlying virtual gamepad input device.
116 [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const;
117
111 /** 118 /**
112 * Returns all available input devices that this Factory can create a new device with. 119 * Returns all available input devices that this Factory can create a new device with.
113 * Each returned ParamPackage should have a `display` field used for display, a `engine` field 120 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 483b534a0..7dca7341c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept {
314 return "VK_ERROR_VALIDATION_FAILED_EXT"; 314 return "VK_ERROR_VALIDATION_FAILED_EXT";
315 case VkResult::VK_ERROR_INVALID_SHADER_NV: 315 case VkResult::VK_ERROR_INVALID_SHADER_NV:
316 return "VK_ERROR_INVALID_SHADER_NV"; 316 return "VK_ERROR_INVALID_SHADER_NV";
317 case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR:
318 return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
319 case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR:
320 return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
321 case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR:
322 return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
323 case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR:
324 return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
325 case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR:
326 return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
327 case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR:
328 return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
317 case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: 329 case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
318 return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; 330 return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
319 case VkResult::VK_ERROR_FRAGMENTATION_EXT: 331 case VkResult::VK_ERROR_FRAGMENTATION_EXT:
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 40b3d91fc..13782869d 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -740,7 +740,9 @@ void GRenderWindow::InitializeCamera() {
740 return; 740 return;
741 } 741 }
742 742
743 camera_data.resize(CAMERA_WIDTH * CAMERA_HEIGHT); 743 const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
744 const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
745 camera_data.resize(camera_width * camera_height);
744 camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); 746 camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
745 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, 747 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
746 &GRenderWindow::OnCameraCapture); 748 &GRenderWindow::OnCameraCapture);
@@ -796,14 +798,22 @@ void GRenderWindow::RequestCameraCapture() {
796} 798}
797 799
798void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { 800void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
801#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
799 // TODO: Capture directly in the format and resolution needed 802 // TODO: Capture directly in the format and resolution needed
803 const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
804 const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
800 const auto converted = 805 const auto converted =
801 img.scaled(CAMERA_WIDTH, CAMERA_HEIGHT, Qt::AspectRatioMode::IgnoreAspectRatio, 806 img.scaled(static_cast<int>(camera_width), static_cast<int>(camera_height),
807 Qt::AspectRatioMode::IgnoreAspectRatio,
802 Qt::TransformationMode::SmoothTransformation) 808 Qt::TransformationMode::SmoothTransformation)
803 .mirrored(false, true); 809 .mirrored(false, true);
804 std::memcpy(camera_data.data(), converted.bits(), CAMERA_WIDTH * CAMERA_HEIGHT * sizeof(u32)); 810 if (camera_data.size() != camera_width * camera_height) {
805 input_subsystem->GetCamera()->SetCameraData(CAMERA_WIDTH, CAMERA_HEIGHT, camera_data); 811 camera_data.resize(camera_width * camera_height);
812 }
813 std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
814 input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
806 pending_camera_snapshots = 0; 815 pending_camera_snapshots = 0;
816#endif
807} 817}
808 818
809bool GRenderWindow::event(QEvent* event) { 819bool GRenderWindow::event(QEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 52867d628..1c2e76369 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -239,16 +239,14 @@ private:
239 bool first_frame = false; 239 bool first_frame = false;
240 InputCommon::TasInput::TasState last_tas_state; 240 InputCommon::TasInput::TasState last_tas_state;
241 241
242#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
242 bool is_virtual_camera; 243 bool is_virtual_camera;
243 int pending_camera_snapshots; 244 int pending_camera_snapshots;
244#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA 245 std::vector<u32> camera_data;
245 std::unique_ptr<QCamera> camera; 246 std::unique_ptr<QCamera> camera;
246 std::unique_ptr<QCameraImageCapture> camera_capture; 247 std::unique_ptr<QCameraImageCapture> camera_capture;
247 static constexpr std::size_t CAMERA_WIDTH = 320;
248 static constexpr std::size_t CAMERA_HEIGHT = 240;
249 std::vector<u32> camera_data;
250#endif
251 std::unique_ptr<QTimer> camera_timer; 248 std::unique_ptr<QTimer> camera_timer;
249#endif
252 250
253 Core::System& system; 251 Core::System& system;
254 252
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index a8d47a2f9..2ea4f367b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -783,8 +783,6 @@ void Config::ReadSystemValues() {
783 } 783 }
784 } 784 }
785 785
786 ReadBasicSetting(Settings::values.device_name);
787
788 if (global) { 786 if (global) {
789 ReadBasicSetting(Settings::values.current_user); 787 ReadBasicSetting(Settings::values.current_user);
790 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, 788 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
@@ -797,6 +795,7 @@ void Config::ReadSystemValues() {
797 } else { 795 } else {
798 Settings::values.custom_rtc = std::nullopt; 796 Settings::values.custom_rtc = std::nullopt;
799 } 797 }
798 ReadBasicSetting(Settings::values.device_name);
800 } 799 }
801 800
802 ReadGlobalSetting(Settings::values.sound_index); 801 ReadGlobalSetting(Settings::values.sound_index);
@@ -1407,7 +1406,6 @@ void Config::SaveSystemValues() {
1407 Settings::values.rng_seed.UsingGlobal()); 1406 Settings::values.rng_seed.UsingGlobal());
1408 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0), 1407 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),
1409 0, Settings::values.rng_seed.UsingGlobal()); 1408 0, Settings::values.rng_seed.UsingGlobal());
1410 WriteBasicSetting(Settings::values.device_name);
1411 1409
1412 if (global) { 1410 if (global) {
1413 WriteBasicSetting(Settings::values.current_user); 1411 WriteBasicSetting(Settings::values.current_user);
@@ -1416,6 +1414,7 @@ void Config::SaveSystemValues() {
1416 false); 1414 false);
1417 WriteSetting(QStringLiteral("custom_rtc"), 1415 WriteSetting(QStringLiteral("custom_rtc"),
1418 QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0); 1416 QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0);
1417 WriteBasicSetting(Settings::values.device_name);
1419 } 1418 }
1420 1419
1421 WriteGlobalSetting(Settings::values.sound_index); 1420 WriteGlobalSetting(Settings::values.sound_index);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c6f285dc2..820f60e61 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2662,6 +2662,9 @@ void GMainWindow::OnMenuInstallToNAND() {
2662 return; 2662 return;
2663 } 2663 }
2664 2664
2665 // Save folder location of the first selected file
2666 UISettings::values.roms_path = QFileInfo(filenames[0]).path();
2667
2665 int remaining = filenames.size(); 2668 int remaining = filenames.size();
2666 2669
2667 // This would only overflow above 2^43 bytes (8.796 TB) 2670 // This would only overflow above 2^43 bytes (8.796 TB)
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 563818362..9f702fe95 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -186,7 +186,7 @@ pid_t SpawnChild(const char* arg0) {
186 return pid; 186 return pid;
187 } else if (pid == 0) { 187 } else if (pid == 0) {
188 // child 188 // child
189 execl(arg0, arg0, nullptr); 189 execlp(arg0, arg0, nullptr);
190 const int err = errno; 190 const int err = errno;
191 fmt::print(stderr, "execl failed with error {}\n", err); 191 fmt::print(stderr, "execl failed with error {}\n", err);
192 _exit(0); 192 _exit(0);
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index b27954512..3fa3d0afb 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -42,7 +42,7 @@ OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QStrin
42 MoveAndResizeWindow(); 42 MoveAndResizeWindow();
43 43
44 // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend 44 // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
45 if (system.IsPoweredOn()) { 45 if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) {
46 input_interpreter = std::make_unique<InputInterpreter>(system); 46 input_interpreter = std::make_unique<InputInterpreter>(system);
47 47
48 StartInputThread(); 48 StartInputThread();
@@ -83,6 +83,11 @@ void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const
83 ui->button_ok_label->setEnabled(false); 83 ui->button_ok_label->setEnabled(false);
84 } 84 }
85 85
86 if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) {
87 ui->buttonsDialog->hide();
88 return;
89 }
90
86 connect( 91 connect(
87 ui->button_cancel, &QPushButton::clicked, this, 92 ui->button_cancel, &QPushButton::clicked, this,
88 [this](bool) { 93 [this](bool) {
@@ -130,6 +135,11 @@ void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QS
130 ui->button_ok_rich->setEnabled(false); 135 ui->button_ok_rich->setEnabled(false);
131 } 136 }
132 137
138 if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) {
139 ui->buttonsRichDialog->hide();
140 return;
141 }
142
133 connect( 143 connect(
134 ui->button_cancel_rich, &QPushButton::clicked, this, 144 ui->button_cancel_rich, &QPushButton::clicked, this,
135 [this](bool) { 145 [this](bool) {