obs-scene.c 71.6 KB
Newer Older
J
jp9000 已提交
1
/******************************************************************************
S
Socapex 已提交
2 3
    Copyright (C) 2013-2015 by Hugh Bailey <obs.jim@gmail.com>
                               Philippe Groarke <philippe.groarke@gmail.com>
J
jp9000 已提交
4 5 6

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 2 of the License, or
J
jp9000 已提交
8 9 10 11 12 13 14 15 16 17 18
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

J
jp9000 已提交
19
#include "util/threading.h"
J
jp9000 已提交
20 21 22
#include "graphics/math-defs.h"
#include "obs-scene.h"

23 24
const struct obs_source_info group_info;

J
jp9000 已提交
25
static void resize_group(obs_sceneitem_t *group);
26
static void resize_scene(obs_scene_t *scene);
27
static void signal_parent(obs_scene_t *parent, const char *name,
J
jp9000 已提交
28 29 30
			  calldata_t *params);
static void get_ungrouped_transform(obs_sceneitem_t *group, struct vec2 *pos,
				    struct vec2 *scale, float *rot);
31 32
static inline bool crop_enabled(const struct obs_sceneitem_crop *crop);
static inline bool item_texture_enabled(const struct obs_scene_item *item);
33
static void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item,
J
jp9000 已提交
34
			 const char *name);
35

36 37 38 39 40 41 42 43 44 45 46
/* NOTE: For proper mutex lock order (preventing mutual cross-locks), never
 * lock the graphics mutex inside either of the scene mutexes.
 *
 * Another thing that must be done to prevent that cross-lock (and improve
 * performance), is to not create/release/update sources within the scene
 * mutexes.
 *
 * It's okay to lock the graphics mutex before locking either of the scene
 * mutexes, but not after.
 */

47 48 49
static const char *obs_scene_signals[] = {
	"void item_add(ptr scene, ptr item)",
	"void item_remove(ptr scene, ptr item)",
S
Socapex 已提交
50
	"void reorder(ptr scene)",
J
jp9000 已提交
51
	"void refresh(ptr scene)",
52
	"void item_visible(ptr scene, ptr item, bool visible)",
53 54 55
	"void item_select(ptr scene, ptr item)",
	"void item_deselect(ptr scene, ptr item)",
	"void item_transform(ptr scene, ptr item)",
56
	"void item_locked(ptr scene, ptr item, bool locked)",
J
jp9000 已提交
57
	NULL,
58 59
};

J
jp9000 已提交
60
static inline void signal_item_remove(struct obs_scene_item *item)
J
jp9000 已提交
61
{
62 63 64 65
	struct calldata params;
	uint8_t stack[128];

	calldata_init_fixed(&params, stack, sizeof(stack));
66
	calldata_set_ptr(&params, "item", item);
J
jp9000 已提交
67

68
	signal_parent(item->parent, "item_remove", &params);
J
jp9000 已提交
69 70
}

71
static const char *scene_getname(void *unused)
72
{
73
	UNUSED_PARAMETER(unused);
74
	return "Scene";
75 76
}

77 78 79 80 81 82
static const char *group_getname(void *unused)
{
	UNUSED_PARAMETER(unused);
	return "Group";
}

83
static void *scene_create(obs_data_t *settings, struct obs_source *source)
J
jp9000 已提交
84
{
P
Palana 已提交
85
	pthread_mutexattr_t attr;
86 87
	struct obs_scene *scene = bzalloc(sizeof(struct obs_scene));
	scene->source = source;
88

89 90 91 92 93 94 95
	if (source->info.id == group_info.id) {
		scene->is_group = true;
		scene->custom_size = true;
		scene->cx = 0;
		scene->cy = 0;
	}

96
	signal_handler_add_array(obs_source_get_signal_handler(source),
J
jp9000 已提交
97
				 obs_scene_signals);
98

P
Palana 已提交
99 100 101 102
	if (pthread_mutexattr_init(&attr) != 0)
		goto fail;
	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		goto fail;
103 104 105 106 107 108 109 110
	if (pthread_mutex_init(&scene->audio_mutex, &attr) != 0) {
		blog(LOG_ERROR, "scene_create: Couldn't initialize audio "
				"mutex");
		goto fail;
	}
	if (pthread_mutex_init(&scene->video_mutex, &attr) != 0) {
		blog(LOG_ERROR, "scene_create: Couldn't initialize video "
				"mutex");
P
Palana 已提交
111
		goto fail;
112
	}
J
jp9000 已提交
113

J
jp9000 已提交
114
	UNUSED_PARAMETER(settings);
J
jp9000 已提交
115
	return scene;
P
Palana 已提交
116 117 118 119 120

fail:
	pthread_mutexattr_destroy(&attr);
	bfree(scene);
	return NULL;
J
jp9000 已提交
121 122
}

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
#define audio_lock(scene) pthread_mutex_lock(&scene->audio_mutex)
#define video_lock(scene) pthread_mutex_lock(&scene->video_mutex)
#define audio_unlock(scene) pthread_mutex_unlock(&scene->audio_mutex)
#define video_unlock(scene) pthread_mutex_unlock(&scene->video_mutex)

static inline void full_lock(struct obs_scene *scene)
{
	video_lock(scene);
	audio_lock(scene);
}

static inline void full_unlock(struct obs_scene *scene)
{
	audio_unlock(scene);
	video_unlock(scene);
}

140 141 142 143 144 145 146 147 148 149 150
static void set_visibility(struct obs_scene_item *item, bool vis);
static inline void detach_sceneitem(struct obs_scene_item *item);

static inline void remove_without_release(struct obs_scene_item *item)
{
	item->removed = true;
	set_visibility(item, false);
	signal_item_remove(item);
	detach_sceneitem(item);
}

151
static void remove_all_items(struct obs_scene *scene)
J
jp9000 已提交
152
{
J
jp9000 已提交
153
	struct obs_scene_item *item;
J
jp9000 已提交
154
	DARRAY(struct obs_scene_item *) items;
155 156

	da_init(items);
J
jp9000 已提交
157

158
	full_lock(scene);
J
jp9000 已提交
159

J
jp9000 已提交
160 161
	item = scene->first_item;

162 163 164 165
	while (item) {
		struct obs_scene_item *del_item = item;
		item = item->next;

166 167
		remove_without_release(del_item);
		da_push_back(items, &del_item);
168
	}
J
jp9000 已提交
169

170
	full_unlock(scene);
171 172 173 174

	for (size_t i = 0; i < items.num; i++)
		obs_sceneitem_release(items.array[i]);
	da_free(items);
175 176 177 178 179
}

static void scene_destroy(void *data)
{
	struct obs_scene *scene = data;
J
jp9000 已提交
180

181
	remove_all_items(scene);
182

183 184
	pthread_mutex_destroy(&scene->video_mutex);
	pthread_mutex_destroy(&scene->audio_mutex);
J
jp9000 已提交
185 186 187
	bfree(scene);
}

J
jp9000 已提交
188 189
static void scene_enum_sources(void *data, obs_source_enum_proc_t enum_callback,
			       void *param, bool active)
190 191 192
{
	struct obs_scene *scene = data;
	struct obs_scene_item *item;
193
	struct obs_scene_item *next;
194

195
	full_lock(scene);
196
	item = scene->first_item;
197

198
	while (item) {
199
		next = item->next;
200 201

		obs_sceneitem_addref(item);
202
		if (!active || os_atomic_load_long(&item->active_refs) > 0)
203
			enum_callback(scene->source, item->source, param);
204 205 206 207 208
		obs_sceneitem_release(item);

		item = next;
	}

209
	full_unlock(scene);
210 211
}

212
static void scene_enum_active_sources(void *data,
J
jp9000 已提交
213 214
				      obs_source_enum_proc_t enum_callback,
				      void *param)
215 216 217 218 219
{
	scene_enum_sources(data, enum_callback, param, true);
}

static void scene_enum_all_sources(void *data,
J
jp9000 已提交
220 221
				   obs_source_enum_proc_t enum_callback,
				   void *param)
222 223 224 225
{
	scene_enum_sources(data, enum_callback, param, false);
}

226 227 228 229 230 231 232 233 234
static inline void detach_sceneitem(struct obs_scene_item *item)
{
	if (item->prev)
		item->prev->next = item->next;
	else
		item->parent->first_item = item->next;

	if (item->next)
		item->next->prev = item->prev;
J
jp9000 已提交
235 236

	item->parent = NULL;
237 238
}

J
jp9000 已提交
239
static inline void attach_sceneitem(struct obs_scene *parent,
J
jp9000 已提交
240 241
				    struct obs_scene_item *item,
				    struct obs_scene_item *prev)
242
{
J
jp9000 已提交
243
	item->prev = prev;
J
jp9000 已提交
244
	item->parent = parent;
245 246 247 248 249 250 251

	if (prev) {
		item->next = prev->next;
		if (prev->next)
			prev->next->prev = item;
		prev->next = item;
	} else {
252 253 254 255
		item->next = parent->first_item;
		if (parent->first_item)
			parent->first_item->prev = item;
		parent->first_item = item;
256 257 258
	}
}

259
void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)
260 261 262 263 264 265 266 267 268 269 270 271 272
{
	if (align & OBS_ALIGN_RIGHT)
		v->x += (float)cx;
	else if ((align & OBS_ALIGN_LEFT) == 0)
		v->x += (float)(cx / 2);

	if (align & OBS_ALIGN_BOTTOM)
		v->y += (float)cy;
	else if ((align & OBS_ALIGN_TOP) == 0)
		v->y += (float)(cy / 2);
}

static void calculate_bounds_data(struct obs_scene_item *item,
J
jp9000 已提交
273 274
				  struct vec2 *origin, struct vec2 *scale,
				  uint32_t *cx, uint32_t *cy)
275
{
J
jp9000 已提交
276 277 278 279 280 281
	float width = (float)(*cx) * fabsf(scale->x);
	float height = (float)(*cy) * fabsf(scale->y);
	float item_aspect = width / height;
	float bounds_aspect = item->bounds.x / item->bounds.y;
	uint32_t bounds_type = item->bounds_type;
	float width_diff, height_diff;
J
jp9000 已提交
282 283 284 285 286 287 288

	if (item->bounds_type == OBS_BOUNDS_MAX_ONLY)
		if (width > item->bounds.x || height > item->bounds.y)
			bounds_type = OBS_BOUNDS_SCALE_INNER;

	if (bounds_type == OBS_BOUNDS_SCALE_INNER ||
	    bounds_type == OBS_BOUNDS_SCALE_OUTER) {
J
jp9000 已提交
289
		bool use_width = (bounds_aspect < item_aspect);
290 291 292 293 294
		float mul;

		if (item->bounds_type == OBS_BOUNDS_SCALE_OUTER)
			use_width = !use_width;

J
jp9000 已提交
295 296
		mul = use_width ? item->bounds.x / width
				: item->bounds.y / height;
297 298 299

		vec2_mulf(scale, scale, mul);

J
jp9000 已提交
300
	} else if (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) {
301 302
		vec2_mulf(scale, scale, item->bounds.x / width);

J
jp9000 已提交
303
	} else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) {
304 305
		vec2_mulf(scale, scale, item->bounds.y / height);

J
jp9000 已提交
306
	} else if (bounds_type == OBS_BOUNDS_STRETCH) {
307 308 309 310
		scale->x = item->bounds.x / (float)(*cx);
		scale->y = item->bounds.y / (float)(*cy);
	}

J
jp9000 已提交
311 312 313
	width = (float)(*cx) * scale->x;
	height = (float)(*cy) * scale->y;
	width_diff = item->bounds.x - width;
314
	height_diff = item->bounds.y - height;
J
jp9000 已提交
315 316
	*cx = (uint32_t)item->bounds.x;
	*cy = (uint32_t)item->bounds.y;
317

J
jp9000 已提交
318 319
	add_alignment(origin, item->bounds_align, (int)-width_diff,
		      (int)-height_diff);
320 321
}

322
static inline uint32_t calc_cx(const struct obs_scene_item *item,
J
jp9000 已提交
323
			       uint32_t width)
324 325 326 327 328 329
{
	uint32_t crop_cx = item->crop.left + item->crop.right;
	return (crop_cx > width) ? 2 : (width - crop_cx);
}

static inline uint32_t calc_cy(const struct obs_scene_item *item,
J
jp9000 已提交
330
			       uint32_t height)
331 332 333 334 335
{
	uint32_t crop_cy = item->crop.top + item->crop.bottom;
	return (crop_cy > height) ? 2 : (height - crop_cy);
}

336
static void update_item_transform(struct obs_scene_item *item, bool update_tex)
337
{
J
jp9000 已提交
338 339 340 341 342 343 344
	uint32_t width;
	uint32_t height;
	uint32_t cx;
	uint32_t cy;
	struct vec2 base_origin;
	struct vec2 origin;
	struct vec2 scale;
345
	struct calldata params;
J
jp9000 已提交
346
	uint8_t stack[128];
347

348 349 350
	if (os_atomic_load_long(&item->defer_update) > 0)
		return;

J
jp9000 已提交
351 352 353 354 355 356
	width = obs_source_get_width(item->source);
	height = obs_source_get_height(item->source);
	cx = calc_cx(item, width);
	cy = calc_cy(item, height);
	scale = item->scale;
	item->last_width = width;
357 358
	item->last_height = height;

359 360 361
	width = cx;
	height = cy;

J
jp9000 已提交
362 363 364
	vec2_zero(&base_origin);
	vec2_zero(&origin);

365 366 367 368 369 370 371 372 373 374 375 376
	/* ----------------------- */

	if (item->bounds_type != OBS_BOUNDS_NONE) {
		calculate_bounds_data(item, &origin, &scale, &cx, &cy);
	} else {
		cx = (uint32_t)((float)cx * scale.x);
		cy = (uint32_t)((float)cy * scale.y);
	}

	add_alignment(&origin, item->align, (int)cx, (int)cy);

	matrix4_identity(&item->draw_transform);
J
jp9000 已提交
377 378
	matrix4_scale3f(&item->draw_transform, &item->draw_transform, scale.x,
			scale.y, 1.0f);
379
	matrix4_translate3f(&item->draw_transform, &item->draw_transform,
J
jp9000 已提交
380 381 382
			    -origin.x, -origin.y, 0.0f);
	matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform, 0.0f,
			    0.0f, 1.0f, RAD(item->rot));
383
	matrix4_translate3f(&item->draw_transform, &item->draw_transform,
J
jp9000 已提交
384
			    item->pos.x, item->pos.y, 0.0f);
385

386 387
	item->output_scale = scale;

388 389 390 391 392
	/* ----------------------- */

	if (item->bounds_type != OBS_BOUNDS_NONE) {
		vec2_copy(&scale, &item->bounds);
	} else {
J
jp9000 已提交
393
		scale.x = (float)width * item->scale.x;
394 395 396
		scale.y = (float)height * item->scale.y;
	}

J
James Park 已提交
397 398
	item->box_scale = scale;

399 400 401
	add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y);

	matrix4_identity(&item->box_transform);
J
jp9000 已提交
402 403
	matrix4_scale3f(&item->box_transform, &item->box_transform, scale.x,
			scale.y, 1.0f);
404
	matrix4_translate3f(&item->box_transform, &item->box_transform,
J
jp9000 已提交
405 406 407
			    -base_origin.x, -base_origin.y, 0.0f);
	matrix4_rotate_aa4f(&item->box_transform, &item->box_transform, 0.0f,
			    0.0f, 1.0f, RAD(item->rot));
408
	matrix4_translate3f(&item->box_transform, &item->box_transform,
J
jp9000 已提交
409
			    item->pos.x, item->pos.y, 0.0f);
410 411 412

	/* ----------------------- */

413
	calldata_init_fixed(&params, stack, sizeof(stack));
414
	calldata_set_ptr(&params, "item", item);
415
	signal_parent(item->parent, "item_transform", &params);
416

417 418 419
	if (!update_tex)
		return;

420 421 422 423 424 425 426 427 428 429 430 431
	if (item->item_render && !item_texture_enabled(item)) {
		obs_enter_graphics();
		gs_texrender_destroy(item->item_render);
		item->item_render = NULL;
		obs_leave_graphics();

	} else if (!item->item_render && item_texture_enabled(item)) {
		obs_enter_graphics();
		item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
		obs_leave_graphics();
	}

432
	os_atomic_set_bool(&item->update_transform, false);
433 434 435 436
}

static inline bool source_size_changed(struct obs_scene_item *item)
{
J
jp9000 已提交
437
	uint32_t width = obs_source_get_width(item->source);
438
	uint32_t height = obs_source_get_height(item->source);
439 440 441 442

	return item->last_width != width || item->last_height != height;
}

443 444 445 446 447
static inline bool crop_enabled(const struct obs_sceneitem_crop *crop)
{
	return crop->left || crop->right || crop->top || crop->bottom;
}

448 449 450 451 452
static inline bool scale_filter_enabled(const struct obs_scene_item *item)
{
	return item->scale_filter != OBS_SCALE_DISABLE;
}

453 454 455 456 457
static inline bool item_is_scene(const struct obs_scene_item *item)
{
	return item->source && item->source->info.type == OBS_SOURCE_TYPE_SCENE;
}

458 459
static inline bool item_texture_enabled(const struct obs_scene_item *item)
{
460
	return crop_enabled(&item->crop) || scale_filter_enabled(item) ||
J
jp9000 已提交
461
	       (item_is_scene(item) && !item->is_group);
462 463 464 465 466
}

static void render_item_texture(struct obs_scene_item *item)
{
	gs_texture_t *tex = gs_texrender_get_texture(item->item_render);
467 468 469 470
	if (!tex) {
		return;
	}

471 472 473
	GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE,
			      "render_item_texture");

474 475 476 477
	gs_effect_t *effect = obs->video.default_effect;
	enum obs_scale_type type = item->scale_filter;
	uint32_t cx = gs_texture_get_width(tex);
	uint32_t cy = gs_texture_get_height(tex);
478
	const char *tech = "Draw";
479 480 481

	if (type != OBS_SCALE_DISABLE) {
		if (type == OBS_SCALE_POINT) {
J
jp9000 已提交
482 483
			gs_eparam_t *image =
				gs_effect_get_param_by_name(effect, "image");
484
			gs_effect_set_next_sampler(image,
J
jp9000 已提交
485
						   obs->video.point_sampler);
486 487

		} else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||
J
jp9000 已提交
488
			   !close_float(item->output_scale.y, 1.0f, EPSILON)) {
489
			gs_eparam_t *scale_param;
490
			gs_eparam_t *scale_i_param;
491 492 493 494 495 496 497 498

			if (item->output_scale.x < 0.5f ||
			    item->output_scale.y < 0.5f) {
				effect = obs->video.bilinear_lowres_effect;
			} else if (type == OBS_SCALE_BICUBIC) {
				effect = obs->video.bicubic_effect;
			} else if (type == OBS_SCALE_LANCZOS) {
				effect = obs->video.lanczos_effect;
J
James Park 已提交
499 500
			} else if (type == OBS_SCALE_AREA) {
				effect = obs->video.area_effect;
501 502 503
				if ((item->output_scale.x >= 1.0f) &&
				    (item->output_scale.y >= 1.0f))
					tech = "DrawUpscale";
504 505
			}

J
jp9000 已提交
506
			scale_param = gs_effect_get_param_by_name(
507
				effect, "base_dimension");
508
			if (scale_param) {
J
jpark37 已提交
509
				struct vec2 base_res = {(float)cx, (float)cy};
510

J
jpark37 已提交
511
				gs_effect_set_vec2(scale_param, &base_res);
512 513 514 515 516
			}

			scale_i_param = gs_effect_get_param_by_name(
				effect, "base_dimension_i");
			if (scale_i_param) {
J
jp9000 已提交
517 518
				struct vec2 base_res_i = {1.0f / (float)cx,
							  1.0f / (float)cy};
519

520
				gs_effect_set_vec2(scale_i_param, &base_res_i);
521 522 523 524
			}
		}
	}

J
James Park 已提交
525 526 527
	gs_blend_state_push();
	gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);

528
	while (gs_effect_loop(effect, tech))
529
		obs_source_draw(tex, 0, 0, 0, 0, 0);
530

J
James Park 已提交
531 532
	gs_blend_state_pop();

533
	GS_DEBUG_MARKER_END();
534 535
}

536 537
static inline void render_item(struct obs_scene_item *item)
{
538
	GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_ITEM, "Item: %s",
J
jp9000 已提交
539
				     obs_source_get_name(item->source));
540

541
	if (item->item_render) {
J
jp9000 已提交
542
		uint32_t width = obs_source_get_width(item->source);
543
		uint32_t height = obs_source_get_height(item->source);
544

545 546 547
		if (!width || !height) {
			goto cleanup;
		}
548

549 550 551
		uint32_t cx = calc_cx(item, width);
		uint32_t cy = calc_cy(item, height);

552
		if (cx && cy && gs_texrender_begin(item->item_render, cx, cy)) {
J
jp9000 已提交
553
			float cx_scale = (float)width / (float)cx;
554
			float cy_scale = (float)height / (float)cy;
555 556
			struct vec4 clear_color;

557
			vec4_zero(&clear_color);
558 559
			gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
			gs_ortho(0.0f, (float)width, 0.0f, (float)height,
J
jp9000 已提交
560
				 -100.0f, 100.0f);
561

562
			gs_matrix_scale3f(cx_scale, cy_scale, 1.0f);
J
jp9000 已提交
563 564
			gs_matrix_translate3f(-(float)item->crop.left,
					      -(float)item->crop.top, 0.0f);
565 566

			obs_source_video_render(item->source);
J
James Park 已提交
567

568
			gs_texrender_end(item->item_render);
569 570 571 572 573
		}
	}

	gs_matrix_push();
	gs_matrix_mul(&item->draw_transform);
574 575
	if (item->item_render) {
		render_item_texture(item);
576 577 578 579
	} else {
		obs_source_video_render(item->source);
	}
	gs_matrix_pop();
580 581 582

cleanup:
	GS_DEBUG_MARKER_END();
583 584 585 586 587 588 589 590 591 592
}

static void scene_video_tick(void *data, float seconds)
{
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

	video_lock(scene);
	item = scene->first_item;
	while (item) {
593 594
		if (item->item_render)
			gs_texrender_reset(item->item_render);
595 596 597 598 599 600 601
		item = item->next;
	}
	video_unlock(scene);

	UNUSED_PARAMETER(seconds);
}

602
/* assumes video lock */
J
jp9000 已提交
603 604 605 606
static void
update_transforms_and_prune_sources(obs_scene_t *scene,
				    struct darray *remove_items,
				    obs_sceneitem_t *group_sceneitem)
607 608
{
	struct obs_scene_item *item = scene->first_item;
J
jp9000 已提交
609 610
	bool rebuild_group =
		group_sceneitem &&
J
jp9000 已提交
611
		os_atomic_load_bool(&group_sceneitem->update_group_resize);
612 613 614 615 616 617 618

	while (item) {
		if (obs_source_removed(item->source)) {
			struct obs_scene_item *del_item = item;
			item = item->next;

			remove_without_release(del_item);
J
jp9000 已提交
619 620
			darray_push_back(sizeof(struct obs_scene_item *),
					 remove_items, &del_item);
J
jp9000 已提交
621
			rebuild_group = true;
622 623 624
			continue;
		}

J
jp9000 已提交
625 626 627 628 629
		if (item->is_group) {
			obs_scene_t *group_scene = item->source->context.data;

			video_lock(group_scene);
			update_transforms_and_prune_sources(group_scene,
J
jp9000 已提交
630
							    remove_items, item);
J
jp9000 已提交
631 632 633
			video_unlock(group_scene);
		}

634 635 636
		if (os_atomic_load_bool(&item->update_transform) ||
		    source_size_changed(item)) {

637
			update_item_transform(item, true);
J
jp9000 已提交
638
			rebuild_group = true;
639 640 641 642
		}

		item = item->next;
	}
J
jp9000 已提交
643

644 645
	if (rebuild_group && group_sceneitem)
		resize_group(group_sceneitem);
646 647
}

648
static void scene_video_render(void *data, gs_effect_t *effect)
J
jp9000 已提交
649
{
J
jp9000 已提交
650
	DARRAY(struct obs_scene_item *) remove_items;
651 652 653
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

654 655
	da_init(remove_items);

656
	video_lock(scene);
J
jp9000 已提交
657

658 659
	if (!scene->is_group) {
		update_transforms_and_prune_sources(scene, &remove_items.da,
J
jp9000 已提交
660
						    NULL);
J
jp9000 已提交
661 662
	}

J
jp9000 已提交
663
	gs_blend_state_push();
664
	gs_reset_blend_state();
J
jp9000 已提交
665

666
	item = scene->first_item;
667
	while (item) {
668 669
		if (item->user_visible)
			render_item(item);
670 671

		item = item->next;
J
jp9000 已提交
672
	}
673

J
jp9000 已提交
674 675
	gs_blend_state_pop();

676
	video_unlock(scene);
J
jp9000 已提交
677

678 679 680 681
	for (size_t i = 0; i < remove_items.num; i++)
		obs_sceneitem_release(remove_items.array[i]);
	da_free(remove_items);

J
jp9000 已提交
682 683 684
	UNUSED_PARAMETER(effect);
}

685
static void set_visibility(struct obs_scene_item *item, bool vis)
686 687 688 689 690 691 692 693
{
	pthread_mutex_lock(&item->actions_mutex);

	da_resize(item->audio_actions, 0);

	if (os_atomic_load_long(&item->active_refs) > 0) {
		if (!vis)
			obs_source_remove_active_child(item->parent->source,
J
jp9000 已提交
694
						       item->source);
695 696 697 698 699 700 701 702 703 704 705
	} else if (vis) {
		obs_source_add_active_child(item->parent->source, item->source);
	}

	os_atomic_set_long(&item->active_refs, vis ? 1 : 0);
	item->visible = vis;
	item->user_visible = vis;

	pthread_mutex_unlock(&item->actions_mutex);
}

J
jp9000 已提交
706 707
static void scene_load(void *data, obs_data_t *settings);

708
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
709
{
J
jp9000 已提交
710 711 712
	const char *name = obs_data_get_string(item_data, "name");
	obs_source_t *source;
	const char *scale_filter_str;
713
	struct obs_scene_item *item;
714
	bool visible;
715
	bool lock;
716

J
jp9000 已提交
717
	if (obs_data_get_bool(item_data, "group_item_backup"))
718
		return;
J
jp9000 已提交
719

720 721
	source = obs_get_source_by_name(name);
	if (!source) {
J
jp9000 已提交
722 723 724 725
		blog(LOG_WARNING,
		     "[scene_load_item] Source %s not "
		     "found!",
		     name);
726
		return;
727 728 729
	}

	item = obs_scene_add(scene, source);
P
Palana 已提交
730
	if (!item) {
J
jp9000 已提交
731 732 733 734 735
		blog(LOG_WARNING,
		     "[scene_load_item] Could not add source '%s' "
		     "to scene '%s'!",
		     name, obs_source_get_name(scene->source));

P
Palana 已提交
736 737 738
		obs_source_release(source);
		return;
	}
739

740
	item->is_group = source->info.id == group_info.id;
J
jp9000 已提交
741

742
	obs_data_set_default_int(item_data, "align",
J
jp9000 已提交
743
				 OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
744

J
jp9000 已提交
745 746 747
	if (obs_data_has_user_value(item_data, "id"))
		item->id = obs_data_get_int(item_data, "id");

J
jp9000 已提交
748 749
	item->rot = (float)obs_data_get_double(item_data, "rot");
	item->align = (uint32_t)obs_data_get_int(item_data, "align");
750
	visible = obs_data_get_bool(item_data, "visible");
751
	lock = obs_data_get_bool(item_data, "locked");
J
jp9000 已提交
752 753
	obs_data_get_vec2(item_data, "pos", &item->pos);
	obs_data_get_vec2(item_data, "scale", &item->scale);
754

755 756 757 758 759 760
	obs_data_release(item->private_settings);
	item->private_settings =
		obs_data_get_obj(item_data, "private_settings");
	if (!item->private_settings)
		item->private_settings = obs_data_create();

761
	set_visibility(item, visible);
762
	obs_sceneitem_set_locked(item, lock);
763

J
jp9000 已提交
764 765
	item->bounds_type = (enum obs_bounds_type)obs_data_get_int(
		item_data, "bounds_type");
766
	item->bounds_align =
J
jp9000 已提交
767
		(uint32_t)obs_data_get_int(item_data, "bounds_align");
768 769
	obs_data_get_vec2(item_data, "bounds", &item->bounds);

J
jp9000 已提交
770 771 772 773 774
	item->crop.left = (uint32_t)obs_data_get_int(item_data, "crop_left");
	item->crop.top = (uint32_t)obs_data_get_int(item_data, "crop_top");
	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");
775

776 777 778 779 780 781 782 783 784 785 786 787
	scale_filter_str = obs_data_get_string(item_data, "scale_filter");
	item->scale_filter = OBS_SCALE_DISABLE;

	if (scale_filter_str) {
		if (astrcmpi(scale_filter_str, "point") == 0)
			item->scale_filter = OBS_SCALE_POINT;
		else if (astrcmpi(scale_filter_str, "bilinear") == 0)
			item->scale_filter = OBS_SCALE_BILINEAR;
		else if (astrcmpi(scale_filter_str, "bicubic") == 0)
			item->scale_filter = OBS_SCALE_BICUBIC;
		else if (astrcmpi(scale_filter_str, "lanczos") == 0)
			item->scale_filter = OBS_SCALE_LANCZOS;
J
James Park 已提交
788 789
		else if (astrcmpi(scale_filter_str, "area") == 0)
			item->scale_filter = OBS_SCALE_AREA;
790
	}
791 792

	if (item->item_render && !item_texture_enabled(item)) {
793
		obs_enter_graphics();
794 795
		gs_texrender_destroy(item->item_render);
		item->item_render = NULL;
796 797
		obs_leave_graphics();

798
	} else if (!item->item_render && item_texture_enabled(item)) {
799
		obs_enter_graphics();
800
		item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
801 802 803
		obs_leave_graphics();
	}

804
	obs_source_release(source);
805

806
	update_item_transform(item, false);
807 808
}

J
jp9000 已提交
809
static void scene_load(void *data, obs_data_t *settings)
810
{
J
jp9000 已提交
811
	struct obs_scene *scene = data;
812
	obs_data_array_t *items = obs_data_get_array(settings, "items");
J
jp9000 已提交
813
	size_t count, i;
814 815 816

	remove_all_items(scene);

J
jp9000 已提交
817 818
	if (!items)
		return;
819 820 821 822

	count = obs_data_array_count(items);

	for (i = 0; i < count; i++) {
823
		obs_data_t *item_data = obs_data_array_item(items, i);
824 825 826 827
		scene_load_item(scene, item_data);
		obs_data_release(item_data);
	}

J
jp9000 已提交
828 829 830
	if (obs_data_has_user_value(settings, "id_counter"))
		scene->id_counter = obs_data_get_int(settings, "id_counter");

831 832 833 834 835 836
	if (obs_data_get_bool(settings, "custom_size")) {
		scene->cx = (uint32_t)obs_data_get_int(settings, "cx");
		scene->cy = (uint32_t)obs_data_get_int(settings, "cy");
		scene->custom_size = true;
	}

837 838 839
	obs_data_array_release(items);
}

J
jp9000 已提交
840 841
static void scene_save(void *data, obs_data_t *settings);

842
static void scene_save_item(obs_data_array_t *array,
J
jp9000 已提交
843 844
			    struct obs_scene_item *item,
			    struct obs_scene_item *backup_group)
845
{
846
	obs_data_t *item_data = obs_data_create();
J
jp9000 已提交
847
	const char *name = obs_source_get_name(item->source);
848
	const char *scale_filter;
J
jp9000 已提交
849 850 851 852
	struct vec2 pos = item->pos;
	struct vec2 scale = item->scale;
	float rot = item->rot;

853 854
	if (backup_group) {
		get_ungrouped_transform(backup_group, &pos, &scale, &rot);
J
jp9000 已提交
855
	}
856

J
jp9000 已提交
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
	obs_data_set_string(item_data, "name", name);
	obs_data_set_bool(item_data, "visible", item->user_visible);
	obs_data_set_bool(item_data, "locked", item->locked);
	obs_data_set_double(item_data, "rot", rot);
	obs_data_set_vec2(item_data, "pos", &pos);
	obs_data_set_vec2(item_data, "scale", &scale);
	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_align", (int)item->bounds_align);
	obs_data_set_vec2(item_data, "bounds", &item->bounds);
	obs_data_set_int(item_data, "crop_left", (int)item->crop.left);
	obs_data_set_int(item_data, "crop_top", (int)item->crop.top);
	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, "group_item_backup", !!backup_group);
J
jp9000 已提交
873 874 875 876 877 878 879 880 881 882 883 884

	if (item->is_group) {
		obs_scene_t *group_scene = item->source->context.data;
		obs_sceneitem_t *group_item;

		/* 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) {
885
			scene_save_item(array, group_item, item);
J
jp9000 已提交
886 887 888 889 890
			group_item = group_item->next;
		}

		full_unlock(group_scene);
	}
891 892 893 894 895 896 897 898 899

	if (item->scale_filter == OBS_SCALE_POINT)
		scale_filter = "point";
	else if (item->scale_filter == OBS_SCALE_BILINEAR)
		scale_filter = "bilinear";
	else if (item->scale_filter == OBS_SCALE_BICUBIC)
		scale_filter = "bicubic";
	else if (item->scale_filter == OBS_SCALE_LANCZOS)
		scale_filter = "lanczos";
J
James Park 已提交
900 901
	else if (item->scale_filter == OBS_SCALE_AREA)
		scale_filter = "area";
902 903 904 905
	else
		scale_filter = "disable";

	obs_data_set_string(item_data, "scale_filter", scale_filter);
906

J
jp9000 已提交
907
	obs_data_set_obj(item_data, "private_settings", item->private_settings);
908

909 910 911 912
	obs_data_array_push_back(array, item_data);
	obs_data_release(item_data);
}

913
static void scene_save(void *data, obs_data_t *settings)
914
{
J
jp9000 已提交
915 916
	struct obs_scene *scene = data;
	obs_data_array_t *array = obs_data_array_create();
917 918
	struct obs_scene_item *item;

919
	full_lock(scene);
920 921 922

	item = scene->first_item;
	while (item) {
923
		scene_save_item(array, item, NULL);
924 925 926
		item = item->next;
	}

J
jp9000 已提交
927
	obs_data_set_int(settings, "id_counter", scene->id_counter);
928 929 930 931 932
	obs_data_set_bool(settings, "custom_size", scene->custom_size);
	if (scene->custom_size) {
		obs_data_set_int(settings, "cx", scene->cx);
		obs_data_set_int(settings, "cy", scene->cy);
	}
J
jp9000 已提交
933

934
	full_unlock(scene);
935

J
jp9000 已提交
936
	obs_data_set_array(settings, "items", array);
937 938 939
	obs_data_array_release(array);
}

J
jp9000 已提交
940 941
static uint32_t scene_getwidth(void *data)
{
942 943
	obs_scene_t *scene = data;
	return scene->custom_size ? scene->cx : obs->video.base_width;
J
jp9000 已提交
944 945
}

J
jp9000 已提交
946
static uint32_t scene_getheight(void *data)
J
jp9000 已提交
947
{
948 949
	obs_scene_t *scene = data;
	return scene->custom_size ? scene->cy : obs->video.base_height;
J
jp9000 已提交
950 951
}

952
static void apply_scene_item_audio_actions(struct obs_scene_item *item,
J
jp9000 已提交
953 954
					   float **p_buf, uint64_t ts,
					   size_t sample_rate)
955 956 957 958
{
	bool cur_visible = item->visible;
	uint64_t frame_num = 0;
	size_t deref_count = 0;
959
	float *buf = NULL;
960

961 962 963 964 965
	if (p_buf) {
		if (!*p_buf)
			*p_buf = malloc(AUDIO_OUTPUT_FRAMES * sizeof(float));
		buf = *p_buf;
	}
966 967 968 969 970 971 972 973 974 975 976 977

	pthread_mutex_lock(&item->actions_mutex);

	for (size_t i = 0; i < item->audio_actions.num; i++) {
		struct item_action action = item->audio_actions.array[i];
		uint64_t timestamp = action.timestamp;
		uint64_t new_frame_num;

		if (timestamp < ts)
			timestamp = ts;

		new_frame_num = (timestamp - ts) * (uint64_t)sample_rate /
J
jp9000 已提交
978
				1000000000ULL;
979

980
		if (ts && new_frame_num >= AUDIO_OUTPUT_FRAMES)
981 982 983 984 985 986 987 988
			break;

		da_erase(item->audio_actions, i--);

		item->visible = action.visible;
		if (!item->visible)
			deref_count++;

989
		if (buf && new_frame_num > frame_num) {
990 991 992 993 994 995 996
			for (; frame_num < new_frame_num; frame_num++)
				buf[frame_num] = cur_visible ? 1.0f : 0.0f;
		}

		cur_visible = item->visible;
	}

997 998 999 1000
	if (buf) {
		for (; frame_num < AUDIO_OUTPUT_FRAMES; frame_num++)
			buf[frame_num] = cur_visible ? 1.0f : 0.0f;
	}
1001 1002 1003 1004 1005 1006

	pthread_mutex_unlock(&item->actions_mutex);

	while (deref_count--) {
		if (os_atomic_dec_long(&item->active_refs) == 0) {
			obs_source_remove_active_child(item->parent->source,
J
jp9000 已提交
1007
						       item->source);
1008 1009 1010 1011
		}
	}
}

J
jp9000 已提交
1012 1013
static bool apply_scene_item_volume(struct obs_scene_item *item, float **buf,
				    uint64_t ts, size_t sample_rate)
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
{
	bool actions_pending;
	struct item_action action;

	pthread_mutex_lock(&item->actions_mutex);

	actions_pending = item->audio_actions.num > 0;
	if (actions_pending)
		action = item->audio_actions.array[0];

	pthread_mutex_unlock(&item->actions_mutex);

	if (actions_pending) {
		uint64_t duration = (uint64_t)AUDIO_OUTPUT_FRAMES *
J
jp9000 已提交
1028
				    1000000000ULL / (uint64_t)sample_rate;
1029

1030
		if (!ts || action.timestamp < (ts + duration)) {
1031
			apply_scene_item_audio_actions(item, buf, ts,
J
jp9000 已提交
1032
						       sample_rate);
1033 1034 1035 1036 1037 1038 1039
			return true;
		}
	}

	return false;
}

1040
static void process_all_audio_actions(struct obs_scene_item *item,
J
jp9000 已提交
1041
				      size_t sample_rate)
1042
{
J
jp9000 已提交
1043 1044
	while (apply_scene_item_volume(item, NULL, 0, sample_rate))
		;
1045 1046
}

1047
static void mix_audio_with_buf(float *p_out, float *p_in, float *buf_in,
J
jp9000 已提交
1048
			       size_t pos, size_t count)
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
{
	register float *out = p_out;
	register float *buf = buf_in + pos;
	register float *in = p_in + pos;
	register float *end = in + count;

	while (in < end)
		*(out++) += *(in++) * *(buf++);
}

J
jp9000 已提交
1059 1060
static inline void mix_audio(float *p_out, float *p_in, size_t pos,
			     size_t count)
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
{
	register float *out = p_out;
	register float *in = p_in + pos;
	register float *end = in + count;

	while (in < end)
		*(out++) += *(in++);
}

static bool scene_audio_render(void *data, uint64_t *ts_out,
J
jp9000 已提交
1071 1072 1073
			       struct obs_source_audio_mix *audio_output,
			       uint32_t mixers, size_t channels,
			       size_t sample_rate)
1074 1075
{
	uint64_t timestamp = 0;
1076
	float *buf = NULL;
1077 1078 1079 1080 1081 1082 1083 1084
	struct obs_source_audio_mix child_audio;
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

	audio_lock(scene);

	item = scene->first_item;
	while (item) {
1085
		if (!obs_source_audio_pending(item->source) && item->visible) {
1086 1087 1088
			uint64_t source_ts =
				obs_source_get_audio_timestamp(item->source);

1089
			if (source_ts && (!timestamp || source_ts < timestamp))
1090 1091 1092 1093 1094 1095 1096
				timestamp = source_ts;
		}

		item = item->next;
	}

	if (!timestamp) {
1097 1098 1099 1100 1101 1102 1103 1104
		/* just process all pending audio actions if no audio playing,
		 * otherwise audio actions will just never be processed */
		item = scene->first_item;
		while (item) {
			process_all_audio_actions(item, sample_rate);
			item = item->next;
		}

1105 1106 1107 1108 1109 1110 1111 1112
		audio_unlock(scene);
		return false;
	}

	item = scene->first_item;
	while (item) {
		uint64_t source_ts;
		size_t pos, count;
1113 1114 1115
		bool apply_buf;

		apply_buf = apply_scene_item_volume(item, &buf, timestamp,
J
jp9000 已提交
1116
						    sample_rate);
1117 1118 1119 1120 1121 1122 1123

		if (obs_source_audio_pending(item->source)) {
			item = item->next;
			continue;
		}

		source_ts = obs_source_get_audio_timestamp(item->source);
1124 1125 1126 1127 1128
		if (!source_ts) {
			item = item->next;
			continue;
		}

1129
		pos = (size_t)ns_to_audio_frames(sample_rate,
J
jp9000 已提交
1130
						 source_ts - timestamp);
1131 1132
		count = AUDIO_OUTPUT_FRAMES - pos;

1133 1134 1135 1136 1137
		if (!apply_buf && !item->visible) {
			item = item->next;
			continue;
		}

1138 1139
		obs_source_get_audio_mix(item->source, &child_audio);
		for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
1140 1141 1142
			if ((mixers & (1 << mix)) == 0)
				continue;

1143 1144 1145 1146
			for (size_t ch = 0; ch < channels; ch++) {
				float *out = audio_output->output[mix].data[ch];
				float *in = child_audio.output[mix].data[ch];

1147 1148
				if (apply_buf)
					mix_audio_with_buf(out, in, buf, pos,
J
jp9000 已提交
1149
							   count);
1150 1151
				else
					mix_audio(out, in, pos, count);
1152 1153 1154 1155 1156 1157 1158 1159
			}
		}

		item = item->next;
	}

	*ts_out = timestamp;
	audio_unlock(scene);
