Merge pull request #272 from BarryCarlyon/scene_item_transform

Support item_transform signal
This commit is contained in:
Stéphane Lepin 2019-04-17 01:21:46 +02:00 committed by GitHub
commit 2eb6463ab0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 75 deletions

View File

@ -28,6 +28,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
Q_DECLARE_METATYPE(OBSScene); Q_DECLARE_METATYPE(OBSScene);
const QHash<obs_bounds_type, QString> boundTypeNames = {
{ OBS_BOUNDS_STRETCH, "OBS_BOUNDS_STRETCH" },
{ OBS_BOUNDS_SCALE_INNER, "OBS_BOUNDS_SCALE_INNER" },
{ OBS_BOUNDS_SCALE_OUTER, "OBS_BOUNDS_SCALE_OUTER" },
{ OBS_BOUNDS_SCALE_TO_WIDTH, "OBS_BOUNDS_SCALE_TO_WIDTH" },
{ OBS_BOUNDS_SCALE_TO_HEIGHT, "OBS_BOUNDS_SCALE_TO_HEIGHT" },
{ OBS_BOUNDS_MAX_ONLY, "OBS_BOUNDS_MAX_ONLY" },
{ OBS_BOUNDS_NONE, "OBS_BOUNDS_NONE" },
};
QString getBoundsNameFromType(obs_bounds_type type) {
QString fallback = boundTypeNames.value(OBS_BOUNDS_NONE);
return boundTypeNames.value(type, fallback);
}
obs_bounds_type getBoundsTypeFromName(QString name) {
return boundTypeNames.key(name);
}
obs_data_array_t* Utils::StringListToArray(char** strings, char* key) { obs_data_array_t* Utils::StringListToArray(char** strings, char* key) {
if (!strings) if (!strings)
return obs_data_array_create(); return obs_data_array_create();
@ -553,3 +572,72 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
config_save(profile); config_save(profile);
return true; return true;
} }
// Transform properties copy-pasted from WSRequestHandler_SceneItems.cpp because typedefs can't be extended yet
/**
* @typedef {Object} `SceneItemTransform`
* @property {int} `position.x` The x position of the scene item from the left.
* @property {int} `position.y` The y position of the scene item from the top.
* @property {int} `position.alignment` The point on the scene item that the item is manipulated from.
* @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.
* @property {double} `scale.x` The x-scale factor of the scene item.
* @property {double} `scale.y` The y-scale factor of the scene item.
* @property {int} `crop.top` The number of pixels cropped off the top of the scene item before scaling.
* @property {int} `crop.right` The number of pixels cropped off the right of the scene item before scaling.
* @property {int} `crop.bottom` The number of pixels cropped off the bottom of the scene item before scaling.
* @property {int} `crop.left` The number of pixels cropped off the left of the scene item before scaling.
* @property {bool} `visible` If the scene item is visible.
* @property {String} `bounds.type` Type of bounding box. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE".
* @property {int} `bounds.alignment` Alignment of the bounding box.
* @property {double} `bounds.x` Width of the bounding box.
* @property {double} `bounds.y` Height of the bounding box.
*/
obs_data_t* Utils::GetSceneItemPropertiesData(obs_sceneitem_t* sceneItem) {
vec2 pos, scale, bounds;
obs_sceneitem_crop crop;
obs_sceneitem_get_pos(sceneItem, &pos);
obs_sceneitem_get_scale(sceneItem, &scale);
obs_sceneitem_get_crop(sceneItem, &crop);
obs_sceneitem_get_bounds(sceneItem, &bounds);
uint32_t alignment = obs_sceneitem_get_alignment(sceneItem);
float rotation = obs_sceneitem_get_rot(sceneItem);
bool isVisible = obs_sceneitem_visible(sceneItem);
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(sceneItem);
uint32_t boundsAlignment = obs_sceneitem_get_bounds_alignment(sceneItem);
QString boundsTypeName = getBoundsNameFromType(boundsType);
OBSDataAutoRelease posData = obs_data_create();
obs_data_set_double(posData, "x", pos.x);
obs_data_set_double(posData, "y", pos.y);
obs_data_set_int(posData, "alignment", alignment);
OBSDataAutoRelease scaleData = obs_data_create();
obs_data_set_double(scaleData, "x", scale.x);
obs_data_set_double(scaleData, "y", scale.y);
OBSDataAutoRelease cropData = obs_data_create();
obs_data_set_int(cropData, "left", crop.left);
obs_data_set_int(cropData, "top", crop.top);
obs_data_set_int(cropData, "right", crop.right);
obs_data_set_int(cropData, "bottom", crop.bottom);
OBSDataAutoRelease boundsData = obs_data_create();
obs_data_set_string(boundsData, "type", boundsTypeName.toUtf8());
obs_data_set_int(boundsData, "alignment", boundsAlignment);
obs_data_set_double(boundsData, "x", bounds.x);
obs_data_set_double(boundsData, "y", bounds.y);
obs_data_t* data = obs_data_create();
obs_data_set_obj(data, "position", posData);
obs_data_set_double(data, "rotation", rotation);
obs_data_set_obj(data, "scale", scaleData);
obs_data_set_obj(data, "crop", cropData);
obs_data_set_bool(data, "visible", isVisible);
obs_data_set_obj(data, "bounds", boundsData);
return data;
}

View File

@ -41,6 +41,7 @@ class Utils {
static obs_sceneitem_t* GetSceneItemFromItem(obs_source_t* source, obs_data_t* item); static obs_sceneitem_t* GetSceneItemFromItem(obs_source_t* source, obs_data_t* item);
static obs_source_t* GetTransitionFromName(QString transitionName); static obs_source_t* GetTransitionFromName(QString transitionName);
static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName); static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
static obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);
static bool IsValidAlignment(const uint32_t alignment); static bool IsValidAlignment(const uint32_t alignment);

View File

@ -256,6 +256,8 @@ void WSEvents::connectSceneSignals(obs_source_t* scene) {
"item_remove", OnSceneItemDelete, this); "item_remove", OnSceneItemDelete, this);
signal_handler_disconnect(sh, signal_handler_disconnect(sh,
"item_visible", OnSceneItemVisibilityChanged, this); "item_visible", OnSceneItemVisibilityChanged, this);
signal_handler_disconnect(sh,
"item_transform", OnSceneItemTransform, this);
} }
currentScene = scene; currentScene = scene;
@ -271,6 +273,8 @@ void WSEvents::connectSceneSignals(obs_source_t* scene) {
"item_remove", OnSceneItemDelete, this); "item_remove", OnSceneItemDelete, this);
signal_handler_connect(sh, signal_handler_connect(sh,
"item_visible", OnSceneItemVisibilityChanged, this); "item_visible", OnSceneItemVisibilityChanged, this);
signal_handler_connect(sh,
"item_transform", OnSceneItemTransform, this);
} }
} }
@ -911,6 +915,42 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
instance->broadcastUpdate("SceneItemVisibilityChanged", fields); instance->broadcastUpdate("SceneItemVisibilityChanged", fields);
} }
/**
* An item's transfrom has been changed.
*
* @return {String} `scene-name` Name of the scene.
* @return {String} `item-name` Name of the item in the scene.
* @return {SceneItemProperties} `transform` Scene item transform properties
*
* @api events
* @name SceneItemTransformChanged
* @category sources
* @since unreleased
*/
void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) {
WSEvents* instance = static_cast<WSEvents*>(param);
obs_scene_t* scene = nullptr;
calldata_get_ptr(data, "scene", &scene);
obs_sceneitem_t* sceneItem = nullptr;
calldata_get_ptr(data, "item", &sceneItem);
const char* sceneName =
obs_source_get_name(obs_scene_get_source(scene));
const char* sceneItemName =
obs_source_get_name(obs_sceneitem_get_source(sceneItem));
OBSDataAutoRelease transform = Utils::GetSceneItemPropertiesData(sceneItem);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "scene-name", sceneName);
obs_data_set_string(fields, "item-name", sceneItemName);
obs_data_set_obj(fields, "transform", transform);
instance->broadcastUpdate("SceneItemTransformChanged", fields);
}
/** /**
* The selected preview scene has changed (only available in Studio Mode). * The selected preview scene has changed (only available in Studio Mode).
* *

View File

@ -116,4 +116,5 @@ private:
static void OnSceneItemAdd(void* param, calldata_t* data); static void OnSceneItemAdd(void* param, calldata_t* data);
static void OnSceneItemDelete(void* param, calldata_t* data); static void OnSceneItemDelete(void* param, calldata_t* data);
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data); static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
static void OnSceneItemTransform(void* param, calldata_t* data);
}; };

View File

@ -21,7 +21,7 @@
* @return {int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling. * @return {int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling.
* @return {int} `crop.left` The number of pixels cropped off the left of the source before scaling. * @return {int} `crop.left` The number of pixels cropped off the left of the source before scaling.
* @return {bool} `visible` If the source is visible. * @return {bool} `visible` If the source is visible.
* @return {String} `bounds.type` Type of bounding box. * @return {String} `bounds.type` Type of bounding box. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE".
* @return {int} `bounds.alignment` Alignment of the bounding box. * @return {int} `bounds.alignment` Alignment of the bounding box.
* @return {double} `bounds.x` Width of the bounding box. * @return {double} `bounds.x` Width of the bounding box.
* @return {double} `bounds.y` Height of the bounding box. * @return {double} `bounds.y` Height of the bounding box.
@ -53,77 +53,9 @@ HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler*
return req->SendErrorResponse("specified scene item doesn't exist"); return req->SendErrorResponse("specified scene item doesn't exist");
} }
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem);
obs_data_set_string(data, "name", itemName.toUtf8()); obs_data_set_string(data, "name", itemName.toUtf8());
OBSDataAutoRelease posData = obs_data_create();
vec2 pos;
obs_sceneitem_get_pos(sceneItem, &pos);
obs_data_set_double(posData, "x", pos.x);
obs_data_set_double(posData, "y", pos.y);
obs_data_set_int(posData, "alignment", obs_sceneitem_get_alignment(sceneItem));
obs_data_set_obj(data, "position", posData);
obs_data_set_double(data, "rotation", obs_sceneitem_get_rot(sceneItem));
OBSDataAutoRelease scaleData = obs_data_create();
vec2 scale;
obs_sceneitem_get_scale(sceneItem, &scale);
obs_data_set_double(scaleData, "x", scale.x);
obs_data_set_double(scaleData, "y", scale.y);
obs_data_set_obj(data, "scale", scaleData);
OBSDataAutoRelease cropData = obs_data_create();
obs_sceneitem_crop crop;
obs_sceneitem_get_crop(sceneItem, &crop);
obs_data_set_int(cropData, "left", crop.left);
obs_data_set_int(cropData, "top", crop.top);
obs_data_set_int(cropData, "right", crop.right);
obs_data_set_int(cropData, "bottom", crop.bottom);
obs_data_set_obj(data, "crop", cropData);
obs_data_set_bool(data, "visible", obs_sceneitem_visible(sceneItem));
OBSDataAutoRelease boundsData = obs_data_create();
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(sceneItem);
if (boundsType == OBS_BOUNDS_NONE) {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_NONE");
}
else {
switch (boundsType) {
case OBS_BOUNDS_STRETCH: {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_STRETCH");
break;
}
case OBS_BOUNDS_SCALE_INNER: {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_INNER");
break;
}
case OBS_BOUNDS_SCALE_OUTER: {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_OUTER");
break;
}
case OBS_BOUNDS_SCALE_TO_WIDTH: {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_TO_WIDTH");
break;
}
case OBS_BOUNDS_SCALE_TO_HEIGHT: {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_TO_HEIGHT");
break;
}
case OBS_BOUNDS_MAX_ONLY: {
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_MAX_ONLY");
break;
}
}
obs_data_set_int(boundsData, "alignment", obs_sceneitem_get_bounds_alignment(sceneItem));
vec2 bounds;
obs_sceneitem_get_bounds(sceneItem, &bounds);
obs_data_set_double(boundsData, "x", bounds.x);
obs_data_set_double(boundsData, "y", bounds.y);
}
obs_data_set_obj(data, "bounds", boundsData);
return req->SendOKResponse(data); return req->SendOKResponse(data);
} }
@ -143,7 +75,7 @@ HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler*
* @param {int} `crop.left` The new amount of pixels cropped off the left of the source before scaling. * @param {int} `crop.left` The new amount of pixels cropped off the left of the source before scaling.
* @param {int} `crop.right` The new amount of pixels cropped off the right of the source before scaling. * @param {int} `crop.right` The new amount of pixels cropped off the right of the source before scaling.
* @param {bool} `visible` The new visibility of the source. 'true' shows source, 'false' hides source. * @param {bool} `visible` The new visibility of the source. 'true' shows source, 'false' hides source.
* @param {String} `bounds.type` The new bounds type of the source. * @param {String} `bounds.type` The new bounds type of the source. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE".
* @param {int} `bounds.alignment` The new alignment of the bounding box. (0-2, 4-6, 8-10) * @param {int} `bounds.alignment` The new alignment of the bounding box. (0-2, 4-6, 8-10)
* @param {double} `bounds.x` The new width of the bounding box. * @param {double} `bounds.x` The new width of the bounding box.
* @param {double} `bounds.y` The new height of the bounding box. * @param {double} `bounds.y` The new height of the bounding box.