diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index fa92372f6a..07846711e6 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -152,7 +152,7 @@ void Host_UpdateProgressDialog(const char* caption, int position, int total) { } -static bool MsgAlert(const char* caption, const char* text, bool yes_no, int /*Style*/) +static bool MsgAlert(const char* caption, const char* text, bool yes_no, MsgType /*style*/) { __android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "%s:%s", caption, text); diff --git a/Source/Core/Common/MsgHandler.cpp b/Source/Core/Common/MsgHandler.cpp index 77c5547996..670023b491 100644 --- a/Source/Core/Common/MsgHandler.cpp +++ b/Source/Core/Common/MsgHandler.cpp @@ -16,7 +16,7 @@ #include #endif -bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style); +bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, MsgType style); static MsgAlertHandler msg_handler = DefaultMsgHandler; static bool AlertEnabled = true; @@ -42,14 +42,14 @@ void SetEnableAlert(bool enable) AlertEnabled = enable; } -std::string GetTranslation(const char* string) +std::string GetStringT(const char* string) { return str_translator(string); } // This is the first stop for gui alerts where the log is updated and the // correct window is shown -bool MsgAlert(bool yes_no, int Style, const char* format, ...) +bool MsgAlert(bool yes_no, MsgType style, const char* format, ...) { // Read message and write it to the log std::string caption; @@ -68,18 +68,18 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...) crit_caption = str_translator(_trans("Critical")); } - switch (Style) + switch (style) { - case INFORMATION: + case MsgType::Information: caption = info_caption; break; - case QUESTION: + case MsgType::Question: caption = ques_caption; break; - case WARNING: + case MsgType::Warning: caption = warn_caption; break; - case CRITICAL: + case MsgType::Critical: caption = crit_caption; break; } @@ -92,24 +92,24 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...) ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored - if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) - return msg_handler(caption.c_str(), buffer, yes_no, Style); + if (msg_handler && (AlertEnabled || style == MsgType::Question || style == MsgType::Critical)) + return msg_handler(caption.c_str(), buffer, yes_no, style); return true; } // Default non library dependent panic alert -bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style) +bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, MsgType style) { #ifdef _WIN32 - int STYLE = MB_ICONINFORMATION; - if (Style == QUESTION) - STYLE = MB_ICONQUESTION; - if (Style == WARNING) - STYLE = MB_ICONWARNING; + int window_style = MB_ICONINFORMATION; + if (style == MsgType::Question) + window_style = MB_ICONQUESTION; + if (style == MsgType::Warning) + window_style = MB_ICONWARNING; return IDYES == MessageBox(0, UTF8ToTStr(text).c_str(), UTF8ToTStr(caption).c_str(), - STYLE | (yes_no ? MB_YESNO : MB_OK)); + window_style | (yes_no ? MB_YESNO : MB_OK)); #else fprintf(stderr, "%s\n", text); diff --git a/Source/Core/Common/MsgHandler.h b/Source/Core/Common/MsgHandler.h index b6fc9c32b3..dcb79f4196 100644 --- a/Source/Core/Common/MsgHandler.h +++ b/Source/Core/Common/MsgHandler.h @@ -7,22 +7,22 @@ #include // Message alerts -enum MSG_TYPE +enum class MsgType { - INFORMATION, - QUESTION, - WARNING, - CRITICAL + Information, + Question, + Warning, + Critical }; -typedef bool (*MsgAlertHandler)(const char* caption, const char* text, bool yes_no, int Style); +typedef bool (*MsgAlertHandler)(const char* caption, const char* text, bool yes_no, MsgType style); typedef std::string (*StringTranslator)(const char* text); void RegisterMsgAlertHandler(MsgAlertHandler handler); void RegisterStringTranslator(StringTranslator translator); -std::string GetTranslation(const char* string); -bool MsgAlert(bool yes_no, int Style, const char* format, ...) +std::string GetStringT(const char* string); +bool MsgAlert(bool yes_no, MsgType style, const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif @@ -30,31 +30,28 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...) void SetEnableAlert(bool enable); #ifdef _WIN32 -#define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) -#define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) -#define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) -#define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) -#define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) +#define SuccessAlert(format, ...) MsgAlert(false, MsgType::Information, format, __VA_ARGS__) +#define PanicAlert(format, ...) MsgAlert(false, MsgType::Warning, format, __VA_ARGS__) +#define PanicYesNo(format, ...) MsgAlert(true, MsgType::Warning, format, __VA_ARGS__) +#define AskYesNo(format, ...) MsgAlert(true, MsgType::Question, format, __VA_ARGS__) +#define CriticalAlert(format, ...) MsgAlert(false, MsgType::Critical, format, __VA_ARGS__) // Use these macros (that do the same thing) if the message should be translated. -#define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) -#define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) -#define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) -#define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) -#define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) +#define SuccessAlertT(format, ...) MsgAlert(false, MsgType::Information, format, __VA_ARGS__) +#define PanicAlertT(format, ...) MsgAlert(false, MsgType::Warning, format, __VA_ARGS__) +#define PanicYesNoT(format, ...) MsgAlert(true, MsgType::Warning, format, __VA_ARGS__) +#define AskYesNoT(format, ...) MsgAlert(true, MsgType::Question, format, __VA_ARGS__) +#define CriticalAlertT(format, ...) MsgAlert(false, MsgType::Critical, format, __VA_ARGS__) -#define GetStringT(string) GetTranslation(string) #else -#define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) -#define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) -#define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) -#define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) -#define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) +#define SuccessAlert(format, ...) MsgAlert(false, MsgType::Information, format, ##__VA_ARGS__) +#define PanicAlert(format, ...) MsgAlert(false, MsgType::Warning, format, ##__VA_ARGS__) +#define PanicYesNo(format, ...) MsgAlert(true, MsgType::Warning, format, ##__VA_ARGS__) +#define AskYesNo(format, ...) MsgAlert(true, MsgType::Question, format, ##__VA_ARGS__) +#define CriticalAlert(format, ...) MsgAlert(false, MsgType::Critical, format, ##__VA_ARGS__) // Use these macros (that do the same thing) if the message should be translated. -#define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) -#define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) -#define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) -#define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) -#define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) - -#define GetStringT(string) GetTranslation(string) +#define SuccessAlertT(format, ...) MsgAlert(false, MsgType::Information, format, ##__VA_ARGS__) +#define PanicAlertT(format, ...) MsgAlert(false, MsgType::Warning, format, ##__VA_ARGS__) +#define PanicYesNoT(format, ...) MsgAlert(true, MsgType::Warning, format, ##__VA_ARGS__) +#define AskYesNoT(format, ...) MsgAlert(true, MsgType::Question, format, ##__VA_ARGS__) +#define CriticalAlertT(format, ...) MsgAlert(false, MsgType::Critical, format, ##__VA_ARGS__) #endif diff --git a/Source/Core/DolphinQt2/Main.cpp b/Source/Core/DolphinQt2/Main.cpp index 5609e70d5b..8d9cd87179 100644 --- a/Source/Core/DolphinQt2/Main.cpp +++ b/Source/Core/DolphinQt2/Main.cpp @@ -8,17 +8,46 @@ #include #include +#include "Common/MsgHandler.h" #include "Core/Analytics.h" #include "Core/BootManager.h" #include "Core/Core.h" #include "DolphinQt2/Host.h" #include "DolphinQt2/InDevelopmentWarning.h" #include "DolphinQt2/MainWindow.h" +#include "DolphinQt2/QtUtils/RunOnObject.h" #include "DolphinQt2/Resources.h" #include "DolphinQt2/Settings.h" #include "UICommon/CommandLineParse.h" #include "UICommon/UICommon.h" +bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no, MsgType style) +{ + return RunOnObject(QApplication::instance(), [&] { + QMessageBox message_box(QApplication::activeWindow()); + message_box.setWindowTitle(QString::fromUtf8(caption)); + message_box.setText(QString::fromUtf8(text)); + message_box.setStandardButtons(yes_no ? QMessageBox::Yes | QMessageBox::No : QMessageBox::Ok); + message_box.setIcon([&] { + switch (style) + { + case MsgType::Information: + return QMessageBox::Information; + case MsgType::Question: + return QMessageBox::Question; + case MsgType::Warning: + return QMessageBox::Warning; + case MsgType::Critical: + return QMessageBox::Critical; + } + // appease MSVC + return QMessageBox::NoIcon; + }()); + + return message_box.exec() == QMessageBox::Yes; + }); +} + // N.B. On Windows, this should be called from WinMain. Link against qtmain and specify // /SubSystem:Windows int main(int argc, char* argv[]) @@ -40,6 +69,9 @@ int main(int argc, char* argv[]) UICommon::Init(); Resources::Init(); + // Hook up alerts from core + RegisterMsgAlertHandler(QtMsgAlertHandler); + // Whenever the event loop is about to go to sleep, dispatch the jobs // queued in the Core first. QObject::connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock, diff --git a/Source/Core/DolphinQt2/QtUtils/RunOnObject.h b/Source/Core/DolphinQt2/QtUtils/RunOnObject.h new file mode 100644 index 0000000000..9fbda0bbc1 --- /dev/null +++ b/Source/Core/DolphinQt2/QtUtils/RunOnObject.h @@ -0,0 +1,34 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/Event.h" +#include "DolphinQt2/QtUtils/QueueOnObject.h" + +class QObject; + +// QWidget and subclasses are not thread-safe! This helper takes arbitrary code from any thread, +// safely runs it on the appropriate GUI thread, waits for it to finish, and returns the result. + +template +auto RunOnObject(QObject* object, F&& functor) +{ + // If we queue up a functor on the current thread, it won't run until we return to the event loop, + // which means waiting for it to finish will never complete. Instead, run it immediately. + if (object->thread() == QThread::currentThread()) + return functor(); + + Common::Event event; + std::result_of_t result; + QueueOnObject(object, [&event, &result, functor = std::forward(functor) ] { + result = functor(); + event.Set(); + }); + event.Wait(); + return result; +} diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp index 1a5fbad188..a462dcfeb1 100644 --- a/Source/Core/DolphinWX/Main.cpp +++ b/Source/Core/DolphinWX/Main.cpp @@ -64,7 +64,7 @@ IMPLEMENT_APP(DolphinApp) -bool wxMsgAlert(const char*, const char*, bool, int); +bool wxMsgAlert(const char*, const char*, bool, MsgType); std::string wxStringTranslator(const char*); CFrame* main_frame = nullptr; @@ -351,7 +351,7 @@ void DolphinApp::OnIdle(wxIdleEvent& ev) // ------------ // Talk to GUI -bool wxMsgAlert(const char* caption, const char* text, bool yes_no, int /*Style*/) +bool wxMsgAlert(const char* caption, const char* text, bool yes_no, MsgType /*style*/) { if (wxIsMainThread()) {