1160 1161

	free(buf);
1162 1163 1164
	return true;
}

J
jp9000 已提交
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
const struct obs_source_info scene_info = {
	.id = "scene",
	.type = OBS_SOURCE_TYPE_SCENE,
	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
			OBS_SOURCE_COMPOSITE,
	.get_name = scene_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,
1180
	.enum_active_sources = scene_enum_active_sources,
J
jp9000 已提交
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
	.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,
1198
	.enum_active_sources = scene_enum_active_sources,
J
jp9000 已提交
1199
	.enum_all_sources = scene_enum_all_sources};
1200

J
jp9000 已提交
1201
static inline obs_scene_t *create_id(const char *id, const char *name)
J
jp9000 已提交
1202
{
J
jp9000 已提交
1203
	struct obs_source *source = obs_source_create(id, name, NULL, NULL);
1204
	return source->context.data;
J
jp9000 已提交
1205 1206
}

J
jp9000 已提交
1207
static inline obs_scene_t *create_private_id(const char *id, const char *name)
1208
{
J
jp9000 已提交
1209
	struct obs_source *source = obs_source_create_private(id, name, NULL);
1210 1211 1212
	return source->context.data;
}

J
jp9000 已提交
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
obs_scene_t *obs_scene_create(const char *name)
{
	return create_id("scene", name);
}

obs_scene_t *obs_scene_create_private(const char *name)
{
	return create_private_id("scene", name);
}

1223
static obs_source_t *get_child_at_idx(obs_scene_t *scene, size_t idx)
1224 1225 1226
{
	struct obs_scene_item *item = scene->first_item;

1227 1228 1229 1230 1231 1232
	while (item && idx--)
		item = item->next;
	return item ? item->source : NULL;
}

static inline obs_source_t *dup_child(struct darray *array, size_t idx,
J
jp9000 已提交
1233
				      obs_scene_t *new_scene, bool private)
1234
{
J
jp9000 已提交
1235
	DARRAY(struct obs_scene_item *) old_items;
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
	obs_source_t *source;

	old_items.da = *array;

	source = old_items.array[idx]->source;

	/* if the old item is referenced more than once in the old scene,
	 * make sure they're referenced similarly in the new scene to reduce
	 * load times */
	for (size_t i = 0; i < idx; i++) {
		struct obs_scene_item *item = old_items.array[i];
		if (item->source == source) {
			source = get_child_at_idx(new_scene, i);
			obs_source_addref(source);
			return source;
		}
	}

	return obs_source_duplicate(source, NULL, private);
}

static inline obs_source_t *new_ref(obs_source_t *source)
{
	obs_source_addref(source);
	return source;
}

1263
static inline void duplicate_item_data(struct obs_scene_item *dst,
J
jp9000 已提交
1264 1265 1266 1267
				       struct obs_scene_item *src,
				       bool defer_texture_update,
				       bool duplicate_hotkeys,
				       bool duplicate_private_data)
1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
{
	struct obs_scene *dst_scene = dst->parent;

	if (!src->user_visible)
		set_visibility(dst, false);

	dst->selected = src->selected;
	dst->pos = src->pos;
	dst->rot = src->rot;
	dst->scale = src->scale;
	dst->align = src->align;
	dst->last_width = src->last_width;
	dst->last_height = src->last_height;
	dst->output_scale = src->output_scale;
	dst->scale_filter = src->scale_filter;
	dst->box_transform = src->box_transform;
J
James Park 已提交
1284
	dst->box_scale = src->box_scale;
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
	dst->draw_transform = src->draw_transform;
	dst->bounds_type = src->bounds_type;
	dst->bounds_align = src->bounds_align;
	dst->bounds = src->bounds;

	if (duplicate_hotkeys && !dst_scene->source->context.private) {
		obs_data_array_t *data0 = NULL;
		obs_data_array_t *data1 = NULL;

		obs_hotkey_pair_save(src->toggle_visibility, &data0, &data1);
		obs_hotkey_pair_load(dst->toggle_visibility, data0, data1);

		obs_data_array_release(data0);
		obs_data_array_release(data1);
	}

	obs_sceneitem_set_crop(dst, &src->crop);

	if (defer_texture_update) {
		os_atomic_set_bool(&dst->update_transform, true);
	} else {
		if (!dst->item_render && item_texture_enabled(dst)) {
			obs_enter_graphics();
J
jp9000 已提交
1308 1309
			dst->item_render =
				gs_texrender_create(GS_RGBA, GS_ZS_NONE);
1310 1311 1312 1313 1314 1315 1316 1317 1318
			obs_leave_graphics();
		}
	}

	if (duplicate_private_data) {
		obs_data_apply(dst->private_settings, src->private_settings);
	}
}

