summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/am
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service/am')
-rw-r--r--src/core/hle/service/am/am_types.h171
-rw-r--r--src/core/hle/service/am/applet.cpp63
-rw-r--r--src/core/hle/service/am/applet.h164
-rw-r--r--src/core/hle/service/am/applet_manager.cpp352
-rw-r--r--src/core/hle/service/am/applet_manager.h59
-rw-r--r--src/core/hle/service/am/hid_registration.cpp29
-rw-r--r--src/core/hle/service/am/hid_registration.h30
-rw-r--r--src/core/hle/service/am/library_applet_storage.cpp140
-rw-r--r--src/core/hle/service/am/library_applet_storage.h36
-rw-r--r--src/core/hle/service/am/managed_layer_holder.cpp59
-rw-r--r--src/core/hle/service/am/managed_layer_holder.h32
-rw-r--r--src/core/hle/service/am/process.cpp138
-rw-r--r--src/core/hle/service/am/process.h50
-rw-r--r--src/core/hle/service/am/system_buffer_manager.cpp49
-rw-r--r--src/core/hle/service/am/system_buffer_manager.h44
15 files changed, 1416 insertions, 0 deletions
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h
new file mode 100644
index 000000000..d0a237a7e
--- /dev/null
+++ b/src/core/hle/service/am/am_types.h
@@ -0,0 +1,171 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::AM {
+
+namespace Frontend {
+class FrontendApplet;
+}
+
+enum class AppletType {
+ Application,
+ LibraryApplet,
+ SystemApplet,
+};
+
+enum class GameplayRecordingState : u32 {
+ Disabled,
+ Enabled,
+};
+
+// This is nn::oe::FocusState
+enum class FocusState : u8 {
+ InFocus = 1,
+ NotInFocus = 2,
+ Background = 3,
+};
+
+// This is nn::oe::OperationMode
+enum class OperationMode : u8 {
+ Handheld = 0,
+ Docked = 1,
+};
+
+// This is nn::am::service::SystemButtonType
+enum class SystemButtonType {
+ None,
+ HomeButtonShortPressing,
+ HomeButtonLongPressing,
+ PowerButtonShortPressing,
+ PowerButtonLongPressing,
+ ShutdownSystem,
+ CaptureButtonShortPressing,
+ CaptureButtonLongPressing,
+};
+
+enum class SysPlatformRegion : s32 {
+ Global = 1,
+ Terra = 2,
+};
+
+struct AppletProcessLaunchReason {
+ u8 flag;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
+ "AppletProcessLaunchReason is an invalid size");
+
+enum class ScreenshotPermission : u32 {
+ Inherit = 0,
+ Enable = 1,
+ Disable = 2,
+};
+
+struct FocusHandlingMode {
+ bool unknown0;
+ bool unknown1;
+ bool unknown2;
+ bool unknown3;
+};
+
+enum class IdleTimeDetectionExtension : u32 {
+ Disabled = 0,
+ Extended = 1,
+ ExtendedUnsafe = 2,
+};
+
+enum class AppletId : u32 {
+ None = 0x00,
+ Application = 0x01,
+ OverlayDisplay = 0x02,
+ QLaunch = 0x03,
+ Starter = 0x04,
+ Auth = 0x0A,
+ Cabinet = 0x0B,
+ Controller = 0x0C,
+ DataErase = 0x0D,
+ Error = 0x0E,
+ NetConnect = 0x0F,
+ ProfileSelect = 0x10,
+ SoftwareKeyboard = 0x11,
+ MiiEdit = 0x12,
+ Web = 0x13,
+ Shop = 0x14,
+ PhotoViewer = 0x15,
+ Settings = 0x16,
+ OfflineWeb = 0x17,
+ LoginShare = 0x18,
+ WebAuth = 0x19,
+ MyPage = 0x1A,
+};
+
+enum class AppletProgramId : u64 {
+ QLaunch = 0x0100000000001000ull,
+ Auth = 0x0100000000001001ull,
+ Cabinet = 0x0100000000001002ull,
+ Controller = 0x0100000000001003ull,
+ DataErase = 0x0100000000001004ull,
+ Error = 0x0100000000001005ull,
+ NetConnect = 0x0100000000001006ull,
+ ProfileSelect = 0x0100000000001007ull,
+ SoftwareKeyboard = 0x0100000000001008ull,
+ MiiEdit = 0x0100000000001009ull,
+ Web = 0x010000000000100Aull,
+ Shop = 0x010000000000100Bull,
+ OverlayDisplay = 0x010000000000100Cull,
+ PhotoViewer = 0x010000000000100Dull,
+ Settings = 0x010000000000100Eull,
+ OfflineWeb = 0x010000000000100Full,
+ LoginShare = 0x0100000000001010ull,
+ WebAuth = 0x0100000000001011ull,
+ Starter = 0x0100000000001012ull,
+ MyPage = 0x0100000000001013ull,
+ MaxProgramId = 0x0100000000001FFFull,
+};
+
+enum class LibraryAppletMode : u32 {
+ AllForeground = 0,
+ Background = 1,
+ NoUI = 2,
+ BackgroundIndirectDisplay = 3,
+ AllForegroundInitiallyHidden = 4,
+};
+
+enum class CommonArgumentVersion : u32 {
+ Version0,
+ Version1,
+ Version2,
+ Version3,
+};
+
+enum class CommonArgumentSize : u32 {
+ Version3 = 0x20,
+};
+
+enum class ThemeColor : u32 {
+ BasicWhite = 0,
+ BasicBlack = 3,
+};
+
+struct CommonArguments {
+ CommonArgumentVersion arguments_version;
+ CommonArgumentSize size;
+ u32 library_version;
+ ThemeColor theme_color;
+ bool play_startup_sound;
+ u64 system_tick;
+};
+static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
+
+using AppletResourceUserId = u64;
+using ProgramId = u64;
+
+struct Applet;
+struct AppletStorageHolder;
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp
new file mode 100644
index 000000000..8f44fab33
--- /dev/null
+++ b/src/core/hle/service/am/applet.cpp
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet.h"
+
+namespace Service::AM {
+
+AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context)
+ : m_event(context) {}
+AppletStorageChannel::~AppletStorageChannel() = default;
+
+void AppletStorageChannel::PushData(std::shared_ptr<IStorage> storage) {
+ std::scoped_lock lk{m_lock};
+
+ m_data.emplace_back(std::move(storage));
+ m_event.Signal();
+}
+
+Result AppletStorageChannel::PopData(std::shared_ptr<IStorage>* out_storage) {
+ std::scoped_lock lk{m_lock};
+
+ SCOPE_EXIT({
+ if (m_data.empty()) {
+ m_event.Clear();
+ }
+ });
+
+ R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel);
+
+ *out_storage = std::move(m_data.front());
+ m_data.pop_front();
+
+ R_SUCCEED();
+}
+
+Kernel::KReadableEvent* AppletStorageChannel::GetEvent() {
+ return m_event.GetHandle();
+}
+
+AppletStorageHolder::AppletStorageHolder(Core::System& system)
+ : context(system, "AppletStorageHolder"), in_data(context), interactive_in_data(context),
+ out_data(context), interactive_out_data(context), state_changed_event(context) {}
+
+AppletStorageHolder::~AppletStorageHolder() = default;
+
+Applet::Applet(Core::System& system, std::unique_ptr<Process> 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) {
+
+ aruid = process->GetProcessId();
+ program_id = process->GetProgramId();
+}
+
+Applet::~Applet() = default;
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
new file mode 100644
index 000000000..9650a2615
--- /dev/null
+++ b/src/core/hle/service/am/applet.h
@@ -0,0 +1,164 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <list>
+#include <mutex>
+
+#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/event.h"
+#include "core/hle/service/kernel_helpers.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/hid_registration.h"
+#include "core/hle/service/am/managed_layer_holder.h"
+#include "core/hle/service/am/process.h"
+#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/system_buffer_manager.h"
+
+namespace Service::Nvnflinger {
+class FbShareBufferManager;
+class Nvnflinger;
+} // namespace Service::Nvnflinger
+
+namespace Service::AM {
+
+class AppletStorageChannel {
+public:
+ explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx);
+ ~AppletStorageChannel();
+
+ void PushData(std::shared_ptr<IStorage> storage);
+ Result PopData(std::shared_ptr<IStorage>* out_storage);
+ Kernel::KReadableEvent* GetEvent();
+
+private:
+ std::mutex m_lock{};
+ std::deque<std::shared_ptr<IStorage>> m_data{};
+ Event m_event;
+};
+
+struct AppletStorageHolder {
+ explicit AppletStorageHolder(Core::System& system);
+ ~AppletStorageHolder();
+
+ KernelHelpers::ServiceContext context;
+
+ AppletStorageChannel in_data;
+ AppletStorageChannel interactive_in_data;
+ AppletStorageChannel out_data;
+ AppletStorageChannel interactive_out_data;
+ Event state_changed_event;
+};
+
+struct Applet {
+ explicit Applet(Core::System& system, std::unique_ptr<Process> process_);
+ ~Applet();
+
+ // Lock
+ std::mutex lock{};
+
+ // Event creation helper
+ KernelHelpers::ServiceContext context;
+
+ // Applet message queue
+ AppletMessageQueue message_queue;
+
+ // Process
+ std::unique_ptr<Process> process;
+
+ // Creation state
+ AppletId applet_id{};
+ AppletResourceUserId aruid{};
+ AppletProcessLaunchReason launch_reason{};
+ AppletType type{};
+ ProgramId program_id{};
+ LibraryAppletMode library_applet_mode{};
+ s32 previous_program_index{-1};
+ ScreenshotPermission previous_screenshot_permission{ScreenshotPermission::Enable};
+
+ // hid state
+ HidRegistration hid_registration;
+
+ // vi state
+ SystemBufferManager system_buffer_manager{};
+ ManagedLayerHolder managed_layer_holder{};
+
+ // Applet common functions
+ Result terminate_result{};
+ s32 display_logical_width{};
+ s32 display_logical_height{};
+ Common::Rectangle<f32> display_magnification{0, 0, 1, 1};
+ bool home_button_double_click_enabled{};
+ bool home_button_short_pressed_blocked{};
+ bool home_button_long_pressed_blocked{};
+ bool vr_mode_curtain_required{};
+ bool sleep_required_by_high_temperature{};
+ bool sleep_required_by_low_battery{};
+ s32 cpu_boost_request_priority{-1};
+ bool handling_capture_button_short_pressed_message_enabled_for_applet{};
+ bool handling_capture_button_long_pressed_message_enabled_for_applet{};
+ u32 application_core_usage_mode{};
+
+ // Application functions
+ bool gameplay_recording_supported{};
+ GameplayRecordingState gameplay_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{};
+ APM::CpuBoostMode boost_mode{};
+ bool request_exit_to_library_applet_at_execute_next_program_enabled{};
+
+ // Channels
+ std::deque<std::vector<u8>> user_channel_launch_parameter{};
+ std::deque<std::vector<u8>> preselected_user_launch_parameter{};
+
+ // Caller applet
+ std::weak_ptr<Applet> caller_applet{};
+ std::shared_ptr<AppletStorageHolder> caller_applet_storage{};
+ 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{};
+ IdleTimeDetectionExtension idle_time_detection_extension{};
+ bool auto_sleep_disabled{};
+ u64 suspended_ticks{};
+ bool album_image_taken_notification_enabled{};
+ bool record_volume_muted{};
+
+ // Events
+ Event gpu_error_detected_event;
+ Event friend_invitation_storage_channel_event;
+ Event notification_storage_channel_event;
+ Event health_warning_disappeared_system_event;
+ Event acquired_sleep_lock_event;
+ Event pop_from_general_channel_event;
+ Event library_applet_launchable_event;
+ Event accumulated_suspended_tick_changed_event;
+ Event sleep_lock_event;
+
+ // Frontend state
+ std::shared_ptr<Frontend::FrontendApplet> frontend{};
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp
new file mode 100644
index 000000000..9f7ccfbf2
--- /dev/null
+++ b/src/core/hle/service/am/applet_manager.cpp
@@ -0,0 +1,352 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "common/uuid.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/applets/applet_cabinet.h"
+#include "core/hle/service/am/applets/applet_controller.h"
+#include "core/hle/service/am/applets/applet_mii_edit_types.h"
+#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::AM {
+
+namespace {
+
+constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA;
+
+struct LaunchParameterAccountPreselectedUser {
+ u32 magic;
+ u32 is_account_selected;
+ Common::UUID current_user;
+ INSERT_PADDING_BYTES(0x70);
+};
+static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
+
+AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system,
+ std::shared_ptr<Applet>& applet) {
+ applet->caller_applet_storage = std::make_shared<AppletStorageHolder>(system);
+ return applet->caller_applet_storage->in_data;
+}
+
+void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) {
+ const CommonArguments arguments{
+ .arguments_version = CommonArgumentVersion::Version3,
+ .size = CommonArgumentSize::Version3,
+ .library_version = 1,
+ .theme_color = ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ std::vector<u8> argument_data(sizeof(arguments));
+ std::vector<u8> settings_data{2};
+ std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data)));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(settings_data)));
+}
+
+void PushInShowController(Core::System& system, AppletStorageChannel& channel) {
+ const CommonArguments common_args = {
+ .arguments_version = CommonArgumentVersion::Version3,
+ .size = CommonArgumentSize::Version3,
+ .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
+ .theme_color = ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ Applets::ControllerSupportArgNew user_args = {
+ .header = {.player_count_min = 1,
+ .player_count_max = 4,
+ .enable_take_over_connection = true,
+ .enable_left_justify = false,
+ .enable_permit_joy_dual = true,
+ .enable_single_mode = false,
+ .enable_identification_color = false},
+ .identification_colors = {},
+ .enable_explain_text = false,
+ .explain_text = {},
+ };
+
+ Applets::ControllerSupportArgPrivate private_args = {
+ .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
+ .arg_size = sizeof(Applets::ControllerSupportArgNew),
+ .is_home_menu = true,
+ .flag_1 = true,
+ .mode = Applets::ControllerSupportMode::ShowControllerSupport,
+ .caller = Applets::ControllerSupportCaller::
+ Application, // switchbrew: Always zero except with
+ // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
+ // which sets this to the input param
+ .style_set = Core::HID::NpadStyleSet::None,
+ .joy_hold_type = 0,
+ };
+ std::vector<u8> common_args_data(sizeof(common_args));
+ std::vector<u8> private_args_data(sizeof(private_args));
+ std::vector<u8> user_args_data(sizeof(user_args));
+
+ std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
+ std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
+ std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
+
+ channel.PushData(std::make_shared<IStorage>(system, std::move(common_args_data)));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(private_args_data)));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(user_args_data)));
+}
+
+void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) {
+ const CommonArguments arguments{
+ .arguments_version = CommonArgumentVersion::Version3,
+ .size = CommonArgumentSize::Version3,
+ .library_version = static_cast<u32>(Applets::CabinetAppletVersion::Version1),
+ .theme_color = ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ const Applets::StartParamForAmiiboSettings amiibo_settings{
+ .param_1 = 0,
+ .applet_mode = system.GetAppletManager().GetCabinetMode(),
+ .flags = Applets::CabinetFlags::None,
+ .amiibo_settings_1 = 0,
+ .device_handle = 0,
+ .tag_info{},
+ .register_info{},
+ .amiibo_settings_3{},
+ };
+
+ std::vector<u8> argument_data(sizeof(arguments));
+ std::vector<u8> settings_data(sizeof(amiibo_settings));
+ std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
+ std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data)));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(settings_data)));
+}
+
+void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) {
+ struct MiiEditV3 {
+ Applets::MiiEditAppletInputCommon common;
+ Applets::MiiEditAppletInputV3 input;
+ };
+ static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size.");
+
+ MiiEditV3 mii_arguments{
+ .common =
+ {
+ .version = Applets::MiiEditAppletVersion::Version3,
+ .applet_mode = Applets::MiiEditAppletMode::ShowMiiEdit,
+ },
+ .input{},
+ };
+
+ std::vector<u8> argument_data(sizeof(mii_arguments));
+ std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments));
+
+ channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data)));
+}
+
+void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& channel) {
+ const CommonArguments arguments{
+ .arguments_version = CommonArgumentVersion::Version3,
+ .size = CommonArgumentSize::Version3,
+ .library_version = static_cast<u32>(Applets::SwkbdAppletVersion::Version524301),
+ .theme_color = ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ std::vector<char16_t> initial_string(0);
+
+ const Applets::SwkbdConfigCommon swkbd_config{
+ .type = Applets::SwkbdType::Qwerty,
+ .ok_text{},
+ .left_optional_symbol_key{},
+ .right_optional_symbol_key{},
+ .use_prediction = false,
+ .key_disable_flags{},
+ .initial_cursor_position = Applets::SwkbdInitialCursorPosition::Start,
+ .header_text{},
+ .sub_text{},
+ .guide_text{},
+ .max_text_length = 500,
+ .min_text_length = 0,
+ .password_mode = Applets::SwkbdPasswordMode::Disabled,
+ .text_draw_type = Applets::SwkbdTextDrawType::Box,
+ .enable_return_button = true,
+ .use_utf8 = false,
+ .use_blur_background = true,
+ .initial_string_offset{},
+ .initial_string_length = static_cast<u32>(initial_string.size()),
+ .user_dictionary_offset{},
+ .user_dictionary_entries{},
+ .use_text_check = false,
+ };
+
+ Applets::SwkbdConfigNew swkbd_config_new{};
+
+ std::vector<u8> argument_data(sizeof(arguments));
+ std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new));
+ std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t));
+
+ std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
+ std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config));
+ std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new,
+ sizeof(Applets::SwkbdConfigNew));
+ std::memcpy(work_buffer.data(), initial_string.data(),
+ swkbd_config.initial_string_length * sizeof(char16_t));
+
+ channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data)));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(swkbd_data)));
+ channel.PushData(std::make_shared<IStorage>(system, std::move(work_buffer)));
+}
+
+} // namespace
+
+AppletManager::AppletManager(Core::System& system) : m_system(system) {}
+AppletManager::~AppletManager() {
+ this->Reset();
+}
+
+void AppletManager::InsertApplet(std::shared_ptr<Applet> applet) {
+ std::scoped_lock lk{m_lock};
+
+ m_applets.emplace(applet->aruid, std::move(applet));
+}
+
+void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) {
+ std::shared_ptr<Applet> applet;
+ {
+ 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);
+ }
+
+ // Terminate process.
+ applet->process->Terminate();
+}
+
+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<Applet>(m_system, std::make_unique<Process>(m_system));
+
+ applet->aruid = aruid;
+ applet->program_id = params.program_id;
+ applet->applet_id = params.applet_id;
+ applet->type = params.applet_type;
+ applet->previous_program_index = params.previous_program_index;
+
+ // Push UserChannel data from previous application
+ if (params.launch_type == LaunchType::ApplicationInitiated) {
+ applet->user_channel_launch_parameter.swap(m_system.GetUserChannel());
+ }
+
+ // TODO: Read whether we need a preselected user from NACP?
+ // TODO: This can be done quite easily from loader
+ {
+ LaunchParameterAccountPreselectedUser lp{};
+
+ lp.magic = LaunchParameterAccountPreselectedUserMagic;
+ lp.is_account_selected = 1;
+
+ Account::ProfileManager profile_manager{};
+ const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
+ ASSERT(uuid.has_value() && uuid->IsValid());
+ lp.current_user = *uuid;
+
+ std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
+ std::memcpy(buffer.data(), &lp, buffer.size());
+
+ applet->preselected_user_launch_parameter.push_back(std::move(buffer));
+ }
+
+ // Starting from frontend, some applets require input data.
+ switch (applet->applet_id) {
+ case AppletId::Cabinet:
+ PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet));
+ break;
+ case AppletId::MiiEdit:
+ PushInShowMiiEditData(m_system, InitializeFakeCallerApplet(m_system, applet));
+ break;
+ case AppletId::PhotoViewer:
+ PushInShowAlbum(m_system, InitializeFakeCallerApplet(m_system, applet));
+ break;
+ case AppletId::SoftwareKeyboard:
+ PushInShowSoftwareKeyboard(m_system, InitializeFakeCallerApplet(m_system, applet));
+ break;
+ case AppletId::Controller:
+ PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet));
+ break;
+ default:
+ break;
+ }
+
+ // Applet was started by frontend, so it is foreground.
+ applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
+ applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
+ applet->focus_state = FocusState::InFocus;
+
+ this->InsertApplet(std::move(applet));
+}
+
+std::shared_ptr<Applet> 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;
+ }
+
+ 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();
+ }
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h
new file mode 100644
index 000000000..4875de309
--- /dev/null
+++ b/src/core/hle/service/am/applet_manager.h
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <mutex>
+
+#include "core/hle/service/am/applet.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::AM {
+
+enum class LaunchType {
+ FrontendInitiated,
+ ApplicationInitiated,
+};
+
+struct FrontendAppletParameters {
+ ProgramId program_id{};
+ AppletId applet_id{};
+ AppletType applet_type{};
+ LaunchType launch_type{};
+ s32 program_index{};
+ s32 previous_program_index{-1};
+};
+
+class AppletManager {
+public:
+ explicit AppletManager(Core::System& system);
+ ~AppletManager();
+
+ void InsertApplet(std::shared_ptr<Applet> applet);
+ void TerminateAndRemoveApplet(AppletResourceUserId aruid);
+
+ void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid,
+ const FrontendAppletParameters& params);
+ std::shared_ptr<Applet> GetByAppletResourceUserId(AppletResourceUserId aruid) const;
+
+ void Reset();
+
+ void RequestExit();
+ void RequestResume();
+ void OperationModeChanged();
+ void FocusStateChanged();
+
+private:
+ Core::System& m_system;
+
+ mutable std::mutex m_lock{};
+ std::map<AppletResourceUserId, std::shared_ptr<Applet>> m_applets{};
+
+ // AudioController state goes here
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp
new file mode 100644
index 000000000..b9426f7b6
--- /dev/null
+++ b/src/core/hle/service/am/hid_registration.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#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/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::IHidServer>("hid");
+
+ if (m_process.IsInitialized()) {
+ m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
+ true);
+ }
+}
+
+HidRegistration::~HidRegistration() {
+ if (m_process.IsInitialized()) {
+ m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId(
+ m_process.GetProcessId());
+ }
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h
new file mode 100644
index 000000000..8a732349c
--- /dev/null
+++ b/src/core/hle/service/am/hid_registration.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+
+namespace Core {
+class System;
+}
+
+namespace Service::HID {
+class IHidServer;
+}
+
+namespace Service::AM {
+
+class Process;
+
+class HidRegistration {
+public:
+ explicit HidRegistration(Core::System& system, Process& process);
+ ~HidRegistration();
+
+private:
+ Process& m_process;
+ std::shared_ptr<Service::HID::IHidServer> m_hid_server;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp
new file mode 100644
index 000000000..46e6c0111
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_storage.cpp
@@ -0,0 +1,140 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/library_applet_storage.h"
+#include "core/memory.h"
+
+namespace Service::AM {
+
+namespace {
+
+Result ValidateOffset(s64 offset, size_t size, size_t data_size) {
+ R_UNLESS(offset >= 0, AM::ResultInvalidOffset);
+
+ const size_t begin = offset;
+ const size_t end = begin + size;
+
+ R_UNLESS(begin <= end && end <= data_size, AM::ResultInvalidOffset);
+ R_SUCCEED();
+}
+
+class BufferLibraryAppletStorage final : public LibraryAppletStorage {
+public:
+ explicit BufferLibraryAppletStorage(std::vector<u8>&& data) : m_data(std::move(data)) {}
+ ~BufferLibraryAppletStorage() = default;
+
+ Result Read(s64 offset, void* buffer, size_t size) override {
+ R_TRY(ValidateOffset(offset, size, m_data.size()));
+
+ std::memcpy(buffer, m_data.data() + offset, size);
+
+ R_SUCCEED();
+ }
+
+ Result Write(s64 offset, const void* buffer, size_t size) override {
+ R_TRY(ValidateOffset(offset, size, m_data.size()));
+
+ std::memcpy(m_data.data() + offset, buffer, size);
+
+ R_SUCCEED();
+ }
+
+ s64 GetSize() override {
+ return m_data.size();
+ }
+
+ Kernel::KTransferMemory* GetHandle() override {
+ return nullptr;
+ }
+
+private:
+ std::vector<u8> m_data;
+};
+
+class TransferMemoryLibraryAppletStorage : public LibraryAppletStorage {
+public:
+ explicit TransferMemoryLibraryAppletStorage(Core::Memory::Memory& memory,
+ Kernel::KTransferMemory* trmem, bool is_writable,
+ s64 size)
+ : m_memory(memory), m_trmem(trmem), m_is_writable(is_writable), m_size(size) {
+ m_trmem->Open();
+ }
+
+ ~TransferMemoryLibraryAppletStorage() {
+ m_trmem->Close();
+ m_trmem = nullptr;
+ }
+
+ Result Read(s64 offset, void* buffer, size_t size) override {
+ R_TRY(ValidateOffset(offset, size, m_size));
+
+ m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size);
+
+ R_SUCCEED();
+ }
+
+ Result Write(s64 offset, const void* buffer, size_t size) override {
+ R_UNLESS(m_is_writable, ResultUnknown);
+ R_TRY(ValidateOffset(offset, size, m_size));
+
+ m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size);
+
+ R_SUCCEED();
+ }
+
+ s64 GetSize() override {
+ return m_size;
+ }
+
+ Kernel::KTransferMemory* GetHandle() override {
+ return nullptr;
+ }
+
+protected:
+ Core::Memory::Memory& m_memory;
+ Kernel::KTransferMemory* m_trmem;
+ bool m_is_writable;
+ s64 m_size;
+};
+
+class HandleLibraryAppletStorage : public TransferMemoryLibraryAppletStorage {
+public:
+ explicit HandleLibraryAppletStorage(Core::Memory::Memory& memory,
+ Kernel::KTransferMemory* trmem, s64 size)
+ : TransferMemoryLibraryAppletStorage(memory, trmem, true, size) {}
+ ~HandleLibraryAppletStorage() = default;
+
+ Kernel::KTransferMemory* GetHandle() override {
+ return m_trmem;
+ }
+};
+
+} // namespace
+
+LibraryAppletStorage::~LibraryAppletStorage() = default;
+
+std::vector<u8> LibraryAppletStorage::GetData() {
+ std::vector<u8> data(this->GetSize());
+ this->Read(0, data.data(), data.size());
+ return data;
+}
+
+std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data) {
+ return std::make_shared<BufferLibraryAppletStorage>(std::move(data));
+}
+
+std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory,
+ Kernel::KTransferMemory* trmem,
+ bool is_writable, s64 size) {
+ return std::make_shared<TransferMemoryLibraryAppletStorage>(memory, trmem, is_writable, size);
+}
+
+std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory,
+ Kernel::KTransferMemory* trmem,
+ s64 size) {
+ return std::make_shared<HandleLibraryAppletStorage>(memory, trmem, size);
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_storage.h b/src/core/hle/service/am/library_applet_storage.h
new file mode 100644
index 000000000..7f53f3a9c
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_storage.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace Kernel {
+class KTransferMemory;
+}
+
+namespace Service::AM {
+
+class LibraryAppletStorage {
+public:
+ virtual ~LibraryAppletStorage();
+ virtual Result Read(s64 offset, void* buffer, size_t size) = 0;
+ virtual Result Write(s64 offset, const void* buffer, size_t size) = 0;
+ virtual s64 GetSize() = 0;
+ virtual Kernel::KTransferMemory* GetHandle() = 0;
+
+ std::vector<u8> GetData();
+};
+
+std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data);
+std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory,
+ Kernel::KTransferMemory* trmem,
+ bool is_writable, s64 size);
+std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory,
+ Kernel::KTransferMemory* trmem, s64 size);
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp
new file mode 100644
index 000000000..61eb8641a
--- /dev/null
+++ b/src/core/hle/service/am/managed_layer_holder.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/managed_layer_holder.h"
+#include "core/hle/service/nvnflinger/nvnflinger.h"
+
+namespace Service::AM {
+
+ManagedLayerHolder::ManagedLayerHolder() = default;
+ManagedLayerHolder::~ManagedLayerHolder() {
+ if (!m_nvnflinger) {
+ return;
+ }
+
+ for (const auto& layer : m_managed_display_layers) {
+ m_nvnflinger->DestroyLayer(layer);
+ }
+
+ for (const auto& layer : m_managed_display_recording_layers) {
+ m_nvnflinger->DestroyLayer(layer);
+ }
+
+ m_nvnflinger = nullptr;
+}
+
+void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) {
+ m_nvnflinger = nvnflinger;
+}
+
+void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) {
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
+ const auto display_id = m_nvnflinger->OpenDisplay("Default");
+ const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
+
+ m_managed_display_layers.emplace(*layer_id);
+
+ *out_layer = *layer_id;
+}
+
+void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer,
+ u64* out_recording_layer) {
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
+ // This calls nn::vi::CreateRecordingLayer() which creates another layer.
+ // Currently we do not support more than 1 layer per display, output 1 layer id for now.
+ // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
+ // side effects.
+ // TODO: Support multiple layers
+ const auto display_id = m_nvnflinger->OpenDisplay("Default");
+ const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
+
+ m_managed_display_layers.emplace(*layer_id);
+
+ *out_layer = *layer_id;
+ *out_recording_layer = 0;
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h
new file mode 100644
index 000000000..f7fe03f24
--- /dev/null
+++ b/src/core/hle/service/am/managed_layer_holder.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <set>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::Nvnflinger {
+class Nvnflinger;
+}
+
+namespace Service::AM {
+
+class ManagedLayerHolder {
+public:
+ ManagedLayerHolder();
+ ~ManagedLayerHolder();
+
+ void Initialize(Nvnflinger::Nvnflinger* nvnflinger);
+ void CreateManagedDisplayLayer(u64* out_layer);
+ void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
+
+private:
+ Nvnflinger::Nvnflinger* m_nvnflinger{};
+ std::set<u64> m_managed_display_layers{};
+ std::set<u64> m_managed_display_recording_layers{};
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp
new file mode 100644
index 000000000..16b685f86
--- /dev/null
+++ b/src/core/hle/service/am/process.cpp
@@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.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/loader/loader.h"
+
+namespace Service::AM {
+
+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() {}
+
+Process::~Process() {
+ this->Finalize();
+}
+
+bool Process::Initialize(u64 program_id) {
+ // 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{};
+
+ // Get the program NCA from built-in storage.
+ bis_system = fsc.GetSystemNANDContents();
+ if (bis_system) {
+ nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
+ }
+
+ // Ensure we retrieved a program NCA.
+ if (!nca) {
+ return false;
+ }
+
+ // Get the appropriate loader to parse this NCA.
+ auto app_loader = Loader::GetLoader(m_system, nca, 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);
+
+ // On exit, ensure we free the additional reference to the process.
+ SCOPE_EXIT({ process->Close(); });
+
+ // Insert process modules into memory.
+ const auto [load_result, load_parameters] = app_loader->Load(*process, m_system);
+
+ // Ensure loading was successful.
+ if (load_result != Loader::ResultStatus::Success) {
+ return false;
+ }
+
+ // TODO: remove this, kernel already tracks this
+ m_system.Kernel().AppendNewProcess(process);
+
+ // Note the load parameters from NPDM.
+ m_main_thread_priority = load_parameters->main_thread_priority;
+ m_main_thread_stack_size = load_parameters->main_thread_stack_size;
+
+ // This process has not started yet.
+ m_process_started = false;
+
+ // Take ownership of the process object.
+ m_process = process;
+ m_process->Open();
+
+ // We succeeded.
+ return true;
+}
+
+void Process::Finalize() {
+ // Terminate, if we are currently holding a process.
+ this->Terminate();
+
+ // Close the process.
+ if (m_process) {
+ m_process->Close();
+
+ // TODO: remove this, kernel already tracks this
+ m_system.Kernel().RemoveProcess(m_process);
+ }
+
+ // Clean up.
+ m_process = nullptr;
+ m_main_thread_priority = 0;
+ m_main_thread_stack_size = 0;
+ m_program_id = 0;
+ m_process_started = false;
+}
+
+bool Process::Run() {
+ // If we already started the process, don't start again.
+ if (m_process_started) {
+ return false;
+ }
+
+ // Start.
+ if (m_process) {
+ m_process->Run(m_main_thread_priority, m_main_thread_stack_size);
+ }
+
+ // Mark as started.
+ m_process_started = true;
+
+ // We succeeded.
+ return true;
+}
+
+void Process::Terminate() {
+ if (m_process) {
+ m_process->Terminate();
+ }
+}
+
+u64 Process::GetProcessId() const {
+ if (m_process) {
+ return m_process->GetProcessId();
+ }
+
+ return 0;
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h
new file mode 100644
index 000000000..4b908ade4
--- /dev/null
+++ b/src/core/hle/service/am/process.h
@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Kernel {
+class KProcess;
+}
+
+namespace Core {
+class System;
+}
+
+namespace Service::AM {
+
+class Process {
+public:
+ explicit Process(Core::System& system);
+ ~Process();
+
+ bool Initialize(u64 program_id);
+ void Finalize();
+
+ bool Run();
+ void Terminate();
+
+ bool IsInitialized() const {
+ return m_process != nullptr;
+ }
+ u64 GetProcessId() const;
+ u64 GetProgramId() const {
+ return m_program_id;
+ }
+ Kernel::KProcess* GetProcess() const {
+ return m_process;
+ }
+
+private:
+ Core::System& m_system;
+ 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
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp
new file mode 100644
index 000000000..7211ef488
--- /dev/null
+++ b/src/core/hle/service/am/system_buffer_manager.cpp
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/system_buffer_manager.h"
+#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
+#include "core/hle/service/nvnflinger/nvnflinger.h"
+
+namespace Service::AM {
+
+SystemBufferManager::SystemBufferManager() = default;
+
+SystemBufferManager::~SystemBufferManager() {
+ if (!m_nvnflinger) {
+ return;
+ }
+
+ // Clean up shared layers.
+ if (m_buffer_sharing_enabled) {
+ }
+}
+
+bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
+ AppletId applet_id) {
+ if (m_nvnflinger) {
+ return m_buffer_sharing_enabled;
+ }
+
+ m_process = process;
+ m_nvnflinger = nvnflinger;
+ m_buffer_sharing_enabled = false;
+ m_system_shared_buffer_id = 0;
+ m_system_shared_layer_id = 0;
+
+ if (applet_id <= AppletId::Application) {
+ return false;
+ }
+
+ const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
+ const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
+ &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id);
+
+ if (res.IsSuccess()) {
+ m_buffer_sharing_enabled = true;
+ }
+
+ return m_buffer_sharing_enabled;
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h
new file mode 100644
index 000000000..c60d73416
--- /dev/null
+++ b/src/core/hle/service/am/system_buffer_manager.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <set>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+#include "core/hle/service/am/am_types.h"
+
+namespace Kernel {
+class KProcess;
+}
+
+namespace Service::Nvnflinger {
+class Nvnflinger;
+}
+
+namespace Service::AM {
+
+class SystemBufferManager {
+public:
+ SystemBufferManager();
+ ~SystemBufferManager();
+
+ bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id);
+
+ void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
+ u64* out_system_shared_layer_id) {
+ *out_system_shared_buffer_id = m_system_shared_buffer_id;
+ *out_system_shared_layer_id = m_system_shared_layer_id;
+ }
+
+private:
+ Kernel::KProcess* m_process{};
+ Nvnflinger::Nvnflinger* m_nvnflinger{};
+ bool m_buffer_sharing_enabled{};
+ u64 m_system_shared_buffer_id{};
+ u64 m_system_shared_layer_id{};
+};
+
+} // namespace Service::AM