Prechádzať zdrojové kódy

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.
jp9000 6 rokov pred
rodič
commit
29a1a97392
2 zmenil súbory, kde vykonal 37 pridanie a 0 odobranie
  1. 7 0
      UI/qt-wrappers.cpp
  2. 30 0
      UI/window-basic-main.cpp

+ 7 - 0
UI/qt-wrappers.cpp

@@ -19,6 +19,7 @@
 #include "obs-app.hpp"
 
 #include <graphics/graphics.h>
+#include <util/threading.h>
 #include <QWidget>
 #include <QLayout>
 #include <QMessageBox>
@@ -224,6 +225,8 @@ QThread *CreateQThread(std::function<void()> func)
 	return new QuickThread(func);
 }
 
+volatile long insideEventLoop = 0;
+
 void ExecuteFuncSafeBlock(std::function<void()> func)
 {
 	QEventLoop eventLoop;
@@ -235,10 +238,12 @@ void ExecuteFuncSafeBlock(std::function<void()> func)
 				Qt::QueuedConnection);
 	};
 
+	os_atomic_inc_long(&insideEventLoop);
 	QScopedPointer<QThread> 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<QThread> thread(CreateQThread(wait));
 	thread->start();
 	dlg.exec();
 	thread->wait();
+	os_atomic_dec_long(&insideEventLoop);
 }
 
 static bool enable_message_boxes = false;

+ 30 - 0
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()