From db5d1f603e0dd3076444f27008fcf1a61a4e151c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 28 Nov 2020 12:32:24 +0100 Subject: Factor out Connector from PatchageCanvas This finally breaks the dependency of the canvas on the entire application, and fixes the confusing situation where it wasn't clear whether connect/disconnect methods made/broke connections on the canvas or on the system. --- src/Connector.cpp | 67 ++++++++++++++++++++++++++++++++ src/Connector.hpp | 43 +++++++++++++++++++++ src/Patchage.cpp | 6 ++- src/Patchage.hpp | 2 + src/PatchageCanvas.cpp | 103 ++++++++++++++----------------------------------- src/PatchageCanvas.hpp | 15 +++---- src/PortID.hpp | 14 +++++++ wscript | 1 + 8 files changed, 169 insertions(+), 82 deletions(-) create mode 100644 src/Connector.cpp create mode 100644 src/Connector.hpp diff --git a/src/Connector.cpp b/src/Connector.cpp new file mode 100644 index 0000000..c26c7a7 --- /dev/null +++ b/src/Connector.cpp @@ -0,0 +1,67 @@ +/* This file is part of Patchage. + * Copyright 2007-2020 David Robillard + * + * Patchage 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 3 of the License, or (at your option) + * any later version. + * + * Patchage 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 Patchage. If not, see . + */ + +#include "Connector.hpp" + +#include "Driver.hpp" +#include "ILog.hpp" +#include "PortID.hpp" + +#include + +Connector::Connector(ILog& log) + : _log(log) +{} + +void +Connector::add_driver(PortID::Type type, Driver* driver) +{ + _drivers.emplace(type, driver); +} + +void +Connector::connect(const PortID& tail, const PortID& head) +{ + if (tail.type() != head.type()) { + _log.warning("Unable to connect incompatible port types"); + return; + } + + auto d = _drivers.find(tail.type()); + if (d == _drivers.end()) { + _log.error("No driver for port type"); + return; + } + + d->second->connect(tail, head); +} + +void +Connector::disconnect(const PortID& tail, const PortID& head) +{ + if (tail.type() != head.type()) { + _log.error("Unable to disconnect incompatible port types"); + return; + } + + auto d = _drivers.find(tail.type()); + if (d == _drivers.end()) { + _log.error("No driver for port type"); + return; + } + + d->second->disconnect(tail, head); +} diff --git a/src/Connector.hpp b/src/Connector.hpp new file mode 100644 index 0000000..084217e --- /dev/null +++ b/src/Connector.hpp @@ -0,0 +1,43 @@ +/* This file is part of Patchage. + * Copyright 2007-2020 David Robillard + * + * Patchage 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 3 of the License, or (at your option) + * any later version. + * + * Patchage 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 Patchage. If not, see . + */ + +#ifndef PATCHAGE_CONNECTOR_HPP +#define PATCHAGE_CONNECTOR_HPP + +#include "PortID.hpp" + +#include + +class Driver; +class ILog; + +/// Controller that makes and breaks connections on the system +class Connector +{ +public: + explicit Connector(ILog& log); + + void add_driver(PortID::Type type, Driver* driver); + + void connect(const PortID& tail, const PortID& head); + void disconnect(const PortID& tail, const PortID& head); + +private: + ILog& _log; + std::unordered_map _drivers; +}; + +#endif // PATCHAGE_CONNECTOR_HPP diff --git a/src/Patchage.cpp b/src/Patchage.cpp index fb269d5..fdd0ce9 100644 --- a/src/Patchage.cpp +++ b/src/Patchage.cpp @@ -168,6 +168,7 @@ Patchage::Patchage(int argc, char** argv) , INIT_WIDGET(_status_text) , _legend(nullptr) , _log(_status_text) + , _connector(_log) , _pane_initialized(false) , _attach(true) , _driver_detached(false) @@ -179,7 +180,7 @@ Patchage::Patchage(int argc, char** argv) #endif { _conf = new Configuration(); - _canvas = std::make_shared(this, 1600 * 2, 1200 * 2); + _canvas = std::make_shared(_connector, 1600 * 2, 1200 * 2); while (argc > 0) { if (!strcmp(*argv, "-h") || !strcmp(*argv, "--help")) { @@ -338,6 +339,8 @@ Patchage::Patchage(int argc, char** argv) #if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) _jack_driver = new JackDriver(this, _log); + _connector.add_driver(PortID::Type::jack, _jack_driver); + _jack_driver->signal_detached.connect( sigc::mem_fun(this, &Patchage::driver_detached)); @@ -349,6 +352,7 @@ Patchage::Patchage(int argc, char** argv) #ifdef HAVE_ALSA _alsa_driver = new AlsaDriver(this, _log); + _connector.add_driver(PortID::Type::alsa, _alsa_driver); #endif connect_widgets(); diff --git a/src/Patchage.hpp b/src/Patchage.hpp index e453b68..19b2e9f 100644 --- a/src/Patchage.hpp +++ b/src/Patchage.hpp @@ -38,6 +38,7 @@ #include #include +#include "Connector.hpp" #include "ILog.hpp" #include "Legend.hpp" #include "TextViewLog.hpp" @@ -210,6 +211,7 @@ protected: Widget _status_text; Legend* _legend; TextViewLog _log; + Connector _connector; Glib::RefPtr _error_tag; Glib::RefPtr _warning_tag; diff --git a/src/PatchageCanvas.cpp b/src/PatchageCanvas.cpp index 6bae432..167df2b 100644 --- a/src/PatchageCanvas.cpp +++ b/src/PatchageCanvas.cpp @@ -18,31 +18,25 @@ #include "patchage_config.h" -#include "Patchage.hpp" +#include "Connector.hpp" #include "PatchageModule.hpp" #include "PatchagePort.hpp" #include "warnings.hpp" -#if defined(HAVE_JACK_DBUS) -# include "JackDbusDriver.hpp" -#elif defined(PATCHAGE_LIBJACK) -# include "JackDriver.hpp" -#endif -#ifdef HAVE_ALSA -# include "AlsaDriver.hpp" -#endif +#include PATCHAGE_DISABLE_GANV_WARNINGS #include "ganv/Edge.hpp" PATCHAGE_RESTORE_WARNINGS -PatchageCanvas::PatchageCanvas(Patchage* app, int width, int height) +PatchageCanvas::PatchageCanvas(Connector& connector, int width, int height) : Ganv::Canvas(width, height) - , _app(app) + , _connector(connector) { signal_event.connect(sigc::mem_fun(this, &PatchageCanvas::on_event)); - signal_connect.connect(sigc::mem_fun(this, &PatchageCanvas::connect)); - signal_disconnect.connect(sigc::mem_fun(this, &PatchageCanvas::disconnect)); + signal_connect.connect(sigc::mem_fun(this, &PatchageCanvas::on_connect)); + signal_disconnect.connect( + sigc::mem_fun(this, &PatchageCanvas::on_disconnect)); } PatchageModule* @@ -189,71 +183,32 @@ PatchageCanvas::find_port_by_name(const std::string& client_name, } void -PatchageCanvas::connect(Ganv::Node* port1, Ganv::Node* port2) +PatchageCanvas::on_connect(Ganv::Node* port1, Ganv::Node* port2) { - auto* p1 = dynamic_cast(port1); - auto* p2 = dynamic_cast(port2); - if (!p1 || !p2) { - return; - } - - if ((p1->type() == PortType::jack_audio && - p2->type() == PortType::jack_audio) || - (p1->type() == PortType::jack_midi && - p2->type() == PortType::jack_midi) || - (p1->type() == PortType::jack_audio && - p2->type() == PortType::jack_cv) || - (p1->type() == PortType::jack_cv && p2->type() == PortType::jack_cv) || - (p1->type() == PortType::jack_osc && - p2->type() == PortType::jack_osc)) { -#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) - _app->jack_driver()->connect(p1->id(), p2->id()); -#endif -#ifdef HAVE_ALSA - } else if (p1->type() == PortType::alsa_midi && - p2->type() == PortType::alsa_midi) { - _app->alsa_driver()->connect(p1->id(), p2->id()); -#endif - } else { - _app->log().warning("Cannot make connection, incompatible port types"); + auto* const p1 = dynamic_cast(port1); + auto* const p2 = dynamic_cast(port2); + + if (p1 && p2) { + if (p1->is_output() && p2->is_input()) { + _connector.connect(p1->id(), p2->id()); + } else if (p2->is_output() && p1->is_input()) { + _connector.connect(p2->id(), p1->id()); + } } } void -PatchageCanvas::disconnect(Ganv::Node* port1, Ganv::Node* port2) +PatchageCanvas::on_disconnect(Ganv::Node* port1, Ganv::Node* port2) { - auto* input = dynamic_cast(port1); - auto* output = dynamic_cast(port2); - if (!input || !output) { - return; - } - - if (input->is_output() && output->is_input()) { - // Damn, guessed wrong - PatchagePort* swap = input; - input = output; - output = swap; - } - - if (!input || !output || input->is_output() || output->is_input()) { - _app->log().error("Attempt to disconnect mismatched/unknown ports"); - return; - } - - if (input->type() == PortType::jack_audio || - input->type() == PortType::jack_midi || - input->type() == PortType::jack_cv || - input->type() == PortType::jack_osc) { -#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) - _app->jack_driver()->disconnect(output->id(), input->id()); - -#endif -#ifdef HAVE_ALSA - } else if (input->type() == PortType::alsa_midi) { - _app->alsa_driver()->disconnect(output->id(), input->id()); -#endif - } else { - _app->log().error("Attempt to disconnect ports with strange types"); + auto* const p1 = dynamic_cast(port1); + auto* const p2 = dynamic_cast(port2); + + if (p1 && p2) { + if (p1->is_output() && p2->is_input()) { + _connector.disconnect(p1->id(), p2->id()); + } else if (p2->is_output() && p1->is_input()) { + _connector.disconnect(p2->id(), p1->id()); + } } } @@ -278,12 +233,12 @@ PatchageCanvas::add_module(const std::string& name, PatchageModule* module) } } -static void +void disconnect_edge(GanvEdge* edge, void* data) { auto* canvas = static_cast(data); Ganv::Edge* edgemm = Glib::wrap(edge); - canvas->disconnect(edgemm->get_tail(), edgemm->get_head()); + canvas->on_disconnect(edgemm->get_tail(), edgemm->get_head()); } bool diff --git a/src/PatchageCanvas.hpp b/src/PatchageCanvas.hpp index 40ec51c..349512b 100644 --- a/src/PatchageCanvas.hpp +++ b/src/PatchageCanvas.hpp @@ -36,14 +36,14 @@ PATCHAGE_RESTORE_WARNINGS #include #include -class Patchage; class PatchageModule; class PatchagePort; +class Connector; class PatchageCanvas : public Ganv::Canvas { public: - PatchageCanvas(Patchage* _app, int width, int height); + PatchageCanvas(Connector& connector, int width, int height); PatchageModule* find_module(const std::string& name, ModuleType type); PatchagePort* find_port(const PortID& id); @@ -54,10 +54,6 @@ public: PatchagePort* find_port_by_name(const std::string& client_name, const std::string& port_name); - void connect(Ganv::Node* port1, Ganv::Node* port2); - - void disconnect(Ganv::Node* port1, Ganv::Node* port2); - void index_port(const PortID& id, PatchagePort* port) { _port_index.insert(std::make_pair(id, port)); @@ -77,10 +73,15 @@ private: using PortIndex = std::map; using ModuleIndex = std::multimap; + friend void disconnect_edge(GanvEdge*, void*); + bool on_event(GdkEvent* ev); bool on_connection_event(Ganv::Edge* c, GdkEvent* ev); - Patchage* _app; + void on_connect(Ganv::Node* port1, Ganv::Node* port2); + void on_disconnect(Ganv::Node* port1, Ganv::Node* port2); + + Connector& _connector; PortIndex _port_index; ModuleIndex _module_index; }; diff --git a/src/PortID.hpp b/src/PortID.hpp index efdd78d..84c08a4 100644 --- a/src/PortID.hpp +++ b/src/PortID.hpp @@ -21,6 +21,7 @@ #include #include #include +#include /// An ID for some port on a client (program) struct PortID @@ -133,4 +134,17 @@ operator<(const PortID& lhs, const PortID& rhs) return false; } +namespace std { + +template<> +struct hash +{ + size_t operator()(const PortID::Type& v) const noexcept + { + return hash()(static_cast(v)); + } +}; + +} // namespace std + #endif // PATCHAGE_PORTID_HPP diff --git a/wscript b/wscript index 2d80c4e..b0a0734 100644 --- a/wscript +++ b/wscript @@ -239,6 +239,7 @@ def build(bld): install_path = '${BINDIR}') prog.source = ''' src/Configuration.cpp + src/Connector.cpp src/Legend.cpp src/Patchage.cpp src/PatchageCanvas.cpp -- cgit v1.2.1