Merge pull request 'feat/qlaunch-home-menu-support' (#115) from feat/qlaunch-home-menu-support into main

Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/115
This commit is contained in:
Zephyron
2026-01-28 07:46:11 +01:00
46 changed files with 795 additions and 43 deletions

View File

@@ -44,6 +44,11 @@ std::unique_ptr<TranslationMap> 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());

View File

@@ -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<u64>(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;

View File

@@ -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<FileSys::ManualContentProvider> 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);

View File

@@ -176,6 +176,7 @@
<addaction name="action_Install_Firmware"/>
<addaction name="action_Verify_installed_contents"/>
<addaction name="separator"/>
<addaction name="action_Load_Home_Menu"/>
<addaction name="menu_cabinet_applet"/>
<addaction name="action_Load_Album"/>
<addaction name="action_Load_Mii_Edit"/>
@@ -487,6 +488,11 @@
<string>Open &amp;Mii Editor</string>
</property>
</action>
<action name="action_Load_Home_Menu">
<property name="text">
<string>Open &amp;Home Menu</string>
</property>
</action>
<action name="action_Configure_Tas">
<property name="text">
<string>&amp;Configure TAS...</string>

View File

@@ -169,6 +169,9 @@ struct Values {
Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
Category::LibraryApplet};
// System Applet
Setting<bool> qlaunch_enabled{linkage, true, "qlaunch_enabled", Category::System};
// Audio
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
Category::Audio, Specialization::RuntimeList};

View File

@@ -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

View File

@@ -335,4 +335,11 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
applet->process->Run();
}
void AppletManager::SetHomeMenuRequestCallback(std::function<void()> callback) {
std::unique_lock lk{m_lock};
if (m_window_system) {
m_window_system->SetHomeMenuRequestCallback(std::move(callback));
}
}
} // namespace Service::AM

View File

@@ -4,6 +4,7 @@
#pragma once
#include <condition_variable>
#include <functional>
#include <mutex>
#include "core/hle/service/am/am_types.h"
@@ -46,6 +47,7 @@ public:
public:
void SetWindowSystem(WindowSystem* window_system);
void SetHomeMenuRequestCallback(std::function<void()> callback);
private:
Core::System& m_system;

View File

@@ -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<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle) {
LOG_DEBUG(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
*out_system_applet_proxy = std::make_shared<ISystemAppletProxy>(
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<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle,
@@ -130,6 +149,18 @@ Result IAllSystemAppletProxiesService::GetSystemAppletControllerForDebug() {
R_THROW(ResultUnknown);
}
Result IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions(
Out<SharedPointer<ISystemProcessCommonFunctions>> out_system_process_common_functions) {
LOG_DEBUG(Service_AM, "called");
*out_system_process_common_functions = std::make_shared<ISystemProcessCommonFunctions>(system);
R_SUCCEED();
}
Result IAllSystemAppletProxiesService::Cmd460() {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
}
Result IAllSystemAppletProxiesService::GetDebugFunctions(
Out<SharedPointer<IDebugFunctions>> out_debug_functions) {
LOG_DEBUG(Service_AM, "called");

View File

@@ -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<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy,
ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle);
Result OpenHomeMenuProxy(Out<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy,
ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle);
Result OpenLibraryAppletProxy(Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy,
ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle,
@@ -49,6 +53,9 @@ private:
ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle);
Result GetSystemAppletControllerForDebug();
Result GetSystemProcessCommonFunctions(
Out<SharedPointer<ISystemProcessCommonFunctions>> out_system_process_common_functions);
Result Cmd460();
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
private:

View File

@@ -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<u64> out_application_
R_SUCCEED();
}
Result IAppletCommonFunctions::IsSystemAppletHomeMenu(Out<bool> 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

View File

@@ -20,6 +20,17 @@ private:
Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
Result SetCpuBoostRequestPriority(s32 priority);
Result GetCurrentApplicationId(Out<u64> out_application_id);
Result IsSystemAppletHomeMenu(Out<bool> 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> applet;
};

View File

@@ -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

View File

@@ -35,6 +35,8 @@ private:
Result CheckRightsEnvironmentAvailable(Out<bool> out_is_available);
Result GetNsRightsEnvironmentHandle(Out<u64> out_handle);
Result ReportApplicationExitTimeout();
Result Cmd300();
Result Cmd301();
WindowSystem& m_window_system;
const std::shared_ptr<Applet> m_applet;

View File

@@ -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

View File

@@ -79,6 +79,11 @@ private:
Result PrepareForJit();
Result GetLaunchRequiredVersionUpgrade(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result Unknown330(); // [20.2.0+]
Result Cmd112();
Result Cmd113();
Result Cmd220();
Result Cmd310();
Result Cmd320();
const std::shared_ptr<Applet> m_applet;
};

View File

@@ -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

View File

@@ -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<IApplicationObserver> {
public:
explicit IApplicationObserver(Core::System& system_);
~IApplicationObserver() override;
};
} // namespace Service::AM

View File

@@ -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<Ap
{1001, nullptr, "GetScreenShotPermission"},
{1010, nullptr, "GetNextProgramArgumentInfo"},
{1011, nullptr, "GetPreviousProgramArgumentInfo"},
{1020, nullptr, "GetGpuErrorDetectedSystemEvent"},
{1020, D<&ICommonStateGetter::GetGpuErrorDetectedSystemEvent>, "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<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->gpu_error_detected_event.GetHandle();
R_SUCCEED();
}
Result ICommonStateGetter::GetFriendInvitationStorageChannelEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->friend_invitation_storage_channel_event.GetHandle();
R_SUCCEED();
}
Result ICommonStateGetter::GetNotificationStorageChannelEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->notification_storage_channel_event.GetHandle();
R_SUCCEED();
}
Result ICommonStateGetter::GetHealthWarningDisappearedSystemEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->health_warning_disappeared_system_event.GetHandle();
R_SUCCEED();
}
} // namespace Service::AM

