Browse Source

fix config, initial gfx stuff

Signed-off-by: crueter <crueter@eden-emu.dev>
pull/3016/head
crueter 1 week ago
parent
commit
47c8601222
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 44
      src/Eden/Config/GlobalConfigureDialog.qml
  2. 9
      src/Eden/Config/fields/BaseField.qml
  3. 3
      src/Eden/Config/fields/ConfigCheckbox.qml
  4. 5
      src/Eden/Config/fields/FieldCheckbox.qml
  5. 7
      src/Eden/Config/pages/SettingsList.qml
  6. 4
      src/Eden/Config/pages/audio/AudioGeneralPage.qml
  7. 4
      src/Eden/Config/pages/cpu/CpuGeneralPage.qml
  8. 4
      src/Eden/Config/pages/debug/DebugAdvancedPage.qml
  9. 4
      src/Eden/Config/pages/debug/DebugCpuPage.qml
  10. 5
      src/Eden/Config/pages/debug/DebugGeneralPage.qml
  11. 4
      src/Eden/Config/pages/debug/DebugGraphicsPage.qml
  12. 4
      src/Eden/Config/pages/general/UiGameListPage.qml
  13. 8
      src/Eden/Config/pages/general/UiGeneralPage.qml
  14. 4
      src/Eden/Config/pages/graphics/RendererAdvancedPage.qml
  15. 4
      src/Eden/Config/pages/graphics/RendererExtensionsPage.qml
  16. 10
      src/Eden/Config/pages/graphics/RendererPage.qml
  17. 5
      src/Eden/Config/pages/system/AppletsPage.qml
  18. 4
      src/Eden/Config/pages/system/FileSystemPage.qml
  19. 4
      src/Eden/Config/pages/system/SystemCorePage.qml
  20. 5
      src/Eden/Config/pages/system/SystemGeneralPage.qml
  21. 4
      src/Eden/Interface/CMakeLists.txt
  22. 330
      src/Eden/Interface/MainWindowInterface.cpp
  23. 41
      src/Eden/Interface/MainWindowInterface.h
  24. 3
      src/Eden/Interface/QMLConfig.h
  25. 6
      src/Eden/Interface/QMLSetting.cpp
  26. 230
      src/Eden/Interface/RenderWindow.cpp
  27. 55
      src/Eden/Interface/RenderWindow.h
  28. 1
      src/Eden/Interface/SettingsInterface.h
  29. 10
      src/Eden/Main/Main.qml
  30. 16
      src/Eden/Native/EdenApplication.cpp
  31. 5
      src/frontend_common/config.cpp
  32. 3
      src/qt_common/CMakeLists.txt
  33. 23
      src/qt_common/config/qt_config.cpp
  34. 1
      src/qt_common/config/qt_config.h
  35. 1
      src/qt_common/qt_common.cpp
  36. 15
      src/qt_common/qt_common.h
  37. 104
      src/qt_common/render/context.h
  38. 79
      src/qt_common/render/emu_thread.cpp
  39. 94
      src/qt_common/render/emu_thread.h
  40. 38
      src/qt_common/util/content.cpp
  41. 3
      src/qt_common/util/content.h
  42. 201
      src/yuzu/bootmanager.cpp
  43. 107
      src/yuzu/bootmanager.h
  44. 123
      src/yuzu/main_window.cpp
  45. 11
      src/yuzu/main_window.h

44
src/Eden/Config/GlobalConfigureDialog.qml

@ -13,6 +13,7 @@ import Carboxyl.Contour
NativeDialog {
property list<var> configs
property list<var> settings
width: Constants.width
height: Constants.height
@ -20,16 +21,29 @@ NativeDialog {
title: qsTr("Eden Configuration")
standardButtons: Dialog.Ok | Dialog.Apply | Dialog.Cancel
Component.onCompleted: configs = Util.searchItem(swipe, "PageScrollView")
Component.onCompleted: {
configs = Util.searchItem(swipe, "PageScrollView")
settings = Util.searchItem(swipe, "BaseField")
syncConfigs()
}
function applyConfigs() {
configs.forEach(config => {
// console.log(config)
config.apply()
})
QtConfig.save()
}
function syncConfigs() {
configs.forEach(setting => {
console.log(setting)
setting.sync()
})
}
MessageDialog {
id: warn
text: qsTr("To apply the new style, Eden will now close and re-open.")
@ -54,12 +68,8 @@ NativeDialog {
onApplied: applyConfigs()
onRejected: {
// TODO
// configs.forEach(config => config.sync())
// QtConfig.reload()
}
onVisibilityChanged: if (visible)
syncConfigs()
CarboxylTabBar {
id: tabBar
@ -133,10 +143,20 @@ NativeDialog {
GlobalGeneralPage {
id: general
}
GlobalSystemPage {}
GlobalCpuPage {}
GlobalGraphicsPage {}
GlobalAudioPage {}
GlobalDebugPage {}
GlobalSystemPage {
id: system
}
GlobalCpuPage {
id: cpu
}
GlobalGraphicsPage {
id: gfx
}
GlobalAudioPage {
id: audio
}
GlobalDebugPage {
id: debug
}
}
}

9
src/Eden/Config/fields/BaseField.qml

@ -1,6 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Layouts
@ -28,12 +27,17 @@ Item {
function apply() {
if (setting.value !== value) {
console.log("Changing value", setting.value, "of setting",
setting.label, "to", value)
setting.value = value
}
}
function sync() {
if (value !== setting.value) {
// console.log("Syncing setting", setting.label, "from", value, "to",
// setting.value)
value = setting.value
}
}
@ -60,9 +64,10 @@ Item {
FieldCheckbox {
id: enable
setting: field.setting
z: 2
setting: field.setting
force: field.forceCheckbox
field: parent
height: 30

3
src/Eden/Config/fields/ConfigCheckbox.qml

@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
BaseField {
forceCheckbox: true
// // TODO: global/custom

5
src/Eden/Config/fields/FieldCheckbox.qml

@ -1,15 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
CheckBox {
property bool force: false
property var setting
property var other: setting.other === null ? setting : setting.other
property var field
property var other: setting.other === null ? field : setting.other
indicator.implicitHeight: 25
indicator.implicitWidth: 25

7
src/Eden/Config/pages/SettingsList.qml

@ -21,6 +21,13 @@ ListView {
itm.apply()
}
}
function sync() {
for (var i = 0; i < count; ++i) {
var itm = itemAtIndex(i)
if (itm !== null)
itm.apply()
}
}
clip: true
boundsBehavior: Flickable.StopAtBounds

4
src/Eden/Config/pages/audio/AudioGeneralPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
audio.apply()
}
function sync() {
audio.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/cpu/CpuGeneralPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
cpu.apply()
}
function sync() {
cpu.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/debug/DebugAdvancedPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
debug.apply()
}
function sync() {
debug.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/debug/DebugCpuPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
cpu.apply()
}
function sync() {
cpu.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

5
src/Eden/Config/pages/debug/DebugGeneralPage.qml

@ -15,6 +15,11 @@ PageScrollView {
debug.apply()
misc.apply()
}
function sync() {
debug.sync()
misc.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/debug/DebugGraphicsPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
gfx.apply()
}
function sync() {
gfx.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/general/UiGameListPage.qml

@ -13,10 +13,14 @@ PageScrollView {
function apply() {
ui.apply()
}
function sync() {
ui.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
// TODO(qml): This is shown twice???????
SettingsList {
id: ui
category: SettingsCategories.UiGameList

8
src/Eden/Config/pages/general/UiGeneralPage.qml

@ -1,4 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
@ -26,6 +25,13 @@ PageScrollView {
Clover.theme = Clover.themes[theme.contentItem.currentIndex]
}
function sync() {
ui.sync()
style.sync()
theme.sync()
accent.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/graphics/RendererAdvancedPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
gfx.apply()
}
function sync() {
gfx.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/graphics/RendererExtensionsPage.qml

@ -13,6 +13,10 @@ PageScrollView {
function apply() {
ext.apply()
}
function sync() {
ext.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

10
src/Eden/Config/pages/graphics/RendererPage.qml

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
@ -19,6 +19,14 @@ PageScrollView {
vsync.apply()
}
function sync() {
gfx.sync()
api.sync()
dev.sync()
shader.sync()
vsync.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

5
src/Eden/Config/pages/system/AppletsPage.qml

@ -1,6 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
@ -10,9 +9,13 @@ import Eden.Config
PageScrollView {
id: scroll
function apply() {
app.apply()
}
function sync() {
app.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/system/FileSystemPage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
fs.apply()
}
function sync() {
fs.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Config/pages/system/SystemCorePage.qml

@ -14,6 +14,10 @@ PageScrollView {
function apply() {
core.apply()
}
function sync() {
core.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

5
src/Eden/Config/pages/system/SystemGeneralPage.qml

@ -15,6 +15,11 @@ PageScrollView {
net.apply()
sys.apply()
}
function sync() {
net.sync()
sys.sync()
}
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth

4
src/Eden/Interface/CMakeLists.txt

@ -5,6 +5,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
find_package(Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
EdenModule(
NAME Interface
@ -24,11 +25,14 @@ EdenModule(
Vulkan::UtilityHeaders
frontend_common
qt_common
glad
)
target_link_libraries(EdenInterface PRIVATE Qt6::Core)
target_link_libraries(EdenInterface PRIVATE Qt6::Widgets)
target_sources(EdenInterface
PRIVATE
MainWindowInterface.h MainWindowInterface.cpp
RenderWindow.h RenderWindow.cpp
)

330
src/Eden/Interface/MainWindowInterface.cpp

@ -3,18 +3,38 @@
#include "Eden/Models/GameListModel.h"
#include "MainWindowInterface.h"
#include "QMLConfig.h"
#include "RenderWindow.h"
#include "common/string_util.h"
#include "core/hle/kernel/k_process.h"
#include "frontend_common/content_manager.h"
#include "hid_core/hid_core.h"
#include "qt_common/util/content.h"
#include "qt_common/util/game.h"
#include "qt_common/abstract/frontend.h"
#include "qt_common/qt_constants.h"
// Applets //
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include <QDesktopServices>
MainWindowInterface::MainWindowInterface(GameListModel* model, QObject* parent)
: QObject{parent}, m_gameList(model) {
MainWindowInterface::MainWindowInterface(GameListModel* model, QMLConfig* config,
QQuickWindow* rootWindow, QObject* parent)
: QObject{parent}, m_gameList(model), m_config(config),
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()} {
m_renderWindow = new RenderWindow(rootWindow, input_subsystem);
checkFirmwareDecryption();
}
@ -124,6 +144,312 @@ void MainWindowInterface::openFAQ() {
openURL(QUrl(QString::fromLocal8Bit(QtCommon::Constants::helpPage)));
}
void MainWindowInterface::openHomeMenu() {
auto result = FirmwareManager::VerifyFirmware(*QtCommon::system.get());
using namespace QtCommon::StringLookup;
switch (result) {
case FirmwareManager::ErrorFirmwareMissing:
QtCommon::Frontend::Warning(this, tr("No firmware available"),
Lookup(FwCheckErrorFirmwareMissing));
return;
case FirmwareManager::ErrorFirmwareCorrupted:
QtCommon::Frontend::Warning(this, tr("Firmware Corrupted"),
Lookup(FwCheckErrorFirmwareCorrupted));
return;
default:
break;
}
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_applet_nca) {
QtCommon::Frontend::Warning(this, tr("Home Menu Applet"),
tr("Home Menu is not available. Please reinstall firmware."));
return;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch);
const auto filename = QString::fromStdString((qlaunch_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
bootGame(filename, libraryAppletParameters(QLaunchId, Service::AM::AppletId::QLaunch));
}
Service::AM::FrontendAppletParameters MainWindowInterface::applicationAppletParameters() {
return Service::AM::FrontendAppletParameters{
.applet_id = Service::AM::AppletId::Application,
.applet_type = Service::AM::AppletType::Application,
};
}
Service::AM::FrontendAppletParameters MainWindowInterface::libraryAppletParameters(
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::LibraryApplet,
};
}
void MainWindowInterface::executeProgram(std::size_t program_index) {
shutdownGame();
auto params = applicationAppletParameters();
params.program_index = static_cast<s32>(program_index);
params.launch_type = Service::AM::LaunchType::ApplicationInitiated;
bootGame(last_filename_booted, params);
}
void MainWindowInterface::bootGame(const QString& filename,
Service::AM::FrontendAppletParameters params,
StartGameType type) {
LOG_INFO(Frontend, "Eden starting...");
// if (params.program_id == 0 ||
// params.program_id > static_cast<u64>(Service::AM::AppletProgramId::MaxProgramId)) {
// StoreRecentFile(filename); // Put the filename on top of the list
// }
// Save configurations
// UpdateUISettings();
m_config->save();
u64 title_id{0};
// last_filename_booted = filename;
QtCommon::Content::configureFilesystemProvider(filename.toStdString());
const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, filename.toUtf8().constData());
const auto loader =
Loader::GetLoader(*QtCommon::system, v_file, params.program_id, params.program_index);
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
type == StartGameType::Normal) {
// Load per game settings
const auto file_path =
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
const auto config_file_name = title_id == 0
? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
QtCommon::system->HIDCore().ReloadInputDevices();
QtCommon::system->ApplySettings();
}
Settings::LogSettings();
// if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) {
// const Core::Frontend::ProfileSelectParameters parameters{
// .mode =
// Service::AM::Frontend::UiMode::UserSelector,
// .invalid_uid_list = {},
// .display_options = {},
// .purpose =
// Service::AM::Frontend::UserSelectionPurpose::General,
// };
// if (SelectAndSetCurrentUser(parameters) == false) {
// return;
// }
// }
if (!loadROM(filename, params)) {
return;
}
qDebug() << "Successfully loaded ROM from" << filename;
QtCommon::system->SetShuttingDown(false);
// Create and start the emulation thread
QtCommon::emu_thread = std::make_unique<EmuThread>();
// emit EmulationStarting();
QtCommon::emu_thread->start();
// Register an ExecuteProgram callback such that Core can execute a sub-program
QtCommon::system->RegisterExecuteProgramCallback(
[this](std::size_t program_index_) { executeProgram(program_index_); });
QtCommon::system->RegisterExitCallback([this] {
QtCommon::emu_thread->ForceStop();
shutdownGame();
});
// connect(render_window, &GRenderWindow::Closed, this, &MainWindow::OnStopGame);
// connect(render_window, &GRenderWindow::MouseActivity, this, &MainWindow::OnMouseActivity);
connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, this,
[](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
qDebug() << (int) stage << value << total;
});
// connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
// &LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
// // Update the GUI
// UpdateStatusButtons();
// if (ui->action_Single_Window_Mode->isChecked()) {
// game_list->hide();
// game_list_placeholder->hide();
// }
// status_bar_update_timer.start(500);
// renderer_status_button->setDisabled(true);
// refresh_button->setDisabled(true);
// if (UISettings::values.hide_mouse || Settings::values.mouse_panning) {
// render_window->installEventFilter(render_window);
// render_window->setAttribute(Qt::WA_Hover, true);
// }
// if (UISettings::values.hide_mouse) {
// mouse_hide_timer.start();
// }
// render_window->InitializeCamera();
std::string title_name;
std::string title_version;
const auto res = QtCommon::system->GetGameName(title_name);
const auto metadata = [title_id] {
const FileSys::PatchManager pm(title_id, QtCommon::system->GetFileSystemController(),
QtCommon::system->GetContentProvider());
return pm.GetControlMetadata();
}();
if (metadata.first != nullptr) {
title_version = metadata.first->GetVersionString();
title_name = metadata.first->GetApplicationName();
}
if (res != Loader::ResultStatus::Success || title_name.empty()) {
title_name = Common::FS::PathToUTF8String(
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
.filename());
}
const bool is_64bit = QtCommon::system->Kernel().ApplicationProcess()->Is64Bit();
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
.arg(QString::fromStdString(title_name), instruction_set_suffix)
.toStdString();
LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
const auto gpu_vendor = QtCommon::system->GPU().Renderer().GetDeviceVendor();
// TODO
// UpdateWindowTitle(title_name, title_version, gpu_vendor);
// loading_screen->Prepare(QtCommon::system->GetAppLoader());
// loading_screen->show();
emulation_running = true;
// if (ui->action_Fullscreen->isChecked()) {
// ShowFullscreen();
// }
// OnStartGame();
QtCommon::emu_thread->SetRunning(true);
}
bool MainWindowInterface::loadROM(const QString& filename, Service::AM::FrontendAppletParameters params) {
// Shutdown previous session if the emu thread is still active...
if (QtCommon::emu_thread != nullptr) {
shutdownGame();
}
if (!m_renderWindow->initRenderTarget()) {
return false;
}
QtCommon::system->SetFilesystem(QtCommon::vfs);
if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) {
QtCommon::system->GetUserChannel().clear();
}
QtCommon::system->SetFrontendAppletSet({
nullptr, // Amiibo Settings
nullptr, // Controller Selector
nullptr, // Error Display
nullptr, // Mii Editor
nullptr, // Parental Controls
nullptr, // Photo Viewer
nullptr, // Profile Selector
nullptr, // Software Keyboard
nullptr, // Web Browser
nullptr, // Net Connect
});
/** firmware check */
if (!QtCommon::Content::CheckGameFirmware(params.program_id, this)) {
return false;
}
/** Exec */
const Core::SystemResultStatus result{
QtCommon::system->Load(*m_renderWindow, filename.toStdString(), params)};
if (result != Core::SystemResultStatus::Success) {
switch (result) {
case Core::SystemResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString());
QtCommon::Frontend::Critical(tr("Error while loading ROM!"),
tr("The ROM format is not supported."));
break;
case Core::SystemResultStatus::ErrorVideoCore:
QtCommon::Frontend::Critical(
tr("An error occurred initializing the video core."),
tr("Eden has encountered an error while running the video core. "
"This is usually caused by outdated GPU drivers, including integrated ones. "
"Please see the log for more details. "
"For more information on accessing the log, please see the following page: "
"<a href='https://yuzu-mirror.github.io/help/reference/log-files/'>"
"How to Upload the Log File</a>. "));
break;
default:
if (result > Core::SystemResultStatus::ErrorLoader) {
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
const u16 error_id = static_cast<u16>(result) - loader_id;
const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id);
LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code);
const auto title =
tr("Error while loading ROM! %1", "%1 signifies a numeric error code.")
.arg(QString::fromStdString(error_code));
const auto description =
tr("%1<br>Please redump your files or ask on Discord/Revolt for help.",
"%1 signifies an error string.")
.arg(QString::fromStdString(
GetResultStatusString(static_cast<Loader::ResultStatus>(error_id))));
QtCommon::Frontend::Critical(title, description);
} else {
QtCommon::Frontend::Critical(
tr("Error while loading ROM!"),
tr("An unknown error occurred. Please see the log for more details."));
}
break;
}
return false;
}
current_game_path = filename;
return true;
}
void MainWindowInterface::shutdownGame() {
if (!emulation_running) {
return;
}
// STUBBED
// TODO(crueter): make this common as well (frontend_common?)
// play_time_manager->Stop();
// OnShutdownBegin();
// OnEmulationStopTimeExpired();
// OnEmulationStopped();
}
/// PROPERTIES ///
QString MainWindowInterface::firmwareDisplay() const {
return m_firmwareDisplay;

41
src/Eden/Interface/MainWindowInterface.h

@ -4,8 +4,17 @@
#pragma once
#include <QObject>
#include <QQuickWindow>
#include "core/hle/service/am/applet_manager.h"
#include "qt_common/qt_common.h"
namespace InputCommon {
class InputSubsystem;
}
class RenderWindow;
class GameListModel;
class QMLConfig;
class MainWindowInterface : public QObject {
Q_OBJECT
Q_PROPERTY(bool firmwareGood READ firmwareGood WRITE setFirmwareGood NOTIFY firmwareGoodChanged FINAL)
@ -15,7 +24,8 @@ class MainWindowInterface : public QObject {
firmwareDisplayChanged FINAL)
public:
explicit MainWindowInterface(GameListModel* model, QObject* parent = nullptr);
explicit MainWindowInterface(GameListModel* model, QMLConfig* config, QQuickWindow* rootWindow,
QObject* parent = nullptr);
Q_INVOKABLE void installFirmware();
Q_INVOKABLE void installFirmwareZip();
@ -32,6 +42,12 @@ public:
Q_INVOKABLE void openQuickstartGuide();
Q_INVOKABLE void openFAQ();
Q_INVOKABLE void openHomeMenu();
Q_INVOKABLE void bootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
StartGameType type = StartGameType::Normal);
Q_INVOKABLE bool loadROM(const QString& filename, Service::AM::FrontendAppletParameters params);
bool firmwareGood() const;
void setFirmwareGood(bool newFirmwareGood);
@ -55,10 +71,29 @@ signals:
private:
GameListModel* m_gameList;
void setFirmwareVersion();
QMLConfig* m_config;
RenderWindow* m_renderWindow;
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
// Whether emulation is currently running in yuzu.
bool emulation_running = false;
// The path to the game currently running
QString current_game_path;
// Whether a user was set on the command line (skips UserSelector if it's forced to show up)
bool user_flag_cmd_line = false;
// Last game booted, used for multi-process apps
QString last_filename_booted;
bool m_firmwareGood = false;
QString m_firmwareDisplay{};
QString m_firmwareTooltip{};
void shutdownGame();
void setFirmwareVersion();
void executeProgram(std::size_t program_index);
Service::AM::FrontendAppletParameters applicationAppletParameters();
Service::AM::FrontendAppletParameters libraryAppletParameters(u64 program_id,
Service::AM::AppletId applet_id);
};

3
src/Eden/Interface/QMLConfig.h

@ -18,9 +18,6 @@ public:
: m_config{new QtConfig}
{}
Q_INVOKABLE inline void reload() {
m_config->ReloadAllValues();
}
Q_INVOKABLE inline void save() {
m_config->SaveAllValues();
}

6
src/Eden/Interface/QMLSetting.cpp

@ -144,11 +144,13 @@ QVariant QMLSetting::value() const
return var;
}
void QMLSetting::setValue(const QVariant &newValue)
{
void QMLSetting::setValue(const QVariant& newValue) {
qDebug() << "changing value" << m_setting->ToString() << "to" << newValue << "for setting"
<< m_setting->GetLabel();
QVariant var = newValue;
var.convert(QMetaType(m_metaType));
qDebug() << var.toString();
m_setting->LoadString(var.toString().toStdString());
emit valueChanged();

230
src/Eden/Interface/RenderWindow.cpp

@ -0,0 +1,230 @@
#include <glad/glad.h>
#include <QQuickWindow>
#include "RenderWindow.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "input_common/main.h"
#include "qt_common/qt_common.h"
#include "qt_common/render/context.h"
#include "qt_common/abstract/frontend.h"
struct OpenGLRenderItem : public QQuickItem {
explicit OpenGLRenderItem(RenderWindow* parent) : QQuickItem(parent) {
window()->setSurfaceType(QWindow::OpenGLSurface);
}
void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) {
context = std::move(context_);
}
private:
std::unique_ptr<Core::Frontend::GraphicsContext> context;
};
struct VulkanRenderItem : public QQuickItem {
explicit VulkanRenderItem(RenderWindow* parent) : QQuickItem(parent) {
window()->setSurfaceType(QWindow::VulkanSurface);
}
};
struct NullRenderItem : public QQuickItem {
explicit NullRenderItem(RenderWindow* parent) : QQuickItem(parent) {}
};
bool RenderWindow::initializeOpenGL() {
#ifdef HAS_OPENGL
if (!QOpenGLContext::supportsThreadedOpenGL()) {
QtCommon::Frontend::Warning(tr("OpenGL not available!"),
tr("OpenGL shared contexts are not supported."));
return false;
}
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
auto child = new OpenGLRenderItem(this);
child_item = child;
child_item->window()->create();
auto context = std::make_shared<OpenGLSharedContext>(child->window());
main_context = context;
child->SetContext(
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->window()));
return true;
#else
QtCommon::Frontend::Warning(tr("OpenGL not available!"),
tr("Eden has not been compiled with OpenGL support."));
return false;
#endif
}
bool RenderWindow::initializeVulkan() {
qDebug() << "initializing Vulkan.";
auto child = new VulkanRenderItem(this);
child_item = child;
// child_item->window()->create();
main_context = std::make_unique<DummyContext>();
return true;
}
void RenderWindow::initializeNull() {
child_item = new NullRenderItem(this);
main_context = std::make_unique<DummyContext>();
}
RenderWindow::RenderWindow(QQuickWindow* window,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
: QQuickItem(window->contentItem()), input_subsystem{std::move(input_subsystem_)} {
// STUBBED
window->setTitle(QStringLiteral("Eden %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
QString::fromUtf8(Common::g_scm_desc)));
input_subsystem->Initialize();
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
}
void RenderWindow::OnFrameDisplayed() {
input_subsystem->GetTas()->UpdateThread();
const InputCommon::TasInput::TasState new_tas_state =
std::get<0>(input_subsystem->GetTas()->GetStatus());
if (!first_frame) {
last_tas_state = new_tas_state;
first_frame = true;
emit FirstFrameDisplayed();
}
if (new_tas_state != last_tas_state) {
last_tas_state = new_tas_state;
emit TasPlaybackStateChanged();
}
}
std::unique_ptr<Core::Frontend::GraphicsContext> RenderWindow::CreateSharedContext() const {
#ifdef HAS_OPENGL
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
// Bind the shared contexts to the main surface in case the backend wants to take over
// presentation
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), child_item->window());
}
#endif
return std::make_unique<DummyContext>();
}
bool RenderWindow::IsShown() const {
// STUBBED
return true;
}
bool RenderWindow::initRenderTarget() {
// STUBBED
main_context.reset();
{
// Create a dummy render widget so that Qt
// places the render window at the correct position.
const QQuickItem dummy_item{this};
}
first_frame = false;
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
if (!initializeOpenGL()) {
return false;
}
break;
case Settings::RendererBackend::Vulkan:
if (!initializeVulkan()) {
return false;
}
break;
case Settings::RendererBackend::Null:
initializeNull();
break;
}
// Update the Window System information with the new render target
window_info = QtCommon::GetWindowSystemInfo(window());
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
onFramebufferSizeChanged();
// BackupGeometry();
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
if (!loadOpenGL()) {
return false;
}
}
return true;
}
void RenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
window()->setMinimumSize(QSize(minimal_size.first, minimal_size.second));
}
bool RenderWindow::loadOpenGL() {
auto context = CreateSharedContext();
auto scope = context->Acquire();
if (!gladLoadGL()) {
QtCommon::Frontend::Warning(
tr("Error while initializing OpenGL!"),
tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver."));
return false;
}
// Display various warnings (but not fatal errors) for missing OpenGL extensions or lack of
// OpenGL 4.6 support
const QString renderer = QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
if (!GLAD_GL_VERSION_4_6) {
QtCommon::Frontend::Warning(tr("Error while initializing OpenGL 4.6!"),
tr("Your GPU may not support OpenGL 4.6, or you do not have the "
"latest graphics driver.<br><br>GL Renderer:<br>%1")
.arg(renderer));
return false;
}
if (QStringList missing_ext = getUnsupportedGLExtensions(); !missing_ext.empty()) {
QtCommon::Frontend::Warning(
tr("Error while initializing OpenGL!"),
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
"have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported "
"extensions:<br>%2")
.arg(renderer, missing_ext.join(QStringLiteral("<br>"))));
// Non fatal
}
return true;
}
QStringList RenderWindow::getUnsupportedGLExtensions() const {
QStringList missing_ext;
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
missing_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
if (!GLAD_GL_ARB_texture_compression_rgtc)
missing_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
if (!missing_ext.empty())
LOG_ERROR(Frontend, "GPU does not support all required extensions");
for (const QString& ext : missing_ext)
LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString());
return missing_ext;
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
//
// Older versions get the window size (density independent pixels),
// and hence, do not support DPI scaling ("retina" displays).
// The result will be a viewport that is smaller than the extent of the window.
void RenderWindow::onFramebufferSizeChanged() {
// Screen changes potentially incur a change in screen DPI, hence we should update the
// framebuffer size
const qreal pixel_ratio = window()->devicePixelRatio();
const u32 width = this->width() * pixel_ratio;
const u32 height = this->height() * pixel_ratio;
UpdateCurrentFramebufferLayout(width, height);
}

55
src/Eden/Interface/RenderWindow.h

@ -0,0 +1,55 @@
#pragma once
#include <QQuickItem>
#include "core/frontend/emu_window.h"
#include "input_common/drivers/tas_input.h"
namespace InputCommon {
class InputSubsystem;
}
namespace Core {
class System;
}
class EmuThread;
class RenderWindow : public QQuickItem, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
RenderWindow(QQuickWindow* window,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
// EmuWindow interface
public:
void OnFrameDisplayed();
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const;
bool IsShown() const;
bool initRenderTarget();
signals:
void FirstFrameDisplayed();
void TasPlaybackStateChanged();
private:
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size);
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
// Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but
// should instead be shared from
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
bool first_frame = false;
InputCommon::TasInput::TasState last_tas_state;
QQuickItem* child_item = nullptr;
void initializeNull();
bool initializeVulkan();
bool initializeOpenGL();
bool loadOpenGL();
QStringList getUnsupportedGLExtensions() const;
void onFramebufferSizeChanged();
};

1
src/Eden/Interface/SettingsInterface.h

@ -48,7 +48,6 @@ enum class Category {
Services = u32(Settings::Category::Services),
Paths = u32(Settings::Category::Paths),
LibraryApplet = u32(Settings::Category::LibraryApplet),
MaxEnum = u32(Settings::Category::MaxEnum),
};
Q_ENUM_NS(Category)
}

10
src/Eden/Main/Main.qml

@ -234,6 +234,7 @@ ApplicationWindow {
Action {
text: qsTr("Open &Home Menu")
onTriggered: MainWindowInterface.openHomeMenu()
}
Action {
@ -329,10 +330,13 @@ ApplicationWindow {
Label {
id: firmware
font.pixelSize: 14
visible: MainWindowInterface.firmwareGood
text: MainWindowInterface.firmwareDisplay
ToolTip.text: MainWindowInterface.firmwareTooltip
visible: typeof MainWindowInterface !== 'undefined'
&& MainWindowInterface.firmwareGood
text: typeof MainWindowInterface
!== 'undefined' ? MainWindowInterface.firmwareDisplay : ""
ToolTip.text: typeof MainWindowInterface
!== 'undefined' ? MainWindowInterface.firmwareTooltip : ""
}
}
}

16
src/Eden/Native/EdenApplication.cpp

@ -107,10 +107,6 @@ int EdenApplication::run() {
TitleManager *title = new TitleManager(&engine);
ctx->setContextProperty(QStringLiteral("TitleManager"), title);
// MainWindow interface
MainWindowInterface* mwint = new MainWindowInterface(gameListModel, &engine);
ctx->setContextProperty(QStringLiteral("MainWindowInterface"), mwint);
// :)
ctx->setContextProperty(QStringLiteral("EdenApplication"), this);
@ -124,6 +120,18 @@ int EdenApplication::run() {
engine.loadFromModule("Eden.Main", "Main");
// MainWindow interface
QObject *root = engine.rootObjects()[0];
QQuickWindow *window = qobject_cast<QQuickWindow *>(root);
if (!window) {
qFatal("Error: Your root item has to be a window.");
return -1;
}
MainWindowInterface* mwint = new MainWindowInterface(gameListModel, config, window, &engine);
ctx->setContextProperty(QStringLiteral("MainWindowInterface"), mwint);
ret = exec();
} while (ret == EXIT_RELOAD);

5
src/frontend_common/config.cpp

@ -6,6 +6,7 @@
#include <algorithm>
#include <array>
#include <iostream>
#include "common/assert.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
@ -881,7 +882,9 @@ const std::string& Config::GetConfigFilePath() const {
void Config::ReadCategory(const Settings::Category category) {
const auto& settings = FindRelevantList(category);
std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); });
std::ranges::for_each(settings, [&](const auto& setting) {
ReadSettingGeneric(setting);
});
}
void Config::WriteCategory(const Settings::Category category) {

3
src/qt_common/CMakeLists.txt

@ -39,6 +39,9 @@ add_library(qt_common STATIC
util/vk_device_info.cpp
util/vk_device_info.h
qt_constants.h
render/emu_thread.h render/emu_thread.cpp
render/context.h
)
if (UNIX)

23
src/qt_common/config/qt_config.cpp

@ -562,12 +562,23 @@ void QtConfig::SaveMultiplayerValues() {
}
std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
// This solution sucks, but by_category is unreliable because the settings backend
// mangles category mapping for some reason.
static auto list = Settings::values.linkage.by_category[category];
auto uilist = UISettings::values.linkage.by_category[category];
list.insert(list.end(), uilist.begin(), uilist.end());
return list;
qDebug() << "-- Category" << u32(category);
auto log_list = [](auto list, QString label) {
qDebug() << "-- !" << label;
for (auto s : list) {
qDebug() << "-- *" << s->GetLabel();
}
};
auto& list = Settings::values.linkage.by_category[category];
log_list(list, QStringLiteral("Settings"));
if (!list.empty())
return list;
auto& list2 = UISettings::values.linkage.by_category[category];
log_list(list2, QStringLiteral("UISettings"));
return list2;
}
void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) {

1
src/qt_common/config/qt_config.h

@ -7,6 +7,7 @@
#pragma once
#include <QMetaType>
#include <QObject>
#include "frontend_common/config.h"

1
src/qt_common/qt_common.cpp

@ -57,6 +57,7 @@ QObject* rootObject = nullptr;
std::unique_ptr<Core::System> system = nullptr;
std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr;
std::unique_ptr<FileSys::ManualContentProvider> provider = nullptr;
std::unique_ptr<EmuThread> emu_thread = nullptr;
const QStringList supported_file_extensions = {QStringLiteral("nro"),
QStringLiteral("nso"),

15
src/qt_common/qt_common.h

@ -4,13 +4,20 @@
#ifndef QT_COMMON_H
#define QT_COMMON_H
#include <memory>
#include <QWindow>
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/file_sys/registered_cache.h"
#include <core/frontend/emu_window.h>
#include <memory>
#include "core/file_sys/vfs/vfs_real.h"
#include <core/file_sys/vfs/vfs_real.h>
#include "qt_common/render/emu_thread.h"
enum class StartGameType {
Normal, // Can use custom configuration
Global, // Only uses global configuration
};
namespace QtCommon {
@ -19,6 +26,8 @@ extern QObject *rootObject;
extern std::unique_ptr<Core::System> system;
extern std::shared_ptr<FileSys::RealVfsFilesystem> vfs;
extern std::unique_ptr<FileSys::ManualContentProvider> provider;
extern std::unique_ptr<EmuThread> emu_thread;
extern const QStringList supported_file_extensions;
typedef std::function<bool(std::size_t, std::size_t)> QtProgressCallback;

104
src/qt_common/render/context.h

@ -0,0 +1,104 @@
#pragma once
#include <QSurface>
#include "common/settings.h"
#include "core/frontend/graphics_context.h"
#include "common/logging/log.h"
#ifdef HAS_OPENGL
#include <QOffscreenSurface>
#include <QOpenGLContext>
#endif
#ifdef HAS_OPENGL
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public:
/// Create the original context that should be shared from
explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} {
QSurfaceFormat format;
format.setVersion(4, 6);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
if (Settings::values.renderer_debug) {
format.setOption(QSurfaceFormat::FormatOption::DebugContext);
}
// TODO: expose a setting for buffer value (ie default/single/double/triple)
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
format.setSwapInterval(0);
context = std::make_unique<QOpenGLContext>();
context->setFormat(format);
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create main openGL context");
}
}
/// Create the shared contexts for rendering and presentation
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
// disable vsync for any shared contexts
auto format = share_context->format();
const int swap_interval =
Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1;
format.setSwapInterval(main_surface ? swap_interval : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
context->setFormat(format);
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create shared openGL context");
}
if (!main_surface) {
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
offscreen_surface->setFormat(format);
offscreen_surface->create();
surface = offscreen_surface.get();
} else {
surface = main_surface;
}
}
~OpenGLSharedContext() {
DoneCurrent();
}
void SwapBuffers() override {
context->swapBuffers(surface);
}
void MakeCurrent() override {
// We can't track the current state of the underlying context in this wrapper class because
// Qt may make the underlying context not current for one reason or another. In particular,
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
// Instead of always just making the context current (which does not have any caching to
// check if the underlying context is already current) we can check for the current context
// in the thread local data by calling `currentContext()` and checking if its ours.
if (QOpenGLContext::currentContext() != context.get()) {
context->makeCurrent(surface);
}
}
void DoneCurrent() override {
context->doneCurrent();
}
QOpenGLContext* GetShareContext() {
return context.get();
}
const QOpenGLContext* GetShareContext() const {
return context.get();
}
private:
// Avoid using Qt parent system here since we might move the QObjects to new threads
// As a note, this means we should avoid using slots/signals with the objects too
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
};
#endif
class DummyContext : public Core::Frontend::GraphicsContext {};

79
src/qt_common/render/emu_thread.cpp

@ -0,0 +1,79 @@
#include "core/core.h"
#include "core/cpu_manager.h"
#include "emu_thread.h"
#include "qt_common/qt_common.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
EmuThread::EmuThread() {}
EmuThread::~EmuThread() = default;
void EmuThread::run() {
Common::SetCurrentThreadName("EmuControlThread");
auto& gpu = QtCommon::system->GPU();
auto stop_token = m_stop_source.get_token();
QtCommon::system->RegisterHostThread();
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution.
gpu.ObtainContext();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
if (Settings::values.use_disk_shader_cache.GetValue()) {
QtCommon::system->Renderer().ReadRasterizer()->LoadDiskResources(
QtCommon::system->GetApplicationProcessProgramID(), stop_token,
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
}
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
gpu.ReleaseContext();
gpu.Start();
QtCommon::system->GetCpuManager().OnGpuReady();
if (QtCommon::system->DebuggerEnabled()) {
QtCommon::system->InitializeDebugger();
}
while (!stop_token.stop_requested()) {
std::unique_lock lk{m_should_run_mutex};
if (m_should_run) {
QtCommon::system->Run();
m_stopped.Reset();
m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; });
} else {
QtCommon::system->Pause();
m_stopped.Set();
EmulationPaused(lk);
m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; });
EmulationResumed(lk);
}
}
// Shutdown the main emulated process
QtCommon::system->DetachDebugger();
QtCommon::system->ShutdownMainProcess();
}
// Unlock while emitting signals so that the main thread can
// continue pumping events.
void EmuThread::EmulationPaused(std::unique_lock<std::mutex>& lk) {
lk.unlock();
emit DebugModeEntered();
lk.lock();
}
void EmuThread::EmulationResumed(std::unique_lock<std::mutex>& lk) {
lk.unlock();
emit DebugModeLeft();
lk.lock();
}

94
src/qt_common/render/emu_thread.h

@ -0,0 +1,94 @@
#pragma once
#include <QThread>
#include "common/logging/log.h"
#include "common/thread.h"
namespace Core {
class System;
} // namespace Core
namespace VideoCore {
enum class LoadCallbackStage;
} // namespace VideoCore
class EmuThread final : public QThread {
Q_OBJECT
public:
explicit EmuThread();
~EmuThread() override;
/**
* Start emulation (on new thread)
* @warning Only call when not running!
*/
void run() override;
/**
* Sets whether the emulation thread should run or not
* @param should_run Boolean value, set the emulation thread to running if true
*/
void SetRunning(bool should_run) {
// TODO: Prevent other threads from modifying the state until we finish.
{
// Notify the running thread to change state.
std::unique_lock run_lk{m_should_run_mutex};
m_should_run = should_run;
m_should_run_cv.notify_one();
}
// Wait until paused, if pausing.
if (!should_run) {
m_stopped.Wait();
}
}
/**
* Check if the emulation thread is running or not
* @return True if the emulation thread is running, otherwise false
*/
bool IsRunning() const {
return m_should_run;
}
/**
* Requests for the emulation thread to immediately stop running
*/
void ForceStop() {
LOG_WARNING(Frontend, "Force stopping EmuThread");
m_stop_source.request_stop();
}
private:
void EmulationPaused(std::unique_lock<std::mutex>& lk);
void EmulationResumed(std::unique_lock<std::mutex>& lk);
private:
std::stop_source m_stop_source;
std::mutex m_should_run_mutex;
std::condition_variable_any m_should_run_cv;
Common::Event m_stopped;
bool m_should_run{true};
signals:
/**
* Emitted when the CPU has halted execution
*
* @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/
void DebugModeEntered();
/**
* Emitted right before the CPU continues execution
*
* @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/
void DebugModeLeft();
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
};

38
src/qt_common/util/content.cpp

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/file_sys/card_image.h"
#include "qt_common/util/content.h"
#include "qt_common/util/game.h"
@ -589,4 +590,41 @@ void InstallFirmwareZip() {
}
}
void configureFilesystemProvider(const std::string& filepath) {
// Ensure all NCAs are registered before launching the game
const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read);
if (!file) {
return;
}
auto loader = Loader::GetLoader(*QtCommon::system, file);
if (!loader) {
return;
}
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return;
}
u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id);
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
QtCommon::provider->AddEntry(FileSys::TitleType::Application,
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
file);
} else if (res2 == Loader::ResultStatus::Success &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
const auto nsp = file_type == Loader::FileType::NSP
? std::make_shared<FileSys::NSP>(file)
: FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) {
for (const auto& entry : title.second) {
QtCommon::provider->AddEntry(entry.first.first, entry.first.second, title.first,
entry.second->GetBaseFile());
}
}
}
}
} // namespace QtCommon::Content

3
src/qt_common/util/content.h

@ -59,6 +59,9 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir dir,
std::function<void()> callback = {});
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
// Loader //
void configureFilesystemProvider(const std::string& filepath);
// Profiles //
void FixProfiles();
}

201
src/yuzu/bootmanager.cpp

@ -6,11 +6,10 @@
#include <cmath>
#include <cstring>
#include <string>
#include <tuple>
#include <type_traits>
#include <glad/glad.h>
#include <QtCore/qglobal.h>
#include <QtGlobal>
#include <qnamespace.h>
#include "common/settings_enums.h"
#include "qt_common/config/uisettings.h"
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
@ -38,27 +37,21 @@
#include <QOpenGLContext>
#endif
#include "common/polyfill_thread.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/settings_input.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/frontend/graphics_context.h"
#include "input_common/drivers/camera.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
#include "input_common/drivers/tas_input.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/main.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "yuzu/bootmanager.h"
#include "yuzu/main_window.h"
#include "qt_common/qt_common.h"
#include "qt_common/render/context.h"
class QObject;
class QPaintEngine;
@ -66,171 +59,6 @@ class QSurface;
constexpr int default_mouse_constrain_timeout = 10;
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
EmuThread::~EmuThread() = default;
void EmuThread::run() {
Common::SetCurrentThreadName("EmuControlThread");
auto& gpu = m_system.GPU();
auto stop_token = m_stop_source.get_token();
m_system.RegisterHostThread();
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution.
gpu.ObtainContext();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
if (Settings::values.use_disk_shader_cache.GetValue()) {
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
m_system.GetApplicationProcessProgramID(), stop_token,
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
}
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
gpu.ReleaseContext();
gpu.Start();
m_system.GetCpuManager().OnGpuReady();
if (m_system.DebuggerEnabled()) {
m_system.InitializeDebugger();
}
while (!stop_token.stop_requested()) {
std::unique_lock lk{m_should_run_mutex};
if (m_should_run) {
m_system.Run();
m_stopped.Reset();
m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; });
} else {
m_system.Pause();
m_stopped.Set();
EmulationPaused(lk);
m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; });
EmulationResumed(lk);
}
}
// Shutdown the main emulated process
m_system.DetachDebugger();
m_system.ShutdownMainProcess();
}
// Unlock while emitting signals so that the main thread can
// continue pumping events.
void EmuThread::EmulationPaused(std::unique_lock<std::mutex>& lk) {
lk.unlock();
emit DebugModeEntered();
lk.lock();
}
void EmuThread::EmulationResumed(std::unique_lock<std::mutex>& lk) {
lk.unlock();
emit DebugModeLeft();
lk.lock();
}
#ifdef HAS_OPENGL
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public:
/// Create the original context that should be shared from
explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} {
QSurfaceFormat format;
format.setVersion(4, 6);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
if (Settings::values.renderer_debug) {
format.setOption(QSurfaceFormat::FormatOption::DebugContext);
}
// TODO: expose a setting for buffer value (ie default/single/double/triple)
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
format.setSwapInterval(0);
context = std::make_unique<QOpenGLContext>();
context->setFormat(format);
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create main openGL context");
}
}
/// Create the shared contexts for rendering and presentation
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
// disable vsync for any shared contexts
auto format = share_context->format();
const int swap_interval =
Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1;
format.setSwapInterval(main_surface ? swap_interval : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
context->setFormat(format);
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create shared openGL context");
}
if (!main_surface) {
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
offscreen_surface->setFormat(format);
offscreen_surface->create();
surface = offscreen_surface.get();
} else {
surface = main_surface;
}
}
~OpenGLSharedContext() {
DoneCurrent();
}
void SwapBuffers() override {
context->swapBuffers(surface);
}
void MakeCurrent() override {
// We can't track the current state of the underlying context in this wrapper class because
// Qt may make the underlying context not current for one reason or another. In particular,
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
// Instead of always just making the context current (which does not have any caching to
// check if the underlying context is already current) we can check for the current context
// in the thread local data by calling `currentContext()` and checking if its ours.
if (QOpenGLContext::currentContext() != context.get()) {
context->makeCurrent(surface);
}
}
void DoneCurrent() override {
context->doneCurrent();
}
QOpenGLContext* GetShareContext() {
return context.get();
}
const QOpenGLContext* GetShareContext() const {
return context.get();
}
private:
// Avoid using Qt parent system here since we might move the QObjects to new threads
// As a note, this means we should avoid using slots/signals with the objects too
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
};
#endif
class DummyContext : public Core::Frontend::GraphicsContext {};
class RenderWidget : public QWidget {
public:
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent) {
@ -271,11 +99,10 @@ struct NullRenderWidget : public RenderWidget {
explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
};
GRenderWindow::GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
Core::System& system_)
GRenderWindow::GRenderWindow(MainWindow* parent,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
: QWidget(parent),
emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, system{system_} {
input_subsystem{std::move(input_subsystem_)} {
setWindowTitle(QStringLiteral("Eden %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
@ -694,7 +521,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
}
void GRenderWindow::ConstrainMouse() {
if (emu_thread == nullptr || !Settings::values.mouse_panning) {
if (QtCommon::emu_thread == nullptr || !Settings::values.mouse_panning) {
mouse_constrain_timer.stop();
return;
}
@ -960,7 +787,7 @@ void GRenderWindow::ReleaseRenderTarget() {
}
void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
auto& renderer = system.Renderer();
auto& renderer = QtCommon::system->Renderer();
if (renderer.IsScreenshotPending()) {
LOG_WARNING(Render,
@ -985,7 +812,11 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
screenshot_image.bits(),
[=, this](bool invert_y) {
const std::string std_screenshot_path = screenshot_path.toStdString();
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
if (screenshot_image.flipped(invert_y ? Qt::Vertical : (Qt::Orientations) 0).save(screenshot_path)) {
#else
if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
#endif
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
} else {
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
@ -1067,7 +898,7 @@ bool GRenderWindow::LoadOpenGL() {
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
"have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported "
"extensions:<br>%2")
.arg(renderer).arg(missing_ext.join(QStringLiteral("<br>"))));
.arg(renderer, missing_ext.join(QStringLiteral("<br>"))));
// Non fatal
}
return true;
@ -1087,12 +918,8 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
return missing_ext;
}
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread_) {
emu_thread = emu_thread_;
}
void GRenderWindow::OnEmulationStopping() {
emu_thread = nullptr;
QtCommon::emu_thread = nullptr;
}
void GRenderWindow::showEvent(QShowEvent* event) {

107
src/yuzu/bootmanager.h

@ -6,12 +6,9 @@
#pragma once
#include <condition_variable>
#include <cstddef>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include <QByteArray>
#include <QImage>
@ -27,9 +24,6 @@
#include <qobjectdefs.h>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/frontend/emu_window.h"
class MainWindow;
@ -45,10 +39,6 @@ class QShowEvent;
class QTouchEvent;
class QWheelEvent;
namespace Core {
class System;
} // namespace Core
namespace InputCommon {
class InputSubsystem;
enum class MouseButton;
@ -58,100 +48,13 @@ namespace InputCommon::TasInput {
enum class TasState;
} // namespace InputCommon::TasInput
namespace VideoCore {
enum class LoadCallbackStage;
} // namespace VideoCore
class EmuThread final : public QThread {
Q_OBJECT
public:
explicit EmuThread(Core::System& system);
~EmuThread() override;
/**
* Start emulation (on new thread)
* @warning Only call when not running!
*/
void run() override;
/**
* Sets whether the emulation thread should run or not
* @param should_run Boolean value, set the emulation thread to running if true
*/
void SetRunning(bool should_run) {
// TODO: Prevent other threads from modifying the state until we finish.
{
// Notify the running thread to change state.
std::unique_lock run_lk{m_should_run_mutex};
m_should_run = should_run;
m_should_run_cv.notify_one();
}
// Wait until paused, if pausing.
if (!should_run) {
m_stopped.Wait();
}
}
/**
* Check if the emulation thread is running or not
* @return True if the emulation thread is running, otherwise false
*/
bool IsRunning() const {
return m_should_run;
}
/**
* Requests for the emulation thread to immediately stop running
*/
void ForceStop() {
LOG_WARNING(Frontend, "Force stopping EmuThread");
m_stop_source.request_stop();
}
private:
void EmulationPaused(std::unique_lock<std::mutex>& lk);
void EmulationResumed(std::unique_lock<std::mutex>& lk);
private:
Core::System& m_system;
std::stop_source m_stop_source;
std::mutex m_should_run_mutex;
std::condition_variable_any m_should_run_cv;
Common::Event m_stopped;
bool m_should_run{true};
signals:
/**
* Emitted when the CPU has halted execution
*
* @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/
void DebugModeEntered();
/**
* Emitted right before the CPU continues execution
*
* @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/
void DebugModeLeft();
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
};
// TODO: This and the Quick render window will probably end up similar
// maybe qt_common those?
class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
explicit GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
Core::System& system_);
explicit GRenderWindow(MainWindow* parent, std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
~GRenderWindow() override;
// EmuWindow implementation.
@ -216,7 +119,6 @@ public:
void Exit();
public slots:
void OnEmulationStarting(EmuThread* emu_thread_);
void OnEmulationStopping();
void OnFramebufferSizeChanged();
@ -246,7 +148,6 @@ private:
bool LoadOpenGL();
QStringList GetUnsupportedGLExtensions() const;
EmuThread* emu_thread;
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
// Main context that will be shared with all other contexts that are requested.
@ -275,8 +176,6 @@ private:
QTimer mouse_constrain_timer;
Core::System& system;
protected:
void showEvent(QShowEvent* event) override;
bool eventFilter(QObject* object, QEvent* event) override;

123
src/yuzu/main_window.cpp

@ -3,6 +3,7 @@
// Qt on macOS doesn't define VMA shit
#include "qt_common/qt_string_lookup.h"
#include "qt_common/render/emu_thread.h"
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION
#endif
@ -104,16 +105,11 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
// Common //
#include "common/fs/fs.h"
#include "common/logging/backend.h"
#include "common/memory_detect.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
// Core //
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/mii_edit.h"
@ -924,7 +920,7 @@ void MainWindow::InitializeWidgets() {
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
ui->action_Report_Compatibility->setVisible(true);
#endif
render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *QtCommon::system);
render_window = new GRenderWindow(this, input_subsystem);
render_window->hide();
game_list = new GameList(QtCommon::vfs, QtCommon::provider.get(), *play_time_manager, *QtCommon::system, this);
@ -1368,11 +1364,11 @@ void MainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
return;
}
if (UISettings::values.pause_when_in_background) {
if (emu_thread->IsRunning() &&
if (QtCommon::emu_thread->IsRunning() &&
(state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
auto_paused = true;
OnPauseGame();
} else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
} else if (!QtCommon::emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
auto_paused = false;
OnStartGame();
}
@ -1428,8 +1424,6 @@ void MainWindow::ConnectWidgetEvents() {
connect(this, &MainWindow::UpdateInstallProgress, this,
&MainWindow::IncrementInstallProgress);
connect(this, &MainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting);
connect(this, &MainWindow::EmulationStopping, render_window,
&GRenderWindow::OnEmulationStopping);
@ -1538,7 +1532,7 @@ void MainWindow::ConnectMenuEvents() {
}
void MainWindow::UpdateMenuState() {
const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning();
const bool is_paused = QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning();
const bool is_firmware_available = CheckFirmwarePresence();
const std::array running_actions{
@ -1610,17 +1604,17 @@ void MainWindow::SetupPrepareForSleep() {
}
void MainWindow::OnPrepareForSleep(bool prepare_sleep) {
if (emu_thread == nullptr) {
if (QtCommon::emu_thread == nullptr) {
return;
}
if (prepare_sleep) {
if (emu_thread->IsRunning()) {
if (QtCommon::emu_thread->IsRunning()) {
auto_paused = true;
OnPauseGame();
}
} else {
if (!emu_thread->IsRunning() && auto_paused) {
if (!QtCommon::emu_thread->IsRunning() && auto_paused) {
auto_paused = false;
OnStartGame();
}
@ -1693,7 +1687,7 @@ void MainWindow::AllowOSSleep() {
bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr) {
if (QtCommon::emu_thread != nullptr) {
ShutdownGame();
}
@ -1812,43 +1806,6 @@ bool MainWindow::SelectAndSetCurrentUser(
return true;
}
void MainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
// Ensure all NCAs are registered before launching the game
const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read);
if (!file) {
return;
}
auto loader = Loader::GetLoader(*QtCommon::system, file);
if (!loader) {
return;
}
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return;
}
u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id);
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
QtCommon::provider->AddEntry(FileSys::TitleType::Application,
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
file);
} else if (res2 == Loader::ResultStatus::Success &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
const auto nsp = file_type == Loader::FileType::NSP
? std::make_shared<FileSys::NSP>(file)
: FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) {
for (const auto& entry : title.second) {
QtCommon::provider->AddEntry(entry.first.first, entry.first.second, title.first,
entry.second->GetBaseFile());
}
}
}
}
void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
StartGameType type) {
LOG_INFO(Frontend, "Eden starting...");
@ -1867,7 +1824,7 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
last_filename_booted = filename;
ConfigureFilesystemProvider(filename.toStdString());
QtCommon::Content::configureFilesystemProvider(filename.toStdString());
const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, filename.toUtf8().constData());
const auto loader = Loader::GetLoader(*QtCommon::system, v_file, params.program_id, params.program_index);
@ -1911,23 +1868,23 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
game_list->setDisabled(true);
// Create and start the emulation thread
emu_thread = std::make_unique<EmuThread>(*QtCommon::system);
emit EmulationStarting(emu_thread.get());
emu_thread->start();
QtCommon::emu_thread = std::make_unique<EmuThread>();
emit EmulationStarting();
QtCommon::emu_thread->start();
// Register an ExecuteProgram callback such that Core can execute a sub-program
QtCommon::system->RegisterExecuteProgramCallback(
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
QtCommon::system->RegisterExitCallback([this] {
emu_thread->ForceStop();
QtCommon::emu_thread->ForceStop();
render_window->Exit();
});
connect(render_window, &GRenderWindow::Closed, this, &MainWindow::OnStopGame);
connect(render_window, &GRenderWindow::MouseActivity, this, &MainWindow::OnMouseActivity);
connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
// Update the GUI
@ -2014,8 +1971,8 @@ bool MainWindow::OnShutdownBegin() {
discord_rpc->Pause();
RequestGameExit();
emu_thread->disconnect();
emu_thread->SetRunning(true);
QtCommon::emu_thread->disconnect();
QtCommon::emu_thread->SetRunning(true);
emit EmulationStopping();
@ -2030,7 +1987,7 @@ bool MainWindow::OnShutdownBegin() {
shutdown_timer.setSingleShot(true);
shutdown_timer.start(shutdown_time);
connect(&shutdown_timer, &QTimer::timeout, this, &MainWindow::OnEmulationStopTimeExpired);
connect(emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped);
connect(QtCommon::emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped);
// Disable everything to prevent anything from being triggered here
ui->action_Pause->setEnabled(false);
@ -2047,17 +2004,17 @@ void MainWindow::OnShutdownBeginDialog() {
}
void MainWindow::OnEmulationStopTimeExpired() {
if (emu_thread) {
emu_thread->ForceStop();
if (QtCommon::emu_thread) {
QtCommon::emu_thread->ForceStop();
}
}
void MainWindow::OnEmulationStopped() {
shutdown_timer.stop();
if (emu_thread) {
emu_thread->disconnect();
emu_thread->wait();
emu_thread.reset();
if (QtCommon::emu_thread) {
QtCommon::emu_thread->disconnect();
QtCommon::emu_thread->wait();
QtCommon::emu_thread.reset();
}
if (shutdown_dialog) {
@ -2931,7 +2888,7 @@ void MainWindow::OnMenuRecentFile() {
void MainWindow::OnStartGame() {
PreventOSSleep();
emu_thread->SetRunning(true);
QtCommon::emu_thread->SetRunning(true);
UpdateMenuState();
OnTasStateChanged();
@ -2957,7 +2914,7 @@ void MainWindow::OnRestartGame() {
}
void MainWindow::OnPauseGame() {
emu_thread->SetRunning(false);
QtCommon::emu_thread->SetRunning(false);
play_time_manager->Stop();
UpdateMenuState();
AllowOSSleep();
@ -2966,7 +2923,7 @@ void MainWindow::OnPauseGame() {
void MainWindow::OnPauseContinueGame() {
if (emulation_running) {
if (emu_thread->IsRunning()) {
if (QtCommon::emu_thread->IsRunning()) {
OnPauseGame();
} else {
OnStartGame();
@ -3552,7 +3509,7 @@ void MainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_
}
void MainWindow::OnLoadAmiibo() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning()) {
return;
}
if (is_amiibo_file_select_active) {
@ -3667,7 +3624,7 @@ void MainWindow::OnVerifyInstalledContents() {
void MainWindow::OnInstallFirmware() {
// Don't do this while emulation is running, that'd probably be a bad idea.
if (emu_thread != nullptr && emu_thread->IsRunning()) {
if (QtCommon::emu_thread != nullptr && QtCommon::emu_thread->IsRunning()) {
return;
}
@ -3677,7 +3634,7 @@ void MainWindow::OnInstallFirmware() {
void MainWindow::OnInstallFirmwareFromZIP() {
// Don't do this while emulation is running, that'd probably be a bad idea.
if (emu_thread != nullptr && emu_thread->IsRunning()) {
if (QtCommon::emu_thread != nullptr && QtCommon::emu_thread->IsRunning()) {
return;
}
@ -3687,7 +3644,7 @@ void MainWindow::OnInstallFirmwareFromZIP() {
void MainWindow::OnInstallDecryptionKeys() {
// Don't do this while emulation is running.
if (emu_thread != nullptr && emu_thread->IsRunning()) {
if (QtCommon::emu_thread != nullptr && QtCommon::emu_thread->IsRunning()) {
return;
}
@ -3899,7 +3856,7 @@ void MainWindow::OnCreateHomeMenuApplicationMenuShortcut() {
}
void MainWindow::OnCaptureScreenshot() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning()) {
return;
}
@ -4035,7 +3992,7 @@ void MainWindow::OnTasStateChanged() {
}
void MainWindow::UpdateStatusBar() {
if (emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) {
if (QtCommon::emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) {
status_bar_update_timer.stop();
return;
}
@ -4172,7 +4129,7 @@ void MainWindow::UpdateInputDrivers() {
}
void MainWindow::HideMouseCursor() {
if (emu_thread == nullptr && UISettings::values.hide_mouse) {
if (QtCommon::emu_thread == nullptr && UISettings::values.hide_mouse) {
mouse_hide_timer.stop();
ShowMouseCursor();
return;
@ -4182,7 +4139,7 @@ void MainWindow::HideMouseCursor() {
void MainWindow::ShowMouseCursor() {
render_window->unsetCursor();
if (emu_thread != nullptr && UISettings::values.hide_mouse) {
if (QtCommon::emu_thread != nullptr && UISettings::values.hide_mouse) {
mouse_hide_timer.start();
}
}
@ -4341,7 +4298,7 @@ bool MainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed
}
bool MainWindow::ConfirmClose() {
if (emu_thread == nullptr ||
if (QtCommon::emu_thread == nullptr ||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
return true;
}
@ -4369,7 +4326,7 @@ void MainWindow::closeEvent(QCloseEvent* event) {
game_list->UnloadController();
// Shutdown session if the emu thread is active...
if (emu_thread != nullptr) {
if (QtCommon::emu_thread != nullptr) {
ShutdownGame();
}
@ -4425,7 +4382,7 @@ void MainWindow::dragMoveEvent(QDragMoveEvent* event) {
}
bool MainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
if (QtCommon::emu_thread == nullptr)
return true;
// Use custom question to link controller navigation
@ -4436,7 +4393,7 @@ bool MainWindow::ConfirmChangeGame() {
}
bool MainWindow::ConfirmForceLockedExit() {
if (emu_thread == nullptr) {
if (QtCommon::emu_thread == nullptr) {
return true;
}
const auto text = tr("The currently running application has requested Eden to not exit.\n\n"

11
src/yuzu/main_window.h

@ -19,6 +19,7 @@
#include "common/common_types.h"
#include "frontend_common/content_manager.h"
#include "input_common/drivers/tas_input.h"
#include "qt_common/qt_common.h"
#include "qt_common/config/qt_config.h"
#include "qt_common/util/game.h"
#include "yuzu/user_data_migration.h"
@ -62,11 +63,6 @@ class QtProfileSelectionDialog;
class QtSoftwareKeyboardDialog;
class QtNXWebEngineView;
enum class StartGameType {
Normal, // Can use custom configuration
Global, // Only uses global configuration
};
namespace Core {
enum class SystemResultStatus : u32;
} // namespace Core
@ -180,7 +176,7 @@ signals:
* @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to
* access/change emulation state).
*/
void EmulationStarting(EmuThread* emu_thread);
void EmulationStarting();
/**
* Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core
@ -448,7 +444,7 @@ private:
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
bool CheckFirmwarePresence();
void SetFirmwareVersion();
void ConfigureFilesystemProvider(const std::string& filepath);
/**
* Open (or not) the right confirm dialog based on current setting and game exit lock
* @returns true if the player confirmed or the settings do no require it
@ -517,7 +513,6 @@ private:
// Whether emulation is currently running in yuzu.
bool emulation_running = false;
std::unique_ptr<EmuThread> emu_thread;
// The path to the game currently running
QString current_game_path;
// Whether a user was set on the command line (skips UserSelector if it's forced to show up)

Loading…
Cancel
Save