#pragma once #include "overlay_animation.h" #include "overlay_controls.h" #include "../../../Utilities/Thread.h" #include "Emu/Memory/vm.h" #include "Emu/IdManager.h" #include "Utilities/Timer.h" #include #include // Utils extern u64 get_system_time(); // Definition of user interface implementations namespace rsx { namespace overlays { // Non-interactable UI element struct overlay { u32 uid = UINT32_MAX; u32 type_index = UINT32_MAX; u16 virtual_width = 1280; u16 virtual_height = 720; u32 min_refresh_duration_us = 16600; atomic_t visible = false; virtual ~overlay() = default; virtual void update() {} virtual compiled_resource get_compiled() = 0; void refresh(); }; // Interactable UI element class user_interface : public overlay { public: // Move this somewhere to avoid duplication enum selection_code { new_save = -1, canceled = -2, error = -3 }; enum pad_button : u8 { dpad_up = 0, dpad_down, dpad_left, dpad_right, select, start, triangle, circle, square, cross, L1, R1, pad_button_max_enum }; protected: Timer input_timer; atomic_t exit = false; atomic_t thread_bits = 0; static thread_local u64 g_thread_bit; u64 alloc_thread_bit(); std::function on_close; public: s32 return_code = 0; // CELL_OK public: void update() override {} compiled_resource get_compiled() override = 0; virtual void on_button_pressed(pad_button /*button_press*/) {} void close(bool use_callback, bool stop_pad_interception); s32 run_input_loop(); }; class display_manager { private: atomic_t m_uid_ctr = 0; std::vector> m_iface_list; std::vector> m_dirty_list; shared_mutex m_list_mutex; std::vector m_uids_to_remove; std::vector m_type_ids_to_remove; bool remove_type(u32 type_id) { bool found = false; for (auto It = m_iface_list.begin(); It != m_iface_list.end();) { if (It->get()->type_index == type_id) { m_dirty_list.push_back(std::move(*It)); It = m_iface_list.erase(It); found = true; } else { ++It; } } return found; } bool remove_uid(u32 uid) { for (auto It = m_iface_list.begin(); It != m_iface_list.end(); It++) { const auto e = It->get(); if (e->uid == uid) { m_dirty_list.push_back(std::move(*It)); m_iface_list.erase(It); return true; } } return false; } void cleanup_internal() { for (const auto &uid : m_uids_to_remove) { remove_uid(uid); } for (const auto &type_id : m_type_ids_to_remove) { remove_type(type_id); } m_uids_to_remove.clear(); m_type_ids_to_remove.clear(); } public: // Disable default construction to make it conditionally available in g_fxo explicit display_manager(int) noexcept { } // Adds an object to the internal list. Optionally removes other objects of the same type. // Original handle loses ownership but a usable pointer is returned template std::shared_ptr add(std::shared_ptr& entry, bool remove_existing = true) { std::lock_guard lock(m_list_mutex); entry->uid = m_uid_ctr.fetch_add(1); entry->type_index = id_manager::typeinfo::get_index(); if (remove_existing) { for (auto It = m_iface_list.begin(); It != m_iface_list.end(); It++) { if (It->get()->type_index == entry->type_index) { // Replace m_dirty_list.push_back(std::move(*It)); *It = std::move(entry); return std::static_pointer_cast(*It); } } } m_iface_list.push_back(std::move(entry)); return std::static_pointer_cast(m_iface_list.back()); } // Allocates object and adds to internal list. Returns pointer to created object template std::shared_ptr create(Args&&... args) { auto object = std::make_shared(std::forward(args)...); return add(object); } // Removes item from list if it matches the uid void remove(u32 uid) { if (m_list_mutex.try_lock()) { remove_uid(uid); m_list_mutex.unlock(); } else { m_uids_to_remove.push_back(uid); } } // Removes all objects of this type from the list template void remove() { const auto type_id = id_manager::typeinfo::get_index(); if (m_list_mutex.try_lock()) { remove_type(type_id); m_list_mutex.unlock(); } else { m_type_ids_to_remove.push_back(type_id); } } // True if any visible elements to draw exist bool has_visible() const { return !m_iface_list.empty(); } // True if any elements have been deleted but their resources may not have been cleaned up bool has_dirty() const { return !m_dirty_list.empty(); } // Returns current list for reading. Caller must ensure synchronization by first locking the list const std::vector>& get_views() const { return m_iface_list; } // Returns current list of removed objects not yet deallocated for reading. // Caller must ensure synchronization by first locking the list const std::vector>& get_dirty() const { return m_dirty_list; } // Deallocate object. Object must first be removed via the remove() functions void dispose(const std::vector& uids) { std::lock_guard lock(m_list_mutex); if (!m_uids_to_remove.empty() || !m_type_ids_to_remove.empty()) { cleanup_internal(); } m_dirty_list.erase ( std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::shared_ptr& e) { return std::find(uids.begin(), uids.end(), e->uid) != uids.end(); }), m_dirty_list.end() ); } // Returns pointer to the object matching the given uid std::shared_ptr get(u32 uid) { reader_lock lock(m_list_mutex); for (const auto& iface : m_iface_list) { if (iface->uid == uid) return iface; } return {}; } // Returns pointer to the first object matching the given type template std::shared_ptr get() { reader_lock lock(m_list_mutex); const auto type_id = id_manager::typeinfo::get_index(); for (const auto& iface : m_iface_list) { if (iface->type_index == type_id) { return std::static_pointer_cast(iface); } } return {}; } // Lock for read-only access (BasicLockable) void lock() { m_list_mutex.lock_shared(); } // Release read-only lock (BasicLockable). May perform internal cleanup before returning void unlock() { m_list_mutex.unlock_shared(); if (!m_uids_to_remove.empty() || !m_type_ids_to_remove.empty()) { std::lock_guard lock(m_list_mutex); cleanup_internal(); } } }; } }