diff --git a/src/citron/configuration/shared_translation.cpp b/src/citron/configuration/shared_translation.cpp index 72c99bca0..31afa5dc8 100644 --- a/src/citron/configuration/shared_translation.cpp +++ b/src/citron/configuration/shared_translation.cpp @@ -44,6 +44,11 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral()); INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral()); + // System Applet + INSERT(Settings, qlaunch_enabled, tr("Enable Home Menu (QLaunch)"), + tr("When enabled, pressing the Home button will launch the firmware's Home Menu.\n" + "Requires firmware to be installed.")); + // Audio INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); diff --git a/src/citron/main.cpp b/src/citron/main.cpp index 534bb97eb..2e89716c5 100644 --- a/src/citron/main.cpp +++ b/src/citron/main.cpp @@ -1725,6 +1725,7 @@ void GMainWindow::ConnectMenuEvents() { connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig); // Tools + connect_menu(ui->action_Load_Home_Menu, &GMainWindow::OnQLaunch); connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum); connect_menu(ui->action_Load_Cabinet_Nickname_Owner, [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); }); @@ -1769,7 +1770,8 @@ void GMainWindow::UpdateMenuState() { ui->action_Pause, }; - const std::array applet_actions{ui->action_Load_Album, + const std::array applet_actions{ui->action_Load_Home_Menu, + ui->action_Load_Album, ui->action_Load_Cabinet_Nickname_Owner, ui->action_Load_Cabinet_Eraser, ui->action_Load_Cabinet_Restorer, @@ -2167,6 +2169,9 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP render_window->Exit(); }); + // Set up home menu callback for QLaunch support + SetupHomeMenuCallback(); + connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); @@ -5393,6 +5398,31 @@ void GMainWindow::OnOpenControllerMenu() { LibraryAppletParameters(ControllerAppletId, Service::AM::AppletId::Controller)); } +void GMainWindow::OnQLaunch() { + if (!Settings::values.qlaunch_enabled.GetValue()) { + return; + } + + constexpr u64 QLaunchId = static_cast(Service::AM::AppletProgramId::QLaunch); + auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + QMessageBox::warning(this, tr("No firmware available"), + tr("Please install firmware to use the Home Menu.")); + return; + } + + auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); + if (!qlaunch_nca) { + QMessageBox::warning(this, tr("Home Menu"), + tr("QLaunch applet not found. Please reinstall firmware.")); + return; + } + + const auto filename = QString::fromStdString(qlaunch_nca->GetFullPath()); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); + BootGame(filename, SystemAppletParameters(QLaunchId, Service::AM::AppletId::QLaunch)); +} + void GMainWindow::OnCaptureScreenshot() { if (emu_thread == nullptr || !emu_thread->IsRunning() || !render_window->IsLoadingComplete()) { return; @@ -6110,6 +6140,22 @@ Service::AM::FrontendAppletParameters GMainWindow::LibraryAppletParameters( }; } +Service::AM::FrontendAppletParameters GMainWindow::SystemAppletParameters( + u64 program_id, Service::AM::AppletId applet_id) { + return Service::AM::FrontendAppletParameters{ + .program_id = program_id, + .applet_id = applet_id, + .applet_type = Service::AM::AppletType::SystemApplet, + }; +} + +void GMainWindow::SetupHomeMenuCallback() { + system->GetAppletManager().SetHomeMenuRequestCallback([this]() { + // Use Qt's thread-safe invocation to call OnQLaunch from the main thread + QMetaObject::invokeMethod(this, "OnQLaunch", Qt::QueuedConnection); + }); +} + void VolumeButton::wheelEvent(QWheelEvent* event) { int num_degrees = event->angleDelta().y() / 8; diff --git a/src/citron/main.h b/src/citron/main.h index 661ab49f2..3338d2e2f 100644 --- a/src/citron/main.h +++ b/src/citron/main.h @@ -201,6 +201,8 @@ private: Core::PerfStatsResults last_perf_stats{}; Service::AM::FrontendAppletParameters ApplicationAppletParameters(); Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id, Service::AM::AppletId applet_id); + Service::AM::FrontendAppletParameters SystemAppletParameters(u64 program_id, Service::AM::AppletId applet_id); + void SetupHomeMenuCallback(); std::unique_ptr autoloader_provider; u64 current_title_id{0}; private slots: @@ -288,6 +290,7 @@ private slots: void OnCabinet(Service::NFP::CabinetMode mode); void OnMiiEdit(); void OnOpenControllerMenu(); + void OnQLaunch(); void OnCaptureScreenshot(); void OnCheckFirmwareDecryption(); void OnLanguageChanged(const QString& locale); diff --git a/src/citron/main.ui b/src/citron/main.ui index f09247940..fd9e7c345 100644 --- a/src/citron/main.ui +++ b/src/citron/main.ui @@ -176,6 +176,7 @@ + @@ -487,6 +488,11 @@ Open &Mii Editor + + + Open &Home Menu + + &Configure TAS... diff --git a/src/common/settings.h b/src/common/settings.h index b61c65299..4d0906964 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -169,6 +169,9 @@ struct Values { Setting my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode", Category::LibraryApplet}; + // System Applet + Setting qlaunch_enabled{linkage, true, "qlaunch_enabled", Category::System}; + // Audio SwitchableSetting sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio, Specialization::RuntimeList}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8778364f0..190ee1021 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -467,6 +467,8 @@ add_library(core STATIC hle/service/am/service/application_creator.h hle/service/am/service/application_functions.cpp hle/service/am/service/application_functions.h + hle/service/am/service/application_observer.cpp + hle/service/am/service/application_observer.h hle/service/am/service/application_proxy.cpp hle/service/am/service/application_proxy.h hle/service/am/service/application_proxy_service.cpp @@ -509,6 +511,8 @@ add_library(core STATIC hle/service/am/service/system_applet_proxy.h hle/service/am/service/system_application_proxy.cpp hle/service/am/service/system_application_proxy.h + hle/service/am/service/system_process_common_functions.cpp + hle/service/am/service/system_process_common_functions.h hle/service/am/service/window_controller.cpp hle/service/am/service/window_controller.h hle/service/am/window_system.cpp diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index c6b7ec8bb..7b5bdf2b3 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -335,4 +335,11 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { applet->process->Run(); } +void AppletManager::SetHomeMenuRequestCallback(std::function callback) { + std::unique_lock lk{m_lock}; + if (m_window_system) { + m_window_system->SetHomeMenuRequestCallback(std::move(callback)); + } +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h index fbdc77140..62a797056 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "core/hle/service/am/am_types.h" @@ -46,6 +47,7 @@ public: public: void SetWindowSystem(WindowSystem* window_system); + void SetHomeMenuRequestCallback(std::function callback); private: Core::System& m_system; diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp index ca27c72d2..475959c18 100644 --- a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp +++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" @@ -11,6 +11,7 @@ #include "core/hle/service/am/service/overlay_applet_proxy.h" #include "core/hle/service/am/service/system_applet_proxy.h" #include "core/hle/service/am/service/system_application_proxy.h" +#include "core/hle/service/am/service/system_process_common_functions.h" #include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" @@ -22,12 +23,15 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys // clang-format off static const FunctionInfo functions[] = { {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"}, + {110, D<&IAllSystemAppletProxiesService::OpenHomeMenuProxy>, "OpenHomeMenuProxy"}, {200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"}, {201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"}, {300, D<&IAllSystemAppletProxiesService::OpenOverlayAppletProxy>, "OpenOverlayAppletProxy"}, {350, D<&IAllSystemAppletProxiesService::OpenSystemApplicationProxy>, "OpenSystemApplicationProxy"}, {400, D<&IAllSystemAppletProxiesService::CreateSelfLibraryAppletCreatorForDevelop>, "CreateSelfLibraryAppletCreatorForDevelop"}, {410, D<&IAllSystemAppletProxiesService::GetSystemAppletControllerForDebug>, "GetSystemAppletControllerForDebug"}, + {450, D<&IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions>, "GetSystemProcessCommonFunctions"}, + {460, D<&IAllSystemAppletProxiesService::Cmd460>, "Cmd460"}, {1000, D<&IAllSystemAppletProxiesService::GetDebugFunctions>, "GetDebugFunctions"}, }; // clang-format on @@ -52,6 +56,21 @@ Result IAllSystemAppletProxiesService::OpenSystemAppletProxy( } } +Result IAllSystemAppletProxiesService::OpenHomeMenuProxy( + Out> out_system_applet_proxy, ClientProcessId pid, + InCopyHandle process_handle) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = this->GetAppletFromProcessId(pid); applet) { + *out_system_applet_proxy = std::make_shared( + system, applet, process_handle.Get(), m_window_system); + R_SUCCEED(); + } else { + LOG_ERROR(Service_AM, "Home menu applet doesn't exist for process_id={}", pid.pid); + R_THROW(ResultUnknown); + } +} + Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy( Out> out_library_applet_proxy, ClientProcessId pid, InCopyHandle process_handle, @@ -130,6 +149,18 @@ Result IAllSystemAppletProxiesService::GetSystemAppletControllerForDebug() { R_THROW(ResultUnknown); } +Result IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions( + Out> out_system_process_common_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_system_process_common_functions = std::make_shared(system); + R_SUCCEED(); +} + +Result IAllSystemAppletProxiesService::Cmd460() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + Result IAllSystemAppletProxiesService::GetDebugFunctions( Out> out_debug_functions) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.h b/src/core/hle/service/am/service/all_system_applet_proxies_service.h index f13a7ea12..72c9252ba 100644 --- a/src/core/hle/service/am/service/all_system_applet_proxies_service.h +++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,6 +19,7 @@ class ILibraryAppletProxy; class IOverlayAppletProxy; class ISystemAppletProxy; class ISystemApplicationProxy; +class ISystemProcessCommonFunctions; class WindowSystem; class IAllSystemAppletProxiesService final @@ -31,6 +32,9 @@ private: Result OpenSystemAppletProxy(Out> out_system_applet_proxy, ClientProcessId pid, InCopyHandle process_handle); + Result OpenHomeMenuProxy(Out> out_system_applet_proxy, + ClientProcessId pid, + InCopyHandle process_handle); Result OpenLibraryAppletProxy(Out> out_library_applet_proxy, ClientProcessId pid, InCopyHandle process_handle, @@ -49,6 +53,9 @@ private: ClientProcessId pid, InCopyHandle process_handle); Result GetSystemAppletControllerForDebug(); + Result GetSystemProcessCommonFunctions( + Out> out_system_process_common_functions); + Result Cmd460(); Result GetDebugFunctions(Out> out_debug_functions); private: diff --git a/src/core/hle/service/am/service/applet_common_functions.cpp b/src/core/hle/service/am/service/applet_common_functions.cpp index a051000af..cabc68667 100644 --- a/src/core/hle/service/am/service/applet_common_functions.cpp +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/applet.h" #include "core/hle/service/am/service/applet_common_functions.h" #include "core/hle/service/cmif_serialization.h" @@ -28,10 +30,24 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, {70, D<&IAppletCommonFunctions::SetCpuBoostRequestPriority>, "SetCpuBoostRequestPriority"}, {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"}, {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"}, + {82, nullptr, "SetBlockingCaptureButtonInEntireSystem"}, {90, nullptr, "OpenNamedChannelAsParent"}, {91, nullptr, "OpenNamedChannelAsChild"}, {100, nullptr, "SetApplicationCoreUsageMode"}, + {160, nullptr, "GetNotificationReceiverService"}, + {161, nullptr, "GetNotificationSenderService"}, {300, D<&IAppletCommonFunctions::GetCurrentApplicationId>, "GetCurrentApplicationId"}, + {310, D<&IAppletCommonFunctions::IsSystemAppletHomeMenu>, "IsSystemAppletHomeMenu"}, + {311, D<&IAppletCommonFunctions::Cmd311>, "Cmd311"}, + {320, D<&IAppletCommonFunctions::SetGpuTimeSliceBoost>, "SetGpuTimeSliceBoost"}, + {321, D<&IAppletCommonFunctions::SetGpuTimeSliceBoostDueToApplication>, "SetGpuTimeSliceBoostDueToApplication"}, + {322, D<&IAppletCommonFunctions::Cmd322>, "Cmd322"}, + {330, D<&IAppletCommonFunctions::Cmd330>, "Cmd330"}, + {340, D<&IAppletCommonFunctions::Cmd340>, "Cmd340"}, + {341, D<&IAppletCommonFunctions::Cmd341>, "Cmd341"}, + {342, D<&IAppletCommonFunctions::Cmd342>, "Cmd342"}, + {350, D<&IAppletCommonFunctions::Cmd350>, "Cmd350"}, + {360, D<&IAppletCommonFunctions::Cmd360>, "Cmd360"}, }; // clang-format on @@ -67,4 +83,60 @@ Result IAppletCommonFunctions::GetCurrentApplicationId(Out out_application_ R_SUCCEED(); } +Result IAppletCommonFunctions::IsSystemAppletHomeMenu(Out out_is_home_menu) { + LOG_WARNING(Service_AM, "(STUBBED) called [19.0.0+]"); + *out_is_home_menu = applet->applet_id == AppletId::QLaunch; + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd311() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::SetGpuTimeSliceBoost(u64 boost) { + LOG_WARNING(Service_AM, "(STUBBED) called, boost={} [19.0.0+]", boost); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::SetGpuTimeSliceBoostDueToApplication(u64 boost) { + LOG_WARNING(Service_AM, "(STUBBED) called, boost={} [19.0.0+]", boost); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd322() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd330() { + LOG_WARNING(Service_AM, "(STUBBED) called [19.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd340() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd341() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd342() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd350() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IAppletCommonFunctions::Cmd360() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/applet_common_functions.h b/src/core/hle/service/am/service/applet_common_functions.h index 376f85acf..a42e57f8c 100644 --- a/src/core/hle/service/am/service/applet_common_functions.h +++ b/src/core/hle/service/am/service/applet_common_functions.h @@ -20,6 +20,17 @@ private: Result GetHomeButtonDoubleClickEnabled(Out out_home_button_double_click_enabled); Result SetCpuBoostRequestPriority(s32 priority); Result GetCurrentApplicationId(Out out_application_id); + Result IsSystemAppletHomeMenu(Out out_is_home_menu); + Result Cmd311(); + Result SetGpuTimeSliceBoost(u64 boost); + Result SetGpuTimeSliceBoostDueToApplication(u64 boost); + Result Cmd322(); + Result Cmd330(); + Result Cmd340(); + Result Cmd341(); + Result Cmd342(); + Result Cmd350(); + Result Cmd360(); const std::shared_ptr applet; }; diff --git a/src/core/hle/service/am/service/application_accessor.cpp b/src/core/hle/service/am/service/application_accessor.cpp index 986abc716..ab9337931 100644 --- a/src/core/hle/service/am/service/application_accessor.cpp +++ b/src/core/hle/service/am/service/application_accessor.cpp @@ -47,6 +47,8 @@ IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_pt {190, nullptr, "PushToNotificationStorageChannel"}, {200, nullptr, "RequestApplicationSoftReset"}, {201, nullptr, "RestartApplicationTimer"}, + {300, D<&IApplicationAccessor::Cmd300>, "Cmd300"}, + {301, D<&IApplicationAccessor::Cmd301>, "Cmd301"}, }; // clang-format on @@ -156,4 +158,14 @@ Result IApplicationAccessor::ReportApplicationExitTimeout() { R_SUCCEED(); } +Result IApplicationAccessor::Cmd300() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IApplicationAccessor::Cmd301() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_accessor.h b/src/core/hle/service/am/service/application_accessor.h index b9797bcc0..dde1113ab 100644 --- a/src/core/hle/service/am/service/application_accessor.h +++ b/src/core/hle/service/am/service/application_accessor.h @@ -35,6 +35,8 @@ private: Result CheckRightsEnvironmentAvailable(Out out_is_available); Result GetNsRightsEnvironmentHandle(Out out_handle); Result ReportApplicationExitTimeout(); + Result Cmd300(); + Result Cmd301(); WindowSystem& m_window_system; const std::shared_ptr m_applet; diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index 001b18ec8..75d528733 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" @@ -69,6 +69,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ {102, D<&IApplicationFunctions::SetApplicationCopyrightVisibility>, "SetApplicationCopyrightVisibility"}, {110, D<&IApplicationFunctions::QueryApplicationPlayStatistics>, "QueryApplicationPlayStatistics"}, {111, D<&IApplicationFunctions::QueryApplicationPlayStatisticsByUid>, "QueryApplicationPlayStatisticsByUid"}, + {112, D<&IApplicationFunctions::Cmd112>, "Cmd112"}, + {113, D<&IApplicationFunctions::Cmd113>, "Cmd113"}, {120, D<&IApplicationFunctions::ExecuteProgram>, "ExecuteProgram"}, {121, D<&IApplicationFunctions::ClearUserChannel>, "ClearUserChannel"}, {122, D<&IApplicationFunctions::UnpopToUserChannel>, "UnpopToUserChannel"}, @@ -88,7 +90,10 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ {200, nullptr, "GetLastApplicationExitReason"}, {210, D<&IApplicationFunctions::GetLaunchRequiredVersionUpgrade>, "GetLaunchRequiredVersionUpgrade"}, {211, nullptr, "GetLaunchRequiredVersionUpgradeStatus"}, + {220, D<&IApplicationFunctions::Cmd220>, "Cmd220"}, {300, nullptr, "RequestToLaunchApplication"}, + {310, D<&IApplicationFunctions::Cmd310>, "Cmd310"}, + {320, D<&IApplicationFunctions::Cmd320>, "Cmd320"}, {301, nullptr, "RequestToLaunchApplicationWithUserAndArguments"}, {310, nullptr, "RequestToLaunchApplicationWithArgumentsAndUserSelectionAndError"}, {330, D<&IApplicationFunctions::Unknown330>, "Unknown330"}, // [20.2.0+] @@ -532,4 +537,29 @@ Result IApplicationFunctions::Unknown330() { R_SUCCEED(); } +Result IApplicationFunctions::Cmd112() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IApplicationFunctions::Cmd113() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IApplicationFunctions::Cmd220() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IApplicationFunctions::Cmd310() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result IApplicationFunctions::Cmd320() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h index 7cc33c911..77f18399f 100644 --- a/src/core/hle/service/am/service/application_functions.h +++ b/src/core/hle/service/am/service/application_functions.h @@ -79,6 +79,11 @@ private: Result PrepareForJit(); Result GetLaunchRequiredVersionUpgrade(OutCopyHandle out_event); Result Unknown330(); // [20.2.0+] + Result Cmd112(); + Result Cmd113(); + Result Cmd220(); + Result Cmd310(); + Result Cmd320(); const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/application_observer.cpp b/src/core/hle/service/am/service/application_observer.cpp new file mode 100644 index 000000000..f3d5b0cb8 --- /dev/null +++ b/src/core/hle/service/am/service/application_observer.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/application_observer.h" + +namespace Service::AM { + +IApplicationObserver::IApplicationObserver(Core::System& system_) + : ServiceFramework{system_, "IApplicationObserver"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {10, nullptr, "Unknown10"}, + {20, nullptr, "Unknown20"}, + {30, nullptr, "Unknown30"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationObserver::~IApplicationObserver() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_observer.h b/src/core/hle/service/am/service/application_observer.h new file mode 100644 index 000000000..4ac5c4619 --- /dev/null +++ b/src/core/hle/service/am/service/application_observer.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IApplicationObserver final : public ServiceFramework { +public: + explicit IApplicationObserver(Core::System& system_); + ~IApplicationObserver() override; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/common_state_getter.cpp b/src/core/hle/service/am/service/common_state_getter.cpp index c8efa6bf8..f48692bad 100644 --- a/src/core/hle/service/am/service/common_state_getter.cpp +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" @@ -82,13 +82,13 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr, "GetGpuErrorDetectedSystemEvent"}, {1021, nullptr, "SetDelayTimeToAbortOnGpuError"}, - {1030, nullptr, "GetFriendInvitationStorageChannelEvent"}, + {1030, D<&ICommonStateGetter::GetFriendInvitationStorageChannelEvent>, "GetFriendInvitationStorageChannelEvent"}, {1031, nullptr, "TryPopFromFriendInvitationStorageChannel"}, - {1040, nullptr, "GetNotificationStorageChannelEvent"}, + {1040, D<&ICommonStateGetter::GetNotificationStorageChannelEvent>, "GetNotificationStorageChannelEvent"}, {1041, nullptr, "TryPopFromNotificationStorageChannel"}, - {1050, nullptr, "GetHealthWarningDisappearedSystemEvent"}, + {1050, D<&ICommonStateGetter::GetHealthWarningDisappearedSystemEvent>, "GetHealthWarningDisappearedSystemEvent"}, {1060, nullptr, "SetHdcpAuthenticationActivated"}, {1061, nullptr, "GetLastForegroundCaptureImageEx"}, {1062, nullptr, "GetLastApplicationCaptureImageEx"}, @@ -297,4 +297,32 @@ Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnab R_SUCCEED(); } +Result ICommonStateGetter::GetGpuErrorDetectedSystemEvent( + OutCopyHandle out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->gpu_error_detected_event.GetHandle(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetFriendInvitationStorageChannelEvent( + OutCopyHandle out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->friend_invitation_storage_channel_event.GetHandle(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetNotificationStorageChannelEvent( + OutCopyHandle out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->notification_storage_channel_event.GetHandle(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetHealthWarningDisappearedSystemEvent( + OutCopyHandle out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->health_warning_disappeared_system_event.GetHandle(); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/common_state_getter.h b/src/core/hle/service/am/service/common_state_getter.h index 59a46fa94..255884c3a 100644 --- a/src/core/hle/service/am/service/common_state_getter.h +++ b/src/core/hle/service/am/service/common_state_getter.h @@ -53,6 +53,10 @@ private: OutArray out_applet_ids); Result GetSettingsPlatformRegion(Out out_settings_platform_region); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); + Result GetGpuErrorDetectedSystemEvent(OutCopyHandle out_event); + Result GetFriendInvitationStorageChannelEvent(OutCopyHandle out_event); + Result GetNotificationStorageChannelEvent(OutCopyHandle out_event); + Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle out_event); void SetCpuBoostMode(HLERequestContext& ctx); diff --git a/src/core/hle/service/am/service/debug_functions.cpp b/src/core/hle/service/am/service/debug_functions.cpp index fcac4776d..9cb1d706b 100644 --- a/src/core/hle/service/am/service/debug_functions.cpp +++ b/src/core/hle/service/am/service/debug_functions.cpp @@ -31,7 +31,13 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"}, {300, nullptr, "TerminateAllRunningApplicationsForDebug"}, + {410, nullptr, "CreateGeneralStorageForDebug"}, + {411, nullptr, "ReadGeneralStorageForDebug"}, + {412, nullptr, "WriteGeneralStorageForDebug"}, + {430, nullptr, "Cmd430"}, + {431, nullptr, "Cmd431"}, {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, + {910, nullptr, "Cmd910"}, }; // clang-format on diff --git a/src/core/hle/service/am/service/global_state_controller.cpp b/src/core/hle/service/am/service/global_state_controller.cpp index 1894aa6d0..98c012146 100644 --- a/src/core/hle/service/am/service/global_state_controller.cpp +++ b/src/core/hle/service/am/service/global_state_controller.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/service/cradle_firmware_updater.h" @@ -10,7 +10,8 @@ namespace Service::AM { IGlobalStateController::IGlobalStateController(Core::System& system_) : ServiceFramework{system_, "IGlobalStateController"}, - m_context{system_, "IGlobalStateController"}, m_hdcp_authentication_failed_event{m_context} { + m_context{system_, "IGlobalStateController"}, m_hdcp_authentication_failed_event{m_context}, + m_accumulated_suspended_tick_changed_event{m_context} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IGlobalStateController::RequestToEnterSleep>, "RequestToEnterSleep"}, @@ -35,7 +36,7 @@ IGlobalStateController::IGlobalStateController(Core::System& system_) {60, nullptr, "SetWirelessPriorityMode"}, {61, nullptr, "GetWirelessPriorityMode"}, {62, nullptr, "GetAccumulatedSuspendedTickValue"}, - {63, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, + {63, D<&IGlobalStateController::GetAccumulatedSuspendedTickChangedEvent>, "GetAccumulatedSuspendedTickChangedEvent"}, {64, nullptr, "SetAlarmTimeChangeEvent"}, {65, nullptr, "GetWakeupCount"}, {66, nullptr, "GetHomeButtonInputProtectionStartTime"}, @@ -130,4 +131,11 @@ Result IGlobalStateController::UpdateDefaultDisplayResolution() { R_SUCCEED(); } +Result IGlobalStateController::GetAccumulatedSuspendedTickChangedEvent( + OutCopyHandle out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_accumulated_suspended_tick_changed_event.GetHandle(); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/global_state_controller.h b/src/core/hle/service/am/service/global_state_controller.h index 49891cc36..f11dc9ce3 100644 --- a/src/core/hle/service/am/service/global_state_controller.h +++ b/src/core/hle/service/am/service/global_state_controller.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -33,9 +33,11 @@ private: Result GetHdcpAuthenticationFailedEvent(OutCopyHandle out_event); Result OpenCradleFirmwareUpdater( Out> out_cradle_firmware_updater); + Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle out_event); KernelHelpers::ServiceContext m_context; Event m_hdcp_authentication_failed_event; + Event m_accumulated_suspended_tick_changed_event; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/service/home_menu_functions.cpp b/src/core/hle/service/am/service/home_menu_functions.cpp index 62e792560..65a220c61 100644 --- a/src/core/hle/service/am/service/home_menu_functions.cpp +++ b/src/core/hle/service/am/service/home_menu_functions.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/result.h" @@ -31,6 +31,8 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr, "IsRebootEnabled"}, {50, D<&IHomeMenuFunctions::LaunchSystemApplet>, "LaunchSystemApplet"}, {51, D<&IHomeMenuFunctions::LaunchStarter>, "LaunchStarter"}, + {60, D<&IHomeMenuFunctions::Cmd60>, "Cmd60"}, + {61, D<&IHomeMenuFunctions::Cmd61>, "Cmd61"}, {100, D<&IHomeMenuFunctions::PopRequestLaunchApplicationForDebug>, "PopRequestLaunchApplicationForDebug"}, {110, D<&IHomeMenuFunctions::IsForceTerminateApplicationDisabledForDebug>, "IsForceTerminateApplicationDisabledForDebug"}, {200, D<&IHomeMenuFunctions::LaunchDevMenu>, "LaunchDevMenu"}, @@ -149,4 +151,14 @@ Result IHomeMenuFunctions::SetLastApplicationExitReason(s32 exit_reason) { R_SUCCEED(); } +Result IHomeMenuFunctions::Cmd60() { + LOG_WARNING(Service_AM, "(STUBBED) called [19.0.0+]"); + R_SUCCEED(); +} + +Result IHomeMenuFunctions::Cmd61() { + LOG_WARNING(Service_AM, "(STUBBED) called [19.0.0+]"); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/home_menu_functions.h b/src/core/hle/service/am/service/home_menu_functions.h index ff89992ea..115dc2dba 100644 --- a/src/core/hle/service/am/service/home_menu_functions.h +++ b/src/core/hle/service/am/service/home_menu_functions.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -39,6 +39,8 @@ private: Out out_is_force_terminate_application_disabled_for_debug); Result LaunchDevMenu(); Result SetLastApplicationExitReason(s32 exit_reason); + Result Cmd60(); + Result Cmd61(); WindowSystem& m_window_system; const std::shared_ptr m_applet; diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index 157c854b5..6e494bf28 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" @@ -74,9 +74,12 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr {160, nullptr, "SetCpuBoostRequestPriority"}, {170, nullptr, "GetCurrentPerformanceConfiguration"}, {180, nullptr, "GetOperationModeSystemInfo"}, - {200, nullptr, "GetSettingsPlatformRegion"}, - {210, nullptr, "ActivateMigrationService"}, - {211, nullptr, "DeactivateMigrationService"}, + {200, D<&ISelfController::Cmd200>, "Cmd200"}, + {210, D<&ISelfController::Cmd210>, "Cmd210"}, + {211, D<&ISelfController::Cmd211>, "Cmd211"}, + {220, D<&ISelfController::Cmd220>, "Cmd220"}, + {221, D<&ISelfController::Cmd221>, "Cmd221"}, + {230, D<&ISelfController::Cmd230>, "Cmd230"}, {300, nullptr, "SendMessage"}, {301, nullptr, "ReceiveMessage"}, {400, nullptr, "CreateAlbumAccessorApplicationAlbumEntry"}, @@ -424,4 +427,34 @@ Result ISelfController::SetRecordVolumeMuted(bool muted) { R_SUCCEED(); } +Result ISelfController::Cmd200() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result ISelfController::Cmd210() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result ISelfController::Cmd211() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result ISelfController::Cmd220() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result ISelfController::Cmd221() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + +Result ISelfController::Cmd230() { + LOG_WARNING(Service_AM, "(STUBBED) called [20.0.0+]"); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h index eca083cfe..c7e293786 100644 --- a/src/core/hle/service/am/service/self_controller.h +++ b/src/core/hle/service/am/service/self_controller.h @@ -63,6 +63,12 @@ private: Result SetAlbumImageTakenNotificationEnabled(bool enabled); Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option); Result SetRecordVolumeMuted(bool muted); + Result Cmd200(); + Result Cmd210(); + Result Cmd211(); + Result Cmd220(); + Result Cmd221(); + Result Cmd230(); Kernel::KProcess* const m_process; const std::shared_ptr m_applet; diff --git a/src/core/hle/service/am/service/system_process_common_functions.cpp b/src/core/hle/service/am/service/system_process_common_functions.cpp new file mode 100644 index 000000000..6c0a8c7ea --- /dev/null +++ b/src/core/hle/service/am/service/system_process_common_functions.cpp @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/application_observer.h" +#include "core/hle/service/am/service/system_process_common_functions.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +ISystemProcessCommonFunctions::ISystemProcessCommonFunctions(Core::System& system_) + : ServiceFramework{system_, "ISystemProcessCommonFunctions"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ISystemProcessCommonFunctions::GetApplicationObserver>, "GetApplicationObserver"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISystemProcessCommonFunctions::~ISystemProcessCommonFunctions() = default; + +Result ISystemProcessCommonFunctions::GetApplicationObserver( + Out> out_observer) { + LOG_DEBUG(Service_AM, "called"); + *out_observer = std::make_shared(system); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/system_process_common_functions.h b/src/core/hle/service/am/service/system_process_common_functions.h new file mode 100644 index 000000000..dd5865a6e --- /dev/null +++ b/src/core/hle/service/am/service/system_process_common_functions.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IApplicationObserver; + +class ISystemProcessCommonFunctions final + : public ServiceFramework { +public: + explicit ISystemProcessCommonFunctions(Core::System& system_); + ~ISystemProcessCommonFunctions() override; + +private: + Result GetApplicationObserver(Out> out_observer); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index 5cf24007c..f4e4e2715 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -139,8 +139,11 @@ void WindowSystem::OnExitRequested() { void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) { std::scoped_lock lk{m_lock}; - // If we don't have a home menu, nothing to do. + // If we don't have a home menu, request frontend to launch QLaunch. if (!m_home_menu) { + if (m_home_menu_request_callback && type == ButtonPressDuration::ShortPressing) { + m_home_menu_request_callback(); + } return; } @@ -312,4 +315,9 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) { } } +void WindowSystem::SetHomeMenuRequestCallback(HomeMenuRequestCallback callback) { + std::scoped_lock lk{m_lock}; + m_home_menu_request_callback = std::move(callback); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/window_system.h b/src/core/hle/service/am/window_system.h index 69e7a27ba..4e1bf4cb4 100644 --- a/src/core/hle/service/am/window_system.h +++ b/src/core/hle/service/am/window_system.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -24,6 +25,8 @@ enum class ButtonPressDuration { LongPressing, }; +using HomeMenuRequestCallback = std::function; + class WindowSystem { public: explicit WindowSystem(Core::System& system); @@ -52,6 +55,9 @@ public: void OnCaptureButtonPressed(ButtonPressDuration type) {} void OnPowerButtonPressed(ButtonPressDuration type) {} +public: + void SetHomeMenuRequestCallback(HomeMenuRequestCallback callback); + private: void PruneTerminatedAppletsLocked(); bool LockHomeMenuIntoForegroundLocked(); @@ -78,6 +84,9 @@ private: // Applet map by aruid. std::map> m_applets{}; + + // Callback for requesting home menu launch from frontend. + HomeMenuRequestCallback m_home_menu_request_callback{}; }; } // namespace Service::AM diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp index 1dbcacbb9..a7a50c093 100644 --- a/src/core/hle/service/audio/audio_controller.cpp +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" @@ -72,7 +72,7 @@ IAudioController::IAudioController(Core::System& system_) {50001, D<&IAudioController::OverrideDefaultTargetForDebug>, "OverrideDefaultTargetForDebug"}, // [19.0.0-19.0.1] {50003, D<&IAudioController::SetForceOverrideExternalDeviceNameForDebug>, "SetForceOverrideExternalDeviceNameForDebug"}, // [19.0.0+] {50004, D<&IAudioController::ClearForceOverrideExternalDeviceNameForDebug>, "ClearForceOverrideExternalDeviceNameForDebug"}, // [19.0.0+] - {5000, nullptr, "Unknown5000"}, // [19.0.0+] + {5000, D<&IAudioController::GetAudioController>, "GetAudioController"}, // [19.0.0+] {10200, D<&IAudioController::Unknown10200>, "Unknown10200"}, // [20.0.0+] }; // clang-format on @@ -407,4 +407,11 @@ Result IAudioController::Unknown10200() { R_SUCCEED(); } +Result IAudioController::GetAudioController( + Out> out_audio_controller) { + LOG_DEBUG(Audio, "called GetAudioController [19.0.0+]"); + *out_audio_controller = std::make_shared(system); + R_SUCCEED(); +} + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h index 9a4e06e5e..5ef671e5c 100644 --- a/src/core/hle/service/audio/audio_controller.h +++ b/src/core/hle/service/audio/audio_controller.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -90,6 +90,7 @@ private: Result OverrideDefaultTargetForDebug(u32 target); // [19.0.0-19.0.1] Result SetForceOverrideExternalDeviceNameForDebug(InLargeData, BufferAttr_HipcMapAlias> device_name); // [19.0.0+] Result ClearForceOverrideExternalDeviceNameForDebug(); // [19.0.0+] + Result GetAudioController(Out> out_audio_controller); // [19.0.0+] Result Unknown10200(); // [20.0.0+] KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp index 7a91727f9..d4a586fcd 100644 --- a/src/core/hle/service/ns/application_manager_interface.cpp +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -303,6 +303,94 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {3013, nullptr, "IsGameCardEnabled"}, {3014, nullptr, "IsLocalContentShareEnabled"}, {3050, nullptr, "ListAssignELicenseTaskResult"}, + // [20.0.0+] + {4000, nullptr, "Cmd4000"}, + {4004, nullptr, "Cmd4004"}, + {4006, nullptr, "Cmd4006"}, + {4007, nullptr, "Cmd4007"}, + {4008, nullptr, "Cmd4008"}, + {4009, nullptr, "Cmd4009"}, + {4010, nullptr, "Cmd4010"}, + {4011, nullptr, "Cmd4011"}, + {4012, nullptr, "Cmd4012"}, + {4013, nullptr, "Cmd4013"}, + {4015, nullptr, "Cmd4015"}, + {4017, nullptr, "Cmd4017"}, + {4019, nullptr, "Cmd4019"}, + {4020, nullptr, "Cmd4020"}, + {4021, nullptr, "Cmd4021"}, + {4022, D<&IApplicationManagerInterface::Cmd4022>, "Cmd4022"}, + {4023, D<&IApplicationManagerInterface::Cmd4023>, "Cmd4023"}, + {4024, nullptr, "Cmd4024"}, + {4025, nullptr, "Cmd4025"}, + {4026, nullptr, "Cmd4026"}, + {4027, nullptr, "Cmd4027"}, + {4028, nullptr, "Cmd4028"}, + {4029, nullptr, "Cmd4029"}, + {4030, nullptr, "Cmd4030"}, + {4031, nullptr, "Cmd4031"}, + {4032, nullptr, "Cmd4032"}, + {4033, nullptr, "Cmd4033"}, + {4034, nullptr, "Cmd4034"}, + {4035, nullptr, "Cmd4035"}, + {4037, nullptr, "Cmd4037"}, + {4038, nullptr, "Cmd4038"}, + {4039, nullptr, "Cmd4039"}, + {4040, nullptr, "Cmd4040"}, + {4041, nullptr, "Cmd4041"}, + {4042, nullptr, "Cmd4042"}, + {4043, nullptr, "Cmd4043"}, + {4044, nullptr, "Cmd4044"}, + {4045, nullptr, "Cmd4045"}, + {4046, nullptr, "Cmd4046"}, + {4049, nullptr, "Cmd4049"}, + {4050, nullptr, "Cmd4050"}, + {4051, nullptr, "Cmd4051"}, + {4052, nullptr, "Cmd4052"}, + {4053, nullptr, "Cmd4053"}, + {4054, nullptr, "Cmd4054"}, + {4055, nullptr, "Cmd4055"}, + {4056, nullptr, "Cmd4056"}, + {4057, nullptr, "Cmd4057"}, + {4058, nullptr, "Cmd4058"}, + {4059, nullptr, "Cmd4059"}, + {4060, nullptr, "Cmd4060"}, + {4061, nullptr, "Cmd4061"}, + {4062, nullptr, "Cmd4062"}, + {4063, nullptr, "Cmd4063"}, + {4064, nullptr, "Cmd4064"}, + {4065, nullptr, "Cmd4065"}, + {4066, nullptr, "Cmd4066"}, + {4067, nullptr, "Cmd4067"}, + {4068, nullptr, "Cmd4068"}, + {4069, nullptr, "Cmd4069"}, + {4070, nullptr, "Cmd4070"}, + {4071, nullptr, "Cmd4071"}, + {4072, nullptr, "Cmd4072"}, + {4073, nullptr, "Cmd4073"}, + {4074, nullptr, "Cmd4074"}, + {4075, nullptr, "Cmd4075"}, + {4076, nullptr, "Cmd4076"}, + {4077, nullptr, "Cmd4077"}, + {4078, nullptr, "Cmd4078"}, + {4079, nullptr, "Cmd4079"}, + {4080, nullptr, "Cmd4080"}, + {4081, nullptr, "Cmd4081"}, + {4083, nullptr, "Cmd4083"}, + {4084, nullptr, "Cmd4084"}, + {4085, nullptr, "Cmd4085"}, + {4086, nullptr, "Cmd4086"}, + {4087, nullptr, "Cmd4087"}, + {4088, D<&IApplicationManagerInterface::Cmd4088>, "Cmd4088"}, + {4089, nullptr, "Cmd4089"}, + {4090, nullptr, "Cmd4090"}, + {4091, nullptr, "Cmd4091"}, + {4092, nullptr, "Cmd4092"}, + {4093, nullptr, "Cmd4093"}, + {4094, nullptr, "Cmd4094"}, + {4095, nullptr, "Cmd4095"}, + {4096, nullptr, "Cmd4096"}, + {4097, nullptr, "Cmd4097"}, {9999, nullptr, "GetApplicationCertificate"}, }; // clang-format on @@ -516,4 +604,24 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out o R_SUCCEED(); } +Result IApplicationManagerInterface::Cmd4022(Out out_result) { + LOG_DEBUG(Service_NS, "(STUBBED) called [20.0.0+]"); + // Return 0 to indicate no pending operations / ready state + *out_result = 0; + R_SUCCEED(); +} + +Result IApplicationManagerInterface::Cmd4023(Out out_result) { + LOG_DEBUG(Service_NS, "(STUBBED) called [20.0.0+]"); + // Return 0 to indicate no pending operations / ready state + *out_result = 0; + R_SUCCEED(); +} + +Result IApplicationManagerInterface::Cmd4088(Out out_result) { + LOG_DEBUG(Service_NS, "(STUBBED) called [20.0.0+]"); + *out_result = 0; + R_SUCCEED(); +} + } // namespace Service::NS diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h index f33d269b3..378a9131b 100644 --- a/src/core/hle/service/ns/application_manager_interface.h +++ b/src/core/hle/service/ns/application_manager_interface.h @@ -50,6 +50,11 @@ public: Result CheckApplicationLaunchVersion(u64 application_id); Result GetApplicationTerminateResult(Out out_result, u64 application_id); + // [20.0.0+] Stub functions for QLaunch compatibility + Result Cmd4022(Out out_result); + Result Cmd4023(Out out_result); + Result Cmd4088(Out out_result); + private: KernelHelpers::ServiceContext service_context; Event record_update_system_event; diff --git a/src/core/hle/service/olsc/native_handle_holder.cpp b/src/core/hle/service/olsc/native_handle_holder.cpp index 3cb5d7b11..f0625b1b2 100644 --- a/src/core/hle/service/olsc/native_handle_holder.cpp +++ b/src/core/hle/service/olsc/native_handle_holder.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_serialization.h" @@ -7,7 +8,8 @@ namespace Service::OLSC { INativeHandleHolder::INativeHandleHolder(Core::System& system_) - : ServiceFramework{system_, "INativeHandleHolder"} { + : ServiceFramework{system_, "INativeHandleHolder"}, + service_context{system_, "INativeHandleHolder"} { // clang-format off static const FunctionInfo functions[] = { {0, D<&INativeHandleHolder::GetNativeHandle>, "GetNativeHandle"}, @@ -15,13 +17,17 @@ INativeHandleHolder::INativeHandleHolder(Core::System& system_) // clang-format on RegisterHandlers(functions); + + event = service_context.CreateEvent("INativeHandleHolder:Event"); } -INativeHandleHolder::~INativeHandleHolder() = default; +INativeHandleHolder::~INativeHandleHolder() { + service_context.CloseEvent(event); +} Result INativeHandleHolder::GetNativeHandle(OutCopyHandle out_event) { - LOG_WARNING(Service_OLSC, "(STUBBED) called"); - *out_event = nullptr; + LOG_DEBUG(Service_OLSC, "called"); + *out_event = &event->GetReadableEvent(); R_SUCCEED(); } diff --git a/src/core/hle/service/olsc/native_handle_holder.h b/src/core/hle/service/olsc/native_handle_holder.h index a44754c20..fad6e1006 100644 --- a/src/core/hle/service/olsc/native_handle_holder.h +++ b/src/core/hle/service/olsc/native_handle_holder.h @@ -1,11 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Kernel { class KReadableEvent; +class KEvent; } namespace Service::OLSC { @@ -17,6 +22,9 @@ public: private: Result GetNativeHandle(OutCopyHandle out_event); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; }; } // namespace Service::OLSC diff --git a/src/core/hle/service/omm/power_state_interface.cpp b/src/core/hle/service/omm/power_state_interface.cpp index 22cac8259..08d9d96e7 100644 --- a/src/core/hle/service/omm/power_state_interface.cpp +++ b/src/core/hle/service/omm/power_state_interface.cpp @@ -1,19 +1,21 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/omm/power_state_interface.h" namespace Service::OMM { IPowerStateInterface::IPowerStateInterface(Core::System& system_) - : ServiceFramework{system_, "spsm"} { + : ServiceFramework{system_, "spsm"}, service_context{system_, "IPowerStateInterface"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetState"}, {1, nullptr, "EnterSleep"}, {2, nullptr, "GetLastWakeReason"}, {3, nullptr, "Shutdown"}, - {4, nullptr, "GetNotificationMessageEventHandle"}, + {4, D<&IPowerStateInterface::GetNotificationMessageEventHandle>, "GetNotificationMessageEventHandle"}, {5, nullptr, "ReceiveNotificationMessage"}, {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, {7, nullptr, "ResetEventLog"}, @@ -25,8 +27,18 @@ IPowerStateInterface::IPowerStateInterface(Core::System& system_) // clang-format on RegisterHandlers(functions); + notification_event = service_context.CreateEvent("IPowerStateInterface:NotificationEvent"); } -IPowerStateInterface::~IPowerStateInterface() = default; +IPowerStateInterface::~IPowerStateInterface() { + service_context.CloseEvent(notification_event); +} + +Result IPowerStateInterface::GetNotificationMessageEventHandle( + OutCopyHandle out_event) { + LOG_DEBUG(Service, "called"); + *out_event = ¬ification_event->GetReadableEvent(); + R_SUCCEED(); +} } // namespace Service::OMM diff --git a/src/core/hle/service/omm/power_state_interface.h b/src/core/hle/service/omm/power_state_interface.h index 825a6512d..6f5c19516 100644 --- a/src/core/hle/service/omm/power_state_interface.h +++ b/src/core/hle/service/omm/power_state_interface.h @@ -3,8 +3,15 @@ #pragma once +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + namespace Core { class System; } @@ -15,6 +22,12 @@ class IPowerStateInterface final : public ServiceFramework public: explicit IPowerStateInterface(Core::System& system_); ~IPowerStateInterface() override; + +private: + Result GetNotificationMessageEventHandle(OutCopyHandle out_event); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* notification_event; }; } // namespace Service::OMM diff --git a/src/core/hle/service/pctl/parental_control_service.cpp b/src/core/hle/service/pctl/parental_control_service.cpp index f57f2f157..0ba878799 100644 --- a/src/core/hle/service/pctl/parental_control_service.cpp +++ b/src/core/hle/service/pctl/parental_control_service.cpp @@ -13,7 +13,8 @@ namespace Service::PCTL { IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_) : ServiceFramework{system_, "IParentalControlService"}, capability{capability_}, service_context{system_, "IParentalControlService"}, synchronization_event{service_context}, - unlinked_event{service_context}, request_suspension_event{service_context} { + unlinked_event{service_context}, request_suspension_event{service_context}, + extended_play_timer_event{service_context} { // clang-format off static const FunctionInfo functions[] = { {1, D<&IParentalControlService::Initialize>, "Initialize"}, @@ -77,11 +78,12 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili {1451, D<&IParentalControlService::StartPlayTimer>, "StartPlayTimer"}, {1452, D<&IParentalControlService::StopPlayTimer>, "StopPlayTimer"}, {1453, D<&IParentalControlService::IsPlayTimerEnabled>, "IsPlayTimerEnabled"}, - {1454, nullptr, "GetPlayTimerRemainingTime"}, + {1454, D<&IParentalControlService::GetPlayTimerRemainingTime>, "GetPlayTimerRemainingTime"}, {1455, D<&IParentalControlService::IsRestrictedByPlayTimer>, "IsRestrictedByPlayTimer"}, {1456, D<&IParentalControlService::GetPlayTimerSettings>, "GetPlayTimerSettings"}, {1457, D<&IParentalControlService::GetPlayTimerEventToRequestSuspension>, "GetPlayTimerEventToRequestSuspension"}, {1458, D<&IParentalControlService::IsPlayTimerAlarmDisabled>, "IsPlayTimerAlarmDisabled"}, + {1459, D<&IParentalControlService::GetPlayTimerRemainingTimeDisplayInfo>, "GetPlayTimerRemainingTimeDisplayInfo"}, // [20.0.0+] {1471, nullptr, "NotifyWrongPinCodeInputManyTimes"}, {1472, nullptr, "CancelNetworkRequest"}, {1473, D<&IParentalControlService::GetUnlinkedEvent>, "GetUnlinkedEvent"}, @@ -117,6 +119,14 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili {2014, nullptr, "FinishSynchronizeParentalControlSettings"}, {2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"}, {2016, nullptr, "RequestUpdateExemptionListAsync"}, + // [18.0.0+] + {1475, D<&IParentalControlService::GetExtendedPlayTimerEvent>, "GetExtendedPlayTimerEvent"}, + {1954, nullptr, "IsBedtimeAlarmEnabled"}, + {1955, nullptr, "GetBedtimeAlarmTime"}, + {1956, nullptr, "GetBedtimeAlarmTimeHour"}, + {1957, nullptr, "GetBedtimeAlarmTimeMinute"}, + {145601, D<&IParentalControlService::GetPlayTimerSettingsVer2>, "GetPlayTimerSettingsVer2"}, + {195101, nullptr, "SetPlayTimerSettingsForDebugVer2"}, }; // clang-format on RegisterHandlers(functions); @@ -377,6 +387,28 @@ Result IParentalControlService::GetPlayTimerSettings( R_SUCCEED(); } +Result IParentalControlService::GetPlayTimerRemainingTime(Out out_remaining_time) { + LOG_WARNING(Service_PCTL, "(STUBBED) called"); + // Return 0 indicating no time restriction (unlimited playtime remaining) + *out_remaining_time = 0; + R_SUCCEED(); +} + +Result IParentalControlService::GetPlayTimerRemainingTimeDisplayInfo( + Out out_display_info) { + LOG_WARNING(Service_PCTL, "(STUBBED) called [20.0.0+]"); + // Return default values indicating no time restriction + *out_display_info = {}; + R_SUCCEED(); +} + +Result IParentalControlService::GetPlayTimerSettingsVer2( + Out out_play_timer_settings) { + LOG_WARNING(Service_PCTL, "(STUBBED) called [18.0.0+]"); + *out_play_timer_settings = {}; + R_SUCCEED(); +} + Result IParentalControlService::GetPlayTimerEventToRequestSuspension( OutCopyHandle out_event) { LOG_INFO(Service_PCTL, "called"); @@ -384,6 +416,13 @@ Result IParentalControlService::GetPlayTimerEventToRequestSuspension( R_SUCCEED(); } +Result IParentalControlService::GetExtendedPlayTimerEvent( + OutCopyHandle out_event) { + LOG_INFO(Service_PCTL, "called [18.0.0+]"); + *out_event = extended_play_timer_event.GetHandle(); + R_SUCCEED(); +} + Result IParentalControlService::IsPlayTimerAlarmDisabled(Out out_play_timer_alarm_disabled) { *out_play_timer_alarm_disabled = false; LOG_INFO(Service_PCTL, "called, is_play_timer_alarm_disabled={}", diff --git a/src/core/hle/service/pctl/parental_control_service.h b/src/core/hle/service/pctl/parental_control_service.h index 03dbaa2e5..b034bc4a5 100644 --- a/src/core/hle/service/pctl/parental_control_service.h +++ b/src/core/hle/service/pctl/parental_control_service.h @@ -47,8 +47,13 @@ private: Result IsPlayTimerEnabled(Out out_is_play_timer_enabled); Result IsRestrictedByPlayTimer(Out out_is_restricted_by_play_timer); Result GetPlayTimerSettings(Out out_play_timer_settings); + Result GetPlayTimerRemainingTime(Out out_remaining_time); + Result GetPlayTimerRemainingTimeDisplayInfo( + Out out_display_info); + Result GetPlayTimerSettingsVer2(Out out_play_timer_settings); Result GetPlayTimerEventToRequestSuspension(OutCopyHandle out_event); Result IsPlayTimerAlarmDisabled(Out out_play_timer_alarm_disabled); + Result GetExtendedPlayTimerEvent(OutCopyHandle out_event); Result GetUnlinkedEvent(OutCopyHandle out_event); Result GetStereoVisionRestriction(Out out_stereo_vision_restriction); Result SetStereoVisionRestriction(bool stereo_vision_restriction); @@ -81,6 +86,7 @@ private: Event synchronization_event; Event unlinked_event; Event request_suspension_event; + Event extended_play_timer_event; }; } // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/pctl_types.h b/src/core/hle/service/pctl/pctl_types.h index daaecdf48..e632d3479 100644 --- a/src/core/hle/service/pctl/pctl_types.h +++ b/src/core/hle/service/pctl/pctl_types.h @@ -40,4 +40,28 @@ struct PlayTimerSettings { }; static_assert(sizeof(PlayTimerSettings) == 0x34, "PlayTimerSettings has incorrect size."); +// This is nn::pctl::PlayTimerSettingsVer2 [18.0.0+] +// Extended version with bedtime alarm settings +struct PlayTimerSettingsVer2 { + PlayTimerSettings base_settings; + bool bedtime_alarm_enabled; + INSERT_PADDING_BYTES(3); + u32 bedtime_alarm_hour; + u32 bedtime_alarm_minute; + INSERT_PADDING_BYTES(4); +}; +static_assert(sizeof(PlayTimerSettingsVer2) == 0x44, "PlayTimerSettingsVer2 has incorrect size."); + +// This is nn::pctl::PlayTimerRemainingTimeDisplayInfo [20.0.0+] +struct PlayTimerRemainingTimeDisplayInfo { + s64 remaining_time_ns; // Remaining time in nanoseconds + u32 display_hours; // Hours to display + u32 display_minutes; // Minutes to display + bool is_restricted; // Whether play time is restricted + bool alarm_active; // Whether the alarm is active + INSERT_PADDING_BYTES(6); +}; +static_assert(sizeof(PlayTimerRemainingTimeDisplayInfo) == 0x18, + "PlayTimerRemainingTimeDisplayInfo has incorrect size."); + } // namespace Service::PCTL diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index fd4614298..ad00374ce 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -30,6 +30,7 @@ public: {10500, &PlayReport::SendReportWithUser, "SendReportWithUser"}, {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"}, {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, + {20102, &PlayReport::SaveSystemReport2, "SaveSystemReport2"}, {20200, &PlayReport::SetOperationMode, "SetOperationMode"}, {30100, &PlayReport::ClearStorage, "ClearStorage"}, {30200, &PlayReport::ClearStatistics, "ClearStatistics"}, @@ -160,6 +161,13 @@ private: rb.Push(ResultSuccess); } + void SaveSystemReport2(HLERequestContext& ctx) { + LOG_WARNING(Service_PREPO, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + void SetOperationMode(HLERequestContext& ctx) { LOG_WARNING(Service_PREPO, "(STUBBED) called"); diff --git a/src/core/hle/service/psc/ovln/receiver.cpp b/src/core/hle/service/psc/ovln/receiver.cpp index 85f62816d..371ee5f83 100644 --- a/src/core/hle/service/psc/ovln/receiver.cpp +++ b/src/core/hle/service/psc/ovln/receiver.cpp @@ -1,24 +1,36 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/psc/ovln/receiver.h" namespace Service::PSC { -IReceiver::IReceiver(Core::System& system_) : ServiceFramework{system_, "IReceiver"} { +IReceiver::IReceiver(Core::System& system_) + : ServiceFramework{system_, "IReceiver"}, service_context{system_, "IReceiver"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "AddSource"}, {1, nullptr, "RemoveSource"}, - {2, nullptr, "GetReceiveEventHandle"}, + {2, D<&IReceiver::GetReceiveEventHandle>, "GetReceiveEventHandle"}, {3, nullptr, "Receive"}, {4, nullptr, "ReceiveWithTick"}, }; // clang-format on RegisterHandlers(functions); + event = service_context.CreateEvent("IReceiver:Event"); } -IReceiver::~IReceiver() = default; +IReceiver::~IReceiver() { + service_context.CloseEvent(event); +} + +Result IReceiver::GetReceiveEventHandle(OutCopyHandle out_event) { + LOG_DEBUG(Service_PSC, "called"); + *out_event = &event->GetReadableEvent(); + R_SUCCEED(); +} } // namespace Service::PSC diff --git a/src/core/hle/service/psc/ovln/receiver.h b/src/core/hle/service/psc/ovln/receiver.h index c47a4ff7e..9155decb7 100644 --- a/src/core/hle/service/psc/ovln/receiver.h +++ b/src/core/hle/service/psc/ovln/receiver.h @@ -3,14 +3,27 @@ #pragma once +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + namespace Service::PSC { class IReceiver final : public ServiceFramework { public: explicit IReceiver(Core::System& system_); ~IReceiver() override; + +private: + Result GetReceiveEventHandle(OutCopyHandle out_event); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; }; } // namespace Service::PSC diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp index 6b0bcb536..c2a1c4329 100644 --- a/src/core/hle/service/vi/application_display_service.cpp +++ b/src/core/hle/service/vi/application_display_service.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/nvnflinger/hos_binder_driver.h" #include "core/hle/service/nvnflinger/parcel.h" @@ -88,8 +91,12 @@ Result IApplicationDisplayService::OpenDisplay(Out out_display_id, DisplayN LOG_WARNING(Service_VI, "(STUBBED) called"); display_name[display_name.size() - 1] = '\0'; - ASSERT_MSG(strcmp(display_name.data(), "Default") == 0, - "Non-default displays aren't supported yet"); + if (strcmp(display_name.data(), "Default") != 0) { + LOG_WARNING(Service_VI, "Non-default display '{}' requested, using Default display", + display_name.data()); + // Use Default display for non-default display requests + R_RETURN(m_container->OpenDisplay(out_display_id, DisplayName{"Default"})); + } R_RETURN(m_container->OpenDisplay(out_display_id, display_name)); } @@ -142,14 +149,39 @@ Result IApplicationDisplayService::SetLayerScalingMode(NintendoScaleMode scale_m Result IApplicationDisplayService::ListDisplays( Out out_count, OutArray out_displays) { - LOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_DEBUG(Service_VI, "called"); - if (out_displays.size() > 0) { - out_displays[0] = DisplayInfo{}; - *out_count = 1; - } else { - *out_count = 0; + // QLaunch expects multiple displays: Default, Edid, Internal, External, Null + struct DisplayEntry { + const char* name; + u8 has_limited_layers; + u64 max_layers; + u64 width; + u64 height; + }; + + static constexpr std::array display_entries{{ + {"Default", 1, 1, 1920, 1080}, + {"Edid", 1, 1, 1920, 1080}, + {"Internal", 1, 1, 1280, 720}, + {"External", 1, 1, 1920, 1080}, + {"Null", 0, 0, 0, 0}, + }}; + + const u64 display_count = + std::min(static_cast(display_entries.size()), out_displays.size()); + + for (u64 i = 0; i < display_count; ++i) { + DisplayInfo info{}; + std::strncpy(info.display_name.data(), display_entries[i].name, + info.display_name.size() - 1); + info.has_limited_layers = display_entries[i].has_limited_layers; + info.max_layers = display_entries[i].max_layers; + info.width = display_entries[i].width; + info.height = display_entries[i].height; + out_displays[i] = info; } + *out_count = display_count; R_SUCCEED(); }