[desktop] More qt_common reorganization (#3916)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run

Ported from QML branch.

Main "big" change is that EmuThread is now a shared state in QtCommon,
not individually managed/passed around by GRenderWindow and MainWindow.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3916
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
crueter 2026-05-20 04:49:16 +02:00
parent 300a646a34
commit feb8c5f88e
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
44 changed files with 963 additions and 932 deletions

View file

@ -582,8 +582,6 @@ if (ENABLE_QT)
else()
AddQt(6.9.3)
endif()
set(YUZU_STATIC_BUILD ON)
else()
message(STATUS "Using system Qt")
if (NOT Qt6_DIR)
@ -592,23 +590,7 @@ if (ENABLE_QT)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
endif()
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
if (YUZU_USE_QT_MULTIMEDIA)
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
endif()
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
find_package(Qt6 REQUIRED COMPONENTS DBus OPTIONAL_COMPONENTS GuiPrivate)
elseif (UNIX AND NOT APPLE)
find_package(Qt6 REQUIRED COMPONENTS DBus Gui)
endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
endif()
find_package(Qt6 REQUIRED COMPONENTS Core)
if (NOT DEFINED QT_TARGET_PATH)
get_target_property(qtcore_path Qt6::Core LOCATION_Release)
@ -631,21 +613,27 @@ if (ENABLE_QT)
## Components ##
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent)
if (PLATFORM_LINUX)
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
list(APPEND YUZU_QT_OPTIONAL GuiPrivate)
endif()
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND YUZU_QT_COMPONENTS Multimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND YUZU_QT_COMPONENTS WebEngineCore WebEngineWidgets)
endif()
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS LinguistTools)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS})
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS} OPTIONAL_COMPONENTS ${YUZU_QT_OPTIONAL})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")

View file

@ -715,8 +715,8 @@ function(AddCIPackage)
"${${ARTIFACT_PACKAGE}_SOURCE_DIR}" PARENT_SCOPE)
if (PKG_ARGS_MODULE)
list(APPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
list(PREPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
Propagate(CMAKE_PREFIX_PATH)
endif()
else()
find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED)
@ -730,7 +730,7 @@ function(AddQt version)
endif()
AddCIPackage(
NAME Qt
NAME qt
PACKAGE Qt6
VERSION ${version}
MIN_VERSION 6
@ -740,5 +740,8 @@ function(AddQt version)
freebsd-amd64 solaris-amd64 openbsd-amd64
MODULE)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
find_package(Qt6 REQUIRED PATHS ${Qt6_SOURCE_DIR} NO_DEFAULT_PATH)
Propagate(CMAKE_PREFIX_PATH)
Propagate(Qt6_SOURCE_DIR)
endfunction()

View file

@ -112,7 +112,8 @@
"options": [
"QUAZIP_QT_MAJOR_VERSION 6",
"QUAZIP_INSTALL OFF",
"QUAZIP_ENABLE_QTEXTCODEC OFF"
"QUAZIP_ENABLE_QTEXTCODEC OFF",
"QUAZIP_BZIP2 OFF"
]
}
}

View file

@ -435,4 +435,16 @@ void ToggleSlowMode() {
SetSpeedMode(SpeedMode::Standard);
}
bool IsOpenGL() {
const auto backend = Settings::values.renderer_backend.GetValue();
switch (backend) {
case RendererBackend::OpenGL_GLSL:
case RendererBackend::OpenGL_GLASM:
case RendererBackend::OpenGL_SPIRV:
return true;
default:
return false;
}
}
} // namespace Settings

View file

@ -863,6 +863,8 @@ bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
bool IsOpenGL();
bool IsDockedMode();
float Volume();

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -16,6 +19,12 @@
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs_types.h"
#undef CreateFile
#undef CopyFile
#undef MoveFile
#undef DeleteFile
#undef CreateDirectory
namespace FileSys {
// An enumeration representing what can be at the end of a path in a VfsFilesystem

View file

@ -7,8 +7,7 @@
add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
yuzu_room.cpp
yuzu_room.h
yuzu_room.rc
)
yuzu_room.rc)
target_link_libraries(yuzu-room PRIVATE common network)
if (ENABLE_WEB_SERVICE)

View file

@ -26,6 +26,7 @@ add_library(qt_common STATIC
util/compress.h util/compress.cpp
util/fs.h util/fs.cpp
util/mod.h util/mod.cpp
util/vk.h util/vk.cpp
abstract/frontend.h abstract/frontend.cpp
abstract/progress.h abstract/progress.cpp
@ -33,7 +34,9 @@ add_library(qt_common STATIC
qt_string_lookup.h
qt_compat.h
discord/discord.h)
discord/discord.h
render/context.h
render/emu_thread.h render/emu_thread.cpp)
if (UNIX)
target_sources(qt_common PRIVATE gui_settings.cpp gui_settings.h)
@ -56,9 +59,7 @@ if (USE_DISCORD_PRESENCE)
endif()
# TODO(crueter)
if (ENABLE_QT)
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
endif()
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
target_compile_definitions(qt_common PUBLIC
# Use QStringBuilder for string concatenation to reduce
@ -80,7 +81,7 @@ target_compile_definitions(qt_common PUBLIC
)
target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip)
target_link_libraries(qt_common PUBLIC frozen::frozen-headers)
target_link_libraries(qt_common PUBLIC frozen::frozen-headers Vulkan::Headers)
target_link_libraries(qt_common PRIVATE gamemode::headers frontend_common)
if (ENABLE_OPENGL)

View file

