mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-11-03 23:28:48 +00:00 
			
		
		
		
	Implement app management support (suspend, resume, close, etc) (#6322)
This commit is contained in:
		
							parent
							
								
									d2caf2d386
								
							
						
					
					
						commit
						c96f54f022
					
				
					 21 changed files with 1043 additions and 110 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue