From 9ac9fc0a1e54100cb1e6d0bde2da4046314d455b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 25 Feb 2024 18:19:54 +1000 Subject: [PATCH] Host: Add ReportFatalError() --- src/duckstation-nogui/nogui_host.cpp | 8 ++++++- src/duckstation-qt/qthost.cpp | 28 ++++++++++++++++++++++++ src/duckstation-regtest/regtest_host.cpp | 7 ++++++ src/util/host.h | 4 ++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp index 714845ca3..0ad90195b 100644 --- a/src/duckstation-nogui/nogui_host.cpp +++ b/src/duckstation-nogui/nogui_host.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "nogui_host.h" @@ -327,6 +327,12 @@ void NoGUIHost::SetDefaultSettings(SettingsInterface& si, bool system, bool cont g_nogui_window->SetDefaultConfig(si); } +void Host::ReportFatalError(const std::string_view& title, const std::string_view& message) +{ + Log_ErrorPrintf("ReportFatalError: %.*s", static_cast(message.size()), message.data()); + abort(); +} + void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) { if (!title.empty() && !message.empty()) diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index a427450fc..e15fa6ba5 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1553,6 +1553,34 @@ void EmuThread::wakeThread() QMetaObject::invokeMethod(m_event_loop, "quit", Qt::QueuedConnection); } +void Host::ReportFatalError(const std::string_view& title, const std::string_view& message) +{ + auto cb = [title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message)]() { + QMessageBox::critical(g_main_window && g_main_window->isVisible() ? g_main_window : nullptr, title, message); +#ifndef __APPLE__ + std::quick_exit(EXIT_FAILURE); +#else + _exit(EXIT_FAILURE); +#endif + }; + + // https://stackoverflow.com/questions/34135624/how-to-properly-execute-gui-operations-in-qt-main-thread + QTimer* timer = new QTimer(); + QThread* ui_thread = qApp->thread(); + if (QThread::currentThread() == ui_thread) + { + // On UI thread, we can do it straight away. + cb(); + } + else + { + timer->moveToThread(ui_thread); + timer->setSingleShot(true); + QObject::connect(timer, &QTimer::timeout, std::move(cb)); + QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0)); + } +} + void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) { if (!title.empty() && !message.empty()) diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index a20deec20..26398c0bb 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -17,6 +17,7 @@ #include "common/assert.h" #include "common/crash_handler.h" +#include "common/error.h" #include "common/file_system.h" #include "common/log.h" #include "common/memory_settings_interface.h" @@ -114,6 +115,12 @@ bool RegTestHost::InitializeConfig() return true; } +void Host::ReportFatalError(const std::string_view& title, const std::string_view& message) +{ + Log_ErrorPrintf("ReportFatalError: %.*s", static_cast(message.size()), message.data()); + abort(); +} + void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) { if (!title.empty() && !message.empty()) diff --git a/src/util/host.h b/src/util/host.h index 5ae509604..680f53f04 100644 --- a/src/util/host.h +++ b/src/util/host.h @@ -25,6 +25,10 @@ std::optional ReadResourceFileToString(std::string_view filename, b /// Returns the modified time of a resource. std::optional GetResourceFileTimestamp(std::string_view filename, bool allow_override); +/// Reports a fatal error on the main thread. This does not assume that the main window exists, +/// unlike ReportErrorAsync(), and will exit the application after the popup is closed. +void ReportFatalError(const std::string_view& title, const std::string_view& message); + /// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller. void ReportErrorAsync(const std::string_view& title, const std::string_view& message); void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...);