Implement app management support (suspend, resume, close, etc) (#6322)

This commit is contained in:
Steveice10 2023-03-09 15:44:26 -08:00 committed by GitHub
parent d2caf2d386
commit c96f54f022
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1043 additions and 110 deletions

View file

@ -272,6 +272,7 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer)
case StateChange::Sleep:
LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware");
UNIMPLEMENTED();
AudioPipeWriteStructAddresses();
dsp_state = DspState::Sleeping;
break;
default:
@ -438,7 +439,7 @@ bool DspHle::Impl::Tick() {
parent.OutputFrame(std::move(current_frame));
return true;
return GetDspState() == DspState::On;
}
void DspHle::Impl::AudioTickCallback(s64 cycles_late) {

View file

@ -423,11 +423,6 @@ void ConfigureInput::OnHotkeysChanged(QList<QKeySequence> new_key_list) {
QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() {
QList<QKeySequence> list;
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
// TODO(adityaruplaha): Add home button to list when we finally emulate it
if (button == Settings::NativeButton::Home) {
continue;
}
const auto& button_param = buttons_param[button];
if (button_param.Get("engine", "") == "keyboard") {
list << QKeySequence(button_param.Get("code", 0));

View file

@ -85,6 +85,7 @@
#include "core/frontend/applets/default_applets.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h"
@ -318,6 +319,8 @@ void GMainWindow::InitializeWidgets() {
updater = new Updater(this);
UISettings::values.updater_found = updater->HasUpdater();
UpdateBootHomeMenuState();
// Create status bar
message_label = new QLabel();
// Configured separately for left alignment
@ -741,6 +744,7 @@ void GMainWindow::ConnectMenuEvents() {
// File
connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA);
connect_menu(ui->action_Boot_Home_Menu, &GMainWindow::OnMenuBootHomeMenu);
connect_menu(ui->action_Exit, &QMainWindow::close);
connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo);
connect_menu(ui->action_Remove_Amiibo, &GMainWindow::OnRemoveAmiibo);
@ -1601,6 +1605,20 @@ void GMainWindow::OnMenuInstallCIA() {
InstallCIA(filepaths);
}
static std::string GetHomeMenuPath() {
static const std::array<u64, 7> home_menu_tids = {
0x0004003000008202, 0x0004003000008F02, 0x0004003000009802, 0x0004003000009802,
0x000400300000A102, 0x000400300000A902, 0x000400300000B102};
Service::CFG::Module cfg{};
return Service::AM::GetTitleContentPath(Service::FS::MediaType::NAND,
home_menu_tids[cfg.GetRegionValue()]);
}
void GMainWindow::OnMenuBootHomeMenu() {
BootGame(QString::fromStdString(GetHomeMenuPath()));
}
void GMainWindow::InstallCIA(QStringList filepaths) {
ui->action_Install_CIA->setEnabled(false);
game_list->SetDirectoryWatcherEnabled(false);
@ -1951,6 +1969,7 @@ void GMainWindow::OnConfigure() {
setMouseTracking(false);
}
UpdateSecondaryWindowVisibility();
UpdateBootHomeMenuState();
} else {
Settings::values.input_profiles = old_input_profiles;
Settings::values.touch_from_button_maps = old_touch_from_button_maps;
@ -2244,6 +2263,12 @@ void GMainWindow::UpdateStatusBar() {
emu_frametime_label->setVisible(true);
}
void GMainWindow::UpdateBootHomeMenuState() {
const std::string home_menu_path = GetHomeMenuPath();
ui->action_Boot_Home_Menu->setEnabled(!home_menu_path.empty() &&
FileUtil::Exists(GetHomeMenuPath()));
}
void GMainWindow::HideMouseCursor() {
if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) {
mouse_hide_timer.stop();

View file

@ -192,6 +192,7 @@ private slots:
void OnConfigurePerGame();
void OnMenuLoadFile();
void OnMenuInstallCIA();
void OnMenuBootHomeMenu();
void OnUpdateProgress(std::size_t written, std::size_t total);
void OnCIAInstallReport(Service::AM::InstallStatus status, QString filepath);
void OnCIAInstallFinished();
@ -238,6 +239,7 @@ private slots:
private:
Q_INVOKABLE void OnMoviePlaybackCompleted();
void UpdateStatusBar();
void UpdateBootHomeMenuState();
void LoadTranslation();
void UpdateWindowTitle();
void UpdateUISettings();

View file

@ -66,6 +66,7 @@
</widget>
<addaction name="action_Load_File"/>
<addaction name="action_Install_CIA"/>
<addaction name="action_Boot_Home_Menu"/>
<addaction name="separator"/>
<addaction name="menu_recent_files"/>
<addaction name="separator"/>
@ -209,6 +210,11 @@
<string>Install CIA...</string>
</property>
</action>
<action name="action_Boot_Home_Menu">
<property name="text">
<string>Boot Home Menu</string>
</property>
</action>
<action name="action_Exit">
<property name="text">
<string>E&amp;xit</string>

View file

@ -56,6 +56,11 @@ void Apply() {
hid->ReloadInputDevices();
}
auto apt = Service::APT::GetModule(system);
if (apt) {
apt->GetAppletManager()->ReloadInputDevices();
}
auto sm = system.ServiceManager();
auto ir_user = sm.GetService<Service::IR::IR_USER>("ir:USER");
if (ir_user)

View file

@ -70,7 +70,7 @@ ResultCode Applet::Create(Service::APT::AppletId id, Service::APT::AppletId pare
}
Service::APT::AppletAttributes attributes;
attributes.applet_pos.Assign(static_cast<u32>(Service::APT::AppletPos::AutoLibrary));
attributes.applet_pos.Assign(Service::APT::AppletPos::AutoLibrary);
attributes.is_home_menu.Assign(false);
const auto lock_handle_data = manager->GetLockHandle(attributes);

View file

@ -133,6 +133,12 @@ public:
std::shared_ptr<Process> CreateProcess(std::shared_ptr<CodeSet> code_set);
/**
* Removes a process from the kernel process list
* @param process Process to remove
*/
void RemoveProcess(std::shared_ptr<Process> process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param name The friendly name desired for the thread

View file

@ -79,6 +79,10 @@ std::shared_ptr<Process> KernelSystem::CreateProcess(std::shared_ptr<CodeSet> co
return process;
}
void KernelSystem::RemoveProcess(std::shared_ptr<Process> process) {
std::erase(process_list, process);
}
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
for (std::size_t i = 0; i < len; ++i) {
u32 descriptor = kernel_caps[i];
@ -208,9 +212,6 @@ void Process::Exit() {
if (plgldr) {
plgldr->OnProcessExit(*this, kernel);
}
// Clear the process's open handles.
handle_table.Clear();
}
VAddr Process::GetLinearHeapAreaAddress() const {
@ -474,6 +475,8 @@ Kernel::Process::Process(KernelSystem& kernel)
kernel.memory.RegisterPageTable(vm_manager.page_table);
}
Kernel::Process::~Process() {
LOG_INFO(Kernel, "Cleaning up process {}", process_id);
// Release all objects this process owns first so that their potential destructor can do clean
// up with this process before further destruction.
// TODO(wwylele): explicitly destroy or invalidate objects this process owns (threads, shared

View file

@ -565,6 +565,9 @@ void SVC::ExitProcess() {
// Kill the current thread
kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
// Remove kernel reference to process so it can be cleaned up.
kernel.RemoveProcess(current_process);
system.PrepareReschedule();
}

View file

@ -1140,6 +1140,7 @@ void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) {
// TODO(shinyquagsire23): Read tickets for this instead?
bool has_rights =
FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::NAND, tid, content_index)) ||
FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);

View file

@ -4,17 +4,23 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/frontend/input.h"
#include "core/hle/applets/applet.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/apt/applet_manager.h"
#include "core/hle/service/apt/errors.h"
#include "core/hle/service/apt/ns.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/gsp/gsp_gpu.h"
#include "video_core/utils.h"
SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager)
namespace Service::APT {
/// The interval at which the home button update callback will be called, 16.6ms
static constexpr u64 home_button_update_interval_us = 16666;
struct AppletTitleData {
// There are two possible applet ids for each applet.
std::array<AppletId, 2> applet_ids;
@ -108,6 +114,14 @@ static u64 GetTitleIdForApplet(AppletId id, u32 region_value) {
return itr->title_ids[region_value];
}
static bool IsSystemAppletId(AppletId applet_id) {
return (static_cast<u32>(applet_id) & static_cast<u32>(AppletId::AnySystemApplet)) != 0;
}
static bool IsApplicationAppletId(AppletId applet_id) {
return (static_cast<u32>(applet_id) & static_cast<u32>(AppletId::Application)) != 0;
}
AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) {
if (id == AppletId::Application) {
if (GetAppletSlot(AppletSlot::Application)->applet_id != AppletId::None)
@ -133,7 +147,7 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) {
if (slot_data->applet_id == AppletId::None)
return AppletSlot::Error;
auto applet_pos = static_cast<AppletPos>(slot_data->attributes.applet_pos.Value());
auto applet_pos = slot_data->attributes.applet_pos.Value();
if ((id == AppletId::AnyLibraryApplet && applet_pos == AppletPos::Library) ||
(id == AppletId::AnySysLibraryApplet && applet_pos == AppletPos::SysLibrary))
return AppletSlot::LibraryApplet;
@ -163,11 +177,11 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromAttributes(AppletAttri
AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
auto applet_pos = attributes.applet_pos;
if (applet_pos >= applet_position_slots.size())
auto applet_pos_value = static_cast<u32>(attributes.applet_pos.Value());
if (applet_pos_value >= applet_position_slots.size())
return AppletSlot::Error;
auto slot = applet_position_slots[applet_pos];
auto slot = applet_position_slots[applet_pos_value];
if (slot == AppletSlot::Error)
return AppletSlot::Error;
@ -212,13 +226,35 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) {
// Otherwise, send the parameter the LLE way.
next_parameter = parameter;
if (parameter.signal == SignalType::RequestForSysApplet) {
// APT handles RequestForSysApplet messages itself.
LOG_DEBUG(Service_APT, "Replying to RequestForSysApplet from {:03X}",
parameter.sender_id);
if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) {
SendCaptureBufferInfo(parameter.buffer);
CaptureFrameBuffers();
}
next_parameter->sender_id = parameter.destination_id;
next_parameter->destination_id = parameter.sender_id;
next_parameter->signal = SignalType::Response;
next_parameter->buffer.clear();
next_parameter->object = nullptr;
} else if (IsSystemAppletId(parameter.sender_id) &&
IsApplicationAppletId(parameter.destination_id) && parameter.object) {
// When a message is sent from a system applet to an application, APT
// replaces its object with the zero handle.
next_parameter->object = nullptr;
}
// Signal the event to let the receiver know that a new parameter is ready to be read
auto slot = GetAppletSlotFromId(parameter.destination_id);
auto slot = GetAppletSlotFromId(next_parameter->destination_id);
if (slot != AppletSlot::Error) {
GetAppletSlot(slot)->parameter_event->Signal();
} else {
LOG_DEBUG(Service_APT, "No applet was registered with ID {:03X}",
parameter.destination_id);
next_parameter->destination_id);
}
}
}
@ -261,6 +297,10 @@ ResultVal<MessageParameter> AppletManager::GlanceParameter(AppletId app_id) {
ResultVal<MessageParameter> AppletManager::ReceiveParameter(AppletId app_id) {
auto result = GlanceParameter(app_id);
if (result.Succeeded()) {
LOG_DEBUG(Service_APT,
"Received parameter from {:03X} to {:03X} with signal {:08X} and size {:08X}",
result->sender_id, result->destination_id, result->signal, result->buffer.size());
// Clear the parameter
next_parameter = {};
}
@ -282,13 +322,13 @@ bool AppletManager::CancelParameter(bool check_sender, AppletId sender_appid, bo
ResultVal<AppletManager::GetLockHandleResult> AppletManager::GetLockHandle(
AppletAttributes attributes) {
auto corrected_attributes = attributes;
if (attributes.applet_pos == static_cast<u32>(AppletPos::Library) ||
attributes.applet_pos == static_cast<u32>(AppletPos::SysLibrary) ||
attributes.applet_pos == static_cast<u32>(AppletPos::AutoLibrary)) {
if (attributes.applet_pos == AppletPos::Library ||
attributes.applet_pos == AppletPos::SysLibrary ||
attributes.applet_pos == AppletPos::AutoLibrary) {
auto corrected_pos = last_library_launcher_slot == AppletSlot::Application
? AppletPos::Library
: AppletPos::SysLibrary;
corrected_attributes.applet_pos.Assign(static_cast<u32>(corrected_pos));
corrected_attributes.applet_pos.Assign(corrected_pos);
LOG_DEBUG(Service_APT, "Corrected applet attributes from {:08X} to {:08X}", attributes.raw,
corrected_attributes.raw);
}
@ -350,6 +390,13 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) {
auto slot_data = GetAppletSlot(slot);
slot_data->registered = true;
if (slot_data->attributes.applet_pos == AppletPos::System &&
slot_data->attributes.is_home_menu) {
slot_data->attributes.raw |= attributes.raw;
LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.",
slot_data->attributes.raw);
}
// Send any outstanding parameters to the newly-registered application
if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) {
// TODO: Real APT would loop trying to send the parameter until it succeeds,
@ -366,6 +413,35 @@ bool AppletManager::IsRegistered(AppletId app_id) {
return slot != AppletSlot::Error && GetAppletSlot(slot)->registered;
}
ResultVal<Notification> AppletManager::InquireNotification(AppletId app_id) {
auto slot = GetAppletSlotFromId(app_id);
if (slot != AppletSlot::Error) {
auto slot_data = GetAppletSlot(slot);
if (slot_data->registered) {
auto notification = slot_data->notification;
slot_data->notification = Notification::None;
return MakeResult<Notification>(notification);
}
}
return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
ErrorLevel::Status);
}
ResultCode AppletManager::SendNotification(Notification notification) {
if (active_slot != AppletSlot::Error) {
const auto slot_data = GetAppletSlot(active_slot);
if (slot_data->registered) {
slot_data->notification = notification;
slot_data->notification_event->Signal();
return RESULT_SUCCESS;
}
}
return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
ErrorLevel::Status};
}
ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) {
// The real APT service returns an error if there's a pending APT parameter when this function
// is called.
@ -589,18 +665,281 @@ ResultCode AppletManager::CloseSystemApplet(std::shared_ptr<Kernel::Object> obje
"Attempting to close a system applet from a non-system applet.");
auto slot = GetAppletSlot(active_slot);
auto closed_applet_id = slot->applet_id;
active_slot = last_system_launcher_slot;
// TODO: Send a parameter to the application only if the application ordered the applet to
// close.
// TODO: Terminate the running applet title
slot->Reset();
if (ordered_to_close_sys_applet) {
ordered_to_close_sys_applet = false;
active_slot = AppletSlot::Application;
CancelAndSendParameter({
.sender_id = closed_applet_id,
.destination_id = AppletId::Application,
.signal = SignalType::WakeupByExit,
.object = std::move(object),
.buffer = buffer,
});
}
// TODO: Terminate the running applet title
return RESULT_SUCCESS;
}
ResultCode AppletManager::OrderToCloseSystemApplet() {
if (active_slot == AppletSlot::Error) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
auto active_slot_data = GetAppletSlot(active_slot);
if (active_slot_data->applet_id == AppletId::None ||
active_slot_data->attributes.applet_pos != AppletPos::Application) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
auto system_slot = GetAppletSlotFromPos(AppletPos::System);
if (system_slot == AppletSlot::Error) {
return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
ErrorLevel::Status};
}
auto system_slot_data = GetAppletSlot(system_slot);
if (!system_slot_data->registered) {
return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
ErrorLevel::Status};
}
ordered_to_close_sys_applet = true;
active_slot = system_slot;
SendParameter({
.sender_id = AppletId::Application,
.destination_id = system_slot_data->applet_id,
.signal = SignalType::WakeupByCancel,
});
return RESULT_SUCCESS;
}
ResultCode AppletManager::PrepareToJumpToHomeMenu() {
if (next_parameter) {
return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
last_jump_to_home_slot = active_slot;
if (last_jump_to_home_slot == AppletSlot::Application) {
EnsureHomeMenuLoaded();
}
return RESULT_SUCCESS;
}
ResultCode AppletManager::JumpToHomeMenu(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer) {
if (last_jump_to_home_slot != AppletSlot::Error) {
auto slot_data = GetAppletSlot(last_jump_to_home_slot);
if (slot_data->applet_id != AppletId::None) {
MessageParameter param;
param.object = std::move(object);
param.buffer = buffer;
switch (slot_data->attributes.applet_pos) {
case AppletPos::Application:
active_slot = AppletSlot::HomeMenu;
param.destination_id = AppletId::HomeMenu;
param.sender_id = AppletId::Application;
param.signal = SignalType::WakeupByPause;
SendParameter(param);
break;
case AppletPos::Library:
param.destination_id = slot_data->applet_id;
param.sender_id = slot_data->applet_id;
param.signal = SignalType::WakeupByCancel;
SendParameter(param);
break;
case AppletPos::System:
if (slot_data->attributes.is_home_menu) {
param.destination_id = slot_data->applet_id;
param.sender_id = slot_data->applet_id;
param.signal = SignalType::WakeupToJumpHome;
SendParameter(param);
}
break;
case AppletPos::SysLibrary: {
const auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet);
param.destination_id = slot_data->applet_id;
param.sender_id = slot_data->applet_id;
param.signal = system_slot_data->registered ? SignalType::WakeupByCancel
: SignalType::WakeupToJumpHome;
SendParameter(param);
break;
}
default:
break;
}
}
}
return RESULT_SUCCESS;
}
ResultCode AppletManager::PrepareToLeaveHomeMenu() {
if (!GetAppletSlot(AppletSlot::Application)->registered) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
if (next_parameter) {
return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
return RESULT_SUCCESS;
}
ResultCode AppletManager::LeaveHomeMenu(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer) {
active_slot = AppletSlot::Application;
SendParameter({
.sender_id = AppletId::HomeMenu,
.destination_id = AppletId::Application,
.signal = SignalType::WakeupByPause,
.object = std::move(object),
.buffer = buffer,
});
return RESULT_SUCCESS;
}
ResultCode AppletManager::OrderToCloseApplication() {
if (active_slot == AppletSlot::Error) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
auto active_slot_data = GetAppletSlot(active_slot);
if (active_slot_data->applet_id == AppletId::None ||
active_slot_data->attributes.applet_pos != AppletPos::System) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
ordered_to_close_application = true;
active_slot = AppletSlot::Application;
SendParameter({
.sender_id = AppletId::HomeMenu,
.destination_id = AppletId::Application,
.signal = SignalType::WakeupByCancel,
});
return RESULT_SUCCESS;
}
ResultCode AppletManager::PrepareToCloseApplication(bool return_to_sys) {
if (active_slot == AppletSlot::Error) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
auto active_slot_data = GetAppletSlot(active_slot);
if (active_slot_data->applet_id == AppletId::None ||
active_slot_data->attributes.applet_pos != AppletPos::Application) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet);
auto home_menu_slot_data = GetAppletSlot(AppletSlot::HomeMenu);
if (!application_cancelled && return_to_sys) {
// TODO: Left side of the OR also includes "&& !power_button_clicked", but this isn't
// implemented yet.
if (!ordered_to_close_application || !system_slot_data->registered) {
application_close_target = AppletSlot::HomeMenu;
} else {
application_close_target = AppletSlot::SystemApplet;
}
} else {
application_close_target = AppletSlot::Error;
}
if (application_close_target != AppletSlot::HomeMenu && !system_slot_data->registered &&
!home_menu_slot_data->registered) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
if (next_parameter) {
return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
if (application_close_target == AppletSlot::HomeMenu) {
EnsureHomeMenuLoaded();
}
return RESULT_SUCCESS;
}
ResultCode AppletManager::CloseApplication(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer) {
ordered_to_close_application = false;
application_cancelled = false;
GetAppletSlot(AppletSlot::Application)->Reset();
if (application_close_target != AppletSlot::Error) {
active_slot = application_close_target;
CancelAndSendParameter({
.sender_id = AppletId::Application,
.destination_id = GetAppletSlot(application_close_target)->applet_id,
.signal = SignalType::WakeupByExit,
.object = std::move(object),
.buffer = buffer,
});
}
// TODO: Terminate the application process.
return RESULT_SUCCESS;
}
ResultVal<AppletManager::AppletManInfo> AppletManager::GetAppletManInfo(
AppletPos requested_applet_pos) {
auto active_applet_pos = AppletPos::Invalid;
auto active_applet_id = AppletId::None;
if (active_slot != AppletSlot::Error) {
auto active_slot_data = GetAppletSlot(active_slot);
if (active_slot_data->applet_id != AppletId::None) {
active_applet_pos = active_slot_data->attributes.applet_pos;
active_applet_id = active_slot_data->applet_id;
}
}
auto requested_applet_id = AppletId::None;
auto requested_slot = GetAppletSlotFromPos(requested_applet_pos);
if (requested_slot != AppletSlot::Error) {
auto requested_slot_data = GetAppletSlot(requested_slot);
if (requested_slot_data->registered) {
requested_applet_id = requested_slot_data->applet_id;
}
}
return MakeResult<AppletManInfo>({
.active_applet_pos = active_applet_pos,
.requested_applet_id = requested_applet_id,
.home_menu_applet_id = AppletId::HomeMenu,
.active_applet_id = active_applet_id,
});
}
ResultVal<AppletManager::AppletInfo> AppletManager::GetAppletInfo(AppletId app_id) {
auto slot = GetAppletSlotFromId(app_id);
if (slot == AppletSlot::Error) {
@ -709,7 +1048,7 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) {
ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) {
if (active_slot == AppletSlot::Error ||
GetAppletSlot(active_slot)->attributes.applet_pos != static_cast<u32>(AppletPos::System)) {
GetAppletSlot(active_slot)->attributes.applet_pos != AppletPos::System) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
@ -771,9 +1110,30 @@ ResultCode AppletManager::WakeupApplication() {
// Send a Wakeup signal via the apt parameter to the application once it registers itself.
// The real APT service does this by spin waiting on another thread until the application is
// registered.
SendApplicationParameterAfterRegistration({.sender_id = AppletId::HomeMenu,
SendApplicationParameterAfterRegistration({
.sender_id = AppletId::HomeMenu,
.destination_id = AppletId::Application,
.signal = SignalType::Wakeup});
.signal = SignalType::Wakeup,
});
return RESULT_SUCCESS;
}
ResultCode AppletManager::CancelApplication() {
auto application_slot_data = GetAppletSlot(AppletSlot::Application);
if (application_slot_data->applet_id == AppletId::None) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
application_cancelled = true;
SendApplicationParameterAfterRegistration({
.sender_id = active_slot != AppletSlot::Error ? GetAppletSlot(active_slot)->applet_id
: AppletId::None,
.destination_id = AppletId::Application,
.signal = SignalType::WakeupByCancel,
});
return RESULT_SUCCESS;
}
@ -813,6 +1173,78 @@ void AppletManager::EnsureHomeMenuLoaded() {
}
}
static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height,
u32 format) {
static constexpr auto screen_capture_base_vaddr = static_cast<VAddr>(0x1F500000);
static constexpr auto screen_width = 240;
static constexpr auto screen_width_pow2 = 256;
const auto bpp = format < 2 ? 3 : 2;
Memory::RasterizerFlushVirtualRegion(src, screen_width * height * bpp,
Memory::FlushMode::Flush);
auto dst_vaddr = screen_capture_base_vaddr + capture_offset;
auto dst_ptr = system.Memory().GetPointer(dst_vaddr);
const auto src_ptr = system.Memory().GetPointer(src);
for (auto y = 0; y < height; y++) {
for (auto x = 0; x < screen_width; x++) {
auto dst_offset =
VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * screen_width_pow2 * bpp;
auto src_offset = bpp * (screen_width * y + x);
std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp);
}
}
Memory::RasterizerFlushVirtualRegion(dst_vaddr, screen_width_pow2 * height * bpp,
Memory::FlushMode::Invalidate);
}
void AppletManager::CaptureFrameBuffers() {
auto gsp =
Core::System::GetInstance().ServiceManager().GetService<Service::GSP::GSP_GPU>("gsp::Gpu");
auto active_thread_id = gsp->GetActiveThreadId();
auto top_screen = gsp->GetFrameBufferInfo(active_thread_id, 0);
auto bottom_screen = gsp->GetFrameBufferInfo(active_thread_id, 1);
auto top_fb = top_screen->framebuffer_info[top_screen->index];
auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index];
CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, bottom_fb.address_left, 320,
capture_info->bottom_screen_format);
CaptureFrameBuffer(system, capture_info->top_screen_left_offset, top_fb.address_left, 400,
capture_info->top_screen_format);
if (capture_info->is_3d) {
CaptureFrameBuffer(system, capture_info->top_screen_right_offset, top_fb.address_right, 400,
capture_info->top_screen_format);
}
}
void AppletManager::LoadInputDevices() {
home_button = Input::CreateDevice<Input::ButtonDevice>(
Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]);
}
void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) {
if (is_device_reload_pending.exchange(false)) {
LoadInputDevices();
}
const bool state = home_button->GetStatus();
// NOTE: We technically do support loading and jumping to home menu even if it isn't
// initially registered. However since the home menu suspend is not bug-free, we don't
// want normal users who didn't launch the home menu accidentally pressing the home
// button binding and freezing their game, so for now, gate it to only environments
// where the home menu was already loaded by the user (last condition).
if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) {
SendNotification(Notification::HomeButtonSingle);
}
last_home_button_state = state;
// Reschedule recurrent event
Core::System::GetInstance().CoreTiming().ScheduleEvent(
usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event);
}
AppletManager::AppletManager(Core::System& system) : system(system) {
lock = system.Kernel().CreateMutex(false, "APT_U:Lock");
for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) {
@ -828,10 +1260,20 @@ AppletManager::AppletManager(Core::System& system) : system(system) {
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter");
}
HLE::Applets::Init();
home_button_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent(
"Home Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) {
HomeButtonUpdateEvent(user_data, cycles_late);
});
Core::System::GetInstance().CoreTiming().ScheduleEvent(
usToCycles(home_button_update_interval_us), home_button_update_event);
}
AppletManager::~AppletManager() {
HLE::Applets::Shutdown();
}
void AppletManager::ReloadInputDevices() {
is_device_reload_pending.store(true);
}
} // namespace Service::APT

View file

@ -13,6 +13,7 @@
#include <boost/serialization/optional.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include "core/frontend/input.h"
#include "core/global.h"
#include "core/hle/kernel/event.h"
#include "core/hle/result.h"
@ -46,6 +47,21 @@ enum class SignalType : u32 {
WakeupToLaunchApplication = 0x11,
};
enum class Notification : u32 {
None = 0,
HomeButtonSingle = 1,
HomeButtonDouble = 2,
SleepQuery = 3,
SleepCancelledByOpen = 4,
SleepAccepted = 5,
SleepAwake = 6,
Shutdown = 7,
PowerButtonClick = 8,
PowerButtonClear = 9,
TrySleep = 10,
OrderToClose = 11,
};
/// App Id's used by APT functions
enum class AppletId : u32 {
None = 0,
@ -103,19 +119,20 @@ private:
friend class boost::serialization::access;
};
enum class AppletPos {
enum class AppletPos : u32 {
Application = 0,
Library = 1,
System = 2,
SysLibrary = 3,
Resident = 4,
AutoLibrary = 5
AutoLibrary = 5,
Invalid = 0xFF,
};
union AppletAttributes {
u32 raw;
BitField<0, 3, u32> applet_pos;
BitField<0, 3, AppletPos> applet_pos;
BitField<29, 1, u32> is_home_menu;
AppletAttributes() : raw(0) {}
@ -178,11 +195,41 @@ private:
friend class boost::serialization::access;
};
/// Used by the application to pass information about the current framebuffer to applets.
struct CaptureBufferInfo {
u32_le size;
u8 is_3d;
INSERT_PADDING_BYTES(0x3); // Padding for alignment
u32_le top_screen_left_offset;
u32_le top_screen_right_offset;
u32_le top_screen_format;
u32_le bottom_screen_left_offset;
u32_le bottom_screen_right_offset;
u32_le bottom_screen_format;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& size;
ar& is_3d;
ar& top_screen_left_offset;
ar& top_screen_right_offset;
ar& top_screen_format;
ar& bottom_screen_left_offset;
ar& bottom_screen_right_offset;
ar& bottom_screen_format;
}
friend class boost::serialization::access;
};
static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
class AppletManager : public std::enable_shared_from_this<AppletManager> {
public:
explicit AppletManager(Core::System& system);
~AppletManager();
void ReloadInputDevices();
/**
* Clears any existing parameter and places a new one. This function is currently only used by
* HLE Applets and should be likely removed in the future
@ -211,6 +258,9 @@ public:
ResultCode Enable(AppletAttributes attributes);
bool IsRegistered(AppletId app_id);
ResultVal<Notification> InquireNotification(AppletId app_id);
ResultCode SendNotification(Notification notification);
ResultCode PrepareToStartLibraryApplet(AppletId applet_id);
ResultCode PreloadLibraryApplet(AppletId applet_id);
ResultCode FinishPreloadingLibraryApplet(AppletId applet_id);
@ -227,6 +277,18 @@ public:
ResultCode PrepareToCloseSystemApplet();
ResultCode CloseSystemApplet(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer);
ResultCode OrderToCloseSystemApplet();
ResultCode PrepareToJumpToHomeMenu();
ResultCode JumpToHomeMenu(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer);
ResultCode PrepareToLeaveHomeMenu();
ResultCode LeaveHomeMenu(std::shared_ptr<Kernel::Object> object, const std::vector<u8>& buffer);
ResultCode OrderToCloseApplication();
ResultCode PrepareToCloseApplication(bool return_to_sys);
ResultCode CloseApplication(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer);
ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type,
ApplicationJumpFlags flags);
@ -239,10 +301,40 @@ public:
deliver_arg = std::move(arg);
}
std::vector<u8> GetCaptureInfo() {
std::vector<u8> buffer;
if (capture_info) {
buffer.resize(sizeof(CaptureBufferInfo));
std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo));
}
return buffer;
}
std::vector<u8> ReceiveCaptureBufferInfo() {
std::vector<u8> buffer = GetCaptureInfo();
capture_info.reset();
return buffer;
}
void SendCaptureBufferInfo(std::vector<u8> buffer) {
ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small.");
capture_info.emplace();
std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo));
}
ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type);
ResultCode StartApplication(const std::vector<u8>& parameter, const std::vector<u8>& hmac,
bool paused);
ResultCode WakeupApplication();
ResultCode CancelApplication();
struct AppletManInfo {
AppletPos active_applet_pos;
AppletId requested_applet_id;
AppletId home_menu_applet_id;
AppletId active_applet_id;
};
ResultVal<AppletManInfo> GetAppletManInfo(AppletPos requested_applet_pos);
struct AppletInfo {
u64 title_id;
@ -273,6 +365,8 @@ private:
boost::optional<ApplicationStartParameters> app_start_parameters{};
boost::optional<DeliverArg> deliver_arg{};
boost::optional<CaptureBufferInfo> capture_info;
static constexpr std::size_t NumAppletSlot = 4;
enum class AppletSlot : u8 {
@ -292,6 +386,7 @@ private:
bool registered;
bool loaded;
AppletAttributes attributes;
Notification notification;
std::shared_ptr<Kernel::Event> notification_event;
std::shared_ptr<Kernel::Event> parameter_event;
@ -311,6 +406,7 @@ private:
ar& registered;
ar& loaded;
ar& attributes.raw;
ar& notification;
ar& notification_event;
ar& parameter_event;
}
@ -325,6 +421,16 @@ private:
SignalType library_applet_closing_command = SignalType::None;
AppletId last_prepared_library_applet = AppletId::None;
AppletSlot last_system_launcher_slot = AppletSlot::Error;
AppletSlot last_jump_to_home_slot = AppletSlot::Error;
bool ordered_to_close_sys_applet = false;
bool ordered_to_close_application = false;
bool application_cancelled = false;
AppletSlot application_close_target = AppletSlot::Error;
Core::TimingEventType* home_button_update_event;
std::atomic<bool> is_device_reload_pending{true};
std::unique_ptr<Input::ButtonDevice> home_button;
bool last_home_button_state = false;
Core::System& system;
@ -346,6 +452,11 @@ private:
void EnsureHomeMenuLoaded();
void CaptureFrameBuffers();
void LoadInputDevices();
void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& next_parameter;
@ -358,10 +469,20 @@ private:
ar& last_library_launcher_slot;
ar& last_prepared_library_applet;
ar& last_system_launcher_slot;
ar& last_jump_to_home_slot;
ar& ordered_to_close_sys_applet;
ar& ordered_to_close_application;
ar& application_cancelled;
ar& application_close_target;
ar& lock;
ar& capture_info;
}
ar& applet_slots;
ar& library_applet_closing_command;
if (Archive::is_loading::value) {
LoadInputDevices();
}
}
friend class boost::serialization::access;
};

View file

@ -41,7 +41,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) {
ar& shared_font_relocated;
ar& cpu_percent;
ar& unknown_ns_state_field;
ar& screen_capture_buffer;
ar& screen_capture_post_permission;
ar& applet_manager;
if (file_version > 0) {
@ -73,6 +72,47 @@ void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx)
LOG_WARNING(Service_APT, "called size={}", size);
}
void Module::NSInterface::ShutdownAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0xE, 0, 0); // 0xE0000
LOG_INFO(Service_APT, "called");
apt->system.RequestShutdown();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void Module::NSInterface::RebootSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x10, 6, 0); // 0x100180
const auto launch_title = rp.Pop<u8>() != 0;
const auto title_id = rp.Pop<u64>();
const auto media_type = static_cast<FS::MediaType>(rp.Pop<u8>());
rp.Skip(1, false); // Skip padding
const auto mem_type = rp.Pop<u8>();
LOG_WARNING(Service_APT,
"called launch_title={}, title_id={:016X}, media_type={:02X}, mem_type={:02X}",
launch_title, title_id, media_type, mem_type);
// TODO: Implement loading a specific title.
apt->system.RequestReset();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void Module::NSInterface::RebootSystemClean(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x16, 0, 0); // 0x160000
LOG_INFO(Service_APT, "called");
apt->system.RequestReset();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void Module::APTInterface::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2, 2, 0); // 0x20080
const auto app_id = rp.PopEnum<AppletId>();
@ -332,16 +372,22 @@ void Module::APTInterface::Enable(Kernel::HLERequestContext& ctx) {
void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x5, 1, 0); // 0x50040
const auto unk = rp.Pop<u32>();
auto applet_pos = rp.PopEnum<AppletPos>();
LOG_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos);
auto info = apt->applet_manager->GetAppletManInfo(applet_pos);
if (info.Failed()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(info.Code());
} else {
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
rb.Push(RESULT_SUCCESS); // No error
rb.Push<u32>(0);
rb.Push<u32>(0);
rb.Push(static_cast<u32>(AppletId::HomeMenu)); // Home menu AppID
rb.Push(static_cast<u32>(AppletId::Application)); // TODO(purpasmart96): Do this correctly
LOG_WARNING(Service_APT, "(STUBBED) called unk={:#010X}", unk);
rb.Push(RESULT_SUCCESS);
rb.PushEnum(info->active_applet_pos);
rb.PushEnum(info->requested_applet_id);
rb.PushEnum(info->home_menu_applet_id);
rb.PushEnum(info->active_applet_id);
}
}
void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) {
@ -357,13 +403,19 @@ void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) {
void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0xB, 1, 0); // 0xB0040
const auto app_id = rp.Pop<u32>();
const auto app_id = rp.PopEnum<AppletId>();
LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id);
auto notification = apt->applet_manager->InquireNotification(app_id);
if (notification.Failed()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(notification.Code());
} else {
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS); // No error
rb.Push(static_cast<u32>(SignalType::None)); // Signal type
LOG_WARNING(Service_APT, "(STUBBED) called app_id={:#010X}", app_id);
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(notification.Unwrap()));
}
}
void Module::APTInterface::SendParameter(Kernel::HLERequestContext& ctx) {
@ -561,6 +613,15 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->WakeupApplication());
}
void Module::APTInterface::CancelApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x1D, 0, 0); // 0x001D0000
LOG_DEBUG(Service_APT, "called");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->CancelApplication());
}
void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x4B, 3, 2); // 0x004B00C2
@ -574,8 +635,18 @@ void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {
"(STUBBED) called command={:#010X}, input_size={:#010X}, output_size={:#010X}",
utility_command, input_size, output_size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
std::vector<u8> out(output_size);
if (utility_command == 0x6 && output_size > 0) {
// Command 0x6 (TryLockTransition) expects a boolean return value indicating
// whether the attempt succeeded. Since we don't implement any of the transition
// locking stuff yet, fake a success result to avoid app crashes.
out[0] = true;
}
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS); // No error
rb.Push(RESULT_SUCCESS); // Utility function result
rb.PushStaticBuffer(out, 0);
}
void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) {
@ -688,18 +759,35 @@ void Module::APTInterface::StartSystemApplet(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer));
}
void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x27, 1, 4);
[[maybe_unused]] const auto parameters_size = rp.Pop<u32>();
[[maybe_unused]] const auto object = rp.PopGenericObject();
[[maybe_unused]] const auto buffer = rp.PopStaticBuffer();
void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x21, 0, 0);
LOG_DEBUG(Service_APT, "called");
apt->system.RequestShutdown();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->OrderToCloseApplication());
}
void Module::APTInterface::PrepareToCloseApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x22, 1, 0);
const auto return_to_sys = rp.Pop<u8>() != 0;
LOG_DEBUG(Service_APT, "called return_to_sys={}", return_to_sys);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(apt->applet_manager->PrepareToCloseApplication(return_to_sys));
}
void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x27, 1, 4);
const auto parameter_size = rp.Pop<u32>();
const auto object = rp.PopGenericObject();
const auto buffer = rp.PopStaticBuffer();
LOG_DEBUG(Service_APT, "called size={}", parameter_size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->CloseApplication(object, buffer));
}
void Module::APTInterface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) {
@ -758,6 +846,57 @@ void Module::APTInterface::CloseSystemApplet(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->CloseSystemApplet(object, buffer));
}
void Module::APTInterface::OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2A, 0, 0); // 0x2A0000
LOG_DEBUG(Service_APT, "called");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->OrderToCloseSystemApplet());
}
void Module::APTInterface::PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2B, 0, 0); // 0x2B0000
LOG_DEBUG(Service_APT, "called");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->PrepareToJumpToHomeMenu());
}
void Module::APTInterface::JumpToHomeMenu(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2C, 1, 4); // 0x2C0044
const auto parameter_size = rp.Pop<u32>();
const auto object = rp.PopGenericObject();
const auto buffer = rp.PopStaticBuffer();
LOG_DEBUG(Service_APT, "called size={}", parameter_size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->JumpToHomeMenu(object, buffer));
}
void Module::APTInterface::PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2D, 0, 0); // 0x2D0000
LOG_DEBUG(Service_APT, "called");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->PrepareToLeaveHomeMenu());
}
void Module::APTInterface::LeaveHomeMenu(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2E, 1, 4); // 0x2E0044
const auto parameter_size = rp.Pop<u32>();
const auto object = rp.PopGenericObject();
const auto buffer = rp.PopStaticBuffer();
LOG_DEBUG(Service_APT, "called size={}", parameter_size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->LeaveHomeMenu(object, buffer));
}
void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040
const auto size = std::min(std::size_t{rp.Pop<u32>()}, SysMenuArgSize);
@ -794,8 +933,7 @@ void Module::APTInterface::SendCaptureBufferInfo(Kernel::HLERequestContext& ctx)
LOG_DEBUG(Service_APT, "called");
ASSERT(size == 0x20);
apt->screen_capture_buffer = buffer;
apt->applet_manager->SendCaptureBufferInfo(buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
@ -807,12 +945,14 @@ void Module::APTInterface::ReceiveCaptureBufferInfo(Kernel::HLERequestContext& c
LOG_DEBUG(Service_APT, "called");
ASSERT(size == 0x20);
auto screen_capture_buffer = apt->applet_manager->ReceiveCaptureBufferInfo();
auto real_size = std::min(static_cast<u32>(screen_capture_buffer.size()), size);
screen_capture_buffer.resize(size);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(apt->screen_capture_buffer.size()));
rb.PushStaticBuffer(std::move(apt->screen_capture_buffer), 0);
rb.Push(real_size);
rb.PushStaticBuffer(std::move(screen_capture_buffer), 0);
}
void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) {
@ -820,13 +960,15 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) {
const auto size = rp.Pop<u32>();
LOG_DEBUG(Service_APT, "called");
ASSERT(size == 0x20);
auto screen_capture_buffer = apt->applet_manager->GetCaptureInfo();
auto real_size = std::min(static_cast<u32>(screen_capture_buffer.size()), size);
screen_capture_buffer.resize(size);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(apt->screen_capture_buffer.size()));
// This service function does not clear the capture buffer.
rb.PushStaticBuffer(apt->screen_capture_buffer, 0);
rb.Push(real_size);
rb.PushStaticBuffer(std::move(screen_capture_buffer), 0);
}
void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) {

View file

@ -31,20 +31,6 @@ class AppletManager;
/// Each APT service can only have up to 2 sessions connected at the same time.
static const u32 MaxAPTSessions = 2;
/// Used by the application to pass information about the current framebuffer to applets.
struct CaptureBufferInfo {
u32_le size;
u8 is_3d;
INSERT_PADDING_BYTES(0x3); // Padding for alignment
u32_le top_screen_left_offset;
u32_le top_screen_right_offset;
u32_le top_screen_format;
u32_le bottom_screen_left_offset;
u32_le bottom_screen_right_offset;
u32_le bottom_screen_format;
};
static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
constexpr std::size_t SysMenuArgSize = 0x40;
enum class StartupArgumentType : u32 {
@ -87,6 +73,37 @@ public:
* 0 : Result of function, 0 on success, otherwise error code
*/
void SetWirelessRebootInfo(Kernel::HLERequestContext& ctx);
/**
* NS::ShutdownAsync service function.
* Inputs:
* 1 : None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ShutdownAsync(Kernel::HLERequestContext& ctx);
/**
* NS::RebootSystem service function.
* Inputs:
* 1 : Boolean indicating whether to launch a title.
* 2-3 : Title ID
* 4 : Media Type
* 5 : Padding
* 6 : Launch memory type
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RebootSystem(Kernel::HLERequestContext& ctx);
/**
* NS::RebootSystemClean service function.
* Inputs:
* 1 : None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RebootSystemClean(Kernel::HLERequestContext& ctx);
};
class APTInterface : public ServiceFramework<APTInterface> {
@ -371,6 +388,16 @@ public:
*/
void WakeupApplication(Kernel::HLERequestContext& ctx);
/**
* APT::CancelApplication service function.
* Inputs:
* 0 : Command header [0x001D0000]
* Outputs:
* 0 : Return Header
* 1 : Result of function, 0 on success, otherwise error code
*/
void CancelApplication(Kernel::HLERequestContext& ctx);
/**
* APT::AppletUtility service function
* Inputs:
@ -491,6 +518,27 @@ public:
*/
void StartSystemApplet(Kernel::HLERequestContext& ctx);
/**
* APT::OrderToCloseApplication service function
* Inputs:
* 0 : Command header [0x00210000]
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void OrderToCloseApplication(Kernel::HLERequestContext& ctx);
/**
* APT::PrepareToCloseApplication service function
* Inputs:
* 0 : Command header [0x00220040]
* 1 : Boolean indicating whether to cancel applet preloads.
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void PrepareToCloseApplication(Kernel::HLERequestContext& ctx);
/**
* APT::CloseApplication service function
* Inputs:
@ -629,6 +677,66 @@ public:
*/
void CloseSystemApplet(Kernel::HLERequestContext& ctx);
/**
* APT::OrderToCloseSystemApplet service function
* Inputs:
* 0 : Command header [0x002A0000]
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx);
/**
* APT::PrepareToJumpToHomeMenu service function
* Inputs:
* 0 : Command header [0x002B0000]
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx);
/**
* APT::JumpToHomeMenu service function
* Inputs:
* 0 : Command header [0x002C0044]
* 1 : Buffer size
* 2 : 0x0
* 3 : Object handle
* 4 : (Size << 14) | 2
* 5 : Input buffer virtual address
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void JumpToHomeMenu(Kernel::HLERequestContext& ctx);
/**
* APT::PrepareToLeaveHomeMenu service function
* Inputs:
* 0 : Command header [0x002B0000]
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx);
/**
* APT::LeaveHomeMenu service function
* Inputs:
* 0 : Command header [0x002C0044]
* 1 : Buffer size
* 2 : 0x0
* 3 : Object handle
* 4 : (Size << 14) | 2
* 5 : Input buffer virtual address
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void LeaveHomeMenu(Kernel::HLERequestContext& ctx);
/**
* APT::LoadSysMenuArg service function
* Inputs:
@ -801,7 +909,6 @@ private:
// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
u8 unknown_ns_state_field = 0;
std::vector<u8> screen_capture_buffer;
std::array<u8, SysMenuArgSize> sys_menu_arg_buffer;
ScreencapPostPermission screen_capture_post_permission =

View file

@ -42,8 +42,8 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
{0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, &APT_A::StartSystemApplet, "StartSystemApplet"},
{0x00200044, nullptr, "StartNewestHomeMenu"},
{0x00210000, nullptr, "OrderToCloseApplication"},
{0x00220040, nullptr, "PrepareToCloseApplication"},
{0x00210000, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"},
{0x00220040, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x00230040, nullptr, "PrepareToJumpToApplication"},
{0x00240044, nullptr, "JumpToApplication"},
{0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
@ -51,11 +51,11 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
{0x00270044, &APT_A::CloseApplication, "CloseApplication"},
{0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"},
{0x00290044, &APT_A::CloseSystemApplet, "CloseSystemApplet"},
{0x002A0000, nullptr, "OrderToCloseSystemApplet"},
{0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
{0x002C0044, nullptr, "JumpToHomeMenu"},
{0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
{0x002E0044, nullptr, "LeaveHomeMenu"},
{0x002A0000, &APT_A::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
{0x002B0000, &APT_A::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
{0x002C0044, &APT_A::JumpToHomeMenu, "JumpToHomeMenu"},
{0x002D0000, &APT_A::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
{0x002E0044, &APT_A::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
{0x00300044, nullptr, "LeaveResidentApplet"},
{0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},

View file

@ -42,8 +42,8 @@ APT_S::APT_S(std::shared_ptr<Module> apt)
{0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, &APT_S::StartSystemApplet, "StartSystemApplet"},
{0x00200044, nullptr, "StartNewestHomeMenu"},
{0x00210000, nullptr, "OrderToCloseApplication"},
{0x00220040, nullptr, "PrepareToCloseApplication"},
{0x00210000, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"},
{0x00220040, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x00230040, nullptr, "PrepareToJumpToApplication"},
{0x00240044, nullptr, "JumpToApplication"},
{0x002500C0, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
@ -51,11 +51,11 @@ APT_S::APT_S(std::shared_ptr<Module> apt)
{0x00270044, &APT_S::CloseApplication, "CloseApplication"},
{0x00280044, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"},
{0x00290044, &APT_S::CloseSystemApplet, "CloseSystemApplet"},
{0x002A0000, nullptr, "OrderToCloseSystemApplet"},
{0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
{0x002C0044, nullptr, "JumpToHomeMenu"},
{0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
{0x002E0044, nullptr, "LeaveHomeMenu"},
{0x002A0000, &APT_S::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
{0x002B0000, &APT_S::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
{0x002C0044, &APT_S::JumpToHomeMenu, "JumpToHomeMenu"},
{0x002D0000, &APT_S::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
{0x002E0044, &APT_S::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
{0x00300044, nullptr, "LeaveResidentApplet"},
{0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},

View file

@ -42,8 +42,8 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
{0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, &APT_U::StartSystemApplet, "StartSystemApplet"},
{0x00200044, nullptr, "StartNewestHomeMenu"},
{0x00210000, nullptr, "OrderToCloseApplication"},
{0x00220040, nullptr, "PrepareToCloseApplication"},
{0x00210000, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"},
{0x00220040, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x00230040, nullptr, "PrepareToJumpToApplication"},
{0x00240044, nullptr, "JumpToApplication"},
{0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
@ -51,11 +51,11 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
{0x00270044, &APT_U::CloseApplication, "CloseApplication"},
{0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"},
{0x00290044, &APT_U::CloseSystemApplet, "CloseSystemApplet"},
{0x002A0000, nullptr, "OrderToCloseSystemApplet"},
{0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
{0x002C0044, nullptr, "JumpToHomeMenu"},
{0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
{0x002E0044, nullptr, "LeaveHomeMenu"},
{0x002A0000, &APT_U::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
{0x002B0000, &APT_U::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
{0x002C0044, &APT_U::JumpToHomeMenu, "JumpToHomeMenu"},
{0x002D0000, &APT_U::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
{0x002E0044, &APT_U::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
{0x00300044, nullptr, "LeaveResidentApplet"},
{0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},

View file

@ -19,12 +19,12 @@ NS_S::NS_S(std::shared_ptr<Service::APT::Module> apt)
{0x00070042, nullptr, "CardUpdateInitialize"},
{0x00080000, nullptr, "CardUpdateShutdown"},
{0x000D0140, nullptr, "SetTWLBannerHMAC"},
{0x000E0000, nullptr, "ShutdownAsync"},
{0x00100180, nullptr, "RebootSystem"},
{0x000E0000, &NS_S::ShutdownAsync, "ShutdownAsync"},
{0x00100180, &NS_S::RebootSystem, "RebootSystem"},
{0x00110100, nullptr, "TerminateTitle"},
{0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
{0x00150140, nullptr, "LaunchApplication"},
{0x00160000, nullptr, "RebootSystemClean"},
{0x00160000, &NS_S::RebootSystemClean, "RebootSystemClean"},
};
RegisterHandlers(functions);
}

View file

@ -663,14 +663,17 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x18, 0, 0);
// TODO(Subv): We're always returning the framebuffer structures for thread_id = 0,
// because we only support a single running application at a time.
// This should always return the framebuffer data that is currently displayed on the screen.
if (active_thread_id == UINT32_MAX) {
LOG_WARNING(Service_GSP, "Called without an active thread.");
u32 thread_id = 0;
// TODO: Find the right error code.
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(-1);
return;
}
FrameBufferUpdate* top_screen = GetFrameBufferInfo(thread_id, 0);
FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1);
FrameBufferUpdate* top_screen = GetFrameBufferInfo(active_thread_id, 0);
FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
struct CaptureInfoEntry {
u32_le address_left;
@ -700,6 +703,39 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_GSP, "called");
}
void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x19, 0, 0);
LOG_INFO(Service_GSP, "called");
// TODO: This should also DMA framebuffers into VRAM and save LCD register state.
Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
Memory::FlushMode::Flush);
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
saved_vram.emplace(std::vector<u8>(Memory::VRAM_SIZE));
std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x1A, 0, 0);
LOG_INFO(Service_GSP, "called");
if (saved_vram) {
// TODO: This should also restore LCD register state.
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE);
Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
Memory::FlushMode::Invalidate);
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x16, 1, 2);
@ -808,8 +844,8 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 2), system
{0x00160042, &GSP_GPU::AcquireRight, "AcquireRight"},
{0x00170000, &GSP_GPU::ReleaseRight, "ReleaseRight"},
{0x00180000, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"},
{0x00190000, nullptr, "SaveVramSysArea"},
{0x001A0000, nullptr, "RestoreVramSysArea"},
{0x00190000, &GSP_GPU::SaveVramSysArea, "SaveVramSysArea"},
{0x001A0000, &GSP_GPU::RestoreVramSysArea, "RestoreVramSysArea"},
{0x001B0000, nullptr, "ResetGpuCore"},
{0x001C0040, &GSP_GPU::SetLedForceOff, "SetLedForceOff"},
{0x001D0040, nullptr, "SetTestCommand"},

View file

@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/optional.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
@ -238,6 +239,13 @@ public:
*/
FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index);
/**
* Retreives the ID of the thread with GPU rights.
*/
u32 GetActiveThreadId() {
return active_thread_id;
}
private:
/**
* Signals that the specified interrupt type has occurred to userland code for the specified GSP
@ -402,6 +410,32 @@ private:
*/
void ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::SaveVramSysArea service function
*
* Returns information about the current framebuffer state
*
* Inputs:
* 0: Header 0x00190000
* Outputs:
* 0: Header Code[0x00190040]
* 1: Result code
*/
void SaveVramSysArea(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::RestoreVramSysArea service function
*
* Returns information about the current framebuffer state
*
* Inputs:
* 0: Header 0x001A0000
* Outputs:
* 0: Header Code[0x001A0040]
* 1: Result code
*/
void RestoreVramSysArea(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::StoreDataCache service function
*
@ -438,6 +472,9 @@ private:
bool first_initialization = true;
/// VRAM copy saved using SaveVramSysArea.
boost::optional<std::vector<u8>> saved_vram;
/// Maximum number of threads that can be registered at the same time in the GSP module.
static constexpr u32 MaxGSPThreads = 4;
@ -453,6 +490,7 @@ private:
ar& active_thread_id;
ar& first_initialization;
ar& used_thread_ids;
ar& saved_vram;
}
friend class boost::serialization::access;