mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
libobs: Add scene item grouping
Allows the ability to group scene items. Groups internally are sub-scenes, which allows the ability to add unique filters and transforms to each group.
This commit is contained in:
parent
690f738a76
commit
26d5560da3
@ -101,6 +101,23 @@ Scene Item Crop Structure (obs_sceneitem_crop)
|
|||||||
Bottom crop value.
|
Bottom crop value.
|
||||||
|
|
||||||
|
|
||||||
|
Scene Item Order Info Structure (*obs_sceneitem_order_info)
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
.. type:: struct obs_sceneitem_order_info
|
||||||
|
|
||||||
|
Scene item order info structure.
|
||||||
|
|
||||||
|
.. member:: obs_sceneitem_t *obs_sceneitem_order_info.group
|
||||||
|
|
||||||
|
Specifies the group this scene item belongs to, or *NULL* if none.
|
||||||
|
|
||||||
|
.. member:: obs_sceneitem_t *obs_sceneitem_order_info.item
|
||||||
|
|
||||||
|
Specifies the scene item.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _scene_signal_reference:
|
.. _scene_signal_reference:
|
||||||
|
|
||||||
Scene Signals
|
Scene Signals
|
||||||
@ -223,6 +240,12 @@ General Scene Functions
|
|||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
.. function:: bool obs_scene_reorder_items2(obs_scene_t *scene, struct obs_sceneitem_order_info *item_order, size_t item_order_size)
|
||||||
|
|
||||||
|
Reorders items within a scene with groups and group sub-items.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
.. _scene_item_reference:
|
.. _scene_item_reference:
|
||||||
|
|
||||||
@ -412,3 +435,119 @@ Scene Item Functions
|
|||||||
:return: An incremented reference to the private settings of the
|
:return: An incremented reference to the private settings of the
|
||||||
scene item. Allows the front-end to set custom information
|
scene item. Allows the front-end to set custom information
|
||||||
which is saved with the scene item
|
which is saved with the scene item
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
|
.. _scene_item_group_reference:
|
||||||
|
|
||||||
|
Scene Item Group Functions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. function:: obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name)
|
||||||
|
|
||||||
|
Adds a group with the specified name.
|
||||||
|
|
||||||
|
:param scene: Scene to add the group to
|
||||||
|
:param name: Name of the group
|
||||||
|
:return: The new group's scene item
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name, obs_sceneitem_t **items, size_t count)
|
||||||
|
|
||||||
|
Creates a group out of the specified scene items. The group will be
|
||||||
|
inserted at the top scene item.
|
||||||
|
|
||||||
|
:param scene: Scene to add the group to
|
||||||
|
:param name: Name of the group
|
||||||
|
:param items: Array of scene items to put in a group
|
||||||
|
:param count: Number of scene items in the array
|
||||||
|
:return: The new group's scene item
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name)
|
||||||
|
|
||||||
|
Finds a group within a scene by its name.
|
||||||
|
|
||||||
|
:param scene: Scene to find the group within
|
||||||
|
:param name: The name of the group to find
|
||||||
|
:return: The group scene item, or *NULL* if not found
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: bool obs_sceneitem_is_group(obs_sceneitem_t *item)
|
||||||
|
|
||||||
|
:param item: Scene item
|
||||||
|
:return: *true* if scene item is a group, *false* otherwise
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: obs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *group)
|
||||||
|
|
||||||
|
:param group: Group scene item
|
||||||
|
:return: Scene of the group, or *NULL* if not a group
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: void obs_sceneitem_group_ungroup(obs_sceneitem_t *group)
|
||||||
|
|
||||||
|
Ungroups the specified group. Scene items within the group will be
|
||||||
|
placed where the group was.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item)
|
||||||
|
|
||||||
|
Adds a scene item to a group.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: void obs_sceneitem_group_remove_item(obs_sceneitem_t *item)
|
||||||
|
|
||||||
|
Rmoves a scene item from a group. The item will be placed before the
|
||||||
|
group in the main scene.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: obs_sceneitem_t *obs_sceneitem_get_group(obs_sceneitem_t *item)
|
||||||
|
|
||||||
|
Returns the parent group of a scene item.
|
||||||
|
|
||||||
|
:param item: Scene item to get the group of
|
||||||
|
:return: The parent group of the scene item, or *NULL* if not in
|
||||||
|
a group
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: obs_sceneitem_t *obs_sceneitem_group_from_scene(obs_scene_t *scene)
|
||||||
|
|
||||||
|
:return: The group associated with the scene, or *NULL* if the
|
||||||
|
specified scene is not a group.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: obs_sceneitem_t *obs_sceneitem_group_from_source(obs_source_t *source)
|
||||||
|
|
||||||
|
:return: The group associated with the scene's source, or *NULL* if
|
||||||
|
the specified source is not a group.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), void *param)
|
||||||
|
|
||||||
|
Enumerates scene items within a group.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. function:: void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item)
|
||||||
|
.. function:: void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item)
|
||||||
|
|
||||||
|
Allows the ability to call any one of the transform functions on
|
||||||
|
scene items within a group without updating the internal matrices of
|
||||||
|
the group until obs_sceneitem_defer_group_resize_end has been called.
|
||||||
|
|
||||||
|
This is necessary if the user is resizing items while they are within
|
||||||
|
a group, as the group's transform will automatically update its
|
||||||
|
transform every frame otherwise.
|
||||||
|
@ -20,8 +20,13 @@
|
|||||||
#include "graphics/math-defs.h"
|
#include "graphics/math-defs.h"
|
||||||
#include "obs-scene.h"
|
#include "obs-scene.h"
|
||||||
|
|
||||||
|
static void resize_group(obs_sceneitem_t *group);
|
||||||
static void signal_parent(obs_scene_t *parent, const char *name,
|
static void signal_parent(obs_scene_t *parent, const char *name,
|
||||||
calldata_t *params);
|
calldata_t *params);
|
||||||
|
static void get_ungrouped_transform(obs_sceneitem_t *group,
|
||||||
|
struct vec2 *pos,
|
||||||
|
struct vec2 *scale,
|
||||||
|
float *rot);
|
||||||
|
|
||||||
/* NOTE: For proper mutex lock order (preventing mutual cross-locks), never
|
/* NOTE: For proper mutex lock order (preventing mutual cross-locks), never
|
||||||
* lock the graphics mutex inside either of the scene mutexes.
|
* lock the graphics mutex inside either of the scene mutexes.
|
||||||
@ -412,7 +417,7 @@ static inline bool item_is_scene(const struct obs_scene_item *item)
|
|||||||
static inline bool item_texture_enabled(const struct obs_scene_item *item)
|
static inline bool item_texture_enabled(const struct obs_scene_item *item)
|
||||||
{
|
{
|
||||||
return crop_enabled(&item->crop) || scale_filter_enabled(item) ||
|
return crop_enabled(&item->crop) || scale_filter_enabled(item) ||
|
||||||
item_is_scene(item);
|
(item_is_scene(item) && !item->is_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_item_texture(struct obs_scene_item *item)
|
static void render_item_texture(struct obs_scene_item *item)
|
||||||
@ -527,7 +532,10 @@ static void scene_video_tick(void *data, float seconds)
|
|||||||
static void update_transforms_and_prune_sources(obs_scene_t *scene,
|
static void update_transforms_and_prune_sources(obs_scene_t *scene,
|
||||||
struct darray *remove_items)
|
struct darray *remove_items)
|
||||||
{
|
{
|
||||||
|
obs_sceneitem_t *group_sceneitem = scene->group_sceneitem;
|
||||||
struct obs_scene_item *item = scene->first_item;
|
struct obs_scene_item *item = scene->first_item;
|
||||||
|
bool rebuild_group = group_sceneitem &&
|
||||||
|
os_atomic_load_bool(&group_sceneitem->update_group_resize);
|
||||||
|
|
||||||
while (item) {
|
while (item) {
|
||||||
if (obs_source_removed(item->source)) {
|
if (obs_source_removed(item->source)) {
|
||||||
@ -537,17 +545,31 @@ static void update_transforms_and_prune_sources(obs_scene_t *scene,
|
|||||||
remove_without_release(del_item);
|
remove_without_release(del_item);
|
||||||
darray_push_back(sizeof(struct obs_scene_item*),
|
darray_push_back(sizeof(struct obs_scene_item*),
|
||||||
remove_items, &del_item);
|
remove_items, &del_item);
|
||||||
|
rebuild_group = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item->is_group) {
|
||||||
|
obs_scene_t *group_scene = item->source->context.data;
|
||||||
|
|
||||||
|
video_lock(group_scene);
|
||||||
|
update_transforms_and_prune_sources(group_scene,
|
||||||
|
remove_items);
|
||||||
|
video_unlock(group_scene);
|
||||||
|
}
|
||||||
|
|
||||||
if (os_atomic_load_bool(&item->update_transform) ||
|
if (os_atomic_load_bool(&item->update_transform) ||
|
||||||
source_size_changed(item)) {
|
source_size_changed(item)) {
|
||||||
|
|
||||||
update_item_transform(item);
|
update_item_transform(item);
|
||||||
|
rebuild_group = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
item = item->next;
|
item = item->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rebuild_group && scene->group_sceneitem)
|
||||||
|
resize_group(scene->group_sceneitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scene_video_render(void *data, gs_effect_t *effect)
|
static void scene_video_render(void *data, gs_effect_t *effect)
|
||||||
@ -560,6 +582,10 @@ static void scene_video_render(void *data, gs_effect_t *effect)
|
|||||||
|
|
||||||
video_lock(scene);
|
video_lock(scene);
|
||||||
|
|
||||||
|
if (!scene->group_sceneitem) {
|
||||||
|
update_transforms_and_prune_sources(scene, &remove_items.da);
|
||||||
|
}
|
||||||
|
|
||||||
gs_blend_state_push();
|
gs_blend_state_push();
|
||||||
gs_reset_blend_state();
|
gs_reset_blend_state();
|
||||||
|
|
||||||
@ -603,19 +629,34 @@ static void set_visibility(struct obs_scene_item *item, bool vis)
|
|||||||
pthread_mutex_unlock(&item->actions_mutex);
|
pthread_mutex_unlock(&item->actions_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void scene_load(void *data, obs_data_t *settings);
|
||||||
|
|
||||||
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
||||||
{
|
{
|
||||||
const char *name = obs_data_get_string(item_data, "name");
|
const char *name = obs_data_get_string(item_data, "name");
|
||||||
obs_source_t *source = obs_get_source_by_name(name);
|
obs_source_t *source;
|
||||||
const char *scale_filter_str;
|
const char *scale_filter_str;
|
||||||
struct obs_scene_item *item;
|
struct obs_scene_item *item;
|
||||||
|
bool is_group;
|
||||||
bool visible;
|
bool visible;
|
||||||
bool lock;
|
bool lock;
|
||||||
|
|
||||||
if (!source) {
|
if (obs_data_get_bool(item_data, "group_item_backup"))
|
||||||
blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
|
|
||||||
name);
|
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item = obs_scene_add(scene, source);
|
item = obs_scene_add(scene, source);
|
||||||
@ -628,6 +669,11 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_group) {
|
||||||
|
item->is_group = true;
|
||||||
|
((obs_scene_t *)source->context.data)->group_sceneitem = item;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_set_default_int(item_data, "align",
|
obs_data_set_default_int(item_data, "align",
|
||||||
OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
|
OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
|
||||||
|
|
||||||
@ -662,6 +708,13 @@ 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.right = (uint32_t)obs_data_get_int(item_data, "crop_right");
|
||||||
item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom");
|
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");
|
scale_filter_str = obs_data_get_string(item_data, "scale_filter");
|
||||||
item->scale_filter = OBS_SCALE_DISABLE;
|
item->scale_filter = OBS_SCALE_DISABLE;
|
||||||
|
|
||||||
@ -723,19 +776,29 @@ static void scene_load(void *data, obs_data_t *settings)
|
|||||||
obs_data_array_release(items);
|
obs_data_array_release(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void scene_save(void *data, obs_data_t *settings);
|
||||||
|
|
||||||
static void scene_save_item(obs_data_array_t *array,
|
static void scene_save_item(obs_data_array_t *array,
|
||||||
struct obs_scene_item *item)
|
struct obs_scene_item *item, bool group_item_backup)
|
||||||
{
|
{
|
||||||
obs_data_t *item_data = obs_data_create();
|
obs_data_t *item_data = obs_data_create();
|
||||||
const char *name = obs_source_get_name(item->source);
|
const char *name = obs_source_get_name(item->source);
|
||||||
const char *scale_filter;
|
const char *scale_filter;
|
||||||
|
struct vec2 pos = item->pos;
|
||||||
|
struct vec2 scale = item->scale;
|
||||||
|
float rot = item->rot;
|
||||||
|
|
||||||
|
if (group_item_backup) {
|
||||||
|
obs_sceneitem_t *group = item->parent->group_sceneitem;
|
||||||
|
get_ungrouped_transform(group, &pos, &scale, &rot);
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_set_string(item_data, "name", name);
|
obs_data_set_string(item_data, "name", name);
|
||||||
obs_data_set_bool (item_data, "visible", item->user_visible);
|
obs_data_set_bool (item_data, "visible", item->user_visible);
|
||||||
obs_data_set_bool (item_data, "locked", item->locked);
|
obs_data_set_bool (item_data, "locked", item->locked);
|
||||||
obs_data_set_double(item_data, "rot", item->rot);
|
obs_data_set_double(item_data, "rot", rot);
|
||||||
obs_data_set_vec2 (item_data, "pos", &item->pos);
|
obs_data_set_vec2 (item_data, "pos", &pos);
|
||||||
obs_data_set_vec2 (item_data, "scale", &item->scale);
|
obs_data_set_vec2 (item_data, "scale", &scale);
|
||||||
obs_data_set_int (item_data, "align", (int)item->align);
|
obs_data_set_int (item_data, "align", (int)item->align);
|
||||||
obs_data_set_int (item_data, "bounds_type", (int)item->bounds_type);
|
obs_data_set_int (item_data, "bounds_type", (int)item->bounds_type);
|
||||||
obs_data_set_int (item_data, "bounds_align", (int)item->bounds_align);
|
obs_data_set_int (item_data, "bounds_align", (int)item->bounds_align);
|
||||||
@ -745,6 +808,32 @@ 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_right", (int)item->crop.right);
|
||||||
obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
|
obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
|
||||||
obs_data_set_int (item_data, "id", item->id);
|
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", group_item_backup);
|
||||||
|
|
||||||
|
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. */
|
||||||
|
full_lock(group_scene);
|
||||||
|
|
||||||
|
group_item = group_scene->first_item;
|
||||||
|
while (group_item) {
|
||||||
|
scene_save_item(array, group_item, true);
|
||||||
|
group_item = group_item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
if (item->scale_filter == OBS_SCALE_POINT)
|
||||||
scale_filter = "point";
|
scale_filter = "point";
|
||||||
@ -776,7 +865,7 @@ static void scene_save(void *data, obs_data_t *settings)
|
|||||||
|
|
||||||
item = scene->first_item;
|
item = scene->first_item;
|
||||||
while (item) {
|
while (item) {
|
||||||
scene_save_item(array, item);
|
scene_save_item(array, item, false);
|
||||||
item = item->next;
|
item = item->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1367,7 +1456,8 @@ static inline bool source_has_audio(obs_source_t *source)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
|
static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
|
||||||
obs_source_t *source, obs_sceneitem_t *insert_after)
|
obs_source_t *source, obs_sceneitem_t *insert_after,
|
||||||
|
bool is_group)
|
||||||
{
|
{
|
||||||
struct obs_scene_item *last;
|
struct obs_scene_item *last;
|
||||||
struct obs_scene_item *item;
|
struct obs_scene_item *item;
|
||||||
@ -1407,6 +1497,7 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
|
|||||||
item->actions_mutex = mutex;
|
item->actions_mutex = mutex;
|
||||||
item->user_visible = true;
|
item->user_visible = true;
|
||||||
item->locked = false;
|
item->locked = false;
|
||||||
|
item->is_group = is_group;
|
||||||
item->private_settings = obs_data_create();
|
item->private_settings = obs_data_create();
|
||||||
os_atomic_set_long(&item->active_refs, 1);
|
os_atomic_set_long(&item->active_refs, 1);
|
||||||
vec2_set(&item->scale, 1.0f, 1.0f);
|
vec2_set(&item->scale, 1.0f, 1.0f);
|
||||||
@ -1459,7 +1550,8 @@ 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 *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
|
||||||
{
|
{
|
||||||
obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL);
|
obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL,
|
||||||
|
false);
|
||||||
struct calldata params;
|
struct calldata params;
|
||||||
uint8_t stack[128];
|
uint8_t stack[128];
|
||||||
|
|
||||||
@ -1551,6 +1643,16 @@ static void signal_parent(obs_scene_t *parent, const char *command,
|
|||||||
{
|
{
|
||||||
calldata_set_ptr(params, "scene", parent);
|
calldata_set_ptr(params, "scene", parent);
|
||||||
signal_handler_signal(parent->source->context.signals, command, params);
|
signal_handler_signal(parent->source->context.signals, command, params);
|
||||||
|
|
||||||
|
if (parent->group_sceneitem) {
|
||||||
|
parent = parent->group_sceneitem->parent;
|
||||||
|
if (!parent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
calldata_set_ptr(params, "scene", parent);
|
||||||
|
signal_handler_signal(parent->source->context.signals, command,
|
||||||
|
params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void obs_sceneitem_select(obs_sceneitem_t *item, bool select)
|
void obs_sceneitem_select(obs_sceneitem_t *item, bool select)
|
||||||
@ -2055,6 +2157,23 @@ void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)
|
|||||||
os_atomic_set_bool(&item->update_transform, true);
|
os_atomic_set_bool(&item->update_transform, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
if (!obs_ptr_valid(item, "obs_sceneitem_defer_group_resize_begin"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
os_atomic_inc_long(&item->defer_group_resize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
if (!obs_ptr_valid(item, "obs_sceneitem_defer_group_resize_end"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (os_atomic_dec_long(&item->defer_group_resize) == 0)
|
||||||
|
os_atomic_set_bool(&item->update_group_resize, true);
|
||||||
|
}
|
||||||
|
|
||||||
int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)
|
int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)
|
||||||
{
|
{
|
||||||
if (!obs_ptr_valid(item, "obs_sceneitem_get_id"))
|
if (!obs_ptr_valid(item, "obs_sceneitem_get_id"))
|
||||||
@ -2071,3 +2190,577 @@ obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item)
|
|||||||
obs_data_addref(item->private_settings);
|
obs_data_addref(item->private_settings);
|
||||||
return item->private_settings;
|
return item->private_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline transform_val(struct vec2 *v2, struct matrix4 *transform)
|
||||||
|
{
|
||||||
|
struct vec3 v;
|
||||||
|
vec3_set(&v, v2->x, v2->y, 0.0f);
|
||||||
|
vec3_transform(&v, &v, transform);
|
||||||
|
v2->x = v.x;
|
||||||
|
v2->y = v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_ungrouped_transform(obs_sceneitem_t *group,
|
||||||
|
struct vec2 *pos,
|
||||||
|
struct vec2 *scale,
|
||||||
|
float *rot)
|
||||||
|
{
|
||||||
|
struct matrix4 transform;
|
||||||
|
struct matrix4 mat;
|
||||||
|
struct vec4 x_base;
|
||||||
|
|
||||||
|
vec4_set(&x_base, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
matrix4_copy(&transform, &group->draw_transform);
|
||||||
|
|
||||||
|
transform_val(pos, &transform);
|
||||||
|
vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
vec4_set(&mat.x, scale->x, 0.0f, 0.0f, 0.0f);
|
||||||
|
vec4_set(&mat.y, 0.0f, scale->y, 0.0f, 0.0f);
|
||||||
|
vec4_set(&mat.z, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||||
|
vec4_set(&mat.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
matrix4_mul(&mat, &mat, &transform);
|
||||||
|
|
||||||
|
scale->x = vec4_len(&mat.x);
|
||||||
|
scale->y = vec4_len(&mat.y);
|
||||||
|
*rot += group->rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_group_transform(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
obs_scene_t *parent = item->parent;
|
||||||
|
if (!parent || !parent->group_sceneitem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_sceneitem_t *group = parent->group_sceneitem;
|
||||||
|
get_ungrouped_transform(group, &item->pos, &item->scale, &item->rot);
|
||||||
|
|
||||||
|
update_item_transform(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_group_transform(obs_sceneitem_t *item, obs_sceneitem_t *group)
|
||||||
|
{
|
||||||
|
struct matrix4 transform;
|
||||||
|
struct matrix4 mat;
|
||||||
|
struct vec4 x_base;
|
||||||
|
|
||||||
|
vec4_set(&x_base, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
matrix4_inv(&transform, &group->draw_transform);
|
||||||
|
|
||||||
|
transform_val(&item->pos, &transform);
|
||||||
|
vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
vec4_set(&mat.x, item->scale.x, 0.0f, 0.0f, 0.0f);
|
||||||
|
vec4_set(&mat.y, 0.0f, item->scale.y, 0.0f, 0.0f);
|
||||||
|
vec4_set(&mat.z, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||||
|
vec4_set(&mat.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
matrix4_mul(&mat, &mat, &transform);
|
||||||
|
|
||||||
|
item->scale.x = vec4_len(&mat.x);
|
||||||
|
item->scale.y = vec4_len(&mat.y);
|
||||||
|
item->rot -= group->rot;
|
||||||
|
|
||||||
|
update_item_transform(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assumes group scene and parent scene is locked */
|
||||||
|
static void resize_group(obs_sceneitem_t *group)
|
||||||
|
{
|
||||||
|
obs_scene_t *scene = group->source->context.data;
|
||||||
|
struct vec2 minv;
|
||||||
|
struct vec2 maxv;
|
||||||
|
struct vec2 scale;
|
||||||
|
|
||||||
|
if (os_atomic_load_long(&group->defer_group_resize) > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vec2_set(&minv, M_INFINITE, M_INFINITE);
|
||||||
|
vec2_set(&maxv, -M_INFINITE, -M_INFINITE);
|
||||||
|
|
||||||
|
obs_sceneitem_t *item = scene->first_item;
|
||||||
|
if (!item) {
|
||||||
|
scene->cx = 0;
|
||||||
|
scene->cy = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (item) {
|
||||||
|
#define get_min_max(x_val, y_val) \
|
||||||
|
do { \
|
||||||
|
struct vec3 v; \
|
||||||
|
vec3_set(&v, x_val, y_val, 0.0f); \
|
||||||
|
vec3_transform(&v, &v, &item->box_transform); \
|
||||||
|
if (v.x < minv.x) minv.x = v.x; \
|
||||||
|
if (v.y < minv.y) minv.y = v.y; \
|
||||||
|
if (v.x > maxv.x) maxv.x = v.x; \
|
||||||
|
if (v.y > maxv.y) maxv.y = v.y; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
get_min_max(0.0f, 0.0f);
|
||||||
|
get_min_max(1.0f, 0.0f);
|
||||||
|
get_min_max(0.0f, 1.0f);
|
||||||
|
get_min_max(1.0f, 1.0f);
|
||||||
|
#undef get_min_max
|
||||||
|
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = scene->first_item;
|
||||||
|
while (item) {
|
||||||
|
vec2_sub(&item->pos, &item->pos, &minv);
|
||||||
|
update_item_transform(item);
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2_sub(&scale, &maxv, &minv);
|
||||||
|
scene->cx = (uint32_t)ceilf(scale.x);
|
||||||
|
scene->cy = (uint32_t)ceilf(scale.y);
|
||||||
|
|
||||||
|
if (group->bounds_type == OBS_BOUNDS_NONE) {
|
||||||
|
struct vec2 new_pos;
|
||||||
|
|
||||||
|
if ((group->align & OBS_ALIGN_LEFT) != 0)
|
||||||
|
new_pos.x = minv.x;
|
||||||
|
else if ((group->align & OBS_ALIGN_RIGHT) != 0)
|
||||||
|
new_pos.x = maxv.x;
|
||||||
|
else
|
||||||
|
new_pos.x = (maxv.x - minv.x) * 0.5f + minv.x;
|
||||||
|
|
||||||
|
if ((group->align & OBS_ALIGN_TOP) != 0)
|
||||||
|
new_pos.y = minv.y;
|
||||||
|
else if ((group->align & OBS_ALIGN_BOTTOM) != 0)
|
||||||
|
new_pos.y = maxv.y;
|
||||||
|
else
|
||||||
|
new_pos.y = (maxv.y - minv.y) * 0.5f + minv.y;
|
||||||
|
|
||||||
|
transform_val(&new_pos, &group->draw_transform);
|
||||||
|
vec2_copy(&group->pos, &new_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
os_atomic_set_bool(&group->update_group_resize, false);
|
||||||
|
|
||||||
|
update_item_transform(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name)
|
||||||
|
{
|
||||||
|
return obs_scene_insert_group(scene, name, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene,
|
||||||
|
const char *name, obs_sceneitem_t **items, size_t count)
|
||||||
|
{
|
||||||
|
if (!scene)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* don't allow groups or sub-items of other groups */
|
||||||
|
for (size_t i = count; i > 0; i--) {
|
||||||
|
obs_sceneitem_t *item = items[i - 1];
|
||||||
|
if (item->parent != scene || item->is_group)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_scene_t *sub_scene = obs_scene_create_private(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->group_sceneitem = item;
|
||||||
|
sub_scene->custom_size = true;
|
||||||
|
|
||||||
|
obs_scene_release(sub_scene);
|
||||||
|
|
||||||
|
if (!items || !count)
|
||||||
|
return item;
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_lock(scene);
|
||||||
|
full_lock(sub_scene);
|
||||||
|
sub_scene->first_item = items ? items[0] : NULL;
|
||||||
|
|
||||||
|
for (size_t i = count; i > 0; i--) {
|
||||||
|
size_t idx = i - 1;
|
||||||
|
remove_group_transform(items[idx]);
|
||||||
|
detach_sceneitem(items[idx]);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
size_t idx = i;
|
||||||
|
if (idx != (count - 1)) {
|
||||||
|
size_t next_idx = idx + 1;
|
||||||
|
items[idx]->next = items[next_idx];
|
||||||
|
items[next_idx]->prev = items[idx];
|
||||||
|
} else {
|
||||||
|
items[idx]->next = NULL;
|
||||||
|
}
|
||||||
|
items[idx]->parent = sub_scene;
|
||||||
|
apply_group_transform(items[idx], item);
|
||||||
|
}
|
||||||
|
items[0]->prev = NULL;
|
||||||
|
resize_group(item);
|
||||||
|
full_unlock(sub_scene);
|
||||||
|
full_unlock(scene);
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name)
|
||||||
|
{
|
||||||
|
if (!scene || !name || !*name) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *group = NULL;
|
||||||
|
obs_sceneitem_t *item;
|
||||||
|
|
||||||
|
full_lock(scene);
|
||||||
|
|
||||||
|
item = scene->first_item;
|
||||||
|
while (item) {
|
||||||
|
if (item->is_group && item->source->context.name) {
|
||||||
|
if (strcmp(item->source->context.name, name) == 0) {
|
||||||
|
group = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_unlock(scene);
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *obs_sceneitem_group_from_scene(obs_scene_t *scene)
|
||||||
|
{
|
||||||
|
return scene ? scene->group_sceneitem : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *obs_sceneitem_group_from_source(obs_source_t *source)
|
||||||
|
{
|
||||||
|
obs_scene_t *scene = obs_scene_from_source(source);
|
||||||
|
return obs_sceneitem_group_from_scene(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool obs_sceneitem_is_group(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
return item && item->is_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
return (item && item->is_group) ? item->source->context.data : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void obs_sceneitem_group_ungroup(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
if (!item || !item->is_group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_scene_t *scene = item->parent;
|
||||||
|
obs_scene_t *subscene = item->source->context.data;
|
||||||
|
obs_sceneitem_t *first;
|
||||||
|
obs_sceneitem_t *last;
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_lock(subscene);
|
||||||
|
first = subscene->first_item;
|
||||||
|
last = first;
|
||||||
|
while (last) {
|
||||||
|
remove_group_transform(last);
|
||||||
|
last->parent = scene;
|
||||||
|
if (!last->next)
|
||||||
|
break;
|
||||||
|
last = last->next;
|
||||||
|
}
|
||||||
|
subscene->first_item = NULL;
|
||||||
|
full_unlock(subscene);
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_lock(scene);
|
||||||
|
if (last) {
|
||||||
|
if (item->prev) {
|
||||||
|
first->prev = item->prev;
|
||||||
|
item->prev->next = first;
|
||||||
|
} else {
|
||||||
|
scene->first_item = first;
|
||||||
|
first->prev = NULL;
|
||||||
|
}
|
||||||
|
last->next = item->next;
|
||||||
|
if (last->next)
|
||||||
|
last->next->prev = last;
|
||||||
|
item->next = item->prev = NULL;
|
||||||
|
item->parent = NULL;
|
||||||
|
} else {
|
||||||
|
detach_sceneitem(item);
|
||||||
|
}
|
||||||
|
full_unlock(scene);
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
obs_sceneitem_release(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
if (!group || !group->is_group || !item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_scene_t *scene = group->parent;
|
||||||
|
obs_scene_t *groupscene = group->source->context.data;
|
||||||
|
obs_sceneitem_t *last;
|
||||||
|
|
||||||
|
if (item->parent != scene)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_lock(scene);
|
||||||
|
remove_group_transform(item);
|
||||||
|
detach_sceneitem(item);
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_lock(groupscene);
|
||||||
|
last = groupscene->first_item;
|
||||||
|
if (last) {
|
||||||
|
for (;;) {
|
||||||
|
if (!last->next)
|
||||||
|
break;
|
||||||
|
last = last->next;
|
||||||
|
}
|
||||||
|
last->next = item;
|
||||||
|
item->prev = last;
|
||||||
|
} else {
|
||||||
|
groupscene->first_item = item;
|
||||||
|
}
|
||||||
|
item->parent = groupscene;
|
||||||
|
item->next = NULL;
|
||||||
|
apply_group_transform(item, group);
|
||||||
|
resize_group(group);
|
||||||
|
full_unlock(groupscene);
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_unlock(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obs_sceneitem_group_remove_item(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_scene_t *groupscene = item->parent;
|
||||||
|
obs_sceneitem_t *groupitem = groupscene->group_sceneitem;
|
||||||
|
if (!groupitem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_scene_t *scene = groupitem->parent;
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
full_lock(scene);
|
||||||
|
full_lock(groupscene);
|
||||||
|
remove_group_transform(item);
|
||||||
|
detach_sceneitem(item);
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
if (groupitem->prev) {
|
||||||
|
groupitem->prev->next = item;
|
||||||
|
item->prev = groupitem->prev;
|
||||||
|
} else {
|
||||||
|
scene->first_item = item;
|
||||||
|
item->prev = NULL;
|
||||||
|
}
|
||||||
|
groupitem->prev = item;
|
||||||
|
item->next = groupitem;
|
||||||
|
item->parent = scene;
|
||||||
|
|
||||||
|
/* ------------------------- */
|
||||||
|
|
||||||
|
resize_group(groupitem);
|
||||||
|
full_unlock(groupscene);
|
||||||
|
full_unlock(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_current_order_info(obs_scene_t *scene,
|
||||||
|
struct obs_sceneitem_order_info **items_out,
|
||||||
|
size_t *size_out)
|
||||||
|
{
|
||||||
|
DARRAY(struct obs_sceneitem_order_info) items;
|
||||||
|
da_init(items);
|
||||||
|
|
||||||
|
obs_sceneitem_t *item = scene->first_item;
|
||||||
|
while (item) {
|
||||||
|
struct obs_sceneitem_order_info info = {
|
||||||
|
.group = NULL,
|
||||||
|
.item = item
|
||||||
|
};
|
||||||
|
|
||||||
|
da_push_back(items, &item);
|
||||||
|
|
||||||
|
if (item->is_group) {
|
||||||
|
obs_scene_t *sub_scene = item->source->context.data;
|
||||||
|
|
||||||
|
full_lock(sub_scene);
|
||||||
|
|
||||||
|
obs_sceneitem_t *sub_item = sub_scene->first_item;
|
||||||
|
|
||||||
|
while (sub_item) {
|
||||||
|
info.group = item;
|
||||||
|
info.item = sub_item;
|
||||||
|
da_push_back(items, &item);
|
||||||
|
|
||||||
|
sub_item = sub_item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_unlock(sub_scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*items_out = items.array;
|
||||||
|
*size_out = items.num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sceneitems_match2(obs_scene_t *scene,
|
||||||
|
struct obs_sceneitem_order_info *items, size_t size)
|
||||||
|
{
|
||||||
|
obs_sceneitem_t *item = scene->first_item;
|
||||||
|
struct obs_sceneitem_order_info *cur_items;
|
||||||
|
size_t cur_size;
|
||||||
|
|
||||||
|
build_current_order_info(scene, &cur_items, &cur_size);
|
||||||
|
if (cur_size != size) {
|
||||||
|
bfree(cur_items);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
struct obs_sceneitem_order_info *new = &items[i];
|
||||||
|
struct obs_sceneitem_order_info *old = &cur_items[i];
|
||||||
|
|
||||||
|
if (new->group != old->group || new->item != new->item) {
|
||||||
|
bfree(cur_items);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bfree(cur_items);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool obs_scene_reorder_items2(obs_scene_t *scene,
|
||||||
|
struct obs_sceneitem_order_info *item_order,
|
||||||
|
size_t item_order_size)
|
||||||
|
{
|
||||||
|
if (!scene || !item_order_size || !item_order)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
obs_scene_addref(scene);
|
||||||
|
full_lock(scene);
|
||||||
|
|
||||||
|
bool order_matches = true;
|
||||||
|
if (sceneitems_match2(scene, item_order, item_order_size)) {
|
||||||
|
full_unlock(scene);
|
||||||
|
obs_scene_release(scene);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < item_order_size; i++) {
|
||||||
|
struct obs_sceneitem_order_info *info = &item_order[i];
|
||||||
|
if (!info->item->is_group) {
|
||||||
|
remove_group_transform(info->item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene->first_item = item_order[0].item;
|
||||||
|
|
||||||
|
obs_sceneitem_t *prev = NULL;
|
||||||
|
for (size_t i = 0; i < item_order_size; i++) {
|
||||||
|
struct obs_sceneitem_order_info *info = &item_order[i];
|
||||||
|
obs_sceneitem_t *item = info->item;
|
||||||
|
|
||||||
|
if (info->item->is_group) {
|
||||||
|
obs_sceneitem_t *sub_prev = NULL;
|
||||||
|
obs_scene_t *sub_scene =
|
||||||
|
info->item->source->context.data;
|
||||||
|
|
||||||
|
sub_scene->first_item = NULL;
|
||||||
|
|
||||||
|
obs_scene_addref(sub_scene);
|
||||||
|
full_lock(sub_scene);
|
||||||
|
|
||||||
|
for (i++; i < item_order_size; i++) {
|
||||||
|
struct obs_sceneitem_order_info *sub_info =
|
||||||
|
&item_order[i];
|
||||||
|
obs_sceneitem_t *sub_item = sub_info->item;
|
||||||
|
|
||||||
|
if (sub_info->group != info->item) {
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sub_scene->first_item)
|
||||||
|
sub_scene->first_item = sub_item;
|
||||||
|
|
||||||
|
sub_item->prev = sub_prev;
|
||||||
|
sub_item->next = NULL;
|
||||||
|
sub_item->parent = sub_scene;
|
||||||
|
|
||||||
|
if (sub_prev)
|
||||||
|
sub_prev->next = sub_item;
|
||||||
|
|
||||||
|
apply_group_transform(sub_info->item,
|
||||||
|
sub_info->group);
|
||||||
|
|
||||||
|
sub_prev = sub_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize_group(info->item);
|
||||||
|
full_unlock(sub_scene);
|
||||||
|
obs_scene_release(sub_scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
item->prev = prev;
|
||||||
|
item->next = NULL;
|
||||||
|
item->parent = scene;
|
||||||
|
|
||||||
|
if (prev)
|
||||||
|
prev->next = item;
|
||||||
|
|
||||||
|
prev = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_unlock(scene);
|
||||||
|
|
||||||
|
signal_reorder(scene->first_item);
|
||||||
|
obs_scene_release(scene);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *obs_sceneitem_get_group(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
return item && item->parent ? item->parent->group_sceneitem : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void obs_sceneitem_group_enum_items(obs_sceneitem_t *group,
|
||||||
|
bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_scene_t *scene = obs_scene_from_source(group->source);
|
||||||
|
if (scene)
|
||||||
|
obs_scene_enum_items(scene, callback, param);
|
||||||
|
}
|
||||||
|
@ -32,7 +32,9 @@ struct obs_scene_item {
|
|||||||
volatile long ref;
|
volatile long ref;
|
||||||
volatile bool removed;
|
volatile bool removed;
|
||||||
|
|
||||||
|
bool is_group;
|
||||||
bool update_transform;
|
bool update_transform;
|
||||||
|
bool update_group_resize;
|
||||||
|
|
||||||
int64_t id;
|
int64_t id;
|
||||||
|
|
||||||
@ -40,6 +42,7 @@ struct obs_scene_item {
|
|||||||
struct obs_source *source;
|
struct obs_source *source;
|
||||||
volatile long active_refs;
|
volatile long active_refs;
|
||||||
volatile long defer_update;
|
volatile long defer_update;
|
||||||
|
volatile long defer_group_resize;
|
||||||
bool user_visible;
|
bool user_visible;
|
||||||
bool visible;
|
bool visible;
|
||||||
bool selected;
|
bool selected;
|
||||||
@ -82,6 +85,7 @@ struct obs_scene_item {
|
|||||||
|
|
||||||
struct obs_scene {
|
struct obs_scene {
|
||||||
struct obs_source *source;
|
struct obs_source *source;
|
||||||
|
struct obs_scene_item *group_sceneitem;
|
||||||
|
|
||||||
bool custom_size;
|
bool custom_size;
|
||||||
uint32_t cx;
|
uint32_t cx;
|
||||||
|
40
libobs/obs.h
40
libobs/obs.h
@ -1325,6 +1325,15 @@ EXPORT void obs_scene_enum_items(obs_scene_t *scene,
|
|||||||
EXPORT bool obs_scene_reorder_items(obs_scene_t *scene,
|
EXPORT bool obs_scene_reorder_items(obs_scene_t *scene,
|
||||||
obs_sceneitem_t * const *item_order, size_t item_order_size);
|
obs_sceneitem_t * const *item_order, size_t item_order_size);
|
||||||
|
|
||||||
|
struct obs_sceneitem_order_info {
|
||||||
|
obs_sceneitem_t *group;
|
||||||
|
obs_sceneitem_t *item;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT bool obs_scene_reorder_items2(obs_scene_t *scene,
|
||||||
|
struct obs_sceneitem_order_info *item_order,
|
||||||
|
size_t item_order_size);
|
||||||
|
|
||||||
/** Adds/creates a new scene item for a source */
|
/** Adds/creates a new scene item for a source */
|
||||||
EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);
|
EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);
|
||||||
|
|
||||||
@ -1421,6 +1430,37 @@ EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item);
|
|||||||
* automatically. Returns an incremented reference. */
|
* automatically. Returns an incremented reference. */
|
||||||
EXPORT obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item);
|
EXPORT obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item);
|
||||||
|
|
||||||
|
EXPORT obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene,
|
||||||
|
const char *name);
|
||||||
|
EXPORT obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene,
|
||||||
|
const char *name, obs_sceneitem_t **items, size_t count);
|
||||||
|
|
||||||
|
EXPORT obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
EXPORT bool obs_sceneitem_is_group(obs_sceneitem_t *item);
|
||||||
|
|
||||||
|
EXPORT obs_scene_t *obs_sceneitem_group_get_scene(
|
||||||
|
const obs_sceneitem_t *group);
|
||||||
|
|
||||||
|
EXPORT void obs_sceneitem_group_ungroup(obs_sceneitem_t *group);
|
||||||
|
|
||||||
|
EXPORT void obs_sceneitem_group_add_item(obs_sceneitem_t *group,
|
||||||
|
obs_sceneitem_t *item);
|
||||||
|
EXPORT void obs_sceneitem_group_remove_item(obs_sceneitem_t *item);
|
||||||
|
|
||||||
|
EXPORT obs_sceneitem_t *obs_sceneitem_get_group(obs_sceneitem_t *item);
|
||||||
|
|
||||||
|
EXPORT obs_sceneitem_t *obs_sceneitem_group_from_scene(obs_scene_t *scene);
|
||||||
|
EXPORT obs_sceneitem_t *obs_sceneitem_group_from_source(obs_source_t *source);
|
||||||
|
|
||||||
|
EXPORT void obs_sceneitem_group_enum_items(obs_sceneitem_t *group,
|
||||||
|
bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*),
|
||||||
|
void *param);
|
||||||
|
|
||||||
|
EXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item);
|
||||||
|
EXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item);
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
/* Outputs */
|
/* Outputs */
|
||||||
|
Loading…
Reference in New Issue
Block a user