Explorar o código

UI: Add migration for relative coordinate system

derrod hai 1 ano
pai
achega
8251005ad3

+ 10 - 0
UI/data/locale/en-US.ini

@@ -594,6 +594,14 @@ Basic.Main.AddSceneCollection.Text="Please enter the name of the scene collectio
 # rename scene collection dialog
 Basic.Main.RenameSceneCollection.Title="Rename Scene Collection"
 
+# remigrate scene collection dialog
+Basic.Main.RemigrateSceneCollection.Title="Update Scene Collection Resolution"
+Basic.Main.RemigrateSceneCollection.Text="Do you want to update the scene collection resolution of \"%1\" to match the current profile's canvas resolution of %2x%3?"
+Basic.Main.RemigrateSceneCollection.CannotMigrate.Active="Cannot update scene collection resolution while outputs are active."
+Basic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution="Failed to update scene collection resolution. The original resolution is unknown."
+Basic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset="Reset not possible: Changing OBS resolution failed."
+Basic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches="Reset not possible: Current resolution already the base resolution of scene collection."
+
 # add profile dialog
 AddProfile.Title="Add Profile"
 AddProfile.Text="Please enter the name of the profile"
@@ -835,6 +843,8 @@ Basic.MainMenu.Profile.Import="Import Profile"
 Basic.MainMenu.Profile.Export="Export Profile"
 Basic.MainMenu.SceneCollection.Import="Import Scene Collection"
 Basic.MainMenu.SceneCollection.Export="Export Scene Collection"
+Basic.MainMenu.SceneCollection.Remigrate="Reset Base Resolution"
+Basic.MainMenu.SceneCollection.Migrate="Set Base Resolution"
 Basic.MainMenu.Profile.Exists="The profile already exists"
 
 # basic mode help menu

+ 6 - 0
UI/forms/OBSBasic.ui

@@ -777,6 +777,7 @@
     <addaction name="actionExportSceneCollection"/>
     <addaction name="separator"/>
     <addaction name="actionShowMissingFiles"/>
+    <addaction name="actionRemigrateSceneCollection"/>
     <addaction name="separator"/>
    </widget>
    <widget class="QMenu" name="viewMenu">
@@ -1934,6 +1935,11 @@
     <string>Basic.MainMenu.File.ShowMissingFiles</string>
    </property>
   </action>
+  <action name="actionRemigrateSceneCollection">
+   <property name="text">
+    <string>Basic.MainMenu.SceneCollection.Remigrate</string>
+   </property>
+  </action>
   <action name="actionNewProfile">
    <property name="text">
     <string>New</string>

+ 91 - 0
UI/window-basic-main-scene-collections.cpp

@@ -434,6 +434,97 @@ void OBSBasic::on_actionExportSceneCollection_triggered()
 	}
 }
 
+void OBSBasic::on_actionRemigrateSceneCollection_triggered()
+{
+	if (Active()) {
+		OBSMessageBox::warning(
+			this,
+			QTStr("Basic.Main.RemigrateSceneCollection.Title"),
+			QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.Active"));
+		return;
+	}
+
+	OBSDataAutoRelease priv = obs_get_private_data();
+
+	if (!usingAbsoluteCoordinates && !migrationBaseResolution) {
+		OBSMessageBox::warning(
+			this,
+			QTStr("Basic.Main.RemigrateSceneCollection.Title"),
+			QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution"));
+		return;
+	}
+
+	obs_video_info ovi;
+	obs_get_video_info(&ovi);
+
+	if (!usingAbsoluteCoordinates &&
+	    migrationBaseResolution->first == ovi.base_width &&
+	    migrationBaseResolution->second == ovi.base_height) {
+		OBSMessageBox::warning(
+			this,
+			QTStr("Basic.Main.RemigrateSceneCollection.Title"),
+			QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches"));
+		return;
+	}
+
+	QString name = config_get_string(App()->GlobalConfig(), "Basic",
+					 "SceneCollection");
+	QString message = QTStr("Basic.Main.RemigrateSceneCollection.Text")
+				  .arg(name)
+				  .arg(ovi.base_width)
+				  .arg(ovi.base_height);
+
+	auto answer = OBSMessageBox::question(
+		this, QTStr("Basic.Main.RemigrateSceneCollection.Title"),
+		message);
+
+	if (answer == QMessageBox::No)
+		return;
+
+	lastOutputResolution = {ovi.base_width, ovi.base_height};
+
+	if (!usingAbsoluteCoordinates) {
+		/* Temporarily change resolution to migration resolution */
+		ovi.base_width = migrationBaseResolution->first;
+		ovi.base_height = migrationBaseResolution->second;
+
+		if (obs_reset_video(&ovi) != OBS_VIDEO_SUCCESS) {
+			OBSMessageBox::critical(
+				this,
+				QTStr("Basic.Main.RemigrateSceneCollection.Title"),
+				QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset"));
+			return;
+		}
+	}
+
+	char path[512];
+	int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/");
+	if (ret <= 0) {
+		blog(LOG_WARNING, "Failed to get scene collection config path");
+		return;
+	}
+
+	std::string fileName = path;
+	fileName += config_get_string(App()->GlobalConfig(), "Basic",
+				      "SceneCollectionFile");
+	fileName += ".json";
+
+	if (api)
+		api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING);
+
+	/* Save and immediately reload to (re-)run migrations. */
+	SaveProjectNow();
+	/* Reset video if we potentially changed to a temporary resolution */
+	if (!usingAbsoluteCoordinates)
+		ResetVideo();
+
+	Load(fileName.c_str(), !usingAbsoluteCoordinates);
+	RefreshSceneCollections();
+
+	if (api)
+		api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
+}
+
 void OBSBasic::ChangeSceneCollection()
 {
 	QAction *action = reinterpret_cast<QAction *>(sender());

+ 117 - 4
UI/window-basic-main.cpp

@@ -953,7 +953,16 @@ void OBSBasic::Save(const char *file)
 		obs_data_set_obj(saveData, "resolution", res);
 	}
 
-	if (!obs_data_save_json_safe(saveData, file, "tmp", "bak"))
+	obs_data_set_int(saveData, "version", usingAbsoluteCoordinates ? 1 : 2);
+
+	if (migrationBaseResolution && !usingAbsoluteCoordinates) {
+		OBSDataAutoRelease res = obs_data_create();
+		obs_data_set_int(res, "x", migrationBaseResolution->first);
+		obs_data_set_int(res, "y", migrationBaseResolution->second);
+		obs_data_set_obj(saveData, "migration_resolution", res);
+	}
+
+	if (!obs_data_save_json_pretty_safe(saveData, file, "tmp", "bak"))
 		blog(LOG_ERROR, "Could not save scene data to %s", file);
 }
 
@@ -1038,6 +1047,20 @@ void OBSBasic::CreateFirstRunSources()
 				 Str("Basic.AuxDevice1"), 3);
 }
 
