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.master
parent
f45247d5c8
commit
5993834815
|
@ -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++));
|
||||
|
|
|
@ -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<FilterAudioSources_t*>(data))(source);
|
||||
}, static_cast<void*>(&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) {
|
||||
|
|
|
@ -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<OBSBasicSourceSelect*>(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<OBSBasic*>(
|
||||
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<OBSBasicSourceSelect*>(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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
12
libobs/obs.c
12
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue