summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--CMakeModules/CopyYuzuQt5Deps.cmake1
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/core.cpp13
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/hle/kernel/k_process.cpp54
-rw-r--r--src/core/hle/kernel/k_process.h4
-rw-r--r--src/core/hle/kernel/k_shared_memory_info.h46
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc.cpp14
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/memory.cpp6
-rw-r--r--src/core/network/network.cpp2
-rw-r--r--src/input_common/sdl/sdl_impl.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp11
-rw-r--r--src/yuzu/bootmanager.cpp5
-rw-r--r--src/yuzu/bootmanager.h4
-rw-r--r--src/yuzu/main.cpp16
-rw-r--r--src/yuzu/main.h1
19 files changed, 202 insertions, 24 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd0f6b978..123a3082a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -237,7 +237,7 @@ yuzu_find_packages()
# Qt5 requires that we find components, so it doesn't fit our pretty little find package function
if(ENABLE_QT)
- set(QT_VERSION 5.12)
+ set(QT_VERSION 5.15)
# We want to load the generated conan qt config so that we get the QT_ROOT var so that we can use the official
# Qt5Config inside the root folder instead of the conan generated one.
if(EXISTS ${CMAKE_BINARY_DIR}/qtConfig.cmake)
@@ -339,8 +339,8 @@ if(ENABLE_QT)
set(QT_PREFIX_HINT)
if(YUZU_USE_BUNDLED_QT)
- if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
- set(QT_BUILD qt-5.12.8-msvc2017_64)
+ if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
+ set(QT_BUILD qt-5.15.2-msvc2019_64)
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
set(QT_BUILD qt5_5_15_2)
else()
@@ -369,7 +369,7 @@ endif()
if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
- if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
+ if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.16")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
@@ -462,7 +462,7 @@ if (CONAN_REQUIRED_LIBS)
if(ENABLE_QT)
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
- find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets)
+ find_package(Qt5 5.15 REQUIRED COMPONENTS Widgets)
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
endif()
diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake
index 4a6aeebbb..dd97f5b2b 100644
--- a/CMakeModules/CopyYuzuQt5Deps.cmake
+++ b/CMakeModules/CopyYuzuQt5Deps.cmake
@@ -33,6 +33,7 @@ function(copy_yuzu_Qt5_deps target_dir)
Qt5Positioning$<$<CONFIG:Debug>:d>.*
Qt5PrintSupport$<$<CONFIG:Debug>:d>.*
Qt5Qml$<$<CONFIG:Debug>:d>.*
+ Qt5QmlModels$<$<CONFIG:Debug>:d>.*
Qt5Quick$<$<CONFIG:Debug>:d>.*
Qt5QuickWidgets$<$<CONFIG:Debug>:d>.*
Qt5WebChannel$<$<CONFIG:Debug>:d>.*
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index aa3b26628..9f0fbba2d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -216,6 +216,7 @@ add_library(core STATIC
hle/kernel/k_session.h
hle/kernel/k_shared_memory.cpp
hle/kernel/k_shared_memory.h
+ hle/kernel/k_shared_memory_info.h
hle/kernel/k_slab_heap.h
hle/kernel/k_spin_lock.cpp
hle/kernel/k_spin_lock.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 50d5dab4b..bb268a319 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -421,6 +421,7 @@ struct System::Impl {
bool is_async_gpu{};
ExecuteProgramCallback execute_program_callback;
+ ExitCallback exit_callback;
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
@@ -798,6 +799,18 @@ void System::ExecuteProgram(std::size_t program_index) {
}
}
+void System::RegisterExitCallback(ExitCallback&& callback) {
+ impl->exit_callback = std::move(callback);
+}
+
+void System::Exit() {
+ if (impl->exit_callback) {
+ impl->exit_callback();
+ } else {
+ LOG_CRITICAL(Core, "exit_callback must be initialized by the frontend");
+ }
+}
+
void System::ApplySettings() {
if (IsPoweredOn()) {
Renderer().RefreshBaseSettings();
diff --git a/src/core/core.h b/src/core/core.h
index 715ab88e7..a796472b2 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -387,6 +387,18 @@ public:
*/
void ExecuteProgram(std::size_t program_index);
+ /// Type used for the frontend to designate a callback for System to exit the application.
+ using ExitCallback = std::function<void()>;
+
+ /**
+ * Registers a callback from the frontend for System to exit the application.
+ * @param callback Callback from the frontend to exit the application.
+ */
+ void RegisterExitCallback(ExitCallback&& callback);
+
+ /// Instructs the frontend to exit the application.
+ void Exit();
+
/// Applies any changes to settings to this core instance.
void ApplySettings();
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 8ead1a769..211157ccc 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -23,6 +23,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -254,10 +255,26 @@ ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAdd
// Lock ourselves, to prevent concurrent access.
KScopedLightLock lk(state_lock);
- // TODO(bunnei): Manage KSharedMemoryInfo list here.
+ // Try to find an existing info for the memory.
+ KSharedMemoryInfo* shemen_info = nullptr;
+ const auto iter = std::find_if(
+ shared_memory_list.begin(), shared_memory_list.end(),
+ [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; });
+ if (iter != shared_memory_list.end()) {
+ shemen_info = *iter;
+ }
+
+ if (shemen_info == nullptr) {
+ shemen_info = KSharedMemoryInfo::Allocate(kernel);
+ R_UNLESS(shemen_info != nullptr, ResultOutOfMemory);
+
+ shemen_info->Initialize(shmem);
+ shared_memory_list.push_back(shemen_info);
+ }
- // Open a reference to the shared memory.
+ // Open a reference to the shared memory and its info.
shmem->Open();
+ shemen_info->Open();
return ResultSuccess;
}
@@ -267,7 +284,20 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr a
// Lock ourselves, to prevent concurrent access.
KScopedLightLock lk(state_lock);
- // TODO(bunnei): Manage KSharedMemoryInfo list here.
+ KSharedMemoryInfo* shemen_info = nullptr;
+ const auto iter = std::find_if(
+ shared_memory_list.begin(), shared_memory_list.end(),
+ [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; });
+ if (iter != shared_memory_list.end()) {
+ shemen_info = *iter;
+ }
+
+ ASSERT(shemen_info != nullptr);
+
+ if (shemen_info->Close()) {
+ shared_memory_list.erase(iter);
+ KSharedMemoryInfo::Free(kernel, shemen_info);
+ }
// Close a reference to the shared memory.
shmem->Close();
@@ -412,6 +442,24 @@ void KProcess::Finalize() {
// Finalize the handle table and close any open handles.
handle_table.Finalize();
+ // Free all shared memory infos.
+ {
+ auto it = shared_memory_list.begin();
+ while (it != shared_memory_list.end()) {
+ KSharedMemoryInfo* info = *it;
+ KSharedMemory* shmem = info->GetSharedMemory();
+
+ while (!info->Close()) {
+ shmem->Close();
+ }
+
+ shmem->Close();
+
+ it = shared_memory_list.erase(it);
+ KSharedMemoryInfo::Free(kernel, info);
+ }
+ }
+
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
}
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index a03c074fb..1a53e2be7 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -34,6 +34,7 @@ class KernelCore;
class KPageTable;
class KResourceLimit;
class KThread;
+class KSharedMemoryInfo;
class TLSPage;
struct CodeSet;
@@ -448,6 +449,9 @@ private:
/// List of threads that are running with this process as their owner.
std::list<const KThread*> thread_list;
+ /// List of shared memory that are running with this process as their owner.
+ std::list<KSharedMemoryInfo*> shared_memory_list;
+
/// Address of the top of the main thread's stack
VAddr main_thread_stack_top{};
diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h
new file mode 100644
index 000000000..bf97a0184
--- /dev/null
+++ b/src/core/hle/kernel/k_shared_memory_info.h
@@ -0,0 +1,46 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <boost/intrusive/list.hpp>
+
+#include "common/assert.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+class KSharedMemory;
+
+class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
+ public boost::intrusive::list_base_hook<> {
+
+public:
+ explicit KSharedMemoryInfo() = default;
+
+ constexpr void Initialize(KSharedMemory* shmem) {
+ shared_memory = shmem;
+ }
+
+ constexpr KSharedMemory* GetSharedMemory() const {
+ return shared_memory;
+ }
+
+ constexpr void Open() {
+ ++reference_count;
+ }
+
+ constexpr bool Close() {
+ return (--reference_count) == 0;
+ }
+
+private:
+ KSharedMemory* shared_memory{};
+ size_t reference_count{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 901d43da9..b6658b437 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -49,6 +49,7 @@ class KScheduler;
class KServerSession;
class KSession;
class KSharedMemory;
+class KSharedMemoryInfo;
class KThread;
class KTransferMemory;
class KWritableEvent;
@@ -309,6 +310,8 @@ public:
return slab_heap_container->session;
} else if constexpr (std::is_same_v<T, KSharedMemory>) {
return slab_heap_container->shared_memory;
+ } else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) {
+ return slab_heap_container->shared_memory_info;
} else if constexpr (std::is_same_v<T, KThread>) {
return slab_heap_container->thread;
} else if constexpr (std::is_same_v<T, KTransferMemory>) {
@@ -362,6 +365,7 @@ private:
KSlabHeap<KResourceLimit> resource_limit;
KSlabHeap<KSession> session;
KSlabHeap<KSharedMemory> shared_memory;
+ KSlabHeap<KSharedMemoryInfo> shared_memory_info;
KSlabHeap<KThread> thread;
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 62fb06c45..f98f24a60 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -320,17 +320,19 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
auto& kernel = system.Kernel();
- KScopedAutoObject session =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
- R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
- LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
-
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
thread->SetState(ThreadState::Waiting);
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
- session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
+
+ {
+ KScopedAutoObject session =
+ kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+ LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
+ session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
+ }
}
KSynchronizationObject* dummy{};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 02de585e4..eccdcc20d 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -331,10 +331,10 @@ ISelfController::~ISelfController() {
void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
- system.Shutdown();
-
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
+
+ system.Exit();
}
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 51c4dea26..88d6ec908 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -587,7 +587,11 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
const Kernel::KProcess& process = *system.CurrentProcess();
const auto& page_table = process.PageTable().PageTableImpl();
- const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
+ const size_t page = vaddr >> PAGE_BITS;
+ if (page >= page_table.pointers.size()) {
+ return false;
+ }
+ const auto [pointer, type] = page_table.pointers[page].PointerType();
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
}
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 72eea52f0..a3e0664b9 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -366,8 +366,6 @@ std::optional<IPv4Address> GetHostIPv4Address() {
if (res != network_interfaces.end()) {
char ip_addr[16] = {};
ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
- LOG_INFO(Network, "IP address: {}", ip_addr);
-
return TranslateIPv4(res->ip_address);
} else {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 03888b7cb..ab6211b29 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -254,11 +254,25 @@ public:
}
bool IsJoyconLeft() const {
- return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr;
+ const std::string controller_name = GetControllerName();
+ if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) {
+ return true;
+ }
+ if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) {
+ return true;
+ }
+ return false;
}
bool IsJoyconRight() const {
- return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr;
+ const std::string controller_name = GetControllerName();
+ if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) {
+ return true;
+ }
+ if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) {
+ return true;
+ }
+ return false;
}
std::string GetControllerName() const {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 11cd41ad7..8634c3316 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -568,12 +568,21 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (!vertex_binding_divisors.empty()) {
vertex_input_ci.pNext = &input_divisor_ci;
}
+ const bool has_tess_stages = spv_modules[1] || spv_modules[2];
auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology);
if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
- if (!spv_modules[1] && !spv_modules[2]) {
+ if (!has_tess_stages) {
LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points");
input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
}
+ } else {
+ if (has_tess_stages) {
+ // The Vulkan spec requires patch list IA topology be used with tessellation
+ // shader stages. Forcing it fixes a crash on some drivers
+ LOG_WARNING(Render_Vulkan,
+ "Patch topology not used with tessellation, using patch list");
+ input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
+ }
}
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1519a46ed..8b9e186b0 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -302,12 +302,17 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
Qt::QueuedConnection);
+ connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
}
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
emit ExecuteProgramSignal(program_index);
}
+void GRenderWindow::Exit() {
+ emit ExitSignal();
+}
+
GRenderWindow::~GRenderWindow() {
input_subsystem->Shutdown();
}
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 402dd2ee1..54c4e2142 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -181,6 +181,9 @@ public:
*/
void ExecuteProgram(std::size_t program_index);
+ /// Instructs the window to exit the application.
+ void Exit();
+
public slots:
void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping();
@@ -191,6 +194,7 @@ signals:
void Closed();
void FirstFrameDisplayed();
void ExecuteProgramSignal(std::size_t program_index);
+ void ExitSignal();
void MouseActivity();
private:
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 3c2824362..552c2cc63 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1384,6 +1384,9 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
system.RegisterExecuteProgramCallback(
[this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
+ // Register an Exit callback such that Core can exit the currently running application.
+ system.RegisterExitCallback([this]() { render_window->Exit(); });
+
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
@@ -2469,6 +2472,10 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
BootGame(last_filename_booted, 0, program_index);
}
+void GMainWindow::OnExit() {
+ OnStopGame();
+}
+
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text,
QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
@@ -2913,8 +2920,13 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
if (title_name.empty()) {
setWindowTitle(QString::fromStdString(window_title));
} else {
- const auto run_title =
- fmt::format("{} | {} | {} | {}", window_title, title_name, title_version, gpu_vendor);
+ const auto run_title = [window_title, title_name, title_version, gpu_vendor]() {
+ if (title_version.empty()) {
+ return fmt::format("{} | {} | {}", window_title, title_name, gpu_vendor);
+ }
+ return fmt::format("{} | {} | {} | {}", window_title, title_name, title_version,
+ gpu_vendor);
+ }();
setWindowTitle(QString::fromStdString(run_title));
}
}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 36eed6103..60ce01471 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -153,6 +153,7 @@ signals:
public slots:
void OnLoadComplete();
void OnExecuteProgram(std::size_t program_index);
+ void OnExit();
void ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters);
void SoftwareKeyboardInitialize(