浏览代码

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 年之前
父节点
当前提交
29a1a97392
共有 2 个文件被更改,包括 37 次插入0 次删除
  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 "obs-app.hpp"
 
 
 #include <graphics/graphics.h>
 #include <graphics/graphics.h>
+#include <util/threading.h>
 #include <QWidget>
 #include <QWidget>
 #include <QLayout>
 #include <QLayout>
 #include <QMessageBox>
 #include <QMessageBox>
@@ -224,6 +225,8 @@ QThread *CreateQThread(std::function<void()> func)
 	return new QuickThread(func);
 	return new QuickThread(func);
 }
 }
 
 
+volatile long insideEventLoop = 0;
+
 void ExecuteFuncSafeBlock(std::function<void()> func)
 void ExecuteFuncSafeBlock(std::function<void()> func)
 {
 {
 	QEventLoop eventLoop;
 	QEventLoop eventLoop;
@@ -235,10 +238,12 @@ void ExecuteFuncSafeBlock(std::function<void()> func)
 				Qt::QueuedConnection);
 				Qt::QueuedConnection);
 	};
 	};
 
 
+	os_atomic_inc_long(&insideEventLoop);
 	QScopedPointer<QThread> thread(CreateQThread(wait));
 	QScopedPointer<QThread> thread(CreateQThread(wait));
 	thread->start();
 	thread->start();
 	eventLoop.exec();
 	eventLoop.exec();
 	thread->wait();
 	thread->wait();
+	os_atomic_dec_long(&insideEventLoop);
 }
 }
 
 
 void ExecuteFuncSafeBlockMsgBox(
 void ExecuteFuncSafeBlockMsgBox(
@@ -258,10 +263,12 @@ void ExecuteFuncSafeBlockMsgBox(
 		QMetaObject::invokeMethod(&dlg, "accept", Qt::QueuedConnection);
 		QMetaObject::invokeMethod(&dlg, "accept", Qt::QueuedConnection);
 	};
 	};
 
 
+	os_atomic_inc_long(&insideEventLoop);
 	QScopedPointer<QThread> thread(CreateQThread(wait));
 	QScopedPointer<QThread> thread(CreateQThread(wait));
 	thread->start();
 	thread->start();
 	dlg.exec();
 	dlg.exec();
 	thread->wait();
 	thread->wait();
+	os_atomic_dec_long(&insideEventLoop);
 }
 }
 
 
 static bool enable_message_boxes = false;
 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(OBSScene);
 Q_DECLARE_METATYPE(OBSSceneItem);
 Q_DECLARE_METATYPE(OBSSceneItem);
 Q_DECLARE_METATYPE(OBSSource);
 Q_DECLARE_METATYPE(OBSSource);
@@ -3761,6 +3763,15 @@ void OBSBasic::ClearSceneData()
 
 
 void OBSBasic::closeEvent(QCloseEvent *event)
 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())
 	if (isVisible())
 		config_set_string(App()->GlobalConfig(),
 		config_set_string(App()->GlobalConfig(),
 				"BasicWindow", "geometry",
 				"BasicWindow", "geometry",
@@ -3860,9 +3871,28 @@ void OBSBasic::on_actionRemux_triggered()
 
 
 void OBSBasic::on_action_Settings_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);
 	OBSBasicSettings settings(this);
 	settings.exec();
 	settings.exec();
 	SystemTray(false);
 	SystemTray(false);
+
+	settings_already_executing = false;
 }
 }
 
 
 void OBSBasic::on_actionAdvAudioProperties_triggered()
 void OBSBasic::on_actionAdvAudioProperties_triggered()