View File

@@ -53,6 +53,10 @@ private:
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
Result GetGpuErrorDetectedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetFriendInvitationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
void SetCpuBoostMode(HLERequestContext& ctx);

View File

@@ -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

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_accumulated_suspended_tick_changed_event.GetHandle();
R_SUCCEED();
}
} // namespace Service::AM

View File

@@ -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<Kernel::KReadableEvent> out_event);
Result OpenCradleFirmwareUpdater(
Out<SharedPointer<ICradleFirmwareUpdater>> out_cradle_firmware_updater);
Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext m_context;
Event m_hdcp_authentication_failed_event;
Event m_accumulated_suspended_tick_changed_event;
};
} // namespace Service::AM

View File

@@ -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<Ap
{41, D<&IHomeMenuFunctions::IsRebootEnabled>, "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

View File

@@ -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<bool> 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<Applet> m_applet;

View File

@@ -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<Applet>
{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

View File

@@ -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<Applet> m_applet;

View File

@@ -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<SharedPointer<IApplicationObserver>> out_observer) {
LOG_DEBUG(Service_AM, "called");
*out_observer = std::make_shared<IApplicationObserver>(system);
R_SUCCEED();
}
} // namespace Service::AM

View File

@@ -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<ISystemProcessCommonFunctions> {
public:
explicit ISystemProcessCommonFunctions(Core::System& system_);
~ISystemProcessCommonFunctions() override;
private:
Result GetApplicationObserver(Out<SharedPointer<IApplicationObserver>> out_observer);
};
} // namespace Service::AM

View File

@@ -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

View File

@@ -3,6 +3,7 @@
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -24,6 +25,8 @@ enum class ButtonPressDuration {
LongPressing,
};
using HomeMenuRequestCallback = std::function<void()>;
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<u64, std::shared_ptr<Applet>> m_applets{};
// Callback for requesting home menu launch from frontend.
HomeMenuRequestCallback m_home_menu_request_callback{};
};
} // namespace Service::AM

View File

@@ -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<SharedPointer<IAudioController>> out_audio_controller) {
LOG_DEBUG(Audio, "called GetAudioController [19.0.0+]");
*out_audio_controller = std::make_shared<IAudioController>(system);
R_SUCCEED();
}
} // namespace Service::Audio