@ -165,8 +165,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, use_disk_shader_cache, tr("Use persistent pipeline cache"),
tr("Allows saving shaders to storage for faster loading on following game "
"boots.\nDisabling it is only intended for debugging."));
INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
INSERT(
Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
"decoding, or perform no decoding at all (black screen on videos).\n"

View file

@ -1,25 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/fs/fs.h"
#include "common/literals.h"
#include "common/memory_detect.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "hid_core/hid_core.h"
#include "network/network.h"
#include "qt_common.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "common/scm_rev.h"
#include "core/memory.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
#include <QGuiApplication>
#include <QStringLiteral>
#include "common/logging.h"
#include "core/frontend/emu_window.h"
#include "qt_common/util/meta.h"
#include <QFile>
#include <QMessageBox>
#include <thread>
#include <JlCompress.h>
#if !defined(WIN32) && !defined(__APPLE__)
#include <qpa/qplatformnativeinterface.h>
#elif defined(__APPLE__)
#include <objc/message.h>
#include <objc/runtime.h>
#endif
#ifdef _WIN32
#include <QApplication>
#include <QSettings>
#include <windows.h>
#include "common/windows/timer_resolution.h"
#include "core/core_timing.h"
#endif
using namespace Common::Literals;
namespace QtCommon {
QWidget* rootObject = nullptr;
@ -27,6 +54,11 @@ QWidget* 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"),
QStringLiteral("nca"), QStringLiteral("xci"),
QStringLiteral("nsp"), QStringLiteral("kip")};
Core::Frontend::WindowSystemType GetWindowSystemType() {
// Determine WSI type based on Qt platform.
@ -58,37 +90,8 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
// Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast<void*>(window->winId());
#elif defined(__APPLE__)
id layer = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)(
wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)(
reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
// In Qt 6, the layer of the NSView might be a QContainerLayer.
// MoltenVK needs a CAMetalLayer. We search for it in sublayers.
Class metal_layer_class = objc_getClass("CAMetalLayer");
id metal_layer = nullptr;
if (layer) {
if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
layer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
metal_layer = layer;
} else {
id sublayers = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)(
layer, sel_registerName("sublayers"));
if (sublayers) {
unsigned long count = reinterpret_cast<unsigned long (*)(id, SEL)>(objc_msgSend)(
sublayers, sel_registerName("count"));
for (unsigned long i = 0; i < count; ++i) {
id sublayer = reinterpret_cast<id (*)(id, SEL, unsigned long)>(objc_msgSend)(
sublayers, sel_registerName("objectAtIndex:"), i);
if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
sublayer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
metal_layer = sublayer;
break;
}
}
}
}
}
wsi.render_surface = reinterpret_cast<void*>(metal_layer ? metal_layer : layer);
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
@ -103,18 +106,148 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
}
const QString tr(const char* str) {
return QGuiApplication::tr(str);
return rootObject->tr(str);
}
const QString tr(const std::string& str) {
return QGuiApplication::tr(str.c_str());
return rootObject->tr(str.c_str());
}
static void LogRuntimes() {
#ifdef _MSC_VER
// It is possible that the name of the dll will change.
// vcruntime140.dll is for 2015 and onwards
static constexpr char runtime_dll_name[] = "vcruntime140.dll";
UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
bool runtime_version_inspection_worked = false;
if (sz > 0) {
std::vector<u8> buf(sz);
if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
VS_FIXEDFILEINFO* pvi;
sz = sizeof(VS_FIXEDFILEINFO);
if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
if (pvi->dwSignature == VS_FFI_SIGNATURE) {
runtime_version_inspection_worked = true;
LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
}
}
}
}
if (!runtime_version_inspection_worked) {
LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
}
#endif
LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
}
static QString PrettyProductName() {
#ifdef _WIN32
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
// With that notation change they changed the registry key used to denote the current version
QSettings windows_registry(
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
QSettings::NativeFormat);
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
if (release_id == QStringLiteral("2009")) {
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
const QString display_version =
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
u32 version = 10;
if (current_build >= 22000) {
version = 11;
}
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
.arg(QString::number(version), display_version, QString::number(current_build),
QString::number(ubr));
}
#endif
return QSysInfo::prettyProductName();
}
static void RemoveCachedContents() {
const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
const auto offline_fonts = cache_dir / "fonts";
const auto offline_manual = cache_dir / "offline_web_applet_manual";
const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
const auto offline_system_data = cache_dir / "offline_web_applet_system_data";
Common::FS::RemoveDirRecursively(offline_fonts);
Common::FS::RemoveDirRecursively(offline_manual);
Common::FS::RemoveDirRecursively(offline_legal_information);
Common::FS::RemoveDirRecursively(offline_system_data);
}
void Init(QWidget* root) {
system = std::make_unique<Core::System>();
rootObject = root;
system = std::make_unique<Core::System>();
vfs = std::make_unique<FileSys::RealVfsFilesystem>();
provider = std::make_unique<FileSys::ManualContentProvider>();
// initialization stuff
Common::FS::CreateEdenPaths();
system->Initialize();
Common::Log::Initialize();
Common::Log::Start();
Network::Init();
QtCommon::Meta::RegisterMetaTypes();
// build version
const auto branch_name = std::string(Common::g_scm_branch);
const auto description = std::string(Common::g_scm_desc);
const auto build_id = std::string(Common::g_build_id);
const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description);
const auto override_build =
fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
const auto processor_count = std::thread::hardware_concurrency();
// info logging
LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version);
LogRuntimes();
#ifdef ARCHITECTURE_x86_64
const auto& caps = Common::GetCPUCaps();
std::string cpu_string = caps.cpu_string;
if (caps.avx || caps.avx2 || caps.avx512f) {
cpu_string += " | AVX";
if (caps.avx512f) {
cpu_string += "512";
} else if (caps.avx2) {
cpu_string += '2';
}
if (caps.fma || caps.fma4) {
cpu_string += " | FMA";
}
}
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
if (std::optional<int> processor_core = Common::GetProcessorCount()) {
LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
}
#endif
LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
#ifdef _WIN32
LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms",
std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(
Common::Windows::SetCurrentTimerResolutionToMaximum())
.count());
QtCommon::system->CoreTiming().SetTimerResolutionNs(
Common::Windows::GetCurrentTimerResolution());
#endif
// Remove cached contents generated during the previous session
RemoveCachedContents();
}
std::filesystem::path GetEdenCommand() {
@ -137,4 +270,15 @@ std::filesystem::path GetEdenCommand() {
return command;
}
void SetupContentProviders() {
system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
provider.get());
system->GetFileSystemController().CreateFactories(*vfs);
}
void SetupHID() {
system->HIDCore().ReloadInputDevices();
}
} // namespace QtCommon

View file

@ -6,19 +6,31 @@
#include <memory>
#include <QWindow>
#include <core/frontend/emu_window.h>
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/frontend/emu_window.h"
#include <core/file_sys/vfs/vfs_real.h>
#include "qt_common/render/emu_thread.h"
#include "core/file_sys/vfs/vfs_real.h"
enum class StartGameType {
Normal, // Can use custom configuration
Global, // Only uses global configuration
};
namespace QtCommon {
// TODO: Remove QWidget dependency
extern QWidget* 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;
@ -28,6 +40,9 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
void Init(QWidget* root);
void SetupContentProviders();
void SetupHID();
const QString tr(const char* str);
const QString tr(const std::string& str);

View file

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QSurface>
#include "common/logging.h"
#include "common/settings.h"
#include "core/frontend/graphics_context.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 {};

View file

@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <qdebug.h>
#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();
}

