소스 검색

UI: Split global config into app and user config

This introduces a split of the current single ConfigFile instance for
all configuration into two separate instances.

The app config instance contains system-wide settings that mainly
concern the working of the app itself, whereas the user config instance
contains settings actually exposed to the user for the configuration.
PatTheMav 1 년 전
부모
커밋
2635cf3a2a

+ 1 - 1
UI/adv-audio-control.cpp

@@ -125,7 +125,7 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
 	stackedWidget->addWidget(percent);
 
 	VolumeType volType = (VolumeType)config_get_int(
-		GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType");
+		App()->GetUserConfig(), "BasicWindow", "AdvAudioVolumeType");
 
 	SetVolumeWidget(volType);
 

+ 294 - 252
UI/api-interface.cpp

@@ -511,310 +511,352 @@ struct OBSStudioAPI : obs_frontend_callbacks {
 	config_t *obs_frontend_get_profile_config(void) override
 	{
 		return main->basicConfig;
-	}
+		config_t *obs_frontend_get_global_config(void) override
+		{
+			blog(LOG_WARNING,
+			     "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead.");
+			return App()->GetAppConfig();
+		}
 
-	config_t *obs_frontend_get_global_config(void) override
-	{
-		return App()->GlobalConfig();
-	}
+		config_t *obs_frontend_get_app_config(void) override
+		{
+			return App()->GetAppConfig();
+		}
 
-	void obs_frontend_open_projector(const char *type, int monitor,
-					 const char *geometry,
-					 const char *name) override
-	{
-		SavedProjectorInfo proj = {
-			ProjectorType::Preview,
-			monitor,
-			geometry ? geometry : "",
-			name ? name : "",
-		};
-		if (type) {
-			if (astrcmpi(type, "Source") == 0)
-				proj.type = ProjectorType::Source;
-			else if (astrcmpi(type, "Scene") == 0)
-				proj.type = ProjectorType::Scene;
-			else if (astrcmpi(type, "StudioProgram") == 0)
-				proj.type = ProjectorType::StudioProgram;
-			else if (astrcmpi(type, "Multiview") == 0)
-				proj.type = ProjectorType::Multiview;
-		}
-		QMetaObject::invokeMethod(main, "OpenSavedProjector",
-					  WaitConnection(),
-					  Q_ARG(SavedProjectorInfo *, &proj));
-	}
+		config_t *obs_frontend_get_user_config(void) override
+		{
+			return App()->GetUserConfig();
+		}
 
-	void obs_frontend_save(void) override { main->SaveProject(); }
+		void obs_frontend_open_projector(const char *type, int monitor,
+						 const char *geometry,
+						 const char *name) override
+		{
+			SavedProjectorInfo proj = {
+				ProjectorType::Preview,
+				monitor,
+				geometry ? geometry : "",
+				name ? name : "",
+			};
+			if (type) {
+				if (astrcmpi(type, "Source") == 0)
+					proj.type = ProjectorType::Source;
+				else if (astrcmpi(type, "Scene") == 0)
+					proj.type = ProjectorType::Scene;
+				else if (astrcmpi(type, "StudioProgram") == 0)
+					proj.type =
+						ProjectorType::StudioProgram;
+				else if (astrcmpi(type, "Multiview") == 0)
+					proj.type = ProjectorType::Multiview;
+			}
+			QMetaObject::invokeMethod(
+				main, "OpenSavedProjector", WaitConnection(),
+				Q_ARG(SavedProjectorInfo *, &proj));
+		}
 
-	void obs_frontend_defer_save_begin(void) override
-	{
-		QMetaObject::invokeMethod(main, "DeferSaveBegin");
-	}
+		void obs_frontend_save(void) override
+		{
+			main->SaveProject();
+		}
 
-	void obs_frontend_defer_save_end(void) override
-	{
-		QMetaObject::invokeMethod(main, "DeferSaveEnd");
-	}
+		void obs_frontend_defer_save_begin(void) override
+		{
+			QMetaObject::invokeMethod(main, "DeferSaveBegin");
+		}
 
-	void obs_frontend_add_save_callback(obs_frontend_save_cb callback,
-					    void *private_data) override
-	{
-		size_t idx =
-			GetCallbackIdx(saveCallbacks, callback, private_data);
-		if (idx == (size_t)-1)
-			saveCallbacks.emplace_back(callback, private_data);
-	}
+		void obs_frontend_defer_save_end(void) override
+		{
+			QMetaObject::invokeMethod(main, "DeferSaveEnd");
+		}
 
-	void obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
-					       void *private_data) override
-	{
-		size_t idx =
-			GetCallbackIdx(saveCallbacks, callback, private_data);
-		if (idx == (size_t)-1)
-			return;
+		void obs_frontend_add_save_callback(
+			obs_frontend_save_cb callback, void *private_data)
+			override
+		{
+			size_t idx = GetCallbackIdx(saveCallbacks, callback,
+						    private_data);
+			if (idx == (size_t)-1)
+				saveCallbacks.emplace_back(callback,
+							   private_data);
+		}
 
-		saveCallbacks.erase(saveCallbacks.begin() + idx);
-	}
+		void obs_frontend_remove_save_callback(
+			obs_frontend_save_cb callback, void *private_data)
+			override
+		{
+			size_t idx = GetCallbackIdx(saveCallbacks, callback,
+						    private_data);
+			if (idx == (size_t)-1)
+				return;
 
-	void obs_frontend_add_preload_callback(obs_frontend_save_cb callback,
-					       void *private_data) override
-	{
-		size_t idx = GetCallbackIdx(preloadCallbacks, callback,
-					    private_data);
-		if (idx == (size_t)-1)
-			preloadCallbacks.emplace_back(callback, private_data);
-	}
+			saveCallbacks.erase(saveCallbacks.begin() + idx);
+		}
 
-	void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback,
-						  void *private_data) override
-	{
-		size_t idx = GetCallbackIdx(preloadCallbacks, callback,
-					    private_data);
-		if (idx == (size_t)-1)
-			return;
+		void obs_frontend_add_preload_callback(
+			obs_frontend_save_cb callback, void *private_data)
+			override
+		{
+			size_t idx = GetCallbackIdx(preloadCallbacks, callback,
+						    private_data);
+			if (idx == (size_t)-1)
+				preloadCallbacks.emplace_back(callback,
+							      private_data);
+		}
 
-		preloadCallbacks.erase(preloadCallbacks.begin() + idx);
-	}
+		void obs_frontend_remove_preload_callback(
+			obs_frontend_save_cb callback, void *private_data)
+			override
+		{
+			size_t idx = GetCallbackIdx(preloadCallbacks, callback,
+						    private_data);
+			if (idx == (size_t)-1)
+				return;
 
-	void obs_frontend_push_ui_translation(
-		obs_frontend_translate_ui_cb translate) override
-	{
-		App()->PushUITranslation(translate);
-	}
+			preloadCallbacks.erase(preloadCallbacks.begin() + idx);
+		}
 
-	void obs_frontend_pop_ui_translation(void) override
-	{
-		App()->PopUITranslation();
-	}
+		void obs_frontend_push_ui_translation(
+			obs_frontend_translate_ui_cb translate) override
+		{
+			App()->PushUITranslation(translate);
+		}
 
-	void obs_frontend_set_streaming_service(obs_service_t *service) override
-	{
-		main->SetService(service);
-	}
+		void obs_frontend_pop_ui_translation(void) override
+		{
+			App()->PopUITranslation();
+		}
 
-	obs_service_t *obs_frontend_get_streaming_service(void) override
-	{
-		return main->GetService();
-	}
+		void obs_frontend_set_streaming_service(obs_service_t * service)
+			override
+		{
+			main->SetService(service);
+		}
 
-	void obs_frontend_save_streaming_service(void) override
-	{
-		main->SaveService();
-	}
+		obs_service_t *obs_frontend_get_streaming_service(void) override
+		{
+			return main->GetService();
+		}
 
-	bool obs_frontend_preview_program_mode_active(void) override
-	{
-		return main->IsPreviewProgramMode();
-	}
+		void obs_frontend_save_streaming_service(void) override
+		{
+			main->SaveService();
+		}
 
-	void obs_frontend_set_preview_program_mode(bool enable) override
-	{
-		main->SetPreviewProgramMode(enable);
-	}
+		bool obs_frontend_preview_program_mode_active(void) override
+		{
+			return main->IsPreviewProgramMode();
+		}
 
-	void obs_frontend_preview_program_trigger_transition(void) override
-	{
-		QMetaObject::invokeMethod(main, "TransitionClicked");
-	}
+		void obs_frontend_set_preview_program_mode(bool enable) override
+		{
+			main->SetPreviewProgramMode(enable);
+		}
 
-	bool obs_frontend_preview_enabled(void) override
-	{
-		return main->previewEnabled;
-	}
+		void obs_frontend_preview_program_trigger_transition(void)
+			override
+		{
+			QMetaObject::invokeMethod(main, "TransitionClicked");
+		}
 
-	void obs_frontend_set_preview_enabled(bool enable) override
-	{
-		if (main->previewEnabled != enable)
-			main->EnablePreviewDisplay(enable);
-	}
+		bool obs_frontend_preview_enabled(void) override
+		{
+			return main->previewEnabled;
+		}
 
-	obs_source_t *obs_frontend_get_current_preview_scene(void) override
-	{
-		if (main->IsPreviewProgramMode()) {
-			OBSSource source = main->GetCurrentSceneSource();
-			return obs_source_get_ref(source);
+		void obs_frontend_set_preview_enabled(bool enable) override
+		{
+			if (main->previewEnabled != enable)
+				main->EnablePreviewDisplay(enable);
 		}
 
-		return nullptr;
-	}
+		obs_source_t *obs_frontend_get_current_preview_scene(void)
+			override
+		{
+			if (main->IsPreviewProgramMode()) {
+				OBSSource source =
+					main->GetCurrentSceneSource();
+				return obs_source_get_ref(source);
+			}
 
-	void
-	obs_frontend_set_current_preview_scene(obs_source_t *scene) override
-	{
-		if (main->IsPreviewProgramMode()) {
-			QMetaObject::invokeMethod(main, "SetCurrentScene",
-						  Q_ARG(OBSSource,
-							OBSSource(scene)),
-						  Q_ARG(bool, false));
+			return nullptr;
 		}
-	}
 
-	void obs_frontend_take_screenshot(void) override
-	{
-		QMetaObject::invokeMethod(main, "Screenshot");
-	}
+		void obs_frontend_set_current_preview_scene(obs_source_t *
+							    scene) override
+		{
+			if (main->IsPreviewProgramMode()) {
+				QMetaObject::invokeMethod(
+					main, "SetCurrentScene",
+					Q_ARG(OBSSource, OBSSource(scene)),
+					Q_ARG(bool, false));
+			}
+		}
 
-	void obs_frontend_take_source_screenshot(obs_source_t *source) override
-	{
-		QMetaObject::invokeMethod(main, "Screenshot",
-					  Q_ARG(OBSSource, OBSSource(source)));
-	}
+		void obs_frontend_take_screenshot(void) override
+		{
+			QMetaObject::invokeMethod(main, "Screenshot");
+		}
 
-	obs_output_t *obs_frontend_get_virtualcam_output(void) override
-	{
-		OBSOutput output = main->outputHandler->virtualCam.Get();
-		return obs_output_get_ref(output);
-	}
+		void obs_frontend_take_source_screenshot(obs_source_t * source)
+			override
+		{
+			QMetaObject::invokeMethod(main, "Screenshot",
+						  Q_ARG(OBSSource,
+							OBSSource(source)));
+		}
 
-	void obs_frontend_start_virtualcam(void) override
-	{
-		QMetaObject::invokeMethod(main, "StartVirtualCam");
-	}
+		obs_output_t *obs_frontend_get_virtualcam_output(void) override
+		{
+			OBSOutput output =
+				main->outputHandler->virtualCam.Get();
+			return obs_output_get_ref(output);
+		}
 
-	void obs_frontend_stop_virtualcam(void) override
-	{
-		QMetaObject::invokeMethod(main, "StopVirtualCam");
-	}
+		void obs_frontend_start_virtualcam(void) override
+		{
+			QMetaObject::invokeMethod(main, "StartVirtualCam");
+		}
 
-	bool obs_frontend_virtualcam_active(void) override
-	{
-		return os_atomic_load_bool(&virtualcam_active);
-	}
+		void obs_frontend_stop_virtualcam(void) override
+		{
+			QMetaObject::invokeMethod(main, "StopVirtualCam");
+		}
 
-	void obs_frontend_reset_video(void) override { main->ResetVideo(); }
+		bool obs_frontend_virtualcam_active(void) override
+		{
+			return os_atomic_load_bool(&virtualcam_active);
+		}
 
-	void obs_frontend_open_source_properties(obs_source_t *source) override
-	{
-		QMetaObject::invokeMethod(main, "OpenProperties",
-					  Q_ARG(OBSSource, OBSSource(source)));
-	}
+		void obs_frontend_reset_video(void) override
+		{
+			main->ResetVideo();
+		}
 
-	void obs_frontend_open_source_filters(obs_source_t *source) override
-	{
-		QMetaObject::invokeMethod(main, "OpenFilters",
-					  Q_ARG(OBSSource, OBSSource(source)));
-	}
+		void obs_frontend_open_source_properties(obs_source_t * source)
+			override
+		{
+			QMetaObject::invokeMethod(main, "OpenProperties",
+						  Q_ARG(OBSSource,
+							OBSSource(source)));
+		}
 
-	void obs_frontend_open_source_interaction(obs_source_t *source) override
-	{
-		QMetaObject::invokeMethod(main, "OpenInteraction",
-					  Q_ARG(OBSSource, OBSSource(source)));
-	}
+		void obs_frontend_open_source_filters(obs_source_t * source)
+			override
+		{
+			QMetaObject::invokeMethod(main, "OpenFilters",
+						  Q_ARG(OBSSource,
+							OBSSource(source)));
+		}
 
-	void obs_frontend_open_sceneitem_edit_transform(
-		obs_sceneitem_t *item) override
-	{
-		QMetaObject::invokeMethod(main, "OpenEditTransform",
-					  Q_ARG(OBSSceneItem,
-						OBSSceneItem(item)));
-	}
+		void obs_frontend_open_source_interaction(obs_source_t * source)
+			override
+		{
+			QMetaObject::invokeMethod(main, "OpenInteraction",
+						  Q_ARG(OBSSource,
+							OBSSource(source)));
+		}
 
-	char *obs_frontend_get_current_record_output_path(void) override
-	{
-		const char *recordOutputPath = main->GetCurrentOutputPath();
+		void obs_frontend_open_sceneitem_edit_transform(
+			obs_sceneitem_t * item) override
+		{
+			QMetaObject::invokeMethod(main, "OpenEditTransform",
+						  Q_ARG(OBSSceneItem,
+							OBSSceneItem(item)));
+		}
 
-		return bstrdup(recordOutputPath);
-	}
+		char *obs_frontend_get_current_record_output_path(void) override
+		{
+			const char *recordOutputPath =
+				main->GetCurrentOutputPath();
 
-	const char *obs_frontend_get_locale_string(const char *string) override
-	{
-		return Str(string);
-	}
+			return bstrdup(recordOutputPath);
+		}
 
-	bool obs_frontend_is_theme_dark(void) override
-	{
-		return App()->IsThemeDark();
-	}
+		const char *obs_frontend_get_locale_string(const char *string)
+			override
+		{
+			return Str(string);
+		}
 
-	char *obs_frontend_get_last_recording(void) override
-	{
-		return bstrdup(main->outputHandler->lastRecordingPath.c_str());
-	}
+		bool obs_frontend_is_theme_dark(void) override
+		{
+			return App()->IsThemeDark();
+		}
 
-	char *obs_frontend_get_last_screenshot(void) override
-	{
-		return bstrdup(main->lastScreenshot.c_str());
-	}
+		char *obs_frontend_get_last_recording(void) override
+		{
+			return bstrdup(
+				main->outputHandler->lastRecordingPath.c_str());
+		}
 
-	char *obs_frontend_get_last_replay(void) override
-	{
-		return bstrdup(main->lastReplay.c_str());
-	}
+		char *obs_frontend_get_last_screenshot(void) override
+		{
+			return bstrdup(main->lastScreenshot.c_str());
+		}
 
-	void obs_frontend_add_undo_redo_action(const char *name,
-					       const undo_redo_cb undo,
-					       const undo_redo_cb redo,
-					       const char *undo_data,
-					       const char *redo_data,
-					       bool repeatable) override
-	{
-		main->undo_s.add_action(
-			name,
-			[undo](const std::string &data) { undo(data.c_str()); },
-			[redo](const std::string &data) { redo(data.c_str()); },
-			undo_data, redo_data, repeatable);
-	}
+		char *obs_frontend_get_last_replay(void) override
+		{
+			return bstrdup(main->lastReplay.c_str());
+		}
 
-	void on_load(obs_data_t *settings) override
-	{
-		for (size_t i = saveCallbacks.size(); i > 0; i--) {
-			auto cb = saveCallbacks[i - 1];
-			cb.callback(settings, false, cb.private_data);
+		void obs_frontend_add_undo_redo_action(
+			const char *name, const undo_redo_cb undo,
+			const undo_redo_cb redo, const char *undo_data,
+			const char *redo_data, bool repeatable) override
+		{
+			main->undo_s.add_action(
+				name,
+				[undo](const std::string &data) {
+					undo(data.c_str());
+				},
+				[redo](const std::string &data) {
+					redo(data.c_str());
+				},
+				undo_data, redo_data, repeatable);
 		}
-	}
 
-	void on_preload(obs_data_t *settings) override
-	{
-		for (size_t i = preloadCallbacks.size(); i > 0; i--) {
-			auto cb = preloadCallbacks[i - 1];
-			cb.callback(settings, false, cb.private_data);
+		void on_load(obs_data_t * settings) override
+		{
+			for (size_t i = saveCallbacks.size(); i > 0; i--) {
+				auto cb = saveCallbacks[i - 1];
+				cb.callback(settings, false, cb.private_data);
+			}
 		}
-	}
 
-	void on_save(obs_data_t *settings) override
-	{
-		for (size_t i = saveCallbacks.size(); i > 0; i--) {
-			auto cb = saveCallbacks[i - 1];
-			cb.callback(settings, true, cb.private_data);
+		void on_preload(obs_data_t * settings) override
+		{
+			for (size_t i = preloadCallbacks.size(); i > 0; i--) {
+				auto cb = preloadCallbacks[i - 1];
+				cb.callback(settings, false, cb.private_data);
+			}
 		}
-	}
 
-	void on_event(enum obs_frontend_event event) override
-	{
-		if (main->disableSaving &&
-		    event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP &&
-		    event != OBS_FRONTEND_EVENT_EXIT)
-			return;
+		void on_save(obs_data_t * settings) override
+		{
+			for (size_t i = saveCallbacks.size(); i > 0; i--) {
+				auto cb = saveCallbacks[i - 1];
+				cb.callback(settings, true, cb.private_data);
+			}
+		}
 
-		for (size_t i = callbacks.size(); i > 0; i--) {
-			auto cb = callbacks[i - 1];
-			cb.callback(event, cb.private_data);
+		void on_event(enum obs_frontend_event event) override
+		{
+			if (main->disableSaving &&
+			    event !=
+				    OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP &&
+			    event != OBS_FRONTEND_EVENT_EXIT)
+				return;
+
+			for (size_t i = callbacks.size(); i > 0; i--) {
+				auto cb = callbacks[i - 1];
+				cb.callback(event, cb.private_data);
+			}
 		}
-	}
-};
+	};
 
-obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main)
-{
-	obs_frontend_callbacks *api = new OBSStudioAPI(main);
-	obs_frontend_set_callbacks_internal(api);
-	return api;
-}
+	obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main)
+	{
+		obs_frontend_callbacks *api = new OBSStudioAPI(main);
+		obs_frontend_set_callbacks_internal(api);
+		return api;
+	}

+ 1 - 1
UI/auth-twitch.cpp

@@ -410,7 +410,7 @@ void TwitchAuth::LoadSecondaryUIPanes()
 		stats->setVisible(false);
 		feed->setVisible(false);
 	} else {
-		uint32_t lastVersion = config_get_int(App()->GlobalConfig(),
+		uint32_t lastVersion = config_get_int(App()->GetAppConfig(),
 						      "General", "LastVersion");
 
 		if (lastVersion <= MAKE_SEMANTIC_VERSION(23, 0, 2)) {

+ 9 - 10
UI/frontend-plugins/frontend-tools/scripts.cpp

@@ -113,7 +113,7 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr)
 
 	resize(600, 400);
 
-	config_t *global_config = obs_frontend_get_global_config();
+	config_t *global_config = obs_frontend_get_user_config();
 	const char *geom =
 		config_get_string(global_config, "ScriptLogWindow", "geometry");
 	if (geom != nullptr) {
@@ -129,7 +129,7 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr)
 
 ScriptLogWindow::~ScriptLogWindow()
 {
-	config_t *global_config = obs_frontend_get_global_config();
+	config_t *global_config = obs_frontend_get_user_config();
 	config_set_string(global_config, "ScriptLogWindow", "geometry",
 			  saveGeometry().toBase64().constData());
 }
@@ -189,7 +189,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)
 	RefreshLists();
 
 #if PYTHON_UI
-	config_t *config = obs_frontend_get_global_config();
+	config_t *config = obs_frontend_get_user_config();
 	const char *path =
 		config_get_string(config, "Python", "Path" ARCH_NAME);
 	ui->pythonPath->setText(path);
@@ -207,16 +207,15 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)
 				      QSizePolicy::Expanding);
 	ui->propertiesLayout->addWidget(propertiesView);
 
-	config_t *global_config = obs_frontend_get_global_config();
-	int row =
-		config_get_int(global_config, "scripts-tool", "prevScriptRow");
+	config_t *user_config = obs_frontend_get_user_config();
+	int row = config_get_int(user_config, "scripts-tool", "prevScriptRow");
 	ui->scripts->setCurrentRow(row);
 }
 
 ScriptsTool::~ScriptsTool()
 {
-	config_t *global_config = obs_frontend_get_global_config();
-	config_set_int(global_config, "scripts-tool", "prevScriptRow",
+	config_t *user_config = obs_frontend_get_user_config();
+	config_set_int(user_config, "scripts-tool", "prevScriptRow",
 		       ui->scripts->currentRow());
 }
 
@@ -465,7 +464,7 @@ void ScriptsTool::on_pythonPathBrowse_clicked()
 	QByteArray array = newPath.toUtf8();
 	const char *path = array.constData();
 
-	config_t *config = obs_frontend_get_global_config();
+	config_t *config = obs_frontend_get_user_config();
 	config_set_string(config, "Python", "Path" ARCH_NAME, path);
 
 	ui->pythonPath->setText(newPath);
@@ -685,7 +684,7 @@ extern "C" void InitScripts()
 		obs_module_text("Scripts"));
 
 #if PYTHON_UI
-	config_t *config = obs_frontend_get_global_config();
+	config_t *config = obs_frontend_get_user_config();
 	const char *python_path =
 		config_get_string(config, "Python", "Path" ARCH_NAME);
 

+ 7 - 7
UI/log-viewer.cpp

@@ -23,12 +23,12 @@ OBSLogViewer::OBSLogViewer(QWidget *parent)
 	ui->setupUi(this);
 
 	bool showLogViewerOnStartup = config_get_bool(
-		App()->GlobalConfig(), "LogViewer", "ShowLogStartup");
+		App()->GetUserConfig(), "LogViewer", "ShowLogStartup");
 
 	ui->showStartup->setChecked(showLogViewerOnStartup);
 
-	const char *geom = config_get_string(App()->GlobalConfig(), "LogViewer",
-					     "geometry");
+	const char *geom = config_get_string(App()->GetUserConfig(),
+					     "LogViewer", "geometry");
 
 	if (geom != nullptr) {
 		QByteArray ba = QByteArray::fromBase64(QByteArray(geom));
@@ -40,13 +40,13 @@ OBSLogViewer::OBSLogViewer(QWidget *parent)
 
 OBSLogViewer::~OBSLogViewer()
 {
-	config_set_string(App()->GlobalConfig(), "LogViewer", "geometry",
+	config_set_string(App()->GetUserConfig(), "LogViewer", "geometry",
 			  saveGeometry().toBase64().constData());
 }
 
 void OBSLogViewer::on_showStartup_clicked(bool checked)
 {
-	config_set_bool(App()->GlobalConfig(), "LogViewer", "ShowLogStartup",
+	config_set_bool(App()->GetUserConfig(), "LogViewer", "ShowLogStartup",
 			checked);
 }
 
@@ -57,7 +57,7 @@ void OBSLogViewer::InitLog()
 	char logDir[512];
 	std::string path;
 
-	if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs")) {
+	if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs")) {
 		path += logDir;
 		path += "/";
 		path += App()->GetCurrentLog();
@@ -124,7 +124,7 @@ void OBSLogViewer::AddLine(int type, const QString &str)
 void OBSLogViewer::on_openButton_clicked()
 {
 	char logDir[512];
-	if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0)
+	if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0)
 		return;
 
 	const char *log = App()->GetCurrentLog();

+ 2 - 2
UI/media-controls.cpp

@@ -67,7 +67,7 @@ MediaControls::MediaControls(QWidget *parent)
 	connect(ui->slider, &AbsoluteSlider::sliderMoved, this,
 		&MediaControls::AbsoluteSliderMoved);
 
-	countDownTimer = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	countDownTimer = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "MediaControlsCountdownTimer");
 
 	QAction *restartAction = new QAction(this);
@@ -465,7 +465,7 @@ void MediaControls::on_durationLabel_clicked()
 {
 	countDownTimer = !countDownTimer;
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"MediaControlsCountdownTimer", countDownTimer);
 
 	if (MediaPaused())

+ 25 - 19
UI/obs-app-theming.cpp

@@ -409,7 +409,6 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
 
 void OBSApp::FindThemes()
 {
-	string themeDir;
 
 	QStringList filters;
 	filters << "*.obt" // OBS Base Theme
@@ -417,18 +416,24 @@ void OBSApp::FindThemes()
 		<< "*.oha" // OBS High-contrast Adjustment layer
 		;
 
-	GetDataFilePath("themes/", themeDir);
-	QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);
-	while (it.hasNext()) {
-		auto theme = ParseThemeMeta(it.next());
-		if (theme && !themes.contains(theme->id))
-			themes[theme->id] = std::move(*theme);
+	{
+		string themeDir;
+		GetDataFilePath("themes/", themeDir);
+		QDirIterator it(QString::fromStdString(themeDir), filters,
+				QDir::Files);
+		while (it.hasNext()) {
+			auto theme = ParseThemeMeta(it.next());
+			if (theme && !themes.contains(theme->id))
+				themes[theme->id] = std::move(*theme);
+		}
 	}
 
-	themeDir.resize(1024);
-	if (GetConfigPath(themeDir.data(), themeDir.capacity(),
-			  "obs-studio/themes/") > 0) {
-		QDirIterator it(QT_UTF8(themeDir.c_str()), filters,
+	{
+		const std::string themeDir =
+			App()->userConfigLocation.u8string() +
+			"/obs-studio/themes";
+
+		QDirIterator it(QString::fromStdString(themeDir), filters,
 				QDir::Files);
 
 		while (it.hasNext()) {
@@ -876,7 +881,8 @@ bool OBSApp::SetTheme(const QString &name)
 
 	filesystem::path debugOut;
 	char configPath[512];
-	if (GetConfigPath(configPath, sizeof(configPath), filename.c_str())) {
+	if (GetAppConfigPath(configPath, sizeof(configPath),
+			     filename.c_str())) {
 		debugOut = absolute(filesystem::u8path(configPath));
 		filesystem::create_directories(debugOut.parent_path());
 	}
@@ -940,7 +946,7 @@ bool OBSApp::InitTheme()
 	}
 
 	char userDir[512];
-	if (GetConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) {
+	if (GetAppConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) {
 		auto configSearchDir = filesystem::u8path(userDir);
 		QDir::addSearchPath("theme", absolute(configSearchDir));
 	}
@@ -948,7 +954,7 @@ bool OBSApp::InitTheme()
 	/* Load list of themes and read their metadata */
 	FindThemes();
 
-	if (config_get_bool(globalConfig, "Appearance", "AutoReload")) {
+	if (config_get_bool(userConfig, "Appearance", "AutoReload")) {
 		/* Set up Qt file watcher to automatically reload themes */
 		themeWatcher = new QFileSystemWatcher(this);
 		connect(themeWatcher.get(), &QFileSystemWatcher::fileChanged,
@@ -956,19 +962,19 @@ bool OBSApp::InitTheme()
 	}
 
 	/* Migrate old theme config key */
-	if (config_has_user_value(globalConfig, "General", "CurrentTheme3") &&
-	    !config_has_user_value(globalConfig, "Appearance", "Theme")) {
-		const char *old = config_get_string(globalConfig, "General",
+	if (config_has_user_value(userConfig, "General", "CurrentTheme3") &&
+	    !config_has_user_value(userConfig, "Appearance", "Theme")) {
+		const char *old = config_get_string(userConfig, "General",
 						    "CurrentTheme3");
 
 		if (themeMigrations.count(old)) {
-			config_set_string(globalConfig, "Appearance", "Theme",
+			config_set_string(userConfig, "Appearance", "Theme",
 					  themeMigrations[old].c_str());
 		}
 	}
 
 	QString themeName =
-		config_get_string(globalConfig, "Appearance", "Theme");
+		config_get_string(userConfig, "Appearance", "Theme");
 
 	if (themeName.isEmpty() || !GetTheme(themeName)) {
 		if (!themeName.isEmpty()) {

+ 289 - 169
UI/obs-app.cpp

@@ -23,6 +23,7 @@
 #include <string>
 #include <sstream>
 #include <mutex>
+#include <filesystem>
 #include <util/bmem.h>
 #include <util/dstr.hpp>
 #include <util/platform.h>
@@ -440,99 +441,119 @@ static void do_log(int log_level, const char *msg, va_list args, void *param)
 
 bool OBSApp::InitGlobalConfigDefaults()
 {
-	config_set_default_uint(globalConfig, "General", "MaxLogs", 10);
-	config_set_default_int(globalConfig, "General", "InfoIncrement", -1);
-	config_set_default_string(globalConfig, "General", "ProcessPriority",
+	config_set_default_uint(appConfig, "General", "MaxLogs", 10);
+	config_set_default_int(appConfig, "General", "InfoIncrement", -1);
+	config_set_default_string(appConfig, "General", "ProcessPriority",
 				  "Normal");
-	config_set_default_bool(globalConfig, "General", "EnableAutoUpdates",
+	config_set_default_bool(appConfig, "General", "EnableAutoUpdates",
 				true);
 
-	config_set_default_bool(globalConfig, "General", "ConfirmOnExit", true);
-
 #if _WIN32
-	config_set_default_string(globalConfig, "Video", "Renderer",
+	config_set_default_string(appConfig, "Video", "Renderer",
 				  "Direct3D 11");
 #else
-	config_set_default_string(globalConfig, "Video", "Renderer", "OpenGL");
+	config_set_default_string(appConfig, "Video", "Renderer", "OpenGL");
 #endif
 
-	config_set_default_bool(globalConfig, "BasicWindow", "PreviewEnabled",
+#ifdef _WIN32
+	config_set_default_bool(appConfig, "Audio", "DisableAudioDucking",
+				true);
+	config_set_default_bool(appConfig, "General", "BrowserHWAccel", true);
+#endif
+
+#ifdef __APPLE__
+	config_set_default_bool(appConfig, "General", "BrowserHWAccel", true);
+	config_set_default_bool(appConfig, "Video", "DisableOSXVSync", true);
+	config_set_default_bool(appConfig, "Video", "ResetOSXVSyncOnExit",
+				true);
+#endif
+
+	return true;
+}
+
+bool OBSApp::InitGlobalLocationDefaults()
+{
+	char path[512];
+
+	int len = GetAppConfigPath(path, sizeof(path), nullptr);
+	if (len <= 0) {
+		OBSErrorBox(NULL, "Unable to get global configuration path.");
+		return false;
+	}
+
+	config_set_default_string(appConfig, "Locations", "Configuration",
+				  path);
+	config_set_default_string(appConfig, "Locations", "SceneCollections",
+				  path);
+	config_set_default_string(appConfig, "Locations", "Profiles", path);
+
+	return true;
+}
+
+void OBSApp::InitUserConfigDefaults()
+{
+	config_set_default_bool(userConfig, "General", "ConfirmOnExit", true);
+
+	config_set_default_string(userConfig, "General", "HotkeyFocusType",
+				  "NeverDisableHotkeys");
+
+	config_set_default_bool(userConfig, "BasicWindow", "PreviewEnabled",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow",
-				"PreviewProgramMode", false);
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow", "PreviewProgramMode",
+				false);
+	config_set_default_bool(userConfig, "BasicWindow",
 				"SceneDuplicationMode", true);
-	config_set_default_bool(globalConfig, "BasicWindow", "SwapScenesMode",
+	config_set_default_bool(userConfig, "BasicWindow", "SwapScenesMode",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow", "SnappingEnabled",
+	config_set_default_bool(userConfig, "BasicWindow", "SnappingEnabled",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow", "ScreenSnapping",
+	config_set_default_bool(userConfig, "BasicWindow", "ScreenSnapping",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow", "SourceSnapping",
+	config_set_default_bool(userConfig, "BasicWindow", "SourceSnapping",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow", "CenterSnapping",
+	config_set_default_bool(userConfig, "BasicWindow", "CenterSnapping",
 				false);
-	config_set_default_double(globalConfig, "BasicWindow", "SnapDistance",
+	config_set_default_double(userConfig, "BasicWindow", "SnapDistance",
 				  10.0);
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"SpacingHelpersEnabled", true);
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"RecordWhenStreaming", false);
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"KeepRecordingWhenStreamStops", false);
-	config_set_default_bool(globalConfig, "BasicWindow", "SysTrayEnabled",
+	config_set_default_bool(userConfig, "BasicWindow", "SysTrayEnabled",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow",
-				"SysTrayWhenStarted", false);
-	config_set_default_bool(globalConfig, "BasicWindow", "SaveProjectors",
+	config_set_default_bool(userConfig, "BasicWindow", "SysTrayWhenStarted",
+				false);
+	config_set_default_bool(userConfig, "BasicWindow", "SaveProjectors",
 				false);
-	config_set_default_bool(globalConfig, "BasicWindow", "ShowTransitions",
+	config_set_default_bool(userConfig, "BasicWindow", "ShowTransitions",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"ShowListboxToolbars", true);
-	config_set_default_bool(globalConfig, "BasicWindow", "ShowStatusBar",
+	config_set_default_bool(userConfig, "BasicWindow", "ShowStatusBar",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow", "ShowSourceIcons",
+	config_set_default_bool(userConfig, "BasicWindow", "ShowSourceIcons",
 				true);
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"ShowContextToolbars", true);
-	config_set_default_bool(globalConfig, "BasicWindow", "StudioModeLabels",
+	config_set_default_bool(userConfig, "BasicWindow", "StudioModeLabels",
 				true);
 
-	config_set_default_string(globalConfig, "General", "HotkeyFocusType",
-				  "NeverDisableHotkeys");
-
-	config_set_default_bool(globalConfig, "BasicWindow",
-				"VerticalVolControl", false);
+	config_set_default_bool(userConfig, "BasicWindow", "VerticalVolControl",
+				false);
 
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"MultiviewMouseSwitch", true);
 
-	config_set_default_bool(globalConfig, "BasicWindow",
-				"MultiviewDrawNames", true);
-
-	config_set_default_bool(globalConfig, "BasicWindow",
-				"MultiviewDrawAreas", true);
-
-#ifdef _WIN32
-	config_set_default_bool(globalConfig, "Audio", "DisableAudioDucking",
+	config_set_default_bool(userConfig, "BasicWindow", "MultiviewDrawNames",
 				true);
-	config_set_default_bool(globalConfig, "General", "BrowserHWAccel",
-				true);
-#endif
 
-#ifdef __APPLE__
-	config_set_default_bool(globalConfig, "General", "BrowserHWAccel",
+	config_set_default_bool(userConfig, "BasicWindow", "MultiviewDrawAreas",
 				true);
-	config_set_default_bool(globalConfig, "Video", "DisableOSXVSync", true);
-	config_set_default_bool(globalConfig, "Video", "ResetOSXVSyncOnExit",
-				true);
-#endif
 
-	config_set_default_bool(globalConfig, "BasicWindow",
+	config_set_default_bool(userConfig, "BasicWindow",
 				"MediaControlsCountdownTimer", true);
-
-	return true;
 }
 
 static bool do_mkdir(const char *path)
@@ -549,36 +570,38 @@ static bool MakeUserDirs()
 {
 	char path[512];
 
-	if (GetConfigPath(path, sizeof(path), "obs-studio/basic") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/basic") <= 0)
 		return false;
 	if (!do_mkdir(path))
 		return false;
 
-	if (GetConfigPath(path, sizeof(path), "obs-studio/logs") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/logs") <= 0)
 		return false;
 	if (!do_mkdir(path))
 		return false;
 
-	if (GetConfigPath(path, sizeof(path), "obs-studio/profiler_data") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/profiler_data") <=
+	    0)
 		return false;
 	if (!do_mkdir(path))
 		return false;
 
 #ifdef _WIN32
-	if (GetConfigPath(path, sizeof(path), "obs-studio/crashes") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/crashes") <= 0)
 		return false;
 	if (!do_mkdir(path))
 		return false;
 #endif
 
 #ifdef WHATSNEW_ENABLED
-	if (GetConfigPath(path, sizeof(path), "obs-studio/updates") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/updates") <= 0)
 		return false;
 	if (!do_mkdir(path))
 		return false;
 #endif
 
-	if (GetConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/plugin_config") <=
+	    0)
 		return false;
 	if (!do_mkdir(path))
 		return false;
@@ -698,7 +721,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
 
 	if (astrcmpi(layout, "horizontaltop") == 0) {
 		config_set_int(
-			globalConfig, "BasicWindow", "MultiviewLayout",
+			userConfig, "BasicWindow", "MultiviewLayout",
 			static_cast<int>(
 				MultiviewLayout::HORIZONTAL_TOP_8_SCENES));
 		return true;
@@ -706,7 +729,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
 
 	if (astrcmpi(layout, "horizontalbottom") == 0) {
 		config_set_int(
-			globalConfig, "BasicWindow", "MultiviewLayout",
+			userConfig, "BasicWindow", "MultiviewLayout",
 			static_cast<int>(
 				MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES));
 		return true;
@@ -714,7 +737,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
 
 	if (astrcmpi(layout, "verticalleft") == 0) {
 		config_set_int(
-			globalConfig, "BasicWindow", "MultiviewLayout",
+			userConfig, "BasicWindow", "MultiviewLayout",
 			static_cast<int>(
 				MultiviewLayout::VERTICAL_LEFT_8_SCENES));
 		return true;
@@ -722,7 +745,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
 
 	if (astrcmpi(layout, "verticalright") == 0) {
 		config_set_int(
-			globalConfig, "BasicWindow", "MultiviewLayout",
+			userConfig, "BasicWindow", "MultiviewLayout",
 			static_cast<int>(
 				MultiviewLayout::VERTICAL_RIGHT_8_SCENES));
 		return true;
@@ -734,120 +757,212 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
 bool OBSApp::InitGlobalConfig()
 {
 	char path[512];
-	bool changed = false;
 
-	int len = GetConfigPath(path, sizeof(path), "obs-studio/global.ini");
+	int len = GetAppConfigPath(path, sizeof(path), "obs-studio/global.ini");
 	if (len <= 0) {
 		return false;
 	}
 
-	int errorcode = globalConfig.Open(path, CONFIG_OPEN_ALWAYS);
+	int errorcode = appConfig.Open(path, CONFIG_OPEN_ALWAYS);
 	if (errorcode != CONFIG_SUCCESS) {
 		OBSErrorBox(NULL, "Failed to open global.ini: %d", errorcode);
 		return false;
 	}
 
+	uint32_t lastVersion =
+		config_get_int(appConfig, "General", "LastVersion");
+
+	if (lastVersion < MAKE_SEMANTIC_VERSION(31, 0, 0)) {
+		bool migratedUserSettings =
+			config_get_bool(appConfig, "General", "Pre31Migrated");
+
+		if (!migratedUserSettings) {
+			bool migrated = MigrateGlobalSettings();
+
+			config_set_bool(appConfig, "General", "Pre31Migrated",
+					migrated);
+			config_save_safe(appConfig, "tmp", nullptr);
+		}
+	}
+
+	InitGlobalConfigDefaults();
+	InitGlobalLocationDefaults();
+
+	userConfigLocation = std::filesystem::u8path(
+		config_get_string(appConfig, "Locations", "Configuration"));
+	userScenesLocation = std::filesystem::u8path(
+		config_get_string(appConfig, "Locations", "SceneCollections"));
+	userProfilesLocation = std::filesystem::u8path(
+		config_get_string(appConfig, "Locations", "Profiles"));
+
+	bool userConfigResult = InitUserConfig(userConfigLocation, lastVersion);
+
+	return userConfigResult;
+}
+
+bool OBSApp::InitUserConfig(std::filesystem::path &userConfigLocation,
+			    uint32_t lastVersion)
+{
+	bool hasChanges = false;
+
+	const std::string userConfigFile =
+		userConfigLocation.u8string() + "/obs-studio/user.ini";
+
+	int errorCode =
+		userConfig.Open(userConfigFile.c_str(), CONFIG_OPEN_ALWAYS);
+
+	if (errorCode != CONFIG_SUCCESS) {
+		OBSErrorBox(nullptr, "Failed to open user.ini: %d", errorCode);
+		return false;
+	}
+
+	hasChanges = MigrateLegacySettings(lastVersion);
+
 	if (!opt_starting_collection.empty()) {
-		string path = GetSceneCollectionFileFromName(
-			opt_starting_collection.c_str());
-		if (!path.empty()) {
-			config_set_string(globalConfig, "Basic",
+		const OBSBasic *basic =
+			reinterpret_cast<OBSBasic *>(GetMainWindow());
+		const std::optional<OBSSceneCollection> foundCollection =
+			basic->GetSceneCollectionByName(
+				opt_starting_collection);
+
+		if (foundCollection) {
+			config_set_string(userConfig, "Basic",
 					  "SceneCollection",
-					  opt_starting_collection.c_str());
-			config_set_string(globalConfig, "Basic",
-					  "SceneCollectionFile", path.c_str());
-			changed = true;
+					  foundCollection.value().name.c_str());
+			config_set_string(
+				userConfig, "Basic", "SceneCollectionFile",
+				foundCollection.value().fileName.c_str());
+			hasChanges = true;
 		}
 	}
 
 	if (!opt_starting_profile.empty()) {
-		string path =
-			GetProfileDirFromName(opt_starting_profile.c_str());
-		if (!path.empty()) {
-			config_set_string(globalConfig, "Basic", "Profile",
-					  opt_starting_profile.c_str());
-			config_set_string(globalConfig, "Basic", "ProfileDir",
-					  path.c_str());
-			changed = true;
-		}
-	}
+		const OBSBasic *basic =
+			reinterpret_cast<OBSBasic *>(GetMainWindow());
 
-	uint32_t lastVersion =
-		config_get_int(globalConfig, "General", "LastVersion");
+		const std::optional<OBSProfile> foundProfile =
+			basic->GetProfileByName(opt_starting_profile);
 
-	if (!config_has_user_value(globalConfig, "General", "Pre19Defaults")) {
-		bool useOldDefaults = lastVersion &&
-				      lastVersion <
-					      MAKE_SEMANTIC_VERSION(19, 0, 0);
+		if (foundProfile) {
+			config_set_string(userConfig, "Basic", "Profile",
+					  foundProfile.value().name.c_str());
+			config_set_string(
+				userConfig, "Basic", "ProfileDir",
+				foundProfile.value().directoryName.c_str());
 
-		config_set_bool(globalConfig, "General", "Pre19Defaults",
-				useOldDefaults);
-		changed = true;
+			hasChanges = true;
+		}
 	}
 
-	if (!config_has_user_value(globalConfig, "General", "Pre21Defaults")) {
-		bool useOldDefaults = lastVersion &&
-				      lastVersion <
-					      MAKE_SEMANTIC_VERSION(21, 0, 0);
-
-		config_set_bool(globalConfig, "General", "Pre21Defaults",
-				useOldDefaults);
-		changed = true;
+	if (hasChanges) {
+		config_save_safe(userConfig, "tmp", nullptr);
 	}
 
-	if (!config_has_user_value(globalConfig, "General", "Pre23Defaults")) {
-		bool useOldDefaults = lastVersion &&
-				      lastVersion <
-					      MAKE_SEMANTIC_VERSION(23, 0, 0);
+	InitUserConfigDefaults();
 
-		config_set_bool(globalConfig, "General", "Pre23Defaults",
-				useOldDefaults);
-		changed = true;
-	}
+	return true;
+}
 
-#define PRE_24_1_DEFS "Pre24.1Defaults"
-	if (!config_has_user_value(globalConfig, "General", PRE_24_1_DEFS)) {
-		bool useOldDefaults = lastVersion &&
-				      lastVersion <
-					      MAKE_SEMANTIC_VERSION(24, 1, 0);
+bool OBSApp::MigrateLegacySettings(const uint32_t lastVersion)
+{
+	bool hasChanges = false;
 
-		config_set_bool(globalConfig, "General", PRE_24_1_DEFS,
-				useOldDefaults);
-		changed = true;
+	const uint32_t v19 = MAKE_SEMANTIC_VERSION(19, 0, 0);
+	const uint32_t v21 = MAKE_SEMANTIC_VERSION(21, 0, 0);
+	const uint32_t v23 = MAKE_SEMANTIC_VERSION(23, 0, 0);
+	const uint32_t v24 = MAKE_SEMANTIC_VERSION(24, 0, 0);
+	const uint32_t v24_1 = MAKE_SEMANTIC_VERSION(24, 1, 0);
+
+	const map<uint32_t, string> defaultsMap{{{v19, "Pre19Defaults"},
+						 {v21, "Pre21Defaults"},
+						 {v23, "Pre23Defaults"},
+						 {v24_1, "Pre24.1Defaults"}}};
+
+	for (auto &[version, configKey] : defaultsMap) {
+		if (!config_has_user_value(userConfig, "General",
+					   configKey.c_str())) {
+			bool useOldDefaults = lastVersion &&
+					      lastVersion < version;
+			config_set_bool(userConfig, "General",
+					configKey.c_str(), useOldDefaults);
+
+			hasChanges = true;
+		}
 	}
-#undef PRE_24_1_DEFS
 
-	if (config_has_user_value(globalConfig, "BasicWindow",
+	if (config_has_user_value(userConfig, "BasicWindow",
 				  "MultiviewLayout")) {
 		const char *layout = config_get_string(
-			globalConfig, "BasicWindow", "MultiviewLayout");
-		changed |= UpdatePre22MultiviewLayout(layout);
+			userConfig, "BasicWindow", "MultiviewLayout");
+
+		bool layoutUpdated = UpdatePre22MultiviewLayout(layout);
+
+		hasChanges = hasChanges | layoutUpdated;
 	}
 
-	if (lastVersion && lastVersion < MAKE_SEMANTIC_VERSION(24, 0, 0)) {
+	if (lastVersion && lastVersion < v24) {
 		bool disableHotkeysInFocus = config_get_bool(
-			globalConfig, "General", "DisableHotkeysInFocus");
-		if (disableHotkeysInFocus)
-			config_set_string(globalConfig, "General",
+			userConfig, "General", "DisableHotkeysInFocus");
+
+		if (disableHotkeysInFocus) {
+			config_set_string(userConfig, "General",
 					  "HotkeyFocusType",
 					  "DisableHotkeysInFocus");
-		changed = true;
+		}
+
+		hasChanges = true;
+	}
+
+	return hasChanges;
+}
+
+static constexpr string_view OBSGlobalIniPath = "/obs-studio/global.ini";
+static constexpr string_view OBSUserIniPath = "/obs-studio/user.ini";
+
+bool OBSApp::MigrateGlobalSettings()
+{
+	char path[512];
+
+	int len = GetAppConfigPath(path, sizeof(path), nullptr);
+	if (len <= 0) {
+		OBSErrorBox(nullptr,
+			    "Unable to get global configuration path.");
+		return false;
 	}
 
-	if (changed)
-		config_save_safe(globalConfig, "tmp", nullptr);
+	std::string legacyConfigFileString;
+	legacyConfigFileString.reserve(strlen(path) + OBSGlobalIniPath.size());
+	legacyConfigFileString.append(path).append(OBSGlobalIniPath);
 
-	return InitGlobalConfigDefaults();
+	const std::filesystem::path legacyGlobalConfigFile =
+		std::filesystem::u8path(legacyConfigFileString);
+
+	std::string configFileString;
+	configFileString.reserve(strlen(path) + OBSUserIniPath.size());
+	configFileString.append(path).append(OBSUserIniPath);
+
+	const std::filesystem::path userConfigFile =
+		std::filesystem::u8path(configFileString);
+
+	if (std::filesystem::exists(userConfigFile)) {
+		OBSErrorBox(
+			nullptr,
+			"Unable to migrate global configuration - user configuration file already exists.");
+		return false;
+	}
+
+	std::filesystem::copy(legacyGlobalConfigFile, userConfigFile);
+
+	return true;
 }
 
 bool OBSApp::InitLocale()
 {
 	ProfileScope("OBSApp::InitLocale");
 
-	const char *lang =
-		config_get_string(globalConfig, "General", "Language");
+	const char *lang = config_get_string(userConfig, "General", "Language");
 	bool userLocale =
-		config_has_user_value(globalConfig, "General", "Language");
+		config_has_user_value(userConfig, "General", "Language");
 	if (!userLocale || !lang || lang[0] == '\0')
 		lang = DEFAULT_LANG;
 
@@ -963,7 +1078,7 @@ bool LoadBranchesFile(vector<UpdateBranch> &out)
 	string branchesText;
 
 	BPtr<char> branchesFilePath =
-		GetConfigPathPtr("obs-studio/updates/branches.json");
+		GetAppConfigPathPtr("obs-studio/updates/branches.json");
 
 	QFile branchesFile(branchesFilePath.Get());
 	if (!branchesFile.open(QIODevice::ReadOnly)) {
@@ -1069,7 +1184,7 @@ OBSApp::~OBSApp()
 {
 #ifdef _WIN32
 	bool disableAudioDucking =
-		config_get_bool(globalConfig, "Audio", "DisableAudioDucking");
+		config_get_bool(userConfig, "Audio", "DisableAudioDucking");
 	if (disableAudioDucking)
 		DisableAudioDucking(false);
 #else
@@ -1080,9 +1195,9 @@ OBSApp::~OBSApp()
 
 #ifdef __APPLE__
 	bool vsyncDisabled =
-		config_get_bool(globalConfig, "Video", "DisableOSXVSync");
+		config_get_bool(userConfig, "Video", "DisableOSXVSync");
 	bool resetVSync =
-		config_get_bool(globalConfig, "Video", "ResetOSXVSyncOnExit");
+		config_get_bool(userConfig, "Video", "ResetOSXVSyncOnExit");
 	if (vsyncDisabled && resetVSync)
 		EnableOSXVSync(true);
 #endif
@@ -1188,40 +1303,40 @@ void OBSApp::AppInit()
 	if (!InitTheme())
 		throw "Failed to load theme";
 
-	config_set_default_string(globalConfig, "Basic", "Profile",
+	config_set_default_string(userConfig, "Basic", "Profile",
 				  Str("Untitled"));
-	config_set_default_string(globalConfig, "Basic", "ProfileDir",
+	config_set_default_string(userConfig, "Basic", "ProfileDir",
 				  Str("Untitled"));
-	config_set_default_string(globalConfig, "Basic", "SceneCollection",
+	config_set_default_string(userConfig, "Basic", "SceneCollection",
 				  Str("Untitled"));
-	config_set_default_string(globalConfig, "Basic", "SceneCollectionFile",
+	config_set_default_string(userConfig, "Basic", "SceneCollectionFile",
 				  Str("Untitled"));
-	config_set_default_bool(globalConfig, "Basic", "ConfigOnNewProfile",
+	config_set_default_bool(userConfig, "Basic", "ConfigOnNewProfile",
 				true);
 
-	if (!config_has_user_value(globalConfig, "Basic", "Profile")) {
-		config_set_string(globalConfig, "Basic", "Profile",
+	if (!config_has_user_value(userConfig, "Basic", "Profile")) {
+		config_set_string(userConfig, "Basic", "Profile",
 				  Str("Untitled"));
-		config_set_string(globalConfig, "Basic", "ProfileDir",
+		config_set_string(userConfig, "Basic", "ProfileDir",
 				  Str("Untitled"));
 	}
 
-	if (!config_has_user_value(globalConfig, "Basic", "SceneCollection")) {
-		config_set_string(globalConfig, "Basic", "SceneCollection",
+	if (!config_has_user_value(userConfig, "Basic", "SceneCollection")) {
+		config_set_string(userConfig, "Basic", "SceneCollection",
 				  Str("Untitled"));
-		config_set_string(globalConfig, "Basic", "SceneCollectionFile",
+		config_set_string(userConfig, "Basic", "SceneCollectionFile",
 				  Str("Untitled"));
 	}
 
 #ifdef _WIN32
 	bool disableAudioDucking =
-		config_get_bool(globalConfig, "Audio", "DisableAudioDucking");
+		config_get_bool(userConfig, "Audio", "DisableAudioDucking");
 	if (disableAudioDucking)
 		DisableAudioDucking(true);
 #endif
 
 #ifdef __APPLE__
-	if (config_get_bool(globalConfig, "Video", "DisableOSXVSync"))
+	if (config_get_bool(userConfig, "Video", "DisableOSXVSync"))
 		EnableOSXVSync(false);
 #endif
 
@@ -1237,7 +1352,7 @@ void OBSApp::AppInit()
 const char *OBSApp::GetRenderModule() const
 {
 	const char *renderer =
-		config_get_string(globalConfig, "Video", "Renderer");
+		config_get_string(userConfig, "Video", "Renderer");
 
 	return (astrcmpi(renderer, "Direct3D 11") == 0) ? DL_D3D11 : DL_OPENGL;
 }
@@ -1246,7 +1361,8 @@ static bool StartupOBS(const char *locale, profiler_name_store_t *store)
 {
 	char path[512];
 
-	if (GetConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0)
+	if (GetAppConfigPath(path, sizeof(path), "obs-studio/plugin_config") <=
+	    0)
 		return false;
 
 	return obs_startup(locale, path, store);
@@ -1265,7 +1381,7 @@ void OBSApp::UpdateHotkeyFocusSetting(bool resetState)
 	enableHotkeysOutOfFocus = true;
 
 	const char *hotkeyFocusType =
-		config_get_string(globalConfig, "General", "HotkeyFocusType");
+		config_get_string(userConfig, "General", "HotkeyFocusType");
 
 	if (astrcmpi(hotkeyFocusType, "DisableHotkeysInFocus") == 0) {
 		enableHotkeysInFocus = false;
@@ -1341,7 +1457,7 @@ bool OBSApp::OBSInit()
 
 #if defined(_WIN32) || defined(__APPLE__)
 	bool browserHWAccel =
-		config_get_bool(globalConfig, "General", "BrowserHWAccel");
+		config_get_bool(userConfig, "General", "BrowserHWAccel");
 
 	OBSDataAutoRelease settings = obs_data_create();
 	obs_data_set_bool(settings, "BrowserHWAccel", browserHWAccel);
@@ -1354,7 +1470,7 @@ bool OBSApp::OBSInit()
 	     browserHWAccel ? "true" : "false");
 #endif
 #ifdef _WIN32
-	bool hideFromCapture = config_get_bool(globalConfig, "BasicWindow",
+	bool hideFromCapture = config_get_bool(userConfig, "BasicWindow",
 					       "HideOBSWindowsFromCapture");
 	blog(LOG_INFO, "Hide OBS windows from screen capture: %s",
 	     hideFromCapture ? "true" : "false");
@@ -1597,13 +1713,13 @@ static uint64_t convert_log_name(bool has_prefix, const char *name)
 
 static void delete_oldest_file(bool has_prefix, const char *location)
 {
-	BPtr<char> logDir(GetConfigPathPtr(location));
+	BPtr<char> logDir(GetAppConfigPathPtr(location));
 	string oldestLog;
 	uint64_t oldest_ts = (uint64_t)-1;
 	struct os_dirent *entry;
 
 	unsigned int maxLogs = (unsigned int)config_get_uint(
-		App()->GlobalConfig(), "General", "MaxLogs");
+		App()->GetUserConfig(), "General", "MaxLogs");
 
 	os_dir_t *dir = os_opendir(logDir);
 	if (dir) {
@@ -1640,7 +1756,7 @@ static void delete_oldest_file(bool has_prefix, const char *location)
 static void get_last_log(bool has_prefix, const char *subdir_to_use,
 			 std::string &last)
 {
-	BPtr<char> logDir(GetConfigPathPtr(subdir_to_use));
+	BPtr<char> logDir(GetAppConfigPathPtr(subdir_to_use));
 	struct os_dirent *entry;
 	os_dir_t *dir = os_opendir(logDir);
 	uint64_t highest_ts = 0;
@@ -1866,7 +1982,7 @@ static void create_log_file(fstream &logFile)
 	currentLogFile = GenerateTimeDateFilename("txt");
 	dst << "obs-studio/logs/" << currentLogFile.c_str();
 
-	BPtr<char> path(GetConfigPathPtr(dst.str().c_str()));
+	BPtr<char> path(GetAppConfigPathPtr(dst.str().c_str()));
 
 #ifdef _WIN32
 	BPtr<wchar_t> wpath;
@@ -1925,7 +2041,7 @@ static void SaveProfilerData(const ProfilerSnapshot &snap)
 	dst.write(LITERAL_SIZE(".csv.gz"));
 #undef LITERAL_SIZE
 
-	BPtr<char> path = GetConfigPathPtr(dst.str().c_str());
+	BPtr<char> path = GetAppConfigPathPtr(dst.str().c_str());
 	if (!profiler_snapshot_dump_csv_gz(snap.get(), path))
 		blog(LOG_WARNING, "Could not save profiler data to '%s'",
 		     static_cast<const char *>(path));
@@ -2160,7 +2276,7 @@ static int run_program(fstream &logFile, int argc, char *argv[])
 			CheckPermission(kScreenCapture);
 
 		int permissionsDialogLastShown =
-			config_get_int(GetGlobalConfig(), "General",
+			config_get_int(App()->GetAppConfig(), "General",
 				       "MacOSPermissionsDialogLastShown");
 		if (permissionsDialogLastShown <
 		    MACOS_PERMISSIONS_DIALOG_VERSION) {
@@ -2246,7 +2362,7 @@ static void main_crash_handler(const char *format, va_list args,
 	string name = crashFilePath + "/";
 	name += "Crash " + GenerateTimeDateFilename("txt");
 
-	BPtr<char> path(GetConfigPathPtr(name.c_str()));
+	BPtr<char> path(GetAppConfigPathPtr(name.c_str()));
 
 	fstream file;
 
@@ -2350,7 +2466,7 @@ static void load_debug_privilege(void)
 #define ALLOW_PORTABLE_MODE 0
 #endif
 
-int GetConfigPath(char *path, size_t size, const char *name)
+int GetAppConfigPath(char *path, size_t size, const char *name)
 {
 #if ALLOW_PORTABLE_MODE
 	if (portable_mode) {
@@ -2367,7 +2483,7 @@ int GetConfigPath(char *path, size_t size, const char *name)
 #endif
 }
 
-char *GetConfigPathPtr(const char *name)
+char *GetAppConfigPathPtr(const char *name)
 {
 #if ALLOW_PORTABLE_MODE
 	if (portable_mode) {
@@ -2511,7 +2627,7 @@ static void check_safe_mode_sentinel(void)
 	if (disable_shutdown_check)
 		return;
 
-	BPtr sentinelPath = GetConfigPathPtr("obs-studio/safe_mode");
+	BPtr sentinelPath = GetAppConfigPathPtr("obs-studio/safe_mode");
 	if (os_file_exists(sentinelPath)) {
 		unclean_shutdown = true;
 		return;
@@ -2523,8 +2639,12 @@ static void check_safe_mode_sentinel(void)
 
 static void delete_safe_mode_sentinel(void)
 {
-	BPtr sentinelPath = GetConfigPathPtr("obs-studio/safe_mode");
+#ifndef NDEBUG
+	return;
+#else
+	BPtr sentinelPath = GetAppConfigPathPtr("obs-studio/safe_mode");
 	os_unlink(sentinelPath);
+#endif
 }
 
 #ifndef _WIN32

+ 19 - 16
UI/obs-app.hpp

@@ -38,6 +38,7 @@
 #include <memory>
 #include <vector>
 #include <deque>
+#include <filesystem>
 
 #include "window-main.hpp"
 #include "obs-app-theming.hpp"
@@ -91,7 +92,8 @@ class OBSApp : public QApplication {
 private:
 	std::string locale;
 
-	ConfigFile globalConfig;
+	ConfigFile appConfig;
+	ConfigFile userConfig;
 	TextLookup textLookup;
 	QPointer<OBSMainWindow> mainWindow;
 	profiler_name_store_t *profilerNameStore = nullptr;
@@ -112,6 +114,15 @@ private:
 
 	bool InitGlobalConfig();
 	bool InitGlobalConfigDefaults();
+	bool InitGlobalLocationDefaults();
+
+	bool MigrateGlobalSettings();
+	bool MigrateLegacySettings(uint32_t lastVersion);
+
+	bool InitUserConfig(std::filesystem::path &userConfigLocation,
+			    uint32_t lastVersion);
+	void InitUserConfigDefaults();
+
 	bool InitLocale();
 	bool InitTheme();
 
@@ -154,7 +165,11 @@ public:
 
 	inline QMainWindow *GetMainWindow() const { return mainWindow.data(); }
 
-	inline config_t *GlobalConfig() const { return globalConfig; }
+	inline config_t *GetAppConfig() const { return appConfig; }
+	inline config_t *GetUserConfig() const { return userConfig; }
+	std::filesystem::path userConfigLocation;
+	std::filesystem::path userScenesLocation;
+	std::filesystem::path userProfilesLocation;
 
 	inline const char *GetLocale() const { return locale.c_str(); }
 
@@ -235,8 +250,8 @@ signals:
 	void StyleChanged();
 };
 
-int GetConfigPath(char *path, size_t size, const char *name);
-char *GetConfigPathPtr(const char *name);
+int GetAppConfigPath(char *path, size_t size, const char *name);
+char *GetAppConfigPathPtr(const char *name);
 
 int GetProgramDataPath(char *path, size_t size, const char *name);
 char *GetProgramDataPathPtr(const char *name);
@@ -246,11 +261,6 @@ inline OBSApp *App()
 	return static_cast<OBSApp *>(qApp);
 }
 
-inline config_t *GetGlobalConfig()
-{
-	return App()->GlobalConfig();
-}
-
 std::vector<std::pair<std::string, std::string>> GetLocaleNames();
 inline const char *Str(const char *lookup)
 {
@@ -267,13 +277,6 @@ bool GetUnusedSceneCollectionFile(std::string &name, std::string &file);
 
 bool WindowPositionValid(QRect rect);
 
-static inline int GetProfilePath(char *path, size_t size, const char *file)
-{
-	OBSMainWindow *window =
-		reinterpret_cast<OBSMainWindow *>(App()->GetMainWindow());
-	return window->GetProfilePath(path, size, file);
-}
-
 extern bool portable_mode;
 extern bool steam;
 extern bool safe_mode;

+ 14 - 2
UI/obs-frontend-api/obs-frontend-api.cpp

@@ -394,12 +394,24 @@ config_t *obs_frontend_get_profile_config(void)
 				   : nullptr;
 }
 
-config_t *obs_frontend_get_global_config(void)
+config_t *obs_frontend_get_app_config(void)
+{
+	return !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr;
+}
+
+config_t *obs_frontend_get_user_config(void)
 {
-	return !!callbacks_valid() ? c->obs_frontend_get_global_config()
+	return !!callbacks_valid() ? c->obs_frontend_get_user_config()
 				   : nullptr;
 }
 
+config_t *obs_frontend_get_global_config(void)
+{
+	blog(LOG_WARNING,
+	     "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead.");
+	return !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr;
+}
+
 void obs_frontend_open_projector(const char *type, int monitor,
 				 const char *geometry, const char *name)
 {

+ 3 - 1
UI/obs-frontend-api/obs-frontend-api.h

@@ -208,7 +208,9 @@ EXPORT obs_output_t *obs_frontend_get_recording_output(void);
 EXPORT obs_output_t *obs_frontend_get_replay_buffer_output(void);
 
 EXPORT config_t *obs_frontend_get_profile_config(void);
-EXPORT config_t *obs_frontend_get_global_config(void);
+OBS_DEPRECATED EXPORT config_t *obs_frontend_get_global_config(void);
+EXPORT config_t *obs_frontend_get_app_config(void);
+EXPORT config_t *obs_frontend_get_user_config(void);
 
 EXPORT void obs_frontend_set_streaming_service(obs_service_t *service);
 EXPORT obs_service_t *obs_frontend_get_streaming_service(void);

+ 5 - 1
UI/obs-frontend-api/obs-frontend-internal.hpp

@@ -86,7 +86,11 @@ struct obs_frontend_callbacks {
 	virtual obs_output_t *obs_frontend_get_replay_buffer_output(void) = 0;
 
 	virtual config_t *obs_frontend_get_profile_config(void) = 0;
-	virtual config_t *obs_frontend_get_global_config(void) = 0;
+	OBS_DEPRECATED virtual config_t *
+	obs_frontend_get_global_config(void) = 0;
+
+	virtual config_t *obs_frontend_get_app_config(void) = 0;
+	virtual config_t *obs_frontend_get_user_config(void) = 0;
 
 	virtual void obs_frontend_open_projector(const char *type, int monitor,
 						 const char *geometry,

+ 1 - 1
UI/platform-windows.cpp

@@ -307,7 +307,7 @@ RunOnceMutex CheckIfAlreadyRunning(bool &already_running)
 		char absPath[512];
 		*path = 0;
 		*absPath = 0;
-		GetConfigPath(path, sizeof(path), "");
+		GetAppConfigPath(path, sizeof(path), "");
 		os_get_abs_path(path, absPath, sizeof(absPath));
 		name = "OBSStudioPortable";
 		name += absPath;

+ 4 - 4
UI/update/mac-update.cpp

@@ -18,8 +18,8 @@ static const char *MAC_DEFAULT_BRANCH = "stable";
 
 bool GetBranch(std::string &selectedBranch)
 {
-	const char *config_branch =
-		config_get_string(GetGlobalConfig(), "General", "UpdateBranch");
+	const char *config_branch = config_get_string(
+		App()->GetAppConfig(), "General", "UpdateBranch");
 	if (!config_branch)
 		return true;
 
@@ -70,8 +70,8 @@ try {
 	 * Validate branch selection           */
 
 	if (!GetBranch(branch)) {
-		config_set_string(GetGlobalConfig(), "General", "UpdateBranch",
-				  MAC_DEFAULT_BRANCH);
+		config_set_string(App()->GetAppConfig(), "General",
+				  "UpdateBranch", MAC_DEFAULT_BRANCH);
 		info(QTStr("Updater.BranchNotFound.Title"),
 		     QTStr("Updater.BranchNotFound.Text"));
 	}

+ 4 - 4
UI/update/shared-update.cpp

@@ -147,8 +147,8 @@ std::string GetProgramGUID()
 	/* NOTE: this is an arbitrary random number that we use to count the
 	 * number of unique OBS installations and is not associated with any
 	 * kind of identifiable information */
-	const char *pguid =
-		config_get_string(GetGlobalConfig(), "General", "InstallGUID");
+	const char *pguid = config_get_string(App()->GetAppConfig(), "General",
+					      "InstallGUID");
 	std::string guid;
 	if (pguid)
 		guid = pguid;
@@ -157,7 +157,7 @@ std::string GetProgramGUID()
 		GenerateGUID(guid);
 
 		if (!guid.empty())
-			config_set_string(GetGlobalConfig(), "General",
+			config_set_string(App()->GetAppConfig(), "General",
 					  "InstallGUID", guid.c_str());
 	}
 
@@ -220,7 +220,7 @@ bool FetchAndVerifyFile(const char *name, const char *file, const char *url,
 	uint8_t fileHash[BLAKE2_HASH_LENGTH];
 	bool success;
 
-	BPtr<char> filePath = GetConfigPathPtr(file);
+	BPtr<char> filePath = GetAppConfigPathPtr(file);
 
 	if (!extraHeaders.empty()) {
 		headers.insert(headers.end(), extraHeaders.begin(),

+ 10 - 10
UI/update/win-update.cpp

@@ -112,8 +112,8 @@ try {
 
 bool GetBranchAndUrl(string &selectedBranch, string &manifestUrl)
 {
-	const char *config_branch =
-		config_get_string(GetGlobalConfig(), "General", "UpdateBranch");
+	const char *config_branch = config_get_string(
+		App()->GetAppConfig(), "General", "UpdateBranch");
 	if (!config_branch)
 		return true;
 
@@ -219,8 +219,8 @@ try {
 	 * check branch and get manifest url   */
 
 	if (!GetBranchAndUrl(branch, manifestUrl)) {
-		config_set_string(GetGlobalConfig(), "General", "UpdateBranch",
-				  WIN_DEFAULT_BRANCH);
+		config_set_string(App()->GetAppConfig(), "General",
+				  "UpdateBranch", WIN_DEFAULT_BRANCH);
 		info(QTStr("Updater.BranchNotFound.Title"),
 		     QTStr("Updater.BranchNotFound.Text"));
 	}
@@ -264,7 +264,7 @@ try {
 	 * skip this version if set to skip    */
 
 	const char *skipUpdateVer = config_get_string(
-		GetGlobalConfig(), "General", "SkipUpdateVersion");
+		App()->GetAppConfig(), "General", "SkipUpdateVersion");
 	if (!manualUpdate && !repairMode && skipUpdateVer &&
 	    updateVer == skipUpdateVer)
 		return;
@@ -288,13 +288,13 @@ try {
 		if (queryResult == OBSUpdate::No) {
 			if (!manualUpdate) {
 				long long t = (long long)time(nullptr);
-				config_set_int(GetGlobalConfig(), "General",
+				config_set_int(App()->GetAppConfig(), "General",
 					       "LastUpdateCheck", t);
 			}
 			return;
 
 		} else if (queryResult == OBSUpdate::Skip) {
-			config_set_string(GetGlobalConfig(), "General",
+			config_set_string(App()->GetAppConfig(), "General",
 					  "SkipUpdateVersion",
 					  updateVer.c_str());
 			return;
@@ -314,7 +314,7 @@ try {
 	 * execute updater                     */
 
 	BPtr<char> updateFilePath =
-		GetConfigPathPtr("obs-studio\\updates\\updater.exe");
+		GetAppConfigPathPtr("obs-studio\\updates\\updater.exe");
 	BPtr<wchar_t> wUpdateFilePath;
 
 	size_t size = os_utf8_to_wcs_ptr(updateFilePath, 0, &wUpdateFilePath);
@@ -366,8 +366,8 @@ try {
 
 	/* force OBS to perform another update check immediately after updating
 	 * in case of issues with the new version */
-	config_set_int(GetGlobalConfig(), "General", "LastUpdateCheck", 0);
-	config_set_string(GetGlobalConfig(), "General", "SkipUpdateVersion",
+	config_set_int(App()->GetAppConfig(), "General", "LastUpdateCheck", 0);
+	config_set_string(App()->GetAppConfig(), "General", "SkipUpdateVersion",
 			  "0");
 
 	QMetaObject::invokeMethod(App()->GetMainWindow(), "close");

+ 20 - 17
UI/volume-control.cpp

@@ -61,9 +61,10 @@ static void ShowUnassignedWarning(const char *name)
 		msgbox.exec();
 
 		if (cb->isChecked()) {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"WarnedAboutUnassignedSources", true);
-			config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+			config_save_safe(App()->GetUserConfig(), "tmp",
+					 nullptr);
 		}
 	};
 
@@ -150,7 +151,7 @@ void VolControl::SetMuted(bool)
 		mute->setCheckState(Qt::PartiallyChecked);
 		/* Show notice about the source no being assigned to any tracks */
 		bool has_shown_warning =
-			config_get_bool(App()->GlobalConfig(), "General",
+			config_get_bool(App()->GetUserConfig(), "General",
 					"WarnedAboutUnassignedSources");
 		if (!has_shown_warning)
 			ShowUnassignedWarning(obs_source_get_name(source));
@@ -459,10 +460,10 @@ void VolumeMeter::setBackgroundNominalColor(QColor c)
 {
 	p_backgroundNominalColor = std::move(c);
 
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
 		backgroundNominalColor = color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "MixerGreen"));
+			App()->GetUserConfig(), "Accessibility", "MixerGreen"));
 	} else {
 		backgroundNominalColor = p_backgroundNominalColor;
 	}
@@ -487,10 +488,11 @@ void VolumeMeter::setBackgroundWarningColor(QColor c)
 {
 	p_backgroundWarningColor = std::move(c);
 
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
-		backgroundWarningColor = color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "MixerYellow"));
+		backgroundWarningColor = color_from_int(
+			config_get_int(App()->GetUserConfig(), "Accessibility",
+				       "MixerYellow"));
 	} else {
 		backgroundWarningColor = p_backgroundWarningColor;
 	}
@@ -515,10 +517,10 @@ void VolumeMeter::setBackgroundErrorColor(QColor c)
 {
 	p_backgroundErrorColor = std::move(c);
 
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
 		backgroundErrorColor = color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "MixerRed"));
+			App()->GetUserConfig(), "Accessibility", "MixerRed"));
 	} else {
 		backgroundErrorColor = p_backgroundErrorColor;
 	}
@@ -543,10 +545,10 @@ void VolumeMeter::setForegroundNominalColor(QColor c)
 {
 	p_foregroundNominalColor = std::move(c);
 
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
 		foregroundNominalColor = color_from_int(
-			config_get_int(GetGlobalConfig(), "Accessibility",
+			config_get_int(App()->GetUserConfig(), "Accessibility",
 				       "MixerGreenActive"));
 	} else {
 		foregroundNominalColor = p_foregroundNominalColor;
@@ -572,10 +574,10 @@ void VolumeMeter::setForegroundWarningColor(QColor c)
 {
 	p_foregroundWarningColor = std::move(c);
 
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
 		foregroundWarningColor = color_from_int(
-			config_get_int(GetGlobalConfig(), "Accessibility",
+			config_get_int(App()->GetUserConfig(), "Accessibility",
 				       "MixerYellowActive"));
 	} else {
 		foregroundWarningColor = p_foregroundWarningColor;
@@ -601,10 +603,11 @@ void VolumeMeter::setForegroundErrorColor(QColor c)
 {
 	p_foregroundErrorColor = std::move(c);
 
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
-		foregroundErrorColor = color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "MixerRedActive"));
+		foregroundErrorColor = color_from_int(
+			config_get_int(App()->GetUserConfig(), "Accessibility",
+				       "MixerRedActive"));
 	} else {
 		foregroundErrorColor = p_foregroundErrorColor;
 	}

+ 3 - 3
UI/window-basic-adv-audio.cpp

@@ -24,7 +24,7 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent)
 	sigs.emplace_back(sh, "source_deactivate", OBSSourceRemoved, this);
 
 	VolumeType volType = (VolumeType)config_get_int(
-		GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType");
+		App()->GetUserConfig(), "BasicWindow", "AdvAudioVolumeType");
 
 	if (volType == VolumeType::Percent)
 		ui->usePercent->setChecked(true);
@@ -140,8 +140,8 @@ void OBSBasicAdvAudio::on_usePercent_toggled(bool checked)
 	for (size_t i = 0; i < controls.size(); i++)
 		controls[i]->SetVolumeWidget(type);
 
-	config_set_int(GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType",
-		       (int)type);
+	config_set_int(App()->GetUserConfig(), "BasicWindow",
+		       "AdvAudioVolumeType", (int)type);
 }
 
 void OBSBasicAdvAudio::on_activeOnly_toggled(bool checked)

+ 6 - 6
UI/window-basic-interaction.cpp

@@ -44,10 +44,10 @@ OBSBasicInteraction::OBSBasicInteraction(QWidget *parent, OBSSource source_)
 			OBSBasicInteraction::SourceRenamed, this),
 	  eventFilter(BuildEventFilter())
 {
-	int cx = (int)config_get_int(App()->GlobalConfig(), "InteractionWindow",
-				     "cx");
-	int cy = (int)config_get_int(App()->GlobalConfig(), "InteractionWindow",
-				     "cy");
+	int cx = (int)config_get_int(App()->GetUserConfig(),
+				     "InteractionWindow", "cx");
+	int cy = (int)config_get_int(App()->GetUserConfig(),
+				     "InteractionWindow", "cy");
 
 	Qt::WindowFlags flags = windowFlags();
 	Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;
@@ -166,9 +166,9 @@ void OBSBasicInteraction::closeEvent(QCloseEvent *event)
 	if (!event->isAccepted())
 		return;
 
-	config_set_int(App()->GlobalConfig(), "InteractionWindow", "cx",
+	config_set_int(App()->GetAppConfig(), "InteractionWindow", "cx",
 		       width());
-	config_set_int(App()->GlobalConfig(), "InteractionWindow", "cy",
+	config_set_int(App()->GetAppConfig(), "InteractionWindow", "cy",
 		       height());
 
 	obs_display_remove_draw_callback(ui->preview->GetDisplay(),

+ 6 - 4
UI/window-basic-main-outputs.cpp

@@ -1246,8 +1246,9 @@ bool SimpleOutput::IsVodTrackEnabled(obs_service_t *service)
 		config_get_bool(main->Config(), "SimpleOutput", "UseAdvanced");
 	bool enable = config_get_bool(main->Config(), "SimpleOutput",
 				      "VodTrackEnabled");
-	bool enableForCustomServer = config_get_bool(
-		GetGlobalConfig(), "General", "EnableCustomServerVodTrack");
+	bool enableForCustomServer =
+		config_get_bool(App()->GetUserConfig(), "General",
+				"EnableCustomServerVodTrack");
 
 	OBSDataAutoRelease settings = obs_service_get_settings(service);
 	const char *name = obs_data_get_string(settings, "service");
@@ -2252,8 +2253,9 @@ AdvancedOutput::VodTrackMixerIdx(obs_service_t *service)
 		config_get_bool(main->Config(), "AdvOut", "VodTrackEnabled");
 	int vodTrackIndex =
 		config_get_int(main->Config(), "AdvOut", "VodTrackIndex");
-	bool enableForCustomServer = config_get_bool(
-		GetGlobalConfig(), "General", "EnableCustomServerVodTrack");
+	bool enableForCustomServer =
+		config_get_bool(App()->GetUserConfig(), "General",
+				"EnableCustomServerVodTrack");
 
 	const char *id = obs_service_get_id(service);
 	if (strcmp(id, "rtmp_custom") == 0) {

+ 5 - 4
UI/window-basic-main-transitions.cpp

@@ -1839,10 +1839,11 @@ int OBSBasic::GetOverrideTransitionDuration(OBSSource source)
 
 void OBSBasic::UpdatePreviewProgramIndicators()
 {
-	bool labels = previewProgramMode ? config_get_bool(GetGlobalConfig(),
-							   "BasicWindow",
-							   "StudioModeLabels")
-					 : false;
+	bool labels = previewProgramMode
+			      ? config_get_bool(App()->GetUserConfig(),
+						"BasicWindow",
+						"StudioModeLabels")
+			      : false;
 
 	ui->previewLabel->setVisible(labels);
 

+ 177 - 160
UI/window-basic-main.cpp

@@ -191,11 +191,11 @@ static void AddExtraModulePaths()
 	int ret = GetProgramDataPath(base_module_dir, sizeof(base_module_dir),
 				     "obs-studio/plugins/%module%");
 #elif defined(__APPLE__)
-	int ret = GetConfigPath(base_module_dir, sizeof(base_module_dir),
-				"obs-studio/plugins/%module%.plugin");
+	int ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir),
+				   "obs-studio/plugins/%module%.plugin");
 #else
-	int ret = GetConfigPath(base_module_dir, sizeof(base_module_dir),
-				"obs-studio/plugins/%module%");
+	int ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir),
+				   "obs-studio/plugins/%module%");
 #endif
 
 	if (ret <= 0)
@@ -219,8 +219,8 @@ static void AddExtraModulePaths()
 
 	/* Legacy User Application Support Search Path */
 	char user_legacy_module_dir[PATH_MAX];
-	GetConfigPath(user_legacy_module_dir, sizeof(user_legacy_module_dir),
-		      "obs-studio/plugins/%module%");
+	GetAppConfigPath(user_legacy_module_dir, sizeof(user_legacy_module_dir),
+			 "obs-studio/plugins/%module%");
 	std::string path_user_legacy = user_legacy_module_dir;
 	obs_add_module_path((path_user_legacy + "/bin").c_str(),
 			    (path_user_legacy + "/data").c_str());
@@ -446,7 +446,7 @@ OBSBasic::OBSBasic(QWidget *parent)
 	ui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false);
 	ui->sources->setAttribute(Qt::WA_MacShowFocusRect, false);
 
-	bool sceneGrid = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	bool sceneGrid = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "gridMode");
 	ui->scenes->SetGridMode(sceneGrid);
 
@@ -602,7 +602,7 @@ OBSBasic::OBSBasic(QWidget *parent)
 	QPoint curPos;
 
 	//restore parent window geometry
-	const char *geometry = config_get_string(App()->GlobalConfig(),
+	const char *geometry = config_get_string(App()->GetUserConfig(),
 						 "BasicWindow", "geometry");
 	if (geometry != NULL) {
 		QByteArray byteArray =
@@ -726,7 +726,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
 	const char *programName = obs_source_get_name(curProgramScene);
 
 	const char *sceneCollection = config_get_string(
-		App()->GlobalConfig(), "Basic", "SceneCollection");
+		App()->GetUserConfig(), "Basic", "SceneCollection");
 
 	obs_data_set_string(saveData, "current_scene", sceneName);
 	obs_data_set_string(saveData, "current_program_scene", programName);
@@ -1244,12 +1244,16 @@ void OBSBasic::Load(const char *file, bool remigrate)
 			}
 		}
 
-		config_set_string(App()->GlobalConfig(), "Basic",
+		config_set_string(App()->GetUserConfig(), "Basic",
 				  "SceneCollection", name.c_str());
-		config_set_string(App()->GlobalConfig(), "Basic",
+		config_set_string(App()->GetUserConfig(), "Basic",
 				  "SceneCollectionFile", name.c_str());
 		blog(LOG_INFO, "No scene file found, creating default scene");
-		CreateDefaultScene(true);
+
+		bool hasFirstRun = config_get_bool(App()->GetUserConfig(),
+						   "General", "FirstRun");
+
+		CreateDefaultScene(!hasFirstRun);
 		SaveProject();
 		return;
 	}
@@ -1342,7 +1346,7 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate)
 		transitionName = obs_source_get_name(fadeTransition);
 
 	const char *curSceneCollection = config_get_string(
-		App()->GlobalConfig(), "Basic", "SceneCollection");
+		App()->GetUserConfig(), "Basic", "SceneCollection");
 
 	obs_data_set_default_string(data, "name", curSceneCollection);
 
@@ -1475,8 +1479,8 @@ retryScene:
 
 	/* ------------------- */
 
-	bool projectorSave = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					     "SaveProjectors");
+	bool projectorSave = config_get_bool(App()->GetUserConfig(),
+					     "BasicWindow", "SaveProjectors");
 
 	if (projectorSave) {
 		OBSDataArrayAutoRelease savedProjectors =
@@ -1494,10 +1498,10 @@ retryScene:
 	std::string file_base = strrchr(file, '/') + 1;
 	file_base.erase(file_base.size() - 5, 5);
 
-	config_set_string(App()->GlobalConfig(), "Basic", "SceneCollection",
+	config_set_string(App()->GetUserConfig(), "Basic", "SceneCollection",
 			  name);
-	config_set_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile",
-			  file_base.c_str());
+	config_set_string(App()->GetUserConfig(), "Basic",
+			  "SceneCollectionFile", file_base.c_str());
 
 	OBSDataArrayAutoRelease quickTransitionData =
 		obs_data_get_array(data, "quick_transitions");
@@ -1733,7 +1737,7 @@ bool OBSBasic::InitBasicConfigDefaults()
 	cy *= devicePixelRatioF();
 
 	bool oldResolutionDefaults = config_get_bool(
-		App()->GlobalConfig(), "General", "Pre19Defaults");
+		App()->GetUserConfig(), "General", "Pre19Defaults");
 
 	/* use 1920x1080 for new default base res if main monitor is above
 	 * 1920x1080, but don't apply for people from older builds -- only to
@@ -1774,7 +1778,7 @@ bool OBSBasic::InitBasicConfigDefaults()
 	/* ----------------------------------------------------- */
 	/* set twitch chat extensions to "both" if prev version  */
 	/* is under 24.1                                         */
-	if (config_get_bool(GetGlobalConfig(), "General", "Pre24.1Defaults") &&
+	if (config_get_bool(App()->GetUserConfig(), "General",
 	    !config_has_user_value(basicConfig, "Twitch", "AddonChoice")) {
 		config_set_int(basicConfig, "Twitch", "AddonChoice", 3);
 		changed = true;
@@ -2036,7 +2040,7 @@ extern bool EncoderAvailable(const char *encoder);
 
 void OBSBasic::InitBasicConfigDefaults2()
 {
-	bool oldEncDefaults = config_get_bool(App()->GlobalConfig(), "General",
+	bool oldEncDefaults = config_get_bool(App()->GetUserConfig(), "General",
 					      "Pre23Defaults");
 	bool useNV = EncoderAvailable("ffmpeg_nvenc") && !oldEncDefaults;
 
@@ -2370,14 +2374,14 @@ void OBSBasic::OBSInit()
 	InitPrimitives();
 
 	sceneDuplicationMode = config_get_bool(
-		App()->GlobalConfig(), "BasicWindow", "SceneDuplicationMode");
-	swapScenesMode = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+		App()->GetUserConfig(), "BasicWindow", "SceneDuplicationMode");
+	swapScenesMode = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "SwapScenesMode");
 	editPropertiesMode = config_get_bool(
-		App()->GlobalConfig(), "BasicWindow", "EditPropertiesMode");
+		App()->GetUserConfig(), "BasicWindow", "EditPropertiesMode");
 
 	if (!opt_studio_mode) {
-		SetPreviewProgramMode(config_get_bool(App()->GlobalConfig(),
+		SetPreviewProgramMode(config_get_bool(App()->GetUserConfig(),
 						      "BasicWindow",
 						      "PreviewProgramMode"));
 	} else {
@@ -2385,14 +2389,14 @@ void OBSBasic::OBSInit()
 		opt_studio_mode = false;
 	}
 
-#define SET_VISIBILITY(name, control)                                         \
-	do {                                                                  \
-		if (config_has_user_value(App()->GlobalConfig(),              \
-					  "BasicWindow", name)) {             \
-			bool visible = config_get_bool(App()->GlobalConfig(), \
-						       "BasicWindow", name);  \
-			ui->control->setChecked(visible);                     \
-		}                                                             \
+#define SET_VISIBILITY(name, control)                                          \
+	do {                                                                   \
+		if (config_has_user_value(App()->GetUserConfig(),              \
+					  "BasicWindow", name)) {              \
+			bool visible = config_get_bool(App()->GetUserConfig(), \
+						       "BasicWindow", name);   \
+			ui->control->setChecked(visible);                      \
+		}                                                              \
 	} while (false)
 
 	SET_VISIBILITY("ShowListboxToolbars", toggleListboxToolbars);
@@ -2400,11 +2404,11 @@ void OBSBasic::OBSInit()
 #undef SET_VISIBILITY
 
 	bool sourceIconsVisible = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "ShowSourceIcons");
+		App()->GetUserConfig(), "BasicWindow", "ShowSourceIcons");
 	ui->toggleSourceIcons->setChecked(sourceIconsVisible);
 
 	bool contextVisible = config_get_bool(
-		App()->GlobalConfig(), "BasicWindow", "ShowContextToolbars");
+		App()->GetUserConfig(), "BasicWindow", "ShowContextToolbars");
 	ui->toggleContextBar->setChecked(contextVisible);
 	ui->contextContainer->setVisible(contextVisible);
 	if (contextVisible)
@@ -2420,7 +2424,7 @@ void OBSBasic::OBSInit()
 
 	loaded = true;
 
-	previewEnabled = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	previewEnabled = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "PreviewEnabled");
 
 	if (!previewEnabled && !IsPreviewProgramMode())
@@ -2449,10 +2453,10 @@ void OBSBasic::OBSInit()
 
 	/* Show the main window, unless the tray icon isn't available
 	 * or neither the setting nor flag for starting minimized is set. */
-	bool sysTrayEnabled = config_get_bool(App()->GlobalConfig(),
+	bool sysTrayEnabled = config_get_bool(App()->GetUserConfig(),
 					      "BasicWindow", "SysTrayEnabled");
 	bool sysTrayWhenStarted = config_get_bool(
-		App()->GlobalConfig(), "BasicWindow", "SysTrayWhenStarted");
+		App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted");
 	bool hideWindowOnStart = QSystemTrayIcon::isSystemTrayAvailable() &&
 				 sysTrayEnabled &&
 				 (opt_minimize_tray || sysTrayWhenStarted);
@@ -2464,8 +2468,8 @@ void OBSBasic::OBSInit()
 		show();
 #endif
 
-	bool alwaysOnTop = config_get_bool(App()->GlobalConfig(), "BasicWindow",
-					   "AlwaysOnTop");
+	bool alwaysOnTop = config_get_bool(App()->GetUserConfig(),
+					   "BasicWindow", "AlwaysOnTop");
 
 #ifdef ENABLE_WAYLAND
 	bool isWayland = obs_get_nix_platform() == OBS_NIX_PLATFORM_WAYLAND;
@@ -2522,7 +2526,7 @@ void OBSBasic::OBSInit()
 #endif
 
 	const char *dockStateStr = config_get_string(
-		App()->GlobalConfig(), "BasicWindow", "DockState");
+		App()->GetUserConfig(), "BasicWindow", "DockState");
 
 	if (!dockStateStr) {
 		on_resetDocks_triggered(true);
@@ -2533,28 +2537,29 @@ void OBSBasic::OBSInit()
 			on_resetDocks_triggered(true);
 	}
 
-	bool pre23Defaults = config_get_bool(App()->GlobalConfig(), "General",
+	bool pre23Defaults = config_get_bool(App()->GetUserConfig(), "General",
 					     "Pre23Defaults");
 	if (pre23Defaults) {
 		bool resetDockLock23 = config_get_bool(
-			App()->GlobalConfig(), "General", "ResetDockLock23");
+			App()->GetUserConfig(), "General", "ResetDockLock23");
 		if (!resetDockLock23) {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"ResetDockLock23", true);
-			config_remove_value(App()->GlobalConfig(),
+			config_remove_value(App()->GetUserConfig(),
 					    "BasicWindow", "DocksLocked");
-			config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+			config_save_safe(App()->GetUserConfig(), "tmp",
+					 nullptr);
 		}
 	}
 
-	bool docksLocked = config_get_bool(App()->GlobalConfig(), "BasicWindow",
-					   "DocksLocked");
+	bool docksLocked = config_get_bool(App()->GetUserConfig(),
+					   "BasicWindow", "DocksLocked");
 	on_lockDocks_toggled(docksLocked);
 	ui->lockDocks->blockSignals(true);
 	ui->lockDocks->setChecked(docksLocked);
 	ui->lockDocks->blockSignals(false);
 
-	bool sideDocks = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	bool sideDocks = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "SideDocks");
 	on_sideDocks_toggled(sideDocks);
 	ui->sideDocks->blockSignals(true);
@@ -2569,15 +2574,15 @@ void OBSBasic::OBSInit()
 	disableColorSpaceConversion(this);
 #endif
 
-	bool has_last_version = config_has_user_value(App()->GlobalConfig(),
+	bool has_last_version = config_has_user_value(App()->GetUserConfig(),
 						      "General", "LastVersion");
 	bool first_run =
-		config_get_bool(App()->GlobalConfig(), "General", "FirstRun");
+		config_get_bool(App()->GetUserConfig(), "General", "FirstRun");
 
 	if (!first_run) {
-		config_set_bool(App()->GlobalConfig(), "General", "FirstRun",
+		config_set_bool(App()->GetUserConfig(), "General", "FirstRun",
 				true);
-		config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+		config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
 	}
 
 	if (!first_run && !has_last_version && !Active())
@@ -2587,18 +2592,18 @@ void OBSBasic::OBSInit()
 #if (defined(_WIN32) || defined(__APPLE__)) && \
 	(OBS_RELEASE_CANDIDATE > 0 || OBS_BETA > 0)
 	/* Automatically set branch to "beta" the first time a pre-release build is run. */
-	if (!config_get_bool(App()->GlobalConfig(), "General",
+	if (!config_get_bool(App()->GetUserConfig(), "General",
 			     "AutoBetaOptIn")) {
-		config_set_string(App()->GlobalConfig(), "General",
+		config_set_string(App()->GetUserConfig(), "General",
 				  "UpdateBranch", "beta");
-		config_set_bool(App()->GlobalConfig(), "General",
+		config_set_bool(App()->GetUserConfig(), "General",
 				"AutoBetaOptIn", true);
-		config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+		config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
 	}
 #endif
 	TimedCheckForUpdates();
 
-	ToggleMixerLayout(config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	ToggleMixerLayout(config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					  "VerticalVolControl"));
 
 	if (config_get_bool(basicConfig, "General", "OpenStatsOnStartup"))
@@ -2707,7 +2712,7 @@ void OBSBasic::OnFirstLoad()
 	Auth::Load();
 
 	bool showLogViewerOnStartup = config_get_bool(
-		App()->GlobalConfig(), "LogViewer", "ShowLogStartup");
+		App()->GetUserConfig(), "LogViewer", "ShowLogStartup");
 
 	if (showLogViewerOnStartup)
 		on_actionViewCurrentLog_triggered();
@@ -2775,28 +2780,28 @@ void OBSBasic::ReceivedIntroJson(const QString &text)
 	constexpr uint64_t currentVersion = (uint64_t)LIBOBS_API_VER << 16ULL |
 					    OBS_RELEASE_CANDIDATE << 8ULL |
 					    OBS_BETA;
-	uint64_t lastVersion = config_get_uint(App()->GlobalConfig(), "General",
+	uint64_t lastVersion = config_get_uint(App()->GetAppConfig(), "General",
 					       lastInfoVersion);
 	int current_version_increment = -1;
 
 	if ((lastVersion & ~0xFFFF0000ULL) <
 	    (currentVersion & ~0xFFFF0000ULL)) {
-		config_set_int(App()->GlobalConfig(), "General",
+		config_set_int(App()->GetAppConfig(), "General",
 			       "InfoIncrement", -1);
-		config_set_uint(App()->GlobalConfig(), "General",
+		config_set_uint(App()->GetAppConfig(), "General",
 				lastInfoVersion, currentVersion);
 	} else {
 		current_version_increment = config_get_int(
-			App()->GlobalConfig(), "General", "InfoIncrement");
+			App()->GetAppConfig(), "General", "InfoIncrement");
 	}
 
 	if (info_increment <= current_version_increment) {
 		return;
 	}
 
-	config_set_int(App()->GlobalConfig(), "General", "InfoIncrement",
+	config_set_int(App()->GetAppConfig(), "General", "InfoIncrement",
 		       info_increment);
-	config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+	config_save_safe(App()->GetAppConfig(), "tmp", nullptr);
 
 	cef->init_browser();
 
@@ -3284,26 +3289,27 @@ OBSBasic::~OBSBasic()
 	 * expect or want it to. */
 	QApplication::sendPostedEvents(nullptr);
 
-	config_set_int(App()->GlobalConfig(), "General", "LastVersion",
+	config_set_int(App()->GetAppConfig(), "General", "LastVersion",
 		       LIBOBS_API_VER);
+	config_save_safe(App()->GetAppConfig(), "tmp", nullptr);
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "PreviewEnabled",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "PreviewEnabled",
 			previewEnabled);
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "AlwaysOnTop",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "AlwaysOnTop",
 			ui->actionAlwaysOnTop->isChecked());
-	config_set_bool(App()->GlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"SceneDuplicationMode", sceneDuplicationMode);
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "SwapScenesMode",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "SwapScenesMode",
 			swapScenesMode);
-	config_set_bool(App()->GlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"EditPropertiesMode", editPropertiesMode);
-	config_set_bool(App()->GlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"PreviewProgramMode", IsPreviewProgramMode());
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "DocksLocked",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "DocksLocked",
 			ui->lockDocks->isChecked());
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "SideDocks",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "SideDocks",
 			ui->sideDocks->isChecked());
-	config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+	config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
 
 #ifdef BROWSER_AVAILABLE
 	DestroyPanelCookieManager();
@@ -4027,7 +4033,7 @@ void OBSBasic::VolControlContextMenu()
 	QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this);
 	toggleControlLayoutAction.setCheckable(true);
 	toggleControlLayoutAction.setChecked(config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "VerticalVolControl"));
+		App()->GetUserConfig(), "BasicWindow", "VerticalVolControl"));
 
 	/* ------------------- */
 
@@ -4126,7 +4132,7 @@ void OBSBasic::StackedMixerAreaContextMenuRequested()
 	QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this);
 	toggleControlLayoutAction.setCheckable(true);
 	toggleControlLayoutAction.setChecked(config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "VerticalVolControl"));
+		App()->GetUserConfig(), "BasicWindow", "VerticalVolControl"));
 
 	/* ------------------- */
 
@@ -4166,10 +4172,10 @@ void OBSBasic::ToggleMixerLayout(bool vertical)
 
 void OBSBasic::ToggleVolControlLayout()
 {
-	bool vertical = !config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool vertical = !config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "VerticalVolControl");
-	config_set_bool(GetGlobalConfig(), "BasicWindow", "VerticalVolControl",
-			vertical);
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
+			"VerticalVolControl", vertical);
 	ToggleMixerLayout(vertical);
 
 	// We need to store it so we can delete current and then add
@@ -4193,7 +4199,7 @@ void OBSBasic::ActivateAudioSource(OBSSource source)
 	if (!obs_source_audio_active(source))
 		return;
 
-	bool vertical = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool vertical = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"VerticalVolControl");
 	VolControl *vol = new VolControl(source, true, vertical);
 
@@ -4286,21 +4292,21 @@ void OBSBasic::TimedCheckForUpdates()
 {
 	if (App()->IsUpdaterDisabled())
 		return;
-	if (!config_get_bool(App()->GlobalConfig(), "General",
+	if (!config_get_bool(App()->GetUserConfig(), "General",
 			     "EnableAutoUpdates"))
 		return;
 
 #if defined(ENABLE_SPARKLE_UPDATER)
 	CheckForUpdates(false);
 #elif _WIN32
-	long long lastUpdate = config_get_int(App()->GlobalConfig(), "General",
+	long long lastUpdate = config_get_int(App()->GetAppConfig(), "General",
 					      "LastUpdateCheck");
 	uint32_t lastVersion =
-		config_get_int(App()->GlobalConfig(), "General", "LastVersion");
+		config_get_int(App()->GetAppConfig(), "General", "LastVersion");
 
 	if (lastVersion < LIBOBS_API_VER) {
 		lastUpdate = 0;
-		config_set_int(App()->GlobalConfig(), "General",
+		config_set_int(App()->GetAppConfig(), "General",
 			       "LastUpdateCheck", 0);
 	}
 
@@ -4977,7 +4983,7 @@ static inline enum video_colorspace GetVideoColorSpaceFromName(const char *name)
 void OBSBasic::ResetUI()
 {
 	bool studioPortraitLayout = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "StudioPortraitLayout");
+		App()->GetUserConfig(), "BasicWindow", "StudioPortraitLayout");
 
 	if (studioPortraitLayout)
 		ui->previewLayout->setDirection(QBoxLayout::BottomToTop);
@@ -5100,7 +5106,7 @@ bool OBSBasic::ResetAudio()
 		ai.speakers = SPEAKERS_STEREO;
 
 	bool lowLatencyAudioBuffering = config_get_bool(
-		GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering");
+		App()->GetUserConfig(), "Audio", "LowLatencyAudioBuffering");
 	if (lowLatencyAudioBuffering) {
 		ai.max_buffering_ms = 20;
 		ai.fixed_buffering = true;
@@ -5365,12 +5371,12 @@ void OBSBasic::closeEvent(QCloseEvent *event)
 #endif
 
 	if (isVisible())
-		config_set_string(App()->GlobalConfig(), "BasicWindow",
+		config_set_string(App()->GetUserConfig(), "BasicWindow",
 				  "geometry",
 				  saveGeometry().toBase64().constData());
 
-	bool confirmOnExit =
-		config_get_bool(GetGlobalConfig(), "General", "ConfirmOnExit");
+	bool confirmOnExit = config_get_bool(App()->GetUserConfig(), "General",
+					     "ConfirmOnExit");
 
 	if (confirmOnExit && outputHandler && outputHandler->Active() &&
 	    !clearingFailed) {
@@ -5437,7 +5443,7 @@ void OBSBasic::closeEvent(QCloseEvent *event)
 
 	delete extraBrowsers;
 
-	config_set_string(App()->GlobalConfig(), "BasicWindow", "DockState",
+	config_set_string(App()->GetUserConfig(), "BasicWindow", "DockState",
 			  saveState().toBase64().constData());
 
 #ifdef BROWSER_AVAILABLE
@@ -5642,7 +5648,7 @@ void OBSBasic::on_actionAdvAudioProperties_triggered()
 		return;
 	}
 
-	bool iconsVisible = config_get_bool(App()->GlobalConfig(),
+	bool iconsVisible = config_get_bool(App()->GetUserConfig(),
 					    "BasicWindow", "ShowSourceIcons");
 
 	advAudioWindow = new OBSBasicAdvAudio(this);
@@ -5665,7 +5671,7 @@ void OBSBasic::on_actionMixerToolbarMenu_triggered()
 	QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this);
 	toggleControlLayoutAction.setCheckable(true);
 	toggleControlLayoutAction.setChecked(config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "VerticalVolControl"));
+		App()->GetUserConfig(), "BasicWindow", "VerticalVolControl"));
 	connect(&toggleControlLayoutAction, &QAction::changed, this,
 		&OBSBasic::ToggleVolControlLayout, Qt::DirectConnection);
 
@@ -5861,14 +5867,15 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
 void OBSBasic::on_actionSceneListMode_triggered()
 {
 	ui->scenes->SetGridMode(false);
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode",
 			false);
 }
 
 void OBSBasic::on_actionSceneGridMode_triggered()
 {
 	ui->scenes->SetGridMode(true);
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", true);
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode",
+			true);
 }
 
 void OBSBasic::GridActionClicked()
@@ -5881,7 +5888,7 @@ void OBSBasic::GridActionClicked()
 	else
 		ui->actionSceneListMode->setChecked(true);
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode",
 			gridMode);
 }
 
@@ -6405,7 +6412,7 @@ void OBSBasic::on_scenes_itemDoubleClicked(QListWidgetItem *witem)
 
 	if (IsPreviewProgramMode()) {
 		bool doubleClickSwitch =
-			config_get_bool(App()->GlobalConfig(), "BasicWindow",
+			config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"TransitionOnDoubleClick");
 
 		if (doubleClickSwitch)
@@ -6795,7 +6802,7 @@ void OBSBasic::on_actionMoveToBottom_triggered()
 static BPtr<char> ReadLogFile(const char *subdir, const char *log)
 {
 	char logDir[512];
-	if (GetConfigPath(logDir, sizeof(logDir), subdir) <= 0)
+	if (GetAppConfigPath(logDir, sizeof(logDir), subdir) <= 0)
 		return nullptr;
 
 	string path = logDir;
@@ -6850,7 +6857,7 @@ void OBSBasic::UploadLog(const char *subdir, const char *file, const bool crash)
 void OBSBasic::on_actionShowLogs_triggered()
 {
 	char logDir[512];
-	if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0)
+	if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0)
 		return;
 
 	QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir));
@@ -6883,7 +6890,7 @@ void OBSBasic::on_actionViewCurrentLog_triggered()
 void OBSBasic::on_actionShowCrashLogs_triggered()
 {
 	char logDir[512];
-	if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/crashes") <= 0)
+	if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/crashes") <= 0)
 		return;
 
 	QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir));
@@ -7208,13 +7215,14 @@ void OBSBasic::ShowYouTubeAutoStartWarning()
 		msgbox.exec();
 
 		if (cb->isChecked()) {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"WarnedAboutYouTubeAutoStart", true);
-			config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+			config_save_safe(App()->GetUserConfig(), "tmp",
+					 nullptr);
 		}
 	};
 
-	bool warned = config_get_bool(App()->GlobalConfig(), "General",
+	bool warned = config_get_bool(App()->GetUserConfig(), "General",
 				      "WarnedAboutYouTubeAutoStart");
 	if (!warned) {
 		QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection,
@@ -7285,13 +7293,13 @@ void OBSBasic::StartStreaming()
 		}
 
 		bool recordWhenStreaming =
-			config_get_bool(GetGlobalConfig(), "BasicWindow",
+			config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"RecordWhenStreaming");
 		if (recordWhenStreaming)
 			StartRecording();
 
 		bool replayBufferWhileStreaming =
-			config_get_bool(GetGlobalConfig(), "BasicWindow",
+			config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"ReplayBufferWhileStreaming");
 		if (replayBufferWhileStreaming)
 			StartReplayBuffer();
@@ -7344,7 +7352,8 @@ void OBSBasic::BroadcastButtonClicked()
 		emit BroadcastStreamStarted(autoStopBroadcast);
 	} else if (!autoStopBroadcast) {
 #ifdef YOUTUBE_ENABLED
-		bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
+		bool confirm = config_get_bool(App()->GetUserConfig(),
+					       "BasicWindow",
 					       "WarnBeforeStoppingStream");
 		if (confirm && isVisible()) {
 			QMessageBox::StandardButton button = OBSMessageBox::question(
@@ -7409,7 +7418,7 @@ void OBSBasic::SetupBroadcast()
 #ifdef _WIN32
 static inline void UpdateProcessPriority()
 {
-	const char *priority = config_get_string(App()->GlobalConfig(),
+	const char *priority = config_get_string(App()->GetAppConfig(),
 						 "General", "ProcessPriority");
 	if (priority && strcmp(priority, "Normal") != 0)
 		SetProcessPriority(priority);
@@ -7417,7 +7426,7 @@ static inline void UpdateProcessPriority()
 
 static inline void ClearProcessPriority()
 {
-	const char *priority = config_get_string(App()->GlobalConfig(),
+	const char *priority = config_get_string(App()->GetAppConfig(),
 						 "General", "ProcessPriority");
 	if (priority && strcmp(priority, "Normal") != 0)
 		SetProcessPriority("Normal");
@@ -7539,17 +7548,18 @@ void OBSBasic::StopStreaming()
 	OnDeactivate();
 
 	bool recordWhenStreaming = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming");
+		App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming");
 	bool keepRecordingWhenStreamStops =
-		config_get_bool(GetGlobalConfig(), "BasicWindow",
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepRecordingWhenStreamStops");
 	if (recordWhenStreaming && !keepRecordingWhenStreamStops)
 		StopRecording();
 
-	bool replayBufferWhileStreaming = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming");
+	bool replayBufferWhileStreaming =
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
+				"ReplayBufferWhileStreaming");
 	bool keepReplayBufferStreamStops =
-		config_get_bool(GetGlobalConfig(), "BasicWindow",
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepReplayBufferStreamStops");
 	if (replayBufferWhileStreaming && !keepReplayBufferStreamStops)
 		StopReplayBuffer();
@@ -7581,17 +7591,18 @@ void OBSBasic::ForceStopStreaming()
 	OnDeactivate();
 
 	bool recordWhenStreaming = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming");
+		App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming");
 	bool keepRecordingWhenStreamStops =
-		config_get_bool(GetGlobalConfig(), "BasicWindow",
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepRecordingWhenStreamStops");
 	if (recordWhenStreaming && !keepRecordingWhenStreamStops)
 		StopRecording();
 
-	bool replayBufferWhileStreaming = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming");
+	bool replayBufferWhileStreaming =
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
+				"ReplayBufferWhileStreaming");
 	bool keepReplayBufferStreamStops =
-		config_get_bool(GetGlobalConfig(), "BasicWindow",
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepReplayBufferStreamStops");
 	if (replayBufferWhileStreaming && !keepReplayBufferStreamStops)
 		StopReplayBuffer();
@@ -7997,13 +8008,14 @@ void OBSBasic::ShowReplayBufferPauseWarning()
 		msgbox.exec();
 
 		if (cb->isChecked()) {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"WarnedAboutReplayBufferPausing", true);
-			config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+			config_save_safe(App()->GetUserConfig(), "tmp",
+					 nullptr);
 		}
 	};
 
-	bool warned = config_get_bool(App()->GlobalConfig(), "General",
+	bool warned = config_get_bool(App()->GetUserConfig(), "General",
 				      "WarnedAboutReplayBufferPausing");
 	if (!warned) {
 		QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection,
@@ -8242,7 +8254,8 @@ void OBSBasic::OnVirtualCamStop(int)
 void OBSBasic::StreamActionTriggered()
 {
 	if (outputHandler->StreamingActive()) {
-		bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
+		bool confirm = config_get_bool(App()->GetUserConfig(),
+					       "BasicWindow",
 					       "WarnBeforeStoppingStream");
 
 #ifdef YOUTUBE_ENABLED
@@ -8294,7 +8307,8 @@ void OBSBasic::StreamActionTriggered()
 			return;
 		}
 
-		bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
+		bool confirm = config_get_bool(App()->GetUserConfig(),
+					       "BasicWindow",
 					       "WarnBeforeStartingStream");
 
 		bool bwtest = false;
@@ -8336,7 +8350,8 @@ void OBSBasic::StreamActionTriggered()
 void OBSBasic::RecordActionTriggered()
 {
 	if (outputHandler->RecordingActive()) {
-		bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
+		bool confirm = config_get_bool(App()->GetUserConfig(),
+					       "BasicWindow",
 					       "WarnBeforeStoppingRecord");
 
 		if (confirm && isVisible()) {
@@ -8461,7 +8476,7 @@ void OBSBasic::on_actionShowWhatsNew_triggered()
 	if (!cef)
 		return;
 
-	config_set_int(App()->GlobalConfig(), "General", "InfoIncrement", -1);
+	config_set_int(App()->GetUserConfig(), "General", "InfoIncrement", -1);
 
 	WhatsNewInfoThread *wnit = new WhatsNewInfoThread();
 	connect(wnit, &WhatsNewInfoThread::Result, this,
@@ -8482,12 +8497,12 @@ void OBSBasic::on_actionReleaseNotes_triggered()
 
 void OBSBasic::on_actionShowSettingsFolder_triggered()
 {
-	char path[512];
-	int ret = GetConfigPath(path, 512, "obs-studio");
-	if (ret <= 0)
-		return;
+	const std::string userConfigPath =
+		App()->userConfigLocation.u8string() + "/obs-studio";
+	const QString userConfigLocation =
+		QString::fromStdString(userConfigPath);
 
-	QDesktopServices::openUrl(QUrl::fromLocalFile(path));
+	QDesktopServices::openUrl(QUrl::fromLocalFile(userConfigLocation));
 }
 
 void OBSBasic::on_actionShowProfileFolder_triggered()
@@ -9449,7 +9464,8 @@ OBSProjector *OBSBasic::OpenProjector(obs_source_t *source, int monitor,
 	if (monitor > 9 || monitor > QGuiApplication::screens().size() - 1)
 		return nullptr;
 
-	bool closeProjectors = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool closeProjectors = config_get_bool(App()->GetUserConfig(),
+					       "BasicWindow",
 					       "CloseExistingProjectors");
 
 	if (closeProjectors && monitor > -1) {
@@ -9604,9 +9620,9 @@ void OBSBasic::UpdateTitleBar()
 	stringstream name;
 
 	const char *profile =
-		config_get_string(App()->GlobalConfig(), "Basic", "Profile");
+		config_get_string(App()->GetUserConfig(), "Basic", "Profile");
 	const char *sceneCollection = config_get_string(
-		App()->GlobalConfig(), "Basic", "SceneCollection");
+		App()->GetUserConfig(), "Basic", "SceneCollection");
 
 	name << "OBS ";
 	if (previewProgramMode)
@@ -9627,8 +9643,8 @@ void OBSBasic::UpdateTitleBar()
 int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const
 {
 	char profiles_path[512];
-	const char *profile =
-		config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir");
+	const char *profile = config_get_string(App()->GetUserConfig(), "Basic",
+						"ProfileDir");
 	int ret;
 
 	if (!profile)
@@ -9638,7 +9654,7 @@ int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const
 	if (!file)
 		file = "";
 
-	ret = GetConfigPath(profiles_path, 512, "obs-studio/basic/profiles");
+	ret = GetAppConfigPath(profiles_path, 512, "obs-studio/basic/profiles");
 	if (ret <= 0)
 		return ret;
 
@@ -9803,7 +9819,7 @@ void OBSBasic::on_resetUI_triggered()
 	ui->scenes->SetGridMode(false);
 	ui->actionSceneListMode->setChecked(true);
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode",
 			false);
 }
 
@@ -9818,7 +9834,7 @@ void OBSBasic::on_toggleListboxToolbars_toggled(bool visible)
 	ui->scenesToolbar->setVisible(visible);
 	ui->mixerToolbar->setVisible(visible);
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"ShowListboxToolbars", visible);
 }
 
@@ -9836,7 +9852,7 @@ void OBSBasic::HideContextBar()
 
 void OBSBasic::on_toggleContextBar_toggled(bool visible)
 {
-	config_set_bool(App()->GlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"ShowContextToolbars", visible);
 	this->ui->contextContainer->setVisible(visible);
 	UpdateContextBar(true);
@@ -9846,7 +9862,7 @@ void OBSBasic::on_toggleStatusBar_toggled(bool visible)
 {
 	ui->statusbar->setVisible(visible);
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "ShowStatusBar",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow", "ShowStatusBar",
 			visible);
 }
 
@@ -9856,8 +9872,8 @@ void OBSBasic::on_toggleSourceIcons_toggled(bool visible)
 	if (advAudioWindow != nullptr)
 		advAudioWindow->SetIconsVisible(visible);
 
-	config_set_bool(App()->GlobalConfig(), "BasicWindow", "ShowSourceIcons",
-			visible);
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
+			"ShowSourceIcons", visible);
 }
 
 void OBSBasic::on_actionLockPreview_triggered()
@@ -9922,7 +9938,7 @@ void OBSBasic::on_actionScaleOutput_triggered()
 void OBSBasic::SetShowing(bool showing)
 {
 	if (!showing && isVisible()) {
-		config_set_string(App()->GlobalConfig(), "BasicWindow",
+		config_set_string(App()->GetUserConfig(), "BasicWindow",
 				  "geometry",
 				  saveGeometry().toBase64().constData());
 
@@ -10107,9 +10123,9 @@ void OBSBasic::SystemTray(bool firstStarted)
 		return;
 
 	bool sysTrayWhenStarted = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "SysTrayWhenStarted");
-	bool sysTrayEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					      "SysTrayEnabled");
+		App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted");
+	bool sysTrayEnabled = config_get_bool(App()->GetUserConfig(),
+					      "BasicWindow", "SysTrayEnabled");
 
 	if (firstStarted)
 		SystemTrayInit();
@@ -10135,7 +10151,7 @@ void OBSBasic::SystemTray(bool firstStarted)
 
 bool OBSBasic::sysTrayMinimizeToTray()
 {
-	return config_get_bool(GetGlobalConfig(), "BasicWindow",
+	return config_get_bool(App()->GetUserConfig(), "BasicWindow",
 			       "SysTrayMinimizeToTray");
 }
 
@@ -11118,17 +11134,17 @@ void OBSBasic::ShowStatusBarMessage(const QString &message)
 
 void OBSBasic::UpdatePreviewSafeAreas()
 {
-	drawSafeAreas = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	drawSafeAreas = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"ShowSafeAreas");
 }
 
 void OBSBasic::UpdatePreviewOverflowSettings()
 {
-	bool hidden = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	bool hidden = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				      "OverflowHidden");
-	bool select = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	bool select = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				      "OverflowSelectionHidden");
-	bool always = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+	bool always = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				      "OverflowAlwaysVisible");
 
 	ui->preview->SetOverflowHidden(hidden);
@@ -11141,7 +11157,7 @@ void OBSBasic::SetDisplayAffinity(QWindow *window)
 	if (!SetDisplayAffinitySupported())
 		return;
 
-	bool hideFromCapture = config_get_bool(App()->GlobalConfig(),
+	bool hideFromCapture = config_get_bool(App()->GetUserConfig(),
 					       "BasicWindow",
 					       "HideOBSWindowsFromCapture");
 
@@ -11175,10 +11191,10 @@ static inline QColor color_from_int(long long val)
 
 QColor OBSBasic::GetSelectionColor() const
 {
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
 		return color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "SelectRed"));
+			App()->GetUserConfig(), "Accessibility", "SelectRed"));
 	} else {
 		return QColor::fromRgb(255, 0, 0);
 	}
@@ -11186,10 +11202,11 @@ QColor OBSBasic::GetSelectionColor() const
 
 QColor OBSBasic::GetCropColor() const
 {
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
-		return color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "SelectGreen"));
+		return color_from_int(config_get_int(App()->GetUserConfig(),
+						     "Accessibility",
+						     "SelectGreen"));
 	} else {
 		return QColor::fromRgb(0, 255, 0);
 	}
@@ -11197,10 +11214,10 @@ QColor OBSBasic::GetCropColor() const
 
 QColor OBSBasic::GetHoverColor() const
 {
-	if (config_get_bool(GetGlobalConfig(), "Accessibility",
+	if (config_get_bool(App()->GetUserConfig(), "Accessibility",
 			    "OverrideColors")) {
 		return color_from_int(config_get_int(
-			GetGlobalConfig(), "Accessibility", "SelectBlue"));
+			App()->GetUserConfig(), "Accessibility", "SelectBlue"));
 	} else {
 		return QColor::fromRgb(0, 127, 255);
 	}
@@ -11209,7 +11226,7 @@ QColor OBSBasic::GetHoverColor() const
 void OBSBasic::UpdatePreviewSpacingHelpers()
 {
 	drawSpacingHelpers = config_get_bool(
-		App()->GlobalConfig(), "BasicWindow", "SpacingHelpersEnabled");
+		App()->GetUserConfig(), "BasicWindow", "SpacingHelpersEnabled");
 }
 
 float OBSBasic::GetDevicePixelRatio()

+ 1 - 1
UI/window-basic-main.hpp

@@ -308,7 +308,7 @@ private:
 	int previewCX = 0, previewCY = 0;
 	float previewScale = 0.0f;
 
-	ConfigFile basicConfig;
+	ConfigFile activeConfiguration;
 
 	std::vector<SavedProjectorInfo *> savedProjectorsArray;
 	std::vector<OBSProjector *> projectors;

+ 7 - 7
UI/window-basic-preview.cpp

@@ -192,17 +192,17 @@ vec3 OBSBasicPreview::GetSnapOffset(const vec3 &tl, const vec3 &br)
 
 	vec3_zero(&clampOffset);
 
-	const bool snap = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	const bool snap = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					  "SnappingEnabled");
 	if (snap == false)
 		return clampOffset;
 
 	const bool screenSnap = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "ScreenSnapping");
+		App()->GetUserConfig(), "BasicWindow", "ScreenSnapping");
 	const bool centerSnap = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "CenterSnapping");
+		App()->GetUserConfig(), "BasicWindow", "CenterSnapping");
 
-	const float clampDist = config_get_double(GetGlobalConfig(),
+	const float clampDist = config_get_double(App()->GetUserConfig(),
 						  "BasicWindow",
 						  "SnapDistance") /
 				main->previewScale;
@@ -995,10 +995,10 @@ void OBSBasicPreview::SnapItemMovement(vec2 &offset)
 
 	vec3 snapOffset = GetSnapOffset(data.tl, data.br);
 
-	const bool snap = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	const bool snap = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					  "SnappingEnabled");
 	const bool sourcesSnap = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "SourceSnapping");
+		App()->GetUserConfig(), "BasicWindow", "SourceSnapping");
 	if (snap == false)
 		return;
 	if (sourcesSnap == false) {
@@ -1007,7 +1007,7 @@ void OBSBasicPreview::SnapItemMovement(vec2 &offset)
 		return;
 	}
 
-	const float clampDist = config_get_double(GetGlobalConfig(),
+	const float clampDist = config_get_double(App()->GetUserConfig(),
 						  "BasicWindow",
 						  "SnapDistance") /
 				main->previewScale;

+ 4 - 4
UI/window-basic-properties.cpp

@@ -53,9 +53,9 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
 			OBSBasicProperties::SourceRenamed, this),
 	  oldSettings(obs_data_create())
 {
-	int cx = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
+	int cx = (int)config_get_int(App()->GetAppConfig(), "PropertiesWindow",
 				     "cx");
-	int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
+	int cy = (int)config_get_int(App()->GetAppConfig(), "PropertiesWindow",
 				     "cy");
 
 	enum obs_source_type type = obs_source_get_type(source);
@@ -450,9 +450,9 @@ void OBSBasicProperties::DrawTransitionPreview(void *data, uint32_t cx,
 
 void OBSBasicProperties::Cleanup()
 {
-	config_set_int(App()->GlobalConfig(), "PropertiesWindow", "cx",
+	config_set_int(App()->GetAppConfig(), "PropertiesWindow", "cx",
 		       width());
-	config_set_int(App()->GlobalConfig(), "PropertiesWindow", "cy",
+	config_set_int(App()->GetAppConfig(), "PropertiesWindow", "cy",
 		       height());
 
 	obs_display_remove_draw_callback(ui->preview->GetDisplay(),

+ 3 - 3
UI/window-basic-settings-a11y.cpp

@@ -43,7 +43,7 @@ QColor OBSBasicSettings::GetColor(uint32_t colorVal, QString label)
 
 void OBSBasicSettings::LoadA11ySettings(bool presetChange)
 {
-	config_t *config = GetGlobalConfig();
+	config_t *config = App()->GetUserConfig();
 
 	loading = true;
 	if (!presetChange) {
@@ -109,7 +109,7 @@ void OBSBasicSettings::LoadA11ySettings(bool presetChange)
 
 void OBSBasicSettings::SaveA11ySettings()
 {
-	config_t *config = GetGlobalConfig();
+	config_t *config = App()->GetUserConfig();
 
 	config_set_bool(config, "Accessibility", "OverrideColors",
 			ui->colorsGroupBox->isChecked());
@@ -163,7 +163,7 @@ void OBSBasicSettings::UpdateA11yColors()
 
 void OBSBasicSettings::SetDefaultColors()
 {
-	config_t *config = GetGlobalConfig();
+	config_t *config = App()->GetUserConfig();
 	config_set_default_int(config, "Accessibility", "SelectRed", selectRed);
 	config_set_default_int(config, "Accessibility", "SelectGreen",
 			       selectGreen);

+ 1 - 1
UI/window-basic-settings-appearance.cpp

@@ -107,7 +107,7 @@ void OBSBasicSettings::LoadAppearanceSettings(bool reload)
 
 void OBSBasicSettings::SaveAppearanceSettings()
 {
-	config_t *config = GetGlobalConfig();
+	config_t *config = App()->GetUserConfig();
 
 	OBSTheme *currentTheme = App()->GetTheme();
 	if (savedTheme != currentTheme) {

+ 3 - 2
UI/window-basic-settings-stream.cpp

@@ -1032,8 +1032,9 @@ void OBSBasicSettings::on_server_currentIndexChanged(int /*index*/)
 
 void OBSBasicSettings::UpdateVodTrackSetting()
 {
-	bool enableForCustomServer = config_get_bool(
-		GetGlobalConfig(), "General", "EnableCustomServerVodTrack");
+	bool enableForCustomServer =
+		config_get_bool(App()->GetUserConfig(), "General",
+				"EnableCustomServerVodTrack");
 	bool enableVodTrack = ui->service->currentText() == "Twitch";
 	bool wasEnabled = !!vodTrackCheckbox;
 

+ 122 - 113
UI/window-basic-settings.cpp

@@ -1333,8 +1333,8 @@ void OBSBasicSettings::LoadBranchesList()
 {
 #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
 	bool configBranchRemoved = true;
-	QString configBranch =
-		config_get_string(GetGlobalConfig(), "General", "UpdateBranch");
+	QString configBranch = config_get_string(App()->GetAppConfig(),
+						 "General", "UpdateBranch");
 
 	for (const UpdateBranch &branch : App()->GetBranches()) {
 		if (branch.name == configBranch)
@@ -1387,8 +1387,8 @@ void OBSBasicSettings::LoadGeneralSettings()
 	LoadLanguageList();
 
 #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
-	bool enableAutoUpdates = config_get_bool(GetGlobalConfig(), "General",
-						 "EnableAutoUpdates");
+	bool enableAutoUpdates = config_get_bool(
+		App()->GetUserConfig(), "General", "EnableAutoUpdates");
 	ui->enableAutoUpdates->setChecked(enableAutoUpdates);
 
 	LoadBranchesList();
@@ -1400,7 +1400,7 @@ void OBSBasicSettings::LoadGeneralSettings()
 #if defined(_WIN32)
 	if (ui->hideOBSFromCapture) {
 		bool hideWindowFromCapture =
-			config_get_bool(GetGlobalConfig(), "BasicWindow",
+			config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"HideOBSWindowsFromCapture");
 		ui->hideOBSFromCapture->setChecked(hideWindowFromCapture);
 
@@ -1415,129 +1415,136 @@ void OBSBasicSettings::LoadGeneralSettings()
 #endif
 
 	bool recordWhenStreaming = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming");
+		App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming");
 	ui->recordWhenStreaming->setChecked(recordWhenStreaming);
 
 	bool keepRecordStreamStops =
-		config_get_bool(GetGlobalConfig(), "BasicWindow",
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepRecordingWhenStreamStops");
 	ui->keepRecordStreamStops->setChecked(keepRecordStreamStops);
 
-	bool replayWhileStreaming = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming");
+	bool replayWhileStreaming =
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
+				"ReplayBufferWhileStreaming");
 	ui->replayWhileStreaming->setChecked(replayWhileStreaming);
 
 	bool keepReplayStreamStops =
-		config_get_bool(GetGlobalConfig(), "BasicWindow",
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepReplayBufferStreamStops");
 	ui->keepReplayStreamStops->setChecked(keepReplayStreamStops);
 
 	bool systemTrayEnabled = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "SysTrayEnabled");
+		App()->GetUserConfig(), "BasicWindow", "SysTrayEnabled");
 	ui->systemTrayEnabled->setChecked(systemTrayEnabled);
 
 	bool systemTrayWhenStarted = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "SysTrayWhenStarted");
+		App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted");
 	ui->systemTrayWhenStarted->setChecked(systemTrayWhenStarted);
 
 	bool systemTrayAlways = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "SysTrayMinimizeToTray");
+		App()->GetUserConfig(), "BasicWindow", "SysTrayMinimizeToTray");
 	ui->systemTrayAlways->setChecked(systemTrayAlways);
 
-	bool saveProjectors = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					      "SaveProjectors");
+	bool saveProjectors = config_get_bool(App()->GetUserConfig(),
+					      "BasicWindow", "SaveProjectors");
 	ui->saveProjectors->setChecked(saveProjectors);
 
-	bool closeProjectors = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool closeProjectors = config_get_bool(App()->GetUserConfig(),
+					       "BasicWindow",
 					       "CloseExistingProjectors");
 	ui->closeProjectors->setChecked(closeProjectors);
 
-	bool snappingEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					       "SnappingEnabled");
+	bool snappingEnabled = config_get_bool(
+		App()->GetUserConfig(), "BasicWindow", "SnappingEnabled");
 	ui->snappingEnabled->setChecked(snappingEnabled);
 
-	bool screenSnapping = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					      "ScreenSnapping");
+	bool screenSnapping = config_get_bool(App()->GetUserConfig(),
+					      "BasicWindow", "ScreenSnapping");
 	ui->screenSnapping->setChecked(screenSnapping);
 
-	bool centerSnapping = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					      "CenterSnapping");
+	bool centerSnapping = config_get_bool(App()->GetUserConfig(),
+					      "BasicWindow", "CenterSnapping");
 	ui->centerSnapping->setChecked(centerSnapping);
 
-	bool sourceSnapping = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					      "SourceSnapping");
+	bool sourceSnapping = config_get_bool(App()->GetUserConfig(),
+					      "BasicWindow", "SourceSnapping");
 	ui->sourceSnapping->setChecked(sourceSnapping);
 
-	double snapDistance = config_get_double(GetGlobalConfig(),
+	double snapDistance = config_get_double(App()->GetUserConfig(),
 						"BasicWindow", "SnapDistance");
 	ui->snapDistance->setValue(snapDistance);
 
-	bool warnBeforeStreamStart = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "WarnBeforeStartingStream");
+	bool warnBeforeStreamStart =
+		config_get_bool(App()->GetUserConfig(), "BasicWindow",
+				"WarnBeforeStartingStream");
 	ui->warnBeforeStreamStart->setChecked(warnBeforeStreamStart);
 
 	bool spacingHelpersEnabled = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "SpacingHelpersEnabled");
+		App()->GetUserConfig(), "BasicWindow", "SpacingHelpersEnabled");
 	ui->previewSpacingHelpers->setChecked(spacingHelpersEnabled);
 
-	bool warnBeforeStreamStop = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingStream");
+	bool warnBeforeStreamStop = config_get_bool(App()->GetUserConfig(),
+						    "BasicWindow",
+						    "WarnBeforeStoppingStream");
 	ui->warnBeforeStreamStop->setChecked(warnBeforeStreamStop);
 
-	bool warnBeforeRecordStop = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingRecord");
+	bool warnBeforeRecordStop = config_get_bool(App()->GetUserConfig(),
+						    "BasicWindow",
+						    "WarnBeforeStoppingRecord");
 	ui->warnBeforeRecordStop->setChecked(warnBeforeRecordStop);
 
 	bool hideProjectorCursor = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "HideProjectorCursor");
+		App()->GetUserConfig(), "BasicWindow", "HideProjectorCursor");
 	ui->hideProjectorCursor->setChecked(hideProjectorCursor);
 
 	bool projectorAlwaysOnTop = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "ProjectorAlwaysOnTop");
+		App()->GetUserConfig(), "BasicWindow", "ProjectorAlwaysOnTop");
 	ui->projectorAlwaysOnTop->setChecked(projectorAlwaysOnTop);
 
-	bool overflowHide = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					    "OverflowHidden");
+	bool overflowHide = config_get_bool(App()->GetUserConfig(),
+					    "BasicWindow", "OverflowHidden");
 	ui->overflowHide->setChecked(overflowHide);
 
 	bool overflowAlwaysVisible = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "OverflowAlwaysVisible");
+		App()->GetUserConfig(), "BasicWindow", "OverflowAlwaysVisible");
 	ui->overflowAlwaysVisible->setChecked(overflowAlwaysVisible);
 
-	bool overflowSelectionHide = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "OverflowSelectionHidden");
+	bool overflowSelectionHide = config_get_bool(App()->GetUserConfig(),
+						     "BasicWindow",
+						     "OverflowSelectionHidden");
 	ui->overflowSelectionHide->setChecked(overflowSelectionHide);
 
-	bool safeAreas = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool safeAreas = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "ShowSafeAreas");
 	ui->previewSafeAreas->setChecked(safeAreas);
 
-	bool automaticSearch = config_get_bool(GetGlobalConfig(), "General",
-					       "AutomaticCollectionSearch");
+	bool automaticSearch = config_get_bool(
+		App()->GetUserConfig(), "General", "AutomaticCollectionSearch");
 	ui->automaticSearch->setChecked(automaticSearch);
 
-	bool doubleClickSwitch = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick");
+	bool doubleClickSwitch = config_get_bool(App()->GetUserConfig(),
+						 "BasicWindow",
+						 "TransitionOnDoubleClick");
 	ui->doubleClickSwitch->setChecked(doubleClickSwitch);
 
 	bool studioPortraitLayout = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "StudioPortraitLayout");
+		App()->GetUserConfig(), "BasicWindow", "StudioPortraitLayout");
 	ui->studioPortraitLayout->setChecked(studioPortraitLayout);
 
-	bool prevProgLabels = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					      "StudioModeLabels");
+	bool prevProgLabels = config_get_bool(
+		App()->GetUserConfig(), "BasicWindow", "StudioModeLabels");
 	ui->prevProgLabelToggle->setChecked(prevProgLabels);
 
 	bool multiviewMouseSwitch = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "MultiviewMouseSwitch");
+		App()->GetUserConfig(), "BasicWindow", "MultiviewMouseSwitch");
 	ui->multiviewMouseSwitch->setChecked(multiviewMouseSwitch);
 
 	bool multiviewDrawNames = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "MultiviewDrawNames");
+		App()->GetUserConfig(), "BasicWindow", "MultiviewDrawNames");
 	ui->multiviewDrawNames->setChecked(multiviewDrawNames);
 
 	bool multiviewDrawAreas = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "MultiviewDrawAreas");
+		App()->GetUserConfig(), "BasicWindow", "MultiviewDrawAreas");
 	ui->multiviewDrawAreas->setChecked(multiviewDrawAreas);
 
 	ui->multiviewLayout->addItem(
@@ -1571,9 +1578,10 @@ void OBSBasicSettings::LoadGeneralSettings()
 		QTStr("Basic.Settings.General.MultiviewLayout.25Scene"),
 		static_cast<int>(MultiviewLayout::SCENES_ONLY_25_SCENES));
 
-	ui->multiviewLayout->setCurrentIndex(ui->multiviewLayout->findData(
-		QVariant::fromValue(config_get_int(
-			GetGlobalConfig(), "BasicWindow", "MultiviewLayout"))));
+	ui->multiviewLayout->setCurrentIndex(
+		ui->multiviewLayout->findData(QVariant::fromValue(
+			config_get_int(App()->GetUserConfig(), "BasicWindow",
+				       "MultiviewLayout"))));
 
 	prevLangIndex = ui->language->currentIndex();
 
@@ -1587,7 +1595,7 @@ void OBSBasicSettings::LoadRendererList()
 {
 #ifdef _WIN32
 	const char *renderer =
-		config_get_string(GetGlobalConfig(), "Video", "Renderer");
+		config_get_string(App()->GetUserConfig(), "Video", "Renderer");
 
 	ui->renderer->addItem(QT_UTF8("Direct3D 11"));
 	if (opt_allow_opengl || strcmp(renderer, "OpenGL") == 0)
@@ -2792,7 +2800,7 @@ void OBSBasicSettings::LoadAudioSettings()
 	uint32_t peakMeterTypeIdx =
 		config_get_uint(main->Config(), "Audio", "PeakMeterType");
 	bool enableLLAudioBuffering = config_get_bool(
-		GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering");
+		App()->GetUserConfig(), "Audio", "LowLatencyAudioBuffering");
 
 	loading = true;
 
@@ -2918,13 +2926,13 @@ void OBSBasicSettings::LoadAdvancedSettings()
 	int rbSize = config_get_int(main->Config(), "AdvOut", "RecRBSize");
 	bool autoRemux = config_get_bool(main->Config(), "Video", "AutoRemux");
 	const char *hotkeyFocusType = config_get_string(
-		App()->GlobalConfig(), "General", "HotkeyFocusType");
+		App()->GetUserConfig(), "General", "HotkeyFocusType");
 	bool dynBitrate =
 		config_get_bool(main->Config(), "Output", "DynamicBitrate");
 	const char *ipFamily =
 		config_get_string(main->Config(), "Output", "IPFamily");
-	bool confirmOnExit =
-		config_get_bool(GetGlobalConfig(), "General", "ConfirmOnExit");
+	bool confirmOnExit = config_get_bool(App()->GetUserConfig(), "General",
+					     "ConfirmOnExit");
 
 	loading = true;
 
@@ -2971,20 +2979,20 @@ void OBSBasicSettings::LoadAdvancedSettings()
 	}
 
 #ifdef __APPLE__
-	bool disableOSXVSync = config_get_bool(App()->GlobalConfig(), "Video",
+	bool disableOSXVSync = config_get_bool(App()->GetUserConfig(), "Video",
 					       "DisableOSXVSync");
-	bool resetOSXVSync = config_get_bool(App()->GlobalConfig(), "Video",
+	bool resetOSXVSync = config_get_bool(App()->GetUserConfig(), "Video",
 					     "ResetOSXVSyncOnExit");
 	ui->disableOSXVSync->setChecked(disableOSXVSync);
 	ui->resetOSXVSync->setChecked(resetOSXVSync);
 	ui->resetOSXVSync->setEnabled(disableOSXVSync);
 #elif _WIN32
 	bool disableAudioDucking = config_get_bool(
-		App()->GlobalConfig(), "Audio", "DisableAudioDucking");
+		App()->GetUserConfig(), "Audio", "DisableAudioDucking");
 	ui->disableAudioDucking->setChecked(disableAudioDucking);
 
 	const char *processPriority = config_get_string(
-		App()->GlobalConfig(), "General", "ProcessPriority");
+		App()->GetAppConfig(), "General", "ProcessPriority");
 	bool enableNewSocketLoop = config_get_bool(main->Config(), "Output",
 						   "NewSocketLoopEnable");
 	bool enableLowLatencyMode =
@@ -3001,7 +3009,7 @@ void OBSBasicSettings::LoadAdvancedSettings()
 		QTStr("Basic.Settings.Advanced.Network.TCPPacing.Tooltip"));
 #endif
 #if defined(_WIN32) || defined(__APPLE__)
-	bool browserHWAccel = config_get_bool(App()->GlobalConfig(), "General",
+	bool browserHWAccel = config_get_bool(App()->GetUserConfig(), "General",
 					      "BrowserHWAccel");
 	ui->browserHWAccel->setChecked(browserHWAccel);
 	prevBrowserAccel = ui->browserHWAccel->isChecked();
@@ -3360,12 +3368,12 @@ void OBSBasicSettings::SaveGeneralSettings()
 	string language = langData.toString().toStdString();
 
 	if (WidgetChanged(ui->language))
-		config_set_string(GetGlobalConfig(), "General", "Language",
+		config_set_string(App()->GetUserConfig(), "General", "Language",
 				  language.c_str());
 
 #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
 	if (WidgetChanged(ui->enableAutoUpdates))
-		config_set_bool(GetGlobalConfig(), "General",
+		config_set_bool(App()->GetUserConfig(), "General",
 				"EnableAutoUpdates",
 				ui->enableAutoUpdates->isChecked());
 	int branchIdx = ui->updateChannelBox->currentIndex();
@@ -3373,15 +3381,15 @@ void OBSBasicSettings::SaveGeneralSettings()
 		ui->updateChannelBox->itemData(branchIdx).toString();
 
 	if (WidgetChanged(ui->updateChannelBox)) {
-		config_set_string(GetGlobalConfig(), "General", "UpdateBranch",
-				  QT_TO_UTF8(branchName));
+		config_set_string(App()->GetAppConfig(), "General",
+				  "UpdateBranch", QT_TO_UTF8(branchName));
 		forceUpdateCheck = true;
 	}
 #endif
 #ifdef _WIN32
 	if (ui->hideOBSFromCapture && WidgetChanged(ui->hideOBSFromCapture)) {
 		bool hide_window = ui->hideOBSFromCapture->isChecked();
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"HideOBSWindowsFromCapture", hide_window);
 
 		QWindowList windows = QGuiApplication::allWindows();
@@ -3399,80 +3407,80 @@ void OBSBasicSettings::SaveGeneralSettings()
 		config_set_bool(main->Config(), "General", "OpenStatsOnStartup",
 				ui->openStatsOnStartup->isChecked());
 	if (WidgetChanged(ui->snappingEnabled))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SnappingEnabled",
 				ui->snappingEnabled->isChecked());
 	if (WidgetChanged(ui->screenSnapping))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"ScreenSnapping",
 				ui->screenSnapping->isChecked());
 	if (WidgetChanged(ui->centerSnapping))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"CenterSnapping",
 				ui->centerSnapping->isChecked());
 	if (WidgetChanged(ui->sourceSnapping))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SourceSnapping",
 				ui->sourceSnapping->isChecked());
 	if (WidgetChanged(ui->snapDistance))
-		config_set_double(GetGlobalConfig(), "BasicWindow",
+		config_set_double(App()->GetUserConfig(), "BasicWindow",
 				  "SnapDistance", ui->snapDistance->value());
 	if (WidgetChanged(ui->overflowAlwaysVisible) ||
 	    WidgetChanged(ui->overflowHide) ||
 	    WidgetChanged(ui->overflowSelectionHide)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"OverflowAlwaysVisible",
 				ui->overflowAlwaysVisible->isChecked());
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"OverflowHidden",
 				ui->overflowHide->isChecked());
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"OverflowSelectionHidden",
 				ui->overflowSelectionHide->isChecked());
 		main->UpdatePreviewOverflowSettings();
 	}
 	if (WidgetChanged(ui->previewSafeAreas)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"ShowSafeAreas",
 				ui->previewSafeAreas->isChecked());
 		main->UpdatePreviewSafeAreas();
 	}
 
 	if (WidgetChanged(ui->previewSpacingHelpers)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SpacingHelpersEnabled",
 				ui->previewSpacingHelpers->isChecked());
 		main->UpdatePreviewSpacingHelpers();
 	}
 
 	if (WidgetChanged(ui->doubleClickSwitch))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"TransitionOnDoubleClick",
 				ui->doubleClickSwitch->isChecked());
 	if (WidgetChanged(ui->automaticSearch))
-		config_set_bool(GetGlobalConfig(), "General",
+		config_set_bool(App()->GetUserConfig(), "General",
 				"AutomaticCollectionSearch",
 				ui->automaticSearch->isChecked());
 
-	config_set_bool(GetGlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"WarnBeforeStartingStream",
 			ui->warnBeforeStreamStart->isChecked());
-	config_set_bool(GetGlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"WarnBeforeStoppingStream",
 			ui->warnBeforeStreamStop->isChecked());
-	config_set_bool(GetGlobalConfig(), "BasicWindow",
+	config_set_bool(App()->GetUserConfig(), "BasicWindow",
 			"WarnBeforeStoppingRecord",
 			ui->warnBeforeRecordStop->isChecked());
 
 	if (WidgetChanged(ui->hideProjectorCursor)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"HideProjectorCursor",
 				ui->hideProjectorCursor->isChecked());
 		main->UpdateProjectorHideCursor();
 	}
 
 	if (WidgetChanged(ui->projectorAlwaysOnTop)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"ProjectorAlwaysOnTop",
 				ui->projectorAlwaysOnTop->isChecked());
 #if defined(_WIN32) || defined(__APPLE__)
@@ -3484,25 +3492,25 @@ void OBSBasicSettings::SaveGeneralSettings()
 	}
 
 	if (WidgetChanged(ui->recordWhenStreaming))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"RecordWhenStreaming",
 				ui->recordWhenStreaming->isChecked());
 	if (WidgetChanged(ui->keepRecordStreamStops))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepRecordingWhenStreamStops",
 				ui->keepRecordStreamStops->isChecked());
 
 	if (WidgetChanged(ui->replayWhileStreaming))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"ReplayBufferWhileStreaming",
 				ui->replayWhileStreaming->isChecked());
 	if (WidgetChanged(ui->keepReplayStreamStops))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"KeepReplayBufferStreamStops",
 				ui->keepReplayStreamStops->isChecked());
 
 	if (WidgetChanged(ui->systemTrayEnabled)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SysTrayEnabled",
 				ui->systemTrayEnabled->isChecked());
 
@@ -3510,27 +3518,27 @@ void OBSBasicSettings::SaveGeneralSettings()
 	}
 
 	if (WidgetChanged(ui->systemTrayWhenStarted))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SysTrayWhenStarted",
 				ui->systemTrayWhenStarted->isChecked());
 
 	if (WidgetChanged(ui->systemTrayAlways))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SysTrayMinimizeToTray",
 				ui->systemTrayAlways->isChecked());
 
 	if (WidgetChanged(ui->saveProjectors))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"SaveProjectors",
 				ui->saveProjectors->isChecked());
 
 	if (WidgetChanged(ui->closeProjectors))
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"CloseExistingProjectors",
 				ui->closeProjectors->isChecked());
 
 	if (WidgetChanged(ui->studioPortraitLayout)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"StudioPortraitLayout",
 				ui->studioPortraitLayout->isChecked());
 
@@ -3538,7 +3546,7 @@ void OBSBasicSettings::SaveGeneralSettings()
 	}
 
 	if (WidgetChanged(ui->prevProgLabelToggle)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"StudioModeLabels",
 				ui->prevProgLabelToggle->isChecked());
 
@@ -3547,28 +3555,28 @@ void OBSBasicSettings::SaveGeneralSettings()
 
 	bool multiviewChanged = false;
 	if (WidgetChanged(ui->multiviewMouseSwitch)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"MultiviewMouseSwitch",
 				ui->multiviewMouseSwitch->isChecked());
 		multiviewChanged = true;
 	}
 
 	if (WidgetChanged(ui->multiviewDrawNames)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"MultiviewDrawNames",
 				ui->multiviewDrawNames->isChecked());
 		multiviewChanged = true;
 	}
 
 	if (WidgetChanged(ui->multiviewDrawAreas)) {
-		config_set_bool(GetGlobalConfig(), "BasicWindow",
+		config_set_bool(App()->GetUserConfig(), "BasicWindow",
 				"MultiviewDrawAreas",
 				ui->multiviewDrawAreas->isChecked());
 		multiviewChanged = true;
 	}
 
 	if (WidgetChanged(ui->multiviewLayout)) {
-		config_set_int(GetGlobalConfig(), "BasicWindow",
+		config_set_int(App()->GetUserConfig(), "BasicWindow",
 			       "MultiviewLayout",
 			       ui->multiviewLayout->currentData().toInt());
 		multiviewChanged = true;
@@ -3616,12 +3624,12 @@ void OBSBasicSettings::SaveAdvancedSettings()
 
 #ifdef _WIN32
 	if (WidgetChanged(ui->renderer))
-		config_set_string(App()->GlobalConfig(), "Video", "Renderer",
+		config_set_string(App()->GetUserConfig(), "Video", "Renderer",
 				  QT_TO_UTF8(ui->renderer->currentText()));
 
 	std::string priority =
 		QT_TO_UTF8(ui->processPriority->currentData().toString());
-	config_set_string(App()->GlobalConfig(), "General", "ProcessPriority",
+	config_set_string(App()->GetAppConfig(), "General", "ProcessPriority",
 			  priority.c_str());
 	if (main->Active())
 		SetProcessPriority(priority.c_str());
@@ -3631,25 +3639,25 @@ void OBSBasicSettings::SaveAdvancedSettings()
 #endif
 #if defined(_WIN32) || defined(__APPLE__)
 	bool browserHWAccel = ui->browserHWAccel->isChecked();
-	config_set_bool(App()->GlobalConfig(), "General", "BrowserHWAccel",
+	config_set_bool(App()->GetUserConfig(), "General", "BrowserHWAccel",
 			browserHWAccel);
 #endif
 
 	if (WidgetChanged(ui->hotkeyFocusType)) {
 		QString str = GetComboData(ui->hotkeyFocusType);
-		config_set_string(App()->GlobalConfig(), "General",
+		config_set_string(App()->GetUserConfig(), "General",
 				  "HotkeyFocusType", QT_TO_UTF8(str));
 	}
 
 #ifdef __APPLE__
 	if (WidgetChanged(ui->disableOSXVSync)) {
 		bool disable = ui->disableOSXVSync->isChecked();
-		config_set_bool(App()->GlobalConfig(), "Video",
+		config_set_bool(App()->GetUserConfig(), "Video",
 				"DisableOSXVSync", disable);
 		EnableOSXVSync(!disable);
 	}
 	if (WidgetChanged(ui->resetOSXVSync))
-		config_set_bool(App()->GlobalConfig(), "Video",
+		config_set_bool(App()->GetUserConfig(), "Video",
 				"ResetOSXVSyncOnExit",
 				ui->resetOSXVSync->isChecked());
 #endif
@@ -3669,14 +3677,15 @@ void OBSBasicSettings::SaveAdvancedSettings()
 #ifdef _WIN32
 	if (WidgetChanged(ui->disableAudioDucking)) {
 		bool disable = ui->disableAudioDucking->isChecked();
-		config_set_bool(App()->GlobalConfig(), "Audio",
+		config_set_bool(App()->GetUserConfig(), "Audio",
 				"DisableAudioDucking", disable);
 		DisableAudioDucking(disable);
 	}
 #endif
 
 	if (WidgetChanged(ui->confirmOnExit))
-		config_set_bool(GetGlobalConfig(), "General", "ConfirmOnExit",
+		config_set_bool(App()->GetUserConfig(), "General",
+				"ConfirmOnExit",
 				ui->confirmOnExit->isChecked());
 
 	SaveEdit(ui->filenameFormatting, "Output", "FilenameFormatting");
@@ -4049,7 +4058,7 @@ void OBSBasicSettings::SaveAudioSettings()
 	if (WidgetChanged(ui->lowLatencyBuffering)) {
 		bool enableLLAudioBuffering =
 			ui->lowLatencyBuffering->isChecked();
-		config_set_bool(GetGlobalConfig(), "Audio",
+		config_set_bool(App()->GetUserConfig(), "Audio",
 				"LowLatencyAudioBuffering",
 				enableLLAudioBuffering);
 	}
@@ -4159,7 +4168,7 @@ void OBSBasicSettings::SaveSettings()
 		main->ResetVideo();
 
 	config_save_safe(main->Config(), "tmp", nullptr);
-	config_save_safe(GetGlobalConfig(), "tmp", nullptr);
+	config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
 	main->SaveProject();
 
 	if (Changed()) {
@@ -4788,7 +4797,7 @@ void OBSBasicSettings::HideOBSWindowWarning(int state)
 	if (loading || state == Qt::Unchecked)
 		return;
 
-	if (config_get_bool(GetGlobalConfig(), "General",
+	if (config_get_bool(App()->GetUserConfig(), "General",
 			    "WarnedAboutHideOBSFromCapture"))
 		return;
 
@@ -4796,9 +4805,9 @@ void OBSBasicSettings::HideOBSWindowWarning(int state)
 		this, QTStr("Basic.Settings.General.HideOBSWindowsFromCapture"),
 		QTStr("Basic.Settings.General.HideOBSWindowsFromCapture.Message"));
 
-	config_set_bool(GetGlobalConfig(), "General",
+	config_set_bool(App()->GetUserConfig(), "General",
 			"WarnedAboutHideOBSFromCapture", true);
-	config_save_safe(GetGlobalConfig(), "tmp", nullptr);
+	config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
 }
 
 /*

+ 4 - 4
UI/window-dock-youtube-app.cpp

@@ -431,16 +431,16 @@ void YouTubeAppDock::CleanupYouTubeUrls()
 	// remove legacy YouTube Browser Docks (once)
 
 	bool youtube_cleanup_done = config_get_bool(
-		App()->GlobalConfig(), "General", "YtDockCleanupDone");
+		App()->GetUserConfig(), "General", "YtDockCleanupDone");
 
 	if (youtube_cleanup_done)
 		return;
 
-	config_set_bool(App()->GlobalConfig(), "General", "YtDockCleanupDone",
+	config_set_bool(App()->GetUserConfig(), "General", "YtDockCleanupDone",
 			true);
 
 	const char *jsonStr = config_get_string(
-		App()->GlobalConfig(), "BasicWindow", "ExtraBrowserDocks");
+		App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks");
 	if (!jsonStr)
 		return;
 
@@ -472,7 +472,7 @@ void YouTubeAppDock::CleanupYouTubeUrls()
 		OBSMessageBox::warning(OBSBasic::Get(), msg_title, msg_text);
 
 		std::string output = save_array.dump();
-		config_set_string(App()->GlobalConfig(), "BasicWindow",
+		config_set_string(App()->GetUserConfig(), "BasicWindow",
 				  "ExtraBrowserDocks", output.c_str());
 	}
 }

+ 4 - 3
UI/window-dock.cpp

@@ -20,13 +20,14 @@ void OBSDock::closeEvent(QCloseEvent *event)
 		msgbox.exec();
 
 		if (cb->isChecked()) {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"WarnedAboutClosingDocks", true);
-			config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
+			config_save_safe(App()->GetUserConfig(), "tmp",
+					 nullptr);
 		}
 	};
 
-	bool warned = config_get_bool(App()->GlobalConfig(), "General",
+	bool warned = config_get_bool(App()->GetUserConfig(), "General",
 				      "WarnedAboutClosingDocks");
 	if (!OBSBasic::Get()->Closing() && !warned) {
 		QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection,

+ 2 - 2
UI/window-extra-browsers.cpp

@@ -473,7 +473,7 @@ void OBSBasic::ClearExtraBrowserDocks()
 void OBSBasic::LoadExtraBrowserDocks()
 {
 	const char *jsonStr = config_get_string(
-		App()->GlobalConfig(), "BasicWindow", "ExtraBrowserDocks");
+		App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks");
 
 	std::string err;
 	Json json = Json::parse(jsonStr, err);
@@ -511,7 +511,7 @@ void OBSBasic::SaveExtraBrowserDocks()
 	}
 
 	std::string output = Json(array).dump();
-	config_set_string(App()->GlobalConfig(), "BasicWindow",
+	config_set_string(App()->GetUserConfig(), "BasicWindow",
 			  "ExtraBrowserDocks", output.c_str());
 }
 

+ 5 - 5
UI/window-importer.cpp

@@ -437,7 +437,7 @@ OBSImporter::OBSImporter(QWidget *parent)
 
 	ImportersInit();
 
-	bool autoSearchPrompt = config_get_bool(App()->GlobalConfig(),
+	bool autoSearchPrompt = config_get_bool(App()->GetUserConfig(),
 						"General", "AutoSearchPrompt");
 
 	if (!autoSearchPrompt) {
@@ -446,18 +446,18 @@ OBSImporter::OBSImporter(QWidget *parent)
 			QTStr("Importer.AutomaticCollectionText"));
 
 		if (button == QMessageBox::Yes) {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"AutomaticCollectionSearch", true);
 		} else {
-			config_set_bool(App()->GlobalConfig(), "General",
+			config_set_bool(App()->GetUserConfig(), "General",
 					"AutomaticCollectionSearch", false);
 		}
 
-		config_set_bool(App()->GlobalConfig(), "General",
+		config_set_bool(App()->GetUserConfig(), "General",
 				"AutoSearchPrompt", true);
 	}
 
-	bool autoSearch = config_get_bool(App()->GlobalConfig(), "General",
+	bool autoSearch = config_get_bool(App()->GetUserConfig(), "General",
 					  "AutomaticCollectionSearch");
 
 	OBSImporterFiles f;

+ 1 - 1
UI/window-permissions.cpp

@@ -96,7 +96,7 @@ void OBSPermissions::on_accessibilityPermissionButton_clicked()
 
 void OBSPermissions::on_continueButton_clicked()
 {
-	config_set_int(GetGlobalConfig(), "General",
+	config_set_int(App()->GetAppConfig(), "General",
 		       "MacOSPermissionsDialogLastShown",
 		       MACOS_PERMISSIONS_DIALOG_VERSION);
 	close();

+ 10 - 9
UI/window-projector.cpp

@@ -28,7 +28,7 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
 				  "destroy", OBSSourceDestroyed, this);
 	}
 
-	isAlwaysOnTop = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	isAlwaysOnTop = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					"ProjectorAlwaysOnTop");
 
 	if (isAlwaysOnTop)
@@ -144,7 +144,7 @@ void OBSProjector::SetHideCursor()
 	if (savedMonitor == -1)
 		return;
 
-	bool hideCursor = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool hideCursor = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					  "HideProjectorCursor");
 
 	if (hideCursor && type != ProjectorType::Multiview)
@@ -331,20 +331,21 @@ void OBSProjector::EscapeTriggered()
 void OBSProjector::UpdateMultiview()
 {
 	MultiviewLayout multiviewLayout = static_cast<MultiviewLayout>(
-		config_get_int(GetGlobalConfig(), "BasicWindow",
+		config_get_int(App()->GetUserConfig(), "BasicWindow",
 			       "MultiviewLayout"));
 
-	bool drawLabel = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	bool drawLabel = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "MultiviewDrawNames");
 
-	bool drawSafeArea = config_get_bool(GetGlobalConfig(), "BasicWindow",
-					    "MultiviewDrawAreas");
+	bool drawSafeArea = config_get_bool(
+		App()->GetUserConfig(), "BasicWindow", "MultiviewDrawAreas");
 
-	mouseSwitching = config_get_bool(GetGlobalConfig(), "BasicWindow",
+	mouseSwitching = config_get_bool(App()->GetUserConfig(), "BasicWindow",
 					 "MultiviewMouseSwitch");
 
-	transitionOnDoubleClick = config_get_bool(
-		GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick");
+	transitionOnDoubleClick = config_get_bool(App()->GetUserConfig(),
+						  "BasicWindow",
+						  "TransitionOnDoubleClick");
 
 	multiview->Update(multiviewLayout, drawLabel, drawSafeArea);
 }