1319
obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name,
J
jp9000 已提交
1320
				 enum obs_scene_duplicate_type type)
1321
{
J
jp9000 已提交
1322 1323 1324
	bool make_unique = ((int)type & (1 << 0)) != 0;
	bool make_private = ((int)type & (1 << 1)) != 0;
	DARRAY(struct obs_scene_item *) items;
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
	struct obs_scene *new_scene;
	struct obs_scene_item *item;
	struct obs_source *source;

	da_init(items);

	if (!obs_ptr_valid(scene, "obs_scene_duplicate"))
		return NULL;

	/* --------------------------------- */

1336
	full_lock(scene);
1337

1338
	item = scene->first_item;
1339
	while (item) {
1340 1341 1342 1343 1344 1345 1346 1347 1348
		da_push_back(items, &item);
		obs_sceneitem_addref(item);
		item = item->next;
	}

	full_unlock(scene);

	/* --------------------------------- */

J
jp9000 已提交
1349
	new_scene = make_private
J
jp9000 已提交
1350 1351
			    ? create_private_id(scene->source->info.id, name)
			    : create_id(scene->source->info.id, name);
1352

S
SuslikV 已提交
1353 1354
	obs_source_copy_filters(new_scene->source, scene->source);

1355
	obs_data_apply(new_scene->source->private_settings,
J
jp9000 已提交
1356
		       scene->source->private_settings);
1357

J
jp9000 已提交
1358 1359 1360 1361
	/* never duplicate sub-items for groups */
	if (scene->is_group)
		make_unique = false;

1362 1363
	for (size_t i = 0; i < items.num; i++) {
		item = items.array[i];
J
jp9000 已提交
1364 1365 1366
		source = make_unique ? dup_child(&items.da, i, new_scene,
						 make_private)
				     : new_ref(item->source);
1367 1368 1369 1370 1371

		if (source) {
			struct obs_scene_item *new_item =
				obs_scene_add(new_scene, source);

1372
			if (!new_item) {
1373
				obs_source_release(source);
1374 1375 1376
				continue;
			}

1377
			duplicate_item_data(new_item, item, false, false,
J
jp9000 已提交
1378
					    false);
1379

1380 1381
			obs_source_release(source);
		}
1382 1383
	}

1384 1385
	for (size_t i = 0; i < items.num; i++)
		obs_sceneitem_release(items.array[i]);
1386

J
jp9000 已提交
1387 1388 1389
	if (new_scene->is_group)
		resize_scene(new_scene);

1390
	da_free(items);
1391 1392 1393
	return new_scene;
}

1394
void obs_scene_addref(obs_scene_t *scene)
1395
{
P
Palana 已提交
1396 1397
	if (scene)
		obs_source_addref(scene->source);
1398 1399
}

1400
void obs_scene_release(obs_scene_t *scene)
J
jp9000 已提交
1401
{
P
Palana 已提交
1402 1403
	if (scene)
		obs_source_release(scene->source);
J
jp9000 已提交
1404 1405
}

1406
obs_source_t *obs_scene_get_source(const obs_scene_t *scene)
J
jp9000 已提交
1407
{
J
jp9000 已提交
1408
	return scene ? scene->source : NULL;
J
jp9000 已提交
1409 1410
}

1411
obs_scene_t *obs_scene_from_source(const obs_source_t *source)
1412
{
1413
	if (!source || source->info.id != scene_info.id)
1414 1415
		return NULL;

1416
	return source->context.data;
1417 1418
}

J
jp9000 已提交
1419 1420 1421 1422 1423 1424 1425 1426
obs_scene_t *obs_group_from_source(const obs_source_t *source)
{
	if (!source || source->info.id != group_info.id)
		return NULL;

	return source->context.data;
}

1427
obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name)
1428 1429 1430
{
	struct obs_scene_item *item;

J
jp9000 已提交
1431 1432 1433
	if (!scene)
		return NULL;

1434
	full_lock(scene);
1435 1436 1437

	item = scene->first_item;
	while (item) {
1438
		if (strcmp(item->source->context.name, name) == 0)
1439 1440 1441 1442 1443
			break;

		item = item->next;
	}

1444
	full_unlock(scene);
1445 1446 1447 1448

	return item;
}

1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
obs_sceneitem_t *obs_scene_find_source_recursive(obs_scene_t *scene,
						 const char *name)
{
	struct obs_scene_item *item;

	if (!scene)
		return NULL;

	full_lock(scene);

	item = scene->first_item;
	while (item) {
		if (strcmp(item->source->context.name, name) == 0)
			break;

		if (item->is_group) {
			obs_scene_t *group = item->source->context.data;
			obs_sceneitem_t *child =
				obs_scene_find_source(group, name);
			if (child) {
				item = child;
				break;
			}
		}

		item = item->next;
	}

	full_unlock(scene);

	return item;
}

J
jp9000 已提交
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id)
{
	struct obs_scene_item *item;

	if (!scene)
		return NULL;

	full_lock(scene);

	item = scene->first_item;
	while (item) {
		if (item->id == id)
			break;

		item = item->next;
	}

	full_unlock(scene);

	return item;
}

1504
void obs_scene_enum_items(obs_scene_t *scene,
J
jp9000 已提交
1505 1506 1507
			  bool (*callback)(obs_scene_t *, obs_sceneitem_t *,
					   void *),
			  void *param)
1508 1509 1510
{
	struct obs_scene_item *item;

J
jp9000 已提交
1511 1512 1513
	if (!scene || !callback)
		return;

1514
	full_lock(scene);
1515 1516 1517

	item = scene->first_item;
	while (item) {
J
jp9000 已提交
1518 1519 1520 1521
		struct obs_scene_item *next = item->next;

		obs_sceneitem_addref(item);

1522 1523
		if (!callback(scene, item, param)) {
			obs_sceneitem_release(item);
1524
			break;
1525
		}
1526

J
jp9000 已提交
1527 1528 1529
		obs_sceneitem_release(item);

		item = next;
1530 1531
	}

1532
	full_unlock(scene);
1533 1534
}

P
Palana 已提交
1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
static obs_sceneitem_t *sceneitem_get_ref(obs_sceneitem_t *si)
{
	long owners = si->ref;
	while (owners > 0) {
		if (os_atomic_compare_swap_long(&si->ref, owners, owners + 1))
			return si;

		owners = si->ref;
	}
	return NULL;
}

static bool hotkey_show_sceneitem(void *data, obs_hotkey_pair_id id,
J
jp9000 已提交
1548
				  obs_hotkey_t *hotkey, bool pressed)
P
Palana 已提交
1549 1550 1551 1552 1553
{
	UNUSED_PARAMETER(id);
	UNUSED_PARAMETER(hotkey);

	obs_sceneitem_t *si = sceneitem_get_ref(data);
1554
	if (pressed && si && !si->user_visible) {
P
Palana 已提交
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564
		obs_sceneitem_set_visible(si, true);
		obs_sceneitem_release(si);
		return true;
	}

	obs_sceneitem_release(si);
	return false;
}

static bool hotkey_hide_sceneitem(void *data, obs_hotkey_pair_id id,
J
jp9000 已提交
1565
				  obs_hotkey_t *hotkey, bool pressed)
P
Palana 已提交
1566 1567 1568 1569 1570
{
	UNUSED_PARAMETER(id);
	UNUSED_PARAMETER(hotkey);

	obs_sceneitem_t *si = sceneitem_get_ref(data);
1571
	if (pressed && si && si->user_visible) {
P
Palana 已提交
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581
		obs_sceneitem_set_visible(si, false);
		obs_sceneitem_release(si);
		return true;
	}

	obs_sceneitem_release(si);
	return false;
}

static void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item,
J
jp9000 已提交
1582
			 const char *name)
P
Palana 已提交
1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598
{
	struct dstr show = {0};
	struct dstr hide = {0};
	struct dstr show_desc = {0};
	struct dstr hide_desc = {0};

	dstr_copy(&show, "libobs.show_scene_item.%1");
	dstr_replace(&show, "%1", name);
	dstr_copy(&hide, "libobs.hide_scene_item.%1");
	dstr_replace(&hide, "%1", name);

	dstr_copy(&show_desc, obs->hotkeys.sceneitem_show);
	dstr_replace(&show_desc, "%1", name);
	dstr_copy(&hide_desc, obs->hotkeys.sceneitem_hide);
	dstr_replace(&hide_desc, "%1", name);

J
jp9000 已提交
1599 1600 1601 1602
	item->toggle_visibility = obs_hotkey_pair_register_source(
		scene->source, show.array, show_desc.array, hide.array,
		hide_desc.array, hotkey_show_sceneitem, hotkey_hide_sceneitem,
		item, item);
P
Palana 已提交
1603 1604 1605 1606 1607 1608 1609

	dstr_free(&show);
	dstr_free(&hide);
	dstr_free(&show_desc);
	dstr_free(&hide_desc);
}

1610
static void sceneitem_rename_hotkey(const obs_sceneitem_t *scene_item,
J
jp9000 已提交
1611
				    const char *new_name)
1612
{
J
jp9000 已提交
1613 1614 1615 1616
	struct dstr show = {0};
	struct dstr hide = {0};
	struct dstr show_desc = {0};
	struct dstr hide_desc = {0};
1617 1618 1619 1620 1621 1622

	dstr_copy(&show, "libobs.show_scene_item.%1");
	dstr_replace(&show, "%1", new_name);
	dstr_copy(&hide, "libobs.hide_scene_item.%1");
	dstr_replace(&hide, "%1", new_name);

J
jp9000 已提交
1623 1624
	obs_hotkey_pair_set_names(scene_item->toggle_visibility, show.array,
				  hide.array);
1625 1626 1627 1628 1629 1630 1631

	dstr_copy(&show_desc, obs->hotkeys.sceneitem_show);
	dstr_replace(&show_desc, "%1", new_name);
	dstr_copy(&hide_desc, obs->hotkeys.sceneitem_hide);
	dstr_replace(&hide_desc, "%1", new_name);

	obs_hotkey_pair_set_descriptions(scene_item->toggle_visibility,
J
jp9000 已提交
1632
					 show_desc.array, hide_desc.array);
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647

	dstr_free(&show);
	dstr_free(&hide);
	dstr_free(&show_desc);
	dstr_free(&hide_desc);
}

static void sceneitem_renamed(void *param, calldata_t *data)
{
	obs_sceneitem_t *scene_item = param;
	const char *name = calldata_string(data, "new_name");

	sceneitem_rename_hotkey(scene_item, name);
}

1648 1649 1650 1651 1652 1653
static inline bool source_has_audio(obs_source_t *source)
{
	return (source->info.output_flags &
		(OBS_SOURCE_AUDIO | OBS_SOURCE_COMPOSITE)) != 0;
}

1654
static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
J
jp9000 已提交
1655 1656
					       obs_source_t *source,
					       obs_sceneitem_t *insert_after)