View file

@ -0,0 +1,97 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QThread>
#include "common/logging.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);
};

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 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"
@ -494,5 +495,94 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str
});
}
// TODO(crueter): Port InstallFirmware et al. from QML Branch
bool CheckKeys() {
if (!ContentManager::AreKeysPresent()) {
QtCommon::Frontend::Information(
tr("Keys not installed"),
tr("Install decryption keys and restart Eden before attempting to install firmware."));
return false;
}
return true;
}
void InstallFirmware() {
if (!CheckKeys())
return;
const QString firmware_source_location =
QtCommon::Frontend::GetExistingDirectory(tr("Select Dumped Firmware Source Location"), {});
if (!firmware_source_location.isEmpty())
QtCommon::Content::InstallFirmware(firmware_source_location, false);
}
void InstallFirmwareZip() {
if (!CheckKeys())
return;
const QString firmware_zip_location = QtCommon::Frontend::GetOpenFileName(
tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)"));
if (firmware_zip_location.isEmpty())
return;
const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location);
// In this case, it has to be done recursively, since sometimes people
// will pack it into a subdirectory after dumping
if (!qCacheDir.isEmpty()) {
QtCommon::Content::InstallFirmware(qCacheDir, true);
std::error_code ec;
std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware",
ec);
if (ec) {
QtCommon::Frontend::Warning(
tr("Firmware cleanup failed"),
tr("Failed to clean up extracted firmware cache.\n"
"Check write permissions in the system temp directory and try "
"again.\nOS reported error: %1")
.arg(QString::fromStdString(ec.message())));
}
}
}
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

View file

@ -37,9 +37,12 @@ inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult
}
void InstallFirmware(const QString& location, bool recursive);
QString UnzipFirmwareToTmp(const QString& location);
bool CheckKeys();
void InstallFirmware();
void InstallFirmwareZip();
// Keys //
void InstallKeys();
@ -54,6 +57,9 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string&
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();
} // namespace QtCommon::Content

View file

@ -11,7 +11,6 @@
#include "qt_common/abstract/frontend.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/qt_common.h"
#include "yuzu/util/util.h"
#include <QDesktopServices>
#include <QStandardPaths>
@ -22,6 +21,7 @@
#include <windows.h>
#include "common/scope_exit.h"
#include "common/string_util.h"
#include "common/fs/file.h"
#else
#include <fstream>
#include "fmt/ostream.h"
@ -367,6 +367,7 @@ void ResetMetadata(bool show_message) {
// Uhhh //
// TODO(crueter): Make QtCommon::Shortcut
// Messages in pre-defined message boxes for less code spaghetti
inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title) {
int result = 0;
@ -533,4 +534,110 @@ void CreateHomeMenuShortcut(ShortcutTarget target) {
CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
}
bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
#if defined(WIN32)
#pragma pack(push, 2)
struct IconDir {
WORD id_reserved;
WORD id_type;
WORD id_count;
};
struct IconDirEntry {
BYTE width;
BYTE height;
BYTE color_count;
BYTE reserved;
WORD planes;
WORD bit_count;
DWORD bytes_in_res;
DWORD image_offset;
};
#pragma pack(pop)
const QImage source_image = image.convertToFormat(QImage::Format_RGB32);
constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16};
constexpr int bytes_per_pixel = 4;
const IconDir icon_dir{
.id_reserved = 0,
.id_type = 1,
.id_count = static_cast<WORD>(scale_sizes.size()),
};
Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
if (!icon_file.IsOpen()) {
return false;
}
if (!icon_file.Write(icon_dir)) {
return false;
}
std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size());
for (std::size_t i = 0; i < scale_sizes.size(); i++) {
const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel;
const IconDirEntry icon_entry{
.width = static_cast<BYTE>(scale_sizes[i]),
.height = static_cast<BYTE>(scale_sizes[i]),
.color_count = 0,
.reserved = 0,
.planes = 1,
.bit_count = bytes_per_pixel * 8,
.bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
.image_offset = static_cast<DWORD>(image_offset),
};
image_offset += icon_entry.bytes_in_res;
if (!icon_file.Write(icon_entry)) {
return false;
}
}
for (std::size_t i = 0; i < scale_sizes.size(); i++) {
const QImage scaled_image = source_image.scaled(
scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
const BITMAPINFOHEADER info_header{
.biSize = sizeof(BITMAPINFOHEADER),
.biWidth = scaled_image.width(),
.biHeight = scaled_image.height() * 2,
.biPlanes = 1,
.biBitCount = bytes_per_pixel * 8,
.biCompression = BI_RGB,
.biSizeImage{},
.biXPelsPerMeter{},
.biYPelsPerMeter{},
.biClrUsed{},
.biClrImportant{},
};
if (!icon_file.Write(info_header)) {
return false;
}
for (int y = 0; y < scaled_image.height(); y++) {
const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y);
std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel);
std::memcpy(line_data.data(), line, line_data.size());
if (!icon_file.Write(line_data)) {
return false;
}
}
}
icon_file.Close();
return true;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
// Convert and write the icon as a PNG
if (!image.save(QString::fromStdString(icon_path.string()))) {
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
} else {
LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
}
return true;
#else
return false;
#endif
}
} // namespace QtCommon::Game

View file

