From a7f61cbd31895135bd161d67c954441256458dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20S=C3=A4dtler?= Date: Fri, 24 Oct 2025 01:04:54 +0200 Subject: [PATCH] frontend: Fix recursion during canvas removal causing crashes --- frontend/widgets/OBSBasic.hpp | 1 + frontend/widgets/OBSBasic_Canvases.cpp | 23 +++++++++++++++---- .../widgets/OBSBasic_SceneCollections.cpp | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/frontend/widgets/OBSBasic.hpp b/frontend/widgets/OBSBasic.hpp index ca213433e0beb8..89521463431acf 100644 --- a/frontend/widgets/OBSBasic.hpp +++ b/frontend/widgets/OBSBasic.hpp @@ -1123,6 +1123,7 @@ private slots: std::vector canvases; static void CanvasRemoved(void *data, calldata_t *params); + void ClearCanvases(); public: const std::vector &GetCanvases() const noexcept { return canvases; } diff --git a/frontend/widgets/OBSBasic_Canvases.cpp b/frontend/widgets/OBSBasic_Canvases.cpp index 9b1f346efa305a..4d8d8be54edbe7 100644 --- a/frontend/widgets/OBSBasic_Canvases.cpp +++ b/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())); + } } diff --git a/frontend/widgets/OBSBasic_SceneCollections.cpp b/frontend/widgets/OBSBasic_SceneCollections.cpp index ee9e4e6086f735..1e28830e4e5072 100644 --- a/frontend/widgets/OBSBasic_SceneCollections.cpp +++ b/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);