J
jp9000 已提交
1657
{
1658
	struct obs_scene_item *last;
1659
	struct obs_scene_item *item;
1660 1661
	pthread_mutex_t mutex;

J
jp9000 已提交
1662 1663
	struct item_action action = {.visible = true,
				     .timestamp = os_gettime_ns()};
1664

1665 1666 1667 1668
	if (!scene)
		return NULL;

	if (!source) {
J
jp9000 已提交
1669
		blog(LOG_ERROR, "Tried to add a NULL source to a scene");
1670 1671 1672
		return NULL;
	}

1673 1674 1675 1676 1677
	if (pthread_mutex_init(&mutex, NULL) != 0) {
		blog(LOG_WARNING, "Failed to create scene item mutex");
		return NULL;
	}

1678
	if (!obs_source_add_active_child(scene->source, source)) {
J
jp9000 已提交
1679
		blog(LOG_WARNING, "Failed to add source to scene due to "
J
jp9000 已提交
1680
				  "infinite source recursion");
1681
		pthread_mutex_destroy(&mutex);
J
jp9000 已提交
1682 1683 1684
		return NULL;
	}

1685
	item = bzalloc(sizeof(struct obs_scene_item));
J
jp9000 已提交
1686 1687 1688 1689 1690
	item->source = source;
	item->id = ++scene->id_counter;
	item->parent = scene;
	item->ref = 1;
	item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
1691 1692
	item->actions_mutex = mutex;
	item->user_visible = true;
1693
	item->locked = false;
1694
	item->is_group = source->info.id == group_info.id;
1695
	item->private_settings = obs_data_create();
1696
	item->toggle_visibility = OBS_INVALID_HOTKEY_PAIR_ID;
1697
	os_atomic_set_long(&item->active_refs, 1);
J
jp9000 已提交
1698
	vec2_set(&item->scale, 1.0f, 1.0f);
1699 1700
	matrix4_identity(&item->draw_transform);
	matrix4_identity(&item->box_transform);
J
jp9000 已提交
1701

1702
	obs_source_addref(source);
1703

1704 1705 1706 1707 1708 1709 1710
	if (source_has_audio(source)) {
		item->visible = false;
		da_push_back(item->audio_actions, &action);
	} else {
		item->visible = true;
	}

1711 1712 1713 1714 1715 1716
	if (item_texture_enabled(item)) {
		obs_enter_graphics();
		item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
		obs_leave_graphics();
	}

1717
	full_lock(scene);
1718

1719 1720
	if (insert_after) {
		obs_sceneitem_t *next = insert_after->next;
J
jp9000 已提交
1721 1722
		if (next)
			next->prev = item;
1723 1724 1725
		item->next = insert_after->next;
		item->prev = insert_after;
		insert_after->next = item;
1726
	} else {
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736
		last = scene->first_item;
		if (!last) {
			scene->first_item = item;
		} else {
			while (last->next)
				last = last->next;

			last->next = item;
			item->prev = last;
		}
1737
	}
J
jp9000 已提交
1738

1739
	full_unlock(scene);
1740

1741 1742
	if (!scene->source->context.private)
		init_hotkeys(scene, item, obs_source_get_name(source));
P
Palana 已提交
1743

1744
	signal_handler_connect(obs_source_get_signal_handler(source), "rename",
J
jp9000 已提交
1745
			       sceneitem_renamed, item);
1746

1747 1748 1749 1750 1751
	return item;
}

obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
{
1752
	obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL);
1753 1754 1755
	struct calldata params;
	uint8_t stack[128];

1756
	calldata_init_fixed(&params, stack, sizeof(stack));
1757 1758
	calldata_set_ptr(&params, "scene", scene);
	calldata_set_ptr(&params, "item", item);
1759
	signal_handler_signal(scene->source->context.signals, "item_add",
J
jp9000 已提交
1760
			      &params);
J
jp9000 已提交
1761 1762 1763
	return item;
}

1764
static void obs_sceneitem_destroy(obs_sceneitem_t *item)
J
jp9000 已提交
1765
{
1766
	if (item) {
1767
		if (item->item_render) {
1768
			obs_enter_graphics();
1769
			gs_texrender_destroy(item->item_render);
1770 1771
			obs_leave_graphics();
		}
1772
		obs_data_release(item->private_settings);
P
Palana 已提交
1773
		obs_hotkey_pair_unregister(item->toggle_visibility);
1774
		pthread_mutex_destroy(&item->actions_mutex);
1775
		signal_handler_disconnect(
J
jp9000 已提交
1776 1777
			obs_source_get_signal_handler(item->source), "rename",
			sceneitem_renamed, item);
1778
		if (item->source)
J
jp9000 已提交
1779
			obs_source_release(item->source);
1780
		da_free(item->audio_actions);
J
jp9000 已提交
1781
		bfree(item);
J
jp9000 已提交
1782 1783 1784
	}
}

1785
void obs_sceneitem_addref(obs_sceneitem_t *item)
J
jp9000 已提交
1786
{
P
Palana 已提交
1787
	if (item)
J
jp9000 已提交
1788
		os_atomic_inc_long(&item->ref);
J
jp9000 已提交
1789
}
1790

1791
void obs_sceneitem_release(obs_sceneitem_t *item)
J
jp9000 已提交
1792
{
P
Palana 已提交
1793 1794
	if (!item)
		return;
P
Palana 已提交
1795

J
jp9000 已提交
1796
	if (os_atomic_dec_long(&item->ref) == 0)
P
Palana 已提交
1797
		obs_sceneitem_destroy(item);
1798 1799
}

1800
void obs_sceneitem_remove(obs_sceneitem_t *item)
J
jp9000 已提交
1801
{
1802
	obs_scene_t *scene;
J
jp9000 已提交
1803

1804
	if (!item)
J
jp9000 已提交
1805 1806
		return;

1807
	scene = item->parent;
J
jp9000 已提交
1808

1809
	full_lock(scene);
J
jp9000 已提交
1810

J
jp9000 已提交
1811
	if (item->removed) {
1812
		if (scene)
1813
			full_unlock(scene);
1814 1815 1816
		return;
	}

J
jp9000 已提交
1817 1818
	item->removed = true;

1819 1820
	assert(scene != NULL);
	assert(scene->source != NULL);
1821 1822

	set_visibility(item, false);
1823

1824
	signal_item_remove(item);
J
jp9000 已提交
1825
	detach_sceneitem(item);
1826

1827
	full_unlock(scene);
J
jp9000 已提交
1828 1829 1830 1831

	obs_sceneitem_release(item);
}

1832
obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item)
J
jp9000 已提交
1833
{
J
jp9000 已提交
1834
	return item ? item->parent : NULL;
J
jp9000 已提交
1835 1836
}

1837
obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item)
1838
{
J
jp9000 已提交
1839
	return item ? item->source : NULL;
J
jp9000 已提交
1840 1841
}

1842
static void signal_parent(obs_scene_t *parent, const char *command,
J
jp9000 已提交
1843
			  calldata_t *params)
1844 1845 1846 1847 1848
{
	calldata_set_ptr(params, "scene", parent);
	signal_handler_signal(parent->source->context.signals, command, params);
}

1849
void obs_sceneitem_select(obs_sceneitem_t *item, bool select)
1850
{
1851 1852
	struct calldata params;
	uint8_t stack[128];
1853 1854
	const char *command = select ? "item_select" : "item_deselect";

1855
	if (!item || item->selected == select || !item->parent)
1856 1857 1858 1859
		return;

	item->selected = select;

1860
	calldata_init_fixed(&params, stack, sizeof(stack));
J
jp9000 已提交
1861
	calldata_set_ptr(&params, "item", item);
1862 1863

	signal_parent(item->parent, command, &params);
1864 1865
}

1866
bool obs_sceneitem_selected(const obs_sceneitem_t *item)
1867 1868 1869 1870
{
	return item ? item->selected : false;
}

J
jp9000 已提交
1871 1872 1873
#define do_update_transform(item)                                          \
	do {                                                               \
		if (!item->parent || item->parent->is_group)               \
1874
			os_atomic_set_bool(&item->update_transform, true); \
J
jp9000 已提交
1875 1876
		else                                                       \
			update_item_transform(item, false);                \
1877 1878
	} while (false)

1879
void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos)
J
jp9000 已提交
1880
{
1881
	if (item) {
J
jp9000 已提交
1882
		vec2_copy(&item->pos, pos);
1883
		do_update_transform(item);
1884
	}
J
jp9000 已提交
1885 1886
}

1887
void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot)
J
jp9000 已提交
1888
{
1889
	if (item) {
J
jp9000 已提交
1890
		item->rot = rot;
1891
		do_update_transform(item);
1892
	}
J
jp9000 已提交
1893 1894
}

1895
void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale)
J
jp9000 已提交
1896
{
1897 1898
	if (item) {
		vec2_copy(&item->scale, scale);
1899
		do_update_transform(item);
1900
	}
J
jp9000 已提交
1901 1902
}

1903
void obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment)
J
jp9000 已提交
1904
{
1905 1906
	if (item) {
		item->align = alignment;
1907
		do_update_transform(item);
1908
	}
J
jp9000 已提交
1909 1910
}

S
Socapex 已提交
1911
static inline void signal_reorder(struct obs_scene_item *item)
J
jp9000 已提交
1912
{
1913
	const char *command = NULL;
1914 1915
	struct calldata params;
	uint8_t stack[128];
J
jp9000 已提交
1916

S
Socapex 已提交
1917
	command = "reorder";
J
jp9000 已提交
1918

1919
	calldata_init_fixed(&params, stack, sizeof(stack));
1920
	signal_parent(item->parent, command, &params);
J
jp9000 已提交
1921 1922
}

J
jp9000 已提交
1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934
static inline void signal_refresh(obs_scene_t *scene)
{
	const char *command = NULL;
	struct calldata params;
	uint8_t stack[128];

	command = "refresh";

	calldata_init_fixed(&params, stack, sizeof(stack));
	signal_parent(scene, command, &params);
}

1935
void obs_sceneitem_set_order(obs_sceneitem_t *item,
J
jp9000 已提交
1936
			     enum obs_order_movement movement)
J
jp9000 已提交
1937
{
J
jp9000 已提交
1938 1939
	if (!item)
		return;
J
jp9000 已提交
1940

J
jp9000 已提交
1941
	struct obs_scene_item *next, *prev;
J
jp9000 已提交
1942 1943
	struct obs_scene *scene = item->parent;

J
jp9000 已提交
1944
	obs_scene_addref(scene);
1945
	full_lock(scene);
1946

J
jp9000 已提交
1947 1948 1949
	next = item->next;
	prev = item->prev;

1950 1951
	detach_sceneitem(item);

J
jp9000 已提交
1952
	if (movement == OBS_ORDER_MOVE_DOWN) {
J
jp9000 已提交
1953
		attach_sceneitem(scene, item, prev ? prev->prev : NULL);
J
jp9000 已提交
1954

J
jp9000 已提交
1955
	} else if (movement == OBS_ORDER_MOVE_UP) {
J
jp9000 已提交
1956
		attach_sceneitem(scene, item, next ? next : prev);
J
jp9000 已提交
1957

J
jp9000 已提交
1958
	} else if (movement == OBS_ORDER_MOVE_TOP) {
J
jp9000 已提交
1959
		struct obs_scene_item *last = next;
1960
		if (!last) {
J
jp9000 已提交
1961
			last = prev;
1962 1963 1964 1965
		} else {
			while (last->next)
				last = last->next;
		}
J
jp9000 已提交
1966

J
jp9000 已提交
1967
		attach_sceneitem(scene, item, last);
1968

J
jp9000 已提交
1969
	} else if (movement == OBS_ORDER_MOVE_BOTTOM) {
J
jp9000 已提交
1970
		attach_sceneitem(scene, item, NULL);
J
jp9000 已提交
1971
	}
1972

1973
	full_unlock(scene);
1974 1975

	signal_reorder(item);
1976
	obs_scene_release(scene);
J
jp9000 已提交
1977 1978
}

J
jp9000 已提交
1979
void obs_sceneitem_set_order_position(obs_sceneitem_t *item, int position)
1980
{
J
jp9000 已提交
1981 1982
	if (!item)
		return;
1983 1984 1985 1986 1987

	struct obs_scene *scene = item->parent;
	struct obs_scene_item *next;

	obs_scene_addref(scene);
1988
	full_lock(scene);
1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004

	detach_sceneitem(item);
	next = scene->first_item;

	if (position == 0) {
		attach_sceneitem(scene, item, NULL);
	} else {
		for (int i = position; i > 1; --i) {
			if (next->next == NULL)
				break;
			next = next->next;
		}

		attach_sceneitem(scene, item, next);
	}

2005
	full_unlock(scene);
2006 2007

	signal_reorder(item);
2008 2009 2010
	obs_scene_release(scene);
}

2011
void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item,
J
jp9000 已提交
2012
				   enum obs_bounds_type type)
2013 2014 2015
{
	if (item) {
		item->bounds_type = type;
2016
		do_update_transform(item);
2017 2018 2019
	}
}

2020
void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item,
J
jp9000 已提交
2021
					uint32_t alignment)
2022 2023 2024
{
	if (item) {
		item->bounds_align = alignment;
2025
		do_update_transform(item);
2026 2027 2028
	}
}

2029
void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds)
2030 2031 2032
{
	if (item) {
		item->bounds = *bounds;
2033
		do_update_transform(item);
2034 2035 2036
	}
}

2037
void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos)
J
jp9000 已提交
2038
{
J
jp9000 已提交
2039 2040
	if (item)
		vec2_copy(pos, &item->pos);
J
jp9000 已提交
2041 2042
}

2043
float obs_sceneitem_get_rot(const obs_sceneitem_t *item)
J
jp9000 已提交
2044
{
J
jp9000 已提交
2045
	return item ? item->rot : 0.0f;
J
jp9000 已提交
2046 2047
}

2048
void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale)
J
jp9000 已提交
2049
{
J
jp9000 已提交
2050
	if (item)
2051
		vec2_copy(scale, &item->scale);
J
jp9000 已提交
2052 2053
}

2054
uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item)
2055 2056 2057 2058
{
	return item ? item->align : 0;
}

2059
enum obs_bounds_type obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item)
2060 2061 2062 2063
{
	return item ? item->bounds_type : OBS_BOUNDS_NONE;
}

2064
uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item)
2065 2066 2067 2068
{
	return item ? item->bounds_align : 0;
}

2069
void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds)
J
jp9000 已提交
2070
{
J
jp9000 已提交
2071
	if (item)
2072 2073 2074
		*bounds = item->bounds;
}

2075
void obs_sceneitem_get_info(const obs_sceneitem_t *item,
J
jp9000 已提交
2076
			    struct obs_transform_info *info)
2077 2078
{
	if (item && info) {
J
jp9000 已提交
2079 2080 2081 2082 2083
		info->pos = item->pos;
		info->rot = item->rot;
		info->scale = item->scale;
		info->alignment = item->align;
		info->bounds_type = item->bounds_type;
2084
		info->bounds_alignment = item->bounds_align;
J
jp9000 已提交
2085
		info->bounds = item->bounds;
2086 2087 2088
	}
}

2089
void obs_sceneitem_set_info(obs_sceneitem_t *item,
J
jp9000 已提交
2090
			    const struct obs_transform_info *info)
2091 2092
{
	if (item && info) {
J
jp9000 已提交
2093 2094 2095 2096 2097
		item->pos = info->pos;
		item->rot = info->rot;
		item->scale = info->scale;
		item->align = info->alignment;
		item->bounds_type = info->bounds_type;
2098
		item->bounds_align = info->bounds_alignment;
J
jp9000 已提交
2099
		item->bounds = info->bounds;
2100
		do_update_transform(item);
2101 2102 2103
	}
}

2104
void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item,
J
jp9000 已提交
2105
				      struct matrix4 *transform)
2106 2107 2108 2109 2110
{
	if (item)
		matrix4_copy(transform, &item->draw_transform);
}

2111
void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item,
J
jp9000 已提交
2112
				     struct matrix4 *transform)
2113 2114 2115
{
	if (item)
		matrix4_copy(transform, &item->box_transform);
J
jp9000 已提交
2116
}
2117

J
James Park 已提交
2118
void obs_sceneitem_get_box_scale(const obs_sceneitem_t *item,
J
jp9000 已提交
2119
				 struct vec2 *scale)
J
James Park 已提交
2120 2121 2122 2123 2124
{
	if (item)
		*scale = item->box_scale;
}

2125 2126
bool obs_sceneitem_visible(const obs_sceneitem_t *item)
{
2127
	return item ? item->user_visible : false;
2128 2129
}

2130
bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
2131
{
2132 2133
	struct calldata cd;
	uint8_t stack[256];
J
jp9000 已提交
2134 2135
	struct item_action action = {.visible = visible,
				     .timestamp = os_gettime_ns()};
2136 2137

	if (!item)
2138
		return false;
2139

2140
	if (item->user_visible == visible)
2141
		return false;
2142 2143

	if (!item->parent)
2144 2145 2146
		return false;

	if (visible) {
2147 2148
		if (os_atomic_inc_long(&item->active_refs) == 1) {
			if (!obs_source_add_active_child(item->parent->source,
J
jp9000 已提交
2149
							 item->source)) {
2150 2151 2152 2153
				os_atomic_dec_long(&item->active_refs);
				return false;
			}
		}
2154 2155
	}

2156
	item->user_visible = visible;
2157

2158
	calldata_init_fixed(&cd, stack, sizeof(stack));
2159 2160 2161
	calldata_set_ptr(&cd, "item", item);
	calldata_set_bool(&cd, "visible", visible);

2162
	signal_parent(item->parent, "item_visible", &cd);
2163 2164 2165 2166 2167 2168 2169 2170

	if (source_has_audio(item->source)) {
		pthread_mutex_lock(&item->actions_mutex);
		da_push_back(item->audio_actions, &action);
		pthread_mutex_unlock(&item->actions_mutex);
	} else {
		set_visibility(item, visible);
	}
2171
	return true;
2172
}
P
Palana 已提交
2173

2174 2175 2176 2177 2178 2179 2180
bool obs_sceneitem_locked(const obs_sceneitem_t *item)
{
	return item ? item->locked : false;
}

bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock)
{
2181 2182 2183
	struct calldata cd;
	uint8_t stack[256];

2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
	if (!item)
		return false;

	if (item->locked == lock)
		return false;

	if (!item->parent)
		return false;

	item->locked = lock;

2195 2196 2197 2198 2199 2200
	calldata_init_fixed(&cd, stack, sizeof(stack));
	calldata_set_ptr(&cd, "item", item);
	calldata_set_bool(&cd, "locked", lock);

	signal_parent(item->parent, "item_locked", &cd);

2201 2202 2203
	return true;
}

J
jp9000 已提交
2204 2205
static bool sceneitems_match(obs_scene_t *scene, obs_sceneitem_t *const *items,
			     size_t size, bool *order_matches)
P
Palana 已提交
2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233
{
	obs_sceneitem_t *item = scene->first_item;

	size_t count = 0;
	while (item) {
		bool found = false;
		for (size_t i = 0; i < size; i++) {
			if (items[i] != item)
				continue;

			if (count != i)
				*order_matches = false;

			found = true;
			break;
		}

		if (!found)
			return false;

		item = item->next;
		count += 1;
	}

	return count == size;
}

bool obs_scene_reorder_items(obs_scene_t *scene,
J
jp9000 已提交
2234 2235
			     obs_sceneitem_t *const *item_order,
			     size_t item_order_size)
P
Palana 已提交
2236 2237 2238 2239 2240
{
	if (!scene || !item_order_size)
		return false;

	obs_scene_addref(scene);
2241
	full_lock(scene);
P
Palana 已提交
2242 2243 2244

	bool order_matches = true;
	if (!sceneitems_match(scene, item_order, item_order_size,
J
jp9000 已提交
2245 2246
			      &order_matches) ||
	    order_matches) {
2247
		full_unlock(scene);
P
Palana 已提交
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264
		obs_scene_release(scene);
		return false;
	}

	scene->first_item = item_order[0];

	obs_sceneitem_t *prev = NULL;
	for (size_t i = 0; i < item_order_size; i++) {
		item_order[i]->prev = prev;
		item_order[i]->next = NULL;

		if (prev)
			prev->next = item_order[i];

		prev = item_order[i];
	}

2265
	full_unlock(scene);
2266 2267

	signal_reorder(scene->first_item);
P
Palana 已提交
2268 2269 2270 2271
	obs_scene_release(scene);
	return true;
}

P
Palana 已提交
2272
void obs_scene_atomic_update(obs_scene_t *scene,
J
jp9000 已提交
2273
			     obs_scene_atomic_update_func func, void *data)
P
Palana 已提交
2274 2275 2276 2277 2278
{
	if (!scene)
		return;

	obs_scene_addref(scene);
2279
	full_lock(scene);
P
Palana 已提交
2280
	func(data, scene);
2281
	full_unlock(scene);
P
Palana 已提交
2282 2283
	obs_scene_release(scene);
}
2284

2285
static inline bool crop_equal(const struct obs_sceneitem_crop *crop1,
J
jp9000 已提交
2286
			      const struct obs_sceneitem_crop *crop2)
2287
{
J
jp9000 已提交
2288 2289
	return crop1->left == crop2->left && crop1->right == crop2->right &&
	       crop1->top == crop2->top && crop1->bottom == crop2->bottom;
2290 2291 2292
}

void obs_sceneitem_set_crop(obs_sceneitem_t *item,
J
jp9000 已提交
2293
			    const struct obs_sceneitem_crop *crop)
2294 2295 2296 2297 2298 2299 2300 2301 2302 2303
{
	if (!obs_ptr_valid(item, "obs_sceneitem_set_crop"))
		return;
	if (!obs_ptr_valid(crop, "obs_sceneitem_set_crop"))
		return;
	if (crop_equal(crop, &item->crop))
		return;

	memcpy(&item->crop, crop, sizeof(*crop));

J
jp9000 已提交
2304 2305 2306 2307 2308 2309 2310 2311
	if (item->crop.left < 0)
		item->crop.left = 0;
	if (item->crop.right < 0)
		item->crop.right = 0;
	if (item->crop.top < 0)
		item->crop.top = 0;
	if (item->crop.bottom < 0)
		item->crop.bottom = 0;
2312

2313
	os_atomic_set_bool(&item->update_transform, true);
2314 2315 2316
}

void obs_sceneitem_get_crop(const obs_sceneitem_t *item,
J
jp9000 已提交
2317
			    struct obs_sceneitem_crop *crop)
2318 2319 2320 2321 2322 2323 2324 2325 2326
{
	if (!obs_ptr_valid(item, "obs_sceneitem_get_crop"))
		return;
	if (!obs_ptr_valid(crop, "obs_sceneitem_get_crop"))
		return;

	memcpy(crop, &item->crop, sizeof(*crop));
}

2327
void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
J
jp9000 已提交
2328
				    enum obs_scale_type filter)
2329 2330 2331 2332 2333 2334
{
	if (!obs_ptr_valid(item, "obs_sceneitem_set_scale_filter"))
		return;

	item->scale_filter = filter;

2335
	os_atomic_set_bool(&item->update_transform, true);
2336 2337
}

J
jp9000 已提交
2338
enum obs_scale_type obs_sceneitem_get_scale_filter(obs_sceneitem_t *item)
2339
{
J
jp9000 已提交
2340 2341 2342
	return obs_ptr_valid(item, "obs_sceneitem_get_scale_filter")
		       ? item->scale_filter
		       : OBS_SCALE_DISABLE;
2343 2344
}

2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358
void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
{
	if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_begin"))
		return;

	os_atomic_inc_long(&item->defer_update);
}

void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)
{
	if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_end"))
		return;

	if (os_atomic_dec_long(&item->defer_update) == 0)
2359
		do_update_transform(item);
2360
}
J
jp9000 已提交
2361

J
jp9000 已提交
2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378
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);
}

J
jp9000 已提交
2379 2380 2381 2382 2383 2384 2385
int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)
{
	if (!obs_ptr_valid(item, "obs_sceneitem_get_id"))
		return 0;

	return item->id;
}
2386 2387 2388 2389 2390 2391 2392 2393 2394

obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item)
{
	if (!obs_ptr_valid(item, "obs_sceneitem_get_private_settings"))
		return NULL;

	obs_data_addref(item->private_settings);
	return item->private_settings;
}
J
jp9000 已提交
2395