@ -69,6 +69,14 @@ void CreateShortcut(const std::string& game_path, const u64 program_id,
std::string GetShortcutPath(ShortcutTarget target);
void CreateHomeMenuShortcut(ShortcutTarget target);
/**
* Saves a windows icon to a file
* @param path The icons path
* @param image The image to save
* @return bool If the operation succeeded
*/
[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
} // namespace QtCommon::Game
#endif // QT_GAME_UTIL_H

View file

@ -11,13 +11,13 @@
#include "common/dynamic_library.h"
#include "common/logging.h"
#include "qt_common/util/vk.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "vulkan/vulkan_core.h"
#include "yuzu/vk_device_info.h"
class QWindow;

71
src/qt_common/util/vk.h Normal file
View file

@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "vulkan/vulkan_core.h"
static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_FIFO_KHR};
// Converts a setting to a present mode (or vice versa)
static inline constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::Fifo:
return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FifoRelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default:
return VK_PRESENT_MODE_FIFO_KHR;
}
}
static inline constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) {
switch (mode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
return Settings::VSyncMode::Immediate;
case VK_PRESENT_MODE_MAILBOX_KHR:
return Settings::VSyncMode::Mailbox;
case VK_PRESENT_MODE_FIFO_KHR:
return Settings::VSyncMode::Fifo;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return Settings::VSyncMode::FifoRelaxed;
default:
return Settings::VSyncMode::Fifo;
}
}
class QWindow;
namespace Settings {
enum class VSyncMode : u32;
}
namespace VkDeviceInfo {
// Short class to record Vulkan driver information for configuration purposes
class Record {
public:
explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
bool has_broken_compute);
~Record();
const std::string name;
const std::vector<VkPresentModeKHR> vsync_support;
const bool has_broken_compute;
};
// TODO(crueter): Port some configure_graphics.cpp stuff
void PopulateRecords(std::vector<Record>& records, QWindow* window);
} // namespace VkDeviceInfo

View file

@ -217,8 +217,6 @@ add_executable(yuzu
util/url_request_interceptor.h
util/util.cpp
util/util.h
vk_device_info.cpp
vk_device_info.h
compatdb.cpp
compatdb.h
user_data_migration.cpp
@ -245,9 +243,7 @@ add_executable(yuzu
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
libqt_common.h libqt_common.cpp
updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui
)
updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui)
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")

View file

@ -6,8 +6,6 @@
#include <cmath>
#include <cstring>
#include <string>
#include <tuple>
#include <type_traits>
#ifdef HAS_OPENGL
#include <glad/glad.h>
@ -45,16 +43,13 @@
#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"
@ -62,189 +57,19 @@
#include "input_common/main.h"
#include "qt_common/qt_common.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/render/context.h"
#include "qt_common/render/emu_thread.h"
class QObject;
class QPaintEngine;
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 {
if (auto window = static_cast<QWindow*>(surface)) {
if (!window->isExposed()) {
LOG_DEBUG(Frontend, "SwapBuffers ignored: window not exposed");
return;
}
}
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) {
@ -285,11 +110,9 @@ 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_)
: QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)},
system{system_} {
GRenderWindow::GRenderWindow(MainWindow* parent,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
: QWidget(parent), 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),
@ -708,10 +531,11 @@ 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;
}
if (!this->isActiveWindow()) {
mouse_constrain_timer.stop();
return;
@ -964,7 +788,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,
@ -989,7 +813,12 @@ 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);
@ -1026,7 +855,8 @@ bool GRenderWindow::InitializeOpenGL() {
return true;
#else
QMessageBox::warning(this, tr("OpenGL not available!"), tr("Eden has not been compiled with OpenGL support."));
QMessageBox::warning(this, tr("OpenGL not available!"),
tr("Eden has not been compiled with OpenGL support."));
return false;
#endif
}
@ -1072,8 +902,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;
@ -1101,14 +930,6 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
return missing_ext;
}
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread_) {
emu_thread = emu_thread_;
}
void GRenderWindow::OnEmulationStopping() {
emu_thread = nullptr;
}
void GRenderWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event);

View file

@ -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.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/frontend/emu_window.h"
class MainWindow;
@ -46,6 +40,7 @@ class QShowEvent;
class QTouchEvent;
class QWheelEvent;
class EmuThread;
namespace Core {
class System;
} // namespace Core
@ -63,96 +58,12 @@ 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);
};
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.
@ -217,8 +128,6 @@ public:
void Exit();
public slots:
void OnEmulationStarting(EmuThread* emu_thread_);
void OnEmulationStopping();
void OnFramebufferSizeChanged();
signals:
@ -247,7 +156,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.
@ -277,8 +185,6 @@ private:
QTimer mouse_constrain_timer;
Core::System& system;
protected:
void showEvent(QShowEvent* event) override;
bool eventFilter(QObject* object, QEvent* event) override;

View file

@ -33,8 +33,8 @@ ModSelectDialog::ModSelectDialog(const QStringList& mods, QWidget* parent)
ui->treeView->expandAll();
int rows = item_model->rowCount();
int height =
4 + ui->treeView->contentsMargins().top() * 4 + ui->treeView->contentsMargins().bottom() * 4;
int height = 4 + ui->treeView->contentsMargins().top() * 4 +
ui->treeView->contentsMargins().bottom() * 4;
int width = 0;
for (int i = 0; i < rows; ++i) {

View file

@ -76,7 +76,8 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
} else if (setting->Id() == Settings::values.cpu_backend.Id()) {
backend_layout->addWidget(widget);
backend_combobox = widget->combobox;
} else if (setting->Id() == Settings::values.fast_cpu_time.Id() || setting->Id() == Settings::values.cpu_ticks.Id()) {
} else if (setting->Id() == Settings::values.fast_cpu_time.Id() ||
setting->Id() == Settings::values.cpu_ticks.Id()) {
ui->general_layout->addWidget(widget);
} else {
// Presently, all other settings here are unsafe checkboxes

View file

@ -51,8 +51,10 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
ui->serial_battery_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));
ui->serial_board_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue())));
ui->serial_battery_edit->setText(
QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));
ui->serial_board_edit->setText(
QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue())));
#ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
#else

View file

@ -10,8 +10,8 @@
#include "common/settings_enums.h"
#include "core/core.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/util/vk.h"
#include "ui_configure.h"
#include "vk_device_info.h"
#include "yuzu/configuration/configure_applets.h"
#include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/configure_cpu.h"

View file

