mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +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
				
			
		|  | @ -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, | ||||
|     SendApplicationParameterAfterRegistration({ | ||||
|         .sender_id = AppletId::HomeMenu, | ||||
|         .destination_id = AppletId::Application, | ||||
|                                                .signal = SignalType::Wakeup}); | ||||
|         .signal = SignalType::Wakeup, | ||||
|     }); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode AppletManager::CancelApplication() { | ||||
|     auto application_slot_data = GetAppletSlot(AppletSlot::Application); | ||||
|     if (application_slot_data->applet_id == AppletId::None) { | ||||
|         return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, | ||||
|                 ErrorLevel::Status}; | ||||
|     } | ||||
| 
 | ||||
|     application_cancelled = true; | ||||
| 
 | ||||
|     SendApplicationParameterAfterRegistration({ | ||||
|         .sender_id = active_slot != AppletSlot::Error ? GetAppletSlot(active_slot)->applet_id | ||||
|                                                       : AppletId::None, | ||||
|         .destination_id = AppletId::Application, | ||||
|         .signal = SignalType::WakeupByCancel, | ||||
|     }); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | @ -813,6 +1173,78 @@ void AppletManager::EnsureHomeMenuLoaded() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height, | ||||
|                                u32 format) { | ||||
|     static constexpr auto screen_capture_base_vaddr = static_cast<VAddr>(0x1F500000); | ||||
|     static constexpr auto screen_width = 240; | ||||
|     static constexpr auto screen_width_pow2 = 256; | ||||
|     const auto bpp = format < 2 ? 3 : 2; | ||||
| 
 | ||||
|     Memory::RasterizerFlushVirtualRegion(src, screen_width * height * bpp, | ||||
|                                          Memory::FlushMode::Flush); | ||||
| 
 | ||||
|     auto dst_vaddr = screen_capture_base_vaddr + capture_offset; | ||||
|     auto dst_ptr = system.Memory().GetPointer(dst_vaddr); | ||||
|     const auto src_ptr = system.Memory().GetPointer(src); | ||||
|     for (auto y = 0; y < height; y++) { | ||||
|         for (auto x = 0; x < screen_width; x++) { | ||||
|             auto dst_offset = | ||||
|                 VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * screen_width_pow2 * bpp; | ||||
|             auto src_offset = bpp * (screen_width * y + x); | ||||
|             std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Memory::RasterizerFlushVirtualRegion(dst_vaddr, screen_width_pow2 * height * bpp, | ||||
|                                          Memory::FlushMode::Invalidate); | ||||
| } | ||||
| 
 | ||||
| void AppletManager::CaptureFrameBuffers() { | ||||
|     auto gsp = | ||||
|         Core::System::GetInstance().ServiceManager().GetService<Service::GSP::GSP_GPU>("gsp::Gpu"); | ||||
|     auto active_thread_id = gsp->GetActiveThreadId(); | ||||
|     auto top_screen = gsp->GetFrameBufferInfo(active_thread_id, 0); | ||||
|     auto bottom_screen = gsp->GetFrameBufferInfo(active_thread_id, 1); | ||||
| 
 | ||||
|     auto top_fb = top_screen->framebuffer_info[top_screen->index]; | ||||
|     auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index]; | ||||
| 
 | ||||
|     CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, bottom_fb.address_left, 320, | ||||
|                        capture_info->bottom_screen_format); | ||||
|     CaptureFrameBuffer(system, capture_info->top_screen_left_offset, top_fb.address_left, 400, | ||||
|                        capture_info->top_screen_format); | ||||
|     if (capture_info->is_3d) { | ||||
|         CaptureFrameBuffer(system, capture_info->top_screen_right_offset, top_fb.address_right, 400, | ||||
|                            capture_info->top_screen_format); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void AppletManager::LoadInputDevices() { | ||||
|     home_button = Input::CreateDevice<Input::ButtonDevice>( | ||||
|         Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]); | ||||
| } | ||||
| 
 | ||||
| void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) { | ||||
|     if (is_device_reload_pending.exchange(false)) { | ||||
|         LoadInputDevices(); | ||||
|     } | ||||
| 
 | ||||
|     const bool state = home_button->GetStatus(); | ||||
|     // NOTE: We technically do support loading and jumping to home menu even if it isn't
 | ||||
|     // initially registered. However since the home menu suspend is not bug-free, we don't
 | ||||
|     // want normal users who didn't launch the home menu accidentally pressing the home
 | ||||
|     // button binding and freezing their game, so for now, gate it to only environments
 | ||||
|     // where the home menu was already loaded by the user (last condition).
 | ||||
|     if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) { | ||||
|         SendNotification(Notification::HomeButtonSingle); | ||||
|     } | ||||
|     last_home_button_state = state; | ||||
| 
 | ||||
