diff --git a/src/Utils.cpp b/src/Utils.cpp index 3f3013cc..ecd6039f 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -28,6 +28,25 @@ with this program. If not, see Q_DECLARE_METATYPE(OBSScene); +const QHash 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) { if (!strings) return obs_data_array_create(); @@ -110,7 +129,7 @@ obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_source_t* source, obs_data_t* i if (obs_data_has_user_value(item, "name") && (QString)obs_source_get_name(obs_sceneitem_get_source(sceneItem)) != (QString)obs_data_get_string(item, "name")) { - return nullptr; + return nullptr; } } else if (obs_data_has_user_value(item, "name")) { @@ -554,75 +573,70 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) { 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) { - obs_data_t* data = obs_data_create(); + 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(); - 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)); + obs_data_set_int(posData, "alignment", alignment); 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_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; diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 99d5cf31..ca3d496d 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -920,6 +920,7 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) { * * @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 @@ -940,9 +941,12 @@ void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) { const char* sceneItemName = obs_source_get_name(obs_sceneitem_get_source(sceneItem)); - OBSDataAutoRelease fields = Utils::GetSceneItemPropertiesData(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); } diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index edd8e74a..6b19ae97 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -21,7 +21,7 @@ * @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 {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 {double} `bounds.x` Width of the bounding box. * @return {double} `bounds.y` Height of the bounding box. @@ -75,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.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 {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 {double} `bounds.x` The new width of the bounding box. * @param {double} `bounds.y` The new height of the bounding box. @@ -238,7 +238,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* if (badRequest) { return req->SendErrorResponse(errorMessage); } - + return req->SendOKResponse(); } @@ -363,7 +363,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* r if (!sceneItem) { return req->SendErrorResponse("specified scene item doesn't exist"); } - + vec2 item_position = { 0 }; item_position.x = obs_data_get_double(req->data, "x"); item_position.y = obs_data_get_double(req->data, "y"); @@ -530,7 +530,7 @@ static void DuplicateSceneItem(void *_data, obs_scene_t *scene) { * @return {Object} `item` New item info * @return {int} `̀€item.id` New item ID * @return {String} `item.name` New item name - * + * * @api requests * @name DuplicateSceneItem * @category scene items