View File

@@ -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<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> device_name); // [19.0.0+]
Result ClearForceOverrideExternalDeviceNameForDebug(); // [19.0.0+]
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller); // [19.0.0+]
Result Unknown10200(); // [20.0.0+]
KernelHelpers::ServiceContext service_context;

View File

@@ -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<Result> o
R_SUCCEED();
}
Result IApplicationManagerInterface::Cmd4022(Out<u64> 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<u64> 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<u64> out_result) {
LOG_DEBUG(Service_NS, "(STUBBED) called [20.0.0+]");
*out_result = 0;
R_SUCCEED();
}
} // namespace Service::NS

View File

@@ -50,6 +50,11 @@ public:
Result CheckApplicationLaunchVersion(u64 application_id);
Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
// [20.0.0+] Stub functions for QLaunch compatibility
Result Cmd4022(Out<u64> out_result);
Result Cmd4023(Out<u64> out_result);
Result Cmd4088(Out<u64> out_result);
private:
KernelHelpers::ServiceContext service_context;
Event record_update_system_event;

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
*out_event = nullptr;
LOG_DEBUG(Service_OLSC, "called");
*out_event = &event->GetReadableEvent();
R_SUCCEED();
}

View File

@@ -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<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* event;
};
} // namespace Service::OLSC

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service, "called");
*out_event = &notification_event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::OMM

View File

@@ -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<IPowerStateInterface>
public:
explicit IPowerStateInterface(Core::System& system_);
~IPowerStateInterface() override;
private:
Result GetNotificationMessageEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* notification_event;
};
} // namespace Service::OMM

View File

@@ -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<s64> 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<PlayTimerRemainingTimeDisplayInfo> 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<PlayTimerSettingsVer2> out_play_timer_settings) {
LOG_WARNING(Service_PCTL, "(STUBBED) called [18.0.0+]");
*out_play_timer_settings = {};
R_SUCCEED();
}
Result IParentalControlService::GetPlayTimerEventToRequestSuspension(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(Service_PCTL, "called");
@@ -384,6 +416,13 @@ Result IParentalControlService::GetPlayTimerEventToRequestSuspension(
R_SUCCEED();
}
Result IParentalControlService::GetExtendedPlayTimerEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(Service_PCTL, "called [18.0.0+]");
*out_event = extended_play_timer_event.GetHandle();
R_SUCCEED();
}
Result IParentalControlService::IsPlayTimerAlarmDisabled(Out<bool> out_play_timer_alarm_disabled) {
*out_play_timer_alarm_disabled = false;
LOG_INFO(Service_PCTL, "called, is_play_timer_alarm_disabled={}",

View File

@@ -47,8 +47,13 @@ private:
Result IsPlayTimerEnabled(Out<bool> out_is_play_timer_enabled);
Result IsRestrictedByPlayTimer(Out<bool> out_is_restricted_by_play_timer);
Result GetPlayTimerSettings(Out<PlayTimerSettings> out_play_timer_settings);
Result GetPlayTimerRemainingTime(Out<s64> out_remaining_time);
Result GetPlayTimerRemainingTimeDisplayInfo(
Out<PlayTimerRemainingTimeDisplayInfo> out_display_info);
Result GetPlayTimerSettingsVer2(Out<PlayTimerSettingsVer2> out_play_timer_settings);
Result GetPlayTimerEventToRequestSuspension(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsPlayTimerAlarmDisabled(Out<bool> out_play_timer_alarm_disabled);
Result GetExtendedPlayTimerEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetUnlinkedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetStereoVisionRestriction(Out<bool> 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

View File

@@ -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

View File

@@ -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");

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_PSC, "called");
*out_event = &event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::PSC

View File

@@ -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<IReceiver> {
public:
explicit IReceiver(Core::System& system_);
~IReceiver() override;
private:
Result GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* event;
};
} // namespace Service::PSC

View File

@@ -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 <cstring>
#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<u64> 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<u64> out_count, OutArray<DisplayInfo, BufferAttr_HipcMapAlias> 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<DisplayEntry, 5> 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<u64>(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();
}