diff --git a/CHANGES b/CHANGES index 70e03d4eb..e92a6c495 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Bugfixes: Misc: - Qt: Disable sync to video by default - GBA: Exit cleanly on FATAL if the port supports it + - Qt: Handle a game crash without crashing 0.1.0: (2014-12-13) - Initial release diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 05d07a9d4..2c3ec3074 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -73,7 +73,12 @@ GameController::GameController(QObject* parent) m_threadContext.logHandler = [] (GBAThread* context, enum GBALogLevel level, const char* format, va_list args) { GameController* controller = static_cast(context->userData); - if (!(controller->m_logLevels & level)) { + if (level == GBA_LOG_FATAL) { + MutexLock(&controller->m_threadContext.stateMutex); + controller->m_threadContext.state = THREAD_EXITING; + MutexUnlock(&controller->m_threadContext.stateMutex); + QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args))); + } else if (!(controller->m_logLevels & level)) { return; } controller->postLog(level, QString().vsprintf(format, args)); @@ -207,6 +212,11 @@ void GameController::closeGame() { emit gameStopped(&m_threadContext); } +void GameController::crashGame(const QString& crashMessage) { + closeGame(); + emit gameCrashed(crashMessage); +} + bool GameController::isPaused() { if (!m_gameOpen) { return false; diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 3bc991aa7..b3b89ca19 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -64,6 +64,7 @@ signals: void gameStopped(GBAThread*); void gamePaused(GBAThread*); void gameUnpaused(GBAThread*); + void gameCrashed(const QString& errorMessage); void stateLoaded(GBAThread*); void postLog(int level, const QString& log); @@ -94,8 +95,10 @@ public slots: void enableLogLevel(int); void disableLogLevel(int); -#ifdef BUILD_SDL private slots: + void crashGame(const QString& crashMessage); + +#ifdef BUILD_SDL void testSDLEvents(); private: diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 1e6eae8c0..9d2d3e453 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "ConfigController.h" @@ -74,6 +75,7 @@ Window::Window(ConfigController* config, QWidget* parent) connect(m_controller, SIGNAL(gameUnpaused(GBAThread*)), m_display, SLOT(unpauseDrawing())); connect(m_controller, SIGNAL(postLog(int, const QString&)), m_logView, SLOT(postLog(int, const QString&))); connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(recordFrame())); + connect(m_controller, SIGNAL(gameCrashed(const QString&)), this, SLOT(gameCrashed(const QString&))); connect(m_logView, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int))); connect(m_logView, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int))); connect(m_logView, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int))); @@ -325,6 +327,14 @@ void Window::gameStopped() { m_fpsTimer.stop(); } +void Window::gameCrashed(const QString& errorMessage) { + QMessageBox* crash = new QMessageBox(QMessageBox::Critical, tr("Crash"), + tr("The game has crashed with the following error:\n\n%1").arg(errorMessage), + QMessageBox::Ok, this, Qt::Sheet); + crash->setAttribute(Qt::WA_DeleteOnClose); + crash->show(); +} + void Window::redoLogo() { if (m_controller->isLoaded()) { return; diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 1e99d41d9..d7d588b36 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -87,6 +87,7 @@ protected: private slots: void gameStarted(GBAThread*); void gameStopped(); + void gameCrashed(const QString&); void redoLogo(); void recordFrame();