Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6635611
utils: Handle json::null types when converting to obs_data_t
tt2468 Feb 25, 2026
ae7b278
requesthandler: Fix unused-parameter build error from GetCanvasList
tt2468 Feb 25, 2026
bf2f720
utils: Add Utils::Obs::ObjectHelper::GetCanvasVideoSettings()
tt2468 Feb 25, 2026
fda36b7
requesthandler: Use GetCanvasVideoSettings util for GetCanvasList
tt2468 Feb 25, 2026
2270da3
requesthandler: Convert GetVideoSettings to use GetCanvasVideoSettings
tt2468 Feb 25, 2026
f25c9ce
docs: Small nitpicks to GetCanvasList documentation
tt2468 Feb 26, 2026
fc1524a
requesthandler: Use object of bools for canvas flags
tt2468 Feb 25, 2026
c1cce55
docs: Small nitpick, pluralize Canvases
tt2468 Feb 26, 2026
d949f5f
requesthandler: Remove canvasName in requests, small perf improvement
tt2468 Feb 27, 2026
8306759
requesthandler: Rename "Validate" style object requests to "Get"
tt2468 Feb 27, 2026
5b2558a
requesthandler: Perform better check for invalid canvas
tt2468 Feb 27, 2026
54cd476
requesthandler: Various fixes and improvements to scenes requests
tt2468 Feb 27, 2026
ed683b3
utils: Remove unused GetGroupList()
tt2468 Feb 27, 2026
201cf8c
utils: Clean up GetCanvasSceneList and set sceneIndex to null
tt2468 Feb 27, 2026
2547fe3
eventhandler: Ignore scene events for non-main canvases
tt2468 Feb 27, 2026
dc4f7ba
requesthandler: Add canvas support to CreateScene
tt2468 Feb 27, 2026
079dc85
requesthandler: Rename "Get" style object requests to "Acquire"
Warchamp7 Feb 27, 2026
1427439
requesthandler: Guard against null canvas
Warchamp7 Feb 27, 2026
23ea344
eventhandler: Guard against null canvas
Warchamp7 Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/generate_md.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'General',
'Config',
'Sources',
'Canvas',
'Canvases',
'Scenes',
'Inputs',
'Transitions',
Expand Down
6 changes: 3 additions & 3 deletions src/eventhandler/EventHandler_Canvases.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @rpcVersion -1
* @initialVersion 5.7.0
* @api events
* @category canvas
* @category canvases
*/
void EventHandler::HandleCanvasCreated(obs_canvas_t *canvas)
{
Expand All @@ -53,7 +53,7 @@ void EventHandler::HandleCanvasCreated(obs_canvas_t *canvas)
* @rpcVersion -1
* @initialVersion 5.7.0
* @api events
* @category canvas
* @category canvases
*/
void EventHandler::HandleCanvasRemoved(obs_canvas_t *canvas)
{
Expand All @@ -76,7 +76,7 @@ void EventHandler::HandleCanvasRemoved(obs_canvas_t *canvas)
* @rpcVersion -1
* @initialVersion 5.7.0
* @api events
* @category canvas
* @category canvases
*/
void EventHandler::HandleCanvasNameChanged(obs_canvas_t *canvas, std::string oldCanvasName, std::string canvasName)
{
Expand Down
63 changes: 28 additions & 35 deletions src/eventhandler/EventHandler_SceneItems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,17 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
if (!sceneItem)
return;

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sourceUuid"] = obs_source_get_uuid(obs_sceneitem_get_source(sceneItem));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemCreated", eventData);
}

Expand Down Expand Up @@ -95,17 +94,16 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
if (!sceneItem)
return;

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sourceUuid"] = obs_source_get_uuid(obs_sceneitem_get_source(sceneItem));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemRemoved", eventData);
}

Expand All @@ -132,15 +130,14 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
if (!scene)
return;

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemListReindexed", eventData);
}

Expand Down Expand Up @@ -174,16 +171,15 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da

bool sceneItemEnabled = calldata_bool(data, "visible");

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemEnabled"] = sceneItemEnabled;
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemEnableStateChanged", eventData);
}

Expand Down Expand Up @@ -217,16 +213,15 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data

bool sceneItemLocked = calldata_bool(data, "locked");

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemLocked"] = sceneItemLocked;
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData);
}

Expand Down Expand Up @@ -257,15 +252,14 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
if (!sceneItem)
return;

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemSelected", eventData);
}

Expand Down Expand Up @@ -300,15 +294,14 @@ void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data
if (!sceneItem)
return;

OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(obs_scene_get_source(scene));
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
eventHandler->BroadcastEvent(EventSubscription::SceneItemTransformChanged, "SceneItemTransformChanged", eventData);
}
30 changes: 15 additions & 15 deletions src/eventhandler/EventHandler_Scenes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,14 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/
void EventHandler::HandleSceneCreated(obs_source_t *source)
{
OBSCanvasAutoRelease canvas = obs_source_get_canvas(source);
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(source);
eventData["sceneUuid"] = obs_source_get_uuid(source);
eventData["isGroup"] = obs_source_is_group(source);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(source);
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
BroadcastEvent(EventSubscription::Scenes, "SceneCreated", eventData);
}

