From 65c0746f8957483f6df6a1998ae7e9d4fcb74aa7 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 12 Feb 2024 00:41:17 -0500 Subject: [PATCH] am: add applet lifecycle manager --- src/core/CMakeLists.txt | 6 +- src/core/hle/service/am/am_types.h | 6 - src/core/hle/service/am/applet.cpp | 67 +++- src/core/hle/service/am/applet.h | 25 +- .../hle/service/am/applet_data_broker.cpp | 18 +- src/core/hle/service/am/applet_data_broker.h | 14 - src/core/hle/service/am/applet_manager.cpp | 29 +- src/core/hle/service/am/applet_manager.h | 1 - .../hle/service/am/applet_message_queue.cpp | 73 ---- .../hle/service/am/applet_message_queue.h | 43 -- src/core/hle/service/am/frontend/applets.cpp | 6 +- src/core/hle/service/am/lifecycle_manager.cpp | 379 ++++++++++++++++++ src/core/hle/service/am/lifecycle_manager.h | 183 +++++++++ .../am/service/application_accessor.cpp | 4 +- .../am/service/common_state_getter.cpp | 9 +- .../am/service/library_applet_accessor.cpp | 10 +- .../am/service/library_applet_creator.cpp | 15 +- .../service/library_applet_self_accessor.cpp | 1 - .../service/am/service/self_controller.cpp | 16 +- .../service/am/service/window_controller.cpp | 16 +- 20 files changed, 691 insertions(+), 230 deletions(-) delete mode 100644 src/core/hle/service/am/applet_message_queue.cpp delete mode 100644 src/core/hle/service/am/applet_message_queue.h create mode 100644 src/core/hle/service/am/lifecycle_manager.cpp create mode 100644 src/core/hle/service/am/lifecycle_manager.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b6d607b694..8700fe7ae0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -401,12 +401,10 @@ add_library(core STATIC hle/service/am/am_types.h hle/service/am/applet.cpp hle/service/am/applet.h + hle/service/am/applet_manager.cpp hle/service/am/applet_data_broker.cpp hle/service/am/applet_data_broker.h - hle/service/am/applet_manager.cpp hle/service/am/applet_manager.h - hle/service/am/applet_message_queue.cpp - hle/service/am/applet_message_queue.h hle/service/am/display_layer_manager.cpp hle/service/am/display_layer_manager.h hle/service/am/frontend/applet_cabinet.cpp @@ -434,6 +432,8 @@ add_library(core STATIC hle/service/am/hid_registration.h hle/service/am/library_applet_storage.cpp hle/service/am/library_applet_storage.h + hle/service/am/lifecycle_manager.cpp + hle/service/am/lifecycle_manager.h hle/service/am/process_creation.cpp hle/service/am/process_creation.h hle/service/am/process_holder.cpp diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index a14defb403..98cc712e48 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -61,12 +61,6 @@ enum class ScreenshotPermission : u32 { Disable = 2, }; -struct FocusHandlingMode { - bool notify; - bool background; - bool suspend; -}; - enum class IdleTimeDetectionExtension : u32 { Disabled = 0, Extended = 1, diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp index 5b9056c124..7b17f4f8d7 100644 --- a/src/core/hle/service/am/applet.cpp +++ b/src/core/hle/service/am/applet.cpp @@ -1,22 +1,20 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/scope_exit.h" - #include "core/core.h" -#include "core/hle/service/am/am_results.h" #include "core/hle/service/am/applet.h" #include "core/hle/service/am/applet_manager.h" namespace Service::AM { -Applet::Applet(Core::System& system, std::unique_ptr process_) - : context(system, "Applet"), message_queue(system), process(std::move(process_)), - hid_registration(system, *process), gpu_error_detected_event(context), - friend_invitation_storage_channel_event(context), notification_storage_channel_event(context), - health_warning_disappeared_system_event(context), acquired_sleep_lock_event(context), - pop_from_general_channel_event(context), library_applet_launchable_event(context), - accumulated_suspended_tick_changed_event(context), sleep_lock_event(context) { +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), + gpu_error_detected_event(context), friend_invitation_storage_channel_event(context), + notification_storage_channel_event(context), health_warning_disappeared_system_event(context), + acquired_sleep_lock_event(context), pop_from_general_channel_event(context), + library_applet_launchable_event(context), accumulated_suspended_tick_changed_event(context), + sleep_lock_event(context), state_changed_event(context) { aruid = process->GetProcessId(); program_id = process->GetProgramId(); @@ -24,4 +22,53 @@ Applet::Applet(Core::System& system, std::unique_ptr process_) Applet::~Applet() = default; +void Applet::UpdateSuspensionStateLocked(bool force_message) { + // Remove any forced resumption. + lifecycle_manager.RemoveForceResumeIfPossible(); + + // Check if we're runnable. + const bool is_runnable = lifecycle_manager.IsRunnable(); + const bool was_running = running; + + if (is_runnable != was_running) { + if (is_runnable) { + process->Suspend(false); + } else { + process->Suspend(true); + lifecycle_manager.RequestResumeNotification(); + } + + running = is_runnable; + } + + if (lifecycle_manager.GetForcedSuspend()) { + // TODO: why is this allowed? + return; + } + + // Signal if the focus state was changed or the process state was changed. + if (lifecycle_manager.UpdateRequestedFocusState() || is_runnable != was_running || + force_message) { + lifecycle_manager.SignalSystemEventIfNeeded(); + } +} + +void Applet::SetInteractibleLocked(bool interactible) { + if (is_interactible == interactible) { + return; + } + + is_interactible = interactible; + + const bool new_state = + window_visible && is_interactible && !lifecycle_manager.GetExitRequested(); + display_layer_manager.SetWindowVisibility(new_state); + hid_registration.EnableAppletToGetInput(new_state); +} + +void Applet::OnProcessTerminatedLocked() { + is_completed = true; + state_changed_event.Signal(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index 4b90eb9f29..7ed6a0d6ad 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "common/math_util.h" @@ -14,15 +15,15 @@ #include "core/hle/service/service.h" #include "core/hle/service/am/am_types.h" -#include "core/hle/service/am/applet_message_queue.h" #include "core/hle/service/am/display_layer_manager.h" #include "core/hle/service/am/hid_registration.h" +#include "core/hle/service/am/lifecycle_manager.h" #include "core/hle/service/am/process_holder.h" namespace Service::AM { struct Applet { - explicit Applet(Core::System& system, std::unique_ptr process_); + explicit Applet(Core::System& system, std::unique_ptr process_, bool is_application); ~Applet(); // Lock @@ -31,8 +32,8 @@ struct Applet { // Event creation helper KernelHelpers::ServiceContext context; - // Applet message queue - AppletMessageQueue message_queue; + // Lifecycle manager + LifecycleManager lifecycle_manager; // Process std::unique_ptr process; @@ -81,7 +82,6 @@ struct Applet { bool application_crash_report_enabled{}; // Common state - FocusState focus_state{}; bool sleep_lock_enabled{}; bool vr_mode_enabled{}; bool lcd_backlight_off_enabled{}; @@ -95,15 +95,11 @@ struct Applet { // Caller applet std::weak_ptr caller_applet{}; std::shared_ptr caller_applet_broker{}; + bool is_completed{}; // Self state bool exit_locked{}; s32 fatal_section_count{}; - bool operation_mode_changed_notification_enabled{true}; - bool performance_mode_changed_notification_enabled{true}; - FocusHandlingMode focus_handling_mode{}; - bool restart_message_enabled{}; - bool out_of_focus_suspension_enabled{true}; Capture::AlbumImageOrientation album_image_orientation{}; bool handles_request_to_display{}; ScreenshotPermission screenshot_permission{}; @@ -112,6 +108,9 @@ struct Applet { u64 suspended_ticks{}; bool album_image_taken_notification_enabled{}; bool record_volume_muted{}; + bool running{}; + bool is_interactible{true}; + bool window_visible{true}; // Events Event gpu_error_detected_event; @@ -123,9 +122,15 @@ struct Applet { Event library_applet_launchable_event; Event accumulated_suspended_tick_changed_event; Event sleep_lock_event; + Event state_changed_event; // Frontend state std::shared_ptr frontend{}; + + // Process state management + void UpdateSuspensionStateLocked(bool force_message); + void SetInteractibleLocked(bool interactible); + void OnProcessTerminatedLocked(); }; } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp index 9057244a92..fff78c5afb 100644 --- a/src/core/hle/service/am/applet_data_broker.cpp +++ b/src/core/hle/service/am/applet_data_broker.cpp @@ -44,24 +44,8 @@ Kernel::KReadableEvent* AppletStorageChannel::GetEvent() { AppletDataBroker::AppletDataBroker(Core::System& system_) : system(system_), context(system_, "AppletDataBroker"), in_data(context), - interactive_in_data(context), out_data(context), interactive_out_data(context), - state_changed_event(context), is_completed(false) {} + interactive_in_data(context), out_data(context), interactive_out_data(context) {} AppletDataBroker::~AppletDataBroker() = default; -void AppletDataBroker::SignalCompletion() { - { - std::scoped_lock lk{lock}; - - if (is_completed) { - return; - } - - is_completed = true; - state_changed_event.Signal(); - } - - system.GetAppletManager().FocusStateChanged(); -} - } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h index 5a1d43c11a..2718f608ad 100644 --- a/src/core/hle/service/am/applet_data_broker.h +++ b/src/core/hle/service/am/applet_data_broker.h @@ -53,16 +53,6 @@ public: return interactive_out_data; } - Event& GetStateChangedEvent() { - return state_changed_event; - } - - bool IsCompleted() const { - return is_completed; - } - - void SignalCompletion(); - private: Core::System& system; KernelHelpers::ServiceContext context; @@ -71,10 +61,6 @@ private: AppletStorageChannel interactive_in_data; AppletStorageChannel out_data; AppletStorageChannel interactive_out_data; - Event state_changed_event; - - std::mutex lock; - bool is_completed; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index 2e109181d1..1f86dc0285 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -255,6 +255,11 @@ void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) { // Terminate process. applet->process->Terminate(); + { + std::scoped_lock lk{applet->lock}; + applet->OnProcessTerminatedLocked(); + } + // If there were no applets left, stop emulation. if (should_stop) { m_system.Exit(); @@ -265,7 +270,8 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters( AppletResourceUserId aruid, const FrontendAppletParameters& params) { // TODO: this should be run inside AM so that the events will have a parent process // TODO: have am create the guest process - auto applet = std::make_shared(m_system, std::make_unique(m_system)); + auto applet = std::make_shared(m_system, std::make_unique(m_system), + params.applet_id == AppletId::Application); applet->aruid = aruid; applet->program_id = params.program_id; @@ -322,9 +328,7 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters( } // Applet was started by frontend, so it is foreground. - applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground); - applet->message_queue.PushMessage(AppletMessage::FocusStateChanged); - applet->focus_state = FocusState::InFocus; + applet->lifecycle_manager.SetFocusState(FocusState::InFocus); this->InsertApplet(std::move(applet)); } @@ -349,7 +353,8 @@ void AppletManager::RequestExit() { std::scoped_lock lk{m_lock}; for (const auto& [aruid, applet] : m_applets) { - applet->message_queue.RequestExit(); + std::scoped_lock lk2{applet->lock}; + applet->lifecycle_manager.RequestExit(); } } @@ -357,7 +362,8 @@ void AppletManager::RequestResume() { std::scoped_lock lk{m_lock}; for (const auto& [aruid, applet] : m_applets) { - applet->message_queue.RequestResume(); + std::scoped_lock lk2{applet->lock}; + applet->lifecycle_manager.RequestResumeNotification(); } } @@ -365,15 +371,8 @@ void AppletManager::OperationModeChanged() { std::scoped_lock lk{m_lock}; for (const auto& [aruid, applet] : m_applets) { - applet->message_queue.OperationModeChanged(); - } -} - -void AppletManager::FocusStateChanged() { - std::scoped_lock lk{m_lock}; - - for (const auto& [aruid, applet] : m_applets) { - applet->message_queue.FocusStateChanged(); + std::scoped_lock lk2{applet->lock}; + applet->lifecycle_manager.OnOperationAndPerformanceModeChanged(); } } diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h index 4875de309e..6c2357be14 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -45,7 +45,6 @@ public: void RequestExit(); void RequestResume(); void OperationModeChanged(); - void FocusStateChanged(); private: Core::System& m_system; diff --git a/src/core/hle/service/am/applet_message_queue.cpp b/src/core/hle/service/am/applet_message_queue.cpp deleted file mode 100644 index 83c3c5a551..0000000000 --- a/src/core/hle/service/am/applet_message_queue.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet_message_queue.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -AppletMessageQueue::AppletMessageQueue(Core::System& system) - : service_context{system, "AppletMessageQueue"} { - on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); - on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged"); -} - -AppletMessageQueue::~AppletMessageQueue() { - service_context.CloseEvent(on_new_message); - service_context.CloseEvent(on_operation_mode_changed); -} - -Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() { - return on_new_message->GetReadableEvent(); -} - -Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() { - return on_operation_mode_changed->GetReadableEvent(); -} - -void AppletMessageQueue::PushMessage(AppletMessage msg) { - { - std::scoped_lock lk{lock}; - messages.push(msg); - } - on_new_message->Signal(); -} - -AppletMessage AppletMessageQueue::PopMessage() { - std::scoped_lock lk{lock}; - if (messages.empty()) { - on_new_message->Clear(); - return AppletMessage::None; - } - auto msg = messages.front(); - messages.pop(); - if (messages.empty()) { - on_new_message->Clear(); - } - return msg; -} - -std::size_t AppletMessageQueue::GetMessageCount() const { - std::scoped_lock lk{lock}; - return messages.size(); -} - -void AppletMessageQueue::RequestExit() { - PushMessage(AppletMessage::Exit); -} - -void AppletMessageQueue::RequestResume() { - PushMessage(AppletMessage::Resume); -} - -void AppletMessageQueue::FocusStateChanged() { - PushMessage(AppletMessage::FocusStateChanged); -} - -void AppletMessageQueue::OperationModeChanged() { - PushMessage(AppletMessage::OperationModeChanged); - PushMessage(AppletMessage::PerformanceModeChanged); - on_operation_mode_changed->Signal(); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_message_queue.h b/src/core/hle/service/am/applet_message_queue.h deleted file mode 100644 index 429b77d370..0000000000 --- a/src/core/hle/service/am/applet_message_queue.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "core/hle/service/am/am_types.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Kernel { -class KReadableEvent; -} // namespace Kernel - -namespace Service::AM { - -class AppletMessageQueue { -public: - explicit AppletMessageQueue(Core::System& system); - ~AppletMessageQueue(); - - Kernel::KReadableEvent& GetMessageReceiveEvent(); - Kernel::KReadableEvent& GetOperationModeChangedEvent(); - void PushMessage(AppletMessage msg); - AppletMessage PopMessage(); - std::size_t GetMessageCount() const; - void RequestExit(); - void RequestResume(); - void FocusStateChanged(); - void OperationModeChanged(); - -private: - KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* on_new_message; - Kernel::KEvent* on_operation_mode_changed; - - mutable std::mutex lock; - std::queue messages; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp index e662c6cd63..cdd4318576 100644 --- a/src/core/hle/service/am/frontend/applets.cpp +++ b/src/core/hle/service/am/frontend/applets.cpp @@ -69,7 +69,11 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr storage) { } void FrontendApplet::Exit() { - applet.lock()->caller_applet_broker->SignalCompletion(); + auto applet_ = applet.lock(); + + std::scoped_lock lk{applet_->lock}; + applet_->is_completed = true; + applet_->state_changed_event.Signal(); } FrontendAppletSet::FrontendAppletSet() = default; diff --git a/src/core/hle/service/am/lifecycle_manager.cpp b/src/core/hle/service/am/lifecycle_manager.cpp new file mode 100644 index 0000000000..0dac27ed08 --- /dev/null +++ b/src/core/hle/service/am/lifecycle_manager.cpp @@ -0,0 +1,379 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/hle/service/am/lifecycle_manager.h" + +namespace Service::AM { + +LifecycleManager::LifecycleManager(Core::System& system, KernelHelpers::ServiceContext& context, + bool is_application) + : m_system_event(context), m_operation_mode_changed_system_event(context), + m_is_application(is_application) {} + +LifecycleManager::~LifecycleManager() = default; + +Event& LifecycleManager::GetSystemEvent() { + return m_system_event; +} + +Event& LifecycleManager::GetOperationModeChangedSystemEvent() { + return m_operation_mode_changed_system_event; +} + +void LifecycleManager::PushUnorderedMessage(AppletMessage message) { + m_unordered_messages.push_back(message); + this->SignalSystemEventIfNeeded(); +} + +AppletMessage LifecycleManager::PopMessageInOrderOfPriority() { + if (m_has_resume) { + m_has_resume = false; + return AppletMessage::Resume; + } + + if (m_has_acknowledged_exit != m_has_requested_exit) { + m_has_acknowledged_exit = m_has_requested_exit; + return AppletMessage::Exit; + } + + if (m_focus_state_changed_notification_enabled) { + if (!m_is_application) { + if (m_requested_focus_state != m_acknowledged_focus_state) { + m_acknowledged_focus_state = m_requested_focus_state; + switch (m_requested_focus_state) { + case FocusState::InFocus: + return AppletMessage::ChangeIntoForeground; + case FocusState::NotInFocus: + return AppletMessage::ChangeIntoBackground; + default: + ASSERT(false); + } + } + } else if (m_has_focus_state_changed) { + m_has_focus_state_changed = false; + return AppletMessage::FocusStateChanged; + } + } + + if (m_has_requested_request_to_prepare_sleep != m_has_acknowledged_request_to_prepare_sleep) { + m_has_acknowledged_request_to_prepare_sleep = true; + return AppletMessage::RequestToPrepareSleep; + } + + if (m_requested_request_to_display_state != m_acknowledged_request_to_display_state) { + m_acknowledged_request_to_display_state = m_requested_request_to_display_state; + return AppletMessage::RequestToDisplay; + } + + if (m_has_operation_mode_changed) { + m_has_operation_mode_changed = false; + return AppletMessage::OperationModeChanged; + } + + if (m_has_performance_mode_changed) { + m_has_performance_mode_changed = false; + return AppletMessage::PerformanceModeChanged; + } + + if (m_has_sd_card_removed) { + m_has_sd_card_removed = false; + return AppletMessage::SdCardRemoved; + } + + if (m_has_sleep_required_by_high_temperature) { + m_has_sleep_required_by_high_temperature = false; + return AppletMessage::SleepRequiredByHighTemperature; + } + + if (m_has_sleep_required_by_low_battery) { + m_has_sleep_required_by_low_battery = false; + return AppletMessage::SleepRequiredByLowBattery; + } + + if (m_has_auto_power_down) { + m_has_auto_power_down = false; + return AppletMessage::AutoPowerDown; + } + + if (m_has_album_screen_shot_taken) { + m_has_album_screen_shot_taken = false; + return AppletMessage::AlbumScreenShotTaken; + } + + if (m_has_album_recording_saved) { + m_has_album_recording_saved = false; + return AppletMessage::AlbumRecordingSaved; + } + + if (!m_unordered_messages.empty()) { + const auto message = m_unordered_messages.front(); + m_unordered_messages.pop_front(); + return message; + } + + return AppletMessage::None; +} + +bool LifecycleManager::ShouldSignalSystemEvent() { + if (m_focus_state_changed_notification_enabled) { + if (!m_is_application) { + if (m_requested_focus_state != m_acknowledged_focus_state) { + return true; + } + } else if (m_has_focus_state_changed) { + return true; + } + } + + return !m_unordered_messages.empty() || m_has_resume || + (m_has_requested_exit != m_has_acknowledged_exit) || + (m_has_requested_request_to_prepare_sleep != + m_has_acknowledged_request_to_prepare_sleep) || + m_has_operation_mode_changed || m_has_performance_mode_changed || + m_has_sd_card_removed || m_has_sleep_required_by_high_temperature || + m_has_sleep_required_by_low_battery || m_has_auto_power_down || + (m_requested_request_to_display_state != m_acknowledged_request_to_display_state) || + m_has_album_screen_shot_taken || m_has_album_recording_saved; +} + +void LifecycleManager::OnOperationAndPerformanceModeChanged() { + if (m_operation_mode_changed_notification_enabled) { + m_has_operation_mode_changed = true; + } + if (m_performance_mode_changed_notification_enabled) { + m_has_performance_mode_changed = true; + } + m_operation_mode_changed_system_event.Signal(); + this->SignalSystemEventIfNeeded(); +} + +void LifecycleManager::SignalSystemEventIfNeeded() { + // Check our cached value for the system event. + const bool applet_message_available = m_applet_message_available; + + // If it's not current, we need to do an update, either clearing or signaling. + if (applet_message_available != this->ShouldSignalSystemEvent()) { + if (!applet_message_available) { + m_system_event.Signal(); + m_applet_message_available = true; + } else { + m_system_event.Clear(); + m_applet_message_available = false; + } + } +} + +bool LifecycleManager::PopMessage(AppletMessage* out_message) { + const auto message = this->PopMessageInOrderOfPriority(); + this->SignalSystemEventIfNeeded(); + + *out_message = message; + return message != AppletMessage::None; +} + +void LifecycleManager::SetFocusHandlingMode(bool suspend) { + switch (m_focus_handling_mode) { + case FocusHandlingMode::AlwaysSuspend: + case FocusHandlingMode::SuspendHomeSleep: + if (!suspend) { + // Disallow suspension. + m_focus_handling_mode = FocusHandlingMode::NoSuspend; + } + break; + case FocusHandlingMode::NoSuspend: + if (suspend) { + // Allow suspension temporally. + m_focus_handling_mode = FocusHandlingMode::SuspendHomeSleep; + } + break; + } +} + +void LifecycleManager::SetOutOfFocusSuspendingEnabled(bool enabled) { + switch (m_focus_handling_mode) { + case FocusHandlingMode::AlwaysSuspend: + if (!enabled) { + // Allow suspension temporally. + m_focus_handling_mode = FocusHandlingMode::SuspendHomeSleep; + } + break; + case FocusHandlingMode::SuspendHomeSleep: + case FocusHandlingMode::NoSuspend: + if (enabled) { + // Allow suspension. + m_focus_handling_mode = FocusHandlingMode::AlwaysSuspend; + } + break; + } +} + +void LifecycleManager::RemoveForceResumeIfPossible() { + // If resume is not forced, we have nothing to do. + if (m_suspend_mode != SuspendMode::ForceResume) { + return; + } + + // Check activity state. + // If we are already resumed, we can remove the forced state. + switch (m_activity_state) { + case ActivityState::ForegroundVisible: + case ActivityState::ForegroundObscured: + m_suspend_mode = SuspendMode::NoOverride; + return; + + default: + break; + } + + // Check focus handling mode. + switch (m_focus_handling_mode) { + case FocusHandlingMode::AlwaysSuspend: + case FocusHandlingMode::SuspendHomeSleep: + // If the applet allows suspension, we can remove the forced state. + m_suspend_mode = SuspendMode::NoOverride; + break; + + case FocusHandlingMode::NoSuspend: + // If the applet is not an application, we can remove the forced state. + // Only applications can be forced to resume. + if (!m_is_application) { + m_suspend_mode = SuspendMode::NoOverride; + } + } +} + +bool LifecycleManager::IsRunnable() const { + // If suspend is forced, return that. + if (m_forced_suspend) { + return false; + } + + // Check suspend mode override. + switch (m_suspend_mode) { + case SuspendMode::NoOverride: + // Continue processing. + break; + + case SuspendMode::ForceResume: + // The applet is runnable during forced resumption when its exit is requested. + return m_has_requested_exit; + + case SuspendMode::ForceSuspend: + // The applet is never runnable during forced suspension. + return false; + } + + // Always run if exit is requested. + if (m_has_requested_exit) { + return true; + } + + if (m_activity_state == ActivityState::ForegroundVisible) { + // The applet is runnable now. + return true; + } + + if (m_activity_state == ActivityState::ForegroundObscured) { + switch (m_focus_handling_mode) { + case FocusHandlingMode::AlwaysSuspend: + // The applet is not runnable while running the applet. + return false; + + case FocusHandlingMode::SuspendHomeSleep: + // The applet is runnable while running the applet. + return true; + + case FocusHandlingMode::NoSuspend: + // The applet is always runnable. + return true; + } + } + + // The activity is a suspended one. + // The applet should be suspended unless it has disabled suspension. + return m_focus_handling_mode == FocusHandlingMode::NoSuspend; +} + +FocusState LifecycleManager::GetFocusStateWhileForegroundObscured() const { + switch (m_focus_handling_mode) { + case FocusHandlingMode::AlwaysSuspend: + // The applet never learns it has lost focus. + return FocusState::InFocus; + + case FocusHandlingMode::SuspendHomeSleep: + // The applet learns it has lost focus when launching a child applet. + return FocusState::NotInFocus; + + case FocusHandlingMode::NoSuspend: + // The applet always learns it has lost focus. + return FocusState::NotInFocus; + + default: + UNREACHABLE(); + } +} + +FocusState LifecycleManager::GetFocusStateWhileBackground(bool is_obscured) const { + switch (m_focus_handling_mode) { + case FocusHandlingMode::AlwaysSuspend: + // The applet never learns it has lost focus. + return FocusState::InFocus; + + case FocusHandlingMode::SuspendHomeSleep: + // The applet learns it has lost focus when launching a child applet. + return is_obscured ? FocusState::NotInFocus : FocusState::InFocus; + + case FocusHandlingMode::NoSuspend: + // The applet always learns it has lost focus. + return m_is_application ? FocusState::Background : FocusState::NotInFocus; + + default: + UNREACHABLE(); + } +} + +bool LifecycleManager::UpdateRequestedFocusState() { + FocusState new_state{}; + + if (m_suspend_mode == SuspendMode::NoOverride) { + // With no forced suspend or resume, we take the focus state designated + // by the combination of the activity flag and the focus handling mode. + switch (m_activity_state) { + case ActivityState::ForegroundVisible: + new_state = FocusState::InFocus; + break; + + case ActivityState::ForegroundObscured: + new_state = this->GetFocusStateWhileForegroundObscured(); + break; + + case ActivityState::BackgroundVisible: + new_state = this->GetFocusStateWhileBackground(false); + break; + + case ActivityState::BackgroundObscured: + new_state = this->GetFocusStateWhileBackground(true); + break; + + default: + UNREACHABLE(); + } + } else { + // With forced suspend or resume, the applet is guaranteed to be background. + new_state = this->GetFocusStateWhileBackground(false); + } + + if (new_state != m_requested_focus_state) { + // Mark the focus state as ready for update. + m_requested_focus_state = new_state; + + // We changed the focus state. + return true; + } + + // We didn't change the focus state. + return false; +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/lifecycle_manager.h b/src/core/hle/service/am/lifecycle_manager.h new file mode 100644 index 0000000000..7c70434a18 --- /dev/null +++ b/src/core/hle/service/am/lifecycle_manager.h @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/os/event.h" + +namespace Core { +class System; +} + +namespace Service::AM { + +enum class ActivityState : u32 { + ForegroundVisible = 0, + ForegroundObscured = 1, + BackgroundVisible = 2, + BackgroundObscured = 3, +}; + +enum class FocusHandlingMode : u32 { + AlwaysSuspend = 0, + SuspendHomeSleep = 1, + NoSuspend = 2, +}; + +enum class SuspendMode : u32 { + NoOverride = 0, + ForceResume = 1, + ForceSuspend = 2, +}; + +class LifecycleManager { +public: + explicit LifecycleManager(Core::System& system, KernelHelpers::ServiceContext& context, + bool is_application); + ~LifecycleManager(); + +public: + Event& GetSystemEvent(); + Event& GetOperationModeChangedSystemEvent(); + +public: + bool IsApplication() { + return m_is_application; + } + + bool GetForcedSuspend() { + return m_forced_suspend; + } + + bool GetExitRequested() { + return m_has_requested_exit; + } + + ActivityState GetActivityState() { + return m_activity_state; + } + + FocusState GetAndClearFocusState() { + m_acknowledged_focus_state = m_requested_focus_state; + return m_acknowledged_focus_state; + } + + void SetFocusState(FocusState state) { + if (m_requested_focus_state != state) { + m_has_focus_state_changed = true; + } + m_requested_focus_state = state; + this->SignalSystemEventIfNeeded(); + } + + void RequestExit() { + m_has_requested_exit = true; + this->SignalSystemEventIfNeeded(); + } + + void RequestResumeNotification() { + // NOTE: this appears to be a bug in am. + // If an applet makes a concurrent request to receive resume notifications + // while it is being suspended, the first resume notification will be lost. + // This is not the case with other notification types. + if (m_resume_notification_enabled) { + m_has_resume = true; + } + } + + void OnOperationAndPerformanceModeChanged(); + +public: + void SetFocusStateChangedNotificationEnabled(bool enabled) { + m_focus_state_changed_notification_enabled = enabled; + this->SignalSystemEventIfNeeded(); + } + + void SetOperationModeChangedNotificationEnabled(bool enabled) { + m_operation_mode_changed_notification_enabled = enabled; + this->SignalSystemEventIfNeeded(); + } + + void SetPerformanceModeChangedNotificationEnabled(bool enabled) { + m_performance_mode_changed_notification_enabled = enabled; + this->SignalSystemEventIfNeeded(); + } + + void SetResumeNotificationEnabled(bool enabled) { + m_resume_notification_enabled = enabled; + } + + void SetActivityState(ActivityState state) { + m_activity_state = state; + } + + void SetSuspendMode(SuspendMode mode) { + m_suspend_mode = mode; + } + + void SetForcedSuspend(bool enabled) { + m_forced_suspend = enabled; + } + +public: + void SetFocusHandlingMode(bool suspend); + void SetOutOfFocusSuspendingEnabled(bool enabled); + void RemoveForceResumeIfPossible(); + bool IsRunnable() const; + bool UpdateRequestedFocusState(); + void SignalSystemEventIfNeeded(); + +public: + void PushUnorderedMessage(AppletMessage message); + bool PopMessage(AppletMessage* out_message); + +private: + FocusState GetFocusStateWhileForegroundObscured() const; + FocusState GetFocusStateWhileBackground(bool is_obscured) const; + +private: + AppletMessage PopMessageInOrderOfPriority(); + bool ShouldSignalSystemEvent(); + +private: + Event m_system_event; + Event m_operation_mode_changed_system_event; + + std::list m_unordered_messages{}; + + bool m_is_application{}; + bool m_focus_state_changed_notification_enabled{true}; + bool m_operation_mode_changed_notification_enabled{true}; + bool m_performance_mode_changed_notification_enabled{true}; + bool m_resume_notification_enabled{}; + + bool m_requested_request_to_display_state{}; + bool m_acknowledged_request_to_display_state{}; + bool m_has_resume{}; + bool m_has_focus_state_changed{true}; + bool m_has_album_recording_saved{}; + bool m_has_album_screen_shot_taken{}; + bool m_has_auto_power_down{}; + bool m_has_sleep_required_by_low_battery{}; + bool m_has_sleep_required_by_high_temperature{}; + bool m_has_sd_card_removed{}; + bool m_has_performance_mode_changed{}; + bool m_has_operation_mode_changed{}; + bool m_has_requested_request_to_prepare_sleep{}; + bool m_has_acknowledged_request_to_prepare_sleep{}; + bool m_has_requested_exit{}; + bool m_has_acknowledged_exit{}; + bool m_applet_message_available{}; + + bool m_forced_suspend{}; + FocusHandlingMode m_focus_handling_mode{FocusHandlingMode::SuspendHomeSleep}; + ActivityState m_activity_state{ActivityState::ForegroundVisible}; + SuspendMode m_suspend_mode{SuspendMode::NoOverride}; + FocusState m_requested_focus_state{}; + FocusState m_acknowledged_focus_state{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_accessor.cpp b/src/core/hle/service/am/service/application_accessor.cpp index 6e7d110e84..a8de4a7405 100644 --- a/src/core/hle/service/am/service/application_accessor.cpp +++ b/src/core/hle/service/am/service/application_accessor.cpp @@ -59,7 +59,7 @@ Result IApplicationAccessor::Start() { Result IApplicationAccessor::RequestExit() { LOG_INFO(Service_AM, "called"); - m_applet->message_queue.RequestExit(); + m_applet->lifecycle_manager.RequestExit(); R_SUCCEED(); } @@ -77,7 +77,7 @@ Result IApplicationAccessor::GetResult() { Result IApplicationAccessor::GetAppletStateChangedEvent( OutCopyHandle out_event) { LOG_INFO(Service_AM, "called"); - *out_event = m_applet->caller_applet_broker->GetStateChangedEvent().GetHandle(); + *out_event = m_applet->state_changed_event.GetHandle(); R_SUCCEED(); } 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 a32855ffa3..f523bcd9e6 100644 --- a/src/core/hle/service/am/service/common_state_getter.cpp +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -80,15 +80,14 @@ ICommonStateGetter::~ICommonStateGetter() = default; Result ICommonStateGetter::GetEventHandle(OutCopyHandle out_event) { LOG_DEBUG(Service_AM, "called"); - *out_event = &m_applet->message_queue.GetMessageReceiveEvent(); + *out_event = m_applet->lifecycle_manager.GetSystemEvent().GetHandle(); R_SUCCEED(); } Result ICommonStateGetter::ReceiveMessage(Out out_applet_message) { LOG_DEBUG(Service_AM, "called"); - *out_applet_message = m_applet->message_queue.PopMessage(); - if (*out_applet_message == AppletMessage::None) { + if (!m_applet->lifecycle_manager.PopMessage(out_applet_message)) { LOG_ERROR(Service_AM, "Tried to pop message but none was available!"); R_THROW(AM::ResultNoMessages); } @@ -100,7 +99,7 @@ Result ICommonStateGetter::GetCurrentFocusState(Out out_focus_state) LOG_DEBUG(Service_AM, "called"); std::scoped_lock lk{m_applet->lock}; - *out_focus_state = m_applet->focus_state; + *out_focus_state = m_applet->lifecycle_manager.GetAndClearFocusState(); R_SUCCEED(); } @@ -137,7 +136,7 @@ Result ICommonStateGetter::GetWriterLockAccessorEx( Result ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent( OutCopyHandle out_event) { LOG_DEBUG(Service_AM, "called"); - *out_event = &m_applet->message_queue.GetOperationModeChangedEvent(); + *out_event = m_applet->lifecycle_manager.GetOperationModeChangedSystemEvent().GetHandle(); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp index 0c2426d4ba..40e3840597 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -47,13 +47,14 @@ ILibraryAppletAccessor::~ILibraryAppletAccessor() = default; Result ILibraryAppletAccessor::GetAppletStateChangedEvent( OutCopyHandle out_event) { LOG_DEBUG(Service_AM, "called"); - *out_event = m_broker->GetStateChangedEvent().GetHandle(); + *out_event = m_applet->state_changed_event.GetHandle(); R_SUCCEED(); } Result ILibraryAppletAccessor::IsCompleted(Out out_is_completed) { LOG_DEBUG(Service_AM, "called"); - *out_is_completed = m_broker->IsCompleted(); + std::scoped_lock lk{m_applet->lock}; + *out_is_completed = m_applet->is_completed; R_SUCCEED(); } @@ -77,7 +78,10 @@ Result ILibraryAppletAccessor::Start() { Result ILibraryAppletAccessor::RequestExit() { LOG_DEBUG(Service_AM, "called"); - m_applet->message_queue.RequestExit(); + { + std::scoped_lock lk{m_applet->lock}; + m_applet->lifecycle_manager.RequestExit(); + } FrontendRequestExit(); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp index 53737f0b98..13fab28d60 100644 --- a/src/core/hle/service/am/service/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -117,7 +117,7 @@ std::shared_ptr CreateGuestApplet(Core::System& system, return {}; } - const auto applet = std::make_shared(system, std::move(process)); + const auto applet = std::make_shared(system, std::move(process), false); applet->program_id = program_id; applet->applet_id = applet_id; applet->type = AppletType::LibraryApplet; @@ -129,15 +129,12 @@ std::shared_ptr CreateGuestApplet(Core::System& system, case LibraryAppletMode::NoUi: case LibraryAppletMode::PartialForeground: case LibraryAppletMode::PartialForegroundIndirectDisplay: - applet->hid_registration.EnableAppletToGetInput(true); - applet->focus_state = FocusState::InFocus; - applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground); + applet->lifecycle_manager.SetFocusState(FocusState::InFocus); + applet->SetInteractibleLocked(true); break; case LibraryAppletMode::AllForegroundInitiallyHidden: - applet->hid_registration.EnableAppletToGetInput(false); - applet->focus_state = FocusState::NotInFocus; - applet->display_layer_manager.SetWindowVisibility(false); - applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground); + applet->lifecycle_manager.SetFocusState(FocusState::NotInFocus); + applet->SetInteractibleLocked(false); break; } @@ -157,7 +154,7 @@ std::shared_ptr CreateFrontendApplet(Core::System& syste const auto program_id = static_cast(AppletIdToProgramId(applet_id)); auto process = std::make_unique(system); - auto applet = std::make_shared(system, std::move(process)); + auto applet = std::make_shared(system, std::move(process), false); applet->program_id = program_id; applet->applet_id = applet_id; applet->type = AppletType::LibraryApplet; diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index 330eb26f05..bc6baa27d5 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -177,7 +177,6 @@ Result ILibraryAppletSelfAccessor::GetMainAppletStorageId(Outaruid); - m_broker->SignalCompletion(); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index 06314407c5..27ebc05986 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -155,7 +155,7 @@ Result ISelfController::SetOperationModeChangedNotification(bool enabled) { LOG_INFO(Service_AM, "called, enabled={}", enabled); std::scoped_lock lk{m_applet->lock}; - m_applet->operation_mode_changed_notification_enabled = enabled; + m_applet->lifecycle_manager.SetOperationModeChangedNotificationEnabled(enabled); R_SUCCEED(); } @@ -164,17 +164,18 @@ Result ISelfController::SetPerformanceModeChangedNotification(bool enabled) { LOG_INFO(Service_AM, "called, enabled={}", enabled); std::scoped_lock lk{m_applet->lock}; - m_applet->performance_mode_changed_notification_enabled = enabled; + m_applet->lifecycle_manager.SetPerformanceModeChangedNotificationEnabled(enabled); R_SUCCEED(); } Result ISelfController::SetFocusHandlingMode(bool notify, bool background, bool suspend) { - LOG_WARNING(Service_AM, "(STUBBED) called, notify={} background={} suspend={}", notify, - background, suspend); + LOG_INFO(Service_AM, "called, notify={} background={} suspend={}", notify, background, suspend); std::scoped_lock lk{m_applet->lock}; - m_applet->focus_handling_mode = {notify, background, suspend}; + m_applet->lifecycle_manager.SetFocusStateChangedNotificationEnabled(notify); + m_applet->lifecycle_manager.SetFocusHandlingMode(suspend); + m_applet->UpdateSuspensionStateLocked(true); R_SUCCEED(); } @@ -183,7 +184,7 @@ Result ISelfController::SetRestartMessageEnabled(bool enabled) { LOG_INFO(Service_AM, "called, enabled={}", enabled); std::scoped_lock lk{m_applet->lock}; - m_applet->restart_message_enabled = enabled; + m_applet->lifecycle_manager.SetResumeNotificationEnabled(enabled); R_SUCCEED(); } @@ -202,7 +203,8 @@ Result ISelfController::SetOutOfFocusSuspendingEnabled(bool enabled) { LOG_INFO(Service_AM, "called, enabled={}", enabled); std::scoped_lock lk{m_applet->lock}; - m_applet->out_of_focus_suspension_enabled = enabled; + m_applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(enabled); + m_applet->UpdateSuspensionStateLocked(false); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp index 99a4f50a2e..09753b22f5 100644 --- a/src/core/hle/service/am/service/window_controller.cpp +++ b/src/core/hle/service/am/service/window_controller.cpp @@ -63,17 +63,13 @@ Result IWindowController::RejectToChangeIntoBackground() { } Result IWindowController::SetAppletWindowVisibility(bool visible) { - m_applet->display_layer_manager.SetWindowVisibility(visible); - m_applet->hid_registration.EnableAppletToGetInput(visible); + LOG_WARNING(Service_AM, "(STUBBED) called"); - if (visible) { - m_applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground); - m_applet->focus_state = FocusState::InFocus; - } else { - m_applet->focus_state = FocusState::NotInFocus; - } - - m_applet->message_queue.PushMessage(AppletMessage::FocusStateChanged); + std::scoped_lock lk{m_applet->lock}; + m_applet->lifecycle_manager.SetFocusState(visible ? FocusState::InFocus + : FocusState::NotInFocus); + m_applet->SetInteractibleLocked(visible); + m_applet->UpdateSuspensionStateLocked(true); R_SUCCEED(); }