From 0f9c8151d5b42b243a499bb31a1e1f0b2e8c5f6f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 8 Feb 2015 07:02:59 +0000 Subject: Server-side copy paste with LV2 state support. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5541 a436a847-0d15-0410-975c-d299462d15a1 --- src/server/events/Copy.cpp | 130 ++++++++++++++++++++++++++++++++++++ src/server/events/Copy.hpp | 73 ++++++++++++++++++++ src/server/events/CreateBlock.cpp | 93 ++++++++++++++------------ src/server/events/CreateBlock.hpp | 19 ++---- src/server/events/CreateGraph.cpp | 47 +++++++++---- src/server/events/CreateGraph.hpp | 16 +++-- src/server/events/CreatePort.cpp | 30 +++------ src/server/events/Delta.cpp | 67 ++++++++++--------- src/server/events/DisconnectAll.cpp | 3 +- src/server/events/Get.cpp | 20 ++++-- src/server/events/Get.hpp | 15 ++++- 11 files changed, 379 insertions(+), 134 deletions(-) create mode 100644 src/server/events/Copy.cpp create mode 100644 src/server/events/Copy.hpp (limited to 'src/server/events') diff --git a/src/server/events/Copy.cpp b/src/server/events/Copy.cpp new file mode 100644 index 00000000..67ab4747 --- /dev/null +++ b/src/server/events/Copy.cpp @@ -0,0 +1,130 @@ +/* + This file is part of Ingen. + Copyright 2007-2015 David Robillard + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see . +*/ + +#include "ingen/Store.hpp" +#include "raul/Path.hpp" + +#include "BlockImpl.hpp" +#include "Broadcaster.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "EnginePort.hpp" +#include "GraphImpl.hpp" +#include "events/Copy.hpp" + +namespace Ingen { +namespace Server { +namespace Events { + +Copy::Copy(Engine& engine, + SPtr client, + int32_t id, + SampleCount timestamp, + const Raul::Path& old_path, + const Raul::URI& new_uri) + : Event(engine, client, id, timestamp) + , _old_path(old_path) + , _new_uri(new_uri) + , _parent(NULL) + , _block(NULL) + , _compiled_graph(NULL) +{} + +bool +Copy::pre_process() +{ + if (_old_path.empty() || + !Node::uri_is_path(_new_uri) || + _new_uri == Node::root_uri()) { + return Event::pre_process_done(Status::BAD_REQUEST); + } + + // Only support a single source for now + const Raul::Path new_path = Node::uri_to_path(_new_uri); + if (!Raul::Symbol::is_valid(new_path.symbol())) { + return Event::pre_process_done(Status::BAD_REQUEST); + } + + std::unique_lock lock(_engine.store()->mutex()); + + // Find the old node + const Store::iterator i = _engine.store()->find(_old_path); + if (i == _engine.store()->end()) { + return Event::pre_process_done(Status::NOT_FOUND, _old_path); + } + + // Ensure the new node doesn't already exists + if (_engine.store()->find(new_path) != _engine.store()->end()) { + return Event::pre_process_done(Status::EXISTS, new_path); + } + + // Get old node block, or fail (ports not supported for now) + BlockImpl* old_block = dynamic_cast(i->second.get()); + if (!old_block) { + return Event::pre_process_done(Status::BAD_OBJECT_TYPE, _old_path); + } + + // Find new parent graph + const Raul::Path parent_path = new_path.parent(); + const Store::iterator p = _engine.store()->find(parent_path); + if (p == _engine.store()->end()) { + return Event::pre_process_done(Status::NOT_FOUND, parent_path); + } + if (!(_parent = dynamic_cast(p->second.get()))) { + return Event::pre_process_done(Status::BAD_OBJECT_TYPE, parent_path); + } + + // Create new block + if (!(_block = dynamic_cast( + old_block->duplicate(_engine, Raul::Symbol(new_path.symbol()), _parent)))) { + return Event::pre_process_done(Status::INTERNAL_ERROR); + } + + _block->activate(*_engine.buffer_factory()); + + // Add block to the store and the graph's pre-processor only block list + _parent->add_block(*_block); + _engine.store()->add(_block); + + // Compile graph with new block added for insertion in audio thread + if (_parent->enabled()) { + _compiled_graph = _parent->compile(); + } + + return Event::pre_process_done(Status::SUCCESS); +} + +void +Copy::execute(ProcessContext& context) +{ + if (_block) { + _parent->set_compiled_graph(_compiled_graph); + _compiled_graph = NULL; // Graph takes ownership + } +} + +void +Copy::post_process() +{ + Broadcaster::Transfer t(*_engine.broadcaster()); + if (respond() == Status::SUCCESS) { + _engine.broadcaster()->copy(_old_path, _new_uri); + } +} + +} // namespace Events +} // namespace Server +} // namespace Ingen diff --git a/src/server/events/Copy.hpp b/src/server/events/Copy.hpp new file mode 100644 index 00000000..26c0c815 --- /dev/null +++ b/src/server/events/Copy.hpp @@ -0,0 +1,73 @@ +/* + This file is part of Ingen. + Copyright 2007-2015 David Robillard + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see . +*/ + +#ifndef INGEN_EVENTS_COPY_HPP +#define INGEN_EVENTS_COPY_HPP + +#include + +#include "ingen/Store.hpp" +#include "raul/Path.hpp" + +#include "Event.hpp" + +namespace Ingen { +namespace Server { + +class BlockImpl; +class CompiledGraph; +class GraphImpl; + +namespace Events { + +/** \page methods + *