+void OBSBasic::DisableRelativeCoordinates(bool enable)
+{
+	/* Allow disabling relative positioning to allow loading collections
+	 * that cannot yet be migrated. */
+	OBSDataAutoRelease priv = obs_get_private_data();
+	obs_data_set_bool(priv, "AbsoluteCoordinates", enable);
+	usingAbsoluteCoordinates = enable;
+
+	ui->actionRemigrateSceneCollection->setText(
+		enable ? QTStr("Basic.MainMenu.SceneCollection.Migrate")
+		       : QTStr("Basic.MainMenu.SceneCollection.Remigrate"));
+	ui->actionRemigrateSceneCollection->setEnabled(enable);
+}
+
 void OBSBasic::CreateDefaultScene(bool firstStart)
 {
 	disableSaving++;
@@ -1048,6 +1071,7 @@ void OBSBasic::CreateDefaultScene(bool firstStart)
 	ui->transitionDuration->setValue(300);
 	SetTransition(fadeTransition);
 
+	DisableRelativeCoordinates(false);
 	OBSSceneAutoRelease scene = obs_scene_create(Str("Basic.Scene"));
 
 	if (firstStart)
@@ -1188,10 +1212,11 @@ void OBSBasic::LogScenes()
 	blog(LOG_INFO, "------------------------------------------------");
 }
 
-void OBSBasic::Load(const char *file)
+void OBSBasic::Load(const char *file, bool remigrate)
 {
 	disableSaving++;
 	lastOutputResolution.reset();
+	migrationBaseResolution.reset();
 
 	obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak");
 	if (!data) {
@@ -1229,7 +1254,7 @@ void OBSBasic::Load(const char *file)
 		return;
 	}
 
-	LoadData(data, file);
+	LoadData(data, file, remigrate);
 }
 
 static inline void AddMissingFiles(void *data, obs_source_t *source)
@@ -1241,7 +1266,27 @@ static inline void AddMissingFiles(void *data, obs_source_t *source)
 	obs_missing_files_destroy(sf);
 }
 
-void OBSBasic::LoadData(obs_data_t *data, const char *file)
+static void ClearRelativePosCb(obs_data_t *data, void *)
+{
+	const string_view id = obs_data_get_string(data, "id");
+	if (id != "scene" && id != "group")
+		return;
+
+	OBSDataAutoRelease settings = obs_data_get_obj(data, "settings");
+	OBSDataArrayAutoRelease items = obs_data_get_array(settings, "items");
+
+	obs_data_array_enum(
+		items,
+		[](obs_data_t *data, void *) {
+			obs_data_unset_user_value(data, "pos_rel");
+			obs_data_unset_user_value(data, "scale_rel");
+			obs_data_unset_user_value(data, "scale_ref");
+			obs_data_unset_user_value(data, "bounds_rel");
+		},
+		nullptr);
+}
+
+void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate)
 {
 	ClearSceneData();
 	ClearContextBar();
@@ -1322,9 +1367,70 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file)
 		obs_data_array_push_back_array(sources, groups);
 	}
 
+	/* Reset relative coordinate data if forcefully remigrating. */
+	if (remigrate) {
+		obs_data_set_int(data, "version", 1);
+		obs_data_array_enum(sources, ClearRelativePosCb, nullptr);
+	}
+
+	bool resetVideo = false;
+	bool disableRelativeCoords = false;
+	obs_video_info ovi;
+
+	int64_t version = obs_data_get_int(data, "version");
+	OBSDataAutoRelease res = obs_data_get_obj(data, "resolution");
+	if (res) {
+		lastOutputResolution = {obs_data_get_int(res, "x"),
+					obs_data_get_int(res, "y")};
+	}
+
+	/* Only migrate legacy collection if resolution is saved. */
+	if (version < 2 && lastOutputResolution) {
+		obs_get_video_info(&ovi);
+
+		uint32_t width = obs_data_get_int(res, "x");
+		uint32_t height = obs_data_get_int(res, "y");
+
+		migrationBaseResolution = {width, height};
+
+		if (ovi.base_height != height || ovi.base_width != width) {
+			ovi.base_width = width;
+			ovi.base_height = height;
+
+			/* Attempt to reset to last known canvas resolution for migration. */
+			resetVideo = obs_reset_video(&ovi) == OBS_VIDEO_SUCCESS;
+			disableRelativeCoords = !resetVideo;
+		}
+
+		/* If migration is possible, and it wasn't forced, back up the original file. */
+		if (!disableRelativeCoords && !remigrate) {
+			auto path = filesystem::u8path(file);
+			auto backupPath = path.concat(".v1");
+			if (!filesystem::exists(backupPath)) {
+				if (!obs_data_save_json_pretty_safe(
+					    data, backupPath.u8string().c_str(),
+					    "tmp", NULL)) {
+					blog(LOG_WARNING,
+					     "Failed to create a backup of existing scene collection data!");
+				}
+			}
+		}
+	} else if (version < 2) {
+		disableRelativeCoords = true;
+	} else if (OBSDataAutoRelease migration_res =
+			   obs_data_get_obj(data, "migration_resolution")) {
+		migrationBaseResolution = {obs_data_get_int(migration_res, "x"),
+					   obs_data_get_int(migration_res,
+							    "y")};
+	}
+
+	DisableRelativeCoordinates(disableRelativeCoords);
+
 	obs_missing_files_t *files = obs_missing_files_create();
 	obs_load_sources(sources, AddMissingFiles, files);
 
+	if (resetVideo)
+		ResetVideo();
 	if (transitions)
 		LoadTransitions(transitions, AddMissingFiles, files);
 	if (sceneOrder)
@@ -4953,6 +5059,13 @@ int OBSBasic::ResetVideo()
 		OBSBasicStats::InitializeValues();
 		OBSProjector::UpdateMultiviewProjectors();
 
+		bool canMigrate =
+			usingAbsoluteCoordinates ||
+			(migrationBaseResolution &&
+			 (migrationBaseResolution->first != ovi.base_width ||
+			  migrationBaseResolution->second != ovi.base_height));
+		ui->actionRemigrateSceneCollection->setEnabled(canMigrate);
+
 		emit CanvasResized(ovi.base_width, ovi.base_height);
 		emit OutputResized(ovi.output_width, ovi.output_height);
 	}

+ 8 - 2
UI/window-basic-main.hpp

@@ -358,6 +358,10 @@ private:
 
 	std::atomic<obs_scene_t *> currentScene = nullptr;
 	std::optional<std::pair<uint32_t, uint32_t>> lastOutputResolution;
+	std::optional<std::pair<uint32_t, uint32_t>> migrationBaseResolution;
+	bool usingAbsoluteCoordinates = false;
+
+	void DisableRelativeCoordinates(bool disable);
 
 	void OnEvent(enum obs_frontend_event event);
 
@@ -377,8 +381,9 @@ private:
 	void UploadLog(const char *subdir, const char *file, const bool crash);
 
 	void Save(const char *file);
-	void LoadData(obs_data_t *data, const char *file);
-	void Load(const char *file);
+	void LoadData(obs_data_t *data, const char *file,
+		      bool remigrate = false);
+	void Load(const char *file, bool remigrate = false);
 
 	void InitHotkeys();
 	void CreateHotkeys();
@@ -1149,6 +1154,7 @@ private slots:
 	void on_actionRemoveSceneCollection_triggered();
 	void on_actionImportSceneCollection_triggered();
 	void on_actionExportSceneCollection_triggered();
+	void on_actionRemigrateSceneCollection_triggered();
 
 	void on_actionNewProfile_triggered();
 	void on_actionDupProfile_triggered();