From a5632d75765b9e4970bdd979fb27fb5fb8787d1a Mon Sep 17 00:00:00 2001 From: Zephyron Date: Wed, 28 Jan 2026 16:42:51 +1000 Subject: [PATCH] feat(ui): add QLaunch/Home Menu support to menubar - Add Tools > Open QLaunch menu action - Implement OnOpenQLaunch() to launch firmware home menu - Add translation strings for QLaunch option --- .../configuration/shared_translation.cpp | 5 ++ src/citron/main.cpp | 48 ++++++++++++++++++- src/citron/main.h | 3 ++ src/citron/main.ui | 6 +++ 4 files changed, 61 insertions(+), 1 deletion(-) 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...