mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2024-11-23 18:19:21 +01:00
Implement app management support (suspend, resume, close, etc) (#6322)
This commit is contained in:
parent
d2caf2d386
commit
c96f54f022
@ -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) {
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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&xit</string>
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
.destination_id = AppletId::Application,
|
||||
.signal = SignalType::Wakeup});
|
||||
SendApplicationParameterAfterRegistration({
|
||||
.sender_id = AppletId::HomeMenu,
|
||||
.destination_id = AppletId::Application,
|
||||
.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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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>();
|
||||
|
||||
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_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos);
|
||||
|
||||
LOG_WARNING(Service_APT, "(STUBBED) called unk={:#010X}", unk);
|
||||
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);
|
||||
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>();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS); // No error
|
||||
rb.Push(static_cast<u32>(SignalType::None)); // Signal type
|
||||
LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id);
|
||||
|
||||
LOG_WARNING(Service_APT, "(STUBBED) 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);
|
||||
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) {
|
||||
|
@ -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 =
|
||||
|
@ -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"},
|
||||
|
@ -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"},
|
||||
|
@ -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"},
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"},
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user