2396
static inline void transform_val(struct vec2 *v2, struct matrix4 *transform)
J
jp9000 已提交
2397 2398 2399 2400 2401 2402 2403 2404
{
	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;
}

J
jp9000 已提交
2405 2406
static void get_ungrouped_transform(obs_sceneitem_t *group, struct vec2 *pos,
				    struct vec2 *scale, float *rot)
J
jp9000 已提交
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424
{
	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);

2425 2426
	scale->x = vec4_len(&mat.x) * (scale->x > 0.0f ? 1.0f : -1.0f);
	scale->y = vec4_len(&mat.y) * (scale->y > 0.0f ? 1.0f : -1.0f);
J
jp9000 已提交
2427 2428 2429
	*rot += group->rot;
}

2430
static void remove_group_transform(obs_sceneitem_t *group,
J
jp9000 已提交
2431
				   obs_sceneitem_t *item)
J
jp9000 已提交
2432 2433
{
	obs_scene_t *parent = item->parent;
2434
	if (!parent || !group)
J
jp9000 已提交
2435 2436 2437 2438
		return;

	get_ungrouped_transform(group, &item->pos, &item->scale, &item->rot);

2439
	update_item_transform(item, false);
J
jp9000 已提交
2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460
}

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);

J
jp9000 已提交
2461 2462 2463 2464
	item->scale.x =
		vec4_len(&mat.x) * (item->scale.x > 0.0f ? 1.0f : -1.0f);
	item->scale.y =
		vec4_len(&mat.y) * (item->scale.y > 0.0f ? 1.0f : -1.0f);
J
jp9000 已提交
2465 2466
	item->rot -= group->rot;

2467
	update_item_transform(item, false);
J
jp9000 已提交
2468 2469
}

J
jp9000 已提交
2470 2471
static bool resize_scene_base(obs_scene_t *scene, struct vec2 *minv,
			      struct vec2 *maxv, struct vec2 *scale)
J
jp9000 已提交
2472
{
2473 2474
	vec2_set(minv, M_INFINITE, M_INFINITE);
	vec2_set(maxv, -M_INFINITE, -M_INFINITE);
J
jp9000 已提交
2475 2476 2477 2478 2479

	obs_sceneitem_t *item = scene->first_item;
	if (!item) {
		scene->cx = 0;
		scene->cy = 0;
2480
		return false;
J
jp9000 已提交
2481 2482 2483
	}

	while (item) {
J
jp9000 已提交
2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497
#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)
J
jp9000 已提交
2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509

		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) {
2510
		vec2_sub(&item->pos, &item->pos, minv);
2511
		update_item_transform(item, false);
J
jp9000 已提交
2512 2513 2514
		item = item->next;
	}

2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541
	vec2_sub(scale, maxv, minv);
	scene->cx = (uint32_t)ceilf(scale->x);
	scene->cy = (uint32_t)ceilf(scale->y);
	return true;
}

static void resize_scene(obs_scene_t *scene)
{
	struct vec2 minv;
	struct vec2 maxv;
	struct vec2 scale;
	resize_scene_base(scene, &minv, &maxv, &scale);
}

/* 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;

	if (!resize_scene_base(scene, &minv, &maxv, &scale))
		return;
J
jp9000 已提交
2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565

	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);

2566
	update_item_transform(group, false);
J
jp9000 已提交
2567 2568 2569 2570 2571 2572 2573
}

obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name)
{
	return obs_scene_insert_group(scene, name, NULL, 0);
}

2574 2575 2576 2577 2578 2579
obs_sceneitem_t *obs_scene_add_group2(obs_scene_t *scene, const char *name,
				      bool signal)
{
	return obs_scene_insert_group2(scene, name, NULL, 0, signal);
}

J
jp9000 已提交
2580 2581
obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name,
					obs_sceneitem_t **items, size_t count)
J
jp9000 已提交
2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592
{
	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;
	}

2593
	obs_scene_t *sub_scene = create_id("group", name);
J
jp9000 已提交
2594 2595
	obs_sceneitem_t *last_item = items ? items[count - 1] : NULL;

J
jp9000 已提交
2596 2597
	obs_sceneitem_t *item =
		obs_scene_add_internal(scene, sub_scene->source, last_item);
J
jp9000 已提交
2598 2599 2600 2601 2602 2603 2604 2605 2606 2607

	obs_scene_release(sub_scene);

	if (!items || !count)
		return item;

	/* ------------------------- */

	full_lock(scene);
	full_lock(sub_scene);
2608
	sub_scene->first_item = items[0];
J
jp9000 已提交
2609 2610 2611

	for (size_t i = count; i > 0; i--) {
		size_t idx = i - 1;
2612
		remove_group_transform(item, items[idx]);
J
jp9000 已提交
2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636
		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;
}

2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647
obs_sceneitem_t *obs_scene_insert_group2(obs_scene_t *scene, const char *name,
					 obs_sceneitem_t **items, size_t count,
					 bool signal)
{
	obs_sceneitem_t *item =
		obs_scene_insert_group(scene, name, items, count);
	if (signal && item)
		signal_refresh(scene);
	return item;
}

J
jp9000 已提交
2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692
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;
}

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;
2693
	obs_sceneitem_t *insert_after = item;
J
jp9000 已提交
2694 2695 2696
	obs_sceneitem_t *first;
	obs_sceneitem_t *last;

2697 2698
	full_lock(scene);

J
jp9000 已提交
2699 2700 2701 2702 2703 2704
	/* ------------------------- */

	full_lock(subscene);
	first = subscene->first_item;
	last = first;
	while (last) {
2705 2706
		obs_sceneitem_t *dst;

2707
		remove_group_transform(item, last);
2708 2709 2710 2711
		dst = obs_scene_add_internal(scene, last->source, insert_after);
		duplicate_item_data(dst, last, true, true, true);
		apply_group_transform(last, item);

J
jp9000 已提交
2712 2713
		if (!last->next)
			break;
2714 2715

		insert_after = dst;
J
jp9000 已提交
2716 2717 2718 2719 2720 2721
		last = last->next;
	}
	full_unlock(subscene);

	/* ------------------------- */

2722
	detach_sceneitem(item);
J
jp9000 已提交
2723 2724 2725 2726 2727
	full_unlock(scene);

	obs_sceneitem_release(item);
}

2728 2729 2730 2731 2732 2733 2734 2735
void obs_sceneitem_group_ungroup2(obs_sceneitem_t *item, bool signal)
{
	obs_scene_t *scene = item->parent;
	obs_sceneitem_group_ungroup(item);
	if (signal)
		signal_refresh(scene);
}

J
jp9000 已提交
2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750
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);
2751
	remove_group_transform(group, item);
J
jp9000 已提交
2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777
	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);
2778 2779

	signal_refresh(scene);
J
jp9000 已提交
2780 2781
}

2782
void obs_sceneitem_group_remove_item(obs_sceneitem_t *group,
J
jp9000 已提交
2783
				     obs_sceneitem_t *item)
J
jp9000 已提交
2784
{
2785
	if (!item || !group || !group->is_group)
J
jp9000 已提交
2786 2787 2788
		return;

	obs_scene_t *groupscene = item->parent;
2789
	obs_scene_t *scene = group->parent;
J
jp9000 已提交
2790 2791 2792 2793 2794

	/* ------------------------- */

	full_lock(scene);
	full_lock(groupscene);
2795
	remove_group_transform(group, item);
J
jp9000 已提交
2796 2797 2798 2799
	detach_sceneitem(item);

	/* ------------------------- */

2800 2801 2802
	if (group->prev) {
		group->prev->next = item;
		item->prev = group->prev;
J
jp9000 已提交
2803 2804 2805 2806
	} else {
		scene->first_item = item;
		item->prev = NULL;
	}
2807 2808
	group->prev = item;
	item->next = group;
J
jp9000 已提交
2809 2810 2811 2812
	item->parent = scene;

	/* ------------------------- */

2813
	resize_group(group);
J
jp9000 已提交
2814 2815
	full_unlock(groupscene);
	full_unlock(scene);
2816 2817

	signal_refresh(scene);
J
jp9000 已提交
2818 2819
}

J
jp9000 已提交
2820 2821 2822 2823
static void
build_current_order_info(obs_scene_t *scene,
			 struct obs_sceneitem_order_info **items_out,
			 size_t *size_out)
J
jp9000 已提交
2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855
{
	DARRAY(struct obs_sceneitem_order_info) items;
	da_init(items);

	obs_sceneitem_t *item = scene->first_item;
	while (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) {
				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,
J
jp9000 已提交
2856 2857
			      struct obs_sceneitem_order_info *items,
			      size_t size)
J
jp9000 已提交
2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871
{
	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];

H
Hunter L. Allen 已提交
2872
		if (new->group != old->group || new->item != old->item) {
J
jp9000 已提交
2873 2874 2875 2876 2877 2878 2879 2880 2881
			bfree(cur_items);
			return false;
		}
	}

	bfree(cur_items);
	return true;
}

J
jp9000 已提交
2882 2883
static obs_sceneitem_t *
get_sceneitem_parent_group(obs_scene_t *scene, obs_sceneitem_t *group_subitem)
2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898
{
	if (group_subitem->is_group)
		return NULL;

	obs_sceneitem_t *item = scene->first_item;
	while (item) {
		if (item->is_group &&
		    item->source->context.data == group_subitem->parent)
			return item;
		item = item->next;
	}

	return NULL;
}

J
jp9000 已提交
2899
bool obs_scene_reorder_items2(obs_scene_t *scene,
J
jp9000 已提交
2900 2901
			      struct obs_sceneitem_order_info *item_order,
			      size_t item_order_size)
J
jp9000 已提交
2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917
{
	if (!scene || !item_order_size || !item_order)
		return false;

	obs_scene_addref(scene);
	full_lock(scene);

	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) {
2918 2919 2920
			obs_sceneitem_t *group =
				get_sceneitem_parent_group(scene, info->item);
			remove_group_transform(group, info->item);
J
jp9000 已提交
2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961
		}
	}

	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,
J
jp9000 已提交
2962
						      sub_info->group);
J
jp9000 已提交
2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988

				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;
}

2989
obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene,
J
jp9000 已提交
2990
					 obs_sceneitem_t *group_subitem)
2991 2992 2993 2994 2995
{
	if (!scene || !group_subitem || group_subitem->is_group)
		return NULL;

	full_lock(scene);
J
jp9000 已提交
2996 2997
	obs_sceneitem_t *group =
		get_sceneitem_parent_group(scene, group_subitem);
2998 2999 3000 3001 3002 3003 3004
	full_unlock(scene);

	return group;
}

bool obs_source_is_group(const obs_source_t *source)
{
3005
	return source && source->info.id == group_info.id;
3006 3007 3008
}

bool obs_scene_is_group(const obs_scene_t *scene)
J
jp9000 已提交
3009
{
3010
	return scene ? scene->is_group : false;
J
jp9000 已提交
3011 3012 3013
}

void obs_sceneitem_group_enum_items(obs_sceneitem_t *group,
J
jp9000 已提交
3014 3015 3016
				    bool (*callback)(obs_scene_t *,
						     obs_sceneitem_t *, void *),
				    void *param)
J
jp9000 已提交
3017
{
3018
	if (!group || !group->is_group)
J
jp9000 已提交
3019 3020
		return;

3021
	obs_scene_t *scene = group->source->context.data;
J
jp9000 已提交
3022 3023 3024
	if (scene)
		obs_scene_enum_items(scene, callback, param);
}
3025 3026 3027 3028 3029 3030 3031 3032 3033

void obs_sceneitem_force_update_transform(obs_sceneitem_t *item)
{
	if (!item)
		return;

	if (os_atomic_set_bool(&item->update_transform, false))
		update_item_transform(item, false);
}