@ -11,8 +11,8 @@
#include <QDialog>
#include "configuration/shared_widget.h"
#include "qt_common/config/shared_translation.h"
#include "qt_common/util/vk.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/vk_device_info.h"
namespace Core {
class System;

View file

@ -4,14 +4,7 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <functional>
#include <iosfwd>
#include <iterator>
#include <string>
#include <tuple>
#include <typeinfo>
#include <utility>
#include <vector>
#include <QBoxLayout>
#include <QCheckBox>
@ -34,52 +27,15 @@
#include <vulkan/vulkan_core.h>
#include "common/common_types.h"
#include "common/dynamic_library.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/core.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/qt_common.h"
#include "qt_common/util/vk.h"
#include "ui_configure_graphics.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/vk_device_info.h"
static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_FIFO_KHR};
// Converts a setting to a present mode (or vice versa)
static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::Fifo:
return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FifoRelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default:
return VK_PRESENT_MODE_FIFO_KHR;
}
}
static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) {
switch (mode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
return Settings::VSyncMode::Immediate;
case VK_PRESENT_MODE_MAILBOX_KHR:
return Settings::VSyncMode::Mailbox;
case VK_PRESENT_MODE_FIFO_KHR:
return Settings::VSyncMode::Fifo;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return Settings::VSyncMode::FifoRelaxed;
default:
return Settings::VSyncMode::Fifo;
}
}
ConfigureGraphics::ConfigureGraphics(
const Core::System& system_, std::vector<VkDeviceInfo::Record>& records_,

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
@ -19,7 +19,7 @@
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "qt_common/config/shared_translation.h"
#include "vk_device_info.h"
#include "qt_common/util/vk.h"
#include "yuzu/configuration/configuration_shared.h"
class QPushButton;

View file

@ -30,6 +30,7 @@
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/util/vk.h"
#include "ui_configure_per_game.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_applets.h"
@ -44,7 +45,6 @@
#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/configuration/configure_system.h"
#include "yuzu/util/util.h"
#include "yuzu/vk_device_info.h"
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
std::vector<VkDeviceInfo::Record>& vk_device_records,

View file

@ -18,7 +18,7 @@
#include "frontend_common/config.h"
#include "qt_common/config/qt_config.h"
#include "qt_common/config/shared_translation.h"
#include "vk_device_info.h"
#include "qt_common/util/vk.h"
#include "yuzu/configuration/configuration_shared.h"
namespace Core {

View file

@ -63,27 +63,31 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
void DataWidget::clear() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
if (!user_id)
return;
QtCommon::Content::ClearDataDir(m_dir, user_id.value());
scan();
}
void DataWidget::open() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value()))));
if (!user_id)
return;
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(
FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value()))));
}
void DataWidget::upload() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
if (!user_id)
return;
QtCommon::Content::ExportDataDir(m_dir, user_id.value(), m_exportName);
}
void DataWidget::download() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
if (!user_id)
return;
QtCommon::Content::ImportDataDir(m_dir, user_id.value(), std::bind(&DataWidget::scan, this));
}
@ -107,7 +111,8 @@ std::optional<std::string> DataWidget::selectProfile() {
std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = GetProfileIDString();
if (user_id.empty()) return std::nullopt;
if (user_id.empty())
return std::nullopt;
}
return user_id;

View file

@ -11,7 +11,6 @@
#endif
#include "common/logging.h"
#include "yuzu/debugger/console.h"
#include "qt_common/config/uisettings.h"
#include "yuzu/debugger/console.h"

View file

@ -36,7 +36,8 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
const auto x_pos = option.rect.left() - (column * cell_width) + static_cast<int>(relative_x);
// also, add some additional padding here to prevent card overlap
QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8, option.rect.height() - margins);
QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8,
option.rect.height() - margins);
// colors
QPalette palette = option.palette;

View file

