obs-scene.c 20.8 KB
Newer Older
J
jp9000 已提交
1 2 3 4 5
/******************************************************************************
    Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>

    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
6
    the Free Software Foundation, either version 2 of the License, or
J
jp9000 已提交
7 8 9 10 11 12 13 14 15 16 17
    (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 已提交
18
#include "util/threading.h"
J
jp9000 已提交
19 20 21
#include "graphics/math-defs.h"
#include "obs-scene.h"

22 23 24
static const char *obs_scene_signals[] = {
	"void item_add(ptr scene, ptr item)",
	"void item_remove(ptr scene, ptr item)",
J
jp9000 已提交
25 26 27 28
	"void item_move_up(ptr scene, ptr item)",
	"void item_move_down(ptr scene, ptr item)",
	"void item_move_top(ptr scene, ptr item)",
	"void item_move_bottom(ptr scene, ptr item)",
29 30 31
	"void item_select(ptr scene, ptr item)",
	"void item_deselect(ptr scene, ptr item)",
	"void item_transform(ptr scene, ptr item)",
32 33 34
	NULL
};

J
jp9000 已提交
35
static inline void signal_item_remove(struct obs_scene_item *item)
J
jp9000 已提交
36
{
J
jp9000 已提交
37 38 39 40
	struct calldata params = {0};
	calldata_setptr(&params, "scene", item->parent);
	calldata_setptr(&params, "item", item);

41 42
	signal_handler_signal(item->parent->source->context.signals,
			"item_remove", &params);
J
jp9000 已提交
43
	calldata_free(&params);
J
jp9000 已提交
44 45
}

46
static const char *scene_getname(void)
47
{
48 49
	/* TODO: locale */
	return "Scene";
50 51
}

52
static void *scene_create(obs_data_t settings, struct obs_source *source)
J
jp9000 已提交
53
{
P
Palana 已提交
54
	pthread_mutexattr_t attr;
J
jp9000 已提交
55
	struct obs_scene *scene = bmalloc(sizeof(struct obs_scene));
56 57 58
	scene->source     = source;
	scene->first_item = NULL;

59 60 61
	signal_handler_add_array(obs_source_signalhandler(source),
			obs_scene_signals);

P
Palana 已提交
62 63 64 65 66
	if (pthread_mutexattr_init(&attr) != 0)
		goto fail;
	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		goto fail;
	if (pthread_mutex_init(&scene->mutex, &attr) != 0) {
67
		blog(LOG_ERROR, "scene_create: Couldn't initialize mutex");
P
Palana 已提交
68
		goto fail;
69
	}
J
jp9000 已提交
70

J
jp9000 已提交
71
	UNUSED_PARAMETER(settings);
J
jp9000 已提交
72
	return scene;
P
Palana 已提交
73 74 75 76 77

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

80
static void remove_all_items(struct obs_scene *scene)
J
jp9000 已提交
81
{
J
jp9000 已提交
82
	struct obs_scene_item *item;
J
jp9000 已提交
83 84

	pthread_mutex_lock(&scene->mutex);
J
jp9000 已提交
85

J
jp9000 已提交
86 87
	item = scene->first_item;

88 89 90 91
	while (item) {
		struct obs_scene_item *del_item = item;
		item = item->next;

J
jp9000 已提交
92
		obs_sceneitem_remove(del_item);
93
	}
J
jp9000 已提交
94

J
jp9000 已提交
95
	pthread_mutex_unlock(&scene->mutex);
96 97 98 99 100
}

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

102
	remove_all_items(scene);
103
	pthread_mutex_destroy(&scene->mutex);
J
jp9000 已提交
104 105 106
	bfree(scene);
}

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
static void scene_enum_sources(void *data,
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
		struct obs_scene_item *next = item->next;

		obs_sceneitem_addref(item);
		enum_callback(scene->source, item->source, param);
		obs_sceneitem_release(item);

		item = next;
	}

	pthread_mutex_unlock(&scene->mutex);
}

