From e16206982d074e62956de00eeef611478f01c430 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 19 Aug 2008 18:51:06 +0000 Subject: Preliminary connecting via HTTP in Gtk client. Better handling of overflowed client event receive buffer. Store fixes, complain only once about orphans, don't request an orphan parent over and over. git-svn-id: http://svn.drobilla.net/lad/ingen@1447 a436a847-0d15-0410-975c-d299462d15a1 --- src/libs/client/ClientStore.cpp | 10 +- src/libs/client/HTTPClientReceiver.cpp | 97 ++++++++ src/libs/client/HTTPClientReceiver.hpp | 62 +++++ src/libs/client/HTTPEngineSender.cpp | 300 +++++++++++++++++++++++++ src/libs/client/HTTPEngineSender.hpp | 162 +++++++++++++ src/libs/client/Makefile.am | 16 +- src/libs/client/NodeModel.cpp | 12 +- src/libs/client/NodeModel.hpp | 9 +- src/libs/client/OSCClientReceiver.hpp | 14 +- src/libs/client/OSCEngineSender.hpp | 8 +- src/libs/client/ObjectModel.cpp | 33 +-- src/libs/client/PortModel.cpp | 4 + src/libs/client/PortModel.hpp | 2 - src/libs/client/SigClientInterface.hpp | 2 + src/libs/client/ThreadedSigClientInterface.cpp | 32 +-- src/libs/client/ThreadedSigClientInterface.hpp | 13 +- src/libs/client/client.cpp | 24 +- src/libs/client/client.hpp | 2 +- 18 files changed, 729 insertions(+), 73 deletions(-) create mode 100644 src/libs/client/HTTPClientReceiver.cpp create mode 100644 src/libs/client/HTTPClientReceiver.hpp create mode 100644 src/libs/client/HTTPEngineSender.cpp create mode 100644 src/libs/client/HTTPEngineSender.hpp (limited to 'src/libs/client') diff --git a/src/libs/client/ClientStore.cpp b/src/libs/client/ClientStore.cpp index 0597aa21..18582046 100644 --- a/src/libs/client/ClientStore.cpp +++ b/src/libs/client/ClientStore.cpp @@ -72,17 +72,15 @@ ClientStore::add_plugin_orphan(SharedPtr node) { if (!_handle_orphans) return; - cerr << "WARNING: Node " << node->path() << " received, but plugin " - << node->plugin_uri() << " unknown." << endl; Raul::Table > >::iterator spawn = _plugin_orphans.find(node->plugin_uri()); - _engine->request_plugin(node->plugin_uri()); - if (spawn != _plugin_orphans.end()) { spawn->second.push_back(node); } else { + cerr << "WARNING: Orphans of plugin " << node->plugin_uri() << " received" << endl; + _engine->request_plugin(node->plugin_uri()); list > l; l.push_back(node); _plugin_orphans[node->plugin_uri()] = l; @@ -108,7 +106,7 @@ ClientStore::resolve_plugin_orphans(SharedPtr plugin) for (list >::iterator i = spawn.begin(); i != spawn.end(); ++i) { (*i)->_plugin = plugin; - add_object(*i); + //add_object(*i); } } } @@ -248,7 +246,6 @@ ClientStore::add_object(SharedPtr object) // one (with precedence to the new values). iterator existing = find(object->path()); if (existing != end()) { - cout << "WARNING: Object " << object->path() << " already exists in store" << endl; PtrCast(existing->second)->set(object); } else { @@ -444,6 +441,7 @@ ClientStore::new_node(const string& path, const string& plugin_uri) if (!plug) { SharedPtr n(new NodeModel(plugin_uri, path)); add_plugin_orphan(n); + add_object(n); } else { SharedPtr n(new NodeModel(plug, path)); add_object(n); diff --git a/src/libs/client/HTTPClientReceiver.cpp b/src/libs/client/HTTPClientReceiver.cpp new file mode 100644 index 00000000..ece55ab2 --- /dev/null +++ b/src/libs/client/HTTPClientReceiver.cpp @@ -0,0 +1,97 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "module/Module.hpp" +#include "HTTPClientReceiver.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Client { + + +HTTPClientReceiver::HTTPClientReceiver( + Shared::World* world, + const std::string& url, + SharedPtr target) + : _target(target) + , _world(world) + , _url(url) + , _session(NULL) +{ + start(false); +} + + +HTTPClientReceiver::~HTTPClientReceiver() +{ + stop(); +} + + +void +HTTPClientReceiver::message_callback(SoupSession* session, SoupMessage* msg, void* ptr) +{ + HTTPClientReceiver* me = (HTTPClientReceiver*)ptr; + cout << "RECEIVED ASYNC MESSAGE: " << msg->response_body->data << endl; + me->_target->response_ok(0); + me->_target->enable(); + me->_parser->parse_string(me->_world, me->_target.get(), Glib::ustring(msg->response_body->data), + Glib::ustring("/"), Glib::ustring("")); +} + + +void +HTTPClientReceiver::start(bool dump) +{ + Glib::Mutex::Lock lock(_world->rdf_world->mutex()); + if (!_parser) { + if (!_world->serialisation_module) + _world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (_world->serialisation_module) { + Parser* (*new_parser)() = NULL; + if (_world->serialisation_module->get_symbol("new_parser", (void*&)new_parser)) + _parser = SharedPtr(new_parser()); + } + } + _session = soup_session_async_new(); + SoupMessage* msg = soup_message_new("GET", _url.c_str()); + soup_session_queue_message (_session, msg, message_callback, this); +} + + +void +HTTPClientReceiver::stop() +{ + if (_session != NULL) { + //unregister_client(); + soup_session_abort(_session); + _session = NULL; + } +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/libs/client/HTTPClientReceiver.hpp b/src/libs/client/HTTPClientReceiver.hpp new file mode 100644 index 00000000..bab55578 --- /dev/null +++ b/src/libs/client/HTTPClientReceiver.hpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HTTPCLIENTRECEIVER_H +#define HTTPCLIENTRECEIVER_H + +#include +#include +#include +#include "interface/ClientInterface.hpp" +#include "serialisation/Parser.hpp" +#include "redlandmm/World.hpp" +#include "raul/Deletable.hpp" + +namespace Ingen { +namespace Client { + + +class HTTPClientReceiver : public boost::noncopyable, public Raul::Deletable +{ +public: + HTTPClientReceiver(Shared::World* world, + const std::string& url, + SharedPtr target); + + ~HTTPClientReceiver(); + + std::string uri() const { return _url; } + + void start(bool dump); + void stop(); + +private: + static void message_callback(SoupSession* session, SoupMessage* msg, void* ptr); + + SharedPtr _target; + + Shared::World* _world; + const std::string _url; + SoupSession* _session; + SharedPtr _parser; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // HTTPCLIENTRECEIVER_H diff --git a/src/libs/client/HTTPEngineSender.cpp b/src/libs/client/HTTPEngineSender.cpp new file mode 100644 index 00000000..882a4bc4 --- /dev/null +++ b/src/libs/client/HTTPEngineSender.cpp @@ -0,0 +1,300 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "HTTPEngineSender.hpp" + +using namespace std; + +namespace Ingen { +namespace Client { + + +HTTPEngineSender::HTTPEngineSender(const string& engine_url) + : _engine_url(engine_url) + , _id(0) + , _enabled(true) +{ + _session = soup_session_sync_new(); +} + + +HTTPEngineSender::~HTTPEngineSender() +{ + soup_session_abort(_session); +} + + +void +HTTPEngineSender::attach(int32_t ping_id, bool block) +{ + /*SoupMessage *msg; + msg = soup_message_new ("GET", _engine_url.c_str()); + int status = soup_session_send_message (_session, msg); + cout << "STATUS: " << status << endl; + cout << "RESPONSE: " << msg->response_body->data << endl;*/ +} + + +/* *** EngineInterface implementation below here *** */ + + +/** Register with the engine via HTTP. + * + * Note that this does not actually use 'key', since the engine creates + * it's own key for HTTP clients (namely the incoming URL), for NAT + * traversal. It is a parameter to remain compatible with EngineInterface. + */ +void +HTTPEngineSender::register_client(ClientInterface* client) +{ + +} + + +void +HTTPEngineSender::unregister_client(const string& uri) +{ + +} + + +// Engine commands +void +HTTPEngineSender::load_plugins() +{ + +} + + +void +HTTPEngineSender::activate() +{ + +} + + +void +HTTPEngineSender::deactivate() +{ + +} + + +void +HTTPEngineSender::quit() +{ + +} + + + +// Object commands + +void +HTTPEngineSender::new_patch(const string& path, + uint32_t poly) +{ +} + + +void +HTTPEngineSender::new_port(const string& path, + uint32_t index, + const string& data_type, + bool is_output) +{ +} + + +void +HTTPEngineSender::new_node(const string& path, + const string& plugin_uri) +{ +} + + +/** Create a node using library name and plugin label (DEPRECATED). + * + * DO NOT USE THIS. + */ +void +HTTPEngineSender::new_node_deprecated(const string& path, + const string& plugin_type, + const string& library_name, + const string& plugin_label) +{ +} + + +void +HTTPEngineSender::rename(const string& old_path, + const string& new_name) +{ +} + + +void +HTTPEngineSender::destroy(const string& path) +{ +} + + +void +HTTPEngineSender::clear_patch(const string& patch_path) +{ +} + + +void +HTTPEngineSender::connect(const string& src_port_path, + const string& dst_port_path) +{ +} + + +void +HTTPEngineSender::disconnect(const string& src_port_path, + const string& dst_port_path) +{ +} + + +void +HTTPEngineSender::disconnect_all(const string& parent_patch_path, + const string& node_path) +{ +} + + +void +HTTPEngineSender::set_port_value(const string& port_path, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_port_value_immediate(const string& port_path, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_voice_value_immediate(const string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_program(const string& node_path, + uint32_t bank, + uint32_t program) +{ +} + + +void +HTTPEngineSender::midi_learn(const string& node_path) +{ +} + + +void +HTTPEngineSender::set_variable(const string& obj_path, + const string& predicate, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_property(const string& obj_path, + const string& predicate, + const Raul::Atom& value) +{ +} + + + +// Requests // + +void +HTTPEngineSender::ping() +{ +} + + +void +HTTPEngineSender::request_plugin(const string& uri) +{ +} + + +void +HTTPEngineSender::request_object(const string& path) +{ +} + + +void +HTTPEngineSender::request_port_value(const string& port_path) +{ +} + + +void +HTTPEngineSender::request_variable(const string& object_path, const string& key) +{ +} + + +void +HTTPEngineSender::request_property(const string& object_path, const string& key) +{ +} + + +void +HTTPEngineSender::request_plugins() +{ +} + + +void +HTTPEngineSender::request_all_objects() +{ +} + + + +} // namespace Client +} // namespace Ingen + + diff --git a/src/libs/client/HTTPEngineSender.hpp b/src/libs/client/HTTPEngineSender.hpp new file mode 100644 index 00000000..f753eea3 --- /dev/null +++ b/src/libs/client/HTTPEngineSender.hpp @@ -0,0 +1,162 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HTTPENGINESENDER_H +#define HTTPENGINESENDER_H + +#include +#include +#include +#include "interface/EngineInterface.hpp" +using std::string; +using Ingen::Shared::EngineInterface; +using Ingen::Shared::ClientInterface; + +namespace Ingen { +namespace Client { + + +/* HTTP (via libsoup) interface to the engine. + * + * Clients can use this opaquely as an EngineInterface to control the engine + * over HTTP (whether over a network or not). + * + * \ingroup IngenClient + */ +class HTTPEngineSender : public EngineInterface { +public: + HTTPEngineSender(const string& engine_url); + ~HTTPEngineSender(); + + string uri() const { return _engine_url; } + + inline int32_t next_id() + { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } + + void set_next_response_id(int32_t id) { _id = id; } + void disable_responses() { _id = -1; } + + void attach(int32_t ping_id, bool block); + + + /* *** EngineInterface implementation below here *** */ + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { transfer_begin(); } + void bundle_end() { transfer_end(); } + + void transfer_begin(); + void transfer_end(); + + // Client registration + void register_client(ClientInterface* client); + void unregister_client(const string& uri); + + // Engine commands + void load_plugins(); + void activate(); + void deactivate(); + void quit(); + + // Object commands + + void new_patch(const string& path, + uint32_t poly); + + void new_port(const string& path, + uint32_t index, + const string& data_type, + bool is_output); + + void new_node(const string& path, + const string& plugin_uri); + + void new_node_deprecated(const string& path, + const string& plugin_type, + const string& library_name, + const string& plugin_label); + + void rename(const string& old_path, + const string& new_name); + + void destroy(const string& path); + + void clear_patch(const string& patch_path); + + void connect(const string& src_port_path, + const string& dst_port_path); + + void disconnect(const string& src_port_path, + const string& dst_port_path); + + void disconnect_all(const string& parent_patch_path, + const string& node_path); + + void set_port_value(const string& port_path, + const Raul::Atom& value); + + void set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value); + + void set_port_value_immediate(const string& port_path, + const Raul::Atom& value); + + void set_voice_value_immediate(const string& port_path, + uint32_t voice, + const Raul::Atom& value); + + void set_program(const string& node_path, + uint32_t bank, + uint32_t program); + + void midi_learn(const string& node_path); + + void set_variable(const string& obj_path, + const string& predicate, + const Raul::Atom& value); + + void set_property(const string& obj_path, + const string& predicate, + const Raul::Atom& value); + + // Requests // + void ping(); + void request_plugin(const string& uri); + void request_object(const string& path); + void request_port_value(const string& port_path); + void request_variable(const string& path, const string& key); + void request_property(const string& path, const string& key); + void request_plugins(); + void request_all_objects(); + +protected: + SoupSession* _session; + const string _engine_url; + int _client_port; + int32_t _id; + bool _enabled; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // HTTPENGINESENDER_H + diff --git a/src/libs/client/Makefile.am b/src/libs/client/Makefile.am index 88e2b393..76835de6 100644 --- a/src/libs/client/Makefile.am +++ b/src/libs/client/Makefile.am @@ -15,7 +15,8 @@ libingen_client_la_CXXFLAGS = \ @LXML2_CFLAGS@ \ @RAUL_CFLAGS@ \ @REDLANDMM_CFLAGS@ \ - @SLV2_CFLAGS@ + @SLV2_CFLAGS@ \ + @SOUP_CFLAGS@ libingen_client_la_LIBADD = \ ../shared/libingen_shared.la \ @@ -25,14 +26,21 @@ libingen_client_la_LIBADD = \ @LXML2_LIBS@ \ @RAUL_LIBS@ \ @REDLANDMM_LIBS@ \ - @SLV2_LIBS@ + @SLV2_LIBS@ \ + @SOUP_LIBS@ libingen_client_la_SOURCES = \ $(top_srcdir)/ingen/src/common/interface/ClientInterface.hpp \ $(top_srcdir)/ingen/src/common/interface/EngineInterface.hpp \ + ClientStore.cpp \ + ClientStore.hpp \ ConnectionModel.hpp \ DeprecatedLoader.cpp \ DeprecatedLoader.hpp \ + HTTPClientReceiver.cpp \ + HTTPClientReceiver.hpp \ + HTTPEngineSender.cpp \ + HTTPEngineSender.hpp \ NodeModel.cpp \ NodeModel.hpp \ OSCClientReceiver.cpp \ @@ -45,13 +53,11 @@ libingen_client_la_SOURCES = \ PatchModel.hpp \ PluginModel.cpp \ PluginModel.hpp \ - PluginUI.hpp \ PluginUI.cpp \ + PluginUI.hpp \ PortModel.cpp \ PortModel.hpp \ SigClientInterface.hpp \ - ClientStore.cpp \ - ClientStore.hpp \ ThreadedSigClientInterface.cpp \ ThreadedSigClientInterface.hpp \ client.cpp \ diff --git a/src/libs/client/NodeModel.cpp b/src/libs/client/NodeModel.cpp index 02a0a678..ac0c8e68 100644 --- a/src/libs/client/NodeModel.cpp +++ b/src/libs/client/NodeModel.cpp @@ -69,7 +69,7 @@ void NodeModel::remove_port(SharedPtr port) { // FIXME: slow - for (PortModelList::iterator i = _ports.begin(); i != _ports.end(); ++i) { + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { if ((*i) == port) { _ports.erase(i); break; @@ -83,7 +83,7 @@ void NodeModel::remove_port(const Path& port_path) { // FIXME: slow - for (PortModelList::iterator i = _ports.begin(); i != _ports.end(); ++i) { + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { if ((*i)->path() == port_path) { _ports.erase(i); break; @@ -141,7 +141,7 @@ NodeModel::add_port(SharedPtr pm) assert(pm->path().is_child_of(_path)); assert(pm->parent().get() == this); - PortModelList::iterator existing = find(_ports.begin(), _ports.end(), pm); + Ports::iterator existing = find(_ports.begin(), _ports.end(), pm); // Store should have handled this by merging the two assert(existing == _ports.end()); @@ -155,7 +155,7 @@ SharedPtr NodeModel::get_port(const string& port_name) const { assert(port_name.find("/") == string::npos); - for (PortModelList::const_iterator i = _ports.begin(); i != _ports.end(); ++i) + for (Ports::const_iterator i = _ports.begin(); i != _ports.end(); ++i) if ((*i)->path().name() == port_name) return (*i); return SharedPtr(); @@ -211,8 +211,10 @@ void NodeModel::set(SharedPtr model) { SharedPtr node = PtrCast(model); - if (node) + if (node) { + _plugin_uri = node->_plugin_uri; _plugin = node->_plugin; + } ObjectModel::set(model); } diff --git a/src/libs/client/NodeModel.hpp b/src/libs/client/NodeModel.hpp index a4bcf4d8..03afc17c 100644 --- a/src/libs/client/NodeModel.hpp +++ b/src/libs/client/NodeModel.hpp @@ -50,6 +50,8 @@ class NodeModel : public ObjectModel, virtual public Ingen::Shared::Node public: NodeModel(const NodeModel& copy); virtual ~NodeModel(); + + typedef vector > Ports; SharedPtr get_port(const string& port_name) const; @@ -58,7 +60,7 @@ public: const string& plugin_uri() const { return _plugin_uri; } const Shared::Plugin* plugin() const { return _plugin.get(); } uint32_t num_ports() const { return _ports.size(); } - const PortModelList& ports() const { return _ports; } + const Ports& ports() const { return _ports; } void port_value_range(SharedPtr port, float& min, float& max); @@ -84,7 +86,7 @@ protected: virtual void clear(); - PortModelList _ports; ///< List of ports (not a Table to preserve order) + Ports _ports; ///< Vector of ports (not a Table to preserve order) string _plugin_uri; ///< Plugin URI (if PluginModel is unknown) SharedPtr _plugin; ///< The plugin this node is an instance of uint32_t _num_values; ///< Size of _min_values and _max_values @@ -93,9 +95,6 @@ protected: }; -typedef Table > NodeModelMap; - - } // namespace Client } // namespace Ingen diff --git a/src/libs/client/OSCClientReceiver.hpp b/src/libs/client/OSCClientReceiver.hpp index f1d71dbb..ea5871b3 100644 --- a/src/libs/client/OSCClientReceiver.hpp +++ b/src/libs/client/OSCClientReceiver.hpp @@ -25,18 +25,13 @@ #include "raul/Deletable.hpp" namespace Ingen { - -/** Client library */ namespace Client { -//class NodeModel; - -/* Some boilerplate killing macros... */ +/** Arguments to a liblo handler */ #define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg -/* Defines a static handler to be passed to lo_add_method, which is a trivial - * wrapper around a non-static method that does the real work. Makes a whoole - * lot of ugly boiler plate go away */ +/** Define a static handler to be passed to lo_add_method, which is a trivial + * wrapper around a non-static method that does the real work. */ #define LO_HANDLER(name) \ int _##name##_cb (LO_HANDLER_ARGS);\ inline static int name##_cb(LO_HANDLER_ARGS, void* osc_listener)\ @@ -62,6 +57,8 @@ public: OSCClientReceiver(int listen_port, SharedPtr target); ~OSCClientReceiver(); + std::string uri() const { return lo_server_thread_get_url(_st); } + void start(bool dump_osc); void stop(); @@ -105,7 +102,6 @@ private: } // namespace Client - } // namespace Ingen #endif // OSCCLIENTRECEIVER_H diff --git a/src/libs/client/OSCEngineSender.hpp b/src/libs/client/OSCEngineSender.hpp index dbb7e8f5..4c92afdb 100644 --- a/src/libs/client/OSCEngineSender.hpp +++ b/src/libs/client/OSCEngineSender.hpp @@ -44,7 +44,7 @@ public: ~OSCEngineSender(); - string engine_url() { return _engine_url; } + std::string uri() const { return _engine_url; } inline int32_t next_id() { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } @@ -148,9 +148,9 @@ public: void request_all_objects(); protected: - string _engine_url; - int _client_port; - int32_t _id; + const string _engine_url; + int _client_port; + int32_t _id; }; diff --git a/src/libs/client/ObjectModel.cpp b/src/libs/client/ObjectModel.cpp index e9a491a6..ede5f822 100644 --- a/src/libs/client/ObjectModel.cpp +++ b/src/libs/client/ObjectModel.cpp @@ -119,21 +119,26 @@ ObjectModel::polyphonic() const * @a model as correct. The paths of the two models MUST be equal. */ void -ObjectModel::set(SharedPtr model) +ObjectModel::set(SharedPtr o) { - assert(_path == model->path()); - - for (Variables::const_iterator other = model->variables().begin(); - other != model->variables().end(); ++other) { - - Variables::const_iterator mine = _variables.find(other->first); - - if (mine != _variables.end()) { - cerr << "WARNING: " << _path << "Client/Server data mismatch: " << other->first << endl; - } - - _variables[other->first] = other->second; - signal_variable.emit(other->first, other->second); + assert(_path == o->path()); + if (o->_parent) + _parent = o->_parent; + + for (Variables::const_iterator v = o->variables().begin(); v != o->variables().end(); ++v) { + Variables::const_iterator mine = _variables.find(v->first); + if (mine != _variables.end()) + cerr << "WARNING: " << _path << "Client/Server variable mismatch: " << v->first << endl; + _variables[v->first] = v->second; + signal_variable.emit(v->first, v->second); + } + + for (Properties::const_iterator v = o->properties().begin(); v != o->properties().end(); ++v) { + Properties::const_iterator mine = _properties.find(v->first); + if (mine != _properties.end()) + cerr << "WARNING: " << _path << "Client/Server property mismatch: " << v->first << endl; + _properties[v->first] = v->second; + signal_variable.emit(v->first, v->second); } } diff --git a/src/libs/client/PortModel.cpp b/src/libs/client/PortModel.cpp index fa3cb345..c18378db 100644 --- a/src/libs/client/PortModel.cpp +++ b/src/libs/client/PortModel.cpp @@ -51,7 +51,11 @@ PortModel::set(SharedPtr model) { SharedPtr port = PtrCast(model); if (port) { + _index = port->_index; + _type = port->_type; + _direction = port->_direction; _current_val = port->_current_val; + _connections = port->_connections; signal_value_changed.emit(_current_val); } diff --git a/src/libs/client/PortModel.hpp b/src/libs/client/PortModel.hpp index a86c9188..a7f52679 100644 --- a/src/libs/client/PortModel.hpp +++ b/src/libs/client/PortModel.hpp @@ -105,8 +105,6 @@ private: size_t _connections; }; -typedef vector > PortModelList; - } // namespace Client } // namespace Ingen diff --git a/src/libs/client/SigClientInterface.hpp b/src/libs/client/SigClientInterface.hpp index 32a0a43b..7ab32c12 100644 --- a/src/libs/client/SigClientInterface.hpp +++ b/src/libs/client/SigClientInterface.hpp @@ -41,6 +41,8 @@ class SigClientInterface : public Ingen::Shared::ClientInterface, public sigc::t { public: SigClientInterface() : _enabled(true) {} + + bool enabled() const { return _enabled; } std::string uri() const { return "(internal)"; } diff --git a/src/libs/client/ThreadedSigClientInterface.cpp b/src/libs/client/ThreadedSigClientInterface.cpp index 3b7af80c..ef95133b 100644 --- a/src/libs/client/ThreadedSigClientInterface.cpp +++ b/src/libs/client/ThreadedSigClientInterface.cpp @@ -17,7 +17,8 @@ #include "ThreadedSigClientInterface.hpp" #include -using std::cerr; using std::endl; + +using namespace std; namespace Ingen { namespace Client { @@ -28,23 +29,19 @@ namespace Client { void ThreadedSigClientInterface::push_sig(Closure ev) { + _attached = true; if (!_enabled) return; bool success = false; - bool first = true; - - // (Very) slow busy-wait if the queue is full - // FIXME: Make this wait on a signal from process_sigs iff this happens while (!success) { - //printf("push %zu\n", _sigs.fill()); success = _sigs.push(ev); if (!success) { - if (first) { - cerr << "[ThreadedSigClientInterface] WARNING: (Client) event queue full. Waiting to try again..." << endl; - first = false; - } - usleep(200000); // 100 milliseconds (2* rate process_sigs is called) + cerr << "WARNING: Client event queue full. Waiting..." << endl; + _mutex.lock(); + _cond.wait(_mutex); + _mutex.unlock(); + cerr << "Queue drained, continuing" << endl; } } } @@ -58,18 +55,21 @@ ThreadedSigClientInterface::push_sig(Closure ev) bool ThreadedSigClientInterface::emit_signals() { - // Process a maximum of queue-size events, to prevent locking the GTK + // Process a limited number of events, to prevent locking the GTK // thread indefinitely while processing continually arriving events - const size_t limit = _sigs.capacity(); + size_t num_processed = 0; - while (!_sigs.empty() && num_processed++ < limit) { - //printf("emit %zu\n", _sigs.fill()); + while (!_sigs.empty() && num_processed++ < (_sigs.capacity() * 3 / 4)) { Closure& ev = _sigs.front(); - _sigs.pop(); ev(); ev.disconnect(); + _sigs.pop(); } + _mutex.lock(); + _cond.broadcast(); + _mutex.unlock(); + return true; } diff --git a/src/libs/client/ThreadedSigClientInterface.hpp b/src/libs/client/ThreadedSigClientInterface.hpp index b3a1b72c..3014c139 100644 --- a/src/libs/client/ThreadedSigClientInterface.hpp +++ b/src/libs/client/ThreadedSigClientInterface.hpp @@ -21,10 +21,12 @@ #include #include #include +#include #include "interface/ClientInterface.hpp" #include "SigClientInterface.hpp" -#include #include +#include + using std::string; /** Returns nothing and takes no parameters (because they have all been bound) */ @@ -65,12 +67,15 @@ public: , port_activity_slot(signal_port_activity.make_slot()) , program_add_slot(signal_program_add.make_slot()) , program_remove_slot(signal_program_remove.make_slot()) - {} + { + } virtual std::string uri() const { return "(internal)"; } virtual void subscribe(Shared::EngineInterface* engine) { throw; } + bool enabled() const { return _attached; } + void bundle_begin() { push_sig(bundle_begin_slot); } @@ -143,7 +148,11 @@ public: private: void push_sig(Closure ev); + Glib::Mutex _mutex; + Glib::Cond _cond; + Raul::SRSWQueue _sigs; + bool _attached; sigc::slot bundle_begin_slot; sigc::slot bundle_end_slot; diff --git a/src/libs/client/client.cpp b/src/libs/client/client.cpp index 8b86e958..32380378 100644 --- a/src/libs/client/client.cpp +++ b/src/libs/client/client.cpp @@ -15,21 +15,37 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include "client.hpp" #include "OSCEngineSender.hpp" +#include "HTTPEngineSender.hpp" + +using namespace std; namespace Ingen { namespace Client { SharedPtr -new_osc_interface(const std::string& url) +new_remote_interface(const std::string& url) { - OSCEngineSender* oes = new OSCEngineSender(url); - oes->attach(rand(), true); - return SharedPtr(oes); + const string scheme = url.substr(0, url.find(":")); + cout << "SCHEME: " << scheme << endl; + if (scheme == "osc.udp" || scheme == "osc.tcp") { + OSCEngineSender* oes = new OSCEngineSender(url); + oes->attach(rand(), true); + return SharedPtr(oes); + } else if (scheme == "http") { + HTTPEngineSender* hes = new HTTPEngineSender(url); + hes->attach(rand(), true); + return SharedPtr(hes); + } else { + cerr << "WARNING: Unknown URI scheme '" << scheme << "'" << endl; + return SharedPtr(); + } } + } // namespace Client } // namespace Ingen diff --git a/src/libs/client/client.hpp b/src/libs/client/client.hpp index 931e4c45..82166da5 100644 --- a/src/libs/client/client.hpp +++ b/src/libs/client/client.hpp @@ -30,7 +30,7 @@ namespace Client { extern "C" { - SharedPtr new_osc_interface(const std::string& url); + SharedPtr new_remote_interface(const std::string& url); SharedPtr new_queued_interface(SharedPtr engine); } -- cgit v1.2.1