|     // Reschedule recurrent event
 | ||||
|     Core::System::GetInstance().CoreTiming().ScheduleEvent( | ||||
|         usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event); | ||||
| } | ||||
| 
 | ||||
| AppletManager::AppletManager(Core::System& system) : system(system) { | ||||
|     lock = system.Kernel().CreateMutex(false, "APT_U:Lock"); | ||||
|     for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) { | ||||
|  | @ -828,10 +1260,20 @@ AppletManager::AppletManager(Core::System& system) : system(system) { | |||
|             system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter"); | ||||
|     } | ||||
|     HLE::Applets::Init(); | ||||
|     home_button_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent( | ||||
|         "Home Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) { | ||||
|             HomeButtonUpdateEvent(user_data, cycles_late); | ||||
|         }); | ||||
|     Core::System::GetInstance().CoreTiming().ScheduleEvent( | ||||
|         usToCycles(home_button_update_interval_us), home_button_update_event); | ||||
| } | ||||
| 
 | ||||
| AppletManager::~AppletManager() { | ||||
|     HLE::Applets::Shutdown(); | ||||
| } | ||||
| 
 | ||||
| void AppletManager::ReloadInputDevices() { | ||||
|     is_device_reload_pending.store(true); | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::APT
 | ||||
|  |  | |||
|  | @ -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>(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos); | ||||
| 
 | ||||
|     auto info = apt->applet_manager->GetAppletManInfo(applet_pos); | ||||
|     if (info.Failed()) { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(info.Code()); | ||||
|     } else { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); | ||||
|     rb.Push(RESULT_SUCCESS); // No error
 | ||||
|     rb.Push<u32>(0); | ||||
|     rb.Push<u32>(0); | ||||
|     rb.Push(static_cast<u32>(AppletId::HomeMenu));    // Home menu AppID
 | ||||
|     rb.Push(static_cast<u32>(AppletId::Application)); // TODO(purpasmart96): Do this correctly
 | ||||
| 
 | ||||
|     LOG_WARNING(Service_APT, "(STUBBED) called unk={:#010X}", unk); | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.PushEnum(info->active_applet_pos); | ||||
|         rb.PushEnum(info->requested_applet_id); | ||||
|         rb.PushEnum(info->home_menu_applet_id); | ||||
|         rb.PushEnum(info->active_applet_id); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -357,13 +403,19 @@ void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
| void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0xB, 1, 0); // 0xB0040
 | ||||
|     const auto app_id = rp.Pop<u32>(); | ||||
|     const auto app_id = rp.PopEnum<AppletId>(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id); | ||||
| 
 | ||||
|     auto notification = apt->applet_manager->InquireNotification(app_id); | ||||
|     if (notification.Failed()) { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(notification.Code()); | ||||
|     } else { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS);                     // No error
 | ||||
|     rb.Push(static_cast<u32>(SignalType::None)); // Signal type
 | ||||
| 
 | ||||
|     LOG_WARNING(Service_APT, "(STUBBED) called app_id={:#010X}", app_id); | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push(static_cast<u32>(notification.Unwrap())); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::SendParameter(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -561,6 +613,15 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push(apt->applet_manager->WakeupApplication()); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::CancelApplication(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x1D, 0, 0); // 0x001D0000
 | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->CancelApplication()); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x4B, 3, 2); // 0x004B00C2
 | ||||
| 
 | ||||