130 131 132 133 134 135 136 137 138
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 已提交
139 140

	item->parent = NULL;
141 142
}

J
jp9000 已提交
143 144
static inline void attach_sceneitem(struct obs_scene *parent,
		struct obs_scene_item *item, struct obs_scene_item *prev)
145
{
J
jp9000 已提交
146 147
	item->prev   = prev;
	item->parent = parent;
148 149 150 151 152 153 154 155 156 157 158 159

	if (prev) {
		item->next = prev->next;
		if (prev->next)
			prev->next->prev = item;
		prev->next = item;
	} else {
		item->next = item->parent->first_item;
		item->parent->first_item = item;
	}
}

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
static void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)
{
	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,
		struct vec2 *origin, struct vec2 *scale,
		uint32_t *cx, uint32_t *cy)
{
J
jp9000 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189
	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;

	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) {
190 191 192 193 194 195 196 197 198 199 200 201
		bool  use_width = (bounds_aspect < item_aspect);
		float mul;

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

		mul = use_width ?
			item->bounds.x / width :
			item->bounds.y / height;

		vec2_mulf(scale, scale, mul);

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

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

J
jp9000 已提交
208
	} else if (bounds_type == OBS_BOUNDS_STRETCH) {
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
		scale->x = item->bounds.x / (float)(*cx);
		scale->y = item->bounds.y / (float)(*cy);
	}

	width       = (float)(*cx) * scale->x;
	height      = (float)(*cy) * scale->y;
	width_diff  = item->bounds.x - width;
	height_diff = item->bounds.y - height;
	*cx         = (uint32_t)item->bounds.x;
	*cy         = (uint32_t)item->bounds.y;

	add_alignment(origin, item->bounds_align,
			(int)-width_diff, (int)-height_diff);
}

static void update_item_transform(struct obs_scene_item *item)
{
	uint32_t        width         = obs_source_getwidth(item->source);
	uint32_t        height        = obs_source_getheight(item->source);
	uint32_t        cx            = width;
	uint32_t        cy            = height;
J
jp9000 已提交
230 231
	struct vec2     base_origin;
	struct vec2     origin;
232 233 234
	struct vec2     scale         = item->scale;
	struct calldata params        = {0};

J
jp9000 已提交
235 236 237
	vec2_zero(&base_origin);
	vec2_zero(&origin);

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	/* ----------------------- */

	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);
	matrix4_scale3f(&item->draw_transform, &item->draw_transform,
			scale.x, scale.y, 1.0f);
	matrix4_translate3f(&item->draw_transform, &item->draw_transform,
			-origin.x, -origin.y, 0.0f);
	matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform,
			0.0f, 0.0f, 1.0f, RAD(item->rot));
	matrix4_translate3f(&item->draw_transform, &item->draw_transform,
			item->pos.x, item->pos.y, 0.0f);

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

	if (item->bounds_type != OBS_BOUNDS_NONE) {
		vec2_copy(&scale, &item->bounds);
	} else {
		scale.x = (float)width  * item->scale.x;
		scale.y = (float)height * item->scale.y;
	}

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

	matrix4_identity(&item->box_transform);
	matrix4_scale3f(&item->box_transform, &item->box_transform,
			scale.x, scale.y, 1.0f);
	matrix4_translate3f(&item->box_transform, &item->box_transform,
			-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));
	matrix4_translate3f(&item->box_transform, &item->box_transform,
			item->pos.x, item->pos.y, 0.0f);

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

	item->last_width  = width;
	item->last_height = height;

	calldata_setptr(&params, "scene", item->parent);
	calldata_setptr(&params, "item", item);
	signal_handler_signal(item->parent->source->context.signals,
			"item_transform", &params);
	calldata_free(&params);
}

