Browse Source

frontend: Fix recursion during canvas removal causing crashes

Dennis Sädtler 1 month ago
parent
commit
606e1932b8

+ 1 - 0
frontend/widgets/OBSBasic.hpp

@@ -1132,6 +1132,7 @@ private:
 	std::vector<OBS::Canvas> canvases;
 
 	static void CanvasRemoved(void *data, calldata_t *params);
+	void ClearCanvases();
 
 public:
 	const std::vector<OBS::Canvas> &GetCanvases() const noexcept { return canvases; }

+ 19 - 4
frontend/widgets/OBSBasic_Canvases.cpp

@@ -33,15 +33,30 @@ const OBS::Canvas &OBSBasic::AddCanvas(const std::string &name, obs_video_info *
 
 bool OBSBasic::RemoveCanvas(OBSCanvas canvas)
 {
+	bool removed = false;
 	if (!canvas)
-		return false;
+		return removed;
 
 	auto canvas_it = std::find(std::begin(canvases), std::end(canvases), canvas);
 	if (canvas_it != std::end(canvases)) {
+		// Move canvas to a temporary object to delay removal of the canvas and calls to its signal handlers
+		// until after erase() completes. This is to avoid issues with recursion coming from the
+		// CanvasRemoved() signal handler.
+		OBS::Canvas tmp = std::move(*canvas_it);
 		canvases.erase(canvas_it);
-		OnEvent(OBS_FRONTEND_EVENT_CANVAS_REMOVED);
-		return true;
+		removed = true;
 	}
 
-	return false;
+	if (removed)
+		OnEvent(OBS_FRONTEND_EVENT_CANVAS_REMOVED);
+
+	return removed;
+}
+
+void OBSBasic::ClearCanvases()
+{
+	// Delete canvases one-by-one to ensure OBS_FRONTEND_EVENT_CANVAS_REMOVED is sent for each
+	while (!canvases.empty()) {
+		RemoveCanvas(OBSCanvas(canvases.back()));
+	}
 }

+ 1 - 1
frontend/widgets/OBSBasic_SceneCollections.cpp

@@ -1539,7 +1539,7 @@ void OBSBasic::ClearSceneData()
 		obs_canvas_enum_scenes(canvas, cb, nullptr);
 	}
 
-	canvases.clear();
+	ClearCanvases();
 
 	OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP);