summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
m---------externals/Vulkan-Headers0
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp12
-rw-r--r--src/yuzu/bootmanager.cpp81
-rw-r--r--src/yuzu/bootmanager.h51
-rw-r--r--src/yuzu/main.cpp152
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/util/overlay_dialog.cpp9
-rw-r--r--src/yuzu/util/overlay_dialog.h1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt9
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
13 files changed, 166 insertions, 188 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 47eddf99e..f71a8b3e3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -208,7 +208,7 @@ find_package(libusb 1.0.24)
find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3)
-find_package(Vulkan 1.3.213)
+find_package(Vulkan 1.3.238)
find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED)
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
-Subproject 2826791bed6a793f164bf534cd859968f13df8a
+Subproject 00671c64ba5c488ade22ad572a0ef81d5e64c80
diff --git a/src/core/core.cpp b/src/core/core.cpp
index a738f221f..47292cd78 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -183,26 +183,20 @@ struct System::Impl {
Initialize(system);
}
- SystemResultStatus Run() {
+ void Run() {
std::unique_lock<std::mutex> lk(suspend_guard);
- status = SystemResultStatus::Success;
kernel.Suspend(false);
core_timing.SyncPause(false);
is_paused.store(false, std::memory_order_relaxed);
-
- return status;
}
- SystemResultStatus Pause() {
+ void Pause() {
std::unique_lock<std::mutex> lk(suspend_guard);
- status = SystemResultStatus::Success;
core_timing.SyncPause(true);
kernel.Suspend(true);
is_paused.store(true, std::memory_order_relaxed);
-
- return status;
}
bool IsPaused() const {
@@ -553,12 +547,12 @@ void System::Initialize() {
impl->Initialize(*this);
}
-SystemResultStatus System::Run() {
- return impl->Run();
+void System::Run() {
+ impl->Run();
}
-SystemResultStatus System::Pause() {
- return impl->Pause();
+void System::Pause() {
+ impl->Pause();
}
bool System::IsPaused() const {
diff --git a/src/core/core.h b/src/core/core.h
index 4ebedffd9..fb5cda2f5 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -152,13 +152,13 @@ public:
* Run the OS and Application
* This function will start emulation and run the relevant devices
*/
- [[nodiscard]] SystemResultStatus Run();
+ void Run();
/**
* Pause the OS and Application
* This function will pause emulation and stop the relevant devices
*/
- [[nodiscard]] SystemResultStatus Pause();
+ void Pause();
/// Check if the core is currently paused.
[[nodiscard]] bool IsPaused() const;
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 483b534a0..7dca7341c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept {
return "VK_ERROR_VALIDATION_FAILED_EXT";
case VkResult::VK_ERROR_INVALID_SHADER_NV:
return "VK_ERROR_INVALID_SHADER_NV";
+ case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
+ case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR:
+ return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VkResult::VK_ERROR_FRAGMENTATION_EXT:
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1368b20d5..3d560f303 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -46,30 +46,28 @@
static Core::Frontend::WindowSystemType GetWindowSystemType();
-EmuThread::EmuThread(Core::System& system_) : system{system_} {}
+EmuThread::EmuThread(Core::System& system) : m_system{system} {}
EmuThread::~EmuThread() = default;
void EmuThread::run() {
- std::string name = "EmuControlThread";
- MicroProfileOnThreadCreate(name.c_str());
- Common::SetCurrentThreadName(name.c_str());
+ const char* name = "EmuControlThread";
+ MicroProfileOnThreadCreate(name);
+ Common::SetCurrentThreadName(name);
- auto& gpu = system.GPU();
- auto stop_token = stop_source.get_token();
- bool debugger_should_start = system.DebuggerEnabled();
+ auto& gpu = m_system.GPU();
+ auto stop_token = m_stop_source.get_token();
- system.RegisterHostThread();
+ 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()) {
- system.Renderer().ReadRasterizer()->LoadDiskResources(
- system.GetCurrentProcessProgramID(), stop_token,
+ m_system.Renderer().ReadRasterizer()->LoadDiskResources(
+ m_system.GetCurrentProcessProgramID(), stop_token,
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
@@ -79,57 +77,34 @@ void EmuThread::run() {
gpu.ReleaseContext();
gpu.Start();
- system.GetCpuManager().OnGpuReady();
+ m_system.GetCpuManager().OnGpuReady();
- system.RegisterExitCallback([this]() {
- stop_source.request_stop();
- SetRunning(false);
- });
+ if (m_system.DebuggerEnabled()) {
+ m_system.InitializeDebugger();
+ }
- // Holds whether the cpu was running during the last iteration,
- // so that the DebugModeLeft signal can be emitted before the
- // next execution step
- bool was_active = false;
while (!stop_token.stop_requested()) {
- if (running) {
- if (was_active) {
- emit DebugModeLeft();
- }
+ std::unique_lock lk{m_should_run_mutex};
+ if (m_should_run) {
+ m_system.Run();
+ m_is_running.store(true);
+ m_is_running.notify_all();
- running_guard = true;
- Core::SystemResultStatus result = system.Run();
- if (result != Core::SystemResultStatus::Success) {
- running_guard = false;
- this->SetRunning(false);
- emit ErrorThrown(result, system.GetStatusDetails());
- }
-
- if (debugger_should_start) {
- system.InitializeDebugger();
- debugger_should_start = false;
- }
-
- running_wait.Wait();
- result = system.Pause();
- if (result != Core::SystemResultStatus::Success) {
- running_guard = false;
- this->SetRunning(false);
- emit ErrorThrown(result, system.GetStatusDetails());
- }
- running_guard = false;
-
- if (!stop_token.stop_requested()) {
- was_active = true;
- emit DebugModeEntered();
- }
+ Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
} else {
- std::unique_lock lock{running_mutex};
- Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); });
+ m_system.Pause();
+ m_is_running.store(false);
+ m_is_running.notify_all();
+
+ emit DebugModeEntered();
+ Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
+ emit DebugModeLeft();
}
}
// Shutdown the main emulated process
- system.ShutdownMainProcess();
+ m_system.DetachDebugger();
+ m_system.ShutdownMainProcess();
#if MICROPROFILE_ENABLED
MicroProfileOnThreadExit();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index b24141fd9..eca16b313 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -47,7 +47,7 @@ class EmuThread final : public QThread {
Q_OBJECT
public:
- explicit EmuThread(Core::System& system_);
+ explicit EmuThread(Core::System& system);
~EmuThread() override;
/**
@@ -57,30 +57,30 @@ public:
void run() override;
/**
- * Sets whether the emulation thread is running or not
- * @param running_ Boolean value, set the emulation thread to running if true
- * @note This function is thread-safe
+ * 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 running_) {
- std::unique_lock lock{running_mutex};
- running = running_;
- lock.unlock();
- running_cv.notify_all();
- if (!running) {
- running_wait.Set();
- /// Wait until effectively paused
- while (running_guard)
- ;
+ 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_is_running.wait(true);
}
}
/**
* Check if the emulation thread is running or not
* @return True if the emulation thread is running, otherwise false
- * @note This function is thread-safe
*/
bool IsRunning() const {
- return running;
+ return m_is_running.load() || m_should_run;
}
/**
@@ -88,18 +88,17 @@ public:
*/
void ForceStop() {
LOG_WARNING(Frontend, "Force stopping EmuThread");
- stop_source.request_stop();
- SetRunning(false);
+ m_stop_source.request_stop();
}
private:
- bool running = false;
- std::stop_source stop_source;
- std::mutex running_mutex;
- std::condition_variable_any running_cv;
- Common::Event running_wait{};
- std::atomic_bool running_guard{false};
- Core::System& system;
+ Core::System& m_system;
+
+ std::stop_source m_stop_source;
+ std::mutex m_should_run_mutex;
+ std::condition_variable_any m_should_run_cv;
+ std::atomic<bool> m_is_running{false};
+ bool m_should_run{true};
signals:
/**
@@ -120,8 +119,6 @@ signals:
*/
void DebugModeLeft();
- void ErrorThrown(Core::SystemResultStatus, std::string);
-
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fe140dce0..6121711e0 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1498,7 +1498,7 @@ void GMainWindow::SetupSigInterrupts() {
void GMainWindow::HandleSigInterrupt(int sig) {
if (sig == SIGINT) {
- exit(1);
+ _exit(1);
}
// Calling into Qt directly from a signal handler is not safe,
@@ -1550,8 +1550,9 @@ void GMainWindow::AllowOSSleep() {
bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
// Shutdown previous session if the emu thread is still active...
- if (emu_thread != nullptr)
+ if (emu_thread != nullptr) {
ShutdownGame();
+ }
if (!render_window->InitRenderTarget()) {
return false;
@@ -1710,6 +1711,11 @@ 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_); });
+ system->RegisterExitCallback([this] {
+ emu_thread->ForceStop();
+ 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
@@ -1779,7 +1785,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
OnStartGame();
}
-void GMainWindow::ShutdownGame() {
+void GMainWindow::OnShutdownBegin() {
if (!emulation_running) {
return;
}
@@ -1794,20 +1800,48 @@ void GMainWindow::ShutdownGame() {
Settings::values.use_speed_limit.SetValue(true);
system->SetShuttingDown(true);
- system->DetachDebugger();
discord_rpc->Pause();
RequestGameExit();
+ emu_thread->disconnect();
+ emu_thread->SetRunning(true);
emit EmulationStopping();
- // Wait for emulation thread to complete and delete it
- if (!emu_thread->wait(5000)) {
+ shutdown_timer.setSingleShot(true);
+ shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000);
+ connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
+ connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
+
+ // Disable everything to prevent anything from being triggered here
+ ui->action_Pause->setEnabled(false);
+ ui->action_Restart->setEnabled(false);
+ ui->action_Stop->setEnabled(false);
+}
+
+void GMainWindow::OnShutdownBeginDialog() {
+ shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."),
+ QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter);
+ shutdown_dialog->open();
+}
+
+void GMainWindow::OnEmulationStopTimeExpired() {
+ if (emu_thread) {
emu_thread->ForceStop();
- emu_thread->wait();
}
+}
+
+void GMainWindow::OnEmulationStopped() {
+ shutdown_timer.stop();
+ emu_thread->disconnect();
+ emu_thread->wait();
emu_thread = nullptr;
+ if (shutdown_dialog) {
+ shutdown_dialog->deleteLater();
+ shutdown_dialog = nullptr;
+ }
+
emulation_running = false;
discord_rpc->Update();
@@ -1853,6 +1887,20 @@ void GMainWindow::ShutdownGame() {
// When closing the game, destroy the GLWindow to clear the context after the game is closed
render_window->ReleaseRenderTarget();
+
+ Settings::RestoreGlobalState(system->IsPoweredOn());
+ system->HIDCore().ReloadInputDevices();
+ UpdateStatusButtons();
+}
+
+void GMainWindow::ShutdownGame() {
+ if (!emulation_running) {
+ return;
+ }
+
+ OnShutdownBegin();
+ OnEmulationStopTimeExpired();
+ OnEmulationStopped();
}
void GMainWindow::StoreRecentFile(const QString& filename) {
@@ -2919,8 +2967,6 @@ void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true);
- connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
-
UpdateMenuState();
OnTasStateChanged();
@@ -2957,11 +3003,8 @@ void GMainWindow::OnStopGame() {
return;
}
- ShutdownGame();
-
- Settings::RestoreGlobalState(system->IsPoweredOn());
- system->HIDCore().ReloadInputDevices();
- UpdateStatusButtons();
+ OnShutdownBegin();
+ OnShutdownBeginDialog();
}
void GMainWindow::OnLoadComplete() {
@@ -3904,79 +3947,6 @@ void GMainWindow::OnMouseActivity() {
mouse_center_timer.stop();
}
-void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
- QMessageBox::StandardButton answer;
- QString status_message;
- const QString common_message =
- tr("The game you are trying to load requires additional files from your Switch to be "
- "dumped "
- "before playing.<br/><br/>For more information on dumping these files, please see the "
- "following wiki page: <a "
- "href='https://yuzu-emu.org/wiki/"
- "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
- "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
- "quit "
- "back to the game list? Continuing emulation may result in crashes, corrupted save "
- "data, or other bugs.");
- switch (result) {
- case Core::SystemResultStatus::ErrorSystemFiles: {
- QString message;
- if (details.empty()) {
- message =
- tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message);
- } else {
- message = tr("yuzu was unable to locate a Switch system archive: %1. %2")
- .arg(QString::fromStdString(details), common_message);
- }
-
- answer = QMessageBox::question(this, tr("System Archive Not Found"), message,
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
- status_message = tr("System Archive Missing");
- break;
- }
-
- case Core::SystemResultStatus::ErrorSharedFont: {
- const QString message =
- tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message);
- answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
- status_message = tr("Shared Font Missing");
- break;
- }
-
- default:
- answer = QMessageBox::question(
- this, tr("Fatal Error"),
- tr("yuzu has encountered a fatal error, please see the log for more details. "
- "For more information on accessing the log, please see the following page: "
- "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
- "to "
- "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
- "list? "
- "Continuing emulation may result in crashes, corrupted save data, or other "
- "bugs."),
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
- status_message = tr("Fatal Error encountered");
- break;
- }
-
- if (answer == QMessageBox::Yes) {
- if (emu_thread) {
- ShutdownGame();
-
- Settings::RestoreGlobalState(system->IsPoweredOn());
- system->HIDCore().ReloadInputDevices();
- UpdateStatusButtons();
- }
- } else {
- // Only show the message if the game is still running.
- if (emu_thread) {
- emu_thread->SetRunning(true);
- message_label->setText(status_message);
- }
- }
-}
-
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (behavior == ReinitializeKeyBehavior::Warning) {
const auto res = QMessageBox::information(
@@ -4121,10 +4091,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
// Shutdown session if the emu thread is active...
if (emu_thread != nullptr) {
ShutdownGame();
-
- Settings::RestoreGlobalState(system->IsPoweredOn());
- system->HIDCore().ReloadInputDevices();
- UpdateStatusButtons();
}
render_window->close();
@@ -4217,6 +4183,10 @@ bool GMainWindow::ConfirmForceLockedExit() {
}
void GMainWindow::RequestGameExit() {
+ if (!system->IsPoweredOn()) {
+ return;
+ }
+
auto& sm{system->ServiceManager()};
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1047ba276..95220b063 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -29,6 +29,7 @@ class GImageInfo;
class GRenderWindow;
class LoadingScreen;
class MicroProfileDialog;
+class OverlayDialog;
class ProfilerWidget;
class ControllerDialog;
class QLabel;
@@ -332,10 +333,13 @@ private slots:
void ResetWindowSize900();
void ResetWindowSize1080();
void OnCaptureScreenshot();
- void OnCoreError(Core::SystemResultStatus, std::string);
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale);
void OnMouseActivity();
+ void OnShutdownBegin();
+ void OnShutdownBeginDialog();
+ void OnEmulationStopped();
+ void OnEmulationStopTimeExpired();
private:
QString GetGameListErrorRemoving(InstalledEntryType type) const;
@@ -385,6 +389,8 @@ private:
GRenderWindow* render_window;
GameList* game_list;
LoadingScreen* loading_screen;
+ QTimer shutdown_timer;
+ OverlayDialog* shutdown_dialog{};
GameListPlaceholder* game_list_placeholder;
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index 3fa3d0afb..796f5bf41 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -3,6 +3,7 @@
#include <QKeyEvent>
#include <QScreen>
+#include <QWindow>
#include "core/core.h"
#include "core/hid/hid_types.h"
@@ -162,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() {
const auto height = static_cast<float>(parentWidget()->height());
// High DPI
- const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
+ const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f;
const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
const auto body_text_font_size =
@@ -259,3 +260,9 @@ void OverlayDialog::InputThread() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
+
+void OverlayDialog::keyPressEvent(QKeyEvent* e) {
+ if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) {
+ QDialog::keyPressEvent(e);
+ }
+}
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index 39c44393c..872283d61 100644
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -94,6 +94,7 @@ private:
/// The thread where input is being polled and processed.
void InputThread();
+ void keyPressEvent(QKeyEvent* e) override;
std::unique_ptr<Ui::OverlayDialog> ui;
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index f6eeb9d8d..61b6cc4e0 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -49,6 +49,15 @@ if(UNIX AND NOT APPLE)
install(TARGETS yuzu-cmd)
endif()
+if(WIN32)
+ # compile as a win32 gui application instead of a console application
+ if(MSVC)
+ set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
+ elseif(MINGW)
+ set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
+ endif()
+endif()
+
if (MSVC)
include(CopyYuzuSDLDeps)
copy_yuzu_SDL_deps(yuzu-cmd)
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index a80649703..91133569d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -174,6 +174,13 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
/// Application entry point
int main(int argc, char** argv) {
+#ifdef _WIN32
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ freopen("CONOUT$", "wb", stdout);
+ freopen("CONOUT$", "wb", stderr);
+ }
+#endif
+
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();