diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index fdfbbc8a..f2f829f0 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -83,16 +83,6 @@ const char* calldata_get_string(const calldata_t* data, const char* name) { return value; } -WSEventsPtr WSEvents::_instance = WSEventsPtr(nullptr); - -WSEventsPtr WSEvents::Current() { - return _instance; -} - -void WSEvents::ResetCurrent(WSServerPtr srv) { - _instance = WSEventsPtr(new WSEvents(srv)); -} - WSEvents::WSEvents(WSServerPtr srv) : _srv(srv), _streamStarttime(0), @@ -114,6 +104,13 @@ WSEvents::WSEvents(WSServerPtr srv) : heartbeatTimer.start(STATUS_INTERVAL); + // Connect to signals of all existing sources + obs_enum_sources([](void* param, obs_source_t* source) { + auto self = reinterpret_cast(param); + self->connectSourceSignals(source); + return true; + }, this); + signal_handler_t* coreSignalHandler = obs_get_signal_handler(); if (coreSignalHandler) { signal_handler_connect(coreSignalHandler, "source_create", OnSourceCreate, this); @@ -128,8 +125,17 @@ WSEvents::~WSEvents() { signal_handler_disconnect(coreSignalHandler, "source_create", OnSourceCreate, this); } + // Disconnect from signals of all existing sources + obs_enum_sources([](void* param, obs_source_t* source) { + auto self = reinterpret_cast(param); + self->disconnectSourceSignals(source); + return true; + }, this); + obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this); os_cpu_usage_info_destroy(cpuUsageInfo); + + blog(LOG_INFO, "wsevents destroyed"); } void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private_data) { @@ -247,6 +253,73 @@ void WSEvents::broadcastUpdate(const char* updateType, } } +void WSEvents::connectSourceSignals(obs_source_t* source) { + if (!source) { + return; + } + + // Disconnect everything first to avoid double-binding + disconnectSourceSignals(source); + + obs_source_type sourceType = obs_source_get_type(source); + signal_handler_t* sh = obs_source_get_signal_handler(source); + + signal_handler_connect(sh, "rename", OnSourceRename, this); + + signal_handler_connect(sh, "mute", OnSourceMuteStateChange, this); + signal_handler_connect(sh, "volume", OnSourceVolumeChange, this); + signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this); + signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, this); + + signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this); + signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this); + signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this); + + if (sourceType == OBS_SOURCE_TYPE_SCENE) { + signal_handler_connect(sh, "reorder", OnSceneReordered, this); + signal_handler_connect(sh, "item_add", OnSceneItemAdd, this); + signal_handler_connect(sh, "item_remove", OnSceneItemDelete, this); + signal_handler_connect(sh, + "item_visible", OnSceneItemVisibilityChanged, this); + signal_handler_connect(sh, "item_transform", OnSceneItemTransform, this); + signal_handler_connect(sh, "item_select", OnSceneItemSelected, this); + signal_handler_connect(sh, "item_deselect", OnSceneItemDeselected, this); + } + if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { + signal_handler_connect(sh, "transition_start", OnTransitionBegin, this); + } +} + +void WSEvents::disconnectSourceSignals(obs_source_t* source) { + if (!source) { + return; + } + + signal_handler_t* sh = obs_source_get_signal_handler(source); + + signal_handler_disconnect(sh, "rename", OnSourceRename, this); + + signal_handler_disconnect(sh, "mute", OnSourceMuteStateChange, this); + signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this); + signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this); + signal_handler_disconnect(sh, "audio_mixers", OnSourceAudioMixersChanged, this); + + signal_handler_disconnect(sh, "filter_add", OnSourceFilterAdded, this); + signal_handler_disconnect(sh, "filter_remove", OnSourceFilterRemoved, this); + signal_handler_disconnect(sh, "reorder_filters", OnSourceFilterOrderChanged, this); + + signal_handler_disconnect(sh, "reorder", OnSceneReordered, this); + signal_handler_disconnect(sh, "item_add", OnSceneItemAdd, this); + signal_handler_disconnect(sh, "item_remove", OnSceneItemDelete, this); + signal_handler_disconnect(sh, + "item_visible", OnSceneItemVisibilityChanged, this); + signal_handler_disconnect(sh, "item_transform", OnSceneItemTransform, this); + signal_handler_disconnect(sh, "item_select", OnSceneItemSelected, this); + signal_handler_disconnect(sh, "item_deselect", OnSceneItemDeselected, this); + + signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); +} + uint64_t WSEvents::GetStreamingTime() { if (obs_frontend_streaming_active()) return (os_gettime_ns() - _streamStarttime); @@ -801,39 +874,15 @@ void WSEvents::OnSourceCreate(void* param, calldata_t* data) { return; } - obs_source_type sourceType = obs_source_get_type(source); - signal_handler_t* sh = obs_source_get_signal_handler(source); - - signal_handler_connect(sh, "rename", OnSourceRename, self); - - signal_handler_connect(sh, "mute", OnSourceMuteStateChange, self); - signal_handler_connect(sh, "volume", OnSourceVolumeChange, self); - signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, self); - signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, self); - - signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, self); - signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, self); - signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, self); - - if (sourceType == OBS_SOURCE_TYPE_SCENE) { - signal_handler_connect(sh, "reorder", OnSceneReordered, self); - signal_handler_connect(sh, "item_add", OnSceneItemAdd, self); - signal_handler_connect(sh, "item_remove", OnSceneItemDelete, self); - signal_handler_connect(sh, - "item_visible", OnSceneItemVisibilityChanged, self); - signal_handler_connect(sh, "item_transform", OnSceneItemTransform, self); - signal_handler_connect(sh, "item_select", OnSceneItemSelected, self); - signal_handler_connect(sh, "item_deselect", OnSceneItemDeselected, self); - } - if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { - signal_handler_connect(sh, "transition_start", OnTransitionBegin, self); - } + self->connectSourceSignals(source); OBSDataAutoRelease sourceSettings = obs_source_get_settings(source); OBSDataAutoRelease fields = obs_data_create(); obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); - obs_data_set_string(fields, "sourceType", sourceTypeToString(sourceType)); + obs_data_set_string(fields, "sourceType", + sourceTypeToString(obs_source_get_type(source)) + ); obs_data_set_string(fields, "sourceKind", obs_source_get_id(source)); obs_data_set_obj(fields, "sourceSettings", sourceSettings); self->broadcastUpdate("SourceCreated", fields); @@ -859,6 +908,8 @@ void WSEvents::OnSourceDestroy(void* param, calldata_t* data) { return; } + self->disconnectSourceSignals(source); + obs_source_type sourceType = obs_source_get_type(source); OBSDataAutoRelease fields = obs_data_create(); diff --git a/src/WSEvents.h b/src/WSEvents.h index 5f1d5afc..9250dc9e 100644 --- a/src/WSEvents.h +++ b/src/WSEvents.h @@ -29,21 +29,16 @@ with this program. If not, see #include "WSServer.h" -class WSEvents; -typedef QSharedPointer WSEventsPtr; - class WSEvents : public QObject { Q_OBJECT public: - static WSEventsPtr Current(); - static void ResetCurrent(WSServerPtr srv); - explicit WSEvents(WSServerPtr srv); ~WSEvents(); - static void FrontendEventHandler( - enum obs_frontend_event event, void* privateData); + + void connectSourceSignals(obs_source_t* source); + void disconnectSourceSignals(obs_source_t* source); uint64_t GetStreamingTime(); const char* GetStreamingTimecode(); @@ -59,8 +54,6 @@ private slots: void TransitionDurationChanged(int ms); private: - static WSEventsPtr _instance; - WSServerPtr _srv; QTimer streamStatusTimer; QTimer heartbeatTimer; @@ -108,6 +101,9 @@ private: void OnExit(); + static void FrontendEventHandler( + enum obs_frontend_event event, void* privateData); + static void OnTransitionBegin(void* param, calldata_t* data); static void OnSourceCreate(void* param, calldata_t* data); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 29bcc8b4..4c4409f0 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -1,3 +1,4 @@ +#include "obs-websocket.h" #include "Config.h" #include "Utils.h" #include "WSEvents.h" @@ -114,7 +115,7 @@ HandlerResponse WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) { return req->SendErrorResponse("Heartbeat parameter missing"); } - auto events = WSEvents::Current(); + auto events = GetEventsSystem(); events->HeartbeatIsActive = obs_data_get_bool(req->data, "enable"); OBSDataAutoRelease response = obs_data_create(); @@ -164,18 +165,18 @@ HandlerResponse WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* /** * Get OBS stats (almost the same info as provided in OBS' stats window) - * + * * @return {OBSStats} `stats` OBS stats - * + * * @api requests * @name GetStats * @category general - * @since 4.6.0 + * @since 4.6.0 */ HandlerResponse WSRequestHandler::HandleGetStats(WSRequestHandler* req) { - OBSDataAutoRelease stats = WSEvents::Current()->GetStats(); + OBSDataAutoRelease stats = GetEventsSystem()->GetStats(); OBSDataAutoRelease response = obs_data_create(); obs_data_set_obj(response, "stats", stats); return req->SendOKResponse(response); -} \ No newline at end of file +} diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index ca517b11..bbbf4bb1 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -1,3 +1,4 @@ +#include "obs-websocket.h" #include "Utils.h" #include "WSEvents.h" @@ -20,7 +21,7 @@ * @since 0.3 */ HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) { - auto events = WSEvents::Current(); + auto events = GetEventsSystem(); OBSDataAutoRelease data = obs_data_create(); obs_data_set_bool(data, "streaming", obs_frontend_streaming_active()); diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index 615d0782..852345bc 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -38,7 +38,7 @@ void ___output_dummy_addref(obs_output_t*) {} OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") -SettingsDialog* settings_dialog; +WSEventsPtr _eventsSystem; bool obs_module_load(void) { blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION); @@ -50,26 +50,27 @@ bool obs_module_load(void) { config->MigrateFromGlobalSettings(); // TODO remove this on the next minor jump config->Load(); - WSEvents::ResetCurrent(WSServer::Current()); + _eventsSystem = WSEventsPtr(new WSEvents(WSServer::Current())); if (config->ServerEnabled) { WSServer::Current()->start(config->ServerPort); } // UI setup - QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction( - obs_module_text("OBSWebsocket.Menu.SettingsItem") - ); - obs_frontend_push_ui_translation(obs_module_get_string); - QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window(); - settings_dialog = new SettingsDialog(main_window); + QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window(); + SettingsDialog* settingsDialog = new SettingsDialog(mainWindow); obs_frontend_pop_ui_translation(); - auto menu_cb = [] { - settings_dialog->ToggleShowHide(); - }; - menu_action->connect(menu_action, &QAction::triggered, menu_cb); + const char* menuActionText = + obs_module_text("OBSWebsocket.Menu.SettingsItem"); + QAction* menuAction = + (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText); + QObject::connect(menuAction, &QAction::triggered, [settingsDialog] { + // The settings dialog belongs to the main window. Should be ok + // to pass the pointer to this QAction belonging to the main window + settingsDialog->ToggleShowHide(); + }); // Loading finished blog(LOG_INFO, "module loaded!"); @@ -82,3 +83,6 @@ void obs_module_unload() { blog(LOG_INFO, "goodbye!"); } +WSEventsPtr GetEventsSystem() { + return _eventsSystem; +} diff --git a/src/obs-websocket.h b/src/obs-websocket.h index 08aed786..692426f6 100644 --- a/src/obs-websocket.h +++ b/src/obs-websocket.h @@ -19,6 +19,7 @@ with this program. If not, see #pragma once #include +#include void ___source_dummy_addref(obs_source_t*); void ___sceneitem_dummy_addref(obs_sceneitem_t*); @@ -37,6 +38,11 @@ using OBSDataArrayAutoRelease = using OBSOutputAutoRelease = OBSRef; +class WSEvents; +typedef std::shared_ptr WSEventsPtr; + +std::shared_ptr GetEventsSystem(); + #define OBS_WEBSOCKET_VERSION "4.6.0" #define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__)