From 93bef47053c97c525998025a35bc2156b7d2a974 Mon Sep 17 00:00:00 2001 From: sebaszm Date: Tue, 24 Mar 2026 20:38:49 +0100 Subject: [PATCH 1/6] Adjust code to work with upgraded tools --- Source/com/ICOM.h | 3 ++- Source/com/IUnknown.h | 4 ++-- Source/core/Services.h | 5 +++++ Source/plugins/CMakeLists.txt | 10 +++++----- Source/plugins/JSONRPC.h | 17 +++++++++++++---- Source/plugins/Module.h | 1 + 6 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Source/com/ICOM.h b/Source/com/ICOM.h index d9312e6462..ad3f81cd51 100644 --- a/Source/com/ICOM.h +++ b/Source/com/ICOM.h @@ -22,7 +22,8 @@ #include "Ids.h" #include "IIteratorType.h" -// @stubgen:include "IIteratorType.h" +// @insert "IIteratorType.h" +// @insert "Ids.h" namespace WPEFramework { namespace RPC { diff --git a/Source/com/IUnknown.h b/Source/com/IUnknown.h index cd7361bb32..3a654dfe19 100644 --- a/Source/com/IUnknown.h +++ b/Source/com/IUnknown.h @@ -345,7 +345,7 @@ namespace ProxyStub { return (result); } - inline uint32_t Complete(const Core::instance_id& impl, const uint32_t id, const RPC::Data::Output::mode how) + inline uint32_t Complete(const Core::instance_id& impl, const uint32_t id, const RPC::Data::Output::mode how) const { // This method is called from the stubs. uint32_t result = Core::ERROR_NONE; @@ -498,7 +498,7 @@ namespace ProxyStub { { return (_unknown.Interface(implementation, id)); } - uint32_t Complete(const Core::instance_id& instance, const uint32_t id, const RPC::Data::Output::mode how) + uint32_t Complete(const Core::instance_id& instance, const uint32_t id, const RPC::Data::Output::mode how) const { return (_unknown.Complete(instance, id, how)); } diff --git a/Source/core/Services.h b/Source/core/Services.h index 36a8eabec1..9faa401860 100644 --- a/Source/core/Services.h +++ b/Source/core/Services.h @@ -246,6 +246,11 @@ namespace Core { mutable uint32_t _referenceCount; }; + template + using SinkType = Sink; + + template + using ServiceType = Service; // Baseclass to turn objects into services template diff --git a/Source/plugins/CMakeLists.txt b/Source/plugins/CMakeLists.txt index defdd2bccd..5dfad498a5 100644 --- a/Source/plugins/CMakeLists.txt +++ b/Source/plugins/CMakeLists.txt @@ -21,13 +21,13 @@ string(TOLOWER ${NAMESPACE} NAMESPACE_LIB) option(VIRTUALINPUT_TOOLS "Build VirtualInput tools" OFF) -ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IPlugin.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") -ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IShell.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IPlugin.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") +ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IShell.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") ProxyStubGenerator(NAMESPACE "WPEFramework::Exchange" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IController.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IControllerDeprecated.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") -ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IStateControl.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") -ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ISubSystem.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") -ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IDispatcher.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IStateControl.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") +ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ISubSystem.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") +ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IDispatcher.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") JsonGenerator(CODE NAMESPACE WPEFramework::Exchange::Controller INPUT ${CMAKE_CURRENT_SOURCE_DIR}/IController.h OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/jsonrpc" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." NO_INCLUDES LEGACY_ALT) diff --git a/Source/plugins/JSONRPC.h b/Source/plugins/JSONRPC.h index d1a46cfcbf..79e28f5052 100644 --- a/Source/plugins/JSONRPC.h +++ b/Source/plugins/JSONRPC.h @@ -58,6 +58,10 @@ namespace PluginHost { }; class EXTERNAL JSONRPC : public ILocalDispatcher, public IDispatcher::ICallback { + public: + using SendIfMethod = std::function; + using SendIfMethodIndexed = std::function; + private: class Observer { private: @@ -160,7 +164,7 @@ namespace PluginHost { } } } - void Event(JSONRPC& parent, const string event, const string& parameter, std::function&& sendifmethod) { + void Event(JSONRPC& parent, const string event, const string& parameter, SendIfMethod&& sendifmethod) { for (const Destination& entry : _designators) { if (!sendifmethod || sendifmethod(entry.second)) { parent.Notify(entry.first, entry.second + '.' + event, parameter); @@ -381,13 +385,18 @@ namespace PluginHost { { return (InternalNotify(event, _T(""))); } - template + template ::value && !std::is_convertible::value, int>::type = 0> uint32_t Notify(const string& event, const JSONOBJECT& parameters) const { string subject; parameters.ToString(subject); return (InternalNotify(event, subject)); } + template ::value || std::is_convertible::value, int>::type = 0> + uint32_t Notify(const string& event, SENDIFMETHOD method) const + { + return InternalNotify(event, _T(""), std::move(method)); + } template uint32_t Notify(const string& event, const JSONOBJECT& parameters, SENDIFMETHOD method) const { @@ -578,7 +587,7 @@ namespace PluginHost { ObserverMap::iterator index = _observers.find(eventId); if (index != _observers.end()) { - index->second.Event(*this, eventId, parameters, std::function()); + index->second.Event(*this, eventId, parameters, SendIfMethod()); } _adminLock.Unlock(); @@ -763,7 +772,7 @@ namespace PluginHost { } return (result); } - uint32_t InternalNotify(const string& event, const string& parameters, std::function&& sendifmethod = std::function()) const + uint32_t InternalNotify(const string& event, const string& parameters, SendIfMethod&& sendifmethod = SendIfMethod()) const { uint32_t result = Core::ERROR_UNKNOWN_KEY; diff --git a/Source/plugins/Module.h b/Source/plugins/Module.h index ec4e75fd31..6ed9b81e38 100644 --- a/Source/plugins/Module.h +++ b/Source/plugins/Module.h @@ -38,3 +38,4 @@ #define EXTERNAL EXTERNAL_EXPORT #endif +// @insert From 62c7efdabc47cee5b910db93017194b2fcb1e250 Mon Sep 17 00:00:00 2001 From: sebaszm Date: Thu, 30 Apr 2026 09:22:22 +0200 Subject: [PATCH 2/6] port index in notifications --- Source/plugins/JSONRPC.h | 449 ++++++++++++++++++++++++++++----------- 1 file changed, 321 insertions(+), 128 deletions(-) diff --git a/Source/plugins/JSONRPC.h b/Source/plugins/JSONRPC.h index 79e28f5052..5fbe95fc7c 100644 --- a/Source/plugins/JSONRPC.h +++ b/Source/plugins/JSONRPC.h @@ -30,7 +30,16 @@ namespace WPEFramework { namespace PluginHost { namespace { - + + static string Pack(const string& event, const string& index) + { + string packed = event; + if (index.empty() == false) { + packed += TCHAR('@') + index; + } + return packed; + } + template uint32_t InvokeOnHandler(const Core::JSONRPC::Context& context, const string& method, const string& parameters, string& response, Core::JSONRPC::Handler& handler, JSONRPCERRORASSESSORTYPE errorhandler) { @@ -65,48 +74,142 @@ namespace PluginHost { private: class Observer { private: - using Destination = std::pair; + class Destination { + public: + Destination() = delete; + Destination(uint32_t channelId, const string& designator) + : _callback(nullptr) + , _channelId(channelId) + , _designator(designator) + , _index() { + } + Destination(uint32_t channelId, const string& designator, const string& index) + : _callback(nullptr) + , _channelId(channelId) + , _designator(designator) + , _index(index) { + } + Destination(IDispatcher::ICallback* callback, const string& designator) + : _callback(callback) + , _channelId(~0) + , _designator(designator) + , _index() { + if (_callback != nullptr) { + _callback->AddRef(); + } + } + Destination(IDispatcher::ICallback* callback, const string& designator, const string& index) + : _callback(callback) + , _channelId(~0) + , _designator(designator) + , _index(index) { + if (_callback != nullptr) { + _callback->AddRef(); + } + } + Destination(Destination&& move) noexcept + : _callback(move._callback) + , _channelId(move._channelId) + , _designator(std::move(move._designator)) + , _index(std::move(move._index)) { + move._callback = nullptr; + move._channelId = ~0; + } + Destination(const Destination& copy) + : _callback(copy._callback) + , _channelId(copy._channelId) + , _designator(copy._designator) + , _index(copy._index) { + if (_callback != nullptr) { + _callback->AddRef(); + } + } + ~Destination() { + if (_callback != nullptr) { + _callback->Release(); + } + } + + Destination& operator=(Destination&& move) noexcept + { + if (_callback != nullptr) { + _callback->Release(); + } + _callback = move._callback; + _channelId = move._channelId; + _designator = std::move(move._designator); + _index = std::move(move._index); + move._callback = nullptr; + move._channelId = ~0; + return (*this); + } + Destination& operator=(const Destination& copy) + { + if (_callback != nullptr) { + _callback->Release(); + } + _callback = copy._callback; + _channelId = copy._channelId; + _designator = copy._designator; + _index = copy._index; + if (_callback != nullptr) { + _callback->AddRef(); + } + return (*this); + } + + public: + inline IDispatcher::ICallback* Callback() { + return (_callback); + } + inline uint32_t ChannelId() const { + return (_channelId); + } + inline const string& Designator() const { + return (_designator); + } + inline const string& Index() const { + return (_index); + } + + private: + IDispatcher::ICallback* _callback; + uint32_t _channelId; + string _designator; + string _index; + }; + using Destinations = std::vector; - using Remotes = std::vector; public: - Observer& operator= (const Observer& copy) = delete; + Observer& operator=(Observer&&) = delete; + Observer& operator=(const Observer&) = delete; Observer() - : _callbacks() - , _designators() { + : _designators() { } Observer(Observer&& move) - : _callbacks(move._callbacks) - , _designators(move._designators) { + : _designators(std::move(move._designators)) { } Observer(const Observer& copy) - : _callbacks(copy._callbacks) - , _designators(copy._designators) { - for (IDispatcher::ICallback*& callback : _callbacks) { - callback->AddRef(); - } - } - ~Observer() { - for (IDispatcher::ICallback*& callback : _callbacks) { - callback->Release(); - } + : _designators(copy._designators) { } + ~Observer() = default; public: bool IsEmpty() const { - return ( (_designators.empty()) && (_callbacks.empty()) ); + return ( _designators.empty() ); } - uint32_t Subscribe(const uint32_t id, const string& event) { + uint32_t Subscribe(const uint32_t id, const string& designator, const string& index) { uint32_t result = Core::ERROR_NONE; - Destinations::iterator index(_designators.begin()); - while ((index != _designators.end()) && ((index->first != id) || (index->second != event))) { - index++; + Destinations::iterator it(_designators.begin()); + while ((it != _designators.end()) && ((it->ChannelId() != id) || (it->Designator() != designator) || (it->Index() != index))) { + it++; } - if (index == _designators.end()) { - _designators.emplace_back(Destination(id, event)); + if (it == _designators.end()) { + _designators.emplace_back(id, designator, index); } else { result = Core::ERROR_DUPLICATE_KEY; @@ -114,16 +217,16 @@ namespace PluginHost { return (result); } - uint32_t Unsubscribe(const uint32_t id, const string& event) { + uint32_t Unsubscribe(const uint32_t id, const string& designator, const string& index) { uint32_t result = Core::ERROR_NONE; - Destinations::iterator index(_designators.begin()); - while ((index != _designators.end()) && ((index->first != id) || (index->second != event))) { - index++; + Destinations::iterator it(_designators.begin()); + while ((it != _designators.end()) && ((it->ChannelId() != id) || (it->Designator() != designator) || (it->Index() != index))) { + it++; } - if (index != _designators.end()) { - _designators.erase(index); + if (it != _designators.end()) { + _designators.erase(it); } else { result = Core::ERROR_BAD_REQUEST; @@ -131,54 +234,106 @@ namespace PluginHost { return (result); } - void Subscribe(IDispatcher::ICallback* callback) { - Remotes::iterator index = std::find(_callbacks.begin(), _callbacks.end(), callback); + uint32_t Subscribe(IDispatcher::ICallback* callback, const string& designator, const string& index) { + uint32_t result = Core::ERROR_NONE; - ASSERT(index == _callbacks.end()); + Destinations::iterator it(_designators.begin()); + while ((it != _designators.end()) && ((it->Designator() != designator) || (it->Index() != index) || (it->Callback() == callback))) { + it++; + } - if (index == _callbacks.end()) { - callback->AddRef(); - _callbacks.emplace_back(callback); + if (it == _designators.end()) { + _designators.emplace_back(callback, designator, index); } + else { + result = Core::ERROR_DUPLICATE_KEY; + } + + return (result); } - void Unsubscribe(const IDispatcher::ICallback* callback) { - Remotes::iterator index = std::find(_callbacks.begin(), _callbacks.end(), callback); + uint32_t Unsubscribe(IDispatcher::ICallback* callback, const string& designator, const string& index) { + uint32_t result = Core::ERROR_NONE; - ASSERT(index != _callbacks.end()); + Destinations::iterator it(_designators.begin()); + while ((it != _designators.end()) && ((it->Designator() != designator) || (it->Index() != index) || (it->Callback() == callback))) { + it++; + } - if (index != _callbacks.end()) { - (*index)->Release(); - _callbacks.erase(index); + if (it != _designators.end()) { + _designators.erase(it); + } + else { + result = Core::ERROR_BAD_REQUEST; + } + + return (result); + } + void Dropped(const IDispatcher::ICallback* callback) { + Destinations::iterator index = _designators.begin(); + while (index != _designators.end()) { + if (index->Callback() == callback) { + index = _designators.erase(index); + } + else { + index++; + } } } template void Dropped(const uint32_t channelId, METHOD unregistered) { Destinations::iterator index = _designators.begin(); while (index != _designators.end()) { - if (index->first == channelId) { - unregistered(index->second); + if ( (index->ChannelId() == channelId) && (index->Callback() == nullptr) ) { + unregistered(index->Designator(), index->Index()); index = _designators.erase(index); - } + } else { index++; } } } - void Event(JSONRPC& parent, const string event, const string& parameter, SendIfMethod&& sendifmethod) { - for (const Destination& entry : _designators) { - if (!sendifmethod || sendifmethod(entry.second)) { - parent.Notify(entry.first, entry.second + '.' + event, parameter); + void Event(const JSONRPC& parent, const string event, const string& parameter, const SendIfMethod& sendifmethod) { + Destinations::iterator index(_designators.begin()); + + while (index != _designators.end()) { + Destination& entry = (*index); + + if (!sendifmethod || sendifmethod(entry.Designator())) { + if (entry.Callback() == nullptr) { + parent.Notify(entry.ChannelId(), (entry.Designator() + '.' + event), parameter); + } + else { + entry.Callback()->Event(event, parameter); + } } + + ++index; } - for (IDispatcher::ICallback*& callback : _callbacks) { - callback->Event(event, parameter); + } + void Event(const JSONRPC& parent, const string event, const string& parameter, const SendIfMethodIndexed& sendifmethod) { + Destinations::iterator index(_designators.begin()); + + while (index != _designators.end()) { + Destination& entry = (*index); + + if (!sendifmethod || sendifmethod(entry.Designator(), entry.Index())) { + if (entry.Callback() == nullptr) { + parent.Notify(entry.ChannelId(), Pack((entry.Designator() + '.' + event), entry.Index()), parameter); + } + else { + // Have to sneak in the index into event name + entry.Callback()->Event(Pack(event, entry.Index()), parameter); + } + } + + ++index; } } private: - Remotes _callbacks; Destinations _designators; }; + using HandlerList = std::list; using ObserverMap = std::unordered_map; @@ -238,16 +393,16 @@ namespace PluginHost { Registration() : Core::JSON::Container() , Event() - , Callsign() + , Id() { Add(_T("event"), &Event); - Add(_T("id"), &Callsign); + Add(_T("id"), &Id); } ~Registration() override = default; public: Core::JSON::String Event; - Core::JSON::String Callsign; + Core::JSON::String Id; }; enum state { @@ -489,7 +644,7 @@ namespace PluginHost { Core::hresult Revoke(IDispatcher::ICallback* callback) override { // See if we re using this callback, we need to abort its use.. for (std::pair& entry : _observers) { - entry.second.Unsubscribe(callback); + entry.second.Dropped(callback); } return (Core::ERROR_NONE); } @@ -502,16 +657,16 @@ namespace PluginHost { uint32_t Invoke(const uint32_t channelId, const uint32_t id, const string& token, const string& method, const string& parameters, string& response) override { uint32_t result = Core::ERROR_INCORRECT_URL; - ASSERT(Core::JSONRPC::Message::Callsign(method).empty() || (Core::JSONRPC::Message::Callsign(method) == _callsign)); + string realMethod = Core::JSONRPC::Message::Method(method); + string callsign = Core::JSONRPC::Message::Callsign(method); - // Seems we are on the right handler.. - // now see if someone supports this version - string realMethod(Core::JSONRPC::Message::Method(method)); + ASSERT((callsign.empty() == true) || (callsign == _callsign)); if (realMethod == _T("register")) { Registration info; info.FromString(parameters); + string index = Core::JSONRPC::Message::Index(method); - result = Subscribe(this, channelId, info.Event.Value(), info.Callsign.Value()); + result = Subscribe(this, channelId, info.Event.Value(), info.Id.Value(), index); if (result == Core::ERROR_NONE) { response = _T("0"); } @@ -521,8 +676,9 @@ namespace PluginHost { } else if (realMethod == _T("unregister")) { Registration info; info.FromString(parameters); + string index = Core::JSONRPC::Message::Index(method); - result = Unsubscribe(this, channelId, info.Event.Value(), info.Callsign.Value()); + result = Unsubscribe(this, channelId, info.Event.Value(), info.Id.Value(), index); if (result == Core::ERROR_NONE) { response = _T("0"); } @@ -531,7 +687,7 @@ namespace PluginHost { } } else { - result = Invoke(this, channelId, id, token, method, parameters, response); + result = Invoke(this, channelId, id, token, Core::JSONRPC::Message::FullMethod(method), parameters, response); } return (result); @@ -565,7 +721,7 @@ namespace PluginHost { while (index != _observers.end()) { const string& eventId = index->first; - index->second.Dropped(channelId, [this, channelId, &eventId](const string& designator) { ProcessUnsubscribed(channelId, eventId, designator); }); + index->second.Dropped(channelId, [this, channelId, &eventId](const string& clientId, const string& index) { ProcessUnsubscribed(channelId, eventId, clientId, index); }); if (index->second.IsEmpty() == true) { index = _observers.erase(index); @@ -618,27 +774,35 @@ namespace PluginHost { return (_service->Submit(channel, Core::ProxyType(message))); } - Core::hresult Subscribe(const uint32_t channel, const string& eventId, const string& designator) final + Core::hresult Subscribe(const uint32_t channel, const string& event, const string& clientId) final { - return ProcessSubscribe(channel, eventId, designator); + // id and index are packed into event name + const string eventId = Core::JSONRPC::Message::Method(event); + const string index = Core::JSONRPC::Message::Index(event); + + return ProcessSubscribe(channel, eventId, clientId, index); } - Core::hresult Unsubscribe(const uint32_t channel, const string& eventId, const string& designator) final + Core::hresult Unsubscribe(const uint32_t channel, const string& event, const string& clientId) final { uint32_t result = Core::ERROR_UNKNOWN_KEY; + const string eventId = Core::JSONRPC::Message::Method(event); + _adminLock.Lock(); - ObserverMap::iterator index = _observers.find(eventId); + ObserverMap::iterator it = _observers.find(eventId); - if (index != _observers.end()) { - result = index->second.Unsubscribe(channel, designator); + if (it != _observers.end()) { + const string index = Core::JSONRPC::Message::Index(event); + + result = it->second.Unsubscribe(channel, clientId, index); if (result == Core::ERROR_NONE) { - ProcessUnsubscribed(channel, eventId, designator); - - if (index->second.IsEmpty() == true) { - _observers.erase(index); + ProcessUnsubscribed(channel, eventId, clientId, index); + + if (it->second.IsEmpty() == true) { + _observers.erase(it); } } } @@ -648,23 +812,23 @@ namespace PluginHost { } protected: - uint32_t DoSubscribe(const uint32_t channelId, const string& eventId, const string& designator) + uint32_t DoSubscribe(const uint32_t channelId, const string& event, const string& clientId, const string& index) { _adminLock.Lock(); - ObserverMap::iterator index = _observers.find(eventId); + ObserverMap::iterator it = _observers.find(event); - if (index == _observers.end()) { - index = _observers.emplace(std::piecewise_construct, - std::forward_as_tuple(eventId), + if (it == _observers.end()) { + it = _observers.emplace(std::piecewise_construct, + std::forward_as_tuple(event), std::forward_as_tuple()) .first; } - uint32_t result = index->second.Subscribe(channelId, designator); + uint32_t result = it->second.Subscribe(channelId, clientId, index); - if ((result != Core::ERROR_NONE) && (index->second.IsEmpty() == true)) { - _observers.erase(index); + if ((result != Core::ERROR_NONE) && (it->second.IsEmpty() == true)) { + _observers.erase(it); } _adminLock.Unlock(); @@ -673,12 +837,12 @@ namespace PluginHost { } private: - virtual uint32_t ProcessSubscribe(const uint32_t channelId, const string& eventId, const string& designator) + virtual uint32_t ProcessSubscribe(const uint32_t channelId, const string& event, const string& clientId, const string& index) { - return DoSubscribe(channelId, eventId, designator); + return DoSubscribe(channelId, event, clientId, index); } - virtual void ProcessUnsubscribed(const uint32_t channelId VARIABLE_IS_NOT_USED, const string& eventId VARIABLE_IS_NOT_USED, const string& designator VARIABLE_IS_NOT_USED) + virtual void ProcessUnsubscribed(const uint32_t channelId VARIABLE_IS_NOT_USED, const string& event VARIABLE_IS_NOT_USED, const string& clientId VARIABLE_IS_NOT_USED, const string& index VARIABLE_IS_NOT_USED) { } @@ -720,27 +884,27 @@ namespace PluginHost { } private: - uint32_t Subscribe(IDispatcher::ICallback* callback, const uint32_t channelId, const string& event, const string& designator) { + uint32_t Subscribe(IDispatcher::ICallback* callback, const uint32_t channelId, const string& eventId, const string& clientId, const string& index) { uint32_t result = Core::ERROR_UNKNOWN_KEY; // This is to make sure that the actuall location (there weher the channels really end) are // aware of distributing the event. - if (callback->Subscribe(channelId, event, designator) == Core::ERROR_NONE) { + if (callback->Subscribe(channelId, Pack(eventId, index), clientId) == Core::ERROR_NONE) { if (callback != this) { // Oops the real location is somewhere else. Register this event also for callbacks // to be forwarded to that actual location _adminLock.Lock(); - ObserverMap::iterator index = _observers.find(event); + ObserverMap::iterator it = _observers.find(eventId); - if (index == _observers.end()) { - index = _observers.emplace(std::piecewise_construct, - std::forward_as_tuple(event), + if (it == _observers.end()) { + it = _observers.emplace(std::piecewise_construct, + std::forward_as_tuple(eventId), std::forward_as_tuple()).first; } - index->second.Subscribe(callback); + it->second.Subscribe(callback, clientId, index); _adminLock.Unlock(); } @@ -748,23 +912,23 @@ namespace PluginHost { } return (result); } - uint32_t Unsubscribe(IDispatcher::ICallback* callback, const uint32_t channelId, const string& event, const string& designator) { + uint32_t Unsubscribe(IDispatcher::ICallback* callback, const uint32_t channelId, const string& eventId, const string& clientId, const string& index) { uint32_t result = Core::ERROR_UNKNOWN_KEY; - if (callback->Unsubscribe(channelId, event, designator) == Core::ERROR_NONE) { + if (callback->Unsubscribe(channelId, Pack(eventId, index), clientId) == Core::ERROR_NONE) { if (callback != this) { // Oops the real location was somewhere else. Unregister this event also for callbacks // to be forwarded to that actual location _adminLock.Lock(); - ObserverMap::iterator index = _observers.find(event); + ObserverMap::iterator it = _observers.find(eventId); - if (index != _observers.end()) { - index->second.Unsubscribe(callback); + if (it != _observers.end()) { + it->second.Unsubscribe(callback, clientId, index); - if ((result == Core::ERROR_NONE) && (index->second.IsEmpty() == true)) { - _observers.erase(index); + if ((result == Core::ERROR_NONE) && (it->second.IsEmpty() == true)) { + _observers.erase(it); } } } @@ -772,23 +936,24 @@ namespace PluginHost { } return (result); } - uint32_t InternalNotify(const string& event, const string& parameters, SendIfMethod&& sendifmethod = SendIfMethod()) const + template + uint32_t InternalNotify(const string& event, const string& parameters, SENDIFMETHOD sendifmethod = nullptr) const { uint32_t result = Core::ERROR_UNKNOWN_KEY; _adminLock.Lock(); - ObserverMap::const_iterator index = _observers.find(event); + ObserverMap::iterator index = _observers.find(event); if (index != _observers.end()) { - const_cast(index->second).Event(const_cast(*this), event, parameters, std::move(sendifmethod)); + index->second.Event(*this, event, parameters, std::move(sendifmethod)); } _adminLock.Unlock(); return (result); } - void Notify(const uint32_t channelId, const string& designator, const string& parameters) + void Notify(const uint32_t channelId, const string& designator, const string& parameters) const { Core::ProxyType message(Core::ProxyType(IFactories::Instance().JSONRPC())); @@ -811,19 +976,40 @@ namespace PluginHost { string _callsign; TokenCheckFunction _validate; VersionList _versions; - ObserverMap _observers; + mutable ObserverMap _observers; }; - class EXTERNAL JSONRPCSupportsEventStatus : public PluginHost::JSONRPC { + class EXTERNAL JSONRPCSupportsEventStatus : virtual public PluginHost::JSONRPC { public: JSONRPCSupportsEventStatus(const JSONRPCSupportsEventStatus&) = delete; JSONRPCSupportsEventStatus& operator=(const JSONRPCSupportsEventStatus&) = delete; + JSONRPCSupportsEventStatus(JSONRPCSupportsEventStatus&&) = delete; + JSONRPCSupportsEventStatus& operator=(JSONRPCSupportsEventStatus&&) = delete; - JSONRPCSupportsEventStatus() = default; - JSONRPCSupportsEventStatus(const PluginHost::JSONRPC::TokenCheckFunction& validation) : JSONRPC(validation) {} - JSONRPCSupportsEventStatus(const std::vector& versions) : JSONRPC(versions) {} - JSONRPCSupportsEventStatus(const std::vector& versions, const TokenCheckFunction& validation) : JSONRPC(versions, validation) {} - virtual ~JSONRPCSupportsEventStatus() = default; + JSONRPCSupportsEventStatus() + : _adminLock() + , _observers() + { + } + JSONRPCSupportsEventStatus(const PluginHost::JSONRPC::TokenCheckFunction& validation) + : JSONRPC(validation) + , _adminLock() + , _observers() + { + } + JSONRPCSupportsEventStatus(const std::vector& versions) + : JSONRPC(versions) + , _adminLock() + , _observers() + { + } + JSONRPCSupportsEventStatus(const std::vector& versions, const TokenCheckFunction& validation) + : JSONRPC(versions, validation) + , _adminLock() + , _observers() + { + } + ~JSONRPCSupportsEventStatus() override = default; enum class Status { registered, @@ -831,7 +1017,7 @@ namespace PluginHost { }; public: - template + template void RegisterEventStatusListener(const string& event, METHOD method) { _adminLock.Lock(); @@ -854,48 +1040,55 @@ namespace PluginHost { _adminLock.Unlock(); } + private: - uint32_t ProcessSubscribe(const uint32_t channel, const string& eventId, const string& designator) override + uint32_t ProcessSubscribe(const uint32_t channel, const string& event, const string& clientId, const string& index) override { - const Core::hresult result = JSONRPC::DoSubscribe(channel, eventId, designator); + Core::hresult result = Core::ERROR_PRIVILIGED_REQUEST; + + _adminLock.Lock(); + + result = JSONRPC::DoSubscribe(channel, event, clientId, index); if (result == Core::ERROR_NONE) { - NotifyObservers(eventId, designator, Status::registered); + NotifyObservers(channel, event, clientId, index, Status::registered); } + _adminLock.Unlock(); + return (result); } - - void ProcessUnsubscribed(const uint32_t channel VARIABLE_IS_NOT_USED, const string& eventId, const string& designator) override + void ProcessUnsubscribed(const uint32_t channel, const string& event, const string& clientId, const string& index) override { - NotifyObservers(eventId, designator, Status::unregistered); + _adminLock.Lock(); + + NotifyObservers(channel, event, clientId, index, Status::unregistered); + + _adminLock.Unlock(); } - protected: - void NotifyObservers(const string& event, const string& client, const Status status) const + private: + void NotifyObservers(const uint32_t channel, const string event, const string& clientId, const string& index, const Status status) const { - _adminLock.Lock(); - StatusCallbackMap::const_iterator it = _observers.find(event); if (it != _observers.cend()) { - it->second(client, status); + it->second(channel, clientId, index, status); } - - _adminLock.Unlock(); } private: - using EventStatusCallback = std::function; + using EventStatusCallback = std::function; using StatusCallbackMap = std::map; mutable Core::CriticalSection _adminLock; StatusCallbackMap _observers; }; -namespace JSONRPCErrorAssessorTypes { + namespace JSONRPCErrorAssessorTypes { using FunctionCallbackType = uint32_t (*) (const Core::JSONRPC::Context&, const string&, const string&, const uint32_t errorcode, string&); using StdFunctionCallbackType = std::function; -} + } + template class EXTERNAL JSONRPCErrorAssessor : public JSONRPC { public: From db2bcf87aa045b26fd2032e854e9e795a6345ada Mon Sep 17 00:00:00 2001 From: sebaszm Date: Tue, 5 May 2026 14:11:11 +0200 Subject: [PATCH 3/6] bw compatibility for old status listener code --- Source/plugins/JSONRPC.h | 45 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/Source/plugins/JSONRPC.h b/Source/plugins/JSONRPC.h index 5fbe95fc7c..97697b54c9 100644 --- a/Source/plugins/JSONRPC.h +++ b/Source/plugins/JSONRPC.h @@ -989,24 +989,28 @@ namespace PluginHost { JSONRPCSupportsEventStatus() : _adminLock() , _observers() + , _indexedObservers() { } JSONRPCSupportsEventStatus(const PluginHost::JSONRPC::TokenCheckFunction& validation) : JSONRPC(validation) , _adminLock() , _observers() + , _indexedObservers() { } JSONRPCSupportsEventStatus(const std::vector& versions) : JSONRPC(versions) , _adminLock() , _observers() + , _indexedObservers() { } JSONRPCSupportsEventStatus(const std::vector& versions, const TokenCheckFunction& validation) : JSONRPC(versions, validation) , _adminLock() , _observers() + , _indexedObservers() { } ~JSONRPCSupportsEventStatus() override = default; @@ -1017,14 +1021,32 @@ namespace PluginHost { }; public: - template - void RegisterEventStatusListener(const string& event, METHOD method) + using EventStatusCallback = std::function; + using IndexedEventStatusCallback = std::function; + + public: + void RegisterEventStatusListener(const string& event, EventStatusCallback method) { + // keep non-indexed register for backward compatibility + _adminLock.Lock(); ASSERT(_observers.find(event) == _observers.end()); + ASSERT(_indexedObservers.find(event) == _indexedObservers.end()); - _observers[event] = method; + _observers[event] = std::move(method); + + _adminLock.Unlock(); + } + + void RegisterEventStatusListener(const string& event, IndexedEventStatusCallback method) + { + _adminLock.Lock(); + + ASSERT(_observers.find(event) == _observers.end()); + ASSERT(_indexedObservers.find(event) == _indexedObservers.end()); + + _indexedObservers[event] = std::move(method); _adminLock.Unlock(); } @@ -1033,14 +1055,14 @@ namespace PluginHost { { _adminLock.Lock(); - ASSERT(_observers.find(event) != _observers.end()); + ASSERT((_observers.find(event) != _observers.end()) || (_indexedObservers.find(event) != _indexedObservers.end())); _observers.erase(event); + _indexedObservers.erase(event); _adminLock.Unlock(); } - private: uint32_t ProcessSubscribe(const uint32_t channel, const string& event, const string& clientId, const string& index) override { @@ -1070,18 +1092,25 @@ namespace PluginHost { private: void NotifyObservers(const uint32_t channel, const string event, const string& clientId, const string& index, const Status status) const { - StatusCallbackMap::const_iterator it = _observers.find(event); - if (it != _observers.cend()) { + // called interlocked + IndexedStatusCallbackMap::const_iterator it = _indexedObservers.find(event); + if (it != _indexedObservers.cend()) { it->second(channel, clientId, index, status); } + + StatusCallbackMap::const_iterator it2 = _observers.find(event); + if (it2 != _observers.cend()) { + it2->second(clientId, status); + } } private: - using EventStatusCallback = std::function; using StatusCallbackMap = std::map; + using IndexedStatusCallbackMap = std::map; mutable Core::CriticalSection _adminLock; StatusCallbackMap _observers; + IndexedStatusCallbackMap _indexedObservers; }; namespace JSONRPCErrorAssessorTypes { From c93b88982674f426500dad09eb179f27e5c0c9c5 Mon Sep 17 00:00:00 2001 From: sebaszm Date: Wed, 6 May 2026 17:52:12 +0200 Subject: [PATCH 4/6] Status has optional index! --- Source/plugins/IController.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/plugins/IController.h b/Source/plugins/IController.h index 58c0ce3ca1..d92d2efef8 100644 --- a/Source/plugins/IController.h +++ b/Source/plugins/IController.h @@ -58,7 +58,7 @@ namespace Controller { // @brief Starts the network discovery. Use this method to initiate SSDP network discovery process. // @param TTL (time to live) parameter for SSDP discovery - virtual Core::hresult StartDiscovery(const uint8_t& ttl) = 0; + virtual Core::hresult StartDiscovery(const uint8_t& ttl /* @optional @default:1 */) = 0; // @property // @brief Provides SSDP network discovery results. @@ -125,7 +125,7 @@ namespace Controller { virtual Core::hresult Proxies(string& response /* @out @opaque */) const = 0; // @property // @brief Provides status of a plugin, including their configurations - virtual Core::hresult Status(const string& index /* @index */, string& response /* @out @opaque */) const = 0; + virtual Core::hresult Status(const string& index /* @index @optional */, string& response /* @out @opaque */) const = 0; // @property // @brief Provides active connection details virtual Core::hresult Links(string& response /* @out @opaque */) const = 0; From e3f26ed87722ad408da1f9fa3f90aa71bb6fa60a Mon Sep 17 00:00:00 2001 From: sebaszm Date: Thu, 14 May 2026 11:45:26 +0200 Subject: [PATCH 5/6] COM add Channel accessor --- Source/com/IUnknown.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/com/IUnknown.h b/Source/com/IUnknown.h index 50f7064bb6..2a96e49303 100644 --- a/Source/com/IUnknown.h +++ b/Source/com/IUnknown.h @@ -317,6 +317,10 @@ namespace ProxyStub { { return (_implementation); } + // Required by proxystubs! + const Core::ProxyType& Channel() const { + return (_channel); + } // ------------------------------------------------------------------------------------------------------------------------------- // Proxy environment calls From eca6162292648b81d487446f1db5646947d0ee51 Mon Sep 17 00:00:00 2001 From: sebaszm Date: Thu, 14 May 2026 14:23:01 +0200 Subject: [PATCH 6/6] COM add unknonw accessor --- Source/com/IUnknown.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/com/IUnknown.h b/Source/com/IUnknown.h index 2a96e49303..147dc34fb6 100644 --- a/Source/com/IUnknown.h +++ b/Source/com/IUnknown.h @@ -473,6 +473,10 @@ namespace ProxyStub { { return(&_unknown); } + const UnknownProxy* Administration() const + { + return(&_unknown); + } // ------------------------------------------------------------------------------------------------------------------------------- // Proxy environment calls