From 29a1a973923efa9c591bab234bcdbc47cda7a0bb Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 26 Mar 2019 22:01:22 -0700 Subject: [PATCH] UI: Don't open settings or close in event subloop Fixes a crash that can happen if you try to use the settings window while in an even subloop, or if you try to close OBS while in an event subloop. Continually retries (defers) the actions every one second until the subloop has finished. --- UI/qt-wrappers.cpp | 7 +++++++ UI/window-basic-main.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/UI/qt-wrappers.cpp b/UI/qt-wrappers.cpp index 0ce9ba0bc..18bd34c88 100644 --- a/UI/qt-wrappers.cpp +++ b/UI/qt-wrappers.cpp @@ -19,6 +19,7 @@ #include "obs-app.hpp" #include +#include #include #include #include @@ -224,6 +225,8 @@ QThread *CreateQThread(std::function func) return new QuickThread(func); } +volatile long insideEventLoop = 0; + void ExecuteFuncSafeBlock(std::function func) { QEventLoop eventLoop; @@ -235,10 +238,12 @@ void ExecuteFuncSafeBlock(std::function func) Qt::QueuedConnection); }; + os_atomic_inc_long(&insideEventLoop); QScopedPointer thread(CreateQThread(wait)); thread->start(); eventLoop.exec(); thread->wait(); + os_atomic_dec_long(&insideEventLoop); } void ExecuteFuncSafeBlockMsgBox( @@ -258,10 +263,12 @@ void ExecuteFuncSafeBlockMsgBox( QMetaObject::invokeMethod(&dlg, "accept", Qt::QueuedConnection); }; + os_atomic_inc_long(&insideEventLoop); QScopedPointer thread(CreateQThread(wait)); thread->start(); dlg.exec(); thread->wait(); + os_atomic_dec_long(&insideEventLoop); } static bool enable_message_boxes = false; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index b72065c0d..15f025b71 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -98,6 +98,8 @@ struct SignalContainer { } +extern volatile long insideEventLoop; + Q_DECLARE_METATYPE(OBSScene); Q_DECLARE_METATYPE(OBSSceneItem); Q_DECLARE_METATYPE(OBSSource); @@ -3761,6 +3763,15 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { + /* Do not close window if inside of a temporary event loop because we + * could be inside of an Auth::LoadUI call. Keep trying once per + * second until we've exit any known sub-loops. */ + if (os_atomic_load_long(&insideEventLoop) != 0) { + QTimer::singleShot(1000, this, SLOT(close())); + event->ignore(); + return; + } + if (isVisible()) config_set_string(App()->GlobalConfig(), "BasicWindow", "geometry", @@ -3860,9 +3871,28 @@ void OBSBasic::on_actionRemux_triggered() void OBSBasic::on_action_Settings_triggered() { + static bool settings_already_executing = false; + + /* Do not load settings window if inside of a temporary event loop + * because we could be inside of an Auth::LoadUI call. Keep trying + * once per second until we've exit any known sub-loops. */ + if (os_atomic_load_long(&insideEventLoop) != 0) { + QTimer::singleShot(1000, this, + SLOT(on_action_Settings_triggered())); + return; + } + + if (settings_already_executing) { + return; + } + + settings_already_executing = true; + OBSBasicSettings settings(this); settings.exec(); SystemTray(false); + + settings_already_executing = false; } void OBSBasic::on_actionAdvAudioProperties_triggered()