aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle/service/am/lifecycle_manager.cpp
diff options
context:
space:
mode:
authoryuzubot <[email protected]>2024-03-04 13:02:54 +0000
committeryuzubot <[email protected]>2024-03-04 13:02:54 +0000
commit537296095ab24eddcb196b5ef98004f91de9c8c2 (patch)
treee75e9e2441dc3f8657cc42f2daaae08737949c2b /src/core/hle/service/am/lifecycle_manager.cpp
parent2ddac7b02b660bbc7bdfe4fef240699df6d52e64 (diff)
downloadyuzu-mainline-master.tar.gz
yuzu-mainline-master.zip
"Merge Tagged PR 13018"HEADmaster
Diffstat (limited to 'src/core/hle/service/am/lifecycle_manager.cpp')
-rw-r--r--src/core/hle/service/am/lifecycle_manager.cpp379
1 files changed, 379 insertions, 0 deletions
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 000000000..0dac27ed0
--- /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