diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index de158eea7..c7f4df802 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -401,14 +401,16 @@ 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/button_poller.cpp + hle/service/am/button_poller.h hle/service/am/display_layer_manager.cpp hle/service/am/display_layer_manager.h + hle/service/am/event_observer.cpp + hle/service/am/event_observer.h hle/service/am/frontend/applet_cabinet.cpp hle/service/am/frontend/applet_cabinet.h hle/service/am/frontend/applet_controller.cpp @@ -434,8 +436,12 @@ 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/process.cpp - hle/service/am/process.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 + hle/service/am/process_holder.h hle/service/am/service/all_system_applet_proxies_service.cpp hle/service/am/service/all_system_applet_proxies_service.h hle/service/am/service/applet_common_functions.cpp @@ -486,6 +492,8 @@ add_library(core STATIC hle/service/am/service/system_applet_proxy.h hle/service/am/service/window_controller.cpp hle/service/am/service/window_controller.h + hle/service/am/window_system.cpp + hle/service/am/window_system.h hle/service/aoc/addon_content_manager.cpp hle/service/aoc/addon_content_manager.h hle/service/aoc/purchase_event_manager.cpp @@ -918,6 +926,8 @@ add_library(core STATIC hle/service/os/multi_wait_utils.h hle/service/os/mutex.cpp hle/service/os/mutex.h + hle/service/os/process.cpp + hle/service/os/process.h hle/service/pcie/pcie.cpp hle/service/pcie/pcie.h hle/service/pctl/parental_control_service_factory.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index dc515bc82..e1c8b41ee 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -20,7 +19,6 @@ #include "core/cpu_manager.h" #include "core/debugger/debugger.h" #include "core/device_memory.h" -#include "core/file_sys/bis_factory.h" #include "core/file_sys/fs_filesystem.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" @@ -38,6 +36,7 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/process_creation.h" #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" @@ -72,30 +71,6 @@ MICROPROFILE_DEFINE(ARM_CPU3, "ARM", "CPU 3", MP_RGB(255, 64, 64)); namespace Core { -namespace { - -FileSys::StorageId GetStorageIdForFrontendSlot( - std::optional slot) { - if (!slot.has_value()) { - return FileSys::StorageId::None; - } - - switch (*slot) { - case FileSys::ContentProviderUnionSlot::UserNAND: - return FileSys::StorageId::NandUser; - case FileSys::ContentProviderUnionSlot::SysNAND: - return FileSys::StorageId::NandSystem; - case FileSys::ContentProviderUnionSlot::SDMC: - return FileSys::StorageId::SdCard; - case FileSys::ContentProviderUnionSlot::FrontendManual: - return FileSys::StorageId::Host; - default: - return FileSys::StorageId::None; - } -} - -} // Anonymous namespace - FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, const std::string& path) { // To account for split 00+01+etc files. @@ -297,9 +272,6 @@ struct System::Impl { } SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { - /// Reset all glue registrations - arp_manager.ResetAll(); - telemetry_session = std::make_unique(); host1x_core = std::make_unique(system); @@ -335,33 +307,17 @@ struct System::Impl { SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) { - app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), - params.program_id, params.program_index); - - if (!app_loader) { - LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); - return SystemResultStatus::ErrorGetLoader; - } - - if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) { - LOG_ERROR(Core, "Failed to find title id for ROM!"); - } - - std::string name = "Unknown program"; - if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { - LOG_ERROR(Core, "Failed to read title for ROM!"); - } - - LOG_INFO(Core, "Loading {} ({})", name, params.program_id); - InitializeKernel(system); - // Create the application process. - auto main_process = Kernel::KProcess::Create(system.Kernel()); - Kernel::KProcess::Register(system.Kernel(), main_process); - kernel.AppendNewProcess(main_process); - kernel.MakeApplicationProcess(main_process); - const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); + const auto file = GetGameFileFromPath(virtual_filesystem, filepath); + + // Create the application process + Loader::ResultStatus load_result{}; + std::vector control; + auto process = + Service::AM::CreateApplicationProcess(control, app_loader, load_result, system, file, + params.program_id, params.program_index); + if (load_result != Loader::ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result); ShutdownMainProcess(); @@ -370,6 +326,25 @@ struct System::Impl { static_cast(SystemResultStatus::ErrorLoader) + static_cast(load_result)); } + if (!app_loader) { + LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); + return SystemResultStatus::ErrorGetLoader; + } + + if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to find program id for ROM!"); + } + + std::string name = "Unknown program"; + if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to read title for ROM!"); + } + + LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id); + + // Make the process created be the application + kernel.MakeApplicationProcess(process->GetHandle()); + // Set up the rest of the system. SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; if (init_result != SystemResultStatus::Success) { @@ -379,7 +354,6 @@ struct System::Impl { return init_result; } - AddGlueRegistrationForProcess(*app_loader, *main_process); telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); // Initialize cheat engine @@ -387,14 +361,9 @@ struct System::Impl { cheat_engine->Initialize(); } - // Register with applet manager. - applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(), - params); - - // All threads are started, begin main process execution, now that we're in the clear. - main_process->Run(load_parameters->main_thread_priority, - load_parameters->main_thread_stack_size); - main_process->Close(); + // Register with applet manager + // All threads are started, begin main process execution, now that we're in the clear + applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params); if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_current_game) { @@ -461,7 +430,6 @@ struct System::Impl { kernel.SuspendEmulation(true); kernel.CloseServices(); kernel.ShutdownCores(); - applet_manager.Reset(); services.reset(); service_manager.reset(); fs_controller.Reset(); @@ -484,6 +452,9 @@ struct System::Impl { room_member->SendGameInfo(game_info); } + // Reset all glue registrations + arp_manager.ResetAll(); + LOG_DEBUG(Core, "Shutdown OK"); } @@ -501,31 +472,6 @@ struct System::Impl { return app_loader->ReadTitle(out); } - void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::KProcess& process) { - std::vector nacp_data; - FileSys::NACP nacp; - if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) { - nacp_data = nacp.GetRawBytes(); - } else { - nacp_data.resize(sizeof(FileSys::RawNACP)); - } - - Service::Glue::ApplicationLaunchProperty launch{}; - launch.title_id = process.GetProgramId(); - - FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider}; - launch.version = pm.GetGameVersion().value_or(0); - - // TODO(DarkLordZach): When FSController/Game Card Support is added, if - // current_process_game_card use correct StorageId - launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry( - launch.title_id, FileSys::ContentRecordType::Program)); - launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry( - FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program)); - - arp_manager.Register(launch.title_id, launch, std::move(nacp_data)); - } - void SetStatus(SystemResultStatus new_status, const char* details = nullptr) { status = new_status; if (details) { diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index cb9a11a63..80566b7e7 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1170,6 +1170,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: // Determine if we are an application. if (pool == KMemoryManager::Pool::Application) { flag |= Svc::CreateProcessFlag::IsApplication; + m_is_application = true; } // If we are 64-bit, create as such. diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 8c4e14f08..2ef393439 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,19 +2,26 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/am.h" +#include "core/hle/service/am/button_poller.h" +#include "core/hle/service/am/event_observer.h" #include "core/hle/service/am/service/all_system_applet_proxies_service.h" #include "core/hle/service/am/service/application_proxy_service.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/server_manager.h" namespace Service::AM { void LoopProcess(Core::System& system) { + WindowSystem window_system(system); + ButtonPoller button_poller(system, window_system); + EventObserver event_observer(system, window_system); + auto server_manager = std::make_unique(system); - server_manager->RegisterNamedService("appletAE", - std::make_shared(system)); - server_manager->RegisterNamedService("appletOE", - std::make_shared(system)); + server_manager->RegisterNamedService( + "appletAE", std::make_shared(system, window_system)); + server_manager->RegisterNamedService( + "appletOE", std::make_shared(system, window_system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/am/am_results.h b/src/core/hle/service/am/am_results.h index a2afc9eec..44846aa2e 100644 --- a/src/core/hle/service/am/am_results.h +++ b/src/core/hle/service/am/am_results.h @@ -9,6 +9,7 @@ namespace Service::AM { constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2}; constexpr Result ResultNoMessages{ErrorModule::AM, 3}; +constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22}; constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511}; constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512}; diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index a14defb40..eb9ad0ac5 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, @@ -239,7 +233,6 @@ struct ApplicationPlayStatistics { static_assert(sizeof(ApplicationPlayStatistics) == 0x18, "ApplicationPlayStatistics has incorrect size."); -using AppletResourceUserId = u64; using ProgramId = u64; struct Applet; diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp index 5b9056c12..6847f250c 100644 --- a/src/core/hle/service/am/applet.cpp +++ b/src/core/hle/service/am/applet.cpp @@ -1,27 +1,71 @@ // 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(); + aruid.pid = process->GetProcessId(); program_id = process->GetProgramId(); } Applet::~Applet() = default; +void Applet::UpdateSuspensionStateLocked(bool force_message) { + // Remove any forced resumption. + lifecycle_manager.RemoveForceResumeIfPossible(); + + // Check if we're runnable. + const bool curr_activity_runnable = lifecycle_manager.IsRunnable(); + const bool prev_activity_runnable = is_activity_runnable; + const bool was_changed = curr_activity_runnable != prev_activity_runnable; + + if (was_changed) { + if (curr_activity_runnable) { + process->Suspend(false); + } else { + process->Suspend(true); + lifecycle_manager.RequestResumeNotification(); + } + + is_activity_runnable = curr_activity_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() || was_changed || force_message) { + lifecycle_manager.SignalSystemEventIfNeeded(); + } +} + +void Applet::SetInteractibleLocked(bool interactible) { + if (is_interactible == interactible) { + return; + } + + is_interactible = interactible; + + hid_registration.EnableAppletToGetInput(interactible && !lifecycle_manager.GetExitRequested()); +} + +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 ad602153e..571904fab 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -3,25 +3,28 @@ #pragma once +#include #include #include "common/math_util.h" #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/os/event.h" +#include "core/hle/service/os/process.h" #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/process.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 @@ -30,11 +33,13 @@ struct Applet { // Event creation helper KernelHelpers::ServiceContext context; - // Applet message queue - AppletMessageQueue message_queue; + // Lifecycle manager + LifecycleManager lifecycle_manager; // Process std::unique_ptr process; + std::optional process_holder; + bool is_process_running{}; // Creation state AppletId applet_id{}; @@ -75,11 +80,9 @@ struct Applet { bool game_play_recording_supported{}; GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled}; bool jit_service_launched{}; - bool is_running{}; bool application_crash_report_enabled{}; // Common state - FocusState focus_state{}; bool sleep_lock_enabled{}; bool vr_mode_enabled{}; bool lcd_backlight_off_enabled{}; @@ -93,15 +96,12 @@ struct Applet { // Caller applet std::weak_ptr caller_applet{}; std::shared_ptr caller_applet_broker{}; + std::list> child_applets{}; + 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{}; @@ -110,6 +110,9 @@ struct Applet { u64 suspended_ticks{}; bool album_image_taken_notification_enabled{}; bool record_volume_muted{}; + bool is_activity_runnable{}; + bool is_interactible{true}; + bool window_visible{true}; // Events Event gpu_error_detected_event; @@ -121,9 +124,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 9057244a9..fff78c5af 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 5a1d43c11..2718f608a 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 2e109181d..c6b7ec8bb 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -13,6 +13,7 @@ #include "core/hle/service/am/frontend/applet_mii_edit_types.h" #include "core/hle/service/am/frontend/applet_software_keyboard_types.h" #include "core/hle/service/am/service/storage.h" +#include "core/hle/service/am/window_system.h" #include "hid_core/hid_types.h" namespace Service::AM { @@ -225,49 +226,46 @@ void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& chan } // namespace AppletManager::AppletManager(Core::System& system) : m_system(system) {} -AppletManager::~AppletManager() { - this->Reset(); -} - -void AppletManager::InsertApplet(std::shared_ptr applet) { - std::scoped_lock lk{m_lock}; - - m_applets.emplace(applet->aruid, std::move(applet)); -} - -void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) { - std::shared_ptr applet; - bool should_stop = false; - { - std::scoped_lock lk{m_lock}; - - const auto it = m_applets.find(aruid); - if (it == m_applets.end()) { - return; - } - - applet = it->second; - m_applets.erase(it); - - should_stop = m_applets.empty(); - } - - // Terminate process. - applet->process->Terminate(); - - // If there were no applets left, stop emulation. - if (should_stop) { - m_system.Exit(); - } -} +AppletManager::~AppletManager() = default; 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)); + std::unique_ptr process, const FrontendAppletParameters& params) { + { + std::scoped_lock lk{m_lock}; + m_pending_process = std::move(process); + m_pending_parameters = params; + } + m_cv.notify_all(); +} + +void AppletManager::RequestExit() { + std::scoped_lock lk{m_lock}; + if (m_window_system) { + m_window_system->OnExitRequested(); + } +} + +void AppletManager::OperationModeChanged() { + std::scoped_lock lk{m_lock}; + if (m_window_system) { + m_window_system->OnOperationModeChanged(); + } +} + +void AppletManager::SetWindowSystem(WindowSystem* window_system) { + std::unique_lock lk{m_lock}; + + m_window_system = window_system; + if (!m_window_system) { + return; + } + + m_cv.wait(lk, [&] { return m_pending_process != nullptr; }); + + const auto& params = m_pending_parameters; + auto applet = std::make_shared(m_system, std::move(m_pending_process), + params.applet_id == AppletId::Application); - applet->aruid = aruid; applet->program_id = params.program_id; applet->applet_id = params.applet_id; applet->type = params.applet_type; @@ -322,59 +320,19 @@ 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)); -} - -std::shared_ptr AppletManager::GetByAppletResourceUserId(AppletResourceUserId aruid) const { - std::scoped_lock lk{m_lock}; - - if (const auto it = m_applets.find(aruid); it != m_applets.end()) { - return it->second; + if (applet->applet_id == AppletId::QLaunch) { + applet->lifecycle_manager.SetFocusHandlingMode(false); + applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(false); + m_window_system->TrackApplet(applet, false); + m_window_system->RequestHomeMenuToGetForeground(); + } else { + m_window_system->TrackApplet(applet, true); + m_window_system->RequestApplicationToGetForeground(); } - return {}; -} - -void AppletManager::Reset() { - std::scoped_lock lk{m_lock}; - - m_applets.clear(); -} - -void AppletManager::RequestExit() { - std::scoped_lock lk{m_lock}; - - for (const auto& [aruid, applet] : m_applets) { - applet->message_queue.RequestExit(); - } -} - -void AppletManager::RequestResume() { - std::scoped_lock lk{m_lock}; - - for (const auto& [aruid, applet] : m_applets) { - applet->message_queue.RequestResume(); - } -} - -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(); - } + applet->process->Run(); } } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h index 4875de309..fbdc77140 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -3,17 +3,23 @@ #pragma once -#include +#include #include -#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/am_types.h" namespace Core { class System; } +namespace Service { +class Process; +} + namespace Service::AM { +class WindowSystem; + enum class LaunchType { FrontendInitiated, ApplicationInitiated, @@ -33,27 +39,24 @@ public: explicit AppletManager(Core::System& system); ~AppletManager(); - void InsertApplet(std::shared_ptr applet); - void TerminateAndRemoveApplet(AppletResourceUserId aruid); - - void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid, + void CreateAndInsertByFrontendAppletParameters(std::unique_ptr process, const FrontendAppletParameters& params); - std::shared_ptr GetByAppletResourceUserId(AppletResourceUserId aruid) const; - - void Reset(); - void RequestExit(); - void RequestResume(); void OperationModeChanged(); - void FocusStateChanged(); + +public: + void SetWindowSystem(WindowSystem* window_system); private: Core::System& m_system; - mutable std::mutex m_lock{}; - std::map> m_applets{}; + std::mutex m_lock; + std::condition_variable m_cv; - // AudioController state goes here + WindowSystem* m_window_system{}; + + FrontendAppletParameters m_pending_parameters{}; + std::unique_ptr m_pending_process{}; }; } // namespace Service::AM 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 83c3c5a55..000000000 --- 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 429b77d37..000000000 --- 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/button_poller.cpp b/src/core/hle/service/am/button_poller.cpp new file mode 100644 index 000000000..aab397085 --- /dev/null +++ b/src/core/hle/service/am/button_poller.cpp @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/button_poller.h" +#include "core/hle/service/am/window_system.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hid_types.h" + +namespace Service::AM { + +namespace { + +ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point start) { + using namespace std::chrono_literals; + + const auto dur = std::chrono::steady_clock::now() - start; + + // TODO: determine actual thresholds + // TODO: these are likely different for each button + if (dur < 500ms) { + return ButtonPressDuration::ShortPressing; + } else if (dur < 1000ms) { + return ButtonPressDuration::MiddlePressing; + } else { + return ButtonPressDuration::LongPressing; + } +} + +} // namespace + +ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) + : m_window_system(window_system) { + // TODO: am reads this from the home button state in hid, which is controller-agnostic. + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = + [this](Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Button) { + this->OnButtonStateChanged(); + } + }, + .is_npad_service = true, + }; + + m_handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + m_handheld_key = m_handheld->SetCallback(engine_callback); + m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + m_player1_key = m_player1->SetCallback(engine_callback); +} + +ButtonPoller::~ButtonPoller() { + m_handheld->DeleteCallback(m_handheld_key); + m_player1->DeleteCallback(m_player1_key); +} + +void ButtonPoller::OnButtonStateChanged() { + const bool home_button = + m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value(); + const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() || + m_player1->GetCaptureButtons().capture.Value(); + + // Buttons pressed which were not previously pressed + if (home_button && !m_home_button_press_start) { + m_home_button_press_start = std::chrono::steady_clock::now(); + } + if (capture_button && !m_capture_button_press_start) { + m_capture_button_press_start = std::chrono::steady_clock::now(); + } + // if (power_button && !m_power_button_press_start) { + // m_power_button_press_start = std::chrono::steady_clock::now(); + // } + + // Buttons released which were previously held + if (!home_button && m_home_button_press_start) { + m_window_system.OnHomeButtonPressed(ClassifyPressDuration(*m_home_button_press_start)); + m_home_button_press_start = std::nullopt; + } + if (!capture_button && m_capture_button_press_start) { + // TODO + m_capture_button_press_start = std::nullopt; + } + // if (!power_button && m_power_button_press_start) { + // // TODO + // m_power_button_press_start = std::nullopt; + // } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/button_poller.h b/src/core/hle/service/am/button_poller.h new file mode 100644 index 000000000..b1c39aad3 --- /dev/null +++ b/src/core/hle/service/am/button_poller.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "hid_core/frontend/emulated_controller.h" + +namespace Core { +namespace HID { +class EmulatedController; +} + +class System; +} // namespace Core + +namespace Service::AM { + +class WindowSystem; + +class ButtonPoller { +public: + explicit ButtonPoller(Core::System& system, WindowSystem& window_system); + ~ButtonPoller(); + +private: + void OnButtonStateChanged(); + +private: + WindowSystem& m_window_system; + + Core::HID::EmulatedController* m_handheld{}; + int m_handheld_key{}; + Core::HID::EmulatedController* m_player1{}; + int m_player1_key{}; + + std::optional m_home_button_press_start{}; + std::optional m_capture_button_press_start{}; + std::optional m_power_button_press_start{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/event_observer.cpp b/src/core/hle/service/am/event_observer.cpp new file mode 100644 index 000000000..5d1d303ed --- /dev/null +++ b/src/core/hle/service/am/event_observer.cpp @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/event_observer.h" +#include "core/hle/service/am/window_system.h" + +namespace Service::AM { + +enum class UserDataTag : u32 { + WakeupEvent, + AppletProcess, +}; + +EventObserver::EventObserver(Core::System& system, WindowSystem& window_system) + : m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system), + m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) { + m_window_system.SetEventObserver(this); + m_wakeup_holder.SetUserData(static_cast(UserDataTag::WakeupEvent)); + m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); + m_thread = std::thread([&] { this->ThreadFunc(); }); +} + +EventObserver::~EventObserver() { + // Signal thread and wait for processing to finish. + m_stop_source.request_stop(); + m_wakeup_event.Signal(); + m_thread.join(); + + // Free remaining owned sessions. + auto it = m_process_holder_list.begin(); + while (it != m_process_holder_list.end()) { + // Get the holder. + auto* const holder = std::addressof(*it); + + // Remove from the list. + it = m_process_holder_list.erase(it); + + // Free the holder. + delete holder; + } +} + +void EventObserver::TrackAppletProcess(Applet& applet) { + // Don't observe dummy processes. + if (!applet.process->IsInitialized()) { + return; + } + + // Allocate new holder. + auto* holder = new ProcessHolder(applet, *applet.process); + holder->SetUserData(static_cast(UserDataTag::AppletProcess)); + + // Insert into list. + { + std::scoped_lock lk{m_lock}; + m_process_holder_list.push_back(*holder); + holder->LinkToMultiWait(std::addressof(m_deferred_wait_list)); + } + + // Signal wakeup. + m_wakeup_event.Signal(); +} + +void EventObserver::RequestUpdate() { + m_wakeup_event.Signal(); +} + +void EventObserver::LinkDeferred() { + std::scoped_lock lk{m_lock}; + m_multi_wait.MoveAll(std::addressof(m_deferred_wait_list)); +} + +MultiWaitHolder* EventObserver::WaitSignaled() { + while (true) { + this->LinkDeferred(); + + // If we're done, return before we start waiting. + if (m_stop_source.stop_requested()) { + return nullptr; + } + + auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); + if (selected != std::addressof(m_wakeup_holder)) { + // Unlink the process. + selected->UnlinkFromMultiWait(); + } + + return selected; + } +} + +void EventObserver::Process(MultiWaitHolder* holder) { + switch (static_cast(holder->GetUserData())) { + case UserDataTag::WakeupEvent: + this->OnWakeupEvent(holder); + break; + case UserDataTag::AppletProcess: + this->OnProcessEvent(static_cast(holder)); + break; + default: + UNREACHABLE(); + } +} + +void EventObserver::OnWakeupEvent(MultiWaitHolder* holder) { + m_wakeup_event.Clear(); + + // Perform recalculation. + m_window_system.Update(); +} + +void EventObserver::OnProcessEvent(ProcessHolder* holder) { + // Check process state. + auto& applet = holder->GetApplet(); + auto& process = holder->GetProcess(); + + { + std::scoped_lock lk{m_lock, applet.lock}; + if (process.IsTerminated()) { + // Destroy the holder. + this->DestroyAppletProcessHolderLocked(holder); + } else { + // Reset signaled state. + process.ResetSignal(); + + // Relink wakeup event. + holder->LinkToMultiWait(std::addressof(m_deferred_wait_list)); + } + + // Set running. + applet.is_process_running = process.IsRunning(); + } + + // Perform recalculation. + m_window_system.Update(); +} + +void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) { + // Remove from owned list. + m_process_holder_list.erase(m_process_holder_list.iterator_to(*holder)); + + // Destroy and free. + delete holder; +} + +void EventObserver::ThreadFunc() { + Common::SetCurrentThreadName("am:EventObserver"); + + while (true) { + auto* signaled_holder = this->WaitSignaled(); + if (!signaled_holder) { + break; + } + + this->Process(signaled_holder); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/event_observer.h b/src/core/hle/service/am/event_observer.h new file mode 100644 index 000000000..3e52e8494 --- /dev/null +++ b/src/core/hle/service/am/event_observer.h @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/polyfill_thread.h" +#include "common/thread.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/os/multi_wait.h" + +namespace Core { +class System; +} + +namespace Service::AM { + +struct Applet; +class ProcessHolder; +class WindowSystem; + +class EventObserver { +public: + explicit EventObserver(Core::System& system, WindowSystem& window_system); + ~EventObserver(); + + void TrackAppletProcess(Applet& applet); + void RequestUpdate(); + +private: + void LinkDeferred(); + MultiWaitHolder* WaitSignaled(); + void Process(MultiWaitHolder* holder); + bool WaitAndProcessImpl(); + void LoopProcess(); + +private: + void OnWakeupEvent(MultiWaitHolder* holder); + void OnProcessEvent(ProcessHolder* holder); + +private: + void DestroyAppletProcessHolderLocked(ProcessHolder* holder); + +private: + void ThreadFunc(); + +private: + // System reference and context. + Core::System& m_system; + KernelHelpers::ServiceContext m_context; + + // Window manager. + WindowSystem& m_window_system; + + // Guest event handle to wake up the event loop processor. + Event m_wakeup_event; + MultiWaitHolder m_wakeup_holder; + + // Mutex to protect remaining members. + std::mutex m_lock{}; + + // List of owned process holders. + Common::IntrusiveListBaseTraits::ListType m_process_holder_list; + + // Multi-wait objects for new tasks. + MultiWait m_multi_wait; + MultiWait m_deferred_wait_list; + + // Processing thread. + std::thread m_thread{}; + std::stop_source m_stop_source{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp index e662c6cd6..cdd431857 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/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp index 8ed49bac1..ea4bd8f45 100644 --- a/src/core/hle/service/am/hid_registration.cpp +++ b/src/core/hle/service/am/hid_registration.cpp @@ -3,24 +3,28 @@ #include "core/core.h" #include "core/hle/service/am/hid_registration.h" -#include "core/hle/service/am/process.h" #include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/os/process.h" #include "core/hle/service/sm/sm.h" #include "hid_core/resource_manager.h" namespace Service::AM { HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) { - m_hid_server = system.ServiceManager().GetService("hid"); + m_hid_server = system.ServiceManager().GetService("hid", true); if (m_process.IsInitialized()) { m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(), true); + m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(), + true); } } HidRegistration::~HidRegistration() { if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(), + false); m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId( m_process.GetProcessId()); } @@ -28,6 +32,8 @@ HidRegistration::~HidRegistration() { void HidRegistration::EnableAppletToGetInput(bool enable) { if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(), + enable); m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable); } } diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h index 67cd84961..54f42af18 100644 --- a/src/core/hle/service/am/hid_registration.h +++ b/src/core/hle/service/am/hid_registration.h @@ -13,9 +13,11 @@ namespace Service::HID { class IHidServer; } -namespace Service::AM { - +namespace Service { class Process; +} + +namespace Service::AM { class HidRegistration { public: 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 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 000000000..7c70434a1 --- /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/process_creation.cpp b/src/core/hle/service/am/process_creation.cpp new file mode 100644 index 000000000..237151d06 --- /dev/null +++ b/src/core/hle/service/am/process_creation.cpp @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs_factory.h" +#include "core/hle/service/am/process_creation.h" +#include "core/hle/service/glue/glue_manager.h" +#include "core/hle/service/os/process.h" +#include "core/loader/loader.h" + +namespace Service::AM { + +namespace { + +FileSys::StorageId GetStorageIdForFrontendSlot( + std::optional slot) { + if (!slot.has_value()) { + return FileSys::StorageId::None; + } + + switch (*slot) { + case FileSys::ContentProviderUnionSlot::UserNAND: + return FileSys::StorageId::NandUser; + case FileSys::ContentProviderUnionSlot::SysNAND: + return FileSys::StorageId::NandSystem; + case FileSys::ContentProviderUnionSlot::SDMC: + return FileSys::StorageId::SdCard; + case FileSys::ContentProviderUnionSlot::FrontendManual: + return FileSys::StorageId::Host; + default: + return FileSys::StorageId::None; + } +} + +std::unique_ptr CreateProcessImpl(std::unique_ptr& out_loader, + Loader::ResultStatus& out_load_result, + Core::System& system, FileSys::VirtualFile file, + u64 program_id, u64 program_index) { + // Get the appropriate loader to parse this NCA. + out_loader = Loader::GetLoader(system, file, program_id, program_index); + + // Ensure we have a loader which can parse the NCA. + if (!out_loader) { + return nullptr; + } + + // Try to load the process. + auto process = std::make_unique(system); + if (process->Initialize(*out_loader, out_load_result)) { + return process; + } + + return nullptr; +} + +} // Anonymous namespace + +std::unique_ptr CreateProcess(Core::System& system, u64 program_id, + u8 minimum_key_generation, u8 maximum_key_generation) { + // Attempt to load program NCA. + FileSys::VirtualFile nca_raw{}; + + // Get the program NCA from storage. + auto& storage = system.GetContentProviderUnion(); + nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + + // Ensure we retrieved a program NCA. + if (!nca_raw) { + return nullptr; + } + + // Ensure we have a suitable version. + if (minimum_key_generation > 0) { + FileSys::NCA nca(nca_raw); + if (nca.GetStatus() == Loader::ResultStatus::Success && + (nca.GetKeyGeneration() < minimum_key_generation || + nca.GetKeyGeneration() > maximum_key_generation)) { + LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, + nca.GetKeyGeneration()); + return nullptr; + } + } + + std::unique_ptr loader; + Loader::ResultStatus status; + return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0); +} + +std::unique_ptr CreateApplicationProcess(std::vector& out_control, + std::unique_ptr& out_loader, + Loader::ResultStatus& out_load_result, + Core::System& system, FileSys::VirtualFile file, + u64 program_id, u64 program_index) { + auto process = + CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); + if (!process) { + return nullptr; + } + + FileSys::NACP nacp; + if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) { + out_control = nacp.GetRawBytes(); + } else { + out_control.resize(sizeof(FileSys::RawNACP)); + } + + auto& storage = system.GetContentProviderUnion(); + Service::Glue::ApplicationLaunchProperty launch{}; + launch.title_id = process->GetProgramId(); + + FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage}; + launch.version = pm.GetGameVersion().value_or(0); + + // TODO(DarkLordZach): When FSController/Game Card Support is added, if + // current_process_game_card use correct StorageId + launch.base_game_storage_id = GetStorageIdForFrontendSlot( + storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program)); + launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry( + FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program)); + + system.GetARPManager().Register(launch.title_id, launch, out_control); + + return process; +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process_creation.h b/src/core/hle/service/am/process_creation.h new file mode 100644 index 000000000..8cfb9e0c9 --- /dev/null +++ b/src/core/hle/service/am/process_creation.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/file_sys/vfs/vfs_types.h" + +namespace Core { +class System; +} + +namespace Loader { +class AppLoader; +enum class ResultStatus : u16; +} // namespace Loader + +namespace Service { +class Process; +} + +namespace Service::AM { + +std::unique_ptr CreateProcess(Core::System& system, u64 program_id, + u8 minimum_key_generation, u8 maximum_key_generation); +std::unique_ptr CreateApplicationProcess(std::vector& out_control, + std::unique_ptr& out_loader, + Loader::ResultStatus& out_load_result, + Core::System& system, FileSys::VirtualFile file, + u64 program_id, u64 program_index); + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process_holder.cpp b/src/core/hle/service/am/process_holder.cpp new file mode 100644 index 000000000..21ef5bf83 --- /dev/null +++ b/src/core/hle/service/am/process_holder.cpp @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/am/process_holder.h" +#include "core/hle/service/os/process.h" + +namespace Service::AM { + +ProcessHolder::ProcessHolder(Applet& applet, Process& process) + : MultiWaitHolder(process.GetHandle()), m_applet(applet), m_process(process) {} + +ProcessHolder::~ProcessHolder() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process_holder.h b/src/core/hle/service/am/process_holder.h new file mode 100644 index 000000000..3a9b81dfb --- /dev/null +++ b/src/core/hle/service/am/process_holder.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/os/multi_wait_holder.h" + +namespace Service { +class Process; +} + +namespace Service::AM { + +struct Applet; + +class ProcessHolder : public MultiWaitHolder, public Common::IntrusiveListBaseNode { +public: + explicit ProcessHolder(Applet& applet, Process& process); + ~ProcessHolder(); + + Applet& GetApplet() const { + return m_applet; + } + + Process& GetProcess() const { + return m_process; + } + +private: + Applet& m_applet; + Process& m_process; +}; + +} // namespace Service::AM 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 21747783a..bc9c86c55 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 @@ -6,12 +6,14 @@ #include "core/hle/service/am/service/all_system_applet_proxies_service.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/window_system.h" #include "core/hle/service/cmif_serialization.h" namespace Service::AM { -IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_) - : ServiceFramework{system_, "appletAE"} { +IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_, + WindowSystem& window_system) + : ServiceFramework{system_, "appletAE"}, m_window_system{window_system} { // clang-format off static const FunctionInfo functions[] = { {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"}, @@ -36,8 +38,8 @@ Result IAllSystemAppletProxiesService::OpenSystemAppletProxy( LOG_DEBUG(Service_AM, "called"); if (const auto applet = this->GetAppletFromProcessId(pid); applet) { - *out_system_applet_proxy = - std::make_shared(system, applet, process_handle.Get()); + *out_system_applet_proxy = std::make_shared( + system, applet, process_handle.Get(), m_window_system); R_SUCCEED(); } else { UNIMPLEMENTED(); @@ -52,8 +54,8 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy( LOG_DEBUG(Service_AM, "called"); if (const auto applet = this->GetAppletFromProcessId(pid); applet) { - *out_library_applet_proxy = - std::make_shared(system, applet, process_handle.Get()); + *out_library_applet_proxy = std::make_shared( + system, applet, process_handle.Get(), m_window_system); R_SUCCEED(); } else { UNIMPLEMENTED(); @@ -73,7 +75,7 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld( std::shared_ptr IAllSystemAppletProxiesService::GetAppletFromProcessId( ProcessId process_id) { - return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid); + return m_window_system.GetByAppletResourceUserId(process_id.pid); } } // namespace Service::AM 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 0e2dcb86d..e3e79dc4f 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 @@ -14,11 +14,12 @@ struct Applet; struct AppletAttribute; class ILibraryAppletProxy; class ISystemAppletProxy; +class WindowSystem; class IAllSystemAppletProxiesService final : public ServiceFramework { public: - explicit IAllSystemAppletProxiesService(Core::System& system_); + explicit IAllSystemAppletProxiesService(Core::System& system_, WindowSystem& window_system); ~IAllSystemAppletProxiesService() override; private: @@ -35,6 +36,8 @@ private: private: std::shared_ptr GetAppletFromProcessId(ProcessId pid); + + WindowSystem& m_window_system; }; } // namespace AM 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 0f29ab285..a051000af 100644 --- a/src/core/hle/service/am/service/applet_common_functions.cpp +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -19,7 +19,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, {21, nullptr, "TryPopFromAppletBoundChannel"}, {40, nullptr, "GetDisplayLogicalResolution"}, {42, nullptr, "SetDisplayMagnification"}, - {50, nullptr, "SetHomeButtonDoubleClickEnabled"}, + {50, D<&IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled>, "SetHomeButtonDoubleClickEnabled"}, {51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"}, {52, nullptr, "IsHomeButtonShortPressedBlocked"}, {60, nullptr, "IsVrModeCurtainRequired"}, @@ -40,6 +40,13 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, IAppletCommonFunctions::~IAppletCommonFunctions() = default; +Result IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled( + bool home_button_double_click_enabled) { + LOG_WARNING(Service_AM, "(STUBBED) called, home_button_double_click_enabled={}", + home_button_double_click_enabled); + R_SUCCEED(); +} + Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled( Out out_home_button_double_click_enabled) { LOG_WARNING(Service_AM, "(STUBBED) called"); 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 4424fc83d..376f85acf 100644 --- a/src/core/hle/service/am/service/applet_common_functions.h +++ b/src/core/hle/service/am/service/applet_common_functions.h @@ -16,6 +16,7 @@ public: ~IAppletCommonFunctions() override; private: + Result SetHomeButtonDoubleClickEnabled(bool home_button_double_click_enabled); Result GetHomeButtonDoubleClickEnabled(Out out_home_button_double_click_enabled); Result SetCpuBoostRequestPriority(s32 priority); Result GetCurrentApplicationId(Out out_application_id); diff --git a/src/core/hle/service/am/service/application_accessor.cpp b/src/core/hle/service/am/service/application_accessor.cpp index 6e7d110e8..986abc716 100644 --- a/src/core/hle/service/am/service/application_accessor.cpp +++ b/src/core/hle/service/am/service/application_accessor.cpp @@ -9,12 +9,16 @@ #include "core/hle/service/am/service/application_accessor.h" #include "core/hle/service/am/service/library_applet_accessor.h" #include "core/hle/service/am/service/storage.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/glue/glue_manager.h" namespace Service::AM { -IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_ptr applet) - : ServiceFramework{system_, "IApplicationAccessor"}, m_applet(std::move(applet)) { +IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system) + : ServiceFramework{system_, "IApplicationAccessor"}, m_window_system(window_system), + m_applet(std::move(applet)) { // clang-format off static const FunctionInfo functions[] = { {0, D<&IApplicationAccessor::GetAppletStateChangedEvent>, "GetAppletStateChangedEvent"}, @@ -59,7 +63,15 @@ Result IApplicationAccessor::Start() { Result IApplicationAccessor::RequestExit() { LOG_INFO(Service_AM, "called"); - m_applet->message_queue.RequestExit(); + + std::scoped_lock lk{m_applet->lock}; + if (m_applet->exit_locked) { + m_applet->lifecycle_manager.RequestExit(); + m_applet->UpdateSuspensionStateLocked(true); + } else { + m_applet->process->Terminate(); + } + R_SUCCEED(); } @@ -71,13 +83,14 @@ Result IApplicationAccessor::Terminate() { Result IApplicationAccessor::GetResult() { LOG_INFO(Service_AM, "called"); - R_SUCCEED(); + std::scoped_lock lk{m_applet->lock}; + R_RETURN(m_applet->terminate_result); } 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(); } @@ -96,8 +109,15 @@ Result IApplicationAccessor::PushLaunchParameter(LaunchParameterKind kind, Result IApplicationAccessor::GetApplicationControlProperty( OutBuffer out_control_property) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - R_THROW(ResultUnknown); + LOG_INFO(Service_AM, "called"); + + std::vector nacp; + R_TRY(system.GetARPManager().GetControlProperty(&nacp, m_applet->program_id)); + + std::memcpy(out_control_property.data(), nacp.data(), + std::min(out_control_property.size(), nacp.size())); + + R_SUCCEED(); } Result IApplicationAccessor::SetUsers(bool enable, @@ -114,8 +134,9 @@ Result IApplicationAccessor::GetCurrentLibraryApplet( } Result IApplicationAccessor::RequestForApplicationToGetForeground() { - LOG_WARNING(Service_AM, "(STUBBED) called"); - R_THROW(ResultUnknown); + LOG_INFO(Service_AM, "called"); + m_window_system.RequestApplicationToGetForeground(); + R_SUCCEED(); } Result IApplicationAccessor::CheckRightsEnvironmentAvailable(Out out_is_available) { diff --git a/src/core/hle/service/am/service/application_accessor.h b/src/core/hle/service/am/service/application_accessor.h index 39a9b2153..b9797bcc0 100644 --- a/src/core/hle/service/am/service/application_accessor.h +++ b/src/core/hle/service/am/service/application_accessor.h @@ -13,10 +13,12 @@ namespace Service::AM { struct Applet; class ILibraryAppletAccessor; class IStorage; +class WindowSystem; class IApplicationAccessor final : public ServiceFramework { public: - explicit IApplicationAccessor(Core::System& system_, std::shared_ptr applet); + explicit IApplicationAccessor(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system); ~IApplicationAccessor() override; private: @@ -34,6 +36,7 @@ private: Result GetNsRightsEnvironmentHandle(Out out_handle); Result ReportApplicationExitTimeout(); + WindowSystem& m_window_system; const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp index 568bb0122..8994f1914 100644 --- a/src/core/hle/service/am/service/application_creator.cpp +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -1,17 +1,57 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" #include "core/hle/service/am/am_types.h" #include "core/hle/service/am/applet.h" #include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/process_creation.h" #include "core/hle/service/am/service/application_accessor.h" #include "core/hle/service/am/service/application_creator.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" +#include "core/loader/loader.h" namespace Service::AM { -IApplicationCreator::IApplicationCreator(Core::System& system_) - : ServiceFramework{system_, "IApplicationCreator"} { +namespace { + +Result CreateGuestApplication(SharedPointer* out_application_accessor, + Core::System& system, WindowSystem& window_system, u64 program_id) { + FileSys::VirtualFile nca_raw{}; + + // Get the program NCA from storage. + auto& storage = system.GetContentProviderUnion(); + nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + + // Ensure we retrieved a program NCA. + R_UNLESS(nca_raw != nullptr, ResultUnknown); + + std::vector control; + std::unique_ptr loader; + Loader::ResultStatus result; + auto process = + CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); + R_UNLESS(process != nullptr, ResultUnknown); + + const auto applet = std::make_shared(system, std::move(process), true); + applet->program_id = program_id; + applet->applet_id = AppletId::Application; + applet->type = AppletType::Application; + applet->library_applet_mode = LibraryAppletMode::AllForeground; + + window_system.TrackApplet(applet, true); + + *out_application_accessor = + std::make_shared(system, applet, window_system); + R_SUCCEED(); +} + +} // namespace + +IApplicationCreator::IApplicationCreator(Core::System& system_, WindowSystem& window_system) + : ServiceFramework{system_, "IApplicationCreator"}, m_window_system{window_system} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"}, @@ -28,8 +68,9 @@ IApplicationCreator::~IApplicationCreator() = default; Result IApplicationCreator::CreateApplication( Out> out_application_accessor, u64 application_id) { - LOG_ERROR(Service_NS, "called, application_id={:x}", application_id); - R_THROW(ResultUnknown); + LOG_INFO(Service_NS, "called, application_id={:016X}", application_id); + R_RETURN( + CreateGuestApplication(out_application_accessor, system, m_window_system, application_id)); } } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_creator.h b/src/core/hle/service/am/service/application_creator.h index 9f939ebf6..287745af8 100644 --- a/src/core/hle/service/am/service/application_creator.h +++ b/src/core/hle/service/am/service/application_creator.h @@ -10,14 +10,17 @@ namespace Service::AM { class IApplicationAccessor; struct Applet; +class WindowSystem; class IApplicationCreator final : public ServiceFramework { public: - explicit IApplicationCreator(Core::System& system_); + explicit IApplicationCreator(Core::System& system_, WindowSystem& window_system); ~IApplicationCreator() override; private: Result CreateApplication(Out>, u64 application_id); + + WindowSystem& m_window_system; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index bfccb6b09..3bab5ac5f 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -181,7 +181,8 @@ Result IApplicationFunctions::GetDesiredLanguage(Out out_language_code) { } Result IApplicationFunctions::SetTerminateResult(Result terminate_result) { - LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({}-{})", terminate_result.GetInnerValue(), + LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({:04}-{:04})", + terminate_result.GetInnerValue(), static_cast(terminate_result.GetModule()) + 2000, terminate_result.GetDescription()); diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp index 19d6a3b89..6e1328fee 100644 --- a/src/core/hle/service/am/service/application_proxy.cpp +++ b/src/core/hle/service/am/service/application_proxy.cpp @@ -17,9 +17,9 @@ namespace Service::AM { IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr applet, - Kernel::KProcess* process) - : ServiceFramework{system_, "IApplicationProxy"}, m_process{process}, m_applet{ - std::move(applet)} { + Kernel::KProcess* process, WindowSystem& window_system) + : ServiceFramework{system_, "IApplicationProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, @@ -70,7 +70,7 @@ Result IApplicationProxy::GetDebugFunctions( Result IApplicationProxy::GetWindowController( Out> out_window_controller) { LOG_DEBUG(Service_AM, "called"); - *out_window_controller = std::make_shared(system, m_applet); + *out_window_controller = std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } @@ -91,7 +91,8 @@ Result IApplicationProxy::GetCommonStateGetter( Result IApplicationProxy::GetLibraryAppletCreator( Out> out_library_applet_creator) { LOG_DEBUG(Service_AM, "called"); - *out_library_applet_creator = std::make_shared(system, m_applet); + *out_library_applet_creator = + std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h index 6da350df7..8c62459c4 100644 --- a/src/core/hle/service/am/service/application_proxy.h +++ b/src/core/hle/service/am/service/application_proxy.h @@ -18,11 +18,12 @@ class ILibraryAppletCreator; class IProcessWindingController; class ISelfController; class IWindowController; +class WindowSystem; class IApplicationProxy final : public ServiceFramework { public: explicit IApplicationProxy(Core::System& system_, std::shared_ptr applet, - Kernel::KProcess* process); + Kernel::KProcess* process, WindowSystem& window_system); ~IApplicationProxy(); private: @@ -40,6 +41,7 @@ private: Out> out_application_functions); private: + WindowSystem& m_window_system; Kernel::KProcess* const m_process; const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp index fd66e77b9..b7d7b3c2d 100644 --- a/src/core/hle/service/am/service/application_proxy_service.cpp +++ b/src/core/hle/service/am/service/application_proxy_service.cpp @@ -6,12 +6,14 @@ #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/service/application_proxy.h" #include "core/hle/service/am/service/application_proxy_service.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" namespace Service::AM { -IApplicationProxyService::IApplicationProxyService(Core::System& system_) - : ServiceFramework{system_, "appletOE"} { +IApplicationProxyService::IApplicationProxyService(Core::System& system_, + WindowSystem& window_system) + : ServiceFramework{system_, "appletOE"}, m_window_system{window_system} { static const FunctionInfo functions[] = { {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"}, }; @@ -26,8 +28,8 @@ Result IApplicationProxyService::OpenApplicationProxy( LOG_DEBUG(Service_AM, "called"); if (const auto applet = this->GetAppletFromProcessId(pid)) { - *out_application_proxy = - std::make_shared(system, applet, process_handle.Get()); + *out_application_proxy = std::make_shared( + system, applet, process_handle.Get(), m_window_system); R_SUCCEED(); } else { UNIMPLEMENTED(); @@ -36,7 +38,7 @@ Result IApplicationProxyService::OpenApplicationProxy( } std::shared_ptr IApplicationProxyService::GetAppletFromProcessId(ProcessId process_id) { - return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid); + return m_window_system.GetByAppletResourceUserId(process_id.pid); } } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_proxy_service.h b/src/core/hle/service/am/service/application_proxy_service.h index 8efafa31a..e5f4ea345 100644 --- a/src/core/hle/service/am/service/application_proxy_service.h +++ b/src/core/hle/service/am/service/application_proxy_service.h @@ -12,10 +12,11 @@ namespace AM { struct Applet; class IApplicationProxy; +class WindowSystem; class IApplicationProxyService final : public ServiceFramework { public: - explicit IApplicationProxyService(Core::System& system_); + explicit IApplicationProxyService(Core::System& system_, WindowSystem& window_system); ~IApplicationProxyService() override; private: @@ -24,6 +25,8 @@ private: private: std::shared_ptr GetAppletFromProcessId(ProcessId pid); + + WindowSystem& m_window_system; }; } // namespace AM 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 a32855ffa..f523bcd9e 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/home_menu_functions.cpp b/src/core/hle/service/am/service/home_menu_functions.cpp index 0c4d24b58..25f78beb5 100644 --- a/src/core/hle/service/am/service/home_menu_functions.cpp +++ b/src/core/hle/service/am/service/home_menu_functions.cpp @@ -4,13 +4,16 @@ #include "core/hle/result.h" #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/service/home_menu_functions.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" namespace Service::AM { -IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr applet) - : ServiceFramework{system_, "IHomeMenuFunctions"}, m_applet{std::move(applet)}, - m_context{system, "IHomeMenuFunctions"}, m_pop_from_general_channel_event{m_context} { +IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system) + : ServiceFramework{system_, "IHomeMenuFunctions"}, m_window_system{window_system}, + m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"}, + m_pop_from_general_channel_event{m_context} { // clang-format off static const FunctionInfo functions[] = { {10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"}, @@ -37,17 +40,20 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr { public: - explicit IHomeMenuFunctions(Core::System& system_, std::shared_ptr applet); + explicit IHomeMenuFunctions(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system); ~IHomeMenuFunctions() override; private: @@ -26,6 +28,7 @@ private: Result IsForceTerminateApplicationDisabledForDebug( Out out_is_force_terminate_application_disabled_for_debug); + WindowSystem& m_window_system; const std::shared_ptr m_applet; KernelHelpers::ServiceContext m_context; Event m_pop_from_general_channel_event; 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 0c2426d4b..cda8c3eb8 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -47,20 +47,21 @@ 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(); } -Result ILibraryAppletAccessor::GetResult(Out out_result) { +Result ILibraryAppletAccessor::GetResult() { LOG_DEBUG(Service_AM, "called"); - *out_result = m_applet->terminate_result; - R_SUCCEED(); + std::scoped_lock lk{m_applet->lock}; + R_RETURN(m_applet->terminate_result); } Result ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero() { @@ -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_accessor.h b/src/core/hle/service/am/service/library_applet_accessor.h index 97d3b6c8a..36712821a 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.h +++ b/src/core/hle/service/am/service/library_applet_accessor.h @@ -21,7 +21,7 @@ public: private: Result GetAppletStateChangedEvent(OutCopyHandle out_event); Result IsCompleted(Out out_is_completed); - Result GetResult(Out out_result); + Result GetResult(); Result PresetLibraryAppletGpuTimeSliceZero(); Result Start(); Result RequestExit(); 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 c97358d81..3ffb03bc9 100644 --- a/src/core/hle/service/am/service/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -7,9 +7,11 @@ #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/process_creation.h" #include "core/hle/service/am/service/library_applet_accessor.h" #include "core/hle/service/am/service/library_applet_creator.h" #include "core/hle/service/am/service/storage.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/sm/sm.h" @@ -93,6 +95,7 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) { } std::shared_ptr CreateGuestApplet(Core::System& system, + WindowSystem& window_system, std::shared_ptr caller_applet, AppletId applet_id, LibraryAppletMode mode) { @@ -110,53 +113,38 @@ std::shared_ptr CreateGuestApplet(Core::System& system, Firmware1700 = 17, }; - auto process = std::make_unique(system); - if (!process->Initialize(program_id, Firmware1400, Firmware1700)) { + auto process = CreateProcess(system, program_id, Firmware1400, Firmware1700); + if (!process) { // Couldn't initialize the guest process 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; applet->library_applet_mode = mode; - - // Set focus state - switch (mode) { - case LibraryAppletMode::AllForeground: - 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); - 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); - break; - } + applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden; auto broker = std::make_shared(system); applet->caller_applet = caller_applet; applet->caller_applet_broker = broker; + caller_applet->child_applets.push_back(applet); - system.GetAppletManager().InsertApplet(applet); + window_system.TrackApplet(applet, false); return std::make_shared(system, broker, applet); } std::shared_ptr CreateFrontendApplet(Core::System& system, + WindowSystem& window_system, std::shared_ptr caller_applet, AppletId applet_id, LibraryAppletMode mode) { 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; @@ -166,14 +154,19 @@ std::shared_ptr CreateFrontendApplet(Core::System& syste applet->caller_applet = caller_applet; applet->caller_applet_broker = storage; applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); + caller_applet->child_applets.push_back(applet); + + window_system.TrackApplet(applet, false); return std::make_shared(system, storage, applet); } } // namespace -ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet) - : ServiceFramework{system_, "ILibraryAppletCreator"}, m_applet{std::move(applet)} { +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system) + : ServiceFramework{system_, "ILibraryAppletCreator"}, + m_window_system{window_system}, m_applet{std::move(applet)} { static const FunctionInfo functions[] = { {0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"}, {1, nullptr, "TerminateAllLibraryApplets"}, @@ -195,10 +188,12 @@ Result ILibraryAppletCreator::CreateLibraryApplet( std::shared_ptr library_applet; if (ShouldCreateGuestApplet(applet_id)) { - library_applet = CreateGuestApplet(system, m_applet, applet_id, library_applet_mode); + library_applet = + CreateGuestApplet(system, m_window_system, m_applet, applet_id, library_applet_mode); } if (!library_applet) { - library_applet = CreateFrontendApplet(system, m_applet, applet_id, library_applet_mode); + library_applet = + CreateFrontendApplet(system, m_window_system, m_applet, applet_id, library_applet_mode); } if (!library_applet) { LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); diff --git a/src/core/hle/service/am/service/library_applet_creator.h b/src/core/hle/service/am/service/library_applet_creator.h index fe6d40eb3..a10a76982 100644 --- a/src/core/hle/service/am/service/library_applet_creator.h +++ b/src/core/hle/service/am/service/library_applet_creator.h @@ -12,10 +12,12 @@ namespace Service::AM { struct Applet; class ILibraryAppletAccessor; class IStorage; +class WindowSystem; class ILibraryAppletCreator final : public ServiceFramework { public: - explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet); + explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system); ~ILibraryAppletCreator() override; private: @@ -29,6 +31,7 @@ private: Result CreateHandleStorage(Out> out_storage, s64 size, InCopyHandle transfer_memory_handle); + WindowSystem& m_window_system; const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/library_applet_proxy.cpp b/src/core/hle/service/am/service/library_applet_proxy.cpp index 58e709347..f9cfb82a9 100644 --- a/src/core/hle/service/am/service/library_applet_proxy.cpp +++ b/src/core/hle/service/am/service/library_applet_proxy.cpp @@ -19,9 +19,9 @@ namespace Service::AM { ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr applet, - Kernel::KProcess* process) - : ServiceFramework{system_, "ILibraryAppletProxy"}, m_process{process}, m_applet{ - std::move(applet)} { + Kernel::KProcess* process, WindowSystem& window_system) + : ServiceFramework{system_, "ILibraryAppletProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, @@ -75,7 +75,7 @@ Result ILibraryAppletProxy::GetDebugFunctions( Result ILibraryAppletProxy::GetWindowController( Out> out_window_controller) { LOG_DEBUG(Service_AM, "called"); - *out_window_controller = std::make_shared(system, m_applet); + *out_window_controller = std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } @@ -96,7 +96,8 @@ Result ILibraryAppletProxy::GetCommonStateGetter( Result ILibraryAppletProxy::GetLibraryAppletCreator( Out> out_library_applet_creator) { LOG_DEBUG(Service_AM, "called"); - *out_library_applet_creator = std::make_shared(system, m_applet); + *out_library_applet_creator = + std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } @@ -118,7 +119,8 @@ Result ILibraryAppletProxy::GetAppletCommonFunctions( Result ILibraryAppletProxy::GetHomeMenuFunctions( Out> out_home_menu_functions) { LOG_DEBUG(Service_AM, "called"); - *out_home_menu_functions = std::make_shared(system, m_applet); + *out_home_menu_functions = + std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/library_applet_proxy.h b/src/core/hle/service/am/service/library_applet_proxy.h index 7d0714b85..792d58582 100644 --- a/src/core/hle/service/am/service/library_applet_proxy.h +++ b/src/core/hle/service/am/service/library_applet_proxy.h @@ -21,11 +21,12 @@ class ILibraryAppletSelfAccessor; class IProcessWindingController; class ISelfController; class IWindowController; +class WindowSystem; class ILibraryAppletProxy final : public ServiceFramework { public: explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr applet, - Kernel::KProcess* process); + Kernel::KProcess* process, WindowSystem& window_system); ~ILibraryAppletProxy(); private: @@ -47,6 +48,7 @@ private: Result GetGlobalStateController( Out> out_global_state_controller); + WindowSystem& m_window_system; Kernel::KProcess* const m_process; const std::shared_ptr m_applet; }; 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 330eb26f0..3fe36b899 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 @@ -176,8 +176,7 @@ Result ILibraryAppletSelfAccessor::GetMainAppletStorageId(Outaruid); - m_broker->SignalCompletion(); + m_applet->process->Terminate(); 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 06314407c..1db02b88f 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -86,8 +86,7 @@ ISelfController::~ISelfController() { Result ISelfController::Exit() { LOG_DEBUG(Service_AM, "called"); - // TODO - system.Exit(); + m_applet->process->Terminate(); R_SUCCEED(); } @@ -95,7 +94,16 @@ Result ISelfController::Exit() { Result ISelfController::LockExit() { LOG_DEBUG(Service_AM, "called"); - system.SetExitLocked(true); + std::scoped_lock lk{m_applet->lock}; + + if (m_applet->lifecycle_manager.GetExitRequested()) { + // With exit already requested, ignore and terminate immediately. + m_applet->process->Terminate(); + } else { + // Otherwise, set exit lock state. + m_applet->exit_locked = true; + system.SetExitLocked(true); + } R_SUCCEED(); } @@ -103,10 +111,13 @@ Result ISelfController::LockExit() { Result ISelfController::UnlockExit() { LOG_DEBUG(Service_AM, "called"); + std::scoped_lock lk{m_applet->lock}; + + m_applet->exit_locked = false; system.SetExitLocked(false); - if (system.GetExitRequested()) { - system.Exit(); + if (m_applet->lifecycle_manager.GetExitRequested()) { + m_applet->process->Terminate(); } R_SUCCEED(); @@ -155,7 +166,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 +175,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 +195,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 +214,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/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp index d1871ef9b..c435288a2 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.cpp +++ b/src/core/hle/service/am/service/system_applet_proxy.cpp @@ -19,9 +19,9 @@ namespace Service::AM { ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr applet, - Kernel::KProcess* process) - : ServiceFramework{system_, "ISystemAppletProxy"}, m_process{process}, m_applet{ - std::move(applet)} { + Kernel::KProcess* process, WindowSystem& window_system) + : ServiceFramework{system_, "ISystemAppletProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, @@ -75,7 +75,7 @@ Result ISystemAppletProxy::GetDebugFunctions( Result ISystemAppletProxy::GetWindowController( Out> out_window_controller) { LOG_DEBUG(Service_AM, "called"); - *out_window_controller = std::make_shared(system, m_applet); + *out_window_controller = std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } @@ -96,14 +96,15 @@ Result ISystemAppletProxy::GetCommonStateGetter( Result ISystemAppletProxy::GetLibraryAppletCreator( Out> out_library_applet_creator) { LOG_DEBUG(Service_AM, "called"); - *out_library_applet_creator = std::make_shared(system, m_applet); + *out_library_applet_creator = + std::make_shared(system, m_applet, m_window_system); R_SUCCEED(); } Result ISystemAppletProxy::GetApplicationCreator( Out> out_application_creator) { LOG_DEBUG(Service_AM, "called"); - *out_application_creator = std::make_shared(system); + *out_application_creator = std::make_shared(system, m_window_system); R_SUCCEED(); } @@ -117,7 +118,8 @@ Result ISystemAppletProxy::GetAppletCommonFunctions( Result ISystemAppletProxy::GetHomeMenuFunctions( Out> out_home_menu_functions) { LOG_DEBUG(Service_AM, "called"); - *out_home_menu_functions = std::make_shared(system, m_applet); + *out_home_menu_functions = + std::make_shared(system, m_applet, m_window_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 67cd50e03..217d9dc8c 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.h +++ b/src/core/hle/service/am/service/system_applet_proxy.h @@ -21,11 +21,12 @@ class ILibraryAppletCreator; class IProcessWindingController; class ISelfController; class IWindowController; +class WindowSystem; class ISystemAppletProxy final : public ServiceFramework { public: explicit ISystemAppletProxy(Core::System& system, std::shared_ptr applet, - Kernel::KProcess* process); + Kernel::KProcess* process, WindowSystem& window_system); ~ISystemAppletProxy(); private: @@ -46,6 +47,7 @@ private: Result GetGlobalStateController( Out> out_global_state_controller); + WindowSystem& m_window_system; Kernel::KProcess* const m_process; const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp index 99a4f50a2..54396affb 100644 --- a/src/core/hle/service/am/service/window_controller.cpp +++ b/src/core/hle/service/am/service/window_controller.cpp @@ -4,12 +4,15 @@ #include "core/hle/service/am/applet.h" #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/service/window_controller.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" namespace Service::AM { -IWindowController::IWindowController(Core::System& system_, std::shared_ptr applet) - : ServiceFramework{system_, "IWindowController"}, m_applet{std::move(applet)} { +IWindowController::IWindowController(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system) + : ServiceFramework{system_, "IWindowController"}, + m_window_system{window_system}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, @@ -63,17 +66,9 @@ Result IWindowController::RejectToChangeIntoBackground() { } Result IWindowController::SetAppletWindowVisibility(bool visible) { - m_applet->display_layer_manager.SetWindowVisibility(visible); - m_applet->hid_registration.EnableAppletToGetInput(visible); + LOG_INFO(Service_AM, "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); + m_window_system.RequestAppletVisibilityState(*m_applet, visible); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/window_controller.h b/src/core/hle/service/am/service/window_controller.h index bfbad9bcc..a784dd4a4 100644 --- a/src/core/hle/service/am/service/window_controller.h +++ b/src/core/hle/service/am/service/window_controller.h @@ -9,10 +9,12 @@ namespace Service::AM { struct Applet; +class WindowSystem; class IWindowController final : public ServiceFramework { public: - explicit IWindowController(Core::System& system_, std::shared_ptr applet); + explicit IWindowController(Core::System& system_, std::shared_ptr applet, + WindowSystem& window_system); ~IWindowController() override; private: @@ -24,6 +26,7 @@ private: Result SetAppletWindowVisibility(bool visible); Result SetAppletGpuTimeSlice(s64 time_slice); + WindowSystem& m_window_system; const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp new file mode 100644 index 000000000..5cf24007c --- /dev/null +++ b/src/core/hle/service/am/window_system.cpp @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/event_observer.h" +#include "core/hle/service/am/window_system.h" + +namespace Service::AM { + +WindowSystem::WindowSystem(Core::System& system) : m_system(system) {} + +WindowSystem::~WindowSystem() { + m_system.GetAppletManager().SetWindowSystem(nullptr); +} + +void WindowSystem::SetEventObserver(EventObserver* observer) { + m_event_observer = observer; + m_system.GetAppletManager().SetWindowSystem(this); +} + +void WindowSystem::Update() { + std::scoped_lock lk{m_lock}; + + // Loop through all applets and remove terminated applets. + this->PruneTerminatedAppletsLocked(); + + // If the home menu is being locked into the foreground, handle that. + if (this->LockHomeMenuIntoForegroundLocked()) { + return; + } + + // 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); +} + +void WindowSystem::TrackApplet(std::shared_ptr applet, bool is_application) { + std::scoped_lock lk{m_lock}; + + if (applet->applet_id == AppletId::QLaunch) { + ASSERT(m_home_menu == nullptr); + m_home_menu = applet.get(); + } else if (is_application) { + ASSERT(m_application == nullptr); + m_application = applet.get(); + } + + m_event_observer->TrackAppletProcess(*applet); + m_applets.emplace(applet->aruid.pid, std::move(applet)); +} + +std::shared_ptr WindowSystem::GetByAppletResourceUserId(u64 aruid) { + std::scoped_lock lk{m_lock}; + + const auto it = m_applets.find(aruid); + if (it == m_applets.end()) { + return nullptr; + } + + return it->second; +} + +std::shared_ptr WindowSystem::GetMainApplet() { + std::scoped_lock lk{m_lock}; + + if (m_application) { + return m_applets.at(m_application->aruid.pid); + } + + return nullptr; +} + +void WindowSystem::RequestHomeMenuToGetForeground() { + { + std::scoped_lock lk{m_lock}; + m_foreground_requested_applet = m_home_menu; + } + + m_event_observer->RequestUpdate(); +} + +void WindowSystem::RequestApplicationToGetForeground() { + { + std::scoped_lock lk{m_lock}; + m_foreground_requested_applet = m_application; + } + + m_event_observer->RequestUpdate(); +} + +void WindowSystem::RequestLockHomeMenuIntoForeground() { + { + std::scoped_lock lk{m_lock}; + m_home_menu_foreground_locked = true; + } + + m_event_observer->RequestUpdate(); +} + +void WindowSystem::RequestUnlockHomeMenuIntoForeground() { + { + std::scoped_lock lk{m_lock}; + m_home_menu_foreground_locked = false; + } + + m_event_observer->RequestUpdate(); +} + +void WindowSystem::RequestAppletVisibilityState(Applet& applet, bool visible) { + { + std::scoped_lock lk{applet.lock}; + applet.window_visible = visible; + } + + m_event_observer->RequestUpdate(); +} + +void WindowSystem::OnOperationModeChanged() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + std::scoped_lock lk2{applet->lock}; + applet->lifecycle_manager.OnOperationAndPerformanceModeChanged(); + } +} + +void WindowSystem::OnExitRequested() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + std::scoped_lock lk2{applet->lock}; + applet->lifecycle_manager.RequestExit(); + } +} + +void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) { + std::scoped_lock lk{m_lock}; + + // If we don't have a home menu, nothing to do. + if (!m_home_menu) { + return; + } + + // Lock. + std::scoped_lock lk2{m_home_menu->lock}; + + // Send home button press event to home menu. + if (type == ButtonPressDuration::ShortPressing) { + m_home_menu->lifecycle_manager.PushUnorderedMessage( + AppletMessage::DetectShortPressingHomeButton); + } +} + +void WindowSystem::PruneTerminatedAppletsLocked() { + for (auto it = m_applets.begin(); it != m_applets.end(); /* ... */) { + const auto& [aruid, applet] = *it; + + std::scoped_lock lk{applet->lock}; + + if (!applet->process->IsTerminated()) { + // Not terminated. + it = std::next(it); + continue; + } + + // Terminated, so ensure all child applets are terminated. + if (!applet->child_applets.empty()) { + this->TerminateChildAppletsLocked(applet.get()); + + // Not ready to unlink until all child applets are terminated. + it = std::next(it); + continue; + } + + // Erase from caller applet's list of children. + if (auto caller_applet = applet->caller_applet.lock(); caller_applet) { + std::scoped_lock lk2{caller_applet->lock}; + std::erase(caller_applet->child_applets, applet); + applet->caller_applet.reset(); + + // We don't need to update the activity state of the caller applet yet. + // It will be recalculated once we fall out of the termination handling path. + } + + // If this applet was foreground, it no longer is. + if (applet.get() == m_foreground_requested_applet) { + m_foreground_requested_applet = nullptr; + } + + // If this was the home menu, we should clean up. + if (applet.get() == m_home_menu) { + m_home_menu = nullptr; + m_foreground_requested_applet = m_application; + } + + // If this was the application, we should try to switch to the home menu. + if (applet.get() == m_application) { + m_application = nullptr; + m_foreground_requested_applet = m_home_menu; + + // If we have a home menu, send it the application exited message. + if (m_home_menu) { + m_home_menu->lifecycle_manager.PushUnorderedMessage( + AppletMessage::ApplicationExited); + } + } + + // Finalize applet. + applet->OnProcessTerminatedLocked(); + + // Request update to ensure quiescence. + m_event_observer->RequestUpdate(); + + // Unlink and advance. + it = m_applets.erase(it); + } + + // If the last applet has exited, exit the system. + if (m_applets.empty()) { + m_system.Exit(); + } +} + +bool WindowSystem::LockHomeMenuIntoForegroundLocked() { + // If the home menu is not locked into foreground, then there's nothing to do. + if (m_home_menu == nullptr || !m_home_menu_foreground_locked) { + m_home_menu_foreground_locked = false; + return false; + } + + // Terminate any direct child applets of the home menu. + std::scoped_lock lk{m_home_menu->lock}; + + this->TerminateChildAppletsLocked(m_home_menu); + + // When there are zero child applets left, we can proceed with the update. + if (m_home_menu->child_applets.empty()) { + m_home_menu->window_visible = true; + m_foreground_requested_applet = m_home_menu; + return false; + } + + return true; +} + +void WindowSystem::TerminateChildAppletsLocked(Applet* applet) { + auto child_applets = applet->child_applets; + + applet->lock.unlock(); + for (const auto& child_applet : child_applets) { + std::scoped_lock lk{child_applet->lock}; + child_applet->process->Terminate(); + child_applet->terminate_result = AM::ResultLibraryAppletTerminated; + } + applet->lock.lock(); +} + +void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) { + // With no applet, we don't have anything to do. + if (!applet) { + return; + } + + std::scoped_lock lk{applet->lock}; + + const bool inherited_foreground = applet->is_process_running && is_foreground; + const auto visible_state = + inherited_foreground ? ActivityState::ForegroundVisible : ActivityState::BackgroundVisible; + const auto obscured_state = inherited_foreground ? ActivityState::ForegroundObscured + : ActivityState::BackgroundObscured; + + const bool has_obscuring_child_applets = [&] { + for (const auto& child_applet : applet->child_applets) { + std::scoped_lock lk2{child_applet->lock}; + const auto mode = child_applet->library_applet_mode; + if (child_applet->is_process_running && child_applet->window_visible && + (mode == LibraryAppletMode::AllForeground || + mode == LibraryAppletMode::AllForegroundInitiallyHidden)) { + return true; + } + } + + return false; + }(); + + // Update visibility state. + applet->display_layer_manager.SetWindowVisibility(is_foreground && applet->window_visible); + + // Update interactibility state. + applet->SetInteractibleLocked(is_foreground && applet->window_visible); + + // Update focus state and suspension. + const bool is_obscured = has_obscuring_child_applets || !applet->window_visible; + const auto state = applet->lifecycle_manager.GetActivityState(); + + if (is_obscured && state != obscured_state) { + // Set obscured state. + applet->lifecycle_manager.SetActivityState(obscured_state); + applet->UpdateSuspensionStateLocked(true); + } else if (!is_obscured && state != visible_state) { + // Set visible state. + applet->lifecycle_manager.SetActivityState(visible_state); + applet->UpdateSuspensionStateLocked(true); + } + + // Recurse into child applets. + for (const auto& child_applet : applet->child_applets) { + this->UpdateAppletStateLocked(child_applet.get(), is_foreground); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/window_system.h b/src/core/hle/service/am/window_system.h new file mode 100644 index 000000000..69e7a27ba --- /dev/null +++ b/src/core/hle/service/am/window_system.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" + +namespace Core { +class System; +} + +namespace Service::AM { + +struct Applet; +class EventObserver; + +enum class ButtonPressDuration { + ShortPressing, + MiddlePressing, + LongPressing, +}; + +class WindowSystem { +public: + explicit WindowSystem(Core::System& system); + ~WindowSystem(); + +public: + void SetEventObserver(EventObserver* event_observer); + void Update(); + +public: + void TrackApplet(std::shared_ptr applet, bool is_application); + std::shared_ptr GetByAppletResourceUserId(u64 aruid); + std::shared_ptr GetMainApplet(); + +public: + void RequestHomeMenuToGetForeground(); + void RequestApplicationToGetForeground(); + void RequestLockHomeMenuIntoForeground(); + void RequestUnlockHomeMenuIntoForeground(); + void RequestAppletVisibilityState(Applet& applet, bool visible); + +public: + void OnOperationModeChanged(); + void OnExitRequested(); + void OnHomeButtonPressed(ButtonPressDuration type); + void OnCaptureButtonPressed(ButtonPressDuration type) {} + void OnPowerButtonPressed(ButtonPressDuration type) {} + +private: + void PruneTerminatedAppletsLocked(); + bool LockHomeMenuIntoForegroundLocked(); + void TerminateChildAppletsLocked(Applet* applet); + void UpdateAppletStateLocked(Applet* applet, bool is_foreground); + +private: + // System reference. + Core::System& m_system; + + // Event observer. + EventObserver* m_event_observer{}; + + // Lock. + std::mutex m_lock{}; + + // Home menu state. + bool m_home_menu_foreground_locked{}; + Applet* m_foreground_requested_applet{}; + + // Foreground roots. + Applet* m_home_menu{}; + Applet* m_application{}; + + // Applet map by aruid. + std::map> m_applets{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1fa9cfbfb..47eedd73b 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -23,11 +23,7 @@ void LoopProcess(Core::System& system) { std::shared_ptr resource_manager = std::make_shared(system, firmware_settings); - // TODO: Remove this hack when am is emulated properly. resource_manager->Initialize(); - resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(), - true); - resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true); server_manager->RegisterNamedService( "hid", std::make_shared(system, resource_manager, firmware_settings)); diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/os/process.cpp similarity index 58% rename from src/core/hle/service/am/process.cpp rename to src/core/hle/service/os/process.cpp index 388d2045c..0dbadc315 100644 --- a/src/core/hle/service/am/process.cpp +++ b/src/core/hle/service/os/process.cpp @@ -3,66 +3,25 @@ #include "common/scope_exit.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/registered_cache.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/service/am/process.h" -#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/kernel/svc_types.h" +#include "core/hle/service/os/process.h" #include "core/loader/loader.h" -namespace Service::AM { +namespace Service { Process::Process(Core::System& system) : m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(), - m_program_id(), m_process_started() {} + m_process_started() {} Process::~Process() { this->Finalize(); } -bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) { +bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) { // First, ensure we are not holding another process. this->Finalize(); - // Get the filesystem controller. - auto& fsc = m_system.GetFileSystemController(); - - // Attempt to load program NCA. - const FileSys::RegisteredCache* bis_system{}; - FileSys::VirtualFile nca_raw{}; - - // Get the program NCA from built-in storage. - bis_system = fsc.GetSystemNANDContents(); - if (bis_system) { - nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); - } - - // Ensure we retrieved a program NCA. - if (!nca_raw) { - return false; - } - - // Ensure we have a suitable version. - if (minimum_key_generation > 0) { - FileSys::NCA nca(nca_raw); - if (nca.GetStatus() == Loader::ResultStatus::Success && - (nca.GetKeyGeneration() < minimum_key_generation || - nca.GetKeyGeneration() > maximum_key_generation)) { - LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, - nca.GetKeyGeneration()); - return false; - } - } - - // Get the appropriate loader to parse this NCA. - auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0); - - // Ensure we have a loader which can parse the NCA. - if (!app_loader) { - return false; - } - // Create the process. auto* const process = Kernel::KProcess::Create(m_system.Kernel()); Kernel::KProcess::Register(m_system.Kernel(), process); @@ -73,7 +32,8 @@ bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_k }; // Insert process modules into memory. - const auto [load_result, load_parameters] = app_loader->Load(*process, m_system); + const auto [load_result, load_parameters] = loader.Load(*process, m_system); + out_load_result = load_result; // Ensure loading was successful. if (load_result != Loader::ResultStatus::Success) { @@ -114,7 +74,6 @@ void Process::Finalize() { m_process = nullptr; m_main_thread_priority = 0; m_main_thread_stack_size = 0; - m_program_id = 0; m_process_started = false; } @@ -142,6 +101,31 @@ void Process::Terminate() { } } +void Process::ResetSignal() { + if (m_process) { + m_process->Reset(); + } +} + +bool Process::IsRunning() const { + if (m_process) { + const auto state = m_process->GetState(); + return state == Kernel::KProcess::State::Running || + state == Kernel::KProcess::State::RunningAttached || + state == Kernel::KProcess::State::DebugBreak; + } + + return false; +} + +bool Process::IsTerminated() const { + if (m_process) { + return m_process->IsTerminated(); + } + + return false; +} + u64 Process::GetProcessId() const { if (m_process) { return m_process->GetProcessId(); @@ -150,4 +134,19 @@ u64 Process::GetProcessId() const { return 0; } -} // namespace Service::AM +u64 Process::GetProgramId() const { + if (m_process) { + return m_process->GetProgramId(); + } + + return 0; +} + +void Process::Suspend(bool suspended) { + if (m_process) { + m_process->SetActivity(suspended ? Kernel::Svc::ProcessActivity::Paused + : Kernel::Svc::ProcessActivity::Runnable); + } +} + +} // namespace Service diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/os/process.h similarity index 62% rename from src/core/hle/service/am/process.h rename to src/core/hle/service/os/process.h index 4b8102fb6..9109b7d0a 100644 --- a/src/core/hle/service/am/process.h +++ b/src/core/hle/service/os/process.h @@ -3,38 +3,47 @@ #pragma once -#include "common/common_funcs.h" #include "common/common_types.h" -namespace Kernel { -class KProcess; -} - namespace Core { class System; } -namespace Service::AM { +namespace Loader { +class AppLoader; +enum class ResultStatus : u16; +} // namespace Loader + +namespace Kernel { +class KProcess; +} + +namespace Service { class Process { public: explicit Process(Core::System& system); ~Process(); - bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation); + bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result); void Finalize(); bool Run(); void Terminate(); + void Suspend(bool suspended); + void ResetSignal(); bool IsInitialized() const { return m_process != nullptr; } + + bool IsRunning() const; + bool IsTerminated() const; + u64 GetProcessId() const; - u64 GetProgramId() const { - return m_program_id; - } - Kernel::KProcess* GetProcess() const { + u64 GetProgramId() const; + + Kernel::KProcess* GetHandle() const { return m_process; } @@ -43,8 +52,7 @@ private: Kernel::KProcess* m_process{}; s32 m_main_thread_priority{}; u64 m_main_thread_stack_size{}; - u64 m_program_id{}; bool m_process_started{}; }; -} // namespace Service::AM +} // namespace Service diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8775369a4..6f7703fce 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -49,8 +49,7 @@ struct Memory::Impl { void SetCurrentPageTable(Kernel::KProcess& process) { current_page_table = &process.GetPageTable().GetImpl(); - if (std::addressof(process) == system.ApplicationProcess() && - Settings::IsFastmemEnabled()) { + if (process.IsApplication() && Settings::IsFastmemEnabled()) { current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); } else { current_page_table->fastmem_arena = nullptr; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c0c0a19b8..19aaff7c6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1461,7 +1461,6 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { OnPauseGame(); } else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) { auto_paused = false; - RequestGameResume(); OnStartGame(); } } @@ -1702,7 +1701,6 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { } else { if (!emu_thread->IsRunning() && auto_paused) { auto_paused = false; - RequestGameResume(); OnStartGame(); } } @@ -3457,7 +3455,6 @@ void GMainWindow::OnPauseContinueGame() { if (emu_thread->IsRunning()) { OnPauseGame(); } else { - RequestGameResume(); OnStartGame(); } } @@ -5013,10 +5010,6 @@ void GMainWindow::RequestGameExit() { system->GetAppletManager().RequestExit(); } -void GMainWindow::RequestGameResume() { - system->GetAppletManager().RequestResume(); -} - void GMainWindow::filterBarSetChecked(bool state) { ui->action_Show_Filter_Bar->setChecked(state); emit(OnToggleFilterBar()); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index fce643f3f..cb68f5a75 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -310,7 +310,6 @@ private: bool ConfirmChangeGame(); bool ConfirmForceLockedExit(); void RequestGameExit(); - void RequestGameResume(); void changeEvent(QEvent* event) override; void closeEvent(QCloseEvent* event) override;