diff options
-rw-r--r-- | bundles/ingen.lv2/ingen.ttl | 6 | ||||
-rw-r--r-- | ingen/client/ClientStore.hpp | 1 | ||||
-rw-r--r-- | src/client/ClientStore.cpp | 6 | ||||
-rw-r--r-- | src/gui/GraphCanvas.cpp | 18 | ||||
-rw-r--r-- | src/gui/GraphCanvas.hpp | 2 | ||||
-rw-r--r-- | src/gui/PluginMenu.cpp | 16 | ||||
-rw-r--r-- | src/gui/PluginMenu.hpp | 1 | ||||
-rw-r--r-- | src/server/BlockFactory.cpp | 41 | ||||
-rw-r--r-- | src/server/BlockFactory.hpp | 7 | ||||
-rw-r--r-- | src/server/ClientUpdate.cpp | 155 | ||||
-rw-r--r-- | src/server/ClientUpdate.hpp | 81 | ||||
-rw-r--r-- | src/server/LV2Plugin.cpp | 18 | ||||
-rw-r--r-- | src/server/LV2Plugin.hpp | 7 | ||||
-rw-r--r-- | src/server/PluginImpl.hpp | 8 | ||||
-rw-r--r-- | src/server/events/CreateBlock.hpp | 4 | ||||
-rw-r--r-- | src/server/events/CreateGraph.hpp | 2 | ||||
-rw-r--r-- | src/server/events/Delta.cpp | 25 | ||||
-rw-r--r-- | src/server/events/Delta.hpp | 1 | ||||
-rw-r--r-- | src/server/events/Get.cpp | 112 | ||||
-rw-r--r-- | src/server/events/Get.hpp | 45 | ||||
-rw-r--r-- | src/server/internals/Controller.cpp | 12 | ||||
-rw-r--r-- | src/server/internals/Controller.hpp | 12 | ||||
-rw-r--r-- | src/server/wscript | 1 |
23 files changed, 395 insertions, 186 deletions
diff --git a/bundles/ingen.lv2/ingen.ttl b/bundles/ingen.lv2/ingen.ttl index 3f0e16f1..b8bf38f0 100644 --- a/bundles/ingen.lv2/ingen.ttl +++ b/bundles/ingen.lv2/ingen.ttl @@ -72,6 +72,12 @@ ingen:sprungLayout rdfs:label "sprung layout" ; rdfs:comment """Whether or not the graph has a "sprung" force-directed layout.""" . +ingen:loadedBundle + a rdf:Property , + owl:ObjectProperty ; + rdfs:label "loaded bundle" ; + rdfs:comment "Whether or not a bundle is loaded into Ingen." . + ingen:value a rdf:Property , owl:DatatypeProperty ; diff --git a/ingen/client/ClientStore.hpp b/ingen/client/ClientStore.hpp index e9fda479..9f18f16d 100644 --- a/ingen/client/ClientStore.hpp +++ b/ingen/client/ClientStore.hpp @@ -109,6 +109,7 @@ public: INGEN_SIGNAL(new_object, void, SPtr<ObjectModel>); INGEN_SIGNAL(new_plugin, void, SPtr<PluginModel>); + INGEN_SIGNAL(plugin_deleted, void, Raul::URI); private: SPtr<ObjectModel> _object(const Raul::Path& path); diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp index a0c789c4..1061fb6b 100644 --- a/src/client/ClientStore.cpp +++ b/src/client/ClientStore.cpp @@ -211,6 +211,12 @@ ClientStore::del(const Raul::URI& uri) { if (Node::uri_is_path(uri)) { remove_object(Node::uri_to_path(uri)); + } else { + Plugins::iterator p = _plugins->find(uri); + if (p != _plugins->end()) { + _plugins->erase(p); + _signal_plugin_deleted.emit(uri); + } } } diff --git a/src/gui/GraphCanvas.cpp b/src/gui/GraphCanvas.cpp index 16739a55..85ab4855 100644 --- a/src/gui/GraphCanvas.cpp +++ b/src/gui/GraphCanvas.cpp @@ -88,6 +88,7 @@ GraphCanvas::GraphCanvas(App& app, , _plugin_menu(NULL) , _human_names(true) , _show_port_names(true) + , _menu_dirty(false) { Glib::RefPtr<Gtk::Builder> xml = WidgetFactory::create("canvas_menu"); xml->get_widget("canvas_menu", _menu); @@ -157,6 +158,8 @@ GraphCanvas::GraphCanvas(App& app, _app.store()->signal_new_plugin().connect( sigc::mem_fun(this, &GraphCanvas::add_plugin)); + _app.store()->signal_plugin_deleted().connect( + sigc::mem_fun(this, &GraphCanvas::remove_plugin)); // Connect widget signals to do things _menu_load_plugin->signal_activate().connect( @@ -178,7 +181,7 @@ GraphCanvas::show_menu(bool position, unsigned button, uint32_t time) { _app.request_plugins_if_necessary(); - if (!_internal_menu) + if (!_internal_menu || _menu_dirty) build_menus(); if (position) @@ -205,7 +208,9 @@ GraphCanvas::build_menus() } // Build skeleton LV2 plugin class heirarchy for 'Plugin' menu - if (!_plugin_menu) { + if (_plugin_menu) { + _plugin_menu->clear(); + } else { _plugin_menu = Gtk::manage(new PluginMenu(*_app.world())); _menu->items().push_back( Gtk::Menu_Helpers::ImageMenuElem( @@ -223,6 +228,8 @@ GraphCanvas::build_menus() for (const auto& p : *plugins.get()) { add_plugin(p.second); } + + _menu_dirty = false; } void @@ -308,6 +315,13 @@ GraphCanvas::add_plugin(SPtr<PluginModel> p) } void +GraphCanvas::remove_plugin(const Raul::URI& uri) +{ + // Flag menus as dirty so they will be rebuilt when needed next + _menu_dirty = true; +} + +void GraphCanvas::add_block(SPtr<const BlockModel> bm) { SPtr<const GraphModel> pm = dynamic_ptr_cast<const GraphModel>(bm); diff --git a/src/gui/GraphCanvas.hpp b/src/gui/GraphCanvas.hpp index 8970a88a..b055d530 100644 --- a/src/gui/GraphCanvas.hpp +++ b/src/gui/GraphCanvas.hpp @@ -63,6 +63,7 @@ public: bool show_port_names() const { return _show_port_names; } void add_plugin(SPtr<Client::PluginModel> pm); + void remove_plugin(const Raul::URI& uri); void add_block(SPtr<const Client::BlockModel> bm); void remove_block(SPtr<const Client::BlockModel> bm); void add_port(SPtr<const Client::PortModel> pm); @@ -149,6 +150,7 @@ private: bool _human_names; bool _show_port_names; + bool _menu_dirty; }; } // namespace GUI diff --git a/src/gui/PluginMenu.cpp b/src/gui/PluginMenu.cpp index 9bc47bff..c997bf19 100644 --- a/src/gui/PluginMenu.cpp +++ b/src/gui/PluginMenu.cpp @@ -25,10 +25,22 @@ PluginMenu::PluginMenu(Ingen::World& world) : _world(world) , _classless_menu(NULL, NULL) { + clear(); +} + +void +PluginMenu::clear() +{ const LilvWorld* lworld = _world.lilv_world(); const LilvPluginClass* lv2_plugin = lilv_world_get_plugin_class(lworld); const LilvPluginClasses* classes = lilv_world_get_plugin_classes(lworld); + // Empty completely + _classless_menu = MenuRecord(NULL, NULL); + _class_menus.clear(); + items().clear(); + + // Build skeleton LV2Children children; LILV_FOREACH(plugin_classes, i, classes) { const LilvPluginClass* c = lilv_plugin_classes_get(classes, i); @@ -44,7 +56,7 @@ PluginMenu::PluginMenu(Ingen::World& world) items().push_back(Gtk::Menu_Helpers::MenuElem("_Uncategorized")); _classless_menu.item = &(items().back()); - _classless_menu.menu = new Gtk::Menu(); + _classless_menu.menu = Gtk::manage(new Gtk::Menu()); _classless_menu.item->set_submenu(*_classless_menu.menu); _classless_menu.item->hide(); } @@ -109,7 +121,7 @@ PluginMenu::build_plugin_class_menu(Gtk::Menu* menu, menu->items().push_back(menu_elem); Gtk::MenuItem* menu_item = &(menu->items().back()); - Gtk::Menu* submenu = new Gtk::Menu(); + Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); menu_item->set_submenu(*submenu); size_t num_child_items = build_plugin_class_menu( diff --git a/src/gui/PluginMenu.hpp b/src/gui/PluginMenu.hpp index c8fda171..bc654db5 100644 --- a/src/gui/PluginMenu.hpp +++ b/src/gui/PluginMenu.hpp @@ -43,6 +43,7 @@ class PluginMenu : public Gtk::Menu public: PluginMenu(Ingen::World& world); + void clear(); void add_plugin(SPtr<Client::PluginModel> p); sigc::signal< void, WPtr<Client::PluginModel> > signal_load_plugin; diff --git a/src/server/BlockFactory.cpp b/src/server/BlockFactory.cpp index 0a51e03e..b715ecb7 100644 --- a/src/server/BlockFactory.cpp +++ b/src/server/BlockFactory.cpp @@ -59,14 +59,46 @@ BlockFactory::plugins() { ThreadManager::assert_thread(THREAD_PRE_PROCESS); if (!_has_loaded) { - _has_loaded = true; - // TODO: Plugin list refreshing load_lv2_plugins(); _has_loaded = true; } return _plugins; } +std::set<PluginImpl*> +BlockFactory::refresh() +{ + // Record current plugins, and those that are currently zombies + const Plugins old_plugins(_plugins); + std::set<PluginImpl*> zombies; + for (const auto& p : _plugins) { + if (p.second->is_zombie()) { + zombies.insert(p.second); + } + } + + // Re-load plugins + load_lv2_plugins(); + + // Add any new plugins to response + std::set<PluginImpl*> new_plugins; + for (const auto& p : _plugins) { + Plugins::const_iterator o = old_plugins.find(p.first); + if (o == old_plugins.end()) { + new_plugins.insert(p.second); + } + } + + // Add any resurrected plugins to response + for (const auto& z : zombies) { + if (!z->is_zombie()) { + new_plugins.insert(z); + } + } + + return new_plugins; +} + PluginImpl* BlockFactory::plugin(const Raul::URI& uri) { @@ -182,9 +214,12 @@ BlockFactory::load_lv2_plugins() continue; } - if (_plugins.find(uri) == _plugins.end()) { + Plugins::iterator p = _plugins.find(uri); + if (p == _plugins.end()) { LV2Plugin* const plugin = new LV2Plugin(_world, lv2_plug); _plugins.insert(make_pair(uri, plugin)); + } else if (lilv_plugin_verify(lv2_plug)) { + p->second->set_is_zombie(false); } } diff --git a/src/server/BlockFactory.hpp b/src/server/BlockFactory.hpp index 776ef948..71e72bbc 100644 --- a/src/server/BlockFactory.hpp +++ b/src/server/BlockFactory.hpp @@ -18,6 +18,7 @@ #define INGEN_ENGINE_BLOCKFACTORY_HPP #include <map> +#include <set> #include "ingen/World.hpp" #include "ingen/types.hpp" @@ -39,6 +40,12 @@ public: explicit BlockFactory(Ingen::World* world); ~BlockFactory(); + /** Reload plugin list. + * + * @return The set of newly loaded plugins. + */ + std::set<PluginImpl*> refresh(); + void load_plugin(const Raul::URI& uri); typedef std::map<Raul::URI, PluginImpl*> Plugins; diff --git a/src/server/ClientUpdate.cpp b/src/server/ClientUpdate.cpp new file mode 100644 index 00000000..217d3a32 --- /dev/null +++ b/src/server/ClientUpdate.cpp @@ -0,0 +1,155 @@ +/* + This file is part of Ingen. + Copyright 2007-2015 David Robillard <http://drobilla.net/> + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "ingen/Interface.hpp" +#include "ingen/URIs.hpp" + +#include "BlockImpl.hpp" +#include "BufferFactory.hpp" +#include "ClientUpdate.hpp" +#include "GraphImpl.hpp" +#include "PortImpl.hpp" + +namespace Ingen { +namespace Server { + +void +ClientUpdate::put(const Raul::URI& uri, + const Resource::Properties& props, + Resource::Graph ctx) +{ + const ClientUpdate::Put put = { uri, props, ctx }; + puts.push_back(put); +} + +void +ClientUpdate::put_port(const PortImpl* port) +{ + const URIs& uris = port->bufs().uris(); + if (port->is_a(PortType::CONTROL) || port->is_a(PortType::CV)) { + Resource::Properties props = port->properties(); + props.erase(uris.ingen_value); + props.insert(std::make_pair(uris.ingen_value, port->value())); + put(port->uri(), props); + } else { + put(port->uri(), port->properties()); + } +} + +void +ClientUpdate::put_block(const BlockImpl* block) +{ + const PluginImpl* const plugin = block->plugin_impl(); + const URIs& uris = plugin->uris(); + + if (uris.ingen_Graph == plugin->type()) { + put_graph((const GraphImpl*)block); + } else { + put(block->uri(), block->properties()); + for (size_t j = 0; j < block->num_ports(); ++j) { + put_port(block->port_impl(j)); + } + } +} + +void +ClientUpdate::put_graph(const GraphImpl* graph) +{ + put(graph->uri(), + graph->properties(Resource::Graph::INTERNAL), + Resource::Graph::INTERNAL); + + put(graph->uri(), + graph->properties(Resource::Graph::EXTERNAL), + Resource::Graph::EXTERNAL); + + // Enqueue blocks + for (const auto& b : graph->blocks()) { + put_block(&b); + } + + // Enqueue ports + for (uint32_t i = 0; i < graph->num_ports_non_rt(); ++i) { + put_port(graph->port_impl(i)); + } + + // Enqueue arcs + for (const auto& a : graph->arcs()) { + const SPtr<const Arc> arc = a.second; + const Connect connect = { arc->tail_path(), arc->head_path() }; + connects.push_back(connect); + } +} + +void +ClientUpdate::put_plugin(PluginImpl* plugin) +{ + put(plugin->uri(), plugin->properties()); + + for (const auto& p : plugin->presets()) { + put_preset(plugin->uris(), plugin->uri(), p.first, p.second); + } +} + +void +ClientUpdate::put_preset(const URIs& uris, + const Raul::URI& plugin, + const Raul::URI& preset, + const std::string& label) +{ + Resource::Properties props{ + { uris.rdf_type, uris.pset_Preset.urid }, + { uris.rdfs_label, uris.forge.alloc(label) }, + { uris.lv2_appliesTo, uris.forge.make_urid(plugin) }}; + put(preset, props); +} + +void +ClientUpdate::del(const Raul::URI& subject) +{ + dels.push_back(subject); +} + +/** Returns true if a is closer to the root than b. */ +static inline bool +put_higher_than(const ClientUpdate::Put& a, const ClientUpdate::Put& b) +{ + return (std::count(a.uri.begin(), a.uri.end(), '/') < + std::count(b.uri.begin(), b.uri.end(), '/')); +} + +void +ClientUpdate::send(Interface* dest) +{ + // Send deletions + for (const Raul::URI& subject : dels) { + dest->del(subject); + } + + // Send puts in increasing depth order so parents are sent first + std::stable_sort(puts.begin(), puts.end(), put_higher_than); + for (const ClientUpdate::Put& put : puts) { + dest->put(put.uri, put.properties, put.ctx); + } + + // Send connections + for (const ClientUpdate::Connect& connect : connects) { + dest->connect(connect.tail, connect.head); + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/ClientUpdate.hpp b/src/server/ClientUpdate.hpp new file mode 100644 index 00000000..dcdcc132 --- /dev/null +++ b/src/server/ClientUpdate.hpp @@ -0,0 +1,81 @@ +/* + This file is part of Ingen. + Copyright 2007-2015 David Robillard <http://drobilla.net/> + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef INGEN_ENGINE_CLIENTUPDATE_HPP +#define INGEN_ENGINE_CLIENTUPDATE_HPP + +#include <string> +#include <vector> + +#include "ingen/Resource.hpp" +#include "raul/Path.hpp" +#include "raul/URI.hpp" + +namespace Ingen { + +class Interface; +class URIs; + +namespace Server { + +class PortImpl; +class BlockImpl; +class GraphImpl; +class PluginImpl; + +/** A sequence of puts/connects/deletes to update clients. + * + * Events like Get construct this in pre_process() and later send it in + * post_process() to avoid the need to lock. + */ +struct ClientUpdate { + void put(const Raul::URI& uri, + const Resource::Properties& props, + Resource::Graph ctx=Resource::Graph::DEFAULT); + + void put_port(const PortImpl* port); + void put_block(const BlockImpl* block); + void put_graph(const GraphImpl* graph); + void put_plugin(PluginImpl* plugin); + void put_preset(const URIs& uris, + const Raul::URI& plugin, + const Raul::URI& preset, + const std::string& label); + + void del(const Raul::URI& subject); + + void send(Interface* dest); + + struct Put { + Raul::URI uri; + Resource::Properties properties; + Resource::Graph ctx; + }; + + struct Connect { + Raul::Path tail; + Raul::Path head; + }; + + std::vector<Raul::URI> dels; + std::vector<Put> puts; + std::vector<Connect> connects; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_CLIENTUPDATE_HPP diff --git a/src/server/LV2Plugin.cpp b/src/server/LV2Plugin.cpp index 5e0dc609..ce920f4e 100644 --- a/src/server/LV2Plugin.cpp +++ b/src/server/LV2Plugin.cpp @@ -39,20 +39,26 @@ LV2Plugin::LV2Plugin(World* world, const LilvPlugin* lplugin) { set_property(_uris.rdf_type, _uris.lv2_Plugin); - LilvNode* minor = lilv_world_get(world->lilv_world(), - lilv_plugin_get_uri(lplugin), + update_properties(); +} + +void +LV2Plugin::update_properties() +{ + LilvNode* minor = lilv_world_get(_world->lilv_world(), + lilv_plugin_get_uri(_lilv_plugin), _uris.lv2_minorVersion, NULL); - LilvNode* micro = lilv_world_get(world->lilv_world(), - lilv_plugin_get_uri(lplugin), + LilvNode* micro = lilv_world_get(_world->lilv_world(), + lilv_plugin_get_uri(_lilv_plugin), _uris.lv2_minorVersion, NULL); if (lilv_node_is_int(minor) && lilv_node_is_int(micro)) { set_property(_uris.lv2_minorVersion, - world->forge().make(lilv_node_as_int(minor))); + _world->forge().make(lilv_node_as_int(minor))); set_property(_uris.lv2_microVersion, - world->forge().make(lilv_node_as_int(micro))); + _world->forge().make(lilv_node_as_int(micro))); } lilv_node_free(minor); diff --git a/src/server/LV2Plugin.hpp b/src/server/LV2Plugin.hpp index 0eee6731..aa10d90a 100644 --- a/src/server/LV2Plugin.hpp +++ b/src/server/LV2Plugin.hpp @@ -52,8 +52,15 @@ public: World* world() const { return _world; } const LilvPlugin* lilv_plugin() const { return _lilv_plugin; } + void update_properties(); + void load_presets(); + Raul::URI bundle_uri() const { + const LilvNode* bundle = lilv_plugin_get_bundle_uri(_lilv_plugin); + return Raul::URI(lilv_node_as_uri(bundle)); + } + private: World* _world; const LilvPlugin* _lilv_plugin; diff --git a/src/server/PluginImpl.hpp b/src/server/PluginImpl.hpp index 414bd1f1..29daba7b 100644 --- a/src/server/PluginImpl.hpp +++ b/src/server/PluginImpl.hpp @@ -49,6 +49,7 @@ public: const Raul::URI& uri) : Resource(uris, uri) , _type(type) + , _is_zombie(false) {} virtual ~PluginImpl() {} @@ -63,6 +64,8 @@ public: const Atom& type() const { return _type; } void set_type(const Atom& t) { _type = t; } + bool is_zombie() const { return _is_zombie; } + void set_is_zombie(bool t) { _is_zombie = t; } typedef std::pair<Raul::URI, std::string> Preset; typedef std::map<Raul::URI, std::string> Presets; @@ -75,12 +78,17 @@ public: return _presets; } + virtual void update_properties() {} + virtual void load_presets() { _presets_loaded = true; } + virtual Raul::URI bundle_uri() const { return Raul::URI("ingen:/"); } + protected: Atom _type; Presets _presets; bool _presets_loaded; + bool _is_zombie; }; } // namespace Server diff --git a/src/server/events/CreateBlock.hpp b/src/server/events/CreateBlock.hpp index 189a0896..40d72f52 100644 --- a/src/server/events/CreateBlock.hpp +++ b/src/server/events/CreateBlock.hpp @@ -19,8 +19,8 @@ #include "ingen/Resource.hpp" +#include "ClientUpdate.hpp" #include "Event.hpp" -#include "events/Get.hpp" namespace Ingen { namespace Server { @@ -54,7 +54,7 @@ public: private: Raul::Path _path; Resource::Properties& _properties; - Events::Get::Response _update; + ClientUpdate _update; GraphImpl* _graph; BlockImpl* _block; CompiledGraph* _compiled_graph; diff --git a/src/server/events/CreateGraph.hpp b/src/server/events/CreateGraph.hpp index bcb857ae..cf40fb41 100644 --- a/src/server/events/CreateGraph.hpp +++ b/src/server/events/CreateGraph.hpp @@ -55,7 +55,7 @@ private: const Raul::Path _path; Resource::Properties _properties; - Events::Get::Response _update; + ClientUpdate _update; GraphImpl* _graph; GraphImpl* _parent; CompiledGraph* _compiled_graph; diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 9b990c2d..66ebf803 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -160,7 +160,7 @@ get_file_node(LilvWorld* lworld, const URIs& uris, const Atom& value) * a patch:Put ; * patch:subject </> ; * patch:body [ - * ingen:loadedBundle <file:///old.lv2> + * ingen:loadedBundle <file:///old.lv2/> * ] . * * # Replace /old.lv2 with /new.lv2 @@ -168,10 +168,10 @@ get_file_node(LilvWorld* lworld, const URIs& uris, const Atom& value) * a patch:Patch ; * patch:subject </> ; * patch:remove [ - * ingen:loadedBundle <file:///old.lv2> + * ingen:loadedBundle <file:///old.lv2/> * ]; * patch:add [ - * ingen:loadedBundle <file:///new.lv2> + * ingen:loadedBundle <file:///new.lv2/> * ] . * @endcode */ @@ -277,7 +277,14 @@ Delta::pre_process() LilvWorld* lworld = _engine.world()->lilv_world(); LilvNode* bundle = get_file_node(lworld, uris, value); if (bundle) { + for (const auto& p : _engine.block_factory()->plugins()) { + if (p.second->bundle_uri() == lilv_node_as_string(bundle)) { + p.second->set_is_zombie(true); + _update.del(p.second->uri()); + } + } lilv_world_unload_bundle(lworld, bundle); + _engine.block_factory()->refresh(); lilv_node_free(bundle); } else { _status = Status::BAD_VALUE; @@ -423,6 +430,14 @@ Delta::pre_process() LilvNode* bundle = get_file_node(lworld, uris, value); if (bundle) { lilv_world_load_bundle(lworld, bundle); + const std::set<PluginImpl*> new_plugins = + _engine.block_factory()->refresh(); + + for (PluginImpl* p : new_plugins) { + if (p->bundle_uri() == lilv_node_as_string(bundle)) { + _update.put_plugin(p); + } + } lilv_node_free(bundle); } else { _status = Status::BAD_VALUE; @@ -568,6 +583,8 @@ Delta::post_process() } if (respond() == Status::SUCCESS) { + _update.send(_engine.broadcaster()); + switch (_type) { case Type::SET: /* Kludge to avoid feedback for set events only. The GUI @@ -583,7 +600,7 @@ Delta::post_process() case Type::PUT: if (_type == Type::PUT && _subject.substr(0, 5) == "file:") { // Preset save - Get::Response response; + ClientUpdate response; response.put(_preset->uri(), _preset->properties()); response.send(_engine.broadcaster()); } else { diff --git a/src/server/events/Delta.hpp b/src/server/events/Delta.hpp index 0782238c..569b3e92 100644 --- a/src/server/events/Delta.hpp +++ b/src/server/events/Delta.hpp @@ -98,6 +98,7 @@ private: Raul::URI _subject; Resource::Properties _properties; Resource::Properties _remove; + ClientUpdate _update; Ingen::Resource* _object; GraphImpl* _graph; CompiledGraph* _compiled_graph; diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp index 6d49657d..fa56f23a 100644 --- a/src/server/events/Get.cpp +++ b/src/server/events/Get.cpp @@ -34,118 +34,6 @@ namespace Ingen { namespace Server { namespace Events { -void -Get::Response::put(const Raul::URI& uri, - const Resource::Properties& props, - Resource::Graph ctx) -{ - const Get::Response::Put put = { uri, props, ctx }; - puts.push_back(put); -} - -void -Get::Response::put_port(const PortImpl* port) -{ - if (port->is_a(PortType::CONTROL) || port->is_a(PortType::CV)) { - Resource::Properties props = port->properties(); - props.erase(port->bufs().uris().ingen_value); - props.insert(std::make_pair(port->bufs().uris().ingen_value, - port->value())); - put(port->uri(), props); - } else { - put(port->uri(), port->properties()); - } -} - -void -Get::Response::put_block(const BlockImpl* block) -{ - const PluginImpl* const plugin = block->plugin_impl(); - const URIs& uris = plugin->uris(); - - if (uris.ingen_Graph == plugin->type()) { - put_graph((const GraphImpl*)block); - } else { - put(block->uri(), block->properties()); - for (size_t j = 0; j < block->num_ports(); ++j) { - put_port(block->port_impl(j)); - } - } -} - -void -Get::Response::put_graph(const GraphImpl* graph) -{ - put(graph->uri(), - graph->properties(Resource::Graph::INTERNAL), - Resource::Graph::INTERNAL); - - put(graph->uri(), - graph->properties(Resource::Graph::EXTERNAL), - Resource::Graph::EXTERNAL); - - // Enqueue blocks - for (const auto& b : graph->blocks()) { - put_block(&b); - } - - // Enqueue ports - for (uint32_t i = 0; i < graph->num_ports_non_rt(); ++i) { - put_port(graph->port_impl(i)); - } - - // Enqueue arcs - for (const auto& a : graph->arcs()) { - const SPtr<const Arc> arc = a.second; - const Connect connect = { arc->tail_path(), arc->head_path() }; - connects.push_back(connect); - } -} - -void -Get::Response::put_plugin(PluginImpl* plugin) -{ - put(plugin->uri(), plugin->properties()); - - for (const auto& p : plugin->presets()) { - put_preset(plugin->uris(), plugin->uri(), p.first, p.second); - } -} - -void -Get::Response::put_preset(const URIs& uris, - const Raul::URI& plugin, - const Raul::URI& preset, - const std::string& label) -{ - Resource::Properties props{ - { uris.rdf_type, uris.pset_Preset.urid }, - { uris.rdfs_label, uris.forge.alloc(label) }, - { uris.lv2_appliesTo, uris.forge.make_urid(plugin) }}; - put(preset, props); -} - -/** Returns true if a is closer to the root than b. */ -static inline bool -put_higher_than(const Get::Response::Put& a, const Get::Response::Put& b) -{ - return (std::count(a.uri.begin(), a.uri.end(), '/') < - std::count(b.uri.begin(), b.uri.end(), '/')); -} - -void -Get::Response::send(Interface* dest) -{ - // Sort puts by increasing depth so parents are sent first - std::stable_sort(puts.begin(), puts.end(), put_higher_than); - 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<Interface> client, int32_t id, diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp index f24e42e0..fc5fcb13 100644 --- a/src/server/events/Get.hpp +++ b/src/server/events/Get.hpp @@ -19,8 +19,9 @@ #include <vector> -#include "Event.hpp" #include "BlockFactory.hpp" +#include "ClientUpdate.hpp" +#include "Event.hpp" #include "types.hpp" namespace Ingen { @@ -50,52 +51,12 @@ public: void execute(ProcessContext& context) {} void post_process(); - /** 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. - * - * Ideally events (both server and client) would always be in a standard - * message format so the Ingen protocol went the whole way through the - * system, but for now things are controlled procedurally through - * Interface, so this interim structure is necessary. - */ - struct Response { - void put(const Raul::URI& uri, - const Resource::Properties& props, - Resource::Graph ctx=Resource::Graph::DEFAULT); - - void put_port(const PortImpl* port); - void put_block(const BlockImpl* block); - void put_graph(const GraphImpl* graph); - void put_plugin(PluginImpl* plugin); - void put_preset(const URIs& uris, - const Raul::URI& plugin, - const Raul::URI& preset, - const std::string& label); - - void send(Interface* dest); - - struct Put { - Raul::URI uri; - Resource::Properties properties; - Resource::Graph ctx; - }; - - struct Connect { - Raul::Path tail; - Raul::Path head; - }; - - std::vector<Put> puts; - std::vector<Connect> connects; - }; - private: const Raul::URI _uri; const Node* _object; PluginImpl* _plugin; BlockFactory::Plugins _plugins; - Response _response; + ClientUpdate _response; }; } // namespace Events diff --git a/src/server/internals/Controller.cpp b/src/server/internals/Controller.cpp index f6555a96..62400efa 100644 --- a/src/server/internals/Controller.cpp +++ b/src/server/internals/Controller.cpp @@ -41,12 +41,12 @@ InternalPlugin* ControllerNode::internal_plugin(URIs& uris) { uris, Raul::URI(NS_INTERNALS "Controller"), Raul::Symbol("controller")); } -ControllerNode::ControllerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) +ControllerNode::ControllerNode(InternalPlugin* plugin, + BufferFactory& bufs, + const Raul::Symbol& symbol, + bool polyphonic, + GraphImpl* parent, + SampleRate srate) : InternalBlock(plugin, symbol, false, parent, srate) , _learning(false) { diff --git a/src/server/internals/Controller.hpp b/src/server/internals/Controller.hpp index 4eb1f508..1c3e05f0 100644 --- a/src/server/internals/Controller.hpp +++ b/src/server/internals/Controller.hpp @@ -38,12 +38,12 @@ namespace Internals { class ControllerNode : public InternalBlock { public: - ControllerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); + ControllerNode(InternalPlugin* plugin, + BufferFactory& bufs, + const Raul::Symbol& symbol, + bool polyphonic, + GraphImpl* parent, + SampleRate srate); void run(ProcessContext& context); diff --git a/src/server/wscript b/src/server/wscript index 7dde5361..ddf47889 100644 --- a/src/server/wscript +++ b/src/server/wscript @@ -9,6 +9,7 @@ def build(bld): Broadcaster.cpp Buffer.cpp BufferFactory.cpp + ClientUpdate.cpp Context.cpp ControlBindings.cpp DuplexPort.cpp |