diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index f06c5139bb..6eb828ec8c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -72,7 +72,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { DEBUG_FLUSH_BY_LINE("flush_line"), USE_LRU_CACHE("use_lru_cache"), - DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"); + DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"), + ENABLE_OVERLAY("enable_overlay"); // external fun isFrameSkippingEnabled(): Boolean diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 248e40cdd1..b2f23fb678 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -849,7 +849,14 @@ abstract class SettingsItem( descriptionId = R.string.airplane_mode_description ) ) + + put( + SwitchSetting( + BooleanSetting.ENABLE_OVERLAY, + titleId = R.string.enable_overlay, + descriptionId = R.string.enable_overlay_description + ) + ) } } } - diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index eb3682757b..1e58fd5223 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -491,6 +491,7 @@ class SettingsFragmentPresenter( sl.apply { add(IntSetting.SWKBD_APPLET.key) add(BooleanSetting.AIRPLANE_MODE.key) + add(BooleanSetting.ENABLE_OVERLAY.key) } } private fun addInputPlayer(sl: ArrayList, playerIndex: Int) { diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index f0e012e6b7..0d863305d1 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1125,7 +1125,7 @@ Applets - (WIP) Change applet frontends and settings + Change applet frontends and settings Custom Frontend Real Applet @@ -1135,6 +1135,9 @@ Airplane Mode Passes Airplane Mode to the Switch OS + Enable Overlay Applet + Enables Horizon\'s built-in overlay applet. Press and hold the home button for 1 second to show it. + Licenses FidelityFX-FSR diff --git a/src/common/settings.h b/src/common/settings.h index 25bdfb1bf2..c8e6211239 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -774,6 +774,8 @@ struct Values { // Per-game overrides bool use_squashed_iterated_blend; + + Setting enable_overlay{linkage, true, "enable_overlay", Category::Core}; }; extern Values values; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 83aabdbc72..d1bbf37eb9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -487,6 +487,10 @@ add_library(core STATIC hle/service/am/service/library_applet_self_accessor.h hle/service/am/service/lock_accessor.cpp hle/service/am/service/lock_accessor.h + hle/service/am/service/overlay_functions.cpp + hle/service/am/service/overlay_functions.h + hle/service/am/service/overlay_applet_proxy.cpp + hle/service/am/service/overlay_applet_proxy.h hle/service/am/service/process_winding_controller.cpp hle/service/am/service/process_winding_controller.h hle/service/am/service/self_controller.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 6c321afdbb..edc2df4851 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -580,6 +580,21 @@ struct System::Impl { gpu_dirty_memory_managers; std::deque> user_channel; + + std::mutex general_channel_mutex; + std::deque> general_channel; + std::unique_ptr general_channel_context; // lazy + std::unique_ptr general_channel_event; // lazy + bool general_channel_initialized{false}; + + void EnsureGeneralChannelInitialized(System& system) { + if (general_channel_initialized) { + return; + } + general_channel_context = std::make_unique(system, "GeneralChannel"); + general_channel_event = std::make_unique(*general_channel_context); + general_channel_initialized = true; + } }; System::System() : impl{std::make_unique(*this)} {} @@ -993,6 +1008,39 @@ std::deque>& System::GetUserChannel() { return impl->user_channel; } +std::deque>& System::GetGeneralChannel() { + return impl->general_channel; +} + +void System::PushGeneralChannelData(std::vector&& data) { + std::scoped_lock lk{impl->general_channel_mutex}; + impl->EnsureGeneralChannelInitialized(*this); + const bool was_empty = impl->general_channel.empty(); + impl->general_channel.push_back(std::move(data)); + if (was_empty) { + impl->general_channel_event->Signal(); + } +} + +bool System::TryPopGeneralChannel(std::vector& out_data) { + std::scoped_lock lk{impl->general_channel_mutex}; + if (!impl->general_channel_initialized || impl->general_channel.empty()) { + return false; + } + out_data = std::move(impl->general_channel.back()); + impl->general_channel.pop_back(); + if (impl->general_channel.empty()) { + impl->general_channel_event->Clear(); + } + return true; +} + +Service::Event& System::GetGeneralChannelEvent() { + std::scoped_lock lk{impl->general_channel_mutex}; + impl->EnsureGeneralChannelInitialized(*this); + return *impl->general_channel_event; +} + void System::RegisterExitCallback(ExitCallback&& callback) { impl->exit_callback = std::move(callback); } diff --git a/src/core/core.h b/src/core/core.h index 12a957791d..60bf73d4e1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -17,6 +17,8 @@ #include "common/common_types.h" #include "core/file_sys/vfs/vfs_types.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/kernel_helpers.h" namespace Core::Frontend { class EmuWindow; @@ -428,6 +430,11 @@ public: */ [[nodiscard]] std::deque>& GetUserChannel(); + [[nodiscard]] std::deque>& GetGeneralChannel(); + void PushGeneralChannelData(std::vector&& data); + bool TryPopGeneralChannel(std::vector& out_data); + [[nodiscard]] Service::Event& GetGeneralChannelEvent(); + /// Type used for the frontend to designate a callback for System to exit the application. using ExitCallback = std::function; diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index ccb37bc85b..25668e32c1 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -5,6 +8,7 @@ #include #include +#include #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 4dfb0ded51..114a18891f 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -95,7 +95,7 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { case SaveDataSpaceId::System: return "/system/"; case SaveDataSpaceId::User: - case SaveDataSpaceId::SdUser: + case SaveDataSpaceId::SdUser: return "/user/"; case SaveDataSpaceId::Temporary: return "/temp/"; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 76b0ba8818..bdae3a012c 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -215,7 +215,7 @@ public: {220, nullptr, "SynchronizeProfileAsync"}, {221, nullptr, "UploadProfileAsync"}, {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"}, - {250, nullptr, "IsLinkedWithNintendoAccount"}, + {250, &IAdministrator::IsLinkedWithNintendoAccount, "IsLinkedWithNintendoAccount"}, {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"}, {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"}, {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"}, @@ -236,6 +236,13 @@ public: RegisterHandlers(functions); } + +private: + void IsLinkedWithNintendoAccount(HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); + } }; class IAuthorizationRequest final : public ServiceFramework { @@ -496,6 +503,35 @@ protected: rb.Push(static_cast(buffer.size())); } + void LoadIdTokenCache(HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + + std::vector token_data(0x100); + std::fill(token_data.begin(), token_data.end(), u8(0)); + + (void)ctx.WriteBuffer(token_data); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(token_data.size())); + } + + void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + + std::vector nas_user_base_for_application(0x68); + (void)ctx.WriteBuffer(nas_user_base_for_application); + + if (ctx.CanWriteBuffer(1)) { + std::vector unknown_out_buffer(ctx.GetWriteBufferSize(1)); + (void)ctx.WriteBuffer(unknown_out_buffer, 1); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(profile_manager.GetLastOpenedUser().Hash()); + } + void Store(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto base = rp.PopRaw(); @@ -707,7 +743,7 @@ private: std::vector token_data(0x100); std::fill(token_data.begin(), token_data.end(), u8(0)); - ctx.WriteBuffer(token_data, 0); + ctx.WriteBuffer(token_data); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -718,7 +754,7 @@ private: LOG_WARNING(Service_ACC, "(STUBBED) called"); std::vector nas_user_base_for_application(0x68); - ctx.WriteBuffer(nas_user_base_for_application, 0); + ctx.WriteBuffer(nas_user_base_for_application); if (ctx.CanWriteBuffer(1)) { std::vector unknown_out_buffer(ctx.GetWriteBufferSize(1)); @@ -934,6 +970,7 @@ Result Module::Interface::InitializeApplicationInfoBase() { application_info.application_type = ApplicationType::GameCard; break; case FileSys::StorageId::Host: + case FileSys::StorageId::NandSystem: case FileSys::StorageId::NandUser: case FileSys::StorageId::SdCard: case FileSys::StorageId::None: // Yuzu specific, differs from hardware @@ -1054,6 +1091,17 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) { rb.PushIpcInterface(system, user_id, *profile_manager); } +void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid = rp.PopRaw(); + + LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, uuid); +} + void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index e64a67674f..b01998e9c9 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -42,6 +42,7 @@ public: void DeleteUser(HLERequestContext& ctx); void SetUserPosition(HLERequestContext& ctx); void GetProfileEditor(HLERequestContext& ctx); + void GetBaasAccountAdministrator(HLERequestContext &ctx); void ListQualifiedUsers(HLERequestContext& ctx); void ListOpenContextStoredUsers(HLERequestContext& ctx); void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 7d07ea0343..9eea0ea2ed 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -55,7 +55,7 @@ ACC_SU::ACC_SU(std::shared_ptr module_, std::shared_ptr {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, {230, nullptr, "AuthenticateServiceAsync"}, - {250, nullptr, "GetBaasAccountAdministrator"}, + {250, &ACC_SU::GetBaasAccountAdministrator, "GetBaasAccountAdministrator"}, {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, {299, nullptr, "SuspendBackgroundDaemon"}, @@ -80,4 +80,4 @@ ACC_SU::ACC_SU(std::shared_ptr module_, std::shared_ptr ACC_SU::~ACC_SU() = default; -} // namespace Service::Account \ No newline at end of file +} // namespace Service::Account diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index eb9ad0ac5e..621a7b4921 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,6 +18,7 @@ class FrontendApplet; enum class AppletType { Application, LibraryApplet, + OverlayApplet, SystemApplet, }; @@ -146,6 +150,7 @@ enum class AppletMessage : u32 { ForceHideApplicationLogo = 57, FloatingApplicationDetected = 60, DetectShortPressingCaptureButton = 90, + DetectLongPressingCaptureButton = 91, AlbumScreenShotTaken = 92, AlbumRecordingSaved = 93, }; diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp index aa355b06d5..2ae3102aff 100644 --- a/src/core/hle/service/am/applet.cpp +++ b/src/core/hle/service/am/applet.cpp @@ -12,7 +12,7 @@ namespace Service::AM { Applet::Applet(Core::System& system, std::unique_ptr process_, bool is_application) : context(system, "Applet"), lifecycle_manager(system, context, is_application), - process(std::move(process_)), hid_registration(system, *process), + process(std::move(process_)), hid_registration(system, *process), overlay_event(context), gpu_error_detected_event(context), friend_invitation_storage_channel_event(context), notification_storage_channel_event(context), health_warning_disappeared_system_event(context), unknown_event(context), acquired_sleep_lock_event(context), pop_from_general_channel_event(context), @@ -63,7 +63,15 @@ void Applet::SetInteractibleLocked(bool interactible) { is_interactible = interactible; - hid_registration.EnableAppletToGetInput(interactible && !lifecycle_manager.GetExitRequested()); + const bool exit_requested = lifecycle_manager.GetExitRequested(); + const bool input_enabled = interactible && !exit_requested; + + if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) { + LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}", + static_cast(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground); + } + + hid_registration.EnableAppletToGetInput(input_enabled); } void Applet::OnProcessTerminatedLocked() { diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index ad84f39dc7..0763a5838e 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -122,8 +122,10 @@ struct Applet { bool is_activity_runnable{}; bool is_interactible{true}; bool window_visible{true}; + bool overlay_in_foreground{false}; // Events + Event overlay_event; Event gpu_error_detected_event; Event friend_invitation_storage_channel_event; Event notification_storage_channel_event; diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index c6b7ec8bb5..cab56f9f7d 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,6 +18,7 @@ #include "core/hle/service/am/service/storage.h" #include "core/hle/service/am/window_system.h" #include "hid_core/hid_types.h" +#include "core/hle/service/am/process_creation.h" namespace Service::AM { @@ -262,6 +266,22 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { m_cv.wait(lk, [&] { return m_pending_process != nullptr; }); + if (Settings::values.enable_overlay) { + if (auto overlay_process = CreateProcess(m_system, static_cast(AppletProgramId::OverlayDisplay), 0, 0)) { + auto overlay_applet = std::make_shared(m_system, std::move(overlay_process), false); + overlay_applet->program_id = static_cast(AppletProgramId::OverlayDisplay); + overlay_applet->applet_id = AppletId::OverlayDisplay; + overlay_applet->type = AppletType::OverlayApplet; + overlay_applet->library_applet_mode = LibraryAppletMode::PartialForeground; + overlay_applet->window_visible = true; + overlay_applet->home_button_short_pressed_blocked = false; + overlay_applet->home_button_long_pressed_blocked = false; + m_window_system->TrackApplet(overlay_applet, false); + overlay_applet->process->Run(); + LOG_INFO(Service_AM, "called, Overlay applet launched before application (initially hidden, watching home button)"); + } + } + const auto& params = m_pending_parameters; auto applet = std::make_shared(m_system, std::move(m_pending_process), params.applet_id == AppletId::Application); diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h index fbdc771400..2b0714adf7 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -46,6 +49,7 @@ public: public: void SetWindowSystem(WindowSystem* window_system); + [[nodiscard]] WindowSystem* GetWindowSystem() const { return m_window_system; } private: Core::System& m_system; diff --git a/src/core/hle/service/am/button_poller.cpp b/src/core/hle/service/am/button_poller.cpp index aab397085f..069a1934d4 100644 --- a/src/core/hle/service/am/button_poller.cpp +++ b/src/core/hle/service/am/button_poller.cpp @@ -1,7 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" +#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/button_poller.h" #include "core/hle/service/am/window_system.h" #include "hid_core/frontend/emulated_controller.h" @@ -19,9 +23,9 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point // TODO: determine actual thresholds // TODO: these are likely different for each button - if (dur < 500ms) { + if (dur < 400ms) { return ButtonPressDuration::ShortPressing; - } else if (dur < 1000ms) { + } else if (dur < 800ms) { return ButtonPressDuration::MiddlePressing; } else { return ButtonPressDuration::LongPressing; @@ -47,14 +51,22 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) m_handheld_key = m_handheld->SetCallback(engine_callback); m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); m_player1_key = m_player1->SetCallback(engine_callback); + + m_thread = std::thread([this] { this->ThreadLoop(); }); } ButtonPoller::~ButtonPoller() { m_handheld->DeleteCallback(m_handheld_key); m_player1->DeleteCallback(m_player1_key); + m_stop = true; + m_cv.notify_all(); + if (m_thread.joinable()) { + m_thread.join(); + } } void ButtonPoller::OnButtonStateChanged() { + std::lock_guard lk{m_mutex}; const bool home_button = m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value(); const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() || @@ -63,22 +75,54 @@ void ButtonPoller::OnButtonStateChanged() { // Buttons pressed which were not previously pressed if (home_button && !m_home_button_press_start) { m_home_button_press_start = std::chrono::steady_clock::now(); + m_home_button_long_sent = false; } if (capture_button && !m_capture_button_press_start) { m_capture_button_press_start = std::chrono::steady_clock::now(); + m_capture_button_long_sent = false; } // if (power_button && !m_power_button_press_start) { // m_power_button_press_start = std::chrono::steady_clock::now(); // } + // While buttons are held, check whether they have crossed the long-press + // threshold and, if so, send the long-press action immediately (only once). + if (home_button && m_home_button_press_start && !m_home_button_long_sent) { + const auto duration = ClassifyPressDuration(*m_home_button_press_start); + if (duration != ButtonPressDuration::ShortPressing) { + m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing); + m_home_button_long_sent = true; + } + } + + if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) { + const auto duration = ClassifyPressDuration(*m_capture_button_press_start); + if (duration != ButtonPressDuration::ShortPressing) { + m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing); + m_capture_button_long_sent = true; + } + } + // Buttons released which were previously held if (!home_button && m_home_button_press_start) { - m_window_system.OnHomeButtonPressed(ClassifyPressDuration(*m_home_button_press_start)); + if(!m_home_button_long_sent) { + const auto duration = ClassifyPressDuration(*m_home_button_press_start); + m_window_system.OnSystemButtonPress( + duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing + : SystemButtonType::HomeButtonLongPressing); + } m_home_button_press_start = std::nullopt; + m_home_button_long_sent = false; } if (!capture_button && m_capture_button_press_start) { - // TODO + if (!m_capture_button_long_sent) { + const auto duration = ClassifyPressDuration(*m_capture_button_press_start); + m_window_system.OnSystemButtonPress( + duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing + : SystemButtonType::CaptureButtonLongPressing); + } m_capture_button_press_start = std::nullopt; + m_capture_button_long_sent = false; } // if (!power_button && m_power_button_press_start) { // // TODO @@ -86,4 +130,16 @@ void ButtonPoller::OnButtonStateChanged() { // } } +void ButtonPoller::ThreadLoop() { + using namespace std::chrono_literals; + std::unique_lock lk{m_mutex}; + while (!m_stop) { + m_cv.wait_for(lk, 50ms); + if (m_stop) break; + lk.unlock(); + OnButtonStateChanged(); + lk.lock(); + } +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/button_poller.h b/src/core/hle/service/am/button_poller.h index b1c39aad30..12e7035aa1 100644 --- a/src/core/hle/service/am/button_poller.h +++ b/src/core/hle/service/am/button_poller.h @@ -1,10 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include +#include +#include #include +#include #include "hid_core/frontend/emulated_controller.h" namespace Core { @@ -26,6 +33,7 @@ public: private: void OnButtonStateChanged(); + void ThreadLoop(); private: WindowSystem& m_window_system; @@ -38,6 +46,15 @@ private: std::optional m_home_button_press_start{}; std::optional m_capture_button_press_start{}; std::optional m_power_button_press_start{}; + + bool m_home_button_long_sent{}; + bool m_capture_button_long_sent{}; + bool m_power_button_long_sent{}; + + std::thread m_thread; + std::atomic m_stop{false}; + std::condition_variable m_cv; + std::mutex m_mutex; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp index 85ff6fb889..a93ca5bc67 100644 --- a/src/core/hle/service/am/display_layer_manager.cpp +++ b/src/core/hle/service/am/display_layer_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -33,6 +36,10 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro m_buffer_sharing_enabled = false; m_blending_enabled = mode == LibraryAppletMode::PartialForeground || mode == LibraryAppletMode::PartialForegroundIndirectDisplay; + + if (m_applet_id != AppletId::Application) { + (void)this->IsSystemBufferSharingEnabled(); + } } void DisplayLayerManager::Finalize() { @@ -69,6 +76,18 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) { out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()})); m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id); + + if (m_applet_id != AppletId::Application) { + (void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id); + if (m_applet_id == AppletId::OverlayDisplay) { + static constexpr s32 kOverlayBackgroundZ = -1; + (void)m_manager_display_service->SetLayerZIndex(kOverlayBackgroundZ, *out_layer_id); + } else { + static constexpr s32 kOverlayZ = 3; + (void)m_manager_display_service->SetLayerZIndex(kOverlayZ, *out_layer_id); + } + } + m_managed_display_layers.emplace(*out_layer_id); R_SUCCEED(); @@ -105,7 +124,15 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() { // We succeeded, so set up remaining state. m_buffer_sharing_enabled = true; + + // Ensure the overlay layer is visible m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); + m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id); + s32 initial_z = 1; + if (m_applet_id == AppletId::OverlayDisplay) { + initial_z = -1; + } + m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id); R_SUCCEED(); } @@ -128,10 +155,14 @@ void DisplayLayerManager::SetWindowVisibility(bool visible) { if (m_manager_display_service) { if (m_system_shared_layer_id) { + LOG_INFO(Service_VI, "shared_layer={} visible={} applet_id={}", + m_system_shared_layer_id, m_visible, static_cast(m_applet_id)); m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); } for (const auto layer_id : m_managed_display_layers) { + LOG_INFO(Service_VI, "managed_layer={} visible={} applet_id={}", + layer_id, m_visible, static_cast(m_applet_id)); m_manager_display_service->SetLayerVisibility(m_visible, layer_id); } } @@ -141,6 +172,22 @@ bool DisplayLayerManager::GetWindowVisibility() const { return m_visible; } +void DisplayLayerManager::SetOverlayZIndex(s32 z_index) { + if (!m_manager_display_service) { + return; + } + + if (m_system_shared_layer_id) { + m_manager_display_service->SetLayerZIndex(z_index, m_system_shared_layer_id); + LOG_INFO(Service_VI, "called, shared_layer={} z={}", m_system_shared_layer_id, z_index); + } + + for (const auto layer_id : m_managed_display_layers) { + m_manager_display_service->SetLayerZIndex(z_index, layer_id); + LOG_INFO(Service_VI, "called, managed_layer={} z={}", layer_id, z_index); + } +} + Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index) { R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied); diff --git a/src/core/hle/service/am/display_layer_manager.h b/src/core/hle/service/am/display_layer_manager.h index a66509c04a..423eb432fa 100644 --- a/src/core/hle/service/am/display_layer_manager.h +++ b/src/core/hle/service/am/display_layer_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -43,6 +46,8 @@ public: void SetWindowVisibility(bool visible); bool GetWindowVisibility() const; + void SetOverlayZIndex(s32 z_index); + Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index); private: diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp index d99482a45c..562d95f001 100644 --- a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp +++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,6 +10,7 @@ #include "core/hle/service/am/service/application_proxy.h" #include "core/hle/service/am/service/library_applet_proxy.h" #include "core/hle/service/am/service/system_applet_proxy.h" +#include "core/hle/service/am/service/overlay_applet_proxy.h" #include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" @@ -21,7 +25,7 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys {110, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxyEx"}, {200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"}, {201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"}, - {300, nullptr, "OpenOverlayAppletProxy"}, + {300, D<&IAllSystemAppletProxiesService::OpenOverlayAppletProxy>, "OpenOverlayAppletProxy"}, {350, D<&IAllSystemAppletProxiesService::OpenSystemApplicationProxy>, "OpenSystemApplicationProxy"}, {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, {410, nullptr, "GetSystemAppletControllerForDebug"}, @@ -67,6 +71,22 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy( } } +Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy( + Out> out_overlay_applet_proxy, ClientProcessId pid, + InCopyHandle process_handle, + InLargeData attribute) { + LOG_WARNING(Service_AM, "called"); + + if (const auto applet = this->GetAppletFromProcessId(pid); applet) { + *out_overlay_applet_proxy = std::make_shared( + system, applet, process_handle.Get(), m_window_system); + R_SUCCEED(); + } else { + UNIMPLEMENTED(); + R_THROW(ResultUnknown); + } +} + Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy( Out> out_system_application_proxy, ClientProcessId pid, InCopyHandle process_handle, diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.h b/src/core/hle/service/am/service/all_system_applet_proxies_service.h index 525525c795..eccd92a376 100644 --- a/src/core/hle/service/am/service/all_system_applet_proxies_service.h +++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -5,6 +8,7 @@ #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" +#include "core/hle/service/am/service/overlay_applet_proxy.h" namespace Service { @@ -31,6 +35,9 @@ private: ClientProcessId pid, InCopyHandle process_handle, InLargeData attribute); + Result OpenOverlayAppletProxy(Out> out_overlay_applet_proxy, + ClientProcessId pid, InCopyHandle process_handle, + InLargeData attribute); Result OpenLibraryAppletProxyOld( Out> out_library_applet_proxy, ClientProcessId pid, InCopyHandle process_handle); diff --git a/src/core/hle/service/am/service/applet_common_functions.cpp b/src/core/hle/service/am/service/applet_common_functions.cpp index 6a73a896f9..2bfcd10d04 100644 --- a/src/core/hle/service/am/service/applet_common_functions.cpp +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,7 +21,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, {20, nullptr, "PushToAppletBoundChannel"}, {21, nullptr, "TryPopFromAppletBoundChannel"}, {40, nullptr, "GetDisplayLogicalResolution"}, - {42, nullptr, "SetDisplayMagnification"}, + {42, D<&IAppletCommonFunctions::SetDisplayMagnification>, "SetDisplayMagnification"}, {50, D<&IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled>, "SetHomeButtonDoubleClickEnabled"}, {51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"}, {52, nullptr, "IsHomeButtonShortPressedBlocked"}, @@ -58,6 +61,14 @@ Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled( R_SUCCEED(); } +Result IAppletCommonFunctions::SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height) { + LOG_DEBUG(Service_AM, "(STUBBED) called, x={}, y={}, width={}, height={}", x, y, width, + height); + std::scoped_lock lk{applet->lock}; + applet->display_magnification = Common::Rectangle{x, y, x + width, y + height}; + R_SUCCEED(); +} + Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) { LOG_WARNING(Service_AM, "(STUBBED) called"); std::scoped_lock lk{applet->lock}; diff --git a/src/core/hle/service/am/service/applet_common_functions.h b/src/core/hle/service/am/service/applet_common_functions.h index 623efdb7fc..be867f00e7 100644 --- a/src/core/hle/service/am/service/applet_common_functions.h +++ b/src/core/hle/service/am/service/applet_common_functions.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,6 +21,7 @@ public: private: Result SetHomeButtonDoubleClickEnabled(bool home_button_double_click_enabled); Result GetHomeButtonDoubleClickEnabled(Out out_home_button_double_click_enabled); + Result SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height); Result SetCpuBoostRequestPriority(s32 priority); Result GetCurrentApplicationId(Out out_application_id); Result Unknown350(Out out_unknown); diff --git a/src/core/hle/service/am/service/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp index 8994f19142..2fc33a303c 100644 --- a/src/core/hle/service/am/service/application_creator.cpp +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -56,7 +59,7 @@ IApplicationCreator::IApplicationCreator(Core::System& system_, WindowSystem& wi static const FunctionInfo functions[] = { {0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"}, {1, nullptr, "PopLaunchRequestedApplication"}, - {10, nullptr, "CreateSystemApplication"}, + {10, D<&IApplicationCreator::CreateSystemApplication>, "CreateSystemApplication"}, {100, nullptr, "PopFloatingApplicationForDevelopment"}, }; // clang-format on @@ -73,4 +76,34 @@ Result IApplicationCreator::CreateApplication( CreateGuestApplication(out_application_accessor, system, m_window_system, application_id)); } +Result IApplicationCreator::CreateSystemApplication( + Out> out_application_accessor, u64 application_id) { + + FileSys::VirtualFile nca_raw{}; + + auto& storage = system.GetContentProviderUnion(); + nca_raw = storage.GetEntryRaw(application_id, FileSys::ContentRecordType::Program); + + R_UNLESS(nca_raw != nullptr, ResultUnknown); + + std::vector control; + std::unique_ptr loader; + + auto process = + CreateProcess(system, application_id, 1, 21); + R_UNLESS(process != nullptr, ResultUnknown); + + const auto applet = std::make_shared(system, std::move(process), true); + applet->program_id = application_id; + applet->applet_id = AppletId::Starter; + applet->type = AppletType::LibraryApplet; + applet->library_applet_mode = LibraryAppletMode::AllForeground; + + m_window_system.TrackApplet(applet, true); + + *out_application_accessor = + std::make_shared(system, applet, m_window_system); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_creator.h b/src/core/hle/service/am/service/application_creator.h index 287745af85..75975b9596 100644 --- a/src/core/hle/service/am/service/application_creator.h +++ b/src/core/hle/service/am/service/application_creator.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,6 +22,7 @@ public: private: Result CreateApplication(Out>, u64 application_id); + Result CreateSystemApplication(Out>, u64 application_id); WindowSystem& m_window_system; }; diff --git a/src/core/hle/service/am/service/common_state_getter.cpp b/src/core/hle/service/am/service/common_state_getter.cpp index 8c54632433..dcd42a2447 100644 --- a/src/core/hle/service/am/service/common_state_getter.cpp +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -9,11 +9,11 @@ #include "core/hle/service/am/applet.h" #include "core/hle/service/am/service/common_state_getter.h" #include "core/hle/service/am/service/lock_accessor.h" +#include "core/hle/service/am/service/storage.h" #include "core/hle/service/apm/apm_interface.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_types.h" namespace Service::AM { @@ -37,7 +37,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr, "ReleaseSleepLockTransiently"}, {13, D<&ICommonStateGetter::GetAcquiredSleepLockEvent>, "GetAcquiredSleepLockEvent"}, {14, nullptr, "GetWakeupCount"}, - {20, nullptr, "PushToGeneralChannel"}, + {20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"}, {30, nullptr, "GetHomeButtonReaderLockAccessor"}, {31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, {32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, @@ -61,7 +61,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, - {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, + {100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"}, {110, nullptr, "OpenMyGpuErrorHandler"}, {120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, {200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"}, @@ -95,6 +95,9 @@ Result ICommonStateGetter::ReceiveMessage(Out out_applet_message) R_THROW(AM::ResultNoMessages); } + LOG_DEBUG(Service_AM, "called, returning message={} to applet_id={}", + static_cast(*out_applet_message), static_cast(m_applet->applet_id)); + R_SUCCEED(); } @@ -262,7 +265,40 @@ Result ICommonStateGetter::GetBuiltInDisplayType(Out out_display_type) { } Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType type) { - LOG_WARNING(Service_AM, "(STUBBED) called, type={}", type); + LOG_DEBUG(Service_AM, "called, type={}", type); + + std::scoped_lock lk{m_applet->lock}; + + switch (type) { + case SystemButtonType::HomeButtonShortPressing: + if (!m_applet->home_button_short_pressed_blocked) { + m_applet->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectShortPressingHomeButton); + } + break; + case SystemButtonType::HomeButtonLongPressing: + if (!m_applet->home_button_long_pressed_blocked) { + m_applet->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectLongPressingHomeButton); + } + break; + case SystemButtonType::CaptureButtonShortPressing: + if (m_applet->handling_capture_button_short_pressed_message_enabled_for_applet) { + m_applet->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectShortPressingCaptureButton); + } + break; + case SystemButtonType::CaptureButtonLongPressing: + if (m_applet->handling_capture_button_long_pressed_message_enabled_for_applet) { + m_applet->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectLongPressingCaptureButton); + } + break; + default: + // Other buttons ignored for now + break; + } + R_SUCCEED(); } @@ -304,4 +340,18 @@ Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnab R_SUCCEED(); } +Result ICommonStateGetter::PushToGeneralChannel(SharedPointer storage) { + LOG_DEBUG(Service_AM, "called"); + system.PushGeneralChannelData(storage->GetData()); + R_SUCCEED(); +} + +Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) { + LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id); + + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_short_pressed_blocked = !enabled; + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/common_state_getter.h b/src/core/hle/service/am/service/common_state_getter.h index 16a9da0c51..a98024182f 100644 --- a/src/core/hle/service/am/service/common_state_getter.h +++ b/src/core/hle/service/am/service/common_state_getter.h @@ -21,6 +21,7 @@ namespace Service::AM { struct Applet; class ILockAccessor; +class IStorage; class ICommonStateGetter final : public ServiceFramework { public: @@ -60,6 +61,8 @@ private: OutArray out_applet_ids); Result GetSettingsPlatformRegion(Out out_settings_platform_region); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); + Result PushToGeneralChannel(SharedPointer storage); // cmd 20 + Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); void SetCpuBoostMode(HLERequestContext& ctx); diff --git a/src/core/hle/service/am/service/home_menu_functions.cpp b/src/core/hle/service/am/service/home_menu_functions.cpp index 25f78beb5a..a0b05f6f36 100644 --- a/src/core/hle/service/am/service/home_menu_functions.cpp +++ b/src/core/hle/service/am/service/home_menu_functions.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,20 +9,21 @@ #include "core/hle/service/am/service/home_menu_functions.h" #include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/service/storage.h" namespace Service::AM { IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr applet, WindowSystem& window_system) : ServiceFramework{system_, "IHomeMenuFunctions"}, m_window_system{window_system}, - m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"}, - m_pop_from_general_channel_event{m_context} { + m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"} { // clang-format off static const FunctionInfo functions[] = { {10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"}, {11, D<&IHomeMenuFunctions::LockForeground>, "LockForeground"}, {12, D<&IHomeMenuFunctions::UnlockForeground>, "UnlockForeground"}, - {20, nullptr, "PopFromGeneralChannel"}, + {20, D<&IHomeMenuFunctions::PopFromGeneralChannel>, "PopFromGeneralChannel"}, {21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"}, {30, nullptr, "GetHomeButtonWriterLockAccessor"}, {31, nullptr, "GetWriterLockAccessorEx"}, @@ -57,10 +61,22 @@ Result IHomeMenuFunctions::UnlockForeground() { R_SUCCEED(); } +Result IHomeMenuFunctions::PopFromGeneralChannel(Out> out_storage) { + LOG_DEBUG(Service_AM, "called"); + + std::vector data; + if (!system.TryPopGeneralChannel(data)) { + R_THROW(AM::ResultNoDataInChannel); + } + + *out_storage = std::make_shared(system, std::move(data)); + R_SUCCEED(); +} + Result IHomeMenuFunctions::GetPopFromGeneralChannelEvent( OutCopyHandle out_event) { LOG_INFO(Service_AM, "called"); - *out_event = m_pop_from_general_channel_event.GetHandle(); + *out_event = system.GetGeneralChannelEvent().GetHandle(); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/home_menu_functions.h b/src/core/hle/service/am/service/home_menu_functions.h index f56094aa9d..140071a74e 100644 --- a/src/core/hle/service/am/service/home_menu_functions.h +++ b/src/core/hle/service/am/service/home_menu_functions.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,6 +15,7 @@ namespace Service::AM { struct Applet; class WindowSystem; +class IStorage; class IHomeMenuFunctions final : public ServiceFramework { public: @@ -23,6 +27,7 @@ private: Result RequestToGetForeground(); Result LockForeground(); Result UnlockForeground(); + Result PopFromGeneralChannel(Out> out_storage); Result GetPopFromGeneralChannelEvent(OutCopyHandle out_event); Result IsRebootEnabled(Out out_is_reboot_enbaled); Result IsForceTerminateApplicationDisabledForDebug( @@ -31,7 +36,6 @@ private: WindowSystem& m_window_system; const std::shared_ptr m_applet; KernelHelpers::ServiceContext m_context; - Event m_pop_from_general_channel_event; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/service/overlay_applet_proxy.cpp b/src/core/hle/service/am/service/overlay_applet_proxy.cpp new file mode 100644 index 0000000000..5e140fe031 --- /dev/null +++ b/src/core/hle/service/am/service/overlay_applet_proxy.cpp @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/am/service/common_state_getter.h" +#include "core/hle/service/am/service/display_controller.h" +#include "core/hle/service/am/service/global_state_controller.h" +#include "core/hle/service/am/service/audio_controller.h" +#include "core/hle/service/am/service/applet_common_functions.h" +#include "core/hle/service/am/service/overlay_applet_proxy.h" +#include "core/hle/service/am/service/library_applet_creator.h" +#include "core/hle/service/am/service/process_winding_controller.h" +#include "core/hle/service/am/service/self_controller.h" +#include "core/hle/service/am/service/window_controller.h" +#include "core/hle/service/am/service/debug_functions.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + IOverlayAppletProxy::IOverlayAppletProxy(Core::System &system_, std::shared_ptr applet, + Kernel::KProcess *process, WindowSystem &window_system) + : ServiceFramework{system_, "IOverlayAppletProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IOverlayAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, + {1, D<&IOverlayAppletProxy::GetSelfController>, "GetSelfController"}, + {2, D<&IOverlayAppletProxy::GetWindowController>, "GetWindowController"}, + {3, D<&IOverlayAppletProxy::GetAudioController>, "GetAudioController"}, + {4, D<&IOverlayAppletProxy::GetDisplayController>, "GetDisplayController"}, + {10, D<&IOverlayAppletProxy::GetProcessWindingController>, "GetProcessWindingController"}, + {11, D<&IOverlayAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"}, + {20, D<&IOverlayAppletProxy::GetOverlayFunctions>, "GetOverlayFunctions"}, + {21, D<&IOverlayAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"}, + {23, D<&IOverlayAppletProxy::GetGlobalStateController>, "GetGlobalStateController"}, + {1000, D<&IOverlayAppletProxy::GetDebugFunctions>, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + IOverlayAppletProxy::~IOverlayAppletProxy() = default; + + Result IOverlayAppletProxy::GetCommonStateGetter( + Out > out_common_state_getter) { + LOG_DEBUG(Service_AM, "called"); + *out_common_state_getter = std::make_shared(system, m_applet); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetSelfController( + Out > out_self_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_self_controller = std::make_shared(system, m_applet, m_process); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetWindowController( + Out > out_window_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_window_controller = std::make_shared(system, m_applet, m_window_system); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetAudioController( + Out > out_audio_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_audio_controller = std::make_shared(system); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetDisplayController( + Out > out_display_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_display_controller = std::make_shared(system, m_applet); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetProcessWindingController( + Out > out_process_winding_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_process_winding_controller = std::make_shared(system, m_applet); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetLibraryAppletCreator( + Out > out_library_applet_creator) { + LOG_DEBUG(Service_AM, "called"); + *out_library_applet_creator = + std::make_shared(system, m_applet, m_window_system); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetOverlayFunctions( + Out > out_overlay_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_overlay_functions = std::make_shared(system, m_applet); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetAppletCommonFunctions( + Out > out_applet_common_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_applet_common_functions = std::make_shared(system, m_applet); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetGlobalStateController( + Out > out_global_state_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_global_state_controller = std::make_shared(system); + R_SUCCEED(); + } + + Result IOverlayAppletProxy::GetDebugFunctions( + Out > out_debug_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_debug_functions = std::make_shared(system); + R_SUCCEED(); + } +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/overlay_applet_proxy.h b/src/core/hle/service/am/service/overlay_applet_proxy.h new file mode 100644 index 0000000000..2c7fa57dfd --- /dev/null +++ b/src/core/hle/service/am/service/overlay_applet_proxy.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" +#include "core/hle/service/am/service/overlay_functions.h" + +namespace Service::AM { + +struct Applet; +class IAppletCommonFunctions; +class IAudioController; +class ICommonStateGetter; +class IDebugFunctions; +class IDisplayController; +class IHomeMenuFunctions; +class IGlobalStateController; +class ILibraryAppletCreator; +class ILibraryAppletSelfAccessor; +class IProcessWindingController; +class ISelfController; +class IWindowController; +class WindowSystem; + +class IOverlayAppletProxy final : public ServiceFramework { +public: + explicit IOverlayAppletProxy(Core::System& system_, std::shared_ptr applet, + Kernel::KProcess* process, WindowSystem& window_system); + ~IOverlayAppletProxy(); + +private: + Result GetCommonStateGetter(Out> out_common_state_getter); + Result GetSelfController(Out> out_self_controller); + Result GetWindowController(Out> out_window_controller); + Result GetAudioController(Out> out_audio_controller); + Result GetDisplayController(Out> out_display_controller); + Result GetProcessWindingController( + Out> out_process_winding_controller); + Result GetLibraryAppletCreator( + Out> out_library_applet_creator); + Result GetOverlayFunctions(Out> out_overlay_functions); + Result GetAppletCommonFunctions( + Out> out_applet_common_functions); + Result GetGlobalStateController( + Out> out_global_state_controller); + Result GetDebugFunctions(Out> out_debug_functions); + + WindowSystem& m_window_system; + Kernel::KProcess* const m_process; + const std::shared_ptr m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/overlay_functions.cpp b/src/core/hle/service/am/service/overlay_functions.cpp new file mode 100644 index 0000000000..8af037734a --- /dev/null +++ b/src/core/hle/service/am/service/overlay_functions.cpp @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/service/overlay_functions.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/window_system.h" + +namespace Service::AM { + IOverlayFunctions::IOverlayFunctions(Core::System &system_, std::shared_ptr applet) + : ServiceFramework{system_, "IOverlayFunctions"}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IOverlayFunctions::BeginToWatchShortHomeButtonMessage>, "BeginToWatchShortHomeButtonMessage"}, + {1, D<&IOverlayFunctions::EndToWatchShortHomeButtonMessage>, "EndToWatchShortHomeButtonMessage"}, + {2, D<&IOverlayFunctions::GetApplicationIdForLogo>, "GetApplicationIdForLogo"}, + {3, nullptr, "SetGpuTimeSliceBoost"}, + {4, D<&IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled>, "SetAutoSleepTimeAndDimmingTimeEnabled"}, + {5, nullptr, "TerminateApplicationAndSetReason"}, + {6, nullptr, "SetScreenShotPermissionGlobally"}, + {10, nullptr, "StartShutdownSequenceForOverlay"}, + {11, nullptr, "StartRebootSequenceForOverlay"}, + {20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"}, + {21, nullptr, "SetHandlingTouchScreenInputEnabled"}, + {30, nullptr, "SetHealthWarningShowingState"}, + {31, nullptr, "IsHealthWarningRequired"}, + {40, nullptr, "GetApplicationNintendoLogo"}, + {41, nullptr, "GetApplicationStartupMovie"}, + {50, nullptr, "SetGpuTimeSliceBoostForApplication"}, + {60, nullptr, "Unknown60"}, + {70, D<&IOverlayFunctions::Unknown70>, "Unknown70"}, + {90, nullptr, "SetRequiresGpuResourceUse"}, + {101, nullptr, "BeginToObserveHidInputForDevelop"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + IOverlayFunctions::~IOverlayFunctions() = default; + + Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() { + LOG_DEBUG(Service_AM, "called"); + + m_applet->overlay_in_foreground = true; + m_applet->home_button_short_pressed_blocked = false; + + if (auto *window_system = system.GetAppletManager().GetWindowSystem()) { + window_system->RequestUpdate(); + } + + R_SUCCEED(); + } + + Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() { + LOG_DEBUG(Service_AM, "called"); + + m_applet->overlay_in_foreground = false; + m_applet->home_button_short_pressed_blocked = false; + + if (auto *window_system = system.GetAppletManager().GetWindowSystem()) { + window_system->RequestUpdate(); + } + + R_SUCCEED(); + } + + Result IOverlayFunctions::GetApplicationIdForLogo(Out out_application_id) { + LOG_DEBUG(Service_AM, "called"); + + // Prefer explicit application_id if available, else fall back to program_id + std::scoped_lock lk{m_applet->lock}; + u64 id = m_applet->screen_shot_identity.application_id; + if (id == 0) { + id = m_applet->program_id; + } + *out_application_id = id; + R_SUCCEED(); + } + + Result IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled) { + LOG_WARNING(Service_AM, "(STUBBED) called, enabled={}", enabled); + std::scoped_lock lk{m_applet->lock}; + m_applet->auto_sleep_disabled = !enabled; + R_SUCCEED(); + } + + Result IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled(bool enabled) { + LOG_DEBUG(Service_AM, "called, enabled={}", enabled); + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_short_pressed_blocked = !enabled; + R_SUCCEED(); + } + + Result IOverlayFunctions::Unknown70() { + LOG_DEBUG(Service_AM, "called"); + R_SUCCEED(); + } +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/overlay_functions.h b/src/core/hle/service/am/service/overlay_functions.h new file mode 100644 index 0000000000..26c5cc0c88 --- /dev/null +++ b/src/core/hle/service/am/service/overlay_functions.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + struct Applet; + + class IOverlayFunctions final : public ServiceFramework { + public: + explicit IOverlayFunctions(Core::System &system_, std::shared_ptr applet); + ~IOverlayFunctions() override; + + private: + Result BeginToWatchShortHomeButtonMessage(); + Result EndToWatchShortHomeButtonMessage(); + Result GetApplicationIdForLogo(Out out_application_id); + Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled); + Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); + Result Unknown70(); + + private: + const std::shared_ptr m_applet; + }; +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/system_applet_proxy.h b/src/core/hle/service/am/service/system_applet_proxy.h index 217d9dc8ca..ab9769b3d9 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.h +++ b/src/core/hle/service/am/service/system_applet_proxy.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,14 +33,14 @@ public: ~ISystemAppletProxy(); private: + Result GetCommonStateGetter(Out> out_common_state_getter); + Result GetSelfController(Out> out_self_controller); + Result GetWindowController(Out> out_window_controller); Result GetAudioController(Out> out_audio_controller); Result GetDisplayController(Out> out_display_controller); Result GetProcessWindingController( Out> out_process_winding_controller); Result GetDebugFunctions(Out> out_debug_functions); - Result GetWindowController(Out> out_window_controller); - Result GetSelfController(Out> out_self_controller); - Result GetCommonStateGetter(Out> out_common_state_getter); Result GetLibraryAppletCreator( Out> out_library_applet_creator); Result GetApplicationCreator(Out> out_application_creator); diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index 5cf24007cc..c78a955f68 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -21,9 +24,18 @@ void WindowSystem::SetEventObserver(EventObserver* observer) { m_system.GetAppletManager().SetWindowSystem(this); } +void WindowSystem::RequestUpdate() { + if (m_event_observer) { + m_event_observer->RequestUpdate(); + } +} + void WindowSystem::Update() { std::scoped_lock lk{m_lock}; + LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}", + m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr); + // Loop through all applets and remove terminated applets. this->PruneTerminatedAppletsLocked(); @@ -32,9 +44,16 @@ void WindowSystem::Update() { return; } + bool overlay_blocks_input = false; + if (m_overlay_display) { + std::scoped_lock lk_overlay{m_overlay_display->lock}; + overlay_blocks_input = m_overlay_display->overlay_in_foreground; + } + // Recursively update each applet root. - this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu); - this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application); + this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input); + this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input); + this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked } void WindowSystem::TrackApplet(std::shared_ptr applet, bool is_application) { @@ -43,6 +62,8 @@ void WindowSystem::TrackApplet(std::shared_ptr applet, bool is_applicati if (applet->applet_id == AppletId::QLaunch) { ASSERT(m_home_menu == nullptr); m_home_menu = applet.get(); + } else if (applet->applet_id == AppletId::OverlayDisplay) { + m_overlay_display = applet.get(); } else if (is_application) { ASSERT(m_application == nullptr); m_application = applet.get(); @@ -136,21 +157,66 @@ void WindowSystem::OnExitRequested() { } } -void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) { - std::scoped_lock lk{m_lock}; - - // If we don't have a home menu, nothing to do. - if (!m_home_menu) { - return; +void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) { + if (m_home_menu) { + std::scoped_lock lk_home{m_home_menu->lock}; + m_home_menu->lifecycle_manager.PushUnorderedMessage(message); } + if (m_overlay_display) { + std::scoped_lock lk_overlay{m_overlay_display->lock}; + m_overlay_display->lifecycle_manager.PushUnorderedMessage(message); + } + if (m_application) { + std::scoped_lock lk_application{m_application->lock}; + m_application->lifecycle_manager.PushUnorderedMessage(message); + } + if (m_event_observer) { + m_event_observer->RequestUpdate(); + } +} - // Lock. - std::scoped_lock lk2{m_home_menu->lock}; +void WindowSystem::OnSystemButtonPress(SystemButtonType type) { + std::scoped_lock lk{m_lock}; + switch (type) { + case SystemButtonType::HomeButtonShortPressing: + SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton); + break; + case SystemButtonType::HomeButtonLongPressing: { + // Toggle overlay foreground visibility on long home press + if (m_overlay_display) { + std::scoped_lock lk_overlay{m_overlay_display->lock}; + m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground; + // Tie window visibility to foreground state so hidden when not active + m_overlay_display->window_visible = m_overlay_display->overlay_in_foreground; + LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible); + } + SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton); + // Force a state update after toggling overlay + if (m_event_observer) { + m_event_observer->RequestUpdate(); + } + break; } + case SystemButtonType::CaptureButtonShortPressing: + SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton); + break; + case SystemButtonType::CaptureButtonLongPressing: + SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingCaptureButton); + break; + default: + break; + } +} - // Send home button press event to home menu. - if (type == ButtonPressDuration::ShortPressing) { - m_home_menu->lifecycle_manager.PushUnorderedMessage( - AppletMessage::DetectShortPressingHomeButton); +void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) { + // Map duration to SystemButtonType for legacy callers + switch (type) { + case ButtonPressDuration::ShortPressing: + OnSystemButtonPress(SystemButtonType::HomeButtonShortPressing); + break; + case ButtonPressDuration::MiddlePressing: + case ButtonPressDuration::LongPressing: + OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing); + break; } } @@ -208,6 +274,10 @@ void WindowSystem::PruneTerminatedAppletsLocked() { } } + if (applet.get() == m_overlay_display) { + m_overlay_display = nullptr; + } + // Finalize applet. applet->OnProcessTerminatedLocked(); @@ -258,7 +328,7 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) { applet->lock.lock(); } -void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) { +void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) { // With no applet, we don't have anything to do. if (!applet) { return; @@ -287,10 +357,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) { }(); // Update visibility state. - applet->display_layer_manager.SetWindowVisibility(is_foreground && applet->window_visible); + // Overlay applets should always be visible when window_visible is true, regardless of foreground state + const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay) + ? applet->window_visible + : (is_foreground && applet->window_visible); + applet->display_layer_manager.SetWindowVisibility(should_be_visible); - // Update interactibility state. - applet->SetInteractibleLocked(is_foreground && applet->window_visible); + + const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay) + ? applet->overlay_in_foreground + : (is_foreground && applet->window_visible && !overlay_blocking); + + if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) { + LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}", + static_cast(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible); + } + + applet->SetInteractibleLocked(should_be_interactible); // Update focus state and suspension. const bool is_obscured = has_obscuring_child_applets || !applet->window_visible; @@ -306,9 +389,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) { applet->UpdateSuspensionStateLocked(true); } + // Z-index logic like in reference C# implementation (tuned for overlay extremes) + s32 z_index = 0; + const bool now_foreground = inherited_foreground; + if (applet->applet_id == AppletId::OverlayDisplay) { + z_index = applet->overlay_in_foreground ? 100000 : -100000; + } else if (now_foreground && !is_obscured) { + z_index = 2; + } else if (now_foreground) { + z_index = 1; + } else { + z_index = 0; + } + applet->display_layer_manager.SetOverlayZIndex(z_index); + // Recurse into child applets. for (const auto& child_applet : applet->child_applets) { - this->UpdateAppletStateLocked(child_applet.get(), is_foreground); + this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking); } } diff --git a/src/core/hle/service/am/window_system.h b/src/core/hle/service/am/window_system.h index 69e7a27ba5..7cd47d81b5 100644 --- a/src/core/hle/service/am/window_system.h +++ b/src/core/hle/service/am/window_system.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,6 +11,7 @@ #include #include "common/common_types.h" +#include "core/hle/service/am/am_types.h" namespace Core { class System; @@ -32,6 +36,7 @@ public: public: void SetEventObserver(EventObserver* event_observer); void Update(); + void RequestUpdate(); public: void TrackApplet(std::shared_ptr applet, bool is_application); @@ -49,6 +54,7 @@ public: void OnOperationModeChanged(); void OnExitRequested(); void OnHomeButtonPressed(ButtonPressDuration type); + void OnSystemButtonPress(SystemButtonType type); void OnCaptureButtonPressed(ButtonPressDuration type) {} void OnPowerButtonPressed(ButtonPressDuration type) {} @@ -56,7 +62,8 @@ private: void PruneTerminatedAppletsLocked(); bool LockHomeMenuIntoForegroundLocked(); void TerminateChildAppletsLocked(Applet* applet); - void UpdateAppletStateLocked(Applet* applet, bool is_foreground); + void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false); + void SendButtonAppletMessageLocked(AppletMessage message); private: // System reference. @@ -75,6 +82,7 @@ private: // Foreground roots. Applet* m_home_menu{}; Applet* m_application{}; + Applet* m_overlay_display{}; // Applet map by aruid. std::map> m_applets{}; diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp index 4456869d35..10b60618e5 100644 --- a/src/core/hle/service/audio/audio_controller.cpp +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -1,15 +1,22 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" +#include "audio_core/audio_core.h" #include "core/hle/service/audio/audio_controller.h" +#include "core/hle/service/audio/audio_out_manager.h" +#include "core/core.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/set/system_settings_server.h" #include "core/hle/service/sm/sm.h" +#include "common/settings.h" +#include namespace Service::Audio { @@ -17,12 +24,12 @@ IAudioController::IAudioController(Core::System& system_) : ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "GetTargetVolume"}, - {1, nullptr, "SetTargetVolume"}, + {0, D<&IAudioController::GetTargetVolume>, "GetTargetVolume"}, + {1, D<&IAudioController::SetTargetVolume>, "SetTargetVolume"}, {2, D<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"}, {3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"}, - {4, nullptr, "IsTargetMute"}, - {5, nullptr, "SetTargetMute"}, + {4, D<&IAudioController::IsTargetMute>, "IsTargetMute"}, + {5, D<&IAudioController::SetTargetMute>, "SetTargetMute"}, {6, nullptr, "IsTargetConnected"}, {7, nullptr, "SetDefaultTarget"}, {8, nullptr, "GetDefaultTarget"}, @@ -49,7 +56,7 @@ IAudioController::IAudioController(Core::System& system_) {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, {30, D<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"}, {31, D<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"}, - {32, nullptr, "GetActiveOutputTarget"}, + {32, D<&IAudioController::GetActiveOutputTarget>, "GetActiveOutputTarget"}, {33, nullptr, "GetTargetDeviceInfo"}, {34, D<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"}, {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, @@ -84,6 +91,38 @@ IAudioController::IAudioController(Core::System& system_) m_set_sys = system.ServiceManager().GetService("set:sys", true); notification_event = service_context.CreateEvent("IAudioController:NotificationEvent"); + + // Probably shouldn't do this in constructor? + try { + const int ui_volume = Settings::values.volume.GetValue(); + const int mapped = static_cast(std::lround((static_cast(ui_volume) / 100.0) * 15.0)); + const auto active_idx = static_cast(m_active_target); + if (active_idx < m_target_volumes.size()) { + m_target_volumes[active_idx] = std::clamp(mapped, 0, 15); + } + + const bool muted = Settings::values.audio_muted.GetValue(); + for (auto& m : m_target_muted) { + m = muted; + } + + if (!muted && active_idx < m_target_volumes.size()) { + const float vol_f = static_cast(m_target_volumes[active_idx]) / 15.0f; + try { + auto& sink = system.AudioCore().GetOutputSink(); + sink.SetSystemVolume(vol_f); + sink.SetDeviceVolume(vol_f); + } catch (...) { + LOG_WARNING(Audio, "Failed to apply initial sink volume from settings"); + } + + if (auto audout_mgr = system.ServiceManager().GetService("audout:u")) { + audout_mgr->SetAllAudioOutVolume(static_cast(m_target_volumes[active_idx]) / 15.0f); + } + } + } catch (...) { + // Catch if something fails, since this is constructor + } } IAudioController::~IAudioController() { @@ -191,4 +230,120 @@ Result IAudioController::Unknown5000(Out> out_au R_SUCCEED(); } + +Result IAudioController::GetTargetVolume(Out out_target_volume, Set::AudioOutputModeTarget target) { + LOG_DEBUG(Audio, "GetTargetVolume called, target={}", target); + + const auto idx = static_cast(target); + if (idx >= m_target_volumes.size()) { + LOG_ERROR(Audio, "GetTargetVolume invalid target {}", idx); + R_THROW(ResultInvalidArgument); + } + + *out_target_volume = m_target_volumes[idx]; + R_SUCCEED(); +} + +Result IAudioController::SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume) { + LOG_INFO(Audio, "SetTargetVolume called, target={}, volume={}", target, target_volume); + + const auto idx = static_cast(target); + if (idx >= m_target_volumes.size()) { + LOG_ERROR(Audio, "SetTargetVolume invalid target {}", idx); + R_THROW(ResultInvalidArgument); + } + + if (target_volume < 0) { + target_volume = 0; + } else if (target_volume > 15) { + target_volume = 15; + } + + m_target_volumes[idx] = target_volume; + + if (m_active_target == target && !m_target_muted[idx]) { + const float vol = static_cast(target_volume) / 15.0f; + // try catch this, as we don't know how it handles it when no output is set. + // we already have audio issues when no output is set, so catch. + try { + auto& sink = system.AudioCore().GetOutputSink(); + sink.SetSystemVolume(vol); + sink.SetDeviceVolume(vol); + } catch (...) { + LOG_WARNING(Audio, "Failed to set sink system volume"); + } + + if (auto audout_mgr = system.ServiceManager().GetService("audout:u")) { + audout_mgr->SetAllAudioOutVolume(vol); + } + } + + if (m_active_target == target) { + const int ui_volume = static_cast(std::lround((static_cast(target_volume) / 15.0) * 100.0)); + Settings::values.volume.SetValue(static_cast(std::clamp(ui_volume, 0, 100))); + } + + R_SUCCEED(); +} + +Result IAudioController::IsTargetMute(Out out_is_target_muted, Set::AudioOutputModeTarget target) { + LOG_DEBUG(Audio, "called, target={}", target); + + const auto idx = static_cast(target); + if (idx >= m_target_muted.size()) { + LOG_ERROR(Audio, "invalid target {}", idx); + R_THROW(ResultInvalidArgument); + } + + *out_is_target_muted = m_target_muted[idx]; + R_SUCCEED(); +} + +Result IAudioController::SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target) { + LOG_INFO(Audio, "called, target={}, muted={}", target, is_muted); + + const auto idx = static_cast(target); + if (idx >= m_target_muted.size()) { + LOG_ERROR(Audio, "invalid target {}", idx); + R_THROW(ResultInvalidArgument); + } + + m_target_muted[idx] = is_muted; + + if (m_active_target == target) { + try { + auto& sink = system.AudioCore().GetOutputSink(); + if (is_muted) { + sink.SetSystemVolume(0.0f); + sink.SetDeviceVolume(0.0f); + } else { + const float vol = static_cast(m_target_volumes[idx]) / 15.0f; + sink.SetSystemVolume(vol); + sink.SetDeviceVolume(vol); + } + } catch (...) { + LOG_WARNING(Audio, "Failed to set sink system volume on mute change"); + } + + // Also update any open audout sessions via the audout:u service. + if (auto audout_mgr = system.ServiceManager().GetService("audout:u")) { + if (is_muted) { + audout_mgr->SetAllAudioOutVolume(0.0f); + } else { + const float vol = static_cast(m_target_volumes[idx]) / 15.0f; + audout_mgr->SetAllAudioOutVolume(vol); + } + } + } + + Settings::values.audio_muted.SetValue(is_muted); + + R_SUCCEED(); +} + +Result IAudioController::GetActiveOutputTarget(Out out_active_target) { + LOG_DEBUG(Audio, "GetActiveOutputTarget called"); + *out_active_target = m_active_target; + R_SUCCEED(); +} } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h index b7645332e6..ff51f4c244 100644 --- a/src/core/hle/service/audio/audio_controller.h +++ b/src/core/hle/service/audio/audio_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -35,6 +38,11 @@ private: Result GetTargetVolumeMin(Out out_target_min_volume); Result GetTargetVolumeMax(Out out_target_max_volume); + Result GetTargetVolume(Out out_target_volume, Set::AudioOutputModeTarget target); + Result SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume); + Result IsTargetMute(Out out_is_target_muted, Set::AudioOutputModeTarget target); + Result SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target); + Result GetActiveOutputTarget(Out out_active_target); Result GetAudioOutputMode(Out out_output_mode, Set::AudioOutputModeTarget target); Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode); @@ -55,6 +63,9 @@ private: Kernel::KEvent* notification_event; std::shared_ptr m_set_sys; + std::array m_target_volumes{{15, 15, 15, 15, 15, 15}}; + std::array m_target_muted{{false, false, false, false, false, false}}; + Set::AudioOutputModeTarget m_active_target{Set::AudioOutputModeTarget::Speaker}; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_out_manager.cpp b/src/core/hle/service/audio/audio_out_manager.cpp index 1534450972..0a8e1ec256 100644 --- a/src/core/hle/service/audio/audio_out_manager.cpp +++ b/src/core/hle/service/audio/audio_out_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -98,4 +101,15 @@ Result IAudioOutManager::OpenAudioOutAuto( R_SUCCEED(); } +Result IAudioOutManager::SetAllAudioOutVolume(f32 volume) { + std::scoped_lock l{impl->mutex}; + for (auto& session : impl->sessions) { + if (session) { + session->GetSystem().SetVolume(volume); + } + } + + R_SUCCEED(); +} + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_out_manager.h b/src/core/hle/service/audio/audio_out_manager.h index eaa27bc798..791274d5e9 100644 --- a/src/core/hle/service/audio/audio_out_manager.h +++ b/src/core/hle/service/audio/audio_out_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,6 +21,8 @@ public: explicit IAudioOutManager(Core::System& system_); ~IAudioOutManager() override; + Result SetAllAudioOutVolume(f32 volume); + private: Result ListAudioOuts(OutArray out_audio_outs, Out out_count); diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index c41345f7e7..8062820de3 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,6 +22,7 @@ constexpr Result ResultInvalidAddressInfo{ErrorModule::Audio, 42}; constexpr Result ResultNotSupported{ErrorModule::Audio, 513}; constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; +constexpr Result ResultInvalidArgument{ErrorModule::Audio, 900}; constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7}; constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8}; diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 52228b830a..72d3977081 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -53,6 +56,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, {10011, nullptr, "SetInternalErrorConversionEnabled"}, {50000, nullptr, "LoadMakerNoteInfoForDebug"}, + {50011, C<&IAlbumAccessorService::GetAlbumAccessResultForDebug>, "GetAlbumAccessResultForDebug"}, {60002, nullptr, "OpenAccessorSession"}, }; // clang-format on @@ -197,4 +201,11 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) { return in_result; } + +Result IAlbumAccessorService::GetAlbumAccessResultForDebug(Out out_result) { + LOG_WARNING(Service_Capture, "(STUBBED) called"); + *out_result = ResultSuccess; + R_SUCCEED(); +} + } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index c7a5208e32..1885ba6be0 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -52,6 +55,8 @@ private: Result TranslateResult(Result in_result); + Result GetAlbumAccessResultForDebug(Out out_result); + std::shared_ptr manager = nullptr; }; diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index dca4a3a770..45d452cee1 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -150,16 +153,17 @@ private: class INpnsUser final : public ServiceFramework { public: - explicit INpnsUser(Core::System& system_) : ServiceFramework{system_, "npns:u"} { + explicit INpnsUser(Core::System& system_) + : ServiceFramework{system_, "npns:u"}, service_context{system, "npns:u"} { // clang-format off static const FunctionInfo functions[] = { {1, nullptr, "ListenAll"}, {2, nullptr, "ListenTo"}, {3, nullptr, "Receive"}, {4, nullptr, "ReceiveRaw"}, - {5, nullptr, "GetReceiveEvent"}, + {5, C<&INpnsUser::GetReceiveEvent>, "GetReceiveEvent"}, {7, nullptr, "GetStateChangeEvent"}, - {8, nullptr, "ListenToByName"}, // 18.0.0+ + {8, C<&INpnsUser::ListenToByName>, "ListenToByName"}, // 18.0.0+ {21, nullptr, "CreateToken"}, {23, nullptr, "DestroyToken"}, {25, nullptr, "QueryIsTokenValid"}, @@ -178,7 +182,33 @@ public: // clang-format on RegisterHandlers(functions); + + get_receive_event = service_context.CreateEvent("npns:u:GetReceiveEvent"); } + + ~INpnsUser() override { + service_context.CloseEvent(get_receive_event); + } + +private: + Result ListenToByName(InBuffer name_buffer) { + const std::string name(reinterpret_cast(name_buffer.data()), name_buffer.size()); + LOG_DEBUG(Service_NPNS, "called, name={}", name); + + // Store the name for future use if needed + // For now, just acknowledge the registration + R_SUCCEED(); + } + + Result GetReceiveEvent(OutCopyHandle out_event) { + LOG_DEBUG(Service_NPNS, "called"); + + *out_event = &get_receive_event->GetReadableEvent(); + R_SUCCEED(); + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* get_receive_event; }; void LoopProcess(Core::System& system) { diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp index e19a1cdd4e..81f42f0937 100644 --- a/src/core/hle/service/ns/application_manager_interface.cpp +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" #include "core/hle/service/cmif_serialization.h" @@ -11,6 +12,7 @@ #include "core/hle/service/ns/application_manager_interface.h" #include "core/hle/service/ns/content_management_interface.h" #include "core/hle/service/ns/read_only_application_control_data_interface.h" +#include "core/file_sys/patch_manager.h" namespace Service::NS { @@ -19,7 +21,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ service_context{system, "IApplicationManagerInterface"}, record_update_system_event{service_context}, sd_card_mount_status_event{service_context}, gamecard_update_detection_event{service_context}, - gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} { + gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context}, + gamecard_waken_ready_event{service_context}, unknown_event{service_context} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"}, @@ -42,7 +45,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {26, nullptr, "BeginInstallApplication"}, {27, nullptr, "DeleteApplicationRecord"}, {30, nullptr, "RequestApplicationUpdateInfo"}, - {31, nullptr, "Unknown31"}, + {31, nullptr, "RequestUpdateApplication"}, {32, nullptr, "CancelApplicationDownload"}, {33, nullptr, "ResumeApplicationDownload"}, {35, nullptr, "UpdateVersionList"}, @@ -138,6 +141,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {508, nullptr, "GetLastGameCardMountFailureResult"}, {509, nullptr, "ListApplicationIdOnGameCard"}, {510, nullptr, "GetGameCardPlatformRegion"}, + {511, D<&IApplicationManagerInterface::GetGameCardWakenReadyEvent>, "GetGameCardWakenReadyEvent"}, + {512, D<&IApplicationManagerInterface::IsGameCardApplicationRunning>, "IsGameCardApplicationRunning"}, {600, nullptr, "CountApplicationContentMeta"}, {601, nullptr, "ListApplicationContentMetaStatus"}, {602, nullptr, "ListAvailableAddOnContent"}, @@ -162,7 +167,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {901, nullptr, "GetApplicationRecordProperty"}, {902, nullptr, "EnableApplicationAutoUpdate"}, {903, nullptr, "DisableApplicationAutoUpdate"}, - {904, nullptr, "TouchApplication"}, + {904, D<&IApplicationManagerInterface::TouchApplication>, "TouchApplication"}, {905, nullptr, "RequestApplicationUpdate"}, {906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"}, {907, nullptr, "WithdrawApplicationUpdateRequest"}, @@ -329,7 +334,7 @@ Result IApplicationManagerInterface::GetApplicationControlData( OutBuffer out_buffer, Out out_actual_size, ApplicationControlSource application_control_source, u64 application_id) { LOG_DEBUG(Service_NS, "called"); - R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlDataOld( + R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData( out_buffer, out_actual_size, application_control_source, application_id)); } @@ -403,6 +408,19 @@ Result IApplicationManagerInterface::GetGameCardMountFailureEvent( R_SUCCEED(); } +Result IApplicationManagerInterface::GetGameCardWakenReadyEvent( + OutCopyHandle out_event) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_event = gamecard_waken_ready_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out out_is_running) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_is_running = false; + R_SUCCEED(); +} + Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled( Out out_is_any_application_entity_installed) { LOG_WARNING(Service_NS, "(STUBBED) called"); @@ -526,6 +544,11 @@ Result IApplicationManagerInterface::GetStorageSize(Out out_total_space_siz R_SUCCEED(); } +Result IApplicationManagerInterface::TouchApplication(u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id); + R_SUCCEED(); +} + Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out out_update_required, Out out_update_version, u64 application_id) { @@ -549,14 +572,18 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out o Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground( u64 control_source, u64 application_id) { - LOG_WARNING(Service_NS, "(STUBBED), control_source={} app={:016X}", control_source, application_id); + LOG_INFO(Service_NS, "called, control_source={} app={:016X}", + control_source, application_id); + + unknown_event.Signal(); R_SUCCEED(); } Result IApplicationManagerInterface::Unknown4022( OutCopyHandle out_event) { LOG_WARNING(Service_NS, "(STUBBED) called"); - *out_event = gamecard_update_detection_event.GetHandle(); + unknown_event.Signal(); + *out_event = unknown_event.GetHandle(); R_SUCCEED(); } diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h index 8a9eda3d13..9c36939257 100644 --- a/src/core/hle/service/ns/application_manager_interface.h +++ b/src/core/hle/service/ns/application_manager_interface.h @@ -32,6 +32,8 @@ public: Out out_count, s32 offset); Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle out_event); Result GetGameCardMountFailureEvent(OutCopyHandle out_event); + Result GetGameCardWakenReadyEvent(OutCopyHandle out_event); + Result IsGameCardApplicationRunning(Out out_is_running); Result IsAnyApplicationEntityInstalled(Out out_is_any_application_entity_installed); Result GetApplicationViewDeprecated( OutArray out_application_views, @@ -52,6 +54,7 @@ public: Result ResumeAll(); Result GetStorageSize(Out out_total_space_size, Out out_free_space_size, FileSys::StorageId storage_id); + Result TouchApplication(u64 application_id); Result IsApplicationUpdateRequested(Out out_update_required, Out out_update_version, u64 application_id); Result CheckApplicationLaunchVersion(u64 application_id); @@ -60,7 +63,7 @@ public: Result Unknown4023(Out out_result); Result Unknown4053(); - Result RequestDownloadApplicationControlDataInBackground(u64 unk, + Result RequestDownloadApplicationControlDataInBackground(u64 control_source, u64 application_id); private: @@ -70,6 +73,8 @@ private: Event gamecard_update_detection_event; Event gamecard_mount_status_event; Event gamecard_mount_failure_event; + Event gamecard_waken_ready_event; + Event unknown_event; }; } // namespace Service::NS diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp index abc0ee8050..b79369faad 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -22,13 +22,13 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { // clang-format off static const FunctionInfo functions[] = { - {0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataOld>, "GetApplicationControlDataOld"}, + {0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData>, "GetApplicationControlData"}, {1, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"}, {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, {4, nullptr, "SelectApplicationDesiredLanguage"}, - {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"}, - {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"}, + {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"}, + {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"}, }; // clang-format on @@ -37,7 +37,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; -Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataOld( +Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData( OutBuffer out_buffer, Out out_actual_size, ApplicationControlSource application_control_source, u64 application_id) { LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", @@ -125,8 +125,8 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan R_SUCCEED(); } -Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData( - OutBuffer out_buffer, Out out_actual_size, +Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon( + OutBuffer out_buffer, Out out_total_size, ApplicationControlSource application_control_source, u64 application_id) { LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", application_control_source, application_id); @@ -136,57 +136,44 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData( const auto control = pm.GetControlMetadata(); const auto size = out_buffer.size(); - const auto icon_size = control.second ? control.second->GetSize() : 0; - const auto total_size = sizeof(FileSys::RawNACP) + icon_size; + const auto nacp_size = sizeof(FileSys::RawNACP); - if (size < total_size) { - LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", - size); + if (size < nacp_size) { + LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", + size, nacp_size); R_THROW(ResultUnknown); } if (control.first != nullptr) { const auto bytes = control.first->GetRawBytes(); - std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); + const auto copy_len = (std::min)(static_cast(bytes.size()), static_cast(nacp_size)); + std::memcpy(out_buffer.data(), bytes.data(), copy_len); + if (copy_len < nacp_size) { + std::memset(out_buffer.data() + copy_len, 0, nacp_size - copy_len); + } } else { LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", application_id); - std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP)); + std::memset(out_buffer.data(), 0, nacp_size); } - if (control.second != nullptr) { - control.second->Read(out_buffer.data() + sizeof(FileSys::RawNACP), icon_size); - } else { - LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}", application_id); + const auto icon_area_size = size - nacp_size; + if (icon_area_size > 0) { + if (control.second != nullptr) { + const auto icon_size = control.second->GetSize(); + const auto to_copy = static_cast((std::min)(icon_size, icon_area_size)); + control.second->Read(out_buffer.data() + nacp_size, to_copy); + if (to_copy < icon_area_size) { + std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy); + } + } else { + std::memset(out_buffer.data() + nacp_size, 0, icon_area_size); + LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}, zero-filling icon area", + application_id); + } } - *out_actual_size = static_cast(total_size); - R_SUCCEED(); -} - -Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon( - OutBuffer out_buffer, - Out out_total_size, - ApplicationControlSource application_control_source, - u64 application_id) { - LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", - application_control_source, application_id); - - constexpr size_t kExpectedBufferSize = 0x14000; - constexpr size_t kNACPSize = sizeof(FileSys::RawNACP); - - const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), - system.GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - - if (control.first != nullptr) { - const auto bytes = control.first->GetRawBytes(); - std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); - } else { - std::memset(out_buffer.data(), 0, kNACPSize); - } - - *out_total_size = kExpectedBufferSize; + *out_total_size = static_cast(size); R_SUCCEED(); } diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.h b/src/core/hle/service/ns/read_only_application_control_data_interface.h index 0a0848bc73..99366c5792 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.h +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.h @@ -19,7 +19,7 @@ public: explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); ~IReadOnlyApplicationControlDataInterface() override; - Result GetApplicationControlDataOld(OutBuffer out_buffer, + Result GetApplicationControlData(OutBuffer out_buffer, Out out_actual_size, ApplicationControlSource application_control_source, u64 application_id); @@ -27,10 +27,6 @@ public: u32 supported_languages); Result ConvertApplicationLanguageToLanguageCode(Out out_language_code, ApplicationLanguage application_language); - Result GetApplicationControlData(OutBuffer out_buffer, - Out out_actual_size, - ApplicationControlSource application_control_source, - u64 application_id); Result GetApplicationControlDataWithoutIcon( OutBuffer out_buffer, Out out_total_size, diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h index 40aa597873..1264465079 100644 --- a/src/core/hle/service/nvnflinger/display.h +++ b/src/core/hle/service/nvnflinger/display.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +15,7 @@ struct Layer { explicit Layer(std::shared_ptr buffer_item_consumer_, s32 consumer_id_) : buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_), - blending(LayerBlending::None), visible(true) {} + blending(LayerBlending::None), visible(true), z_index(0) {} ~Layer() { buffer_item_consumer->Abandon(); } @@ -21,6 +24,7 @@ struct Layer { s32 consumer_id; LayerBlending blending; bool visible; + s32 z_index; }; struct LayerStack { diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index 85634b591b..4f8f5da69a 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -95,7 +95,6 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, const auto& item = buffer.item; const auto& igbp_buffer = *item.graphic_buffer; - // TODO: get proper Z-index from layer if (layer->visible) { composition_stack.emplace_back(HwcLayer{ .buffer_handle = igbp_buffer.BufferId(), @@ -104,7 +103,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, .width = igbp_buffer.Width(), .height = igbp_buffer.Height(), .stride = igbp_buffer.Stride(), - .z_index = 0, + .z_index = layer->z_index, .blending = layer->blending, .transform = static_cast(item.transform), .crop_rect = item.crop, @@ -128,9 +127,9 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, // If any new buffers were acquired, we can present. if (has_acquired_buffer && !composition_stack.empty()) { - // Sort by Z-index. + // Sort back-to-front: lower z first, higher z last so top-most draws last (on top). std::stable_sort(composition_stack.begin(), composition_stack.end(), - [&](auto& l, auto& r) { return l.z_index < r.z_index; }); + [&](const HwcLayer& l, const HwcLayer& r) { return l.z_index < r.z_index; }); // Composite. nvdisp.Composite(composition_stack); diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h index 406281c838..d771909a82 100644 --- a/src/core/hle/service/nvnflinger/surface_flinger.h +++ b/src/core/hle/service/nvnflinger/surface_flinger.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -45,9 +48,10 @@ public: void SetLayerVisibility(s32 consumer_binder_id, bool visible); void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending); + std::shared_ptr FindLayer(s32 consumer_binder_id); + private: Display* FindDisplay(u64 display_id); - std::shared_ptr FindLayer(s32 consumer_binder_id); public: // TODO: these don't belong here diff --git a/src/core/hle/service/olsc/daemon_controller.cpp b/src/core/hle/service/olsc/daemon_controller.cpp index fe4252c24a..45f0f75b94 100644 --- a/src/core/hle/service/olsc/daemon_controller.cpp +++ b/src/core/hle/service/olsc/daemon_controller.cpp @@ -13,16 +13,17 @@ IDaemonController::IDaemonController(Core::System& system_) : ServiceFramework{system_, "IDaemonController"} { // clang-format off static const FunctionInfo functions[] = { - {0, D<&IDaemonController::GetAutoTransferEnabledForAccountAndApplication>, "GetAutoTransferEnabledForAccountAndApplication"}, - {1, nullptr, "SetAutoTransferEnabledForAccountAndApplication"}, - {2, nullptr, "GetGlobalUploadEnabledForAccount"}, - {3, nullptr, "SetGlobalUploadEnabledForAccount"}, - {4, nullptr, "TouchAccount"}, - {5, nullptr, "GetGlobalDownloadEnabledForAccount"}, - {6, nullptr, "SetGlobalDownloadEnabledForAccount"}, - {10, nullptr, "GetForbiddenSaveDataIndication"}, - {11, nullptr, "GetStopperObject"}, - {12, D<&IDaemonController::GetState>, "GetState"}, + {0, D<&IDaemonController::GetApplicationAutoTransferSetting>, "GetApplicationAutoTransferSetting"}, + {1, D<&IDaemonController::SetApplicationAutoTransferSetting>, "SetApplicationAutoTransferSetting"}, + {2, D<&IDaemonController::GetGlobalAutoUploadSetting>, "GetGlobalAutoUploadSetting"}, + {3, D<&IDaemonController::SetGlobalAutoUploadSetting>, "SetGlobalAutoUploadSetting"}, + {4, D<&IDaemonController::RunTransferTaskAutonomyRegistration>, "RunTransferTaskAutonomyRegistration"}, + {5, D<&IDaemonController::GetGlobalAutoDownloadSetting>, "GetGlobalAutoDownloadSetting"}, // 11.0.0+ + {6, D<&IDaemonController::SetGlobalAutoDownloadSetting>, "SetGlobalAutoDownloadSetting"}, // 11.0.0+ + {10, nullptr, "CreateForbiddenSaveDataInidication"}, + {11, nullptr, "StopAutonomyTaskExecution"}, + {12, D<&IDaemonController::GetAutonomyTaskStatus>, "GetAutonomyTaskStatus"}, + {13, nullptr, "Unknown13_20_0_0_Plus"}, // 20.0.0+ }; // clang-format on @@ -31,19 +32,73 @@ IDaemonController::IDaemonController(Core::System& system_) IDaemonController::~IDaemonController() = default; -Result IDaemonController::GetAutoTransferEnabledForAccountAndApplication(Out out_is_enabled, - Common::UUID user_id, - u64 application_id) { - LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={} application_id={:016X}", - user_id.FormattedString(), application_id); - *out_is_enabled = false; +Result IDaemonController::GetApplicationAutoTransferSetting(Out out_is_enabled, + Common::UUID user_id, + u64 application_id) { + LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X}", user_id.FormattedString(), + application_id); + AppKey key{user_id, application_id}; + const auto it = app_auto_transfer_.find(key); + *out_is_enabled = (it != app_auto_transfer_.end()) ? it->second : false; R_SUCCEED(); } -Result IDaemonController::GetState(Out state, Common::UUID user_id, u64 application_id) { - LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={} application_id={:016X}", - user_id.FormattedString(), application_id); - *state = 0; +Result IDaemonController::SetApplicationAutoTransferSetting(bool is_enabled, Common::UUID user_id, + u64 application_id) { + LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X} is_enabled={}", + user_id.FormattedString(), application_id, is_enabled); + AppKey key{user_id, application_id}; + app_auto_transfer_[key] = is_enabled; + R_SUCCEED(); +} + +Result IDaemonController::GetGlobalAutoUploadSetting(Out out_is_enabled, + Common::UUID user_id) { + LOG_INFO(Service_OLSC, "called, user_id={}", user_id.FormattedString()); + const auto it = global_auto_upload_.find(user_id); + *out_is_enabled = (it != global_auto_upload_.end()) ? it->second : false; + R_SUCCEED(); +} + +Result IDaemonController::SetGlobalAutoUploadSetting(bool is_enabled, Common::UUID user_id) { + LOG_INFO(Service_OLSC, "called, user_id={} is_enabled={}", user_id.FormattedString(), is_enabled); + global_auto_upload_[user_id] = is_enabled; + R_SUCCEED(); +} + +Result IDaemonController::RunTransferTaskAutonomyRegistration(Common::UUID user_id, + u64 application_id) { + LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X}", user_id.FormattedString(), + application_id); + // Simulate starting an autonomy task: set status to 1 (running) then back to 0 (idle) + AppKey key{user_id, application_id}; + autonomy_task_status_[key] = 1; // running + // TODO: In a real implementation this would be asynchronous. We'll immediately set to idle. + autonomy_task_status_[key] = 0; // idle + R_SUCCEED(); +} + +Result IDaemonController::GetGlobalAutoDownloadSetting(Out out_is_enabled, + Common::UUID user_id) { + LOG_INFO(Service_OLSC, "called, user_id={}", user_id.FormattedString()); + const auto it = global_auto_download_.find(user_id); + *out_is_enabled = (it != global_auto_download_.end()) ? it->second : false; + R_SUCCEED(); +} + +Result IDaemonController::SetGlobalAutoDownloadSetting(bool is_enabled, Common::UUID user_id) { + LOG_INFO(Service_OLSC, "called, user_id={} is_enabled={}", user_id.FormattedString(), is_enabled); + global_auto_download_[user_id] = is_enabled; + R_SUCCEED(); +} + +Result IDaemonController::GetAutonomyTaskStatus(Out out_state, Common::UUID user_id, + u64 application_id) { + LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X}", user_id.FormattedString(), + application_id); + AppKey key{user_id, application_id}; + const auto it = autonomy_task_status_.find(key); + *out_state = (it != autonomy_task_status_.end()) ? it->second : 0; // default idle R_SUCCEED(); } diff --git a/src/core/hle/service/olsc/daemon_controller.h b/src/core/hle/service/olsc/daemon_controller.h index 18c7b6204a..7e5416562e 100644 --- a/src/core/hle/service/olsc/daemon_controller.h +++ b/src/core/hle/service/olsc/daemon_controller.h @@ -1,9 +1,12 @@ +#pragma once // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/uuid.h" #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" @@ -16,9 +19,43 @@ public: ~IDaemonController() override; private: - Result GetAutoTransferEnabledForAccountAndApplication(Out out_is_enabled, - Common::UUID user_id, u64 application_id); - Result GetState(Out state, Common::UUID user_id, u64 application_id); + Result GetApplicationAutoTransferSetting(Out out_is_enabled, Common::UUID user_id, + u64 application_id); + Result SetApplicationAutoTransferSetting(bool is_enabled, Common::UUID user_id, + u64 application_id); + + Result GetGlobalAutoUploadSetting(Out out_is_enabled, Common::UUID user_id); + Result SetGlobalAutoUploadSetting(bool is_enabled, Common::UUID user_id); + + Result RunTransferTaskAutonomyRegistration(Common::UUID user_id, u64 application_id); + + Result GetGlobalAutoDownloadSetting(Out out_is_enabled, Common::UUID user_id); + Result SetGlobalAutoDownloadSetting(bool is_enabled, Common::UUID user_id); + + Result GetAutonomyTaskStatus(Out out_state, Common::UUID user_id, u64 application_id); + + // Internal in-memory state to back the above APIs + struct AppKey { + Common::UUID user_id; + u64 application_id{}; + friend constexpr bool operator==(const AppKey& a, const AppKey& b) { + return a.user_id == b.user_id && a.application_id == b.application_id; + } + }; + struct AppKeyHash { + size_t operator()(const AppKey& k) const noexcept { + // Combine UUID hash and application_id + size_t h1 = std::hash{}(k.user_id); + size_t h2 = std::hash{}(k.application_id); + // Simple hash combine + return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2)); + } + }; + + std::unordered_map app_auto_transfer_{}; + std::unordered_map global_auto_upload_{}; + std::unordered_map global_auto_download_{}; + std::unordered_map autonomy_task_status_{}; }; } // namespace Service::OLSC diff --git a/src/core/hle/service/psc/ovln/ovln_types.h b/src/core/hle/service/psc/ovln/ovln_types.h index 343b05dcc0..f96b52fb49 100644 --- a/src/core/hle/service/psc/ovln/ovln_types.h +++ b/src/core/hle/service/psc/ovln/ovln_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,4 +21,13 @@ union MessageFlags { }; static_assert(sizeof(MessageFlags) == 0x8, "MessageFlags has incorrect size"); +struct SourceName { + char name[0x16]; + + const char* GetString() const { + return name; + } +}; +; + } // namespace Service::PSC diff --git a/src/core/hle/service/psc/ovln/receiver.cpp b/src/core/hle/service/psc/ovln/receiver.cpp index 85f62816d3..f7d3882a05 100644 --- a/src/core/hle/service/psc/ovln/receiver.cpp +++ b/src/core/hle/service/psc/ovln/receiver.cpp @@ -1,24 +1,118 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/psc/ovln/receiver.h" +#include "core/hle/service/cmif_serialization.h" namespace Service::PSC { -IReceiver::IReceiver(Core::System& system_) : ServiceFramework{system_, "IReceiver"} { +IReceiver::IReceiver(Core::System& system_) + : ServiceFramework{system_, "IReceiver"}, service_context{system_, "IReceiver"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "AddSource"}, - {1, nullptr, "RemoveSource"}, - {2, nullptr, "GetReceiveEventHandle"}, - {3, nullptr, "Receive"}, - {4, nullptr, "ReceiveWithTick"}, + {0, D<&IReceiver::AddSource>, "AddSource"}, + {1, D<&IReceiver::RemoveSource>, "RemoveSource"}, + {2, D<&IReceiver::GetReceiveEventHandle>, "GetReceiveEventHandle"}, + {3, D<&IReceiver::Receive>, "Receive"}, + {4, D<&IReceiver::ReceiveWithTick>, "ReceiveWithTick"}, }; // clang-format on RegisterHandlers(functions); + + receive_event = service_context.CreateEvent("IReceiver::ReceiveEvent"); } -IReceiver::~IReceiver() = default; +IReceiver::~IReceiver() { + service_context.CloseEvent(receive_event); +} + +Result IReceiver::AddSource(SourceName source_name) { + const std::string name = source_name.GetString(); + LOG_INFO(Service_PSC, "called: source_name={}", name); + + // Add source if it doesn't already exist + if (message_sources.find(name) == message_sources.end()) { + message_sources[name] = {}; + } + + R_SUCCEED(); +} + +Result IReceiver::RemoveSource(SourceName source_name) { + const std::string name = source_name.GetString(); + LOG_INFO(Service_PSC, "called: source_name={}", name); + + // Remove source if it exists + message_sources.erase(name); + + R_SUCCEED(); +} + +Result IReceiver::GetReceiveEventHandle(OutCopyHandle out_event) { + LOG_INFO(Service_PSC, "called"); + *out_event = &receive_event->GetReadableEvent(); + R_SUCCEED(); +} + +Result IReceiver::Receive(Out out_notification, Out out_flags) { + u64 tick; + return ReceiveWithTick(out_notification, out_flags, Out(&tick)); +} + +Result IReceiver::ReceiveWithTick(Out out_notification, + Out out_flags, Out out_tick) { + LOG_DEBUG(Service_PSC, "called"); + + // Find the message with the lowest ID across all sources + const std::string* target_source = nullptr; + size_t target_index = 0; + + for (const auto& [source_name, messages] : message_sources) { + if (!messages.empty()) { + if (target_source == nullptr) { + target_source = &source_name; + target_index = 0; + } + // Note: In the real implementation, we would track message IDs + // For now, just use FIFO order + } + } + + if (target_source != nullptr) { + auto& messages = message_sources[*target_source]; + *out_notification = messages[target_index].first; + *out_flags = messages[target_index].second; + *out_tick = 0; // TODO: Implement tick tracking + + // Remove the message + messages.erase(messages.begin() + target_index); + + // Clear event if no more messages + bool has_messages = false; + for (const auto& [_, msgs] : message_sources) { + if (!msgs.empty()) { + has_messages = true; + break; + } + } + if (!has_messages) { + receive_event->Clear(); + } + + R_SUCCEED(); + } + + // No messages available + *out_notification = {}; + *out_flags = {}; + *out_tick = 0; + + LOG_WARNING(Service_PSC, "No messages available"); + R_THROW(ResultUnknown); // TODO: Use proper OvlnResult::NoMessages when available +} } // namespace Service::PSC diff --git a/src/core/hle/service/psc/ovln/receiver.h b/src/core/hle/service/psc/ovln/receiver.h index c47a4ff7e9..2b96216a1a 100644 --- a/src/core/hle/service/psc/ovln/receiver.h +++ b/src/core/hle/service/psc/ovln/receiver.h @@ -1,9 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include +#include +#include +#include "core/hle/result.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" +#include "core/hle/service/psc/ovln/ovln_types.h" + +namespace Kernel { +class KReadableEvent; +} namespace Service::PSC { @@ -11,6 +25,22 @@ class IReceiver final : public ServiceFramework { public: explicit IReceiver(Core::System& system_); ~IReceiver() override; + + IReceiver(const IReceiver&) = delete; + IReceiver& operator=(const IReceiver&) = delete; + +private: + Result AddSource(SourceName source_name); + Result RemoveSource(SourceName source_name); + Result GetReceiveEventHandle(OutCopyHandle out_event); + Result Receive(Out out_notification, Out out_flags); + Result ReceiveWithTick(Out out_notification, Out out_flags, + Out out_tick); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* receive_event; + + std::map>> message_sources; }; } // namespace Service::PSC diff --git a/src/core/hle/service/psc/ovln/receiver_service.cpp b/src/core/hle/service/psc/ovln/receiver_service.cpp index bb988e9057..d869325181 100644 --- a/src/core/hle/service/psc/ovln/receiver_service.cpp +++ b/src/core/hle/service/psc/ovln/receiver_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -21,7 +24,7 @@ IReceiverService::~IReceiverService() = default; Result IReceiverService::OpenReceiver(Out> out_receiver) { LOG_DEBUG(Service_PSC, "called"); - *out_receiver = std::make_shared(system); + *out_receiver = std::shared_ptr(new IReceiver(system)); R_SUCCEED(); } diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp index caa30c2d5c..258e213e13 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.cpp +++ b/src/core/hle/service/set/setting_formats/system_settings.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h index a5b1552a50..75deb1a93f 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.h +++ b/src/core/hle/service/set/setting_formats/system_settings.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp index 9074f4ae0d..e7d3b4757b 100644 --- a/src/core/hle/service/vi/container.cpp +++ b/src/core/hle/service/vi/container.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -131,6 +134,38 @@ Result Container::SetLayerBlending(u64 layer_id, bool enabled) { R_SUCCEED(); } +Result Container::SetLayerZIndex(u64 layer_id, s32 z_index) { + std::scoped_lock lk{m_lock}; + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) { + LOG_DEBUG(Service_VI, "called, SetLayerZIndex layer_id={} z={} (cid={})", layer_id, + z_index, layer->GetConsumerBinderId()); + layer_ref->z_index = z_index; + } else { + LOG_DEBUG(Service_VI, "called, SetLayerZIndex failed to find layer for layer_id={} (cid={})", + layer_id, layer->GetConsumerBinderId()); + } + + R_SUCCEED(); +} + +Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) { + std::scoped_lock lk{m_lock}; + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) { + *out_z_index = layer_ref->z_index; + R_SUCCEED(); + } + + R_RETURN(VI::ResultNotFound); +} + void Container::LinkVsyncEvent(u64 display_id, Event* event) { std::scoped_lock lk{m_lock}; m_conductor->LinkVsyncEvent(display_id, event); diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h index 5eac4d77dd..544a47889a 100644 --- a/src/core/hle/service/vi/container.h +++ b/src/core/hle/service/vi/container.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -62,6 +65,8 @@ public: Result SetLayerVisibility(u64 layer_id, bool visible); Result SetLayerBlending(u64 layer_id, bool enabled); + Result SetLayerZIndex(u64 layer_id, s32 z_index); + Result GetLayerZIndex(u64 layer_id, s32* out_z_index); void LinkVsyncEvent(u64 display_id, Event* event); void UnlinkVsyncEvent(u64 display_id, Event* event); diff --git a/src/core/hle/service/vi/manager_display_service.cpp b/src/core/hle/service/vi/manager_display_service.cpp index 9f856282e6..e4d22ca11c 100644 --- a/src/core/hle/service/vi/manager_display_service.cpp +++ b/src/core/hle/service/vi/manager_display_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -116,6 +119,10 @@ Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) { R_RETURN(m_container->SetLayerBlending(layer_id, enabled)); } +Result IManagerDisplayService::SetLayerZIndex(s32 z_index, u64 layer_id) { + R_RETURN(m_container->SetLayerZIndex(layer_id, z_index)); +} + Result IManagerDisplayService::CreateManagedLayer(Out out_layer_id, u32 flags, u64 display_id, AppletResourceUserId aruid) { LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid); diff --git a/src/core/hle/service/vi/manager_display_service.h b/src/core/hle/service/vi/manager_display_service.h index b1bdf7f411..14a107cb43 100644 --- a/src/core/hle/service/vi/manager_display_service.h +++ b/src/core/hle/service/vi/manager_display_service.h @@ -1,6 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" @@ -22,6 +27,7 @@ public: void DestroySharedLayerSession(Kernel::KProcess* owner_process); Result SetLayerBlending(bool enabled, u64 layer_id); + Result SetLayerZIndex(s32 z_index, u64 layer_id); public: Result CreateManagedLayer(Out out_layer_id, u32 flags, u64 display_id, diff --git a/src/core/hle/service/vi/shared_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp index 12cba16fab..e1808f74ca 100644 --- a/src/core/hle/service/vi/shared_buffer_manager.cpp +++ b/src/core/hle/service/vi/shared_buffer_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -38,13 +41,13 @@ Result AllocateSharedBufferMemory(std::unique_ptr* out_page_ Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, Kernel::KMemoryManager::Direction::FromBack))); - // Fill the output data with red. + // Initialize to fully transparent black to avoid covering content before first present. for (auto& block : *pg) { u32* start = system.DeviceMemory().GetPointer(block.GetAddress()); u32* end = system.DeviceMemory().GetPointer(block.GetAddress() + block.GetSize()); - for (; start < end; start++) { - *start = 0xFF0000FF; + for (; start < end; ++start) { + *start = 0x00000000; // ARGB/RGBA with alpha=0 } } @@ -252,8 +255,9 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id), std::addressof(session.layer_id), display_id)); - // Configure blending. + // Configure blending and z-index R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending)); + R_ASSERT(m_container.SetLayerZIndex(session.layer_id, 100000)); // Get the producer and set preallocated buffers. std::shared_ptr producer; @@ -370,6 +374,11 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, android::Status::NoError, VI::ResultOperationFailed); + // Ensure the layer is visible when content is presented. + // Re-assert overlay priority in case clients reset it. + (void)m_container.SetLayerZIndex(layer_id, 100000); + (void)m_container.SetLayerVisibility(layer_id, true); + // We succeeded. R_SUCCEED(); } @@ -406,23 +415,51 @@ Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* // TODO: this could be optimized s64 e = -1280 * 768 * 4; for (auto& block : *m_buffer_page_group) { - u8* start = m_system.DeviceMemory().GetPointer(block.GetAddress()); - u8* end = m_system.DeviceMemory().GetPointer(block.GetAddress() + block.GetSize()); - - for (; start < end; start++) { - *start = 0; + u8* const block_start = m_system.DeviceMemory().GetPointer(block.GetAddress()); + u8* ptr = block_start; + u8* const block_end = m_system.DeviceMemory().GetPointer(block.GetAddress() + block.GetSize()); + for (; ptr < block_end; ++ptr) { if (e >= 0 && e < static_cast(capture_buffer.size())) { - *start = capture_buffer[e]; + *ptr = capture_buffer[static_cast(e)]; + } else { + *ptr = 0; } - e++; + ++e; } - m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) { - m_system.GPU().InvalidateRegion(addr, end - start); + m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(block_start, scratch, [&](DAddr addr) { + m_system.GPU().InvalidateRegion(addr, block_end - block_start); }); } + // After writing, present a frame on each active shared layer so it becomes visible. + for (auto& [aruid, session] : m_sessions) { + std::shared_ptr producer; + if (R_FAILED(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id))) { + continue; + } + s32 slot = -1; + android::Fence fence = android::Fence::NoFence(); + if (producer->DequeueBuffer(&slot, &fence, SharedBufferAsync != 0, SharedBufferWidth, + SharedBufferHeight, SharedBufferBlockLinearFormat, 0) != + android::Status::NoError) { + continue; + } + std::shared_ptr gb; + if (producer->RequestBuffer(slot, &gb) != android::Status::NoError) { + producer->CancelBuffer(slot, android::Fence::NoFence()); + continue; + } + android::QueueBufferInput qin{}; + android::QueueBufferOutput qout{}; + qin.crop = {0, 0, static_cast(SharedBufferWidth), static_cast(SharedBufferHeight)}; + qin.fence = android::Fence::NoFence(); + qin.transform = static_cast(0); + qin.swap_interval = 1; + (void)producer->QueueBuffer(slot, qin, &qout); + } + *out_was_written = true; *out_layer_index = 1; R_SUCCEED(); diff --git a/src/core/hle/service/vi/shared_buffer_manager.h b/src/core/hle/service/vi/shared_buffer_manager.h index 7c9bb71992..9ef6234867 100644 --- a/src/core/hle/service/vi/shared_buffer_manager.h +++ b/src/core/hle/service/vi/shared_buffer_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,6 @@ #include "common/math_util.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/nvnflinger/ui/fence.h" namespace Kernel { diff --git a/src/core/hle/service/vi/system_display_service.cpp b/src/core/hle/service/vi/system_display_service.cpp index c3c50b07b3..0956053dcf 100644 --- a/src/core/hle/service/vi/system_display_service.cpp +++ b/src/core/hle/service/vi/system_display_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -20,7 +23,7 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_, {1204, nullptr, "SetDisplayMagnification"}, {2201, nullptr, "SetLayerPosition"}, {2203, nullptr, "SetLayerSize"}, - {2204, nullptr, "GetLayerZ"}, + {2204, C<&ISystemDisplayService::GetLayerZ>, "GetLayerZ"}, {2205, C<&ISystemDisplayService::SetLayerZ>, "SetLayerZ"}, {2207, C<&ISystemDisplayService::SetLayerVisibility>, "SetLayerVisibility"}, {2209, nullptr, "SetLayerAlpha"}, @@ -68,16 +71,26 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_, ISystemDisplayService::~ISystemDisplayService() = default; -Result ISystemDisplayService::SetLayerZ(u32 z_value, u64 layer_id) { - LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}, z_value={}", layer_id, z_value); +Result ISystemDisplayService::GetLayerZ(Out out_z_value, u64 layer_id) { + LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id); + s32 z{}; + const auto res = m_container->GetLayerZIndex(layer_id, &z); + R_TRY(res); + *out_z_value = static_cast(z); R_SUCCEED(); } +Result ISystemDisplayService::SetLayerZ(u64 layer_id, u64 z_value) { + LOG_DEBUG(Service_VI, "called. layer_id={}, z_value={}", layer_id, z_value); + // Forward to container using internal API when available + R_RETURN(m_container->SetLayerZIndex(layer_id, static_cast(z_value))); +} + // This function currently does nothing but return a success error code in // the vi library itself, so do the same thing, but log out the passed in values. Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) { LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible); - R_SUCCEED(); + R_RETURN(m_container->SetLayerVisibility(layer_id, visible)); } Result ISystemDisplayService::ListDisplayModes( diff --git a/src/core/hle/service/vi/system_display_service.h b/src/core/hle/service/vi/system_display_service.h index 7228d826e2..d6cc8c75f1 100644 --- a/src/core/hle/service/vi/system_display_service.h +++ b/src/core/hle/service/vi/system_display_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,7 +21,8 @@ public: ~ISystemDisplayService() override; private: - Result SetLayerZ(u32 z_value, u64 layer_id); + Result GetLayerZ(Out out_z_value, u64 layer_id); + Result SetLayerZ(u64 layer_id, u64 z_value); Result SetLayerVisibility(bool visible, u64 layer_id); Result ListDisplayModes(Out out_count, u64 display_id, OutArray out_display_modes); diff --git a/src/hid_core/resources/npad/npad_data.cpp b/src/hid_core/resources/npad/npad_data.cpp index a4b46a1ecf..a490fe8a54 100644 --- a/src/hid_core/resources/npad/npad_data.cpp +++ b/src/hid_core/resources/npad/npad_data.cpp @@ -68,8 +68,7 @@ bool NPadData::IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const { } void NPadData::SetNpadSystemCommonPolicy(bool is_full_policy) { - supported_npad_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::JoyDual | - Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; + supported_npad_style_set = Core::HID::NpadStyleSet::All; handheld_activation_mode = NpadHandheldActivationMode::Dual; status.is_supported_styleset_set.Assign(true); diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 9fc254eed2..c601d25d80 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -47,6 +47,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, login_share_applet_mode, tr("Login share"), QString()); INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QString()); INSERT(Settings, my_page_applet_mode, tr("My page"), QString()); + INSERT(Settings, enable_overlay, tr("Enable Overlay Applet"), QString()); // Audio INSERT(Settings, sink_id, tr("Output Engine:"), QString()); @@ -732,4 +733,3 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) return translations; } } // namespace ConfigurationShared - diff --git a/src/qt_common/config/uisettings.h b/src/qt_common/config/uisettings.h index 5ca1a362f7..65d03cc990 100644 --- a/src/qt_common/config/uisettings.h +++ b/src/qt_common/config/uisettings.h @@ -98,7 +98,6 @@ struct Values { Setting single_window_mode{linkage, true, "singleWindowMode", Category::Ui}; Setting fullscreen{linkage, false, "fullscreen", Category::Ui}; - Setting display_titlebar{linkage, true, "displayTitleBars", Category::Ui}; Setting show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; Setting show_status_bar{linkage, true, "showStatusBar", Category::Ui}; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 161cc6c921..e9f0814ac7 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -131,7 +131,7 @@ - + @@ -298,12 +298,12 @@ QAction::MenuRole::PreferencesRole - + true - Display D&ock Widget Headers + Enable Overlay Display Applet diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 92741bd328..62b2f0561f 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -1513,8 +1513,7 @@ void MainWindow::RestoreUIState() { ui->action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue()); - ui->action_Display_Dock_Widget_Headers->setChecked( - UISettings::values.display_titlebar.GetValue()); + ui->action_Enable_Overlay_Applet->setChecked(Settings::values.enable_overlay.GetValue()); ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue()); game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked()); @@ -4408,10 +4407,11 @@ void MainWindow::UpdateUISettings() { UISettings::values.state = saveState(); UISettings::values.single_window_mode = ui->action_Single_Window_Mode->isChecked(); UISettings::values.fullscreen = ui->action_Fullscreen->isChecked(); - UISettings::values.display_titlebar = ui->action_Display_Dock_Widget_Headers->isChecked(); UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked(); UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked(); UISettings::values.first_start = false; + + Settings::values.enable_overlay = ui->action_Enable_Overlay_Applet->isChecked(); } void MainWindow::UpdateInputDrivers() {