static inline bool source_size_changed(struct obs_scene_item *item)
{
	uint32_t width  = obs_source_getwidth(item->source);
	uint32_t height = obs_source_getheight(item->source);

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

J
jp9000 已提交
300
static void scene_video_render(void *data, effect_t effect)
J
jp9000 已提交
301
{
302 303 304
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

305 306
	pthread_mutex_lock(&scene->mutex);

307
	item = scene->first_item;
J
jp9000 已提交
308

309
	while (item) {
310
		if (obs_source_removed(item->source)) {
311 312 313
			struct obs_scene_item *del_item = item;
			item = item->next;

J
jp9000 已提交
314
			obs_sceneitem_remove(del_item);
315 316 317
			continue;
		}

318 319
		if (source_size_changed(item))
			update_item_transform(item);
J
jp9000 已提交
320

321 322
		gs_matrix_push();
		gs_matrix_mul(&item->draw_transform);
323
		obs_source_video_render(item->source);
J
jp9000 已提交
324
		gs_matrix_pop();
325 326

		item = item->next;
J
jp9000 已提交
327
	}
328 329

	pthread_mutex_unlock(&scene->mutex);
J
jp9000 已提交
330 331 332 333

	UNUSED_PARAMETER(effect);
}

334 335 336 337 338 339 340 341 342 343 344 345 346 347
static void scene_load_item(struct obs_scene *scene, obs_data_t item_data)
{
	const char            *name = obs_data_getstring(item_data, "name");
	obs_source_t          source = obs_get_source_by_name(name);
	struct obs_scene_item *item;

	if (!source) {
		blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
				name);
		return;
	}

	item = obs_scene_add(scene, source);

348 349 350
	obs_data_set_default_int(item_data, "align",
			OBS_ALIGN_TOP | OBS_ALIGN_LEFT);

351
	item->rot     = (float)obs_data_getdouble(item_data, "rot");
352
	item->align   = (uint32_t)obs_data_getint(item_data, "align");
353 354 355
	item->visible = obs_data_getbool(item_data, "visible");
	obs_data_get_vec2(item_data, "pos",    &item->pos);
	obs_data_get_vec2(item_data, "scale",  &item->scale);
356 357 358 359 360 361 362

	item->bounds_type =
		(enum obs_bounds_type)obs_data_getint(item_data, "bounds_type");
	item->bounds_align =
		(uint32_t)obs_data_getint(item_data, "bounds_align");
	obs_data_get_vec2(item_data, "bounds", &item->bounds);

363
	obs_source_release(source);
364 365

	update_item_transform(item);
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
}

static void scene_load(void *scene, obs_data_t settings)
{
	obs_data_array_t items = obs_data_getarray(settings, "items");
	size_t           count, i;

	remove_all_items(scene);

	if (!items) return;

	count = obs_data_array_count(items);

	for (i = 0; i < count; i++) {
		obs_data_t item_data = obs_data_array_item(items, i);
		scene_load_item(scene, item_data);
		obs_data_release(item_data);
	}

	obs_data_array_release(items);
}

J
jp9000 已提交
388
static void scene_save_item(obs_data_array_t array, struct obs_scene_item *item)
389 390 391 392
{
	obs_data_t item_data = obs_data_create();
	const char *name     = obs_source_getname(item->source);

393 394 395 396 397 398 399 400 401
	obs_data_setstring(item_data, "name",         name);
	obs_data_setbool  (item_data, "visible",      item->visible);
	obs_data_setdouble(item_data, "rot",          item->rot);
	obs_data_set_vec2 (item_data, "pos",          &item->pos);
	obs_data_set_vec2 (item_data, "scale",        &item->scale);
	obs_data_setint   (item_data, "align",        (int)item->align);
	obs_data_setint   (item_data, "bounds_type",  (int)item->bounds_type);
	obs_data_setint   (item_data, "bounds_align", (int)item->bounds_align);
	obs_data_set_vec2 (item_data, "bounds",       &item->bounds);
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416

	obs_data_array_push_back(array, item_data);
	obs_data_release(item_data);
}

static void scene_save(void *data, obs_data_t settings)
{
	struct obs_scene      *scene = data;
	obs_data_array_t      array  = obs_data_array_create();
	struct obs_scene_item *item;

	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
J
jp9000 已提交
417
		scene_save_item(array, item);
418 419 420 421 422 423 424 425 426
		item = item->next;
	}

	pthread_mutex_unlock(&scene->mutex);

	obs_data_setarray(settings, "items", array);
	obs_data_array_release(array);
}

J
jp9000 已提交
427 428 429 430
static uint32_t scene_getwidth(void *data)
{
	UNUSED_PARAMETER(data);
	return obs->video.base_width;
J
jp9000 已提交
431 432
}

J
jp9000 已提交
433
static uint32_t scene_getheight(void *data)
J
jp9000 已提交
434
{
J
jp9000 已提交
435 436
	UNUSED_PARAMETER(data);
	return obs->video.base_height;
J
jp9000 已提交
437 438
}

439
const struct obs_source_info scene_info =
J
jp9000 已提交
440
{
J
jp9000 已提交
441
	.id           = "scene",
442
	.type         = OBS_SOURCE_TYPE_INPUT,
J
jp9000 已提交
443 444 445 446 447
	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
	.getname      = scene_getname,
	.create       = scene_create,
	.destroy      = scene_destroy,
	.video_render = scene_video_render,
J
jp9000 已提交
448 449
	.getwidth     = scene_getwidth,
	.getheight    = scene_getheight,
450 451
	.load         = scene_load,
	.save         = scene_save,
452
	.enum_sources = scene_enum_sources
J
jp9000 已提交
453 454
};

455
obs_scene_t obs_scene_create(const char *name)
J
jp9000 已提交
456
{
457 458 459
	struct obs_source *source =
		obs_source_create(OBS_SOURCE_TYPE_INPUT, "scene", name, NULL);
	return source->context.data;
J
jp9000 已提交
460 461
}

P
Palana 已提交
462
void obs_scene_addref(obs_scene_t scene)
463
{
P
Palana 已提交
464 465
	if (scene)
		obs_source_addref(scene->source);
466 467
}

P
Palana 已提交
468
void obs_scene_release(obs_scene_t scene)
J
jp9000 已提交
469
{
P
Palana 已提交
470 471
	if (scene)
		obs_source_release(scene->source);
J
jp9000 已提交
472 473
}

474
obs_source_t obs_scene_getsource(obs_scene_t scene)
J
jp9000 已提交
475
{
J
jp9000 已提交
476
	return scene ? scene->source : NULL;
J
jp9000 已提交
477 478
}

479 480
obs_scene_t obs_scene_fromsource(obs_source_t source)
{
481
	if (!source || source->info.id != scene_info.id)
482 483
		return NULL;

484
	return source->context.data;
485 486
}

487 488 489 490
obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name)
{
	struct obs_scene_item *item;

J
jp9000 已提交
491 492 493
	if (!scene)
		return NULL;

494 495 496 497
	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
498
		if (strcmp(item->source->context.name, name) == 0)
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
			break;

		item = item->next;
	}

	pthread_mutex_unlock(&scene->mutex);

	return item;
}

void obs_scene_enum_items(obs_scene_t scene,
		bool (*callback)(obs_scene_t, obs_sceneitem_t, void*),
		void *param)
{
	struct obs_scene_item *item;

J
jp9000 已提交
515 516 517
	if (!scene || !callback)
		return;

518 519 520 521
	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
J
jp9000 已提交
522 523 524 525
		struct obs_scene_item *next = item->next;

		obs_sceneitem_addref(item);

526 527
		if (!callback(scene, item, param)) {
			obs_sceneitem_release(item);
528
			break;
529
		}
530

J
jp9000 已提交
531 532 533
		obs_sceneitem_release(item);

		item = next;
534 535 536 537 538
	}

	pthread_mutex_unlock(&scene->mutex);
}

539
obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
J
jp9000 已提交
540
{
541
	struct obs_scene_item *last;
542
	struct obs_scene_item *item;
J
jp9000 已提交
543
	struct calldata params = {0};
544

545 546 547 548
	if (!scene)
		return NULL;

	if (!source) {
J
jp9000 已提交
549
		blog(LOG_ERROR, "Tried to add a NULL source to a scene");
550 551 552 553
		return NULL;
	}

	item = bzalloc(sizeof(struct obs_scene_item));
J
jp9000 已提交
554 555 556
	item->source  = source;
	item->visible = true;
	item->parent  = scene;
J
jp9000 已提交
557
	item->ref     = 1;
558
	item->align   = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
J
jp9000 已提交
559
	vec2_set(&item->scale, 1.0f, 1.0f);
560 561
	matrix4_identity(&item->draw_transform);
	matrix4_identity(&item->box_transform);
J
jp9000 已提交
562

563 564
	obs_source_addref(source);
	obs_source_add_child(scene->source, source);
565

566 567 568 569 570 571 572 573 574 575 576 577
	pthread_mutex_lock(&scene->mutex);

	last = scene->first_item;
	if (!last) {
		scene->first_item = item;
	} else {
		while (last->next)
			last = last->next;

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

579 580
	pthread_mutex_unlock(&scene->mutex);

J
jp9000 已提交
581 582
	calldata_setptr(&params, "scene", scene);
	calldata_setptr(&params, "item", item);
583 584
	signal_handler_signal(scene->source->context.signals, "item_add",
			&params);
J
jp9000 已提交
585 586
	calldata_free(&params);

J
jp9000 已提交
587 588 589
	return item;
}

J
jp9000 已提交
590
static void obs_sceneitem_destroy(obs_sceneitem_t item)
J
jp9000 已提交
591
{
592
	if (item) {
593
		if (item->source)
J
jp9000 已提交
594
			obs_source_release(item->source);
J
jp9000 已提交
595
		bfree(item);
J
jp9000 已提交
596 597 598
	}
}

P
Palana 已提交
599
void obs_sceneitem_addref(obs_sceneitem_t item)
J
jp9000 已提交
600
{
P
Palana 已提交
601
	if (item)
J
jp9000 已提交
602
		os_atomic_inc_long(&item->ref);
J
jp9000 已提交
603
}
604

P
Palana 已提交
605
void obs_sceneitem_release(obs_sceneitem_t item)
J
jp9000 已提交
606
{
P
Palana 已提交
607 608
	if (!item)
		return;
P
Palana 已提交
609

J
jp9000 已提交
610
	if (os_atomic_dec_long(&item->ref) == 0)
P
Palana 已提交
611
		obs_sceneitem_destroy(item);
612 613
}

J
jp9000 已提交
614 615 616 617
void obs_sceneitem_remove(obs_sceneitem_t item)
{
	obs_scene_t scene;

618
	if (!item)
J
jp9000 已提交
619 620
		return;

621
	scene = item->parent;
J
jp9000 已提交
622

623 624
	if (scene)
		pthread_mutex_lock(&scene->mutex);
J
jp9000 已提交
625

J
jp9000 已提交
626
	if (item->removed) {
627 628 629 630 631
		if (scene)
			pthread_mutex_unlock(&scene->mutex);
		return;
	}

J
jp9000 已提交
632 633
	item->removed = true;

634 635
	assert(scene != NULL);
	assert(scene->source != NULL);
636 637
	obs_source_remove_child(scene->source, item->source);

638
	signal_item_remove(item);
J
jp9000 已提交
639
	detach_sceneitem(item);
640

641
	pthread_mutex_unlock(&scene->mutex);
J
jp9000 已提交
642 643 644 645

	obs_sceneitem_release(item);
}

J
jp9000 已提交
646 647
obs_scene_t obs_sceneitem_getscene(obs_sceneitem_t item)
{
J
jp9000 已提交
648
	return item ? item->parent : NULL;
J
jp9000 已提交
649 650
}

651 652
obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item)
{
J
jp9000 已提交
653
	return item ? item->source : NULL;
J
jp9000 已提交
654 655
}

656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
void obs_sceneitem_select(obs_sceneitem_t item, bool select)
{
	struct calldata params = {0};
	const char *command = select ? "item_select" : "item_deselect";

	if (!item || item->selected == select)
		return;

	item->selected = select;

	calldata_setptr(&params, "scene", item->parent);
	calldata_setptr(&params, "item",  item);
	signal_handler_signal(item->parent->source->context.signals,
			command, &params);

	calldata_free(&params);
}

bool obs_sceneitem_selected(obs_sceneitem_t item)
{
	return item ? item->selected : false;
}

679
void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos)
J
jp9000 已提交
680
{
681
	if (item) {
J
jp9000 已提交
682
		vec2_copy(&item->pos, pos);
683 684
		update_item_transform(item);
	}
J
jp9000 已提交
685 686
}

687
void obs_sceneitem_setrot(obs_sceneitem_t item, float rot)
J
jp9000 已提交
688
{
689
	if (item) {
J
jp9000 已提交
690
		item->rot = rot;
691 692
		update_item_transform(item);
	}
J
jp9000 已提交
693 694
}

695
void obs_sceneitem_setscale(obs_sceneitem_t item, const struct vec2 *scale)
J
jp9000 已提交
696
{
697 698 699 700
	if (item) {
		vec2_copy(&item->scale, scale);
		update_item_transform(item);
	}
J
jp9000 已提交
701 702
}

703
void obs_sceneitem_setalignment(obs_sceneitem_t item, uint32_t alignment)
J
jp9000 已提交
704
{
705 706 707 708
	if (item) {
		item->align = alignment;
		update_item_transform(item);
	}
J
jp9000 已提交
709 710
}

J
jp9000 已提交
711 712 713
static inline void signal_move_dir(struct obs_scene_item *item,
		enum order_movement movement)
{
714
	const char *command = NULL;
J
jp9000 已提交
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
	struct calldata params = {0};

	switch (movement) {
	case ORDER_MOVE_UP:     command = "item_move_up";     break;
	case ORDER_MOVE_DOWN:   command = "item_move_down";   break;
	case ORDER_MOVE_TOP:    command = "item_move_top";    break;
	case ORDER_MOVE_BOTTOM: command = "item_move_bottom"; break;
	}

	calldata_setptr(&params, "scene", item->parent);
	calldata_setptr(&params, "item",  item);

	signal_handler_signal(item->parent->source->context.signals,
			command, &params);

	calldata_free(&params);
}

733
void obs_sceneitem_setorder(obs_sceneitem_t item, enum order_movement movement)
J
jp9000 已提交
734
{
J
jp9000 已提交
735 736
	if (!item) return;

J
jp9000 已提交
737
	struct obs_scene_item *next, *prev;
J
jp9000 已提交
738 739
	struct obs_scene *scene = item->parent;

J
jp9000 已提交
740
	obs_scene_addref(scene);
741
	pthread_mutex_lock(&scene->mutex);
742

J
jp9000 已提交
743 744 745
	next = item->next;
	prev = item->prev;

746 747
	detach_sceneitem(item);

J
jp9000 已提交
748 749
	if (movement == ORDER_MOVE_DOWN) {
		attach_sceneitem(scene, item, prev ? prev->prev : NULL);
J
jp9000 已提交
750

J
jp9000 已提交
751 752
	} else if (movement == ORDER_MOVE_UP) {
		attach_sceneitem(scene, item, next ? next : prev);
J
jp9000 已提交
753 754

	} else if (movement == ORDER_MOVE_TOP) {
J
jp9000 已提交
755
		struct obs_scene_item *last = next;
756
		if (!last) {
J
jp9000 已提交
757
			last = prev;
758 759 760 761
		} else {
			while (last->next)
				last = last->next;
		}
J
jp9000 已提交
762

J
jp9000 已提交
763
		attach_sceneitem(scene, item, last);
764 765

	} else if (movement == ORDER_MOVE_BOTTOM) {
J
jp9000 已提交
766
		attach_sceneitem(scene, item, NULL);
J
jp9000 已提交
767
	}
768

J
jp9000 已提交
769 770
	signal_move_dir(item, movement);

771
	pthread_mutex_unlock(&scene->mutex);
772
	obs_scene_release(scene);
J
jp9000 已提交
773 774
}

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
void obs_sceneitem_set_bounds_type(obs_sceneitem_t item,
		enum obs_bounds_type type)
{
	if (item) {
		item->bounds_type = type;
		update_item_transform(item);
	}
}

void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t item,
		uint32_t alignment)
{
	if (item) {
		item->bounds_align = alignment;
		update_item_transform(item);
	}
}

void obs_sceneitem_set_bounds(obs_sceneitem_t item, const struct vec2 *bounds)
{
	if (item) {
		item->bounds = *bounds;
		update_item_transform(item);
	}
}

801
void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos)
J
jp9000 已提交
802
{
J
jp9000 已提交
803 804
	if (item)
		vec2_copy(pos, &item->pos);
J
jp9000 已提交
805 806
}

807
float obs_sceneitem_getrot(obs_sceneitem_t item)
J
jp9000 已提交
808
{
J
jp9000 已提交
809
	return item ? item->rot : 0.0f;
J
jp9000 已提交
810 811
}

812
void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale)
J
jp9000 已提交
813
{
J
jp9000 已提交
814
	if (item)
815
		vec2_copy(scale, &item->scale);
J
jp9000 已提交
816 817
}

818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
uint32_t obs_sceneitem_getalignment(obs_sceneitem_t item)
{
	return item ? item->align : 0;
}

enum obs_bounds_type obs_sceneitem_get_bounds_type(obs_sceneitem_t item)
{
	return item ? item->bounds_type : OBS_BOUNDS_NONE;
}

uint32_t obs_sceneitem_get_bounds_alignment(obs_sceneitem_t item)
{
	return item ? item->bounds_align : 0;
}

void obs_sceneitem_get_bounds(obs_sceneitem_t item, struct vec2 *bounds)
J
jp9000 已提交
834
{
J
jp9000 已提交
835
	if (item)
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
		*bounds = item->bounds;
}

void obs_sceneitem_get_info(obs_sceneitem_t item,
		struct obs_sceneitem_info *info)
{
	if (item && info) {
		info->pos              = item->pos;
		info->rot              = item->rot;
		info->scale            = item->scale;
		info->alignment        = item->align;
		info->bounds_type      = item->bounds_type;
		info->bounds_alignment = item->bounds_align;
		info->bounds           = item->bounds;
	}
}

void obs_sceneitem_set_info(obs_sceneitem_t item,
		const struct obs_sceneitem_info *info)
{
	if (item && info) {
		item->pos          = info->pos;
		item->rot          = info->rot;
		item->scale        = info->scale;
		item->align        = info->alignment;
		item->bounds_type  = info->bounds_type;
		item->bounds_align = info->bounds_alignment;
		item->bounds       = info->bounds;
		update_item_transform(item);
	}
}

void obs_sceneitem_get_draw_transform(obs_sceneitem_t item,
		struct matrix4 *transform)
{
	if (item)
		matrix4_copy(transform, &item->draw_transform);
}

void obs_sceneitem_get_box_transform(obs_sceneitem_t item,
		struct matrix4 *transform)
{
	if (item)
		matrix4_copy(transform, &item->box_transform);
J
jp9000 已提交
880
}