@ -1113,10 +1113,6 @@ void GameList::LoadInterfaceLayout() {
header->resizeSection(COLUMN_NAME, 840);
}
const QStringList GameList::supported_file_extensions = {
QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
void GameList::RefreshGameDirectory() {
// Reset the externals watcher whenever the game list is reloaded,
// primarily ensures that new titles and external dirs are caught.

View file

@ -95,8 +95,6 @@ public:
/// Disables events from the emulated controller
void UnloadController();
static const QStringList supported_file_extensions;
bool IsTreeMode();
void ResetViewMode();

View file

@ -20,17 +20,18 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/qt_common.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/game/game_list.h"
#include "yuzu/game/game_list_p.h"
@ -148,7 +149,7 @@ void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const
bool HasSupportedFileExtension(const std::string& file_name) {
const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
return QtCommon::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
bool IsExtractedNCAMain(const std::string& file_name) {

View file

@ -2,17 +2,17 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Qt on macOS doesn't define VMA shit
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION
#endif
#include <boost/algorithm/string/split.hpp>
#include "common/fs/path_util.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "frontend_common/settings_generator.h"
#include "qt_common/qt_string_lookup.h"
#include "render/performance_overlay.h"
#include "updater/update_dialog.h"
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION
#endif
#include "common/fs/ryujinx_compat.h"
#include "main_window.h"
@ -31,10 +31,10 @@
#include "bootmanager.h"
#include "loading_screen.h"
#include "qt_common/util/vk.h"
#include "ryujinx_dialog.h"
#include "set_play_time_dialog.h"
#include "util/util.h"
#include "vk_device_info.h"
#include "yuzu/game/game_list.h"
#include "applets/qt_amiibo_settings.h"
@ -86,6 +86,7 @@
#include "qt_common/abstract/frontend.h"
#include "qt_common/qt_common.h"
#include "qt_common/qt_string_lookup.h"
#include "qt_common/util/content.h"
#include "qt_common/util/fs.h"
@ -93,6 +94,8 @@
#include "qt_common/util/mod.h"
#include "qt_common/util/path.h"
#include "qt_common/render/emu_thread.h"
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
// defines.
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(const std::string& path,
@ -315,73 +318,6 @@ enum class CalloutFlag : uint32_t {
const int MainWindow::max_recent_files_item;
static void RemoveCachedContents() {
const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
const auto offline_fonts = cache_dir / "fonts";
const auto offline_manual = cache_dir / "offline_web_applet_manual";
const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
const auto offline_system_data = cache_dir / "offline_web_applet_system_data";
Common::FS::RemoveDirRecursively(offline_fonts);
Common::FS::RemoveDirRecursively(offline_manual);
Common::FS::RemoveDirRecursively(offline_legal_information);
Common::FS::RemoveDirRecursively(offline_system_data);
}
static void LogRuntimes() {
#ifdef _MSC_VER
// It is possible that the name of the dll will change.
// vcruntime140.dll is for 2015 and onwards
static constexpr char runtime_dll_name[] = "vcruntime140.dll";
UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
bool runtime_version_inspection_worked = false;
if (sz > 0) {
std::vector<u8> buf(sz);
if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
VS_FIXEDFILEINFO* pvi;
sz = sizeof(VS_FIXEDFILEINFO);
if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
if (pvi->dwSignature == VS_FFI_SIGNATURE) {
runtime_version_inspection_worked = true;
LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
}
}
}
}
if (!runtime_version_inspection_worked) {
LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
}
#endif
LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
}
static QString PrettyProductName() {
#ifdef _WIN32
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
// With that notation change they changed the registry key used to denote the current version
QSettings windows_registry(
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
QSettings::NativeFormat);
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
if (release_id == QStringLiteral("2009")) {
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
const QString display_version =
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
u32 version = 10;
if (current_build >= 22000) {
version = 11;
}
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
.arg(QString::number(version), display_version, QString::number(current_build),
QString::number(ubr));
}
#endif
return QSysInfo::prettyProductName();
}
namespace {
constexpr std::array<std::pair<u32, const char*>, 5> default_game_icon_sizes{
@ -447,11 +383,6 @@ MainWindow::MainWindow(bool has_broken_vulkan)
UISettings::RestoreWindowState(config);
QtCommon::system->Initialize();
Common::Log::Initialize();
Common::Log::Start();
LoadTranslation();
FrontendCommon::GenerateSettings();
@ -472,10 +403,6 @@ MainWindow::MainWindow(bool has_broken_vulkan)
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>();
Network::Init();
QtCommon::Meta::RegisterMetaTypes();
InitializeWidgets();
InitializeDebugWidgets();
InitializeRecentFileMenuActions();
@ -487,53 +414,9 @@ MainWindow::MainWindow(bool has_broken_vulkan)
ConnectMenuEvents();
ConnectWidgetEvents();
QtCommon::system->HIDCore().ReloadInputDevices();
QtCommon::SetupHID();
controller_dialog->refreshConfiguration();
const auto branch_name = std::string(Common::g_scm_branch);
const auto description = std::string(Common::g_scm_desc);
const auto build_id = std::string(Common::g_build_id);
const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description);
const auto override_build =
fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
const auto processor_count = std::thread::hardware_concurrency();
LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version);
LogRuntimes();
#ifdef ARCHITECTURE_x86_64
const auto& caps = Common::GetCPUCaps();
std::string cpu_string = caps.cpu_string;
if (caps.avx || caps.avx2 || caps.avx512f) {
cpu_string += " | AVX";
if (caps.avx512f) {
cpu_string += "512";
} else if (caps.avx2) {
cpu_string += '2';
}
if (caps.fma || caps.fma4) {
cpu_string += " | FMA";
}
}
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
if (std::optional<int> processor_core = Common::GetProcessorCount()) {
LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
}
#endif
LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
#ifdef _WIN32
LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms",
std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(
Common::Windows::SetCurrentTimerResolutionToMaximum())
.count());
QtCommon::system->CoreTiming().SetTimerResolutionNs(
Common::Windows::GetCurrentTimerResolution());
#endif
UpdateWindowTitle();
show();
@ -548,13 +431,8 @@ MainWindow::MainWindow(bool has_broken_vulkan)
}
#endif
QtCommon::system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
QtCommon::provider.get());
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
// Remove cached contents generated during the previous session
RemoveCachedContents();
// Setup content providers.
QtCommon::SetupContentProviders();
// Gen keys if necessary
OnCheckFirmwareDecryption();
@ -1079,7 +957,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,
@ -1539,11 +1417,12 @@ 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();
}
@ -1595,11 +1474,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);
// Software Keyboard Applet
connect(this, &MainWindow::EmulationStarting, this, &MainWindow::SoftwareKeyboardExit);
connect(this, &MainWindow::EmulationStopping, this, &MainWindow::SoftwareKeyboardExit);
@ -1753,7 +1627,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{
@ -1821,17 +1695,16 @@ 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();
}
@ -1904,19 +1777,16 @@ 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();
}
if (!render_window->InitRenderTarget()) {
if (!render_window->InitRenderTarget())
return false;
}
QtCommon::system->SetFilesystem(QtCommon::vfs);
if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) {
if (params.launch_type == Service::AM::LaunchType::FrontendInitiated)
QtCommon::system->GetUserChannel().clear();
}
QtCommon::system->SetFrontendAppletSet({
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
@ -2023,36 +1893,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;
}
if (QtCommon::provider->AddEntriesFromContainer(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);
}
}
void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
StartGameType type) {
LOG_INFO(Frontend, "Eden starting...");
@ -2071,7 +1911,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);
@ -2116,23 +1956,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
@ -2227,9 +2067,10 @@ bool MainWindow::OnShutdownBegin() {
discord_rpc->Pause();
RequestGameExit();
emu_thread->disconnect();
emu_thread->SetRunning(true);
QtCommon::emu_thread->disconnect();
QtCommon::emu_thread->SetRunning(true);
connect(QtCommon::emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped);
emit EmulationStopping();
int shutdown_time = 1000;
@ -2243,7 +2084,6 @@ 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);
// Disable everything to prevent anything from being triggered here
ui->action_Pause->setEnabled(false);
@ -2261,17 +2101,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) {
@ -2912,7 +2752,7 @@ void MainWindow::OnMenuLoadFile() {
is_load_file_select_active = true;
const QString extensions =
QStringLiteral("*.")
.append(GameList::supported_file_extensions.join(QStringLiteral(" *.")))
.append(QtCommon::supported_file_extensions.join(QStringLiteral(" *.")))
.append(QStringLiteral(" main"));
const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
"%1 is an identifier for the Switch executable file extensions.")
@ -3148,7 +2988,7 @@ void MainWindow::OnMenuRecentFile() {
void MainWindow::OnStartGame() {
PreventOSSleep();
emu_thread->SetRunning(true);
QtCommon::emu_thread->SetRunning(true);
UpdateMenuState();
OnTasStateChanged();
@ -3174,7 +3014,7 @@ void MainWindow::OnRestartGame() {
}
void MainWindow::OnPauseGame() {
emu_thread->SetRunning(false);
QtCommon::emu_thread->SetRunning(false);
play_time_manager->Stop();
UpdateMenuState();
AllowOSSleep();
@ -3183,7 +3023,7 @@ void MainWindow::OnPauseGame() {
void MainWindow::OnPauseContinueGame() {
if (emulation_running) {
if (emu_thread->IsRunning()) {
if (QtCommon::emu_thread->IsRunning()) {
OnPauseGame();
} else {
OnStartGame();
@ -3863,12 +3703,9 @@ 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() ||
is_amiibo_file_select_active)
return;
}
if (is_amiibo_file_select_active) {
return;
}
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
@ -3972,79 +3809,21 @@ void MainWindow::OnVerifyInstalledContents() {
QtCommon::Content::VerifyInstalledContents();
}
void MainWindow::InstallFirmware(const QString& location, bool recursive) {
QtCommon::Content::InstallFirmware(location, recursive);
void MainWindow::OnInstallFirmware() {
QtCommon::Content::InstallFirmware();
OnCheckFirmwareDecryption();
}
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()) {
return;
}
// Check for installed keys, error out, suggest restart?
if (!ContentManager::AreKeysPresent()) {
QMessageBox::information(
this, tr("Keys not installed"),
tr("Install decryption keys and restart Eden before attempting to install firmware."));
return;
}
const QString firmware_source_location = QFileDialog::getExistingDirectory(
this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly);
if (firmware_source_location.isEmpty()) {
return;
}
InstallFirmware(firmware_source_location);
}
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()) {
return;
}
// Check for installed keys, error out, suggest restart?
if (!ContentManager::AreKeysPresent()) {
QMessageBox::information(
this, tr("Keys not installed"),
tr("Install decryption keys and restart Eden before attempting to install firmware."));
return;
}
const QString firmware_zip_location = QFileDialog::getOpenFileName(
this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)"));
if (firmware_zip_location.isEmpty()) {
return;
}
const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location);
// In this case, it has to be done recursively, since sometimes people
// will pack it into a subdirectory after dumping
if (!qCacheDir.isEmpty()) {
InstallFirmware(qCacheDir, true);
std::error_code ec;
std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware",
ec);
if (ec) {
QMessageBox::warning(this, tr("Firmware cleanup failed"),
tr("Failed to clean up extracted firmware cache.\n"
"Check write permissions in the system temp directory and try "
"again.\nOS reported error: %1")
.arg(QString::fromStdString(ec.message())));
}
}
QtCommon::Content::InstallFirmwareZip();
OnCheckFirmwareDecryption();
}
// TODO(crueter): QtCommon this: game list populate can be a signal?
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;
}
QtCommon::Content::InstallKeys();
@ -4072,11 +3851,10 @@ void MainWindow::OnDataDialog() {
void MainWindow::OnToggleFilterBar() {
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
if (ui->action_Show_Filter_Bar->isChecked()) {
if (ui->action_Show_Filter_Bar->isChecked())
game_list->SetFilterFocus();
} else {
else
game_list->ClearFilter();
}
}
void MainWindow::OnToggleStatusBar() {
@ -4084,9 +3862,8 @@ void MainWindow::OnToggleStatusBar() {
}
void MainWindow::OnTogglePerfOverlay() {
if (perf_overlay) {
if (perf_overlay)
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
}
}
void MainWindow::OnGameListRefresh() {
@ -4191,9 +3968,8 @@ void MainWindow::OnCreateHomeMenuApplicationMenuShortcut() {
}
void MainWindow::OnCaptureScreenshot() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning())
return;
}
const u64 title_id = QtCommon::system->GetApplicationProcessProgramID();
const auto screenshot_path =
@ -4205,9 +3981,8 @@ void MainWindow::OnCaptureScreenshot() {
.arg(title_id, 16, 16, QLatin1Char{'0'})
.arg(date);
if (!Common::FS::CreateDir(screenshot_path.toStdString())) {
if (!Common::FS::CreateDir(screenshot_path.toStdString()))
return;
}
#ifdef _WIN32
if (UISettings::values.enable_screenshot_save_as) {
@ -4313,16 +4088,15 @@ 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;
}
if (Settings::values.tas_enable) {
if (Settings::values.tas_enable)
tas_label->setText(GetTasStateDescription());
} else {
else
tas_label->clear();
}
auto results = QtCommon::system->GetAndResetPerfStats();
auto& shader_notify = QtCommon::system->GPU().ShaderNotify();
@ -4444,14 +4218,13 @@ void MainWindow::UpdateUISettings() {
}
void MainWindow::UpdateInputDrivers() {
if (!input_subsystem) {
if (!input_subsystem)
return;
}
input_subsystem->PumpEvents();
}
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;
@ -4461,9 +4234,8 @@ 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();
}
}
void MainWindow::OnMouseActivity() {
@ -4643,14 +4415,14 @@ bool MainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed
}
bool MainWindow::ConfirmClose() {
if (emu_thread == nullptr ||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
if (QtCommon::emu_thread == nullptr ||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never)
return true;
}
if (!QtCommon::system->GetExitLocked() &&
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) {
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game)
return true;
}
const auto text = tr("Are you sure you want to close Eden?");
return question(this, tr("Eden"), text);
}
@ -4671,9 +4443,8 @@ 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();
}
render_window->close();
multiplayer_state->Close();
@ -4736,7 +4507,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
@ -4747,9 +4518,9 @@ 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"
"Would you like to bypass this and exit anyway?");
@ -4757,9 +4528,8 @@ bool MainWindow::ConfirmForceLockedExit() {
}
void MainWindow::RequestGameExit() {
if (!QtCommon::system->IsPoweredOn()) {
if (!QtCommon::system->IsPoweredOn())
return;
}
QtCommon::system->SetExitRequested(true);
QtCommon::system->GetAppletManager().RequestExit();
@ -4772,14 +4542,14 @@ void MainWindow::filterBarSetChecked(bool state) {
static void AdjustLinkColor() {
QPalette new_pal(qApp->palette());
if (UISettings::IsDarkTheme()) {
if (UISettings::IsDarkTheme())
new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
} else {
else
new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
}
if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) {
if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link))
qApp->setPalette(new_pal);
}
}
void MainWindow::UpdateUITheme() {
@ -4787,9 +4557,8 @@ void MainWindow::UpdateUITheme() {
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
QString current_theme = QString::fromStdString(UISettings::values.theme);
if (current_theme.isEmpty()) {
if (current_theme.isEmpty())
current_theme = default_theme;
}
#ifdef _WIN32
QIcon::setThemeName(current_theme);
@ -4850,17 +4619,15 @@ void MainWindow::LoadTranslation() {
QStringLiteral(":/languages/"));
}
if (loaded) {
if (loaded)
qApp->installTranslator(&translator);
} else {
else
UISettings::values.language = std::string("en");
}
}
void MainWindow::OnLanguageChanged(const QString& locale) {
if (UISettings::values.language.GetValue() != std::string("en")) {
if (UISettings::values.language.GetValue() != std::string("en"))
qApp->removeTranslator(&translator);
}
QList<QAction*> actions = game_size_actions->actions();
for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
@ -4876,11 +4643,10 @@ void MainWindow::OnLanguageChanged(const QString& locale) {
void MainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#ifdef USE_DISCORD_PRESENCE
if (state) {
if (state)
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(*QtCommon::system);
} else {
else
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
}
#else
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
#endif

View file

@ -20,9 +20,9 @@
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "frontend_common/content_manager.h"
#include "frontend_common/update_checker.h"
#include "input_common/drivers/tas_input.h"
#include "qt_common/config/qt_config.h"
#include "qt_common/qt_common.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h"
@ -38,6 +38,7 @@
#ifdef ENABLE_UPDATE_CHECKER
#include <QFuture>
#include <QFutureWatcher>
#include "common/net/net.h"
#endif
class QtConfig;
@ -66,11 +67,6 @@ class QtProfileSelectionDialog;
class QtSoftwareKeyboardDialog;
class QtNXWebEngineView;
enum class StartGameType {
Normal, // Can use custom configuration
Global, // Only uses global configuration
};
namespace VideoCore {
class ShaderNotify;
}
@ -184,11 +180,8 @@ signals:
* Signal that is emitted when a new EmuThread has been created and an emulation session is
* about to start. At this time, the core system emulation has been initialized, and all
* emulation handles and memory should be valid.
*
* @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
@ -465,7 +458,6 @@ private:
bool CheckFirmwarePresence();
void SetFirmwareVersion();
void SetFPSSuffix();
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
@ -538,7 +530,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)
@ -599,8 +590,6 @@ private:
const std::string& game_title, QtCommon::Game::ShortcutTarget target,
std::string arguments, const bool needs_title);
void InstallFirmware(const QString& location, bool recursive = false);
protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;

