diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 62dab070e3..04b79e2c2c 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/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index eb9ad0ac5e..7c8811be08 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -15,6 +15,7 @@ class FrontendApplet; enum class AppletType { Application, LibraryApplet, + OverlayApplet, SystemApplet, }; diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp index aa355b06d5..af3fc82f94 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), diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index ad84f39dc7..5082b03ccf 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -124,6 +124,7 @@ struct Applet { bool window_visible{true}; // 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..e8bd23a7ac 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -15,6 +15,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 +263,19 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { m_cv.wait(lk, [&] { return m_pending_process != nullptr; }); + // Launch overlay applet before tracking the application, if available. + 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; + // Use PartialForeground so blending is enabled and overlay can be composed on top + overlay_applet->library_applet_mode = LibraryAppletMode::PartialForeground; + m_window_system->TrackApplet(overlay_applet, true); + overlay_applet->process->Run(); + LOG_INFO(Service_AM, "Overlay applet launched before application"); + } + 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/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp index 85ff6fb889..baebe01b97 100644 --- a/src/core/hle/service/am/display_layer_manager.cpp +++ b/src/core/hle/service/am/display_layer_manager.cpp @@ -33,6 +33,11 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro m_buffer_sharing_enabled = false; m_blending_enabled = mode == LibraryAppletMode::PartialForeground || mode == LibraryAppletMode::PartialForegroundIndirectDisplay; + + // For non-application applets, proactively enable shared buffer session so the layer exists + if (m_applet_id != AppletId::Application) { + (void)this->IsSystemBufferSharingEnabled(); + } } void DisplayLayerManager::Finalize() { @@ -99,13 +104,29 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() { // Create the shared layer. u64 display_id; R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"})); - R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id, - &m_system_shared_layer_id, display_id, - m_blending_enabled)); + R_TRY(m_manager_display_service->CreateSharedLayerSession( + m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, + m_blending_enabled)); + + LOG_DEBUG(Service_VI, + "DLM: SharedBuffer enabled buffer_id={} layer_id={} blending={} display={}", + m_system_shared_buffer_id, m_system_shared_layer_id, m_blending_enabled, display_id); // We succeeded, so set up remaining state. m_buffer_sharing_enabled = true; + + // Ensure the overlay layer is visible and above the application layer when applicable m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); + if (m_applet_id != AppletId::Application) { + static constexpr s32 kOverlayZ = 100000; + m_manager_display_service->SetLayerZIndex(kOverlayZ, m_system_shared_layer_id); + m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id); + LOG_INFO(Service_VI, + "DLM: Overlay session ready buffer_id={} layer_id={} z={} visible={} blending={}", + m_system_shared_buffer_id, m_system_shared_layer_id, kOverlayZ, m_visible, + m_blending_enabled); + } + R_SUCCEED(); } 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..87f21decf8 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 @@ -7,6 +7,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 +22,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 +68,22 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy( } } +Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy( + Out> out_overlay_applet_proxy, ClientProcessId pid, + InCopyHandle process_handle, + InLargeData attribute) { + LOG_DEBUG(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..80a8e3eac0 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 @@ -5,6 +5,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 +32,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..b30bae7310 100644 --- a/src/core/hle/service/am/service/applet_common_functions.cpp +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -18,7 +18,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 +58,14 @@ Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled( R_SUCCEED(); } +Result IAppletCommonFunctions::SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height) { + LOG_WARNING(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..aa8ec539bb 100644 --- a/src/core/hle/service/am/service/applet_common_functions.h +++ b/src/core/hle/service/am/service/applet_common_functions.h @@ -18,6 +18,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..6a57c0d9c9 100644 --- a/src/core/hle/service/am/service/application_creator.cpp +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -31,6 +31,10 @@ Result CreateGuestApplication(SharedPointer* out_applicati std::vector control; std::unique_ptr loader; Loader::ResultStatus result; + + // Launch overlay first if available. + // TryLaunchOverlayApplet(system, window_system); + auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); R_UNLESS(process != nullptr, ResultUnknown); 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 c179102f76..0aa22f0e21 100644 --- a/src/core/hle/service/am/service/common_state_getter.cpp +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -6,6 +6,7 @@ #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" @@ -34,7 +35,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr, "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"}, @@ -245,7 +246,28 @@ 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; + default: + // Other buttons ignored for now + break; + } + R_SUCCEED(); } @@ -287,4 +309,15 @@ Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnab R_SUCCEED(); } +Result ICommonStateGetter::PushToGeneralChannel(SharedPointer storage) { + LOG_DEBUG(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + // Push to a general-purpose channel and notify listeners via event + m_applet->user_channel_launch_parameter.push_back(storage->GetData()); + m_applet->pop_from_general_channel_event.Signal(); + + 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 0cafbfe176..dfe64dbf28 100644 --- a/src/core/hle/service/am/service/common_state_getter.h +++ b/src/core/hle/service/am/service/common_state_getter.h @@ -18,6 +18,7 @@ namespace Service::AM { struct Applet; class ILockAccessor; +class IStorage; // forward declaration for storage handle type class ICommonStateGetter final : public ServiceFramework { public: @@ -55,6 +56,7 @@ private: OutArray out_applet_ids); Result GetSettingsPlatformRegion(Out out_settings_platform_region); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); + Result PushToGeneralChannel(SharedPointer storage); // cmd 20 void SetCpuBoostMode(HLERequestContext& ctx); 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..248d829052 --- /dev/null +++ b/src/core/hle/service/am/service/overlay_applet_proxy.cpp @@ -0,0 +1,122 @@ +// 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..4206383303 --- /dev/null +++ b/src/core/hle/service/am/service/overlay_functions.cpp @@ -0,0 +1,82 @@ +// 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" + +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"}, + {90, nullptr, "SetRequiresGpuResourceUse"}, + {101, nullptr, "BeginToObserveHidInputForDevelop"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IOverlayFunctions::~IOverlayFunctions() = default; + +Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() { + LOG_DEBUG(Service_AM, "called"); + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_short_pressed_blocked = false; + R_SUCCEED(); +} + +Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() { + LOG_DEBUG(Service_AM, "called"); + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_short_pressed_blocked = true; + 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(); +} + +} // 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..8f3975cb5d --- /dev/null +++ b/src/core/hle/service/am/service/overlay_functions.h @@ -0,0 +1,33 @@ +// 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: + // 0 + Result BeginToWatchShortHomeButtonMessage(); + // 1 + Result EndToWatchShortHomeButtonMessage(); + // 2 + Result GetApplicationIdForLogo(Out out_application_id); + // 4 + Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled); + // 20 + Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); + +private: + const std::shared_ptr m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp index c435288a26..ecf69f72d7 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.cpp +++ b/src/core/hle/service/am/service/system_applet_proxy.cpp @@ -44,52 +44,52 @@ ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr> out_audio_controller) { +Result ISystemAppletProxy::GetCommonStateGetter( + Out> out_common_state_getter) { LOG_DEBUG(Service_AM, "called"); - *out_audio_controller = std::make_shared(system); + *out_common_state_getter = std::make_shared(system, m_applet); R_SUCCEED(); } -Result ISystemAppletProxy::GetDisplayController( - Out> out_display_controller) { +Result ISystemAppletProxy::GetSelfController( + Out> out_self_controller) { LOG_DEBUG(Service_AM, "called"); - *out_display_controller = std::make_shared(system, m_applet); + *out_self_controller = std::make_shared(system, m_applet, m_process); R_SUCCEED(); } -Result ISystemAppletProxy::GetProcessWindingController( - Out> out_process_winding_controller) { +Result ISystemAppletProxy::GetWindowController( + Out> out_window_controller) { LOG_DEBUG(Service_AM, "called"); - *out_process_winding_controller = std::make_shared(system, m_applet); + *out_window_controller = std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } -Result ISystemAppletProxy::GetDebugFunctions( - Out> out_debug_functions) { +Result ISystemAppletProxy::GetAudioController( + Out> out_audio_controller) { LOG_DEBUG(Service_AM, "called"); - *out_debug_functions = std::make_shared(system); + *out_audio_controller = std::make_shared(system); R_SUCCEED(); } -Result ISystemAppletProxy::GetWindowController( - Out> out_window_controller) { +Result ISystemAppletProxy::GetDisplayController( + Out> out_display_controller) { LOG_DEBUG(Service_AM, "called"); - *out_window_controller = std::make_shared(system, m_applet, m_window_system); + *out_display_controller = std::make_shared(system, m_applet); R_SUCCEED(); } -Result ISystemAppletProxy::GetSelfController( - Out> out_self_controller) { +Result ISystemAppletProxy::GetProcessWindingController( + Out> out_process_winding_controller) { LOG_DEBUG(Service_AM, "called"); - *out_self_controller = std::make_shared(system, m_applet, m_process); + *out_process_winding_controller = std::make_shared(system, m_applet); R_SUCCEED(); } -Result ISystemAppletProxy::GetCommonStateGetter( - Out> out_common_state_getter) { +Result ISystemAppletProxy::GetDebugFunctions( + Out> out_debug_functions) { LOG_DEBUG(Service_AM, "called"); - *out_common_state_getter = std::make_shared(system, m_applet); + *out_debug_functions = std::make_shared(system); R_SUCCEED(); } 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..bcd1f431e8 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.h +++ b/src/core/hle/service/am/service/system_applet_proxy.h @@ -30,14 +30,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..1efffc2e40 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -35,6 +35,7 @@ void WindowSystem::Update() { // 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_overlay_display, true); // overlay is always updated } void WindowSystem::TrackApplet(std::shared_ptr applet, bool is_application) { @@ -43,6 +44,9 @@ 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) { + // Track overlay display applet + m_overlay_display = applet.get(); } else if (is_application) { ASSERT(m_application == nullptr); m_application = applet.get(); @@ -139,6 +143,19 @@ void WindowSystem::OnExitRequested() { void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) { std::scoped_lock lk{m_lock}; + // Prefer sending to overlay display if present. + if (m_overlay_display) { + std::scoped_lock lk3{m_overlay_display->lock}; + if (type == ButtonPressDuration::ShortPressing) { + m_overlay_display->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectShortPressingHomeButton); + } else if (type == ButtonPressDuration::LongPressing) { + m_overlay_display->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectLongPressingHomeButton); + } + return; + } + // If we don't have a home menu, nothing to do. if (!m_home_menu) { return; @@ -151,6 +168,9 @@ void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) { if (type == ButtonPressDuration::ShortPressing) { m_home_menu->lifecycle_manager.PushUnorderedMessage( AppletMessage::DetectShortPressingHomeButton); + } else if (type == ButtonPressDuration::LongPressing) { + m_home_menu->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectLongPressingHomeButton); } } @@ -208,6 +228,11 @@ void WindowSystem::PruneTerminatedAppletsLocked() { } } + // If this was the overlay display, clear the pointer. + if (applet.get() == m_overlay_display) { + m_overlay_display = nullptr; + } + // Finalize applet. applet->OnProcessTerminatedLocked(); diff --git a/src/core/hle/service/am/window_system.h b/src/core/hle/service/am/window_system.h index 69e7a27ba5..eeb2eed2ad 100644 --- a/src/core/hle/service/am/window_system.h +++ b/src/core/hle/service/am/window_system.h @@ -75,6 +75,7 @@ private: // Foreground roots. Applet* m_home_menu{}; Applet* m_application{}; + Applet* m_overlay_display{}; // overlay display applet (DevOverlay) // Applet map by aruid. std::map> m_applets{}; diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 52228b830a..23d5f6bea1 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -53,6 +53,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 +198,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..bb6e207ed8 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -52,6 +52,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..2d9c3058e5 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -150,16 +150,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 +179,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/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h index 40aa597873..26d80f58dc 100644 --- a/src/core/hle/service/nvnflinger/display.h +++ b/src/core/hle/service/nvnflinger/display.h @@ -12,7 +12,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 +21,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..b960821253 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -95,7 +95,7 @@ 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 + // Proper Z-index from the layer metadata if (layer->visible) { composition_stack.emplace_back(HwcLayer{ .buffer_handle = igbp_buffer.BufferId(), @@ -104,7 +104,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 +128,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..1112707335 100644 --- a/src/core/hle/service/nvnflinger/surface_flinger.h +++ b/src/core/hle/service/nvnflinger/surface_flinger.h @@ -45,9 +45,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); // expose for Container + private: Display* FindDisplay(u64 display_id); - std::shared_ptr FindLayer(s32 consumer_binder_id); public: // TODO: these don't belong here @@ -66,4 +67,4 @@ private: HardwareComposer m_composer; }; -} // namespace Service::Nvnflinger +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/psc/ovln/ovln_types.h b/src/core/hle/service/psc/ovln/ovln_types.h index 343b05dcc0..9612397655 100644 --- a/src/core/hle/service/psc/ovln/ovln_types.h +++ b/src/core/hle/service/psc/ovln/ovln_types.h @@ -18,4 +18,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..6339f89cac 100644 --- a/src/core/hle/service/psc/ovln/receiver.cpp +++ b/src/core/hle/service/psc/ovln/receiver.cpp @@ -2,23 +2,114 @@ // 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() { + 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(); } -IReceiver::~IReceiver() = default; +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..ce9ed537c0 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,23 @@ 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; + + // Message sources map (source name -> message queue) + 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..772e445e5d 100644 --- a/src/core/hle/service/psc/ovln/receiver_service.cpp +++ b/src/core/hle/service/psc/ovln/receiver_service.cpp @@ -21,7 +21,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/vi/container.cpp b/src/core/hle/service/vi/container.cpp index 9074f4ae0d..18b8bf410f 100644 --- a/src/core/hle/service/vi/container.cpp +++ b/src/core/hle/service/vi/container.cpp @@ -131,6 +131,39 @@ 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); + + // Forward to nvnflinger layer via surface flinger (store on the layer struct) + if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) { + LOG_INFO(Service_VI, "Container: SetLayerZIndex layer_id={} z={} (cid={})", layer_id, + z_index, layer->GetConsumerBinderId()); + layer_ref->z_index = z_index; + } else { + LOG_INFO(Service_VI, "Container: 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..739a195c05 100644 --- a/src/core/hle/service/vi/container.h +++ b/src/core/hle/service/vi/container.h @@ -62,6 +62,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..bdd524e03f 100644 --- a/src/core/hle/service/vi/manager_display_service.cpp +++ b/src/core/hle/service/vi/manager_display_service.cpp @@ -116,6 +116,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..2d1dc8efd7 100644 --- a/src/core/hle/service/vi/manager_display_service.h +++ b/src/core/hle/service/vi/manager_display_service.h @@ -1,6 +1,8 @@ // 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 +24,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..d554d30bc4 100644 --- a/src/core/hle/service/vi/shared_buffer_manager.cpp +++ b/src/core/hle/service/vi/shared_buffer_manager.cpp @@ -38,13 +38,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 +252,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; @@ -261,6 +262,8 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle); MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle); + // Do not queue a frame here. The layer will be made visible on first Present. + // Assign outputs. *out_buffer_id = m_buffer_id; *out_layer_handle = session.layer_id; @@ -370,6 +373,9 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, android::Status::NoError, VI::ResultOperationFailed); + // Ensure the layer is visible when content is presented. + (void)m_container.SetLayerVisibility(layer_id, true); + // We succeeded. R_SUCCEED(); } @@ -399,6 +405,7 @@ Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadabl R_SUCCEED(); } +// Keep the present-on-write path to make overlay visible when data arrives. Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { std::vector capture_buffer(m_system.GPU().GetAppletCaptureBuffer()); Common::ScratchBuffer scratch; @@ -406,23 +413,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/system_display_service.cpp b/src/core/hle/service/vi/system_display_service.cpp index c3c50b07b3..90943cefa3 100644 --- a/src/core/hle/service/vi/system_display_service.cpp +++ b/src/core/hle/service/vi/system_display_service.cpp @@ -20,7 +20,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 +68,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..07d20ee408 100644 --- a/src/core/hle/service/vi/system_display_service.h +++ b/src/core/hle/service/vi/system_display_service.h @@ -18,7 +18,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);