diff options
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  | 