View file

@ -15,7 +15,7 @@ class UpdateDialog : public QDialog {
Q_OBJECT
public:
explicit UpdateDialog(const Common::Net::Release &release, QWidget* parent = nullptr);
explicit UpdateDialog(const Common::Net::Release& release, QWidget* parent = nullptr);
~UpdateDialog();
private slots:
@ -23,6 +23,6 @@ private slots:
private:
Ui::UpdateDialog* ui;
QList<QRadioButton *> m_buttons;
QList<QRadioButton*> m_buttons;
Common::Net::Asset m_asset;
};

View file

@ -43,111 +43,6 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
return circle_pixmap;
}
bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
#if defined(WIN32)
#pragma pack(push, 2)
struct IconDir {
WORD id_reserved;
WORD id_type;
WORD id_count;
};
struct IconDirEntry {
BYTE width;
BYTE height;
BYTE color_count;
BYTE reserved;
WORD planes;
WORD bit_count;
DWORD bytes_in_res;
DWORD image_offset;
};
#pragma pack(pop)
const QImage source_image = image.convertToFormat(QImage::Format_RGB32);
constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16};
constexpr int bytes_per_pixel = 4;
const IconDir icon_dir{
.id_reserved = 0,
.id_type = 1,
.id_count = static_cast<WORD>(scale_sizes.size()),
};
Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
if (!icon_file.IsOpen()) {
return false;
}
if (!icon_file.Write(icon_dir)) {
return false;
}
std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size());
for (std::size_t i = 0; i < scale_sizes.size(); i++) {
const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel;
const IconDirEntry icon_entry{
.width = static_cast<BYTE>(scale_sizes[i]),
.height = static_cast<BYTE>(scale_sizes[i]),
.color_count = 0,
.reserved = 0,
.planes = 1,
.bit_count = bytes_per_pixel * 8,
.bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
.image_offset = static_cast<DWORD>(image_offset),
};
image_offset += icon_entry.bytes_in_res;
if (!icon_file.Write(icon_entry)) {
return false;
}
}
for (std::size_t i = 0; i < scale_sizes.size(); i++) {
const QImage scaled_image = source_image.scaled(
scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
const BITMAPINFOHEADER info_header{
.biSize = sizeof(BITMAPINFOHEADER),
.biWidth = scaled_image.width(),
.biHeight = scaled_image.height() * 2,
.biPlanes = 1,
.biBitCount = bytes_per_pixel * 8,
.biCompression = BI_RGB,
.biSizeImage{},
.biXPelsPerMeter{},
.biYPelsPerMeter{},
.biClrUsed{},
.biClrImportant{},
};
if (!icon_file.Write(info_header)) {
return false;
}
for (int y = 0; y < scaled_image.height(); y++) {
const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y);
std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel);
std::memcpy(line_data.data(), line, line_data.size());
if (!icon_file.Write(line_data)) {
return false;
}
}
}
icon_file.Close();
return true;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
// Convert and write the icon as a PNG
if (!image.save(QString::fromStdString(icon_path.string()))) {
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
} else {
LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
}
return true;
#else
return false;
#endif
}
const std::optional<Common::UUID> GetProfileID() {
// if there's only a single profile, the user probably wants to use that... right?
const auto& profiles = QtCommon::system->GetProfileManager().FindExistingProfileUUIDs();

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
@ -6,7 +6,6 @@
#pragma once
#include <filesystem>
#include <QFont>
#include <QString>
#include "common/uuid.h"
@ -24,14 +23,6 @@
*/
[[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color);
/**
* Saves a windows icon to a file
* @param path The icons path
* @param image The image to save
* @return bool If the operation succeeded
*/
[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
/**
* Prompt the user for a profile ID. If there is only one valid profile, returns that profile.
* @return The selected profile, or an std::nullopt if none were selected

View file

@ -1,36 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "common/common_types.h"
#include "vulkan/vulkan_core.h"
class QWindow;
namespace Settings {
enum class VSyncMode : u32;
}
// #include "common/settings.h"
namespace VkDeviceInfo {
// Short class to record Vulkan driver information for configuration purposes
class Record {
public:
explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
bool has_broken_compute);
~Record();
const std::string name;
const std::vector<VkPresentModeKHR> vsync_support;
const bool has_broken_compute;
};
void PopulateRecords(std::vector<Record>& records, QWindow* window);
} // namespace VkDeviceInfo