mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Merge pull request #272 from BarryCarlyon/scene_item_transform
Support item_transform signal
This commit is contained in:
commit
2eb6463ab0
@ -28,6 +28,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
|
||||
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) {
|
||||
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")) {
|
||||
@ -553,3 +572,72 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
|
||||
config_save(profile);
|
||||
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;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ class Utils {
|
||||
static obs_sceneitem_t* GetSceneItemFromItem(obs_source_t* source, obs_data_t* item);
|
||||
static obs_source_t* GetTransitionFromName(QString transitionName);
|
||||
static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
|
||||
static obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);
|
||||
|
||||
static bool IsValidAlignment(const uint32_t alignment);
|
||||
|
||||
|
@ -256,6 +256,8 @@ void WSEvents::connectSceneSignals(obs_source_t* scene) {
|
||||
"item_remove", OnSceneItemDelete, this);
|
||||
signal_handler_disconnect(sh,
|
||||
"item_visible", OnSceneItemVisibilityChanged, this);
|
||||
signal_handler_disconnect(sh,
|
||||
"item_transform", OnSceneItemTransform, this);
|
||||
}
|
||||
|
||||
currentScene = scene;
|
||||
@ -271,6 +273,8 @@ void WSEvents::connectSceneSignals(obs_source_t* scene) {
|
||||
"item_remove", OnSceneItemDelete, this);
|
||||
signal_handler_connect(sh,
|
||||
"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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
|
@ -116,4 +116,5 @@ private:
|
||||
static void OnSceneItemAdd(void* param, calldata_t* data);
|
||||
static void OnSceneItemDelete(void* param, calldata_t* data);
|
||||
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
|
||||
static void OnSceneItemTransform(void* param, calldata_t* data);
|
||||
};
|
||||
|
@ -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.
|
||||
@ -53,77 +53,9 @@ HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler*
|
||||
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());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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.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.
|
||||
@ -306,7 +238,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
|
||||
if (badRequest) {
|
||||
return req->SendErrorResponse(errorMessage);
|
||||
}
|
||||
|
||||
|
||||
return req->SendOKResponse();
|
||||
}
|
||||
|
||||
@ -431,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");
|
||||
@ -598,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
|
||||
|
Loading…
x
Reference in New Issue
Block a user