summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
32 files changed, 688 insertions, 323 deletions
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();
#define ASSERT_OR_EXECUTE(_a_, _b_) \
do { \
ASSERT(_a_); \
- if (!(_a_)) { \
+ if (!(_a_)) [[unlikely]] { \
_b_ \
} \
} while (0)
@@ -78,7 +78,7 @@ void assert_fail_impl();
#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
do { \
ASSERT_MSG(_a_, __VA_ARGS__); \
- if (!(_a_)) { \
+ if (!(_a_)) [[unlikely]] { \
_b_ \
} \
} 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
hle/kernel/k_event_info.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
+ hle/kernel/k_hardware_timer_base.h
+ hle/kernel/k_hardware_timer.cpp
+ hle/kernel/k_hardware_timer.h
hle/kernel/k_interrupt_manager.cpp
hle/kernel/k_interrupt_manager.h
hle/kernel/k_light_condition_variable.cpp
@@ -268,6 +271,7 @@ add_library(core STATIC
hle/kernel/k_thread_local_page.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
+ hle/kernel/k_timer_task.h
hle/kernel/k_trace.h
hle/kernel/k_transfer_memory.cpp
hle/kernel/k_transfer_memory.h
@@ -290,8 +294,6 @@ add_library(core STATIC
hle/kernel/svc_common.h
hle/kernel/svc_types.h
hle/kernel/svc_wrap.h
- hle/kernel/time_manager.cpp
- hle/kernel/time_manager.h
hle/result.h
hle/service/acc/acc.cpp
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() {
output_params[3].Set("output", true);
LoadTASParams();
+ LoadVirtualGamepadParams();
std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
@@ -163,6 +164,12 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateInputDevice);
std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
Common::Input::CreateInputDevice);
+
+ // Initialize virtual gamepad devices
+ std::ranges::transform(virtual_button_params, virtual_button_devices.begin(),
+ Common::Input::CreateInputDevice);
+ std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(),
+ Common::Input::CreateInputDevice);
}
void EmulatedController::LoadTASParams() {
@@ -205,6 +212,46 @@ void EmulatedController::LoadTASParams() {
tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
}
+void EmulatedController::LoadVirtualGamepadParams() {
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ Common::ParamPackage common_params{};
+ common_params.Set("engine", "virtual_gamepad");
+ common_params.Set("port", static_cast<int>(player_index));
+ for (auto& param : virtual_button_params) {
+ param = common_params;
+ }
+ for (auto& param : virtual_stick_params) {
+ param = common_params;
+ }
+
+ // TODO(german77): Replace this with an input profile or something better
+ virtual_button_params[Settings::NativeButton::A].Set("button", 0);
+ virtual_button_params[Settings::NativeButton::B].Set("button", 1);
+ virtual_button_params[Settings::NativeButton::X].Set("button", 2);
+ virtual_button_params[Settings::NativeButton::Y].Set("button", 3);
+ virtual_button_params[Settings::NativeButton::LStick].Set("button", 4);
+ virtual_button_params[Settings::NativeButton::RStick].Set("button", 5);
+ virtual_button_params[Settings::NativeButton::L].Set("button", 6);
+ virtual_button_params[Settings::NativeButton::R].Set("button", 7);
+ virtual_button_params[Settings::NativeButton::ZL].Set("button", 8);
+ virtual_button_params[Settings::NativeButton::ZR].Set("button", 9);
+ virtual_button_params[Settings::NativeButton::Plus].Set("button", 10);
+ virtual_button_params[Settings::NativeButton::Minus].Set("button", 11);
+ virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12);
+ virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
+ virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
+ virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
+ virtual_button_params[Settings::NativeButton::SL].Set("button", 16);
+ virtual_button_params[Settings::NativeButton::SR].Set("button", 17);
+ virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
+ virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
+
+ virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
+ virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
+ virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
+ virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
+}
+
void EmulatedController::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
LoadDevices();
@@ -322,6 +369,35 @@ void EmulatedController::ReloadInput() {
},
});
}
+
+ // Use a common UUID for Virtual Gamepad
+ static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
+
+ // Register virtual devices. No need to force update
+ for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) {
+ if (!virtual_button_devices[index]) {
+ continue;
+ }
+ virtual_button_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetButton(callback, index, VIRTUAL_UUID);
+ },
+ });
+ }
+
+ for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) {
+ if (!virtual_stick_devices[index]) {
+ continue;
+ }
+ virtual_stick_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetStick(callback, index, VIRTUAL_UUID);
+ },
+ });
+ }
}
void EmulatedController::UnloadInput() {
@@ -349,6 +425,12 @@ void EmulatedController::UnloadInput() {
for (auto& stick : tas_stick_devices) {
stick.reset();
}
+ for (auto& button : virtual_button_devices) {
+ button.reset();
+ }
+ for (auto& stick : virtual_stick_devices) {
+ stick.reset();
+ }
camera_devices.reset();
nfc_devices.reset();
}
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:
/// Set the params for TAS devices
void LoadTASParams();
+ /// Set the params for virtual pad devices
+ void LoadVirtualGamepadParams();
+
/**
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller style is fullkey
@@ -500,6 +503,12 @@ private:
ButtonDevices tas_button_devices;
StickDevices tas_stick_devices;
+ // Virtual gamepad related variables
+ ButtonParams virtual_button_params;
+ StickParams virtual_stick_params;
+ ButtonDevices virtual_button_devices;
+ StickDevices virtual_stick_devices;
+
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
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 @@
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
-#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"
namespace 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/k_hardware_timer.h"
+#include "core/hle/kernel/k_scheduler.h"
+
+namespace Kernel {
+
+void KHardwareTimer::Initialize() {
+ // Create the timing callback to register with CoreTiming.
+ m_event_type = Core::Timing::CreateEvent(
+ "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
+ reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
+ return std::nullopt;
+ });
+}
+
+void KHardwareTimer::Finalize() {
+ this->DisableInterrupt();
+ m_event_type.reset();
+}
+
+void KHardwareTimer::DoTask() {
+ // Handle the interrupt.
+ {
+ KScopedSchedulerLock slk{m_kernel};
+ KScopedSpinLock lk(this->GetLock());
+
+ //! Ignore this event if needed.
+ if (!this->GetInterruptEnabled()) {
+ return;
+ }
+
+ // Disable the timer interrupt while we handle this.
+ this->DisableInterrupt();
+
+ if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
+ 0 < next_time && next_time <= m_wakeup_time) {
+ // We have a next time, so we should set the time to interrupt and turn the interrupt
+ // on.
+ this->EnableInterrupt(next_time);
+ }
+ }
+
+ // Clear the timer interrupt.
+ // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer,
+ // GetCurrentCoreId());
+}
+
+void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
+ this->DisableInterrupt();
+
+ m_wakeup_time = wakeup_time;
+ m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
+ m_event_type, reinterpret_cast<uintptr_t>(this),
+ true);
+}
+
+void KHardwareTimer::DisableInterrupt() {
+ m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+ m_wakeup_time = std::numeric_limits<s64>::max();
+}
+
+s64 KHardwareTimer::GetTick() const {
+ return m_kernel.System().CoreTiming().GetGlobalTimeNs().count();
+}
+
+bool KHardwareTimer::GetInterruptEnabled() {
+ return m_wakeup_time != std::numeric_limits<s64>::max();
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_hardware_timer_base.h"
+
+namespace Core::Timing {
+struct EventType;
+} // namespace Core::Timing
+
+namespace Kernel {
+
+class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
+public:
+ explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
+
+ // Public API.
+ void Initialize();
+ void Finalize();
+
+ s64 GetCount() const {
+ return GetTick();
+ }
+
+ void RegisterTask(KTimerTask* task, s64 time_from_now) {
+ this->RegisterAbsoluteTask(task, GetTick() + time_from_now);
+ }
+
+ void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
+ KScopedDisableDispatch dd{m_kernel};
+ KScopedSpinLock lk{this->GetLock()};
+
+ if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
+ if (task_time <= m_wakeup_time) {
+ this->EnableInterrupt(task_time);
+ }
+ }
+ }
+
+private:
+ void EnableInterrupt(s64 wakeup_time);
+ void DisableInterrupt();
+ bool GetInterruptEnabled();
+ s64 GetTick() const;
+ void DoTask();
+
+private:
+ // Absolute time in nanoseconds
+ s64 m_wakeup_time{std::numeric_limits<s64>::max()};
+ std::shared_ptr<Core::Timing::EventType> m_event_type{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_timer_task.h"
+
+namespace Kernel {
+
+class KHardwareTimerBase {
+public:
+ explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {}
+
+ void CancelTask(KTimerTask* task) {
+ KScopedDisableDispatch dd{m_kernel};
+ KScopedSpinLock lk{m_lock};
+
+ if (const s64 task_time = task->GetTime(); task_time > 0) {
+ this->RemoveTaskFromTree(task);
+ }
+ }
+
+protected:
+ KSpinLock& GetLock() {
+ return m_lock;
+ }
+
+ s64 DoInterruptTaskImpl(s64 cur_time) {
+ // We want to handle all tasks, returning the next time that a task is scheduled.
+ while (true) {
+ // Get the next task. If there isn't one, return 0.
+ KTimerTask* task = m_next_task;
+ if (task == nullptr) {
+ return 0;
+ }
+
+ // If the task needs to be done in the future, do it in the future and not now.
+ if (const s64 task_time = task->GetTime(); task_time > cur_time) {
+ return task_time;
+ }
+
+ // Remove the task from the tree of tasks, and update our next task.
+ this->RemoveTaskFromTree(task);
+
+ // Handle the task.
+ task->OnTimer();
+ }
+ }
+
+ bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) {
+ ASSERT(task_time > 0);
+
+ // Set the task's time, and insert it into our tree.
+ task->SetTime(task_time);
+ m_task_tree.insert(*task);
+
+ // Update our next task if relevant.
+ if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) {
+ return false;
+ }
+ m_next_task = task;
+ return true;
+ }
+
+private:
+ void RemoveTaskFromTree(KTimerTask* task) {
+ // Erase from the tree.
+ auto it = m_task_tree.erase(m_task_tree.iterator_to(*task));
+
+ // Clear the task's scheduled time.
+ task->SetTime(0);
+
+ // Update our next task if relevant.
+ if (m_next_task == task) {
+ m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr;
+ }
+ }
+
+protected:
+ KernelCore& m_kernel;
+
+private:
+ using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>;
+
+ KSpinLock m_lock{};
+ TimerTaskTree m_task_tree{};
+ KTimerTask* m_next_task{};
+};
+
+} // 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 @@
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
+#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/time_manager.h"
namespace Kernel {
@@ -22,7 +22,7 @@ public:
~KScopedSchedulerLockAndSleep() {
// Register the sleep.
if (timeout_tick > 0) {
- kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
+ kernel.HardwareTimer().RegisterTask(thread, timeout_tick);
}
// 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 @@
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_timer_task.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_common.h"
@@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
- public boost::intrusive::list_base_hook<> {
+ public boost::intrusive::list_base_hook<>,
+ public KTimerTask {
KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
private:
@@ -840,4 +842,8 @@ private:
KernelCore& kernel;
};
+inline void KTimerTask::OnTimer() {
+ static_cast<KThread*>(this)->OnTimer();
+}
+
} // 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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/time_manager.h"
namespace Kernel {
@@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {
waiting_thread->ClearWaitQueue();
// Cancel the thread task.
- kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
+ kernel.HardwareTimer().CancelTask(waiting_thread);
}
void 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
// Cancel the thread task.
if (cancel_timer_task) {
- kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
+ kernel.HardwareTimer().CancelTask(waiting_thread);
}
}
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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/intrusive_red_black_tree.h"
+
+namespace Kernel {
+
+class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> {
+public:
+ static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) {
+ if (lhs.GetTime() < rhs.GetTime()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ constexpr explicit KTimerTask() = default;
+
+ constexpr void SetTime(s64 t) {
+ m_time = t;
+ }
+
+ constexpr s64 GetTime() const {
+ return m_time;
+ }
+
+ // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a
+ // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have
+ // devirtualized (see inline declaration for this inside k_thread.h).
+ void OnTimer();
+
+private:
+ // Absolute time in nanoseconds
+ s64 m_time{};
+};
+
+} // 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 @@
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_handle_table.h"
+#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_buffer.h"
@@ -39,7 +40,6 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/service_thread.h"
-#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/hle/service/sm/sm.h"
#include "core/memory.h"
@@ -55,7 +55,7 @@ struct KernelCore::Impl {
static constexpr size_t ReservedDynamicPageCount = 64;
explicit Impl(Core::System& system_, KernelCore& kernel_)
- : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
+ : service_threads_manager{1, "ServiceThreadsManager"},
service_thread_barrier{2}, system{system_} {}
void SetMulticore(bool is_multi) {
@@ -63,6 +63,9 @@ struct KernelCore::Impl {
}
void Initialize(KernelCore& kernel) {
+ hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel);
+ hardware_timer->Initialize();
+
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
@@ -193,6 +196,9 @@ struct KernelCore::Impl {
// Ensure that the object list container is finalized and properly shutdown.
global_object_list_container->Finalize();
global_object_list_container.reset();
+
+ hardware_timer->Finalize();
+ hardware_timer.reset();
}
void CloseServices() {
@@ -832,7 +838,7 @@ struct KernelCore::Impl {
std::vector<KProcess*> process_list;
std::atomic<KProcess*> current_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
- Kernel::TimeManager time_manager;
+ std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
Init::KSlabResourceCounts slab_resource_counts{};
KResourceLimit* system_resource_limit{};
@@ -1019,12 +1025,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
return impl->schedulers[core_id].get();
}
-Kernel::TimeManager& KernelCore::TimeManager() {
- return impl->time_manager;
-}
-
-const Kernel::TimeManager& KernelCore::TimeManager() const {
- return impl->time_manager;
+Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
+ return *impl->hardware_timer;
}
Core::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;
class KEvent;
class KEventInfo;
class KHandleTable;
+class KHardwareTimer;
class KLinkedListNode;
class KMemoryLayout;
class KMemoryManager;
@@ -63,7 +64,6 @@ class KCodeMemory;
class PhysicalCore;
class ServiceThread;
class Synchronization;
-class TimeManager;
using ServiceInterfaceFactory =
std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
@@ -175,11 +175,8 @@ public:
/// Gets the an instance of the current physical CPU core.
const Kernel::PhysicalCore& CurrentPhysicalCore() const;
- /// Gets the an instance of the TimeManager Interface.
- Kernel::TimeManager& TimeManager();
-
- /// Gets the an instance of the TimeManager Interface.
- const Kernel::TimeManager& TimeManager() const;
+ /// Gets the an instance of the hardware timer.
+ Kernel::KHardwareTimer& HardwareTimer();
/// Stops execution of 'id' core, in order to reschedule a new thread.
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 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/assert.h"
-#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/time_manager.h"
-
-namespace Kernel {
-
-TimeManager::TimeManager(Core::System& system_) : system{system_} {
- time_manager_event_type = Core::Timing::CreateEvent(
- "Kernel::TimeManagerCallback",
- [this](std::uintptr_t thread_handle, s64 time,
- std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
- KThread* thread = reinterpret_cast<KThread*>(thread_handle);
- {
- KScopedSchedulerLock sl(system.Kernel());
- thread->OnTimer();
- }
- return std::nullopt;
- });
-}
-
-void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
- std::scoped_lock lock{mutex};
- if (nanoseconds > 0) {
- ASSERT(thread);
- ASSERT(thread->GetState() != ThreadState::Runnable);
- system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
- time_manager_event_type,
- reinterpret_cast<uintptr_t>(thread));
- }
-}
-
-void TimeManager::UnscheduleTimeEvent(KThread* thread) {
- std::scoped_lock lock{mutex};
- system.CoreTiming().UnscheduleEvent(time_manager_event_type,
- reinterpret_cast<uintptr_t>(thread));
-}
-
-} // 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 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-#include <mutex>
-
-namespace Core {
-class System;
-} // namespace Core
-
-namespace Core::Timing {
-struct EventType;
-} // namespace Core::Timing
-
-namespace Kernel {
-
-class KThread;
-
-/**
- * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
- * method when the event is triggered.
- */
-class TimeManager {
-public:
- explicit TimeManager(Core::System& system);
-
- /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
- void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
-
- /// Unschedule an existing time event
- void UnscheduleTimeEvent(KThread* thread);
-
-private:
- Core::System& system;
- std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
- std::mutex mutex;
-};
-
-} // 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) {
}
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_NFC, "called");
+ LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
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) {
}
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_NFP, "called");
+ LOG_DEBUG(Service_NFP, "called");
if (state == State::NonInitialized) {
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
drivers/udp_client.h
drivers/virtual_amiibo.cpp
drivers/virtual_amiibo.h
+ drivers/virtual_gamepad.cpp
+ drivers/virtual_gamepad.h
helpers/stick_from_buttons.cpp
helpers/stick_from_buttons.h
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:
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
Common::Input::CameraFormat camera_format) override;
+private:
Common::Input::CameraStatus status{};
};
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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "input_common/drivers/virtual_gamepad.h"
+
+namespace InputCommon {
+constexpr std::size_t PlayerIndexCount = 10;
+
+VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
+ for (std::size_t i = 0; i < PlayerIndexCount; i++) {
+ PreSetController(GetIdentifier(i));
+ }
+}
+
+void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) {
+ if (player_index > PlayerIndexCount) {
+ return;
+ }
+ const auto identifier = GetIdentifier(player_index);
+ SetButton(identifier, button_id, value);
+}
+
+void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) {
+ SetButtonState(player_index, static_cast<int>(button_id), value);
+}
+
+void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value,
+ float y_value) {
+ if (player_index > PlayerIndexCount) {
+ return;
+ }
+ const auto identifier = GetIdentifier(player_index);
+ SetAxis(identifier, axis_id * 2, x_value);
+ SetAxis(identifier, (axis_id * 2) + 1, y_value);
+}
+
+void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
+ float y_value) {
+ SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value);
+}
+
+void VirtualGamepad::ResetControllers() {
+ for (std::size_t i = 0; i < PlayerIndexCount; i++) {
+ SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f);
+ SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f);
+
+ SetButtonState(i, VirtualButton::ButtonA, false);
+ SetButtonState(i, VirtualButton::ButtonB, false);
+ SetButtonState(i, VirtualButton::ButtonX, false);
+ SetButtonState(i, VirtualButton::ButtonY, false);
+ SetButtonState(i, VirtualButton::StickL, false);
+ SetButtonState(i, VirtualButton::StickR, false);
+ SetButtonState(i, VirtualButton::TriggerL, false);
+ SetButtonState(i, VirtualButton::TriggerR, false);
+ SetButtonState(i, VirtualButton::TriggerZL, false);
+ SetButtonState(i, VirtualButton::TriggerZR, false);
+ SetButtonState(i, VirtualButton::ButtonPlus, false);
+ SetButtonState(i, VirtualButton::ButtonMinus, false);
+ SetButtonState(i, VirtualButton::ButtonLeft, false);
+ SetButtonState(i, VirtualButton::ButtonUp, false);
+ SetButtonState(i, VirtualButton::ButtonRight, false);
+ SetButtonState(i, VirtualButton::ButtonDown, false);
+ SetButtonState(i, VirtualButton::ButtonSL, false);
+ SetButtonState(i, VirtualButton::ButtonSR, false);
+ SetButtonState(i, VirtualButton::ButtonHome, false);
+ SetButtonState(i, VirtualButton::ButtonCapture, false);
+ }
+}
+
+PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const {
+ return {
+ .guid = Common::UUID{},
+ .port = player_index,
+ .pad = 0,
+ };
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "input_common/input_engine.h"
+
+namespace InputCommon {
+
+/**
+ * A virtual controller that is always assigned to the game input
+ */
+class VirtualGamepad final : public InputEngine {
+public:
+ enum class VirtualButton {
+ ButtonA,
+ ButtonB,
+ ButtonX,
+ ButtonY,
+ StickL,
+ StickR,
+ TriggerL,
+ TriggerR,
+ TriggerZL,
+ TriggerZR,
+ ButtonPlus,
+ ButtonMinus,
+ ButtonLeft,
+ ButtonUp,
+ ButtonRight,
+ ButtonDown,
+ ButtonSL,
+ ButtonSR,
+ ButtonHome,
+ ButtonCapture,
+ };
+
+ enum class VirtualStick {
+ Left = 0,
+ Right = 1,
+ };
+
+ explicit VirtualGamepad(std::string input_engine_);
+
+ /**
+ * Sets the status of all buttons bound with the key to pressed
+ * @param player_index the player number that will take this action
+ * @param button_id the id of the button
+ * @param value indicates if the button is pressed or not
+ */
+ void SetButtonState(std::size_t player_index, int button_id, bool value);
+ void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value);
+
+ /**
+ * Sets the status of all buttons bound with the key to released
+ * @param player_index the player number that will take this action
+ * @param axis_id the id of the axis to move
+ * @param x_value the position of the stick in the x axis
+ * @param y_value the position of the stick in the y axis
+ */
+ void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value);
+ void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
+ float y_value);
+
+ /// Restores all inputs into the neutral position
+ void ResetControllers();
+
+private:
+ /// Returns the correct identifier corresponding to the player index
+ PadIdentifier GetIdentifier(std::size_t player_index) const;
+};
+
+} // 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 {
return false;
}
// The following drivers don't need to be mapped
- if (data.engine == "tas") {
- return false;
- }
- if (data.engine == "touch") {
- return false;
- }
if (data.engine == "touch_from_button") {
return false;
}
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 @@
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/udp_client.h"
#include "input_common/drivers/virtual_amiibo.h"
+#include "input_common/drivers/virtual_gamepad.h"
#include "input_common/helpers/stick_from_buttons.h"
#include "input_common/helpers/touch_from_buttons.h"
#include "input_common/input_engine.h"
@@ -25,73 +26,33 @@
namespace InputCommon {
struct InputSubsystem::Impl {
- void Initialize() {
- mapping_factory = std::make_shared<MappingFactory>();
+ template <typename Engine>
+ void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
- keyboard = std::make_shared<Keyboard>("keyboard");
- keyboard->SetMappingCallback(mapping_callback);
- keyboard_factory = std::make_shared<InputFactory>(keyboard);
- keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
- Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory);
- Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory);
-
- mouse = std::make_shared<Mouse>("mouse");
- mouse->SetMappingCallback(mapping_callback);
- mouse_factory = std::make_shared<InputFactory>(mouse);
- mouse_output_factory = std::make_shared<OutputFactory>(mouse);
- Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory);
- Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory);
-
- touch_screen = std::make_shared<TouchScreen>("touch");
- touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
- Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory);
-
- gcadapter = std::make_shared<GCAdapter>("gcpad");
- gcadapter->SetMappingCallback(mapping_callback);
- gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
- gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
- Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory);
- Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory);
-
- udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
- udp_client->SetMappingCallback(mapping_callback);
- udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
- udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
- Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory);
- Common::Input::RegisterOutputFactory(udp_client->GetEngineName(),
- udp_client_output_factory);
-
- tas_input = std::make_shared<TasInput::Tas>("tas");
- tas_input->SetMappingCallback(mapping_callback);
- tas_input_factory = std::make_shared<InputFactory>(tas_input);
- tas_output_factory = std::make_shared<OutputFactory>(tas_input);
- Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory);
- Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory);
-
- camera = std::make_shared<Camera>("camera");
- camera->SetMappingCallback(mapping_callback);
- camera_input_factory = std::make_shared<InputFactory>(camera);
- camera_output_factory = std::make_shared<OutputFactory>(camera);
- Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory);
- Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory);
-
- virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
- virtual_amiibo->SetMappingCallback(mapping_callback);
- virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
- virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
- Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(),
- virtual_amiibo_input_factory);
- Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(),
- virtual_amiibo_output_factory);
+ engine = std::make_shared<Engine>(name);
+ engine->SetMappingCallback(mapping_callback);
+
+ std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine);
+ std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine);
+ Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory));
+ Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory));
+ }
+
+ void Initialize() {
+ mapping_factory = std::make_shared<MappingFactory>();
+ RegisterEngine("keyboard", keyboard);
+ RegisterEngine("mouse", mouse);
+ RegisterEngine("touch", touch_screen);
+ RegisterEngine("gcpad", gcadapter);
+ RegisterEngine("cemuhookudp", udp_client);
+ RegisterEngine("tas", tas_input);
+ RegisterEngine("camera", camera);
+ RegisterEngine("virtual_amiibo", virtual_amiibo);
+ RegisterEngine("virtual_gamepad", virtual_gamepad);
#ifdef HAVE_SDL2
- sdl = std::make_shared<SDLDriver>("sdl");
- sdl->SetMappingCallback(mapping_callback);
- sdl_input_factory = std::make_shared<InputFactory>(sdl);
- sdl_output_factory = std::make_shared<OutputFactory>(sdl);
- Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory);
- Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory);
+ RegisterEngine("sdl", sdl);
#endif
Common::Input::RegisterInputFactory("touch_from_button",
@@ -100,42 +61,25 @@ struct InputSubsystem::Impl {
std::make_shared<StickFromButton>());
}
- void Shutdown() {
- Common::Input::UnregisterInputFactory(keyboard->GetEngineName());
- Common::Input::UnregisterOutputFactory(keyboard->GetEngineName());
- keyboard.reset();
-
- Common::Input::UnregisterInputFactory(mouse->GetEngineName());
- Common::Input::UnregisterOutputFactory(mouse->GetEngineName());
- mouse.reset();
-
- Common::Input::UnregisterInputFactory(touch_screen->GetEngineName());
- touch_screen.reset();
-
- Common::Input::UnregisterInputFactory(gcadapter->GetEngineName());
- Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName());
- gcadapter.reset();
-
- Common::Input::UnregisterInputFactory(udp_client->GetEngineName());
- Common::Input::UnregisterOutputFactory(udp_client->GetEngineName());
- udp_client.reset();
-
- Common::Input::UnregisterInputFactory(tas_input->GetEngineName());
- Common::Input::UnregisterOutputFactory(tas_input->GetEngineName());
- tas_input.reset();
-
- Common::Input::UnregisterInputFactory(camera->GetEngineName());
- Common::Input::UnregisterOutputFactory(camera->GetEngineName());
- camera.reset();
-
- Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName());
- Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());
- virtual_amiibo.reset();
+ template <typename Engine>
+ void UnregisterEngine(std::shared_ptr<Engine>& engine) {
+ Common::Input::UnregisterInputFactory(engine->GetEngineName());
+ Common::Input::UnregisterOutputFactory(engine->GetEngineName());
+ engine.reset();
+ }
+ void Shutdown() {
+ UnregisterEngine(keyboard);
+ UnregisterEngine(mouse);
+ UnregisterEngine(touch_screen);
+ UnregisterEngine(gcadapter);
+ UnregisterEngine(udp_client);
+ UnregisterEngine(tas_input);
+ UnregisterEngine(camera);
+ UnregisterEngine(virtual_amiibo);
+ UnregisterEngine(virtual_gamepad);
#ifdef HAVE_SDL2
- Common::Input::UnregisterInputFactory(sdl->GetEngineName());
- Common::Input::UnregisterOutputFactory(sdl->GetEngineName());
- sdl.reset();
+ UnregisterEngine(sdl);
#endif
Common::Input::UnregisterInputFactory("touch_from_button");
@@ -163,117 +107,86 @@ struct InputSubsystem::Impl {
return devices;
}
- [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
+ [[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine(
const Common::ParamPackage& params) const {
if (!params.Has("engine") || params.Get("engine", "") == "any") {
- return {};
+ return nullptr;
}
const std::string engine = params.Get("engine", "");
+ if (engine == keyboard->GetEngineName()) {
+ return keyboard;
+ }
if (engine == mouse->GetEngineName()) {
- return mouse->GetAnalogMappingForDevice(params);
+ return mouse;
}
if (engine == gcadapter->GetEngineName()) {
- return gcadapter->GetAnalogMappingForDevice(params);
+ return gcadapter;
}
if (engine == udp_client->GetEngineName()) {
- return udp_client->GetAnalogMappingForDevice(params);
- }
- if (engine == tas_input->GetEngineName()) {
- return tas_input->GetAnalogMappingForDevice(params);
+ return udp_client;
}
#ifdef HAVE_SDL2
if (engine == sdl->GetEngineName()) {
- return sdl->GetAnalogMappingForDevice(params);
+ return sdl;
}
#endif
- return {};
+ return nullptr;
}
- [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
+ [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
const Common::ParamPackage& params) const {
- if (!params.Has("engine") || params.Get("engine", "") == "any") {
+ const auto input_engine = GetInputEngine(params);
+
+ if (input_engine == nullptr) {
return {};
}
- const std::string engine = params.Get("engine", "");
- if (engine == gcadapter->GetEngineName()) {
- return gcadapter->GetButtonMappingForDevice(params);
- }
- if (engine == udp_client->GetEngineName()) {
- return udp_client->GetButtonMappingForDevice(params);
- }
- if (engine == tas_input->GetEngineName()) {
- return tas_input->GetButtonMappingForDevice(params);
- }
-#ifdef HAVE_SDL2
- if (engine == sdl->GetEngineName()) {
- return sdl->GetButtonMappingForDevice(params);
+
+ return input_engine->GetAnalogMappingForDevice(params);
+ }
+
+ [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
+ const Common::ParamPackage& params) const {
+ const auto input_engine = GetInputEngine(params);
+
+ if (input_engine == nullptr) {
+ return {};
}
-#endif
- return {};
+
+ return input_engine->GetButtonMappingForDevice(params);
}
[[nodiscard]] MotionMapping GetMotionMappingForDevice(
const Common::ParamPackage& params) const {
- if (!params.Has("engine") || params.Get("engine", "") == "any") {
+ const auto input_engine = GetInputEngine(params);
+
+ if (input_engine == nullptr) {
return {};
}
- const std::string engine = params.Get("engine", "");
- if (engine == udp_client->GetEngineName()) {
- return udp_client->GetMotionMappingForDevice(params);
- }
-#ifdef HAVE_SDL2
- if (engine == sdl->GetEngineName()) {
- return sdl->GetMotionMappingForDevice(params);
- }
-#endif
- return {};
+
+ return input_engine->GetMotionMappingForDevice(params);
}
Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
if (!params.Has("engine") || params.Get("engine", "") == "any") {
return Common::Input::ButtonNames::Undefined;
}
- const std::string engine = params.Get("engine", "");
- if (engine == mouse->GetEngineName()) {
- return mouse->GetUIName(params);
- }
- if (engine == gcadapter->GetEngineName()) {
- return gcadapter->GetUIName(params);
- }
- if (engine == udp_client->GetEngineName()) {
- return udp_client->GetUIName(params);
- }
- if (engine == tas_input->GetEngineName()) {
- return tas_input->GetUIName(params);
- }
-#ifdef HAVE_SDL2
- if (engine == sdl->GetEngineName()) {
- return sdl->GetUIName(params);
+ const auto input_engine = GetInputEngine(params);
+
+ if (input_engine == nullptr) {
+ return Common::Input::ButtonNames::Invalid;
}
-#endif
- return Common::Input::ButtonNames::Invalid;
+
+ return input_engine->GetUIName(params);
}
bool IsStickInverted(const Common::ParamPackage& params) {
- const std::string engine = params.Get("engine", "");
- if (engine == mouse->GetEngineName()) {
- return mouse->IsStickInverted(params);
- }
- if (engine == gcadapter->GetEngineName()) {
- return gcadapter->IsStickInverted(params);
- }
- if (engine == udp_client->GetEngineName()) {
- return udp_client->IsStickInverted(params);
- }
- if (engine == tas_input->GetEngineName()) {
- return tas_input->IsStickInverted(params);
- }
-#ifdef HAVE_SDL2
- if (engine == sdl->GetEngineName()) {
- return sdl->IsStickInverted(params);
+ const auto input_engine = GetInputEngine(params);
+
+ if (input_engine == nullptr) {
+ return false;
}
-#endif
- return false;
+
+ return input_engine->IsStickInverted(params);
}
bool IsController(const Common::ParamPackage& params) {
@@ -290,6 +203,9 @@ struct InputSubsystem::Impl {
if (engine == tas_input->GetEngineName()) {
return true;
}
+ if (engine == virtual_gamepad->GetEngineName()) {
+ return true;
+ }
#ifdef HAVE_SDL2
if (engine == sdl->GetEngineName()) {
return true;
@@ -338,28 +254,10 @@ struct InputSubsystem::Impl {
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
std::shared_ptr<Camera> camera;
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
-
- std::shared_ptr<InputFactory> keyboard_factory;
- std::shared_ptr<InputFactory> mouse_factory;
- std::shared_ptr<InputFactory> gcadapter_input_factory;
- std::shared_ptr<InputFactory> touch_screen_factory;
- std::shared_ptr<InputFactory> udp_client_input_factory;
- std::shared_ptr<InputFactory> tas_input_factory;
- std::shared_ptr<InputFactory> camera_input_factory;
- std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
-
- std::shared_ptr<OutputFactory> keyboard_output_factory;
- std::shared_ptr<OutputFactory> mouse_output_factory;
- std::shared_ptr<OutputFactory> gcadapter_output_factory;
- std::shared_ptr<OutputFactory> udp_client_output_factory;
- std::shared_ptr<OutputFactory> tas_output_factory;
- std::shared_ptr<OutputFactory> camera_output_factory;
- std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
+ std::shared_ptr<VirtualGamepad> virtual_gamepad;
#ifdef HAVE_SDL2
std::shared_ptr<SDLDriver> sdl;
- std::shared_ptr<InputFactory> sdl_input_factory;
- std::shared_ptr<OutputFactory> sdl_output_factory;
#endif
};
@@ -423,6 +321,14 @@ const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
return impl->virtual_amiibo.get();
}
+VirtualGamepad* InputSubsystem::GetVirtualGamepad() {
+ return impl->virtual_gamepad.get();
+}
+
+const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const {
+ return impl->virtual_gamepad.get();
+}
+
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
return impl->GetInputDevices();
}
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;
class Mouse;
class TouchScreen;
class VirtualAmiibo;
+class VirtualGamepad;
struct MappingData;
} // namespace InputCommon
@@ -108,6 +109,12 @@ public:
/// Retrieves the underlying virtual amiibo input device.
[[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
+ /// Retrieves the underlying virtual gamepad input device.
+ [[nodiscard]] VirtualGamepad* GetVirtualGamepad();
+
+ /// Retrieves the underlying virtual gamepad input device.
+ [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const;
+
/**
* Returns all available input devices that this Factory can create a new device with.
* 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 {
return "VK_ERROR_VALIDATION_FAILED_EXT";
case VkResult::VK_ERROR_INVALID_SHADER_NV:
return "VK_ERROR_INVALID_SHADER_NV";
+ case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
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() {
return;
}
- camera_data.resize(CAMERA_WIDTH * CAMERA_HEIGHT);
+ const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
+ const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
+ camera_data.resize(camera_width * camera_height);
camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
&GRenderWindow::OnCameraCapture);
@@ -796,14 +798,22 @@ void GRenderWindow::RequestCameraCapture() {
}
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
// TODO: Capture directly in the format and resolution needed
+ const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
+ const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
const auto converted =
- img.scaled(CAMERA_WIDTH, CAMERA_HEIGHT, Qt::AspectRatioMode::IgnoreAspectRatio,
+ img.scaled(static_cast<int>(camera_width), static_cast<int>(camera_height),
+ Qt::AspectRatioMode::IgnoreAspectRatio,
Qt::TransformationMode::SmoothTransformation)
.mirrored(false, true);
- std::memcpy(camera_data.data(), converted.bits(), CAMERA_WIDTH * CAMERA_HEIGHT * sizeof(u32));
- input_subsystem->GetCamera()->SetCameraData(CAMERA_WIDTH, CAMERA_HEIGHT, camera_data);
+ if (camera_data.size() != camera_width * camera_height) {
+ camera_data.resize(camera_width * camera_height);
+ }
+ std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
+ input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
pending_camera_snapshots = 0;
+#endif
}
bool 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:
bool first_frame = false;
InputCommon::TasInput::TasState last_tas_state;
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
bool is_virtual_camera;
int pending_camera_snapshots;
-#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
+ std::vector<u32> camera_data;
std::unique_ptr<QCamera> camera;
std::unique_ptr<QCameraImageCapture> camera_capture;
- static constexpr std::size_t CAMERA_WIDTH = 320;
- static constexpr std::size_t CAMERA_HEIGHT = 240;
- std::vector<u32> camera_data;
-#endif
std::unique_ptr<QTimer> camera_timer;
+#endif
Core::System& system;
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() {
}
}
- ReadBasicSetting(Settings::values.device_name);
-
if (global) {
ReadBasicSetting(Settings::values.current_user);
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
@@ -797,6 +795,7 @@ void Config::ReadSystemValues() {
} else {
Settings::values.custom_rtc = std::nullopt;
}
+ ReadBasicSetting(Settings::values.device_name);
}
ReadGlobalSetting(Settings::values.sound_index);
@@ -1407,7 +1406,6 @@ void Config::SaveSystemValues() {
Settings::values.rng_seed.UsingGlobal());
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),
0, Settings::values.rng_seed.UsingGlobal());
- WriteBasicSetting(Settings::values.device_name);
if (global) {
WriteBasicSetting(Settings::values.current_user);
@@ -1416,6 +1414,7 @@ void Config::SaveSystemValues() {
false);
WriteSetting(QStringLiteral("custom_rtc"),
QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0);
+ WriteBasicSetting(Settings::values.device_name);
}
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() {
return;
}
+ // Save folder location of the first selected file
+ UISettings::values.roms_path = QFileInfo(filenames[0]).path();
+
int remaining = filenames.size();
// 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) {
return pid;
} else if (pid == 0) {
// child
- execl(arg0, arg0, nullptr);
+ execlp(arg0, arg0, nullptr);
const int err = errno;
fmt::print(stderr, "execl failed with error {}\n", err);
_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
MoveAndResizeWindow();
// TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
- if (system.IsPoweredOn()) {
+ if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) {
input_interpreter = std::make_unique<InputInterpreter>(system);
StartInputThread();
@@ -83,6 +83,11 @@ void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const
ui->button_ok_label->setEnabled(false);
}
+ if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) {
+ ui->buttonsDialog->hide();
+ return;
+ }
+
connect(
ui->button_cancel, &QPushButton::clicked, this,
[this](bool) {
@@ -130,6 +135,11 @@ void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QS
ui->button_ok_rich->setEnabled(false);
}
+ if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) {
+ ui->buttonsRichDialog->hide();
+ return;
+ }
+
connect(
ui->button_cancel_rich, &QPushButton::clicked, this,
[this](bool) {