diff options
Diffstat (limited to '')
47 files changed, 494 insertions, 197 deletions
diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 36c3b289a090aaf59a24346f57ebe1b13efb36c +Subproject 828959caedfac2d456a0c877fda4612e35fffc0 diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 0c5d2b3b0..7a047803e 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -27,6 +27,10 @@ struct AnalogProperties { float range; float threshold; }; +template <typename StatusType> +struct InputCallback { + std::function<void(StatusType)> on_change; +}; /// An abstract class template for an input device (a button, an analog input, etc.). template <typename StatusType> @@ -50,6 +54,17 @@ public: [[maybe_unused]] f32 freq_high) const { return {}; } + void SetCallback(InputCallback<StatusType> callback_) { + callback = std::move(callback_); + } + void TriggerOnChange() { + if (callback.on_change) { + callback.on_change(GetStatus()); + } + } + +private: + InputCallback<StatusType> callback; }; /// An abstract class template for a factory that can create input devices. diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 2b5c30f7a..260af87e5 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -30,9 +30,16 @@ namespace Kernel { -SessionRequestHandler::SessionRequestHandler() = default; +SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) + : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} -SessionRequestHandler::~SessionRequestHandler() = default; +SessionRequestHandler::~SessionRequestHandler() { + kernel.ReleaseServiceThread(service_thread); +} + +SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} + +SessionRequestManager::~SessionRequestManager() = default; void SessionRequestHandler::ClientConnected(KServerSession* session) { session->SetSessionHandler(shared_from_this()); diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b47e363cc..2aaf93fca 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -46,6 +46,7 @@ class KThread; class KReadableEvent; class KSession; class KWritableEvent; +class ServiceThread; enum class ThreadWakeupReason; @@ -56,7 +57,7 @@ enum class ThreadWakeupReason; */ class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { public: - SessionRequestHandler(); + SessionRequestHandler(KernelCore& kernel, const char* service_name_); virtual ~SessionRequestHandler(); /** @@ -83,6 +84,14 @@ public: * @param server_session ServerSession associated with the connection. */ void ClientDisconnected(KServerSession* session); + + std::shared_ptr<ServiceThread> GetServiceThread() const { + return service_thread.lock(); + } + +protected: + KernelCore& kernel; + std::weak_ptr<ServiceThread> service_thread; }; using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; @@ -94,7 +103,8 @@ using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; */ class SessionRequestManager final { public: - SessionRequestManager() = default; + explicit SessionRequestManager(KernelCore& kernel); + ~SessionRequestManager(); bool IsDomain() const { return is_domain; @@ -142,10 +152,18 @@ public: session_handler = std::move(handler); } + std::shared_ptr<ServiceThread> GetServiceThread() const { + return session_handler->GetServiceThread(); + } + private: bool is_domain{}; SessionRequestHandlerPtr session_handler; std::vector<SessionRequestHandlerPtr> domain_handlers; + +private: + KernelCore& kernel; + std::weak_ptr<ServiceThread> service_thread; }; /** diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index bc18582be..88a052f65 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -7,10 +7,11 @@ #include <atomic> #include <string> +#include <boost/intrusive/rbtree.hpp> + #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/intrusive_red_black_tree.h" #include "core/hle/kernel/k_class_token.h" namespace Kernel { @@ -175,7 +176,7 @@ private: class KAutoObjectWithListContainer; -class KAutoObjectWithList : public KAutoObject { +class KAutoObjectWithList : public KAutoObject, public boost::intrusive::set_base_hook<> { public: explicit KAutoObjectWithList(KernelCore& kernel_) : KAutoObject(kernel_) {} @@ -192,6 +193,10 @@ public: } } + friend bool operator<(const KAutoObjectWithList& left, const KAutoObjectWithList& right) { + return &left < &right; + } + public: virtual u64 GetId() const { return reinterpret_cast<u64>(this); @@ -203,8 +208,6 @@ public: private: friend class KAutoObjectWithListContainer; - - Common::IntrusiveRedBlackTreeNode list_node; }; template <typename T> diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp index fc0c28874..010006bb7 100644 --- a/src/core/hle/kernel/k_auto_object_container.cpp +++ b/src/core/hle/kernel/k_auto_object_container.cpp @@ -9,13 +9,13 @@ namespace Kernel { void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) { KScopedLightLock lk(m_lock); - m_object_list.insert(*obj); + m_object_list.insert_unique(*obj); } void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) { KScopedLightLock lk(m_lock); - m_object_list.erase(m_object_list.iterator_to(*obj)); + m_object_list.erase(*obj); } size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) { diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h index ff40cf5a7..459953450 100644 --- a/src/core/hle/kernel/k_auto_object_container.h +++ b/src/core/hle/kernel/k_auto_object_container.h @@ -6,6 +6,8 @@ #include <atomic> +#include <boost/intrusive/rbtree.hpp> + #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -23,8 +25,7 @@ class KAutoObjectWithListContainer { YUZU_NON_MOVEABLE(KAutoObjectWithListContainer); public: - using ListType = Common::IntrusiveRedBlackTreeMemberTraits< - &KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>; + using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; public: class ListAccessor : public KScopedLightLock { diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 23d830d1f..50606bd91 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -16,11 +16,11 @@ namespace Kernel { KClientPort::KClientPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} KClientPort::~KClientPort() = default; -void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_) { +void KClientPort::Initialize(KPort* parent_port_, s32 max_sessions_, std::string&& name_) { // Set member variables. num_sessions = 0; peak_sessions = 0; - parent = parent_; + parent = parent_port_; max_sessions = max_sessions_; name = std::move(name_); } @@ -56,7 +56,8 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -ResultCode KClientPort::CreateSession(KClientSession** out) { +ResultCode KClientPort::CreateSession(KClientSession** out, + std::shared_ptr<SessionRequestManager> session_manager) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), LimitableResource::Sessions); @@ -101,7 +102,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) { } // Initialize the session. - session->Initialize(this, parent->GetName()); + session->Initialize(this, parent->GetName(), session_manager); // Commit the session reservation. session_reservation.Commit(); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index f2fff3b01..54bb05e20 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -16,6 +16,7 @@ namespace Kernel { class KClientSession; class KernelCore; class KPort; +class SessionRequestManager; class KClientPort final : public KSynchronizationObject { KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject); @@ -52,7 +53,8 @@ public: void Destroy() override; bool IsSignaled() const override; - ResultCode CreateSession(KClientSession** out); + ResultCode CreateSession(KClientSession** out, + std::shared_ptr<SessionRequestManager> session_manager = nullptr); private: std::atomic<s32> num_sessions{}; diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index b11d5b4e3..230e3b6b8 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -36,9 +36,9 @@ public: explicit KClientSession(KernelCore& kernel_); ~KClientSession() override; - void Initialize(KSession* parent_, std::string&& name_) { + void Initialize(KSession* parent_session_, std::string&& name_) { // Set member variables. - parent = parent_; + parent = parent_session_; name = std::move(name_); } diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index b2850ac7b..149fa78dd 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -21,9 +21,9 @@ public: explicit KReadableEvent(KernelCore& kernel_); ~KReadableEvent() override; - void Initialize(KEvent* parent_, std::string&& name_) { + void Initialize(KEvent* parent_event_, std::string&& name_) { is_signaled = false; - parent = parent_; + parent = parent_event_; name = std::move(name_); } diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 8cbde177a..c5dc58387 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -17,9 +17,9 @@ namespace Kernel { KServerPort::KServerPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} KServerPort::~KServerPort() = default; -void KServerPort::Initialize(KPort* parent_, std::string&& name_) { +void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) { // Set member variables. - parent = parent_; + parent = parent_port_; name = std::move(name_); } diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index 55481d63f..67a36da40 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -29,7 +29,7 @@ public: explicit KServerPort(KernelCore& kernel_); ~KServerPort() override; - void Initialize(KPort* parent_, std::string&& name_); + void Initialize(KPort* parent_port_, std::string&& name_); /// Whether or not this server port has an HLE handler available. bool HasSessionRequestHandler() const { diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index dbf03b462..528ca8614 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -13,8 +13,10 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_handle_table.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" @@ -23,18 +25,21 @@ namespace Kernel { -KServerSession::KServerSession(KernelCore& kernel_) - : KSynchronizationObject{kernel_}, manager{std::make_shared<SessionRequestManager>()} {} +KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} -KServerSession::~KServerSession() { - kernel.ReleaseServiceThread(service_thread); -} +KServerSession::~KServerSession() {} -void KServerSession::Initialize(KSession* parent_, std::string&& name_) { +void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, + std::shared_ptr<SessionRequestManager> manager_) { // Set member variables. - parent = parent_; + parent = parent_session_; name = std::move(name_); - service_thread = kernel.CreateServiceThread(name); + + if (manager_) { + manager = manager_; + } else { + manager = std::make_shared<SessionRequestManager>(kernel); + } } void KServerSession::Destroy() { @@ -114,9 +119,11 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); - if (auto strong_ptr = service_thread.lock()) { + if (auto strong_ptr = manager->GetServiceThread(); strong_ptr) { strong_ptr->QueueSyncRequest(*parent, std::move(context)); return ResultSuccess; + } else { + ASSERT_MSG(false, "strong_ptr was nullptr!"); } return ResultSuccess; diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 27b757ad2..9efd400bc 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -32,6 +32,7 @@ class HLERequestContext; class KernelCore; class KSession; class SessionRequestHandler; +class SessionRequestManager; class KThread; class KServerSession final : public KSynchronizationObject, @@ -46,7 +47,8 @@ public: void Destroy() override; - void Initialize(KSession* parent_, std::string&& name_); + void Initialize(KSession* parent_session_, std::string&& name_, + std::shared_ptr<SessionRequestManager> manager_); KSession* GetParent() { return parent; @@ -104,16 +106,6 @@ public: return manager; } - /// Gets the session request manager, which forwards requests to the underlying service - const std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() const { - return manager; - } - - /// Sets the session request manager, which forwards requests to the underlying service - void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) { - manager = std::move(manager_); - } - private: /// Queues a sync request from the emulated application. ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); @@ -131,9 +123,6 @@ private: /// When set to True, converts the session to a domain at the end of the command bool convert_to_domain{}; - /// Thread to dispatch service requests - std::weak_ptr<ServiceThread> service_thread; - /// KSession that owns this KServerSession KSession* parent{}; }; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index 025b8b555..940878e03 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -15,7 +15,8 @@ KSession::KSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} KSession::~KSession() = default; -void KSession::Initialize(KClientPort* port_, const std::string& name_) { +void KSession::Initialize(KClientPort* port_, const std::string& name_, + std::shared_ptr<SessionRequestManager> manager_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed @@ -27,7 +28,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_) { KAutoObject::Create(std::addressof(client)); // Initialize our sub sessions. - server.Initialize(this, name_ + ":Server"); + server.Initialize(this, name_ + ":Server", manager_); client.Initialize(this, name_ + ":Client"); // Set state and name. diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index 4ddd080d2..62c328a68 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -13,6 +13,8 @@ namespace Kernel { +class SessionRequestManager; + class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> { KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject); @@ -20,7 +22,8 @@ public: explicit KSession(KernelCore& kernel_); ~KSession() override; - void Initialize(KClientPort* port_, const std::string& name_); + void Initialize(KClientPort* port_, const std::string& name_, + std::shared_ptr<SessionRequestManager> manager_ = nullptr); void Finalize() override; diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp index b7b83c151..bdb1db6d5 100644 --- a/src/core/hle/kernel/k_writable_event.cpp +++ b/src/core/hle/kernel/k_writable_event.cpp @@ -13,8 +13,8 @@ KWritableEvent::KWritableEvent(KernelCore& kernel_) KWritableEvent::~KWritableEvent() = default; -void KWritableEvent::Initialize(KEvent* parent_, std::string&& name_) { - parent = parent_; +void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) { + parent = parent_event_; name = std::move(name_); parent->GetReadableEvent().Open(); } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 605236552..a755008d5 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -124,21 +124,21 @@ union ResultCode { constexpr ResultCode(ErrorModule module_, u32 description_) : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} - constexpr bool IsSuccess() const { + [[nodiscard]] constexpr bool IsSuccess() const { return raw == 0; } - constexpr bool IsError() const { - return raw != 0; + [[nodiscard]] constexpr bool IsError() const { + return !IsSuccess(); } }; -constexpr bool operator==(const ResultCode& a, const ResultCode& b) { +[[nodiscard]] constexpr bool operator==(const ResultCode& a, const ResultCode& b) { return a.raw == b.raw; } -constexpr bool operator!=(const ResultCode& a, const ResultCode& b) { - return a.raw != b.raw; +[[nodiscard]] constexpr bool operator!=(const ResultCode& a, const ResultCode& b) { + return !operator==(a, b); } // Convenience functions for creating some common kinds of errors: @@ -200,7 +200,7 @@ public: * specify the success code. `success_code` must not be an error code. */ template <typename... Args> - static ResultVal WithCode(ResultCode success_code, Args&&... args) { + [[nodiscard]] static ResultVal WithCode(ResultCode success_code, Args&&... args) { ResultVal<T> result; result.emplace(success_code, std::forward<Args>(args)...); return result; @@ -259,49 +259,49 @@ public: } /// Returns true if the `ResultVal` contains an error code and no value. - bool empty() const { + [[nodiscard]] bool empty() const { return result_code.IsError(); } /// Returns true if the `ResultVal` contains a return value. - bool Succeeded() const { + [[nodiscard]] bool Succeeded() const { return result_code.IsSuccess(); } /// Returns true if the `ResultVal` contains an error code and no value. - bool Failed() const { + [[nodiscard]] bool Failed() const { return empty(); } - ResultCode Code() const { + [[nodiscard]] ResultCode Code() const { return result_code; } - const T& operator*() const { + [[nodiscard]] const T& operator*() const { return object; } - T& operator*() { + [[nodiscard]] T& operator*() { return object; } - const T* operator->() const { + [[nodiscard]] const T* operator->() const { return &object; } - T* operator->() { + [[nodiscard]] T* operator->() { return &object; } /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. template <typename U> - T ValueOr(U&& value) const { + [[nodiscard]] T ValueOr(U&& value) const { return !empty() ? object : std::move(value); } /// Asserts that the result succeeded and returns a reference to it. - T& Unwrap() & { + [[nodiscard]] T& Unwrap() & { ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); return **this; } - T&& Unwrap() && { + [[nodiscard]] T&& Unwrap() && { ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); return std::move(**this); } @@ -320,7 +320,7 @@ private: * `T` with and creates a success `ResultVal` contained the constructed value. */ template <typename T, typename... Args> -ResultVal<T> MakeResult(Args&&... args) { +[[nodiscard]] ResultVal<T> MakeResult(Args&&... args) { return ResultVal<T>::WithCode(ResultSuccess, std::forward<Args>(args)...); } @@ -329,7 +329,7 @@ ResultVal<T> MakeResult(Args&&... args) { * copy or move constructing. */ template <typename Arg> -ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { +[[nodiscard]] ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { return ResultVal<std::remove_reference_t<Arg>>::WithCode(ResultSuccess, std::forward<Arg>(arg)); } diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 6e5ba26a3..74cc45f1e 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -254,8 +254,6 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NS, "called"); // Create shared font memory object - auto& kernel = system.Kernel(); - std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), impl->shared_font->size()); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 7a15eeba0..4e1541630 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -93,8 +93,8 @@ namespace Service { ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, u32 max_sessions_, InvokerFn* handler_invoker_) - : system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, - handler_invoker{handler_invoker_} {} + : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, + service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying @@ -111,7 +111,7 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) port_installed = true; } -Kernel::KClientPort& ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) { +Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); ASSERT(!port_installed); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 4c048173b..ec757753c 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -23,6 +23,7 @@ namespace Kernel { class HLERequestContext; class KClientPort; class KServerSession; +class ServiceThread; } // namespace Kernel namespace Service { @@ -41,7 +42,7 @@ class ServiceManager; static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) /// Arbitrary default number of maximum connections to an HLE service. -static const u32 DefaultMaxSessions = 10; +static const u32 DefaultMaxSessions = 64; /** * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it @@ -74,7 +75,7 @@ public: void InvokeRequestTipc(Kernel::HLERequestContext& ctx); /// Creates a port pair and registers it on the kernel's global port registry. - Kernel::KClientPort& CreatePort(Kernel::KernelCore& kernel); + Kernel::KClientPort& CreatePort(); /// Handles a synchronization request for the service. ResultCode HandleSyncRequest(Kernel::KServerSession& session, diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index 5fa5e0512..8b9418e0f 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -28,42 +28,25 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { } void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { - // TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong - // and that we probably want to actually make an entirely new Session, but we still need to - // verify this on hardware. - LOG_DEBUG(Service, "called"); - auto& kernel = system.Kernel(); - auto* session = ctx.Session()->GetParent(); - auto* port = session->GetParent()->GetParent(); + auto& parent_session = *ctx.Session()->GetParent(); + auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); + auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); - // Reserve a new session from the process resource limit. - Kernel::KScopedResourceReservation session_reservation( - kernel.CurrentProcess()->GetResourceLimit(), Kernel::LimitableResource::Sessions); - if (!session_reservation.Succeeded()) { + // Create a session. + Kernel::KClientSession* session{}; + const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager); + if (result.IsError()) { + LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultLimitReached); + rb.Push(result); } - // Create a new session. - auto* clone = Kernel::KSession::Create(kernel); - clone->Initialize(&port->GetClientPort(), session->GetName()); - - // Commit the session reservation. - session_reservation.Commit(); - - // Enqueue the session with the named port. - port->EnqueueSession(&clone->GetServerSession()); - - // Set the session request manager. - clone->GetServerSession().SetSessionRequestManager( - session->GetServerSession().GetSessionRequestManager()); - // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(ResultSuccess); - rb.PushMoveObjects(clone->GetClientSession()); + rb.PushMoveObjects(session); } void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index d8b20a3f2..bffa9ffcb 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -46,7 +46,7 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core self.sm_interface = sm; self.controller_interface = std::make_unique<Controller>(system); - return sm->CreatePort(system.Kernel()); + return sm->CreatePort(); } ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name, diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index f8ec179d0..100138d11 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -21,104 +21,153 @@ public: : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) { - update_thread_running.store(true); - update_thread = std::thread(&Analog::UpdateStatus, this); + Input::InputCallback<bool> callbacks{ + [this]([[maybe_unused]] bool status) { UpdateStatus(); }}; + up->SetCallback(callbacks); + down->SetCallback(callbacks); + left->SetCallback(callbacks); + right->SetCallback(callbacks); } - ~Analog() override { - if (update_thread_running.load()) { - update_thread_running.store(false); - if (update_thread.joinable()) { - update_thread.join(); - } - } + bool IsAngleGreater(float old_angle, float new_angle) const { + constexpr float TAU = Common::PI * 2.0f; + // Use wider angle to ease the transition. + constexpr float aperture = TAU * 0.15f; + const float top_limit = new_angle + aperture; + return (old_angle > new_angle && old_angle <= top_limit) || + (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); } - void MoveToDirection(bool enable, float to_angle) { - if (!enable) { - return; - } + bool IsAngleSmaller(float old_angle, float new_angle) const { constexpr float TAU = Common::PI * 2.0f; // Use wider angle to ease the transition. constexpr float aperture = TAU * 0.15f; - const float top_limit = to_angle + aperture; - const float bottom_limit = to_angle - aperture; - - if ((angle > to_angle && angle <= top_limit) || - (angle + TAU > to_angle && angle + TAU <= top_limit)) { - angle -= modifier_angle; - if (angle < 0) { - angle += TAU; + const float bottom_limit = new_angle - aperture; + return (old_angle >= bottom_limit && old_angle < new_angle) || + (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); + } + + float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { + constexpr float TAU = Common::PI * 2.0f; + float new_angle = angle; + + auto time_difference = static_cast<float>( + std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); + time_difference /= 1000.0f * 1000.0f; + if (time_difference > 0.5f) { + time_difference = 0.5f; + } + + if (IsAngleGreater(new_angle, goal_angle)) { + new_angle -= modifier_angle * time_difference; + if (new_angle < 0) { + new_angle += TAU; + } + if (!IsAngleGreater(new_angle, goal_angle)) { + return goal_angle; } - } else if ((angle >= bottom_limit && angle < to_angle) || - (angle - TAU >= bottom_limit && angle - TAU < to_angle)) { - angle += modifier_angle; - if (angle >= TAU) { - angle -= TAU; + } else if (IsAngleSmaller(new_angle, goal_angle)) { + new_angle += modifier_angle * time_difference; + if (new_angle >= TAU) { + new_angle -= TAU; + } + if (!IsAngleSmaller(new_angle, goal_angle)) { + return goal_angle; } } else { - angle = to_angle; + return goal_angle; } + return new_angle; } - void UpdateStatus() { - while (update_thread_running.load()) { - const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; - - bool r = right->GetStatus(); - bool l = left->GetStatus(); - bool u = up->GetStatus(); - bool d = down->GetStatus(); - - // Eliminate contradictory movements - if (r && l) { - r = false; - l = false; - } - if (u && d) { - u = false; - d = false; - } + void SetGoalAngle(bool r, bool l, bool u, bool d) { + // Move to the right + if (r && !u && !d) { + goal_angle = 0.0f; + } + + // Move to the upper right + if (r && u && !d) { + goal_angle = Common::PI * 0.25f; + } - // Move to the right - MoveToDirection(r && !u && !d, 0.0f); + // Move up + if (u && !l && !r) { + goal_angle = Common::PI * 0.5f; + } - // Move to the upper right - MoveToDirection(r && u && !d, Common::PI * 0.25f); + // Move to the upper left + if (l && u && !d) { + goal_angle = Common::PI * 0.75f; + } - // Move up - MoveToDirection(u && !l && !r, Common::PI * 0.5f); + // Move to the left + if (l && !u && !d) { + goal_angle = Common::PI; + } - // Move to the upper left - MoveToDirection(l && u && !d, Common::PI * 0.75f); + // Move to the bottom left + if (l && !u && d) { + goal_angle = Common::PI * 1.25f; + } - // Move to the left - MoveToDirection(l && !u && !d, Common::PI); + // Move down + if (d && !l && !r) { + goal_angle = Common::PI * 1.5f; + } - // Move to the bottom left - MoveToDirection(l && !u && d, Common::PI * 1.25f); + // Move to the bottom right + if (r && !u && d) { + goal_angle = Common::PI * 1.75f; + } + } - // Move down - MoveToDirection(d && !l && !r, Common::PI * 1.5f); + void UpdateStatus() { + const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; - // Move to the bottom right - MoveToDirection(r && !u && d, Common::PI * 1.75f); + bool r = right->GetStatus(); + bool l = left->GetStatus(); + bool u = up->GetStatus(); + bool d = down->GetStatus(); - // Move if a key is pressed - if (r || l || u || d) { - amplitude = coef; - } else { - amplitude = 0; - } + // Eliminate contradictory movements + if (r && l) { + r = false; + l = false; + } + if (u && d) { + u = false; + d = false; + } - // Delay the update rate to 100hz - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // Move if a key is pressed + if (r || l || u || d) { + amplitude = coef; + } else { + amplitude = 0; } + + const auto now = std::chrono::steady_clock::now(); + const auto time_difference = static_cast<u64>( + std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count()); + + if (time_difference < 10) { + // Disable analog mode if inputs are too fast + SetGoalAngle(r, l, u, d); + angle = goal_angle; + } else { + angle = GetAngle(now); + SetGoalAngle(r, l, u, d); + } + + last_update = now; } std::tuple<float, float> GetStatus() const override { if (Settings::values.emulate_analog_keyboard) { - return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude); + const auto now = std::chrono::steady_clock::now(); + float angle_ = GetAngle(now); + return std::make_tuple(std::cos(angle_) * amplitude, std::sin(angle_) * amplitude); } constexpr float SQRT_HALF = 0.707106781f; int x = 0, y = 0; @@ -166,9 +215,9 @@ private: float modifier_scale; float modifier_angle; float angle{}; + float goal_angle{}; float amplitude{}; - std::thread update_thread; - std::atomic<bool> update_thread_running{}; + std::chrono::time_point<std::chrono::steady_clock> last_update; }; std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { @@ -179,7 +228,7 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); auto modifier_scale = params.Get("modifier_scale", 0.5f); - auto modifier_angle = params.Get("modifier_angle", 0.035f); + auto modifier_angle = params.Get("modifier_angle", 5.5f); return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), std::move(right), std::move(modifier), modifier_scale, modifier_angle); diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index c467ff4c5..8261e76fd 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp @@ -75,6 +75,7 @@ public: } else { pair.key_button->UnlockButton(); } + pair.key_button->TriggerOnChange(); } } } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9e6b87960..d371b842f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -110,6 +110,8 @@ public: void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size); + void DisableGraphicsUniformBuffer(size_t stage, u32 index); + void UpdateGraphicsBuffers(bool is_indexed); void UpdateComputeBuffers(); @@ -419,10 +421,6 @@ template <class P> void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) { const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); - if (!cpu_addr) { - uniform_buffers[stage][index] = NULL_BINDING; - return; - } const Binding binding{ .cpu_addr = *cpu_addr, .size = size, @@ -432,6 +430,11 @@ void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr } template <class P> +void BufferCache<P>::DisableGraphicsUniformBuffer(size_t stage, u32 index) { + uniform_buffers[stage][index] = NULL_BINDING; +} + +template <class P> void BufferCache<P>::UpdateGraphicsBuffers(bool is_indexed) { MICROPROFILE_SCOPE(GPU_PrepareBuffers); do { diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 75517a4f7..aab6b8f7a 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -578,8 +578,12 @@ void Maxwell3D::ProcessCBBind(size_t stage_index) { buffer.size = regs.const_buffer.cb_size; const bool is_enabled = bind_data.valid.Value() != 0; - const GPUVAddr gpu_addr = is_enabled ? regs.const_buffer.BufferAddress() : 0; - const u32 size = is_enabled ? regs.const_buffer.cb_size : 0; + if (!is_enabled) { + rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.index); + return; + } + const GPUVAddr gpu_addr = regs.const_buffer.BufferAddress(); + const u32 size = regs.const_buffer.cb_size; rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); } diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index eb58ac6b6..7124c755c 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -163,6 +163,9 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size } std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { + if (gpu_addr == 0) { + return std::nullopt; + } const auto page_entry{GetPageEntry(gpu_addr)}; if (!page_entry.IsValid()) { return std::nullopt; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 50491b758..f968b5b16 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -54,6 +54,9 @@ public: virtual void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) = 0; + /// Signal disabling of a uniform buffer + virtual void DisableGraphicsUniformBuffer(size_t stage, u32 index) = 0; + /// Signal a GPU based semaphore as a fence virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a5dbb9adf..f87bb269b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -526,6 +526,10 @@ void RasterizerOpenGL::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAd buffer_cache.BindGraphicsUniformBuffer(stage, index, gpu_addr, size); } +void RasterizerOpenGL::DisableGraphicsUniformBuffer(size_t stage, u32 index) { + buffer_cache.DisableGraphicsUniformBuffer(stage, index); +} + void RasterizerOpenGL::FlushAll() {} void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 3745cf637..76298517f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -72,6 +72,7 @@ public: void ResetCounter(VideoCore::QueryType type) override; void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; + void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void FlushAll() override; void FlushRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size) override; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e9a0e7811..1c9120170 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -476,6 +476,10 @@ void RasterizerVulkan::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAd buffer_cache.BindGraphicsUniformBuffer(stage, index, gpu_addr, size); } +void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 index) { + buffer_cache.DisableGraphicsUniformBuffer(stage, index); +} + void RasterizerVulkan::FlushAll() {} void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 235afc6f3..cb8c5c279 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -64,6 +64,7 @@ public: void ResetCounter(VideoCore::QueryType type) override; void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; + void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void FlushAll() override; void FlushRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size) override; diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 3a463d5db..f1f523ad1 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -63,6 +63,14 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe const u32 unswizzled_offset = slice * pitch * height + line * pitch + column * bytes_per_pixel; + if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset); + offset >= input.size()) { + // TODO(Rodrigo): This is an out of bounds access that should never happen. To + // avoid crashing the emulator, break. + ASSERT_MSG(false, "offset {} exceeds input size {}!", offset, input.size()); + break; + } + u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; std::memcpy(dst, src, bytes_per_pixel); diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 6028135c5..371bc01b1 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -27,6 +27,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, ui->inputTab->Initialize(input_subsystem); + ui->generalTab->SetResetCallback([&] { this->close(); }); + SetConfiguration(); PopulateSelectionList(); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 55a6a37bd..38edb4d8d 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -2,11 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <functional> +#include <utility> #include <QCheckBox> +#include <QMessageBox> #include <QSpinBox> #include "common/settings.h" #include "core/core.h" #include "ui_configure_general.h" +#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_general.h" #include "yuzu/uisettings.h" @@ -23,6 +27,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); }); } + + connect(ui->button_reset_defaults, &QPushButton::clicked, this, + &ConfigureGeneral::ResetDefaults); } ConfigureGeneral::~ConfigureGeneral() = default; @@ -41,6 +48,8 @@ void ConfigureGeneral::SetConfiguration() { ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); + ui->button_reset_defaults->setEnabled(runtime_lock); + if (Settings::IsConfiguringGlobal()) { ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue()); } else { @@ -49,6 +58,25 @@ void ConfigureGeneral::SetConfiguration() { } } +// Called to set the callback when resetting settings to defaults +void ConfigureGeneral::SetResetCallback(std::function<void()> callback) { + reset_callback = std::move(callback); +} + +void ConfigureGeneral::ResetDefaults() { + QMessageBox::StandardButton answer = QMessageBox::question( + this, tr("yuzu"), + tr("This reset all settings and remove all per-game configurations. This will not delete " + "game directories, profiles, or input profiles. Proceed?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (answer == QMessageBox::No) { + return; + } + UISettings::values.reset_to_defaults = true; + UISettings::values.is_game_list_reload_pending.exchange(true); + reset_callback(); +} + void ConfigureGeneral::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, use_multi_core); @@ -105,6 +133,8 @@ void ConfigureGeneral::SetupPerGameUI() { ui->toggle_background_pause->setVisible(false); ui->toggle_hide_mouse->setVisible(false); + ui->button_reset_defaults->setVisible(false); + ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, Settings::values.use_frame_limit, use_frame_limit); ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 323ffbd8f..a0fd52492 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -4,9 +4,12 @@ #pragma once +#include <functional> #include <memory> #include <QWidget> +class ConfigureDialog; + namespace ConfigurationShared { enum class CheckState; } @@ -24,6 +27,8 @@ public: explicit ConfigureGeneral(QWidget* parent = nullptr); ~ConfigureGeneral() override; + void SetResetCallback(std::function<void()> callback); + void ResetDefaults(); void ApplyConfiguration(); private: @@ -34,6 +39,8 @@ private: void SetupPerGameUI(); + std::function<void()> reset_callback; + std::unique_ptr<Ui::ConfigureGeneral> ui; ConfigurationShared::CheckState use_frame_limit; diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 2711116a2..bc7041090 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>300</width> + <width>329</width> <height>407</height> </rect> </property> @@ -104,6 +104,45 @@ </property> </spacer> </item> + <item> + <layout class="QHBoxLayout" name="layout_reset"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> + <item> + <widget class="QPushButton" name="button_reset_defaults"> + <property name="text"> + <string>Reset All Settings</string> + </property> + </widget> + </item> + <item> + <spacer name="spacer_reset"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> </layout> </item> </layout> diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 61ba91cef..f50cda2f3 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -85,6 +85,8 @@ void PlayerControlPreview::SetConnectedStatus(bool checked) { led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off; led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off; led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off; + is_enabled = checked; + ResetInputs(); } void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) { @@ -108,6 +110,7 @@ void PlayerControlPreview::EndMapping() { analog_mapping_index = Settings::NativeAnalog::NumAnalogs; mapping_active = false; blink_counter = 0; + ResetInputs(); } void PlayerControlPreview::UpdateColors() { @@ -156,7 +159,23 @@ void PlayerControlPreview::UpdateColors() { // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right); } +void PlayerControlPreview::ResetInputs() { + for (std::size_t index = 0; index < button_values.size(); ++index) { + button_values[index] = false; + } + + for (std::size_t index = 0; index < axis_values.size(); ++index) { + axis_values[index].properties = {0, 1, 0}; + axis_values[index].value = {0, 0}; + axis_values[index].raw_value = {0, 0}; + } + update(); +} + void PlayerControlPreview::UpdateInput() { + if (!is_enabled && !mapping_active) { + return; + } bool input_changed = false; const auto& button_state = buttons; for (std::size_t index = 0; index < button_values.size(); ++index) { diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h index 51bb84eb6..5fc16d8af 100644 --- a/src/yuzu/configuration/configure_input_player_widget.h +++ b/src/yuzu/configuration/configure_input_player_widget.h @@ -100,6 +100,7 @@ private: static LedPattern GetColorPattern(std::size_t index, bool player_on); void UpdateColors(); + void ResetInputs(); // Draw controller functions void DrawHandheldController(QPainter& p, QPointF center); @@ -176,6 +177,7 @@ private: using StickArray = std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>; + bool is_enabled{}; bool mapping_active{}; int blink_counter{}; QColor button_color{}; diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index d85408ac6..c1fc69578 100644 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp @@ -28,6 +28,7 @@ ControllerDialog::ControllerDialog(QWidget* parent) : QWidget(parent, Qt::Dialog // Configure focus so that widget is focusable and the dialog automatically forwards focus to // it. setFocusProxy(widget); + widget->SetConnectedStatus(false); widget->setFocusPolicy(Qt::StrongFocus); widget->setFocus(); } @@ -36,9 +37,8 @@ void ControllerDialog::refreshConfiguration() { const auto& players = Settings::values.players.GetValue(); constexpr std::size_t player = 0; widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); - widget->SetConnectedStatus(players[player].connected); widget->SetControllerType(players[player].controller_type); - widget->repaint(); + widget->SetConnectedStatus(players[player].connected); } QAction* ControllerDialog::toggleViewAction() { @@ -56,6 +56,7 @@ void ControllerDialog::showEvent(QShowEvent* ev) { if (toggle_view_action) { toggle_view_action->setChecked(isVisible()); } + refreshConfiguration(); QWidget::showEvent(ev); } @@ -63,5 +64,6 @@ void ControllerDialog::hideEvent(QHideEvent* ev) { if (toggle_view_action) { toggle_view_action->setChecked(isVisible()); } + widget->SetConnectedStatus(false); QWidget::hideEvent(ev); } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index c2e84ef79..9308cfef8 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -341,11 +341,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); - connect(tree_view->header(), &QHeaderView::sectionResized, this, - &GameList::SaveInterfaceLayout); - connect(tree_view->header(), &QHeaderView::sectionMoved, this, &GameList::SaveInterfaceLayout); - connect(tree_view->header(), &QHeaderView::sortIndicatorChanged, this, - &GameList::SaveInterfaceLayout); + // We must register all custom types with the Qt Automoc system so that we are able to use // it with signals/slots. In this case, QList falls under the umbrells of custom types. qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 237e26829..e683fb920 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2596,13 +2596,53 @@ void GMainWindow::OnConfigure() { &GMainWindow::OnLanguageChanged); const auto result = configure_dialog.exec(); - if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { + if (result != QDialog::Accepted && !UISettings::values.configuration_applied && + !UISettings::values.reset_to_defaults) { + // Runs if the user hit Cancel or closed the window, and did not ever press the Apply button + // or `Reset to Defaults` button return; } else if (result == QDialog::Accepted) { + // Only apply new changes if user hit Okay + // This is here to avoid applying changes if the user hit Apply, made some changes, then hit + // Cancel configure_dialog.ApplyConfiguration(); - controller_dialog->refreshConfiguration(); + } else if (UISettings::values.reset_to_defaults) { + LOG_INFO(Frontend, "Resetting all settings to defaults"); + if (!Common::FS::RemoveFile(config->GetConfigFilePath())) { + LOG_WARNING(Frontend, "Failed to remove configuration file"); + } + if (!Common::FS::RemoveDirContentsRecursively( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "custom")) { + LOG_WARNING(Frontend, "Failed to remove custom configuration files"); + } + if (!Common::FS::RemoveDirRecursively( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list")) { + LOG_WARNING(Frontend, "Failed to remove game metadata cache files"); + } + + // Explicitly save the game directories, since reinitializing config does not explicitly do + // so. + QVector<UISettings::GameDir> old_game_dirs = std::move(UISettings::values.game_dirs); + QVector<u64> old_favorited_ids = std::move(UISettings::values.favorited_ids); + + Settings::values.disabled_addons.clear(); + + config = std::make_unique<Config>(); + UISettings::values.reset_to_defaults = false; + + UISettings::values.game_dirs = std::move(old_game_dirs); + UISettings::values.favorited_ids = std::move(old_favorited_ids); + + InitializeRecentFileMenuActions(); + + SetDefaultUIGeometry(); + RestoreUIState(); + + ShowTelemetryCallout(); } + controller_dialog->refreshConfiguration(); InitializeHotkeys(); + if (UISettings::values.theme != old_theme) { UpdateUITheme(); } diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 49122ec32..cdcb83f9f 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -97,6 +97,7 @@ struct Values { bool cache_game_list; bool configuration_applied; + bool reset_to_defaults; }; extern Values values; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index a2ab69cdd..63f368fe5 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -317,6 +317,43 @@ void Config::ReadValues() { sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); Settings::values.touchscreen.diameter_y = sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); + + int num_touch_from_button_maps = + sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); + if (num_touch_from_button_maps > 0) { + for (int i = 0; i < num_touch_from_button_maps; ++i) { + Settings::TouchFromButtonMap map; + map.name = sdl2_config->Get("ControlsGeneral", + std::string("touch_from_button_maps_") + std::to_string(i) + + std::string("_name"), + "default"); + const int num_touch_maps = sdl2_config->GetInteger( + "ControlsGeneral", + std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), + 0); + map.buttons.reserve(num_touch_maps); + + for (int j = 0; j < num_touch_maps; ++j) { + std::string touch_mapping = + sdl2_config->Get("ControlsGeneral", + std::string("touch_from_button_maps_") + std::to_string(i) + + std::string("_bind_") + std::to_string(j), + ""); + map.buttons.emplace_back(std::move(touch_mapping)); + } + + Settings::values.touch_from_button_maps.emplace_back(std::move(map)); + } + } else { + Settings::values.touch_from_button_maps.emplace_back( + Settings::TouchFromButtonMap{"default", {}}); + num_touch_from_button_maps = 1; + } + Settings::values.use_touch_from_button = + sdl2_config->GetBoolean("ControlsGeneral", "use_touch_from_button", false); + Settings::values.touch_from_button_map_index = + std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1); + Settings::values.udp_input_servers = sdl2_config->Get("Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_SRV); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 4ce8e08e4..8ce2967ac 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -7,7 +7,7 @@ namespace DefaultINI { const char* sdl2_config_file = R"( -[Controls] +[ControlsGeneral] # The input devices and parameters for each Switch native input # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values @@ -86,6 +86,18 @@ motion_device= # - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system touch_device= +# Whether to enable or disable touch input from button +# 0 (default): Disabled, 1: Enabled +use_touch_from_button= + +# for mapping buttons to touch inputs. +#touch_from_button_map=1 +#touch_from_button_maps_0_name=default +#touch_from_button_maps_0_count=2 +#touch_from_button_maps_0_bind_0=foo +#touch_from_button_maps_0_bind_1=bar +# etc. + # Most desktop operating systems do not expose a way to poll the motion state of the controllers # so as a way around it, cemuhook created a udp client/server protocol to broadcast the data directly # from a controller device to the client program. Citra has a client that can connect and read |