am: add new datatypes for per-applet state

This commit is contained in:
Liam 2024-01-02 19:22:02 -05:00
parent 7de6b41030
commit a7e9d7842d
19 changed files with 1496 additions and 1 deletions

View file

@ -393,8 +393,13 @@ add_library(core STATIC
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/am_results.h
hle/service/am/am_types.h
hle/service/am/applet.cpp
hle/service/am/applet.h
hle/service/am/applet_ae.cpp
hle/service/am/applet_ae.h
hle/service/am/applet_manager.cpp
hle/service/am/applet_manager.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/applets/applet_cabinet.cpp
@ -438,6 +443,8 @@ add_library(core STATIC
hle/service/am/display_controller.h
hle/service/am/global_state_controller.cpp
hle/service/am/global_state_controller.h
hle/service/am/hid_registration.cpp
hle/service/am/hid_registration.h
hle/service/am/home_menu_functions.cpp
hle/service/am/home_menu_functions.h
hle/service/am/idle.cpp
@ -450,16 +457,24 @@ add_library(core STATIC
hle/service/am/library_applet_proxy.h
hle/service/am/library_applet_self_accessor.cpp
hle/service/am/library_applet_self_accessor.h
hle/service/am/library_applet_storage.cpp
hle/service/am/library_applet_storage.h
hle/service/am/lock_accessor.cpp
hle/service/am/lock_accessor.h
hle/service/am/managed_layer_holder.cpp
hle/service/am/managed_layer_holder.h
hle/service/am/omm.cpp
hle/service/am/omm.h
hle/service/am/process_winding_controller.cpp
hle/service/am/process_winding_controller.h
hle/service/am/process.cpp
hle/service/am/process.h
hle/service/am/self_controller.cpp
hle/service/am/self_controller.h
hle/service/am/system_applet_proxy.cpp
hle/service/am/system_applet_proxy.h
hle/service/am/system_buffer_manager.cpp
hle/service/am/system_buffer_manager.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
hle/service/am/storage_accessor.cpp
@ -531,6 +546,8 @@ add_library(core STATIC
hle/service/es/es.h
hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h
hle/service/event.cpp
hle/service/event.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service {
Event::Event(KernelHelpers::ServiceContext& ctx) {
m_event = ctx.CreateEvent("Event");
}
Event::~Event() {
m_event->GetReadableEvent().Close();
m_event->Close();
}
void Event::Signal() {
m_event->Signal();
}
void Event::Clear() {
m_event->Clear();
}
Kernel::KReadableEvent* Event::GetHandle() {
return &m_event->GetReadableEvent();
}
} // namespace Service

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service {
namespace KernelHelpers {
class ServiceContext;
}
class Event {
public:
explicit Event(KernelHelpers::ServiceContext& ctx);
~Event();
void Signal();
void Clear();
Kernel::KReadableEvent* GetHandle();
private:
Kernel::KEvent* m_event;
};
} // namespace Service

View file

@ -128,7 +128,7 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
// Ensure we maintain a clean state on failure.
ON_RESULT_FAILURE {
ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)));
R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd));
};
// Assign the allocated memory to the handle.