mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40: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: |         case StateChange::Sleep: | ||||||
|             LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); |             LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); | ||||||
|             UNIMPLEMENTED(); |             UNIMPLEMENTED(); | ||||||
|  |             AudioPipeWriteStructAddresses(); | ||||||
|             dsp_state = DspState::Sleeping; |             dsp_state = DspState::Sleeping; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|  | @ -438,7 +439,7 @@ bool DspHle::Impl::Tick() { | ||||||
| 
 | 
 | ||||||
|     parent.OutputFrame(std::move(current_frame)); |     parent.OutputFrame(std::move(current_frame)); | ||||||
| 
 | 
 | ||||||
|     return true; |     return GetDspState() == DspState::On; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DspHle::Impl::AudioTickCallback(s64 cycles_late) { | 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> ConfigureInput::GetUsedKeyboardKeys() { | ||||||
|     QList<QKeySequence> list; |     QList<QKeySequence> list; | ||||||
|     for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { |     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]; |         const auto& button_param = buttons_param[button]; | ||||||
|         if (button_param.Get("engine", "") == "keyboard") { |         if (button_param.Get("engine", "") == "keyboard") { | ||||||
|             list << QKeySequence(button_param.Get("code", 0)); |             list << QKeySequence(button_param.Get("code", 0)); | ||||||
|  |  | ||||||
|  | @ -85,6 +85,7 @@ | ||||||
| #include "core/frontend/applets/default_applets.h" | #include "core/frontend/applets/default_applets.h" | ||||||
| #include "core/frontend/scope_acquire_context.h" | #include "core/frontend/scope_acquire_context.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
|  | #include "core/hle/service/cfg/cfg.h" | ||||||
| #include "core/hle/service/fs/archive.h" | #include "core/hle/service/fs/archive.h" | ||||||
| #include "core/hle/service/nfc/nfc.h" | #include "core/hle/service/nfc/nfc.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
|  | @ -318,6 +319,8 @@ void GMainWindow::InitializeWidgets() { | ||||||
|     updater = new Updater(this); |     updater = new Updater(this); | ||||||
|     UISettings::values.updater_found = updater->HasUpdater(); |     UISettings::values.updater_found = updater->HasUpdater(); | ||||||
| 
 | 
 | ||||||
|  |     UpdateBootHomeMenuState(); | ||||||
|  | 
 | ||||||
|     // Create status bar
 |     // Create status bar
 | ||||||
|     message_label = new QLabel(); |     message_label = new QLabel(); | ||||||
|     // Configured separately for left alignment
 |     // Configured separately for left alignment
 | ||||||
|  | @ -741,6 +744,7 @@ void GMainWindow::ConnectMenuEvents() { | ||||||
|     // File
 |     // File
 | ||||||
|     connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile); |     connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile); | ||||||
|     connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA); |     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_Exit, &QMainWindow::close); | ||||||
|     connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo); |     connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo); | ||||||
|     connect_menu(ui->action_Remove_Amiibo, &GMainWindow::OnRemoveAmiibo); |     connect_menu(ui->action_Remove_Amiibo, &GMainWindow::OnRemoveAmiibo); | ||||||
|  | @ -1601,6 +1605,20 @@ void GMainWindow::OnMenuInstallCIA() { | ||||||
|     InstallCIA(filepaths); |     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) { | void GMainWindow::InstallCIA(QStringList filepaths) { | ||||||
|     ui->action_Install_CIA->setEnabled(false); |     ui->action_Install_CIA->setEnabled(false); | ||||||
|     game_list->SetDirectoryWatcherEnabled(false); |     game_list->SetDirectoryWatcherEnabled(false); | ||||||
|  | @ -1951,6 +1969,7 @@ void GMainWindow::OnConfigure() { | ||||||
|             setMouseTracking(false); |             setMouseTracking(false); | ||||||
|         } |         } | ||||||
|         UpdateSecondaryWindowVisibility(); |         UpdateSecondaryWindowVisibility(); | ||||||
|  |         UpdateBootHomeMenuState(); | ||||||
|     } else { |     } else { | ||||||
|         Settings::values.input_profiles = old_input_profiles; |         Settings::values.input_profiles = old_input_profiles; | ||||||
|         Settings::values.touch_from_button_maps = old_touch_from_button_maps; |         Settings::values.touch_from_button_maps = old_touch_from_button_maps; | ||||||
|  | @ -2244,6 +2263,12 @@ void GMainWindow::UpdateStatusBar() { | ||||||
|     emu_frametime_label->setVisible(true); |     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() { | void GMainWindow::HideMouseCursor() { | ||||||
|     if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) { |     if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) { | ||||||
|         mouse_hide_timer.stop(); |         mouse_hide_timer.stop(); | ||||||
|  |  | ||||||
|  | @ -192,6 +192,7 @@ private slots: | ||||||
|     void OnConfigurePerGame(); |     void OnConfigurePerGame(); | ||||||
|     void OnMenuLoadFile(); |     void OnMenuLoadFile(); | ||||||
|     void OnMenuInstallCIA(); |     void OnMenuInstallCIA(); | ||||||
|  |     void OnMenuBootHomeMenu(); | ||||||
|     void OnUpdateProgress(std::size_t written, std::size_t total); |     void OnUpdateProgress(std::size_t written, std::size_t total); | ||||||
|     void OnCIAInstallReport(Service::AM::InstallStatus status, QString filepath); |     void OnCIAInstallReport(Service::AM::InstallStatus status, QString filepath); | ||||||
|     void OnCIAInstallFinished(); |     void OnCIAInstallFinished(); | ||||||
|  | @ -238,6 +239,7 @@ private slots: | ||||||
| private: | private: | ||||||
|     Q_INVOKABLE void OnMoviePlaybackCompleted(); |     Q_INVOKABLE void OnMoviePlaybackCompleted(); | ||||||
|     void UpdateStatusBar(); |     void UpdateStatusBar(); | ||||||
|  |     void UpdateBootHomeMenuState(); | ||||||
|     void LoadTranslation(); |     void LoadTranslation(); | ||||||
|     void UpdateWindowTitle(); |     void UpdateWindowTitle(); | ||||||
|     void UpdateUISettings(); |     void UpdateUISettings(); | ||||||
|  |  | ||||||
|  | @ -66,6 +66,7 @@ | ||||||
|     </widget> |     </widget> | ||||||
|     <addaction name="action_Load_File"/> |     <addaction name="action_Load_File"/> | ||||||
|     <addaction name="action_Install_CIA"/> |     <addaction name="action_Install_CIA"/> | ||||||
|  |     <addaction name="action_Boot_Home_Menu"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|     <addaction name="menu_recent_files"/> |     <addaction name="menu_recent_files"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|  | @ -209,6 +210,11 @@ | ||||||
|     <string>Install CIA...</string> |     <string>Install CIA...</string> | ||||||
|    </property> |    </property> | ||||||
|   </action> |   </action> | ||||||
|  |   <action name="action_Boot_Home_Menu"> | ||||||
|  |    <property name="text"> | ||||||
|  |     <string>Boot Home Menu</string> | ||||||
|  |    </property> | ||||||
|  |   </action> | ||||||
|   <action name="action_Exit"> |   <action name="action_Exit"> | ||||||
|    <property name="text"> |    <property name="text"> | ||||||
|     <string>E&xit</string> |     <string>E&xit</string> | ||||||
|  |  | ||||||
|  | @ -56,6 +56,11 @@ void Apply() { | ||||||
|             hid->ReloadInputDevices(); |             hid->ReloadInputDevices(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         auto apt = Service::APT::GetModule(system); | ||||||
|  |         if (apt) { | ||||||
|  |             apt->GetAppletManager()->ReloadInputDevices(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         auto sm = system.ServiceManager(); |         auto sm = system.ServiceManager(); | ||||||
|         auto ir_user = sm.GetService<Service::IR::IR_USER>("ir:USER"); |         auto ir_user = sm.GetService<Service::IR::IR_USER>("ir:USER"); | ||||||
|         if (ir_user) |         if (ir_user) | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ ResultCode Applet::Create(Service::APT::AppletId id, Service::APT::AppletId pare | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Service::APT::AppletAttributes attributes; |     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); |     attributes.is_home_menu.Assign(false); | ||||||
|     const auto lock_handle_data = manager->GetLockHandle(attributes); |     const auto lock_handle_data = manager->GetLockHandle(attributes); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -133,6 +133,12 @@ public: | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<Process> CreateProcess(std::shared_ptr<CodeSet> code_set); |     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 |      * Creates and returns a new thread. The new thread is immediately scheduled | ||||||
|      * @param name The friendly name desired for the thread |      * @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; |     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) { | void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { | ||||||
|     for (std::size_t i = 0; i < len; ++i) { |     for (std::size_t i = 0; i < len; ++i) { | ||||||
|         u32 descriptor = kernel_caps[i]; |         u32 descriptor = kernel_caps[i]; | ||||||
|  | @ -208,9 +212,6 @@ void Process::Exit() { | ||||||
|     if (plgldr) { |     if (plgldr) { | ||||||
|         plgldr->OnProcessExit(*this, kernel); |         plgldr->OnProcessExit(*this, kernel); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     // Clear the process's open handles.
 |  | ||||||
|     handle_table.Clear(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| VAddr Process::GetLinearHeapAreaAddress() const { | VAddr Process::GetLinearHeapAreaAddress() const { | ||||||
|  | @ -474,6 +475,8 @@ Kernel::Process::Process(KernelSystem& kernel) | ||||||
|     kernel.memory.RegisterPageTable(vm_manager.page_table); |     kernel.memory.RegisterPageTable(vm_manager.page_table); | ||||||
| } | } | ||||||
| Kernel::Process::~Process() { | 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
 |     // Release all objects this process owns first so that their potential destructor can do clean
 | ||||||
|     // up with this process before further destruction.
 |     // up with this process before further destruction.
 | ||||||
|     // TODO(wwylele): explicitly destroy or invalidate objects this process owns (threads, shared
 |     // TODO(wwylele): explicitly destroy or invalidate objects this process owns (threads, shared
 | ||||||
|  |  | ||||||
|  | @ -565,6 +565,9 @@ void SVC::ExitProcess() { | ||||||
|     // Kill the current thread
 |     // Kill the current thread
 | ||||||
|     kernel.GetCurrentThreadManager().GetCurrentThread()->Stop(); |     kernel.GetCurrentThreadManager().GetCurrentThread()->Stop(); | ||||||
| 
 | 
 | ||||||
|  |     // Remove kernel reference to process so it can be cleaned up.
 | ||||||
|  |     kernel.RemoveProcess(current_process); | ||||||
|  | 
 | ||||||
|     system.PrepareReschedule(); |     system.PrepareReschedule(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1140,6 +1140,7 @@ void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) { | ||||||
| 
 | 
 | ||||||
|     // TODO(shinyquagsire23): Read tickets for this instead?
 |     // TODO(shinyquagsire23): Read tickets for this instead?
 | ||||||
|     bool has_rights = |     bool has_rights = | ||||||
|  |         FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::NAND, tid, content_index)) || | ||||||
|         FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index)); |         FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index)); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|  |  | ||||||
|  | @ -4,17 +4,23 @@ | ||||||
| 
 | 
 | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  | #include "core/frontend/input.h" | ||||||
| #include "core/hle/applets/applet.h" | #include "core/hle/applets/applet.h" | ||||||
| #include "core/hle/service/am/am.h" | #include "core/hle/service/am/am.h" | ||||||
| #include "core/hle/service/apt/applet_manager.h" | #include "core/hle/service/apt/applet_manager.h" | ||||||
| #include "core/hle/service/apt/errors.h" | #include "core/hle/service/apt/errors.h" | ||||||
| #include "core/hle/service/apt/ns.h" | #include "core/hle/service/apt/ns.h" | ||||||
| #include "core/hle/service/cfg/cfg.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) | SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager) | ||||||
| 
 | 
 | ||||||
| namespace Service::APT { | 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 { | struct AppletTitleData { | ||||||
|     // There are two possible applet ids for each applet.
 |     // There are two possible applet ids for each applet.
 | ||||||
|     std::array<AppletId, 2> applet_ids; |     std::array<AppletId, 2> applet_ids; | ||||||
|  | @ -108,6 +114,14 @@ static u64 GetTitleIdForApplet(AppletId id, u32 region_value) { | ||||||
|     return itr->title_ids[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) { | AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) { | ||||||
|     if (id == AppletId::Application) { |     if (id == AppletId::Application) { | ||||||
|         if (GetAppletSlot(AppletSlot::Application)->applet_id != AppletId::None) |         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) |         if (slot_data->applet_id == AppletId::None) | ||||||
|             return AppletSlot::Error; |             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) || |         if ((id == AppletId::AnyLibraryApplet && applet_pos == AppletPos::Library) || | ||||||
|             (id == AppletId::AnySysLibraryApplet && applet_pos == AppletPos::SysLibrary)) |             (id == AppletId::AnySysLibraryApplet && applet_pos == AppletPos::SysLibrary)) | ||||||
|             return AppletSlot::LibraryApplet; |             return AppletSlot::LibraryApplet; | ||||||
|  | @ -163,11 +177,11 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromAttributes(AppletAttri | ||||||
|         AppletSlot::Application,   AppletSlot::LibraryApplet, AppletSlot::SystemApplet, |         AppletSlot::Application,   AppletSlot::LibraryApplet, AppletSlot::SystemApplet, | ||||||
|         AppletSlot::LibraryApplet, AppletSlot::Error,         AppletSlot::LibraryApplet}; |         AppletSlot::LibraryApplet, AppletSlot::Error,         AppletSlot::LibraryApplet}; | ||||||
| 
 | 
 | ||||||
|     auto applet_pos = attributes.applet_pos; |     auto applet_pos_value = static_cast<u32>(attributes.applet_pos.Value()); | ||||||
|     if (applet_pos >= applet_position_slots.size()) |     if (applet_pos_value >= applet_position_slots.size()) | ||||||
|         return AppletSlot::Error; |         return AppletSlot::Error; | ||||||
| 
 | 
 | ||||||
|     auto slot = applet_position_slots[applet_pos]; |     auto slot = applet_position_slots[applet_pos_value]; | ||||||
|     if (slot == AppletSlot::Error) |     if (slot == AppletSlot::Error) | ||||||
|         return AppletSlot::Error; |         return AppletSlot::Error; | ||||||
| 
 | 
 | ||||||
|  | @ -212,13 +226,35 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) { | ||||||
|         // Otherwise, send the parameter the LLE way.
 |         // Otherwise, send the parameter the LLE way.
 | ||||||
|         next_parameter = parameter; |         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
 |         // 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) { |         if (slot != AppletSlot::Error) { | ||||||
|             GetAppletSlot(slot)->parameter_event->Signal(); |             GetAppletSlot(slot)->parameter_event->Signal(); | ||||||
|         } else { |         } else { | ||||||
|             LOG_DEBUG(Service_APT, "No applet was registered with ID {:03X}", |             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) { | ResultVal<MessageParameter> AppletManager::ReceiveParameter(AppletId app_id) { | ||||||
|     auto result = GlanceParameter(app_id); |     auto result = GlanceParameter(app_id); | ||||||
|     if (result.Succeeded()) { |     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
 |         // Clear the parameter
 | ||||||
|         next_parameter = {}; |         next_parameter = {}; | ||||||
|     } |     } | ||||||
|  | @ -282,13 +322,13 @@ bool AppletManager::CancelParameter(bool check_sender, AppletId sender_appid, bo | ||||||
| ResultVal<AppletManager::GetLockHandleResult> AppletManager::GetLockHandle( | ResultVal<AppletManager::GetLockHandleResult> AppletManager::GetLockHandle( | ||||||
|     AppletAttributes attributes) { |     AppletAttributes attributes) { | ||||||
|     auto corrected_attributes = attributes; |     auto corrected_attributes = attributes; | ||||||
|     if (attributes.applet_pos == static_cast<u32>(AppletPos::Library) || |     if (attributes.applet_pos == AppletPos::Library || | ||||||
|         attributes.applet_pos == static_cast<u32>(AppletPos::SysLibrary) || |         attributes.applet_pos == AppletPos::SysLibrary || | ||||||
|         attributes.applet_pos == static_cast<u32>(AppletPos::AutoLibrary)) { |         attributes.applet_pos == AppletPos::AutoLibrary) { | ||||||
|         auto corrected_pos = last_library_launcher_slot == AppletSlot::Application |         auto corrected_pos = last_library_launcher_slot == AppletSlot::Application | ||||||
|                                  ? AppletPos::Library |                                  ? AppletPos::Library | ||||||
|                                  : AppletPos::SysLibrary; |                                  : 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, |         LOG_DEBUG(Service_APT, "Corrected applet attributes from {:08X} to {:08X}", attributes.raw, | ||||||
|                   corrected_attributes.raw); |                   corrected_attributes.raw); | ||||||
|     } |     } | ||||||
|  | @ -350,6 +390,13 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) { | ||||||
|     auto slot_data = GetAppletSlot(slot); |     auto slot_data = GetAppletSlot(slot); | ||||||
|     slot_data->registered = true; |     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
 |     // Send any outstanding parameters to the newly-registered application
 | ||||||
|     if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) { |     if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) { | ||||||
|         // TODO: Real APT would loop trying to send the parameter until it succeeds,
 |         // 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; |     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) { | ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { | ||||||
|     // The real APT service returns an error if there's a pending APT parameter when this function
 |     // The real APT service returns an error if there's a pending APT parameter when this function
 | ||||||
|     // is called.
 |     // 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."); |                "Attempting to close a system applet from a non-system applet."); | ||||||
| 
 | 
 | ||||||
|     auto slot = GetAppletSlot(active_slot); |     auto slot = GetAppletSlot(active_slot); | ||||||
|  |     auto closed_applet_id = slot->applet_id; | ||||||
| 
 | 
 | ||||||
|     active_slot = last_system_launcher_slot; |     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(); |     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; |     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) { | ResultVal<AppletManager::AppletInfo> AppletManager::GetAppletInfo(AppletId app_id) { | ||||||
|     auto slot = GetAppletSlotFromId(app_id); |     auto slot = GetAppletSlotFromId(app_id); | ||||||
|     if (slot == AppletSlot::Error) { |     if (slot == AppletSlot::Error) { | ||||||
|  | @ -709,7 +1048,7 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) { | ||||||
| 
 | 
 | ||||||
| ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) { | ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) { | ||||||
|     if (active_slot == AppletSlot::Error || |     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, |         return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, | ||||||
|                 ErrorLevel::Status}; |                 ErrorLevel::Status}; | ||||||
|     } |     } | ||||||
|  | @ -771,9 +1110,30 @@ ResultCode AppletManager::WakeupApplication() { | ||||||
|     // Send a Wakeup signal via the apt parameter to the application once it registers itself.
 |     // 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
 |     // The real APT service does this by spin waiting on another thread until the application is
 | ||||||
|     // registered.
 |     // registered.
 | ||||||
|     SendApplicationParameterAfterRegistration({.sender_id = AppletId::HomeMenu, |     SendApplicationParameterAfterRegistration({ | ||||||
|                                                .destination_id = AppletId::Application, |         .sender_id = AppletId::HomeMenu, | ||||||
|                                                .signal = SignalType::Wakeup}); |         .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; |     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) { | AppletManager::AppletManager(Core::System& system) : system(system) { | ||||||
|     lock = system.Kernel().CreateMutex(false, "APT_U:Lock"); |     lock = system.Kernel().CreateMutex(false, "APT_U:Lock"); | ||||||
|     for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) { |     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"); |             system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter"); | ||||||
|     } |     } | ||||||
|     HLE::Applets::Init(); |     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() { | AppletManager::~AppletManager() { | ||||||
|     HLE::Applets::Shutdown(); |     HLE::Applets::Shutdown(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AppletManager::ReloadInputDevices() { | ||||||
|  |     is_device_reload_pending.store(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Service::APT
 | } // namespace Service::APT
 | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ | ||||||
| #include <boost/serialization/optional.hpp> | #include <boost/serialization/optional.hpp> | ||||||
| #include <boost/serialization/shared_ptr.hpp> | #include <boost/serialization/shared_ptr.hpp> | ||||||
| #include <boost/serialization/vector.hpp> | #include <boost/serialization/vector.hpp> | ||||||
|  | #include "core/frontend/input.h" | ||||||
| #include "core/global.h" | #include "core/global.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
|  | @ -46,6 +47,21 @@ enum class SignalType : u32 { | ||||||
|     WakeupToLaunchApplication = 0x11, |     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
 | /// App Id's used by APT functions
 | ||||||
| enum class AppletId : u32 { | enum class AppletId : u32 { | ||||||
|     None = 0, |     None = 0, | ||||||
|  | @ -103,19 +119,20 @@ private: | ||||||
|     friend class boost::serialization::access; |     friend class boost::serialization::access; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum class AppletPos { | enum class AppletPos : u32 { | ||||||
|     Application = 0, |     Application = 0, | ||||||
|     Library = 1, |     Library = 1, | ||||||
|     System = 2, |     System = 2, | ||||||
|     SysLibrary = 3, |     SysLibrary = 3, | ||||||
|     Resident = 4, |     Resident = 4, | ||||||
|     AutoLibrary = 5 |     AutoLibrary = 5, | ||||||
|  |     Invalid = 0xFF, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| union AppletAttributes { | union AppletAttributes { | ||||||
|     u32 raw; |     u32 raw; | ||||||
| 
 | 
 | ||||||
|     BitField<0, 3, u32> applet_pos; |     BitField<0, 3, AppletPos> applet_pos; | ||||||
|     BitField<29, 1, u32> is_home_menu; |     BitField<29, 1, u32> is_home_menu; | ||||||
| 
 | 
 | ||||||
|     AppletAttributes() : raw(0) {} |     AppletAttributes() : raw(0) {} | ||||||
|  | @ -178,11 +195,41 @@ private: | ||||||
|     friend class boost::serialization::access; |     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> { | class AppletManager : public std::enable_shared_from_this<AppletManager> { | ||||||
| public: | public: | ||||||
|     explicit AppletManager(Core::System& system); |     explicit AppletManager(Core::System& system); | ||||||
|     ~AppletManager(); |     ~AppletManager(); | ||||||
| 
 | 
 | ||||||
|  |     void ReloadInputDevices(); | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Clears any existing parameter and places a new one. This function is currently only used by |      * 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 |      * HLE Applets and should be likely removed in the future | ||||||
|  | @ -211,6 +258,9 @@ public: | ||||||
|     ResultCode Enable(AppletAttributes attributes); |     ResultCode Enable(AppletAttributes attributes); | ||||||
|     bool IsRegistered(AppletId app_id); |     bool IsRegistered(AppletId app_id); | ||||||
| 
 | 
 | ||||||
|  |     ResultVal<Notification> InquireNotification(AppletId app_id); | ||||||
|  |     ResultCode SendNotification(Notification notification); | ||||||
|  | 
 | ||||||
|     ResultCode PrepareToStartLibraryApplet(AppletId applet_id); |     ResultCode PrepareToStartLibraryApplet(AppletId applet_id); | ||||||
|     ResultCode PreloadLibraryApplet(AppletId applet_id); |     ResultCode PreloadLibraryApplet(AppletId applet_id); | ||||||
|     ResultCode FinishPreloadingLibraryApplet(AppletId applet_id); |     ResultCode FinishPreloadingLibraryApplet(AppletId applet_id); | ||||||
|  | @ -227,6 +277,18 @@ public: | ||||||
|     ResultCode PrepareToCloseSystemApplet(); |     ResultCode PrepareToCloseSystemApplet(); | ||||||
|     ResultCode CloseSystemApplet(std::shared_ptr<Kernel::Object> object, |     ResultCode CloseSystemApplet(std::shared_ptr<Kernel::Object> object, | ||||||
|                                  const std::vector<u8>& buffer); |                                  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, |     ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, | ||||||
|                                           ApplicationJumpFlags flags); |                                           ApplicationJumpFlags flags); | ||||||
|  | @ -239,10 +301,40 @@ public: | ||||||
|         deliver_arg = std::move(arg); |         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 PrepareToStartApplication(u64 title_id, FS::MediaType media_type); | ||||||
|     ResultCode StartApplication(const std::vector<u8>& parameter, const std::vector<u8>& hmac, |     ResultCode StartApplication(const std::vector<u8>& parameter, const std::vector<u8>& hmac, | ||||||
|                                 bool paused); |                                 bool paused); | ||||||
|     ResultCode WakeupApplication(); |     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 { |     struct AppletInfo { | ||||||
|         u64 title_id; |         u64 title_id; | ||||||
|  | @ -273,6 +365,8 @@ private: | ||||||
|     boost::optional<ApplicationStartParameters> app_start_parameters{}; |     boost::optional<ApplicationStartParameters> app_start_parameters{}; | ||||||
|     boost::optional<DeliverArg> deliver_arg{}; |     boost::optional<DeliverArg> deliver_arg{}; | ||||||
| 
 | 
 | ||||||
|  |     boost::optional<CaptureBufferInfo> capture_info; | ||||||
|  | 
 | ||||||
|     static constexpr std::size_t NumAppletSlot = 4; |     static constexpr std::size_t NumAppletSlot = 4; | ||||||
| 
 | 
 | ||||||
|     enum class AppletSlot : u8 { |     enum class AppletSlot : u8 { | ||||||
|  | @ -292,6 +386,7 @@ private: | ||||||
|         bool registered; |         bool registered; | ||||||
|         bool loaded; |         bool loaded; | ||||||
|         AppletAttributes attributes; |         AppletAttributes attributes; | ||||||
|  |         Notification notification; | ||||||
|         std::shared_ptr<Kernel::Event> notification_event; |         std::shared_ptr<Kernel::Event> notification_event; | ||||||
|         std::shared_ptr<Kernel::Event> parameter_event; |         std::shared_ptr<Kernel::Event> parameter_event; | ||||||
| 
 | 
 | ||||||
|  | @ -311,6 +406,7 @@ private: | ||||||
|             ar& registered; |             ar& registered; | ||||||
|             ar& loaded; |             ar& loaded; | ||||||
|             ar& attributes.raw; |             ar& attributes.raw; | ||||||
|  |             ar& notification; | ||||||
|             ar& notification_event; |             ar& notification_event; | ||||||
|             ar& parameter_event; |             ar& parameter_event; | ||||||
|         } |         } | ||||||
|  | @ -325,6 +421,16 @@ private: | ||||||
|     SignalType library_applet_closing_command = SignalType::None; |     SignalType library_applet_closing_command = SignalType::None; | ||||||
|     AppletId last_prepared_library_applet = AppletId::None; |     AppletId last_prepared_library_applet = AppletId::None; | ||||||
|     AppletSlot last_system_launcher_slot = AppletSlot::Error; |     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; |     Core::System& system; | ||||||
| 
 | 
 | ||||||
|  | @ -346,6 +452,11 @@ private: | ||||||
| 
 | 
 | ||||||
|     void EnsureHomeMenuLoaded(); |     void EnsureHomeMenuLoaded(); | ||||||
| 
 | 
 | ||||||
|  |     void CaptureFrameBuffers(); | ||||||
|  | 
 | ||||||
|  |     void LoadInputDevices(); | ||||||
|  |     void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late); | ||||||
|  | 
 | ||||||
|     template <class Archive> |     template <class Archive> | ||||||
|     void serialize(Archive& ar, const unsigned int file_version) { |     void serialize(Archive& ar, const unsigned int file_version) { | ||||||
|         ar& next_parameter; |         ar& next_parameter; | ||||||
|  | @ -358,10 +469,20 @@ private: | ||||||
|             ar& last_library_launcher_slot; |             ar& last_library_launcher_slot; | ||||||
|             ar& last_prepared_library_applet; |             ar& last_prepared_library_applet; | ||||||
|             ar& last_system_launcher_slot; |             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& lock; | ||||||
|  |             ar& capture_info; | ||||||
|         } |         } | ||||||
|         ar& applet_slots; |         ar& applet_slots; | ||||||
|         ar& library_applet_closing_command; |         ar& library_applet_closing_command; | ||||||
|  | 
 | ||||||
|  |         if (Archive::is_loading::value) { | ||||||
|  |             LoadInputDevices(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     friend class boost::serialization::access; |     friend class boost::serialization::access; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -41,7 +41,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) { | ||||||
|     ar& shared_font_relocated; |     ar& shared_font_relocated; | ||||||
|     ar& cpu_percent; |     ar& cpu_percent; | ||||||
|     ar& unknown_ns_state_field; |     ar& unknown_ns_state_field; | ||||||
|     ar& screen_capture_buffer; |  | ||||||
|     ar& screen_capture_post_permission; |     ar& screen_capture_post_permission; | ||||||
|     ar& applet_manager; |     ar& applet_manager; | ||||||
|     if (file_version > 0) { |     if (file_version > 0) { | ||||||
|  | @ -73,6 +72,47 @@ void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx) | ||||||
|     LOG_WARNING(Service_APT, "called size={}", size); |     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) { | void Module::APTInterface::Initialize(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x2, 2, 0); // 0x20080
 |     IPC::RequestParser rp(ctx, 0x2, 2, 0); // 0x20080
 | ||||||
|     const auto app_id = rp.PopEnum<AppletId>(); |     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) { | void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x5, 1, 0); // 0x50040
 |     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); |     LOG_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos); | ||||||
|     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); |     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) { | 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) { | void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0xB, 1, 0); // 0xB0040
 |     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); |     LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id); | ||||||
|     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); |     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) { | void Module::APTInterface::SendParameter(Kernel::HLERequestContext& ctx) { | ||||||
|  | @ -561,6 +613,15 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) { | ||||||
|     rb.Push(apt->applet_manager->WakeupApplication()); |     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) { | void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x4B, 3, 2); // 0x004B00C2
 |     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}", |                 "(STUBBED) called command={:#010X}, input_size={:#010X}, output_size={:#010X}", | ||||||
|                 utility_command, input_size, output_size); |                 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); // No error
 | ||||||
|  |     rb.Push(RESULT_SUCCESS); // Utility function result
 | ||||||
|  |     rb.PushStaticBuffer(out, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) { | 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)); |     rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { | void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x27, 1, 4); |     IPC::RequestParser rp(ctx, 0x21, 0, 0); | ||||||
|     [[maybe_unused]] const auto parameters_size = rp.Pop<u32>(); |  | ||||||
|     [[maybe_unused]] const auto object = rp.PopGenericObject(); |  | ||||||
|     [[maybe_unused]] const auto buffer = rp.PopStaticBuffer(); |  | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_APT, "called"); |     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); |     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) { | 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)); |     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) { | void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040
 |     IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040
 | ||||||
|     const auto size = std::min(std::size_t{rp.Pop<u32>()}, SysMenuArgSize); |     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"); |     LOG_DEBUG(Service_APT, "called"); | ||||||
| 
 | 
 | ||||||
|     ASSERT(size == 0x20); |     apt->applet_manager->SendCaptureBufferInfo(buffer); | ||||||
|     apt->screen_capture_buffer = buffer; |  | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|  | @ -807,12 +945,14 @@ void Module::APTInterface::ReceiveCaptureBufferInfo(Kernel::HLERequestContext& c | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_APT, "called"); |     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); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.Push(static_cast<u32>(apt->screen_capture_buffer.size())); |     rb.Push(real_size); | ||||||
|     rb.PushStaticBuffer(std::move(apt->screen_capture_buffer), 0); |     rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { | void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  | @ -820,13 +960,15 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { | ||||||
|     const auto size = rp.Pop<u32>(); |     const auto size = rp.Pop<u32>(); | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_APT, "called"); |     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); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.Push(static_cast<u32>(apt->screen_capture_buffer.size())); |     rb.Push(real_size); | ||||||
|     // This service function does not clear the capture buffer.
 |     rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); | ||||||
|     rb.PushStaticBuffer(apt->screen_capture_buffer, 0); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) { | 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.
 | /// Each APT service can only have up to 2 sessions connected at the same time.
 | ||||||
| static const u32 MaxAPTSessions = 2; | 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; | constexpr std::size_t SysMenuArgSize = 0x40; | ||||||
| 
 | 
 | ||||||
| enum class StartupArgumentType : u32 { | enum class StartupArgumentType : u32 { | ||||||
|  | @ -87,6 +73,37 @@ public: | ||||||
|          *     0 : Result of function, 0 on success, otherwise error code |          *     0 : Result of function, 0 on success, otherwise error code | ||||||
|          */ |          */ | ||||||
|         void SetWirelessRebootInfo(Kernel::HLERequestContext& ctx); |         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> { |     class APTInterface : public ServiceFramework<APTInterface> { | ||||||
|  | @ -371,6 +388,16 @@ public: | ||||||
|          */ |          */ | ||||||
|         void WakeupApplication(Kernel::HLERequestContext& ctx); |         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 |          * APT::AppletUtility service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|  | @ -491,6 +518,27 @@ public: | ||||||
|          */ |          */ | ||||||
|         void StartSystemApplet(Kernel::HLERequestContext& ctx); |         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 |          * APT::CloseApplication service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|  | @ -629,6 +677,66 @@ public: | ||||||
|          */ |          */ | ||||||
|         void CloseSystemApplet(Kernel::HLERequestContext& ctx); |         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 |          * APT::LoadSysMenuArg service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|  | @ -801,7 +909,6 @@ private: | ||||||
|     // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
 |     // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
 | ||||||
|     u8 unknown_ns_state_field = 0; |     u8 unknown_ns_state_field = 0; | ||||||
| 
 | 
 | ||||||
|     std::vector<u8> screen_capture_buffer; |  | ||||||
|     std::array<u8, SysMenuArgSize> sys_menu_arg_buffer; |     std::array<u8, SysMenuArgSize> sys_menu_arg_buffer; | ||||||
| 
 | 
 | ||||||
|     ScreencapPostPermission screen_capture_post_permission = |     ScreencapPostPermission screen_capture_post_permission = | ||||||
|  |  | ||||||
|  | @ -42,8 +42,8 @@ APT_A::APT_A(std::shared_ptr<Module> apt) | ||||||
|         {0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, |         {0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, | ||||||
|         {0x001F0084, &APT_A::StartSystemApplet, "StartSystemApplet"}, |         {0x001F0084, &APT_A::StartSystemApplet, "StartSystemApplet"}, | ||||||
|         {0x00200044, nullptr, "StartNewestHomeMenu"}, |         {0x00200044, nullptr, "StartNewestHomeMenu"}, | ||||||
|         {0x00210000, nullptr, "OrderToCloseApplication"}, |         {0x00210000, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"}, | ||||||
|         {0x00220040, nullptr, "PrepareToCloseApplication"}, |         {0x00220040, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"}, | ||||||
|         {0x00230040, nullptr, "PrepareToJumpToApplication"}, |         {0x00230040, nullptr, "PrepareToJumpToApplication"}, | ||||||
|         {0x00240044, nullptr, "JumpToApplication"}, |         {0x00240044, nullptr, "JumpToApplication"}, | ||||||
|         {0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, |         {0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, | ||||||
|  | @ -51,11 +51,11 @@ APT_A::APT_A(std::shared_ptr<Module> apt) | ||||||
|         {0x00270044, &APT_A::CloseApplication, "CloseApplication"}, |         {0x00270044, &APT_A::CloseApplication, "CloseApplication"}, | ||||||
|         {0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"}, |         {0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"}, | ||||||
|         {0x00290044, &APT_A::CloseSystemApplet, "CloseSystemApplet"}, |         {0x00290044, &APT_A::CloseSystemApplet, "CloseSystemApplet"}, | ||||||
|         {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, |         {0x002A0000, &APT_A::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, | ||||||
|         {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, |         {0x002B0000, &APT_A::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, | ||||||
|         {0x002C0044, nullptr, "JumpToHomeMenu"}, |         {0x002C0044, &APT_A::JumpToHomeMenu, "JumpToHomeMenu"}, | ||||||
|         {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, |         {0x002D0000, &APT_A::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, | ||||||
|         {0x002E0044, nullptr, "LeaveHomeMenu"}, |         {0x002E0044, &APT_A::LeaveHomeMenu, "LeaveHomeMenu"}, | ||||||
|         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, |         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, | ||||||
|         {0x00300044, nullptr, "LeaveResidentApplet"}, |         {0x00300044, nullptr, "LeaveResidentApplet"}, | ||||||
|         {0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, |         {0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, | ||||||
|  |  | ||||||
|  | @ -42,8 +42,8 @@ APT_S::APT_S(std::shared_ptr<Module> apt) | ||||||
|         {0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, |         {0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, | ||||||
|         {0x001F0084, &APT_S::StartSystemApplet, "StartSystemApplet"}, |         {0x001F0084, &APT_S::StartSystemApplet, "StartSystemApplet"}, | ||||||
|         {0x00200044, nullptr, "StartNewestHomeMenu"}, |         {0x00200044, nullptr, "StartNewestHomeMenu"}, | ||||||
|         {0x00210000, nullptr, "OrderToCloseApplication"}, |         {0x00210000, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"}, | ||||||
|         {0x00220040, nullptr, "PrepareToCloseApplication"}, |         {0x00220040, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"}, | ||||||
|         {0x00230040, nullptr, "PrepareToJumpToApplication"}, |         {0x00230040, nullptr, "PrepareToJumpToApplication"}, | ||||||
|         {0x00240044, nullptr, "JumpToApplication"}, |         {0x00240044, nullptr, "JumpToApplication"}, | ||||||
|         {0x002500C0, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, |         {0x002500C0, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, | ||||||
|  | @ -51,11 +51,11 @@ APT_S::APT_S(std::shared_ptr<Module> apt) | ||||||
|         {0x00270044, &APT_S::CloseApplication, "CloseApplication"}, |         {0x00270044, &APT_S::CloseApplication, "CloseApplication"}, | ||||||
|         {0x00280044, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"}, |         {0x00280044, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"}, | ||||||
|         {0x00290044, &APT_S::CloseSystemApplet, "CloseSystemApplet"}, |         {0x00290044, &APT_S::CloseSystemApplet, "CloseSystemApplet"}, | ||||||
|         {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, |         {0x002A0000, &APT_S::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, | ||||||
|         {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, |         {0x002B0000, &APT_S::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, | ||||||
|         {0x002C0044, nullptr, "JumpToHomeMenu"}, |         {0x002C0044, &APT_S::JumpToHomeMenu, "JumpToHomeMenu"}, | ||||||
|         {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, |         {0x002D0000, &APT_S::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, | ||||||
|         {0x002E0044, nullptr, "LeaveHomeMenu"}, |         {0x002E0044, &APT_S::LeaveHomeMenu, "LeaveHomeMenu"}, | ||||||
|         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, |         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, | ||||||
|         {0x00300044, nullptr, "LeaveResidentApplet"}, |         {0x00300044, nullptr, "LeaveResidentApplet"}, | ||||||
|         {0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, |         {0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, | ||||||
|  |  | ||||||
|  | @ -42,8 +42,8 @@ APT_U::APT_U(std::shared_ptr<Module> apt) | ||||||
|         {0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, |         {0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, | ||||||
|         {0x001F0084, &APT_U::StartSystemApplet, "StartSystemApplet"}, |         {0x001F0084, &APT_U::StartSystemApplet, "StartSystemApplet"}, | ||||||
|         {0x00200044, nullptr, "StartNewestHomeMenu"}, |         {0x00200044, nullptr, "StartNewestHomeMenu"}, | ||||||
|         {0x00210000, nullptr, "OrderToCloseApplication"}, |         {0x00210000, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"}, | ||||||
|         {0x00220040, nullptr, "PrepareToCloseApplication"}, |         {0x00220040, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"}, | ||||||
|         {0x00230040, nullptr, "PrepareToJumpToApplication"}, |         {0x00230040, nullptr, "PrepareToJumpToApplication"}, | ||||||
|         {0x00240044, nullptr, "JumpToApplication"}, |         {0x00240044, nullptr, "JumpToApplication"}, | ||||||
|         {0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, |         {0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, | ||||||
|  | @ -51,11 +51,11 @@ APT_U::APT_U(std::shared_ptr<Module> apt) | ||||||
|         {0x00270044, &APT_U::CloseApplication, "CloseApplication"}, |         {0x00270044, &APT_U::CloseApplication, "CloseApplication"}, | ||||||
|         {0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"}, |         {0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"}, | ||||||
|         {0x00290044, &APT_U::CloseSystemApplet, "CloseSystemApplet"}, |         {0x00290044, &APT_U::CloseSystemApplet, "CloseSystemApplet"}, | ||||||
|         {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, |         {0x002A0000, &APT_U::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, | ||||||
|         {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, |         {0x002B0000, &APT_U::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, | ||||||
|         {0x002C0044, nullptr, "JumpToHomeMenu"}, |         {0x002C0044, &APT_U::JumpToHomeMenu, "JumpToHomeMenu"}, | ||||||
|         {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, |         {0x002D0000, &APT_U::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, | ||||||
|         {0x002E0044, nullptr, "LeaveHomeMenu"}, |         {0x002E0044, &APT_U::LeaveHomeMenu, "LeaveHomeMenu"}, | ||||||
|         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, |         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, | ||||||
|         {0x00300044, nullptr, "LeaveResidentApplet"}, |         {0x00300044, nullptr, "LeaveResidentApplet"}, | ||||||
|         {0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, |         {0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, | ||||||
|  |  | ||||||
|  | @ -19,12 +19,12 @@ NS_S::NS_S(std::shared_ptr<Service::APT::Module> apt) | ||||||
|         {0x00070042, nullptr, "CardUpdateInitialize"}, |         {0x00070042, nullptr, "CardUpdateInitialize"}, | ||||||
|         {0x00080000, nullptr, "CardUpdateShutdown"}, |         {0x00080000, nullptr, "CardUpdateShutdown"}, | ||||||
|         {0x000D0140, nullptr, "SetTWLBannerHMAC"}, |         {0x000D0140, nullptr, "SetTWLBannerHMAC"}, | ||||||
|         {0x000E0000, nullptr, "ShutdownAsync"}, |         {0x000E0000, &NS_S::ShutdownAsync, "ShutdownAsync"}, | ||||||
|         {0x00100180, nullptr, "RebootSystem"}, |         {0x00100180, &NS_S::RebootSystem, "RebootSystem"}, | ||||||
|         {0x00110100, nullptr, "TerminateTitle"}, |         {0x00110100, nullptr, "TerminateTitle"}, | ||||||
|         {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, |         {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, | ||||||
|         {0x00150140, nullptr, "LaunchApplication"}, |         {0x00150140, nullptr, "LaunchApplication"}, | ||||||
|         {0x00160000, nullptr, "RebootSystemClean"}, |         {0x00160000, &NS_S::RebootSystemClean, "RebootSystemClean"}, | ||||||
|     }; |     }; | ||||||
|     RegisterHandlers(functions); |     RegisterHandlers(functions); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -663,14 +663,17 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) { | ||||||
| void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { | void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x18, 0, 0); |     IPC::RequestParser rp(ctx, 0x18, 0, 0); | ||||||
| 
 | 
 | ||||||
|     // TODO(Subv): We're always returning the framebuffer structures for thread_id = 0,
 |     if (active_thread_id == UINT32_MAX) { | ||||||
|     // because we only support a single running application at a time.
 |         LOG_WARNING(Service_GSP, "Called without an active thread."); | ||||||
|     // This should always return the framebuffer data that is currently displayed on the screen.
 |  | ||||||
| 
 | 
 | ||||||
|     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* top_screen = GetFrameBufferInfo(active_thread_id, 0); | ||||||
|     FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1); |     FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1); | ||||||
| 
 | 
 | ||||||
|     struct CaptureInfoEntry { |     struct CaptureInfoEntry { | ||||||
|         u32_le address_left; |         u32_le address_left; | ||||||
|  | @ -700,6 +703,39 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { | ||||||
|     LOG_WARNING(Service_GSP, "called"); |     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) { | void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x16, 1, 2); |     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"}, |         {0x00160042, &GSP_GPU::AcquireRight, "AcquireRight"}, | ||||||
|         {0x00170000, &GSP_GPU::ReleaseRight, "ReleaseRight"}, |         {0x00170000, &GSP_GPU::ReleaseRight, "ReleaseRight"}, | ||||||
|         {0x00180000, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, |         {0x00180000, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, | ||||||
|         {0x00190000, nullptr, "SaveVramSysArea"}, |         {0x00190000, &GSP_GPU::SaveVramSysArea, "SaveVramSysArea"}, | ||||||
|         {0x001A0000, nullptr, "RestoreVramSysArea"}, |         {0x001A0000, &GSP_GPU::RestoreVramSysArea, "RestoreVramSysArea"}, | ||||||
|         {0x001B0000, nullptr, "ResetGpuCore"}, |         {0x001B0000, nullptr, "ResetGpuCore"}, | ||||||
|         {0x001C0040, &GSP_GPU::SetLedForceOff, "SetLedForceOff"}, |         {0x001C0040, &GSP_GPU::SetLedForceOff, "SetLedForceOff"}, | ||||||
|         {0x001D0040, nullptr, "SetTestCommand"}, |         {0x001D0040, nullptr, "SetTestCommand"}, | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include <boost/serialization/base_object.hpp> | #include <boost/serialization/base_object.hpp> | ||||||
|  | #include <boost/serialization/optional.hpp> | ||||||
| #include <boost/serialization/shared_ptr.hpp> | #include <boost/serialization/shared_ptr.hpp> | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | @ -238,6 +239,13 @@ public: | ||||||
|      */ |      */ | ||||||
|     FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); |     FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Retreives the ID of the thread with GPU rights. | ||||||
|  |      */ | ||||||
|  |     u32 GetActiveThreadId() { | ||||||
|  |         return active_thread_id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     /**
 |     /**
 | ||||||
|      * Signals that the specified interrupt type has occurred to userland code for the specified GSP |      * 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); |     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 |      * GSP_GPU::StoreDataCache service function | ||||||
|      * |      * | ||||||
|  | @ -438,6 +472,9 @@ private: | ||||||
| 
 | 
 | ||||||
|     bool first_initialization = true; |     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.
 |     /// Maximum number of threads that can be registered at the same time in the GSP module.
 | ||||||
|     static constexpr u32 MaxGSPThreads = 4; |     static constexpr u32 MaxGSPThreads = 4; | ||||||
| 
 | 
 | ||||||
|  | @ -453,6 +490,7 @@ private: | ||||||
|         ar& active_thread_id; |         ar& active_thread_id; | ||||||
|         ar& first_initialization; |         ar& first_initialization; | ||||||
|         ar& used_thread_ids; |         ar& used_thread_ids; | ||||||
|  |         ar& saved_vram; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     friend class boost::serialization::access; |     friend class boost::serialization::access; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue