Browse Source

frontend: Add support loading/saving additional canvases

Dennis Sädtler 10 months ago
parent
commit
368185019e

+ 1 - 0
frontend/cmake/ui-widgets.cmake

@@ -15,6 +15,7 @@ target_sources(
     widgets/OBSBasic.cpp
     widgets/OBSBasic.hpp
     widgets/OBSBasic_Browser.cpp
+    widgets/OBSBasic_Canvases.cpp
     widgets/OBSBasic_Clipboard.cpp
     widgets/OBSBasic_ContextToolbar.cpp
     widgets/OBSBasic_Docks.cpp

+ 2 - 1
frontend/widgets/OBSBasic.cpp

@@ -842,7 +842,7 @@ void OBSBasic::InitOBSCallbacks()
 {
 	ProfileScope("OBSBasic::InitOBSCallbacks");
 
-	signalHandlers.reserve(signalHandlers.size() + 9);
+	signalHandlers.reserve(signalHandlers.size() + 10);
 	signalHandlers.emplace_back(obs_get_signal_handler(), "source_create", OBSBasic::SourceCreated, this);
 	signalHandlers.emplace_back(obs_get_signal_handler(), "source_remove", OBSBasic::SourceRemoved, this);
 	signalHandlers.emplace_back(obs_get_signal_handler(), "source_activate", OBSBasic::SourceActivated, this);
@@ -866,6 +866,7 @@ void OBSBasic::InitOBSCallbacks()
 						  Qt::QueuedConnection);
 		},
 		this);
+	signalHandlers.emplace_back(obs_get_signal_handler(), "canvas_remove", OBSBasic::CanvasRemoved, this);
 }
 
 #define STARTUP_SEPARATOR "==== Startup complete ==============================================="

+ 18 - 0
frontend/widgets/OBSBasic.hpp

@@ -23,6 +23,7 @@
 #include <OBSApp.hpp>
 #include <oauth/Auth.hpp>
 #include <utility/BasicOutputHandler.hpp>
+#include <utility/OBSCanvas.hpp>
 #include <utility/VCamConfig.hpp>
 #include <utility/platform.hpp>
 #include <utility/undo_stack.hpp>
@@ -1108,6 +1109,23 @@ public:
 	std::optional<SceneCollection> GetSceneCollectionByName(const std::string &collectionName) const;
 	std::optional<SceneCollection> GetSceneCollectionByFileName(const std::string &fileName) const;
 
+	/* -------------------------------------
+	 * MARK: - OBSBasic_Canvases
+	 * -------------------------------------
+	 */
+private:
+	std::vector<OBS::Canvas> canvases;
+
+	static void CanvasRemoved(void *data, calldata_t *params);
+
+public:
+	const std::vector<OBS::Canvas> &GetCanvases() const noexcept { return canvases; }
+
+	const OBS::Canvas &AddCanvas(const std::string &name, obs_video_info *ovi = nullptr, int flags = 0);
+
+public slots:
+	bool RemoveCanvas(obs_canvas_t *canvas);
+
 	/* -------------------------------------
 	 * MARK: - OBSBasic_SceneItems
 	 * -------------------------------------

+ 47 - 0
frontend/widgets/OBSBasic_Canvases.cpp

@@ -0,0 +1,47 @@
+/******************************************************************************
+    Copyright (C) 2025 by Dennis Sädtler <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "OBSBasic.hpp"
+
+void OBSBasic::CanvasRemoved(void *data, calldata_t *params)
+{
+	obs_canvas_t *canvas = static_cast<obs_canvas_t *>(calldata_ptr(params, "canvas"));
+	QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "RemoveCanvas", Q_ARG(OBSCanvas, OBSCanvas(canvas)));
+}
+
+const OBS::Canvas &OBSBasic::AddCanvas(const std::string &name, obs_video_info *ovi, int flags)
+{
+	OBSCanvas canvas = obs_canvas_create(name.c_str(), ovi, flags);
+	auto &it = canvases.emplace_back(canvas);
+	OnEvent(OBS_FRONTEND_EVENT_CANVAS_ADDED);
+	return it;
+}
+
+bool OBSBasic::RemoveCanvas(obs_canvas_t *canvas)
+{
+	if (!canvas)
+		return false;
+
+	auto canvas_it = std::find(std::begin(canvases), std::end(canvases), canvas);
+	if (canvas_it != std::end(canvases)) {
+		canvases.erase(canvas_it);
+		OnEvent(OBS_FRONTEND_EVENT_CANVAS_REMOVED);
+		return true;
+	}
+
+	return false;
+}

+ 16 - 2
frontend/widgets/OBSBasic_SceneCollections.cpp

@@ -808,7 +808,8 @@ static void SaveAudioDevice(const char *name, int channel, obs_data_t *parent, v
 
 static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, obs_data_array_t *quickTransitionData,
 				    int transitionDuration, obs_data_array_t *transitions, OBSScene &scene,
-				    OBSSource &curProgramScene, obs_data_array_t *savedProjectorList)
+				    OBSSource &curProgramScene, obs_data_array_t *savedProjectorList,
+				    obs_data_array_t *savedCanvases)
 {
 	obs_data_t *saveData = obs_data_create();
 
@@ -865,6 +866,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, obs_data_array
 	obs_data_set_array(saveData, "quick_transitions", quickTransitionData);
 	obs_data_set_array(saveData, "transitions", transitions);
 	obs_data_set_array(saveData, "saved_projectors", savedProjectorList);
+	obs_data_set_array(saveData, "canvases", savedCanvases);
 	obs_data_array_release(sourcesArray);
 	obs_data_array_release(groupsArray);
 
@@ -885,8 +887,10 @@ void OBSBasic::Save(SceneCollection &collection)
 	OBSDataArrayAutoRelease transitions = SaveTransitions();
 	OBSDataArrayAutoRelease quickTrData = SaveQuickTransitions();
 	OBSDataArrayAutoRelease savedProjectorList = SaveProjectors();
+	OBSDataArrayAutoRelease savedCanvases = OBS::Canvas::SaveCanvases(canvases);
 	OBSDataAutoRelease saveData = GenerateSaveData(sceneOrder, quickTrData, ui->transitionDuration->value(),
-						       transitions, scene, curProgramScene, savedProjectorList);
+						       transitions, scene, curProgramScene, savedProjectorList,
+						       savedCanvases);
 
 	obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked());
 	obs_data_set_bool(saveData, "scaling_enabled", ui->preview->IsFixedScaling());
@@ -1171,6 +1175,7 @@ void OBSBasic::LoadData(obs_data_t *data, SceneCollection &collection)
 	OBSDataArrayAutoRelease sources = obs_data_get_array(data, "sources");
 	OBSDataArrayAutoRelease groups = obs_data_get_array(data, "groups");
 	OBSDataArrayAutoRelease transitions = obs_data_get_array(data, "transitions");
+	OBSDataArrayAutoRelease collection_canvases = obs_data_get_array(data, "canvases");
 	const char *sceneName = obs_data_get_string(data, "current_scene");
 	const char *programSceneName = obs_data_get_string(data, "current_program_scene");
 	const char *transitionName = obs_data_get_string(data, "current_transition");
@@ -1207,6 +1212,9 @@ void OBSBasic::LoadData(obs_data_t *data, SceneCollection &collection)
 	LoadAudioDevice(AUX_AUDIO_3, 5, data);
 	LoadAudioDevice(AUX_AUDIO_4, 6, data);
 
+	if (collection_canvases)
+		canvases = OBS::Canvas::LoadCanvases(collection_canvases);
+
 	if (!sources) {
 		sources = std::move(groups);
 	} else {
@@ -1525,6 +1533,12 @@ void OBSBasic::ClearSceneData()
 	obs_enum_scenes(cb, nullptr);
 	obs_enum_sources(cb, nullptr);
 
+	for (const auto &canvas : canvases) {
+		obs_canvas_enum_scenes(canvas, cb, nullptr);
+	}
+
+	canvases.clear();
+
 	OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP);
 
 	undo_s.clear();