Expand All @@ -65,15 +64,17 @@ void EventHandler::HandleSceneCreated(obs_source_t *source)
*/
void EventHandler::HandleSceneRemoved(obs_source_t *source)
{
OBSCanvasAutoRelease canvas = obs_source_get_canvas(source);
// NOTE: Groups do not emit source_remove when they are deleted and canvas will already be NULL
// during source_destroy. As a result, this event will never be emitted here for groups.
// This should be fixed in the future when more thorough canvas support is added.
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneName"] = obs_source_get_name(source);
eventData["sceneUuid"] = obs_source_get_uuid(source);
eventData["isGroup"] = obs_source_is_group(source);
OBSCanvasAutoRelease canvas = obs_source_get_canvas(source);
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
BroadcastEvent(EventSubscription::Scenes, "SceneRemoved", eventData);
}

Expand All @@ -94,15 +95,14 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
*/
void EventHandler::HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName)
{
OBSCanvasAutoRelease canvas = obs_source_get_canvas(source);
if (!canvas || !(obs_canvas_get_flags(canvas) & MAIN))
return;

json eventData;
eventData["sceneUuid"] = obs_source_get_uuid(source);
eventData["oldSceneName"] = oldSceneName;
eventData["sceneName"] = sceneName;
OBSCanvasAutoRelease canvas = obs_source_get_canvas(source);
if (canvas) {
eventData["canvasName"] = obs_canvas_get_name(canvas);
eventData["canvasUuid"] = obs_canvas_get_uuid(canvas);
}
BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged", eventData);
}

Expand Down
40 changes: 21 additions & 19 deletions src/requesthandler/RequestHandler_Canvases.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,41 +22,43 @@ with this program. If not, see <https://www.gnu.org/licenses/>
/**
* Gets an array of canvases in OBS.
*
* @responseField canvases | Array<Object> | Array of canvases
* @responseField canvases | Array<Object> | Array of canvases
*
* @requestType GetCanvasList
* @complexity 2
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.7.0
* @api requests
* @category canvas
* @category canvases
*/
RequestResult RequestHandler::GetCanvasList(const Request &request)
RequestResult RequestHandler::GetCanvasList(const Request &)
{
json responseData;
std::vector<json> ret;
std::vector<json> canvases;

obs_enum_canvases(
[](void *param, obs_canvas_t *canvas) {
auto ret = static_cast<std::vector<json> *>(param);
auto canvases = static_cast<std::vector<json> *>(param);

json canvasJson;
canvasJson["canvasName"] = obs_canvas_get_name(canvas);
canvasJson["canvasUuid"] = obs_canvas_get_uuid(canvas);
canvasJson["flags"] = obs_canvas_get_flags(canvas);
struct obs_video_info ovi;
if (obs_canvas_get_video_info(canvas, &ovi)) {
canvasJson["fpsNumerator"] = ovi.fps_num;
canvasJson["fpsDenominator"] = ovi.fps_den;
canvasJson["baseWidth"] = ovi.base_width;
canvasJson["baseHeight"] = ovi.base_height;
canvasJson["outputWidth"] = ovi.output_width;
canvasJson["outputHeight"] = ovi.output_height;
}
ret->push_back(canvasJson);

auto flags = obs_canvas_get_flags(canvas);
json canvasFlags;
canvasFlags["MAIN"] = !!(flags & MAIN);
canvasFlags["ACTIVATE"] = !!(flags & ACTIVATE);
canvasFlags["MIX_AUDIO"] = !!(flags & MIX_AUDIO);
canvasFlags["SCENE_REF"] = !!(flags & SCENE_REF);
canvasFlags["EPHEMERAL"] = !!(flags & EPHEMERAL);
canvasJson["canvasFlags"] = canvasFlags;

canvasJson["canvasVideoSettings"] = Utils::Obs::ObjectHelper::GetCanvasVideoSettings(canvas);
canvases->push_back(canvasJson);
return true;
},
&ret);
responseData["canvases"] = ret;
&canvases);
responseData["canvases"] = canvases;

return RequestResult::Success(responseData);
}
14 changes: 4 additions & 10 deletions src/requesthandler/RequestHandler_Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,17 +440,11 @@ RequestResult RequestHandler::SetProfileParameter(const Request &request)
*/
RequestResult RequestHandler::GetVideoSettings(const Request &)
{
struct obs_video_info ovi;
if (!obs_get_video_info(&ovi))
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to get internal OBS video info.");
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
if (!mainCanvas)
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to get internal main OBS canvas.");

json responseData;
responseData["fpsNumerator"] = ovi.fps_num;
responseData["fpsDenominator"] = ovi.fps_den;
responseData["baseWidth"] = ovi.base_width;
responseData["baseHeight"] = ovi.base_height;
responseData["outputWidth"] = ovi.output_width;
responseData["outputHeight"] = ovi.output_height;
json responseData = Utils::Obs::ObjectHelper::GetCanvasVideoSettings(mainCanvas);

return RequestResult::Success(responseData);
}
Expand Down
Loading