|  | @ -574,8 +635,18 @@ void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { | |||
|                 "(STUBBED) called command={:#010X}, input_size={:#010X}, output_size={:#010X}", | ||||
|                 utility_command, input_size, output_size); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     std::vector<u8> out(output_size); | ||||
|     if (utility_command == 0x6 && output_size > 0) { | ||||
|         // Command 0x6 (TryLockTransition) expects a boolean return value indicating
 | ||||
|         // whether the attempt succeeded. Since we don't implement any of the transition
 | ||||
|         // locking stuff yet, fake a success result to avoid app crashes.
 | ||||
|         out[0] = true; | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||
|     rb.Push(RESULT_SUCCESS); // No error
 | ||||
|     rb.Push(RESULT_SUCCESS); // Utility function result
 | ||||
|     rb.PushStaticBuffer(out, 0); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -688,18 +759,35 @@ void Module::APTInterface::StartSystemApplet(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer)); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x27, 1, 4); | ||||
|     [[maybe_unused]] const auto parameters_size = rp.Pop<u32>(); | ||||
|     [[maybe_unused]] const auto object = rp.PopGenericObject(); | ||||
|     [[maybe_unused]] const auto buffer = rp.PopStaticBuffer(); | ||||
| void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x21, 0, 0); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     apt->system.RequestShutdown(); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->OrderToCloseApplication()); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::PrepareToCloseApplication(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x22, 1, 0); | ||||
|     const auto return_to_sys = rp.Pop<u8>() != 0; | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called return_to_sys={}", return_to_sys); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(apt->applet_manager->PrepareToCloseApplication(return_to_sys)); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x27, 1, 4); | ||||
|     const auto parameter_size = rp.Pop<u32>(); | ||||
|     const auto object = rp.PopGenericObject(); | ||||
|     const auto buffer = rp.PopStaticBuffer(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called size={}", parameter_size); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->CloseApplication(object, buffer)); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -758,6 +846,57 @@ void Module::APTInterface::CloseSystemApplet(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push(apt->applet_manager->CloseSystemApplet(object, buffer)); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x2A, 0, 0); // 0x2A0000
 | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->OrderToCloseSystemApplet()); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x2B, 0, 0); // 0x2B0000
 | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->PrepareToJumpToHomeMenu()); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::JumpToHomeMenu(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x2C, 1, 4); // 0x2C0044
 | ||||
|     const auto parameter_size = rp.Pop<u32>(); | ||||
|     const auto object = rp.PopGenericObject(); | ||||
|     const auto buffer = rp.PopStaticBuffer(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called size={}", parameter_size); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->JumpToHomeMenu(object, buffer)); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x2D, 0, 0); // 0x2D0000
 | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->PrepareToLeaveHomeMenu()); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::LeaveHomeMenu(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x2E, 1, 4); // 0x2E0044
 | ||||
|     const auto parameter_size = rp.Pop<u32>(); | ||||
|     const auto object = rp.PopGenericObject(); | ||||
|     const auto buffer = rp.PopStaticBuffer(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called size={}", parameter_size); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(apt->applet_manager->LeaveHomeMenu(object, buffer)); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040
 | ||||
|     const auto size = std::min(std::size_t{rp.Pop<u32>()}, SysMenuArgSize); | ||||
|  | @ -794,8 +933,7 @@ void Module::APTInterface::SendCaptureBufferInfo(Kernel::HLERequestContext& ctx) | |||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     ASSERT(size == 0x20); | ||||
|     apt->screen_capture_buffer = buffer; | ||||
|     apt->applet_manager->SendCaptureBufferInfo(buffer); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|  | @ -807,12 +945,14 @@ void Module::APTInterface::ReceiveCaptureBufferInfo(Kernel::HLERequestContext& c | |||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
| 
 | ||||
|     ASSERT(size == 0x20); | ||||
|     auto screen_capture_buffer = apt->applet_manager->ReceiveCaptureBufferInfo(); | ||||
|     auto real_size = std::min(static_cast<u32>(screen_capture_buffer.size()), size); | ||||
|     screen_capture_buffer.resize(size); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(static_cast<u32>(apt->screen_capture_buffer.size())); | ||||
|     rb.PushStaticBuffer(std::move(apt->screen_capture_buffer), 0); | ||||
|     rb.Push(real_size); | ||||
|     rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -820,13 +960,15 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { | |||
|     const auto size = rp.Pop<u32>(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_APT, "called"); | ||||
|     ASSERT(size == 0x20); | ||||
| 
 | ||||
|     auto screen_capture_buffer = apt->applet_manager->GetCaptureInfo(); | ||||
|     auto real_size = std::min(static_cast<u32>(screen_capture_buffer.size()), size); | ||||
|     screen_capture_buffer.resize(size); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(static_cast<u32>(apt->screen_capture_buffer.size())); | ||||
|     // This service function does not clear the capture buffer.
 | ||||
|     rb.PushStaticBuffer(apt->screen_capture_buffer, 0); | ||||
|     rb.Push(real_size); | ||||
|     rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); | ||||
| } | ||||
| 
 | ||||
| void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) { | ||||
|  |  | |||
|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue