From 5993834815eee0d2e6dbc7721bc362306c2af3f7 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 15 Jul 2018 18:50:33 -0700 Subject: [PATCH] libobs: Change groups to actual public types (This commit also modifies UI) Changes groups to their own independent type, "group". This allows them to be used like other regular types, and allows the ability to reference groups in multiple scenes. Before, a group would always be linked to the scene it was in. This made it cumbersome for users to modify groups if they had a similar group in multiple scenes (they would have to modify each group in each scene). Making groups like other source types makes more sense to solve this issue so they can be referenced in multiple scenes at once. This also removes a significant amount of group-specific handling code required for implementing groups in the front-end. One limitation however: due to the way sub-items of groups are seamlessly modifiable and sortable as part of the whole scene, the user cannot have multiple references to the same group within one scene. --- UI/source-tree.cpp | 30 +++++----- UI/window-basic-main.cpp | 32 +++++++++- UI/window-basic-source-select.cpp | 21 +++++++ UI/window-basic-source-select.hpp | 1 + libobs/obs-scene.c | 97 +++++++++++++++++-------------- libobs/obs.c | 12 +++- 6 files changed, 128 insertions(+), 65 deletions(-) diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 72cd516f2..29b0c9c57 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -228,16 +228,10 @@ void SourceTreeItem::ExitEditMode(bool save) /* ----------------------------------------- */ /* check for existing source */ - bool exists = false; - - if (obs_sceneitem_is_group(sceneitem)) { - exists = !!obs_scene_get_group(scene, newName.c_str()); - } else { - obs_source_t *existingSource = - obs_get_source_by_name(newName.c_str()); - obs_source_release(existingSource); - exists = !!existingSource; - } + obs_source_t *existingSource = + obs_get_source_by_name(newName.c_str()); + obs_source_release(existingSource); + bool exists = !!existingSource; if (exists) { OBSMessageBox::information(main, @@ -550,11 +544,15 @@ void SourceTreeModel::ReorderItems() void SourceTreeModel::Add(obs_sceneitem_t *item) { - beginInsertRows(QModelIndex(), 0, 0); - items.insert(0, item); - endInsertRows(); + if (obs_sceneitem_is_group(item)) { + SceneChanged(); + } else { + beginInsertRows(QModelIndex(), 0, 0); + items.insert(0, item); + endInsertRows(); - st->UpdateWidget(createIndex(0, 0, nullptr), item); + st->UpdateWidget(createIndex(0, 0, nullptr), item); + } } void SourceTreeModel::Remove(obs_sceneitem_t *item) @@ -652,8 +650,8 @@ QString SourceTreeModel::GetNewGroupName() int i = 2; for (;;) { - obs_sceneitem_t *group = obs_scene_get_group(scene, - QT_TO_UTF8(name)); + obs_source_t *group = obs_get_source_by_name(QT_TO_UTF8(name)); + obs_source_release(group); if (!group) break; name = QTStr("Basic.Main.Group").arg(QString::number(i++)); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index d23b8ae81..575f5677c 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -315,8 +315,14 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, SaveAudioDevice(AUX_AUDIO_2, 4, saveData, audioSources); SaveAudioDevice(AUX_AUDIO_3, 5, saveData, audioSources); + /* -------------------------------- */ + /* save non-group sources */ + auto FilterAudioSources = [&](obs_source_t *source) { + if (obs_source_is_group(source)) + return false; + return find(begin(audioSources), end(audioSources), source) == end(audioSources); }; @@ -328,6 +334,18 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, return (*static_cast(data))(source); }, static_cast(&FilterAudioSources)); + /* -------------------------------- */ + /* save group sources separately */ + + /* saving separately ensures they won't be loaded in older versions */ + obs_data_array_t *groupsArray = obs_save_sources_filtered( + [](void*, obs_source_t *source) + { + return obs_source_is_group(source); + }, nullptr); + + /* -------------------------------- */ + obs_source_t *transition = obs_get_output_source(0); obs_source_t *currentScene = obs_scene_get_source(scene); const char *sceneName = obs_source_get_name(currentScene); @@ -341,10 +359,12 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, obs_data_set_array(saveData, "scene_order", sceneOrder); obs_data_set_string(saveData, "name", sceneCollection); obs_data_set_array(saveData, "sources", sourcesArray); + obs_data_set_array(saveData, "groups", groupsArray); obs_data_set_array(saveData, "quick_transitions", quickTransitionData); obs_data_set_array(saveData, "transitions", transitions); obs_data_set_array(saveData, "saved_projectors", savedProjectorList); obs_data_array_release(sourcesArray); + obs_data_array_release(groupsArray); obs_data_set_string(saveData, "current_transition", obs_source_get_name(transition)); @@ -719,6 +739,7 @@ void OBSBasic::Load(const char *file) obs_data_array_t *sceneOrder = obs_data_get_array(data, "scene_order"); obs_data_array_t *sources = obs_data_get_array(data, "sources"); + obs_data_array_t *groups = obs_data_get_array(data, "groups"); obs_data_array_t *transitions= obs_data_get_array(data, "transitions"); const char *sceneName = obs_data_get_string(data, "current_scene"); @@ -759,6 +780,13 @@ void OBSBasic::Load(const char *file) LoadAudioDevice(AUX_AUDIO_2, 4, data); LoadAudioDevice(AUX_AUDIO_3, 5, data); + if (!sources) { + sources = groups; + groups = nullptr; + } else { + obs_data_array_push_back_array(sources, groups); + } + obs_load_sources(sources, nullptr, nullptr); if (transitions) @@ -803,6 +831,7 @@ retryScene: obs_source_release(curProgramScene); obs_data_array_release(sources); + obs_data_array_release(groups); obs_data_array_release(sceneOrder); /* ------------------- */ @@ -4043,8 +4072,9 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu() popup->addSeparator(); QAction *addGroup = new QAction(QTStr("Group"), this); + addGroup->setData(QT_UTF8("group")); connect(addGroup, SIGNAL(triggered(bool)), - ui->sources, SLOT(AddGroup())); + this, SLOT(AddSourceFromAction())); popup->addAction(addGroup); if (!foundDeprecated) { diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index 72b355806..da81782c4 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -38,6 +38,25 @@ bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source) return true; } +bool OBSBasicSourceSelect::EnumGroups(void *data, obs_source_t *source) +{ + OBSBasicSourceSelect *window = static_cast(data); + const char *name = obs_source_get_name(source); + const char *id = obs_source_get_id(source); + + if (strcmp(id, window->id) == 0) { + OBSBasic *main = reinterpret_cast( + App()->GetMainWindow()); + OBSScene scene = main->GetCurrentScene(); + + obs_sceneitem_t *existing = obs_scene_get_group(scene, name); + if (!existing) + window->ui->sourceList->addItem(QT_UTF8(name)); + } + + return true; +} + void OBSBasicSourceSelect::OBSSourceAdded(void *data, calldata_t *calldata) { OBSBasicSourceSelect *window = static_cast(data); @@ -277,6 +296,8 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) const char *name = obs_source_get_name(sceneSource); ui->sourceList->addItem(QT_UTF8(name)); } + } else if (strcmp(id_, "group") == 0) { + obs_enum_sources(EnumGroups, this); } else { obs_enum_sources(EnumSources, this); } diff --git a/UI/window-basic-source-select.hpp b/UI/window-basic-source-select.hpp index 7bab4768c..90496798c 100644 --- a/UI/window-basic-source-select.hpp +++ b/UI/window-basic-source-select.hpp @@ -32,6 +32,7 @@ private: const char *id; static bool EnumSources(void *data, obs_source_t *source); + static bool EnumGroups(void *data, obs_source_t *source); static void OBSSourceRemoved(void *data, calldata_t *calldata); static void OBSSourceAdded(void *data, calldata_t *calldata); diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index e7c718fce..65707166a 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -20,7 +20,10 @@ #include "graphics/math-defs.h" #include "obs-scene.h" +const struct obs_source_info group_info; + static void resize_group(obs_sceneitem_t *group); +static void resize_scene(obs_scene_t *scene); static void signal_parent(obs_scene_t *parent, const char *name, calldata_t *params); static void get_ungrouped_transform(obs_sceneitem_t *group, @@ -67,12 +70,25 @@ static const char *scene_getname(void *unused) return "Scene"; } +static const char *group_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return "Group"; +} + static void *scene_create(obs_data_t *settings, struct obs_source *source) { pthread_mutexattr_t attr; struct obs_scene *scene = bzalloc(sizeof(struct obs_scene)); scene->source = source; + if (source->info.id == group_info.id) { + scene->is_group = true; + scene->custom_size = true; + scene->cx = 0; + scene->cy = 0; + } + signal_handler_add_array(obs_source_get_signal_handler(source), obs_scene_signals); @@ -637,26 +653,17 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) obs_source_t *source; const char *scale_filter_str; struct obs_scene_item *item; - bool is_group; bool visible; bool lock; if (obs_data_get_bool(item_data, "group_item_backup")) return; - is_group = obs_data_get_bool(item_data, "is_group"); - - if (is_group) { - obs_scene_t *sub_scene = obs_scene_create_private(name); - source = sub_scene->source; - - } else { - source = obs_get_source_by_name(name); - if (!source) { - blog(LOG_WARNING, "[scene_load_item] Source %s not " - "found!", name); - return; - } + source = obs_get_source_by_name(name); + if (!source) { + blog(LOG_WARNING, "[scene_load_item] Source %s not " + "found!", name); + return; } item = obs_scene_add(scene, source); @@ -669,10 +676,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) return; } - if (is_group) { - item->is_group = true; - ((obs_scene_t *)source->context.data)->is_group = true; - } + item->is_group = source->info.id == group_info.id; obs_data_set_default_int(item_data, "align", OBS_ALIGN_TOP | OBS_ALIGN_LEFT); @@ -708,13 +712,6 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right"); item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom"); - if (item->is_group) { - obs_data_t *group_data = obs_data_get_obj(item_data, - "group_source"); - scene_load(source->context.data, group_data); - obs_data_release(group_data); - } - scale_filter_str = obs_data_get_string(item_data, "scale_filter"); item->scale_filter = OBS_SCALE_DISABLE; @@ -808,16 +805,12 @@ static void scene_save_item(obs_data_array_t *array, obs_data_set_int (item_data, "crop_right", (int)item->crop.right); obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom); obs_data_set_int (item_data, "id", item->id); - obs_data_set_bool (item_data, "is_group", item->is_group); obs_data_set_bool (item_data, "group_item_backup", !!backup_group); if (item->is_group) { - obs_data_t *group_data = obs_data_create(); obs_scene_t *group_scene = item->source->context.data; obs_sceneitem_t *group_item; - scene_save(group_scene, group_data); - /* save group items as part of main scene, but ignored. * causes an automatic ungroup if scene collection file * is loaded in previous versions. */ @@ -830,9 +823,6 @@ static void scene_save_item(obs_data_array_t *array, } full_unlock(group_scene); - - obs_data_set_obj(item_data, "group_source", group_data); - obs_data_release(group_data); } if (item->scale_filter == OBS_SCALE_POINT) @@ -1125,6 +1115,27 @@ const struct obs_source_info scene_info = .enum_all_sources = scene_enum_all_sources }; +const struct obs_source_info group_info = +{ + .id = "group", + .type = OBS_SOURCE_TYPE_SCENE, + .output_flags = OBS_SOURCE_VIDEO | + OBS_SOURCE_CUSTOM_DRAW | + OBS_SOURCE_COMPOSITE, + .get_name = group_getname, + .create = scene_create, + .destroy = scene_destroy, + .video_tick = scene_video_tick, + .video_render = scene_video_render, + .audio_render = scene_audio_render, + .get_width = scene_getwidth, + .get_height = scene_getheight, + .load = scene_load, + .save = scene_save, + .enum_active_sources = scene_enum_active_sources, + .enum_all_sources = scene_enum_all_sources +}; + static inline obs_scene_t *create_id(const char *id, const char *name) { struct obs_source *source = obs_source_create(id, name, NULL, @@ -1505,8 +1516,7 @@ static inline bool source_has_audio(obs_source_t *source) } static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, - obs_source_t *source, obs_sceneitem_t *insert_after, - bool is_group) + obs_source_t *source, obs_sceneitem_t *insert_after) { struct obs_scene_item *last; struct obs_scene_item *item; @@ -1546,7 +1556,7 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, item->actions_mutex = mutex; item->user_visible = true; item->locked = false; - item->is_group = is_group; + item->is_group = source->info.id == group_info.id; item->private_settings = obs_data_create(); os_atomic_set_long(&item->active_refs, 1); vec2_set(&item->scale, 1.0f, 1.0f); @@ -1602,8 +1612,7 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) { - obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL, - false); + obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL); struct calldata params; uint8_t stack[128]; @@ -2425,12 +2434,11 @@ obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, return NULL; } - obs_scene_t *sub_scene = create_private_id("scene", name); + obs_scene_t *sub_scene = create_id("group", name); obs_sceneitem_t *last_item = items ? items[count - 1] : NULL; obs_sceneitem_t *item = obs_scene_add_internal( - scene, sub_scene->source, last_item, true); - sub_scene->custom_size = true; + scene, sub_scene->source, last_item); obs_scene_release(sub_scene); @@ -2531,6 +2539,8 @@ void obs_sceneitem_group_ungroup(obs_sceneitem_t *item) last = last->next; } subscene->first_item = NULL; + subscene->cx = 0; + subscene->cy = 0; full_unlock(subscene); /* ------------------------- */ @@ -2822,10 +2832,7 @@ obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene, bool obs_source_is_group(const obs_source_t *source) { - if (!source || source->info.id != scene_info.id) - return false; - - return ((obs_scene_t *)source->context.data)->is_group; + return source && source->info.id == group_info.id; } bool obs_scene_is_group(const obs_scene_t *scene) @@ -2840,7 +2847,7 @@ void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, if (!group || !group->is_group) return; - obs_scene_t *scene = obs_scene_from_source(group->source); + obs_scene_t *scene = group->source->context.data; if (scene) obs_scene_enum_items(scene, callback, param); } diff --git a/libobs/obs.c b/libobs/obs.c index f835b05a1..ef4db62b1 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -741,6 +741,7 @@ static inline void obs_free_hotkeys(void) } extern const struct obs_source_info scene_info; +extern const struct obs_source_info group_info; extern void log_system_info(void); @@ -771,6 +772,7 @@ static bool obs_init(const char *locale, const char *module_config_path, obs->module_config_path = bstrdup(module_config_path); obs->locale = bstrdup(locale); obs_register_source(&scene_info); + obs_register_source(&group_info); add_default_module_paths(); return true; } @@ -1304,10 +1306,14 @@ void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param) obs_source_t *next_source = (obs_source_t*)source->context.next; - if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 && - !source->context.private && - !enum_proc(param, source)) + if (source->info.id == group_info.id && + !enum_proc(param, source)) { break; + } else if (source->info.type == OBS_SOURCE_TYPE_INPUT && + !source->context.private && + !enum_proc(param, source)) { + break; + } source = next_source; }