static bool FindItemAtPos(obs_scene_t scene, obs_sceneitem_t item, void *param)
{
	SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
	matrix4       transform;
	vec3          transformedPos;
	vec3          pos3;

	vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);

	obs_sceneitem_get_box_transform(item, &transform);

	matrix4_inv(&transform, &transform);
	vec3_transform(&transformedPos, &pos3, &transform);

	if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
	    transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
		if (data->selectBelow && obs_sceneitem_selected(item)) {
			if (data->item)
				return false;
			else
				data->selectBelow = false;
		}

		data->item = item;
	}

	UNUSED_PARAMETER(scene);
	return true;
}
static bool AddItemBounds(obs_scene_t scene, obs_sceneitem_t item,
		void *param)
{
	SelectedItemBounds *data = reinterpret_cast<SelectedItemBounds*>(param);

	if (!obs_sceneitem_selected(item))
		return true;

	matrix4 boxTransform;
	obs_sceneitem_get_box_transform(item, &boxTransform);

	vec3 t[4] = {
		GetTransformedPos(0.0f, 0.0f, boxTransform),
		GetTransformedPos(1.0f, 0.0f, boxTransform),
		GetTransformedPos(0.0f, 1.0f, boxTransform),
		GetTransformedPos(1.0f, 1.0f, boxTransform)
	};

	for (const vec3 &v : t) {
		if (data->first) {
			vec3_copy(&data->tl, &v);
			vec3_copy(&data->br, &v);
			data->first = false;
		} else {
			vec3_min(&data->tl, &data->tl, &v);
			vec3_max(&data->br, &data->br, &v);
		}
	}

	UNUSED_PARAMETER(scene);
	return true;
}
void OBSBasicPreview::DoCtrlSelect(const vec2 &pos)
{
	OBSSceneItem item = GetItemAtPos(pos, false);
	if (!item)
		return;

	bool selected = obs_sceneitem_selected(item);
	obs_sceneitem_select(item, !selected);
}
static bool move_items(obs_scene_t scene, obs_sceneitem_t item, void *param)
{
	vec2 *offset = reinterpret_cast<vec2*>(param);

	if (obs_sceneitem_selected(item)) {
		vec2 pos;
		obs_sceneitem_getpos(item, &pos);
		vec2_add(&pos, &pos, offset);
		obs_sceneitem_setpos(item, &pos);
	}

	UNUSED_PARAMETER(scene);
	return true;
}
static OBSSceneItem FindASelectedItem(OBSScene scene)
{
	auto func = [] (obs_scene_t *scene, obs_sceneitem_t *item, void *param)
	{
		OBSSceneItem &dst = *reinterpret_cast<OBSSceneItem*>(param);

		if (obs_sceneitem_selected(item)) {
			dst = item;
			return false;
		}

		UNUSED_PARAMETER(scene);
		return true;
	};

	OBSSceneItem item;
	obs_scene_enum_items(scene, func, &item);
	return item;
}
static bool FindHandleAtPos(obs_scene_t scene, obs_sceneitem_t item,
		void *param)
{
	if (!obs_sceneitem_selected(item))
		return true;

	HandleFindData *data = reinterpret_cast<HandleFindData*>(param);
	matrix4        transform;
	vec3           pos3;
	float          closestHandle = HANDLE_SEL_RADIUS;

	vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);

	obs_sceneitem_get_box_transform(item, &transform);

	auto TestHandle = [&] (float x, float y, ItemHandle handle)
	{
		vec3 handlePos = GetTransformedPosScaled(x, y, transform,
				data->scale);

		float dist = vec3_dist(&handlePos, &pos3);
		if (dist < HANDLE_SEL_RADIUS) {
			if (dist < closestHandle) {
				closestHandle = dist;
				data->handle  = handle;
				data->item    = item;
			}
		}
	};

	TestHandle(0.0f, 0.0f, ItemHandle::TopLeft);
	TestHandle(0.5f, 0.0f, ItemHandle::TopCenter);
	TestHandle(1.0f, 0.0f, ItemHandle::TopRight);
	TestHandle(0.0f, 0.5f, ItemHandle::CenterLeft);
	TestHandle(1.0f, 0.5f, ItemHandle::CenterRight);
	TestHandle(0.0f, 1.0f, ItemHandle::BottomLeft);
	TestHandle(0.5f, 1.0f, ItemHandle::BottomCenter);
	TestHandle(1.0f, 1.0f, ItemHandle::BottomRight);

	UNUSED_PARAMETER(scene);
	return true;
}
bool OBSBasicPreview::DrawSelectedItem(obs_scene_t scene, obs_sceneitem_t item,
		void *param)
{
	if (!obs_sceneitem_selected(item))
		return true;

	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());

	gs_load_vertexbuffer(main->circle);

	matrix4 boxTransform;
	obs_sceneitem_get_box_transform(item, &boxTransform);

	gs_matrix_push();
	gs_matrix_scale3f(HANDLE_RADIUS, HANDLE_RADIUS, 1.0f);
	DrawCircleAtPos(0.0f, 0.0f, boxTransform, main->previewScale);
	DrawCircleAtPos(0.0f, 1.0f, boxTransform, main->previewScale);
	DrawCircleAtPos(1.0f, 0.0f, boxTransform, main->previewScale);
	DrawCircleAtPos(1.0f, 1.0f, boxTransform, main->previewScale);
	DrawCircleAtPos(0.5f, 0.0f, boxTransform, main->previewScale);
	DrawCircleAtPos(0.0f, 0.5f, boxTransform, main->previewScale);
	DrawCircleAtPos(0.5f, 1.0f, boxTransform, main->previewScale);
	DrawCircleAtPos(1.0f, 0.5f, boxTransform, main->previewScale);
	gs_matrix_pop();

	gs_load_vertexbuffer(main->box);

	gs_matrix_push();
	gs_matrix_set(&boxTransform);
	gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
	gs_draw(GS_LINESTRIP, 0, 0);

	gs_matrix_pop();

	UNUSED_PARAMETER(scene);
	UNUSED_PARAMETER(param);
	return true;
}