COPY

+ * As per WebDAV (RFC4918 S9.8). + * + * Copy an object from its current location and insert it at a new location + * in a single operation. + */ + +/** COPY a graph object to a new path (see \ref methods). + * \ingroup engine + */ +class Copy : public Event +{ +public: + Copy(Engine& engine, + SPtr client, + int32_t id, + SampleCount timestamp, + const Raul::Path& old_path, + const Raul::URI& new_uri); + + bool pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const Raul::Path _old_path; + const Raul::URI _new_uri; + GraphImpl* _parent; + BlockImpl* _block; + CompiledGraph* _compiled_graph; +}; + +} // namespace Events +} // namespace Server +} // namespace Ingen + +#endif // INGEN_EVENTS_COPY_HPP diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp index 1e1e2228..e14a3356 100644 --- a/src/server/events/CreateBlock.cpp +++ b/src/server/events/CreateBlock.cpp @@ -23,6 +23,7 @@ #include "BlockImpl.hpp" #include "Broadcaster.hpp" #include "CreateBlock.hpp" +#include "Driver.hpp" #include "Engine.hpp" #include "GraphImpl.hpp" #include "PluginImpl.hpp" @@ -54,58 +55,72 @@ CreateBlock::~CreateBlock() bool CreateBlock::pre_process() { - Ingen::URIs& uris = _engine.world()->uris(); - typedef Resource::Properties::const_iterator iterator; + const Ingen::URIs& uris = _engine.world()->uris(); + const SPtr store = _engine.store(); + + // Check sanity of target path if (_path.is_root()) { return Event::pre_process_done(Status::BAD_URI, _path); + } else if (store->get(_path)) { + return Event::pre_process_done(Status::EXISTS, _path); + } else if (!(_graph = dynamic_cast(store->get(_path.parent())))) { + return Event::pre_process_done(Status::PARENT_NOT_FOUND, _path.parent()); } - std::string plugin_uri_str; + // Get prototype URI const iterator t = _properties.find(uris.ingen_prototype); - if (t != _properties.end() && t->second.type() == uris.forge.URI) { - plugin_uri_str = t->second.ptr(); - } else { + if (t == _properties.end() || t->second.type() != uris.forge.URI) { return Event::pre_process_done(Status::BAD_REQUEST); } - if (_engine.store()->get(_path)) { - return Event::pre_process_done(Status::EXISTS, _path); - } - - _graph = dynamic_cast(_engine.store()->get(_path.parent())); - if (!_graph) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, _path.parent()); - } - - const Raul::URI plugin_uri(plugin_uri_str); - PluginImpl* plugin = _engine.block_factory()->plugin(plugin_uri); - if (!plugin) { - return Event::pre_process_done(Status::PLUGIN_NOT_FOUND, - Raul::URI(plugin_uri)); - } + const Raul::URI prototype(t->second.ptr()); + + // Find polyphony + const iterator p = _properties.find(uris.ingen_polyphonic); + const bool polyphonic = (p != _properties.end() && + p->second.type() == uris.forge.Bool && + p->second.get()); + + // Find and instantiate/duplicate prototype (plugin/existing node) + if (Node::uri_is_path(prototype)) { + // Prototype is an existing block + BlockImpl* const ancestor = dynamic_cast( + store->get(Node::uri_to_path(prototype))); + if (!ancestor) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } else if (!(_block = ancestor->duplicate( + _engine, Raul::Symbol(_path.symbol()), _graph))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } - const iterator p = _properties.find(uris.ingen_polyphonic); - const bool polyphonic = ( - p != _properties.end() && - p->second.type() == _engine.world()->forge().Bool && - p->second.get()); - - if (!(_block = plugin->instantiate(*_engine.buffer_factory(), - Raul::Symbol(_path.symbol()), - polyphonic, - _graph, - _engine))) { - return Event::pre_process_done(Status::CREATION_FAILED, _path); + /* Replace prototype with the ancestor's. This is less informative, + but the client expects an actual LV2 plugin as prototype. */ + _properties.erase(uris.ingen_prototype); + _properties.insert(std::make_pair(uris.ingen_prototype, + uris.forge.alloc_uri(ancestor->plugin()->uri()))); + } else { + // Prototype is a plugin + PluginImpl* const plugin = _engine.block_factory()->plugin(prototype); + if (!plugin) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } else if (!(_block = plugin->instantiate(*_engine.buffer_factory(), + Raul::Symbol(_path.symbol()), + polyphonic, + _graph, + _engine))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } } + // Activate block _block->properties().insert(_properties.begin(), _properties.end()); _block->activate(*_engine.buffer_factory()); // Add block to the store and the graph's pre-processor only block list _graph->add_block(*_block); - _engine.store()->add(_block); + store->add(_block); /* Compile graph with new block added for insertion in audio thread TODO: Since the block is not connected at this point, a full compilation @@ -114,11 +129,7 @@ CreateBlock::pre_process() _compiled_graph = _graph->compile(); } - _update.push_back(make_pair(_block->uri(), _block->properties())); - for (uint32_t i = 0; i < _block->num_ports(); ++i) { - const PortImpl* port = _block->port_impl(i); - _update.push_back(std::make_pair(port->uri(), port->properties())); - } + _update.put_block(_block); return Event::pre_process_done(Status::SUCCESS); } @@ -137,9 +148,7 @@ CreateBlock::post_process() { Broadcaster::Transfer t(*_engine.broadcaster()); if (respond() == Status::SUCCESS) { - for (const auto& u : _update) { - _engine.broadcaster()->put(u.first, u.second); - } + _update.send(_engine.broadcaster()); } } diff --git a/src/server/events/CreateBlock.hpp b/src/server/events/CreateBlock.hpp index 36e35775..6e0d7f4d 100644 --- a/src/server/events/CreateBlock.hpp +++ b/src/server/events/CreateBlock.hpp @@ -17,12 +17,10 @@ #ifndef INGEN_EVENTS_CREATEBLOCK_HPP #define INGEN_EVENTS_CREATEBLOCK_HPP -#include -#include - #include "ingen/Resource.hpp" #include "Event.hpp" +#include "events/Get.hpp" namespace Ingen { namespace Server { @@ -54,15 +52,12 @@ public: void post_process(); private: - /// Update put message to broadcast to clients - typedef std::list< std::pair > Update; - - Raul::Path _path; - Resource::Properties _properties; - Update _update; - GraphImpl* _graph; - BlockImpl* _block; - CompiledGraph* _compiled_graph; + Raul::Path _path; + Resource::Properties _properties; + Events::Get::Response _update; + GraphImpl* _graph; + BlockImpl* _block; + CompiledGraph* _compiled_graph; }; } // namespace Events diff --git a/src/server/events/CreateGraph.cpp b/src/server/events/CreateGraph.cpp index 0355b0bb..06445a6b 100644 --- a/src/server/events/CreateGraph.cpp +++ b/src/server/events/CreateGraph.cpp @@ -41,8 +41,7 @@ CreateGraph::CreateGraph(Engine& engine, , _graph(NULL) , _parent(NULL) , _compiled_graph(NULL) -{ -} +{} bool CreateGraph::pre_process() @@ -76,13 +75,33 @@ CreateGraph::pre_process() } const Raul::Symbol symbol((_path.is_root()) ? "root" : _path.symbol()); - _graph = new GraphImpl(_engine, symbol, ext_poly, _parent, - _engine.driver()->sample_rate(), int_poly); - _graph->properties().insert(_properties.begin(), _properties.end()); - _graph->add_property(uris.rdf_type, uris.ingen_Graph); - _graph->add_property(uris.rdf_type, - Resource::Property(uris.ingen_Block, - Resource::Graph::EXTERNAL)); + + // Create graph based on prototype + const iterator t = _properties.find(uris.ingen_prototype); + if (t != _properties.end() && + Raul::URI::is_valid(t->second.ptr()) && + Node::uri_is_path(Raul::URI(t->second.ptr()))) { + // Create a duplicate of an existing graph + const Raul::URI prototype(t->second.ptr()); + GraphImpl* ancestor = dynamic_cast( + _engine.store()->get(Node::uri_to_path(prototype))); + if (!ancestor) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } else if (!(_graph = dynamic_cast( + ancestor->duplicate(_engine, symbol, _parent)))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } + } else { + // Create a new graph + _graph = new GraphImpl(_engine, symbol, ext_poly, _parent, + _engine.driver()->sample_rate(), int_poly); + _graph->add_property(uris.rdf_type, uris.ingen_Graph); + _graph->add_property(uris.rdf_type, + Resource::Property(uris.ingen_Block, + Resource::Graph::EXTERNAL)); + } + + _graph->set_properties(_properties); _parent->add_block(*_graph); if (_parent->enabled()) { @@ -92,10 +111,12 @@ CreateGraph::pre_process() _graph->activate(*_engine.buffer_factory()); - // Insert into Store + // Insert into store and build update to send to clients _engine.store()->add(_graph); - - _update = _graph->properties(); + _update.put_graph(_graph); + for (BlockImpl& block : _graph->blocks()) { + _engine.store()->add(&block); + } return Event::pre_process_done(Status::SUCCESS); } @@ -113,7 +134,7 @@ CreateGraph::post_process() { Broadcaster::Transfer t(*_engine.broadcaster()); if (respond() == Status::SUCCESS) { - _engine.broadcaster()->put(Node::path_to_uri(_path), _update); + _update.send(_engine.broadcaster()); } } diff --git a/src/server/events/CreateGraph.hpp b/src/server/events/CreateGraph.hpp index 64fb92bd..542a2921 100644 --- a/src/server/events/CreateGraph.hpp +++ b/src/server/events/CreateGraph.hpp @@ -17,9 +17,11 @@ #ifndef INGEN_EVENTS_CREATEGRAPH_HPP #define INGEN_EVENTS_CREATEGRAPH_HPP -#include "Event.hpp" #include "ingen/Resource.hpp" +#include "Event.hpp" +#include "events/Get.hpp" + namespace Ingen { namespace Server { @@ -47,12 +49,12 @@ public: void post_process(); private: - const Raul::Path _path; - Resource::Properties _properties; - Resource::Properties _update; - GraphImpl* _graph; - GraphImpl* _parent; - CompiledGraph* _compiled_graph; + const Raul::Path _path; + Resource::Properties _properties; + Events::Get::Response _update; + GraphImpl* _graph; + GraphImpl* _parent; + CompiledGraph* _compiled_graph; }; } // namespace Events diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp index 2a1a7994..21602df6 100644 --- a/src/server/events/CreatePort.cpp +++ b/src/server/events/CreatePort.cpp @@ -87,31 +87,23 @@ CreatePort::pre_process() { if (_port_type == PortType::UNKNOWN) { return Event::pre_process_done(Status::UNKNOWN_TYPE, _path); - } - - if (_path.is_root()) { + } else if (_path.is_root()) { return Event::pre_process_done(Status::BAD_URI, _path); - } - - if (_engine.store()->get(_path)) { + } else if (_engine.store()->get(_path)) { return Event::pre_process_done(_status, _path); } - Node* parent = _engine.store()->get(_path.parent()); + const Raul::Path parent_path = _path.parent(); + Node* const parent = _engine.store()->get(parent_path); if (!parent) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, - _path.parent()); + return Event::pre_process_done(Status::PARENT_NOT_FOUND, parent_path); + } else if (!(_graph = dynamic_cast(parent))) { + return Event::pre_process_done(Status::INVALID_PARENT, parent_path); } - if (!(_graph = dynamic_cast(parent))) { - return Event::pre_process_done(Status::INVALID_PARENT_PATH, - _path.parent()); - } - - const URIs& uris = _engine.world()->uris(); - const BufferFactory& buffer_factory = *_engine.buffer_factory(); - - const uint32_t buf_size = buffer_factory.default_size(_buf_type); + const URIs& uris = _engine.world()->uris(); + BufferFactory& bufs = *_engine.buffer_factory(); + const uint32_t buf_size = bufs.default_size(_buf_type); const int32_t old_n_ports = _graph->num_ports_non_rt(); typedef Resource::Properties::const_iterator PropIter; @@ -133,7 +125,7 @@ CreatePort::pre_process() poly_i->second.get()); if (!(_graph_port = _graph->create_port( - *_engine.buffer_factory(), Raul::Symbol(_path.symbol()), + bufs, Raul::Symbol(_path.symbol()), _port_type, _buf_type, buf_size, _is_output, polyphonic))) { return Event::pre_process_done(Status::CREATION_FAILED, _path); } diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 23285b9b..02f53410 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -135,9 +135,11 @@ Delta::pre_process() path, is_output, _properties); } if (_create_event) { - _create_event->pre_process(); - // Grab the object for applying properties, if the create-event succeeded - _object = _engine.store()->get(path); + if (_create_event->pre_process()) { + _object = _engine.store()->get(path); // Get object for setting + } else { + return Event::pre_process_done(Status::CREATION_FAILED, _subject); + } } else { return Event::pre_process_done(Status::BAD_OBJECT_TYPE, _subject); } @@ -382,36 +384,39 @@ Delta::post_process() Broadcaster::Transfer t(*_engine.broadcaster()); - for (auto& s : _set_events) - s->post_process(); + if (_create_event) { + _create_event->post_process(); + if (_create_event->status() != Status::SUCCESS) { + return; // Creation failed, nothing else to do + } + } - if (_status == Status::SUCCESS) { - if (_create_event) { - _create_event->post_process(); - } else { - respond(); - switch (_type) { - case Type::SET: - /* Kludge to avoid feedback for set events only. The GUI - depends on put responses to e.g. initially place blocks. - Some more sensible way of controlling this is needed. */ - _engine.broadcaster()->set_ignore_client(_request_client); - _engine.broadcaster()->set_property( - _subject, - (*_properties.begin()).first, - (*_properties.begin()).second); - _engine.broadcaster()->clear_ignore_client(); - break; - case Type::PUT: - _engine.broadcaster()->put(_subject, _properties, _context); - break; - case Type::PATCH: - _engine.broadcaster()->delta(_subject, _remove, _properties); - break; - } + for (auto& s : _set_events) { + if (s->status() != Status::SUCCESS) { + s->post_process(); // Set failed, report error + } + } + + if (respond() == Status::SUCCESS) { + switch (_type) { + case Type::SET: + /* Kludge to avoid feedback for set events only. The GUI + depends on put responses to e.g. initially place blocks. + Some more sensible way of controlling this is needed. */ + _engine.broadcaster()->set_ignore_client(_request_client); + _engine.broadcaster()->set_property( + _subject, + (*_properties.begin()).first, + (*_properties.begin()).second); + _engine.broadcaster()->clear_ignore_client(); + break; + case Type::PUT: + _engine.broadcaster()->put(_subject, _properties, _context); + break; + case Type::PATCH: + _engine.broadcaster()->delta(_subject, _remove, _properties); + break; } - } else { - respond(); } } diff --git a/src/server/events/DisconnectAll.cpp b/src/server/events/DisconnectAll.cpp index 22560c7e..262bfea8 100644 --- a/src/server/events/DisconnectAll.cpp +++ b/src/server/events/DisconnectAll.cpp @@ -100,8 +100,7 @@ DisconnectAll::pre_process() if (object->parent_graph() != _parent && object->parent()->parent_graph() != _parent) { - return Event::pre_process_done(Status::INVALID_PARENT_PATH, - _parent_path); + return Event::pre_process_done(Status::INVALID_PARENT, _parent_path); } // Only one of these will succeed diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp index c984e576..8a9eb227 100644 --- a/src/server/events/Get.cpp +++ b/src/server/events/Get.cpp @@ -100,6 +100,19 @@ Get::Response::put_graph(const GraphImpl* graph) } } +void +Get::Response::send(Interface* dest) +{ + // Sort puts by URI so parents are sent first + std::sort(puts.begin(), puts.end()); + for (const Response::Put& put : puts) { + dest->put(put.uri, put.properties, put.ctx); + } + for (const Response::Connect& connect : connects) { + dest->connect(connect.tail, connect.head); + } +} + Get::Get(Engine& engine, SPtr client, int32_t id, @@ -162,12 +175,7 @@ Get::post_process() uris.param_sampleRate, uris.forge.make(int32_t(_engine.driver()->sample_rate()))); } else { - for (const Response::Put& put : _response.puts) { - _request_client->put(put.uri, put.properties, put.ctx); - } - for (const Response::Connect& connect : _response.connects) { - _request_client->connect(connect.tail, connect.head); - } + _response.send(_request_client.get()); } } } diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp index 7aea8c77..e0ed3483 100644 --- a/src/server/events/Get.hpp +++ b/src/server/events/Get.hpp @@ -17,6 +17,8 @@ #ifndef INGEN_EVENTS_GET_HPP #define INGEN_EVENTS_GET_HPP +#include + #include "Event.hpp" #include "BlockFactory.hpp" #include "types.hpp" @@ -24,7 +26,10 @@ namespace Ingen { namespace Server { +class BlockImpl; +class GraphImpl; class PluginImpl; +class PortImpl; namespace Events { @@ -45,7 +50,6 @@ public: void execute(ProcessContext& context) {} void post_process(); -private: /** A sequence of puts and connects to respond to client with. * This is constructed in the pre_process() and later sent in * post_process() to avoid the need to lock. @@ -63,11 +67,17 @@ private: void put_port(const PortImpl* port); void put_block(const BlockImpl* block); void put_graph(const GraphImpl* graph); - + + void send(Interface* dest); + struct Put { Raul::URI uri; Resource::Properties properties; Resource::Graph ctx; + + inline bool operator<(const Put& other) { + return uri < other.uri; + } }; struct Connect { @@ -79,6 +89,7 @@ private: std::vector connects; }; +private: const Raul::URI _uri; const Node* _object; const PluginImpl* _plugin; -- cgit v1.2.1