diff options
-rw-r--r-- | src/AlsaDriver.cpp | 286 | ||||
-rw-r--r-- | src/AlsaDriver.hpp | 35 | ||||
-rw-r--r-- | src/Driver.hpp | 12 | ||||
-rw-r--r-- | src/JackDbusDriver.cpp | 319 | ||||
-rw-r--r-- | src/JackDbusDriver.hpp | 64 | ||||
-rw-r--r-- | src/JackDriver.cpp | 244 | ||||
-rw-r--r-- | src/JackDriver.hpp | 15 | ||||
-rw-r--r-- | src/Patchage.cpp | 28 | ||||
-rw-r--r-- | src/PatchageCanvas.cpp | 87 | ||||
-rw-r--r-- | src/PatchageCanvas.hpp | 8 | ||||
-rw-r--r-- | src/handle_event.cpp | 36 |
11 files changed, 303 insertions, 831 deletions
diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp index 293ee63..6e1cebb 100644 --- a/src/AlsaDriver.cpp +++ b/src/AlsaDriver.cpp @@ -20,8 +20,6 @@ #include "ClientInfo.hpp" #include "Patchage.hpp" #include "PatchageCanvas.hpp" -#include "PatchageModule.hpp" -#include "PatchagePort.hpp" #include "PortInfo.hpp" #include "PortType.hpp" #include "SignalDirection.hpp" @@ -33,6 +31,7 @@ PATCHAGE_DISABLE_FMT_WARNINGS PATCHAGE_RESTORE_WARNINGS #include <cassert> +#include <limits> #include <set> #include <string> #include <utility> @@ -85,9 +84,8 @@ port_info(const snd_seq_port_info_t* const pinfo) } // namespace -AlsaDriver::AlsaDriver(Patchage* app, ILog& log) - : _app(app) - , _log(log) +AlsaDriver::AlsaDriver(ILog& log) + : _log(log) , _seq(nullptr) , _refresh_thread{} {} @@ -136,32 +134,14 @@ AlsaDriver::detach() } } -static bool -is_alsa_port(const PatchagePort* port) -{ - return port->type() == PortType::alsa_midi; -} - void -AlsaDriver::destroy_all() +AlsaDriver::refresh(const EventSink& sink) { - _app->canvas()->remove_ports(is_alsa_port); - _modules.clear(); - _port_addrs.clear(); -} - -void -AlsaDriver::refresh() -{ - if (!is_attached()) { + if (!is_attached() || !_seq) { return; } - assert(_seq); - - _modules.clear(); _ignored.clear(); - _port_addrs.clear(); snd_seq_client_info_t* cinfo = nullptr; snd_seq_client_info_alloca(&cinfo); @@ -170,233 +150,74 @@ AlsaDriver::refresh() snd_seq_port_info_t* pinfo = nullptr; snd_seq_port_info_alloca(&pinfo); - // Create port views - { - PatchageModule* parent = nullptr; - PatchagePort* port = nullptr; - - while (snd_seq_query_next_client(_seq, cinfo) >= 0) { - snd_seq_port_info_set_client(pinfo, - snd_seq_client_info_get_client(cinfo)); - snd_seq_port_info_set_port(pinfo, -1); - while (snd_seq_query_next_port(_seq, pinfo) >= 0) { - const snd_seq_addr_t& addr = *snd_seq_port_info_get_addr(pinfo); - if (ignore(addr)) { - continue; - } + // Emit all clients + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(_seq, cinfo) >= 0) { + const auto client_id = snd_seq_client_info_get_client(cinfo); - create_port_view_internal(addr, parent, port); - } - } + assert(client_id < std::numeric_limits<uint8_t>::max()); + sink({ClientCreationEvent{ + ClientID::alsa(static_cast<uint8_t>(client_id)), + client_info(cinfo)}}); } - // Create connections + // Emit all ports snd_seq_client_info_set_client(cinfo, -1); while (snd_seq_query_next_client(_seq, cinfo) >= 0) { - snd_seq_port_info_set_client(pinfo, - snd_seq_client_info_get_client(cinfo)); + const auto client_id = snd_seq_client_info_get_client(cinfo); + + snd_seq_port_info_set_client(pinfo, client_id); snd_seq_port_info_set_port(pinfo, -1); while (snd_seq_query_next_port(_seq, pinfo) >= 0) { - const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(pinfo); - if (ignore(*addr)) { - continue; - } - - PatchagePort* const port1 = _app->canvas()->find_port( - PortID::alsa(addr->client, addr->port, false)); - if (!port1) { - continue; - } - - snd_seq_query_subscribe_t* subsinfo = nullptr; - snd_seq_query_subscribe_alloca(&subsinfo); - snd_seq_query_subscribe_set_root(subsinfo, addr); - snd_seq_query_subscribe_set_index(subsinfo, 0); - while (!snd_seq_query_port_subscribers(_seq, subsinfo)) { - const snd_seq_addr_t* addr2 = - snd_seq_query_subscribe_get_addr(subsinfo); - if (addr2) { - const PortID id2 = - PortID::alsa(addr2->client, addr2->port, true); - PatchagePort* port2 = _app->canvas()->find_port(id2); - if (port2 && !_app->canvas()->get_edge(port1, port2)) { - _app->canvas()->make_connection(port1, port2); - } + const auto addr = *snd_seq_port_info_get_addr(pinfo); + if (!ignore(addr)) { + const auto caps = snd_seq_port_info_get_capability(pinfo); + auto info = port_info(pinfo); + + if (caps & SND_SEQ_PORT_CAP_READ) { + info.direction = SignalDirection::input; + sink({PortCreationEvent{addr_to_id(addr, true), info}}); } - snd_seq_query_subscribe_set_index( - subsinfo, snd_seq_query_subscribe_get_index(subsinfo) + 1); + if (caps & SND_SEQ_PORT_CAP_WRITE) { + info.direction = SignalDirection::output; + sink({PortCreationEvent{addr_to_id(addr, false), info}}); + } } } } -} - -PatchagePort* -AlsaDriver::create_port_view(Patchage*, const PortID& id) -{ - PatchageModule* parent = nullptr; - PatchagePort* port = nullptr; - create_port_view_internal({id.alsa_client(), id.alsa_port()}, parent, port); - - return port; -} - -PatchageModule* -AlsaDriver::find_module(uint8_t client_id, SignalDirection type) -{ - const Modules::const_iterator i = _modules.find(client_id); - if (i == _modules.end()) { - return nullptr; - } - - PatchageModule* io_module = nullptr; - for (Modules::const_iterator j = i; - j != _modules.end() && j->first == client_id; - ++j) { - if (j->second->type() == type) { - return j->second; - } - if (j->second->type() == SignalDirection::duplex) { - io_module = j->second; - } - } - - // Return InputOutput module for Input or Output, or null if not found - return io_module; -} - -PatchageModule* -AlsaDriver::find_or_create_module(Patchage* patchage, - uint8_t client_id, - const std::string& client_name, - SignalDirection type) -{ - PatchageModule* m = find_module(client_id, type); - if (!m) { - m = new PatchageModule( - patchage, client_name, type, ClientID::alsa(client_id)); - m->load_location(); - _app->canvas()->add_module(ClientID::alsa(client_id), m); - _modules.insert(std::make_pair(client_id, m)); - } - return m; -} - -void -AlsaDriver::create_port_view_internal(snd_seq_addr_t addr, - PatchageModule*& parent, - PatchagePort*& port) -{ - if (ignore(addr)) { - return; - } - - snd_seq_client_info_t* cinfo = nullptr; - snd_seq_client_info_alloca(&cinfo); - snd_seq_client_info_set_client(cinfo, addr.client); - snd_seq_get_any_client_info(_seq, addr.client, cinfo); - - snd_seq_port_info_t* pinfo = nullptr; - snd_seq_port_info_alloca(&pinfo); - snd_seq_port_info_set_client(pinfo, addr.client); - snd_seq_port_info_set_port(pinfo, addr.port); - snd_seq_get_any_port_info(_seq, addr.client, addr.port, pinfo); - - const std::string client_name = snd_seq_client_info_get_name(cinfo); - const std::string port_name = snd_seq_port_info_get_name(pinfo); - bool is_input = false; - bool is_duplex = false; - bool is_application = true; - - const int caps = snd_seq_port_info_get_capability(pinfo); - const int type = snd_seq_port_info_get_type(pinfo); - - // Figure out direction - if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_WRITE)) { - is_duplex = true; - } else if (caps & SND_SEQ_PORT_CAP_READ) { - is_input = false; - } else if (caps & SND_SEQ_PORT_CAP_WRITE) { - is_input = true; - } - - is_application = (type & SND_SEQ_PORT_TYPE_APPLICATION); - - // Because there would be name conflicts, we must force a split if (stupid) - // alsa duplex ports are present on the client - bool split = false; - if (is_duplex) { - split = true; - if (!_app->conf()->get_module_split(client_name, !is_application)) { - _app->conf()->set_module_split(client_name, true); - } - } else { - split = _app->conf()->get_module_split(client_name, !is_application); - } - - const auto port_id = PortID::alsa(addr.client, addr.port, is_input); + // Emit all connections + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(_seq, cinfo) >= 0) { + const auto client_id = snd_seq_client_info_get_client(cinfo); - if (!split) { - parent = find_or_create_module( - _app, addr.client, client_name, SignalDirection::duplex); - if (!parent->get_port(port_id)) { - port = create_port(*parent, port_name, is_input, addr); - port->show(); - } + snd_seq_port_info_set_client(pinfo, client_id); + snd_seq_port_info_set_port(pinfo, -1); + while (snd_seq_query_next_port(_seq, pinfo) >= 0) { + const auto tail_addr = *snd_seq_port_info_get_addr(pinfo); + const auto tail_id = addr_to_id(tail_addr, false); + if (ignore(tail_addr)) { + continue; + } - } else { // split - { - const SignalDirection module_type = - ((is_input) ? SignalDirection::input : SignalDirection::output); + snd_seq_query_subscribe_t* sinfo = nullptr; + snd_seq_query_subscribe_alloca(&sinfo); + snd_seq_query_subscribe_set_root(sinfo, &tail_addr); + snd_seq_query_subscribe_set_index(sinfo, 0); + while (!snd_seq_query_port_subscribers(_seq, sinfo)) { + const auto head_addr = *snd_seq_query_subscribe_get_addr(sinfo); + const auto head_id = addr_to_id(head_addr, true); - parent = find_or_create_module( - _app, addr.client, client_name, module_type); - if (!parent->get_port(port_id)) { - port = create_port(*parent, port_name, is_input, addr); - port->show(); - } - } + sink({ConnectionEvent{tail_id, head_id}}); - if (is_duplex) { - const SignalDirection flipped_module_type = - ((!is_input) ? SignalDirection::input - : SignalDirection::output); - parent = find_or_create_module( - _app, addr.client, client_name, flipped_module_type); - if (!parent->get_port(port_id)) { - port = create_port(*parent, port_name, !is_input, addr); - port->show(); + snd_seq_query_subscribe_set_index( + sinfo, snd_seq_query_subscribe_get_index(sinfo) + 1); } } } } -PatchagePort* -AlsaDriver::create_port(PatchageModule& parent, - const std::string& name, - bool is_input, - snd_seq_addr_t addr) -{ - const PortID id = PortID::alsa(addr.client, addr.port, is_input); - - auto* ret = - new PatchagePort(parent, - PortType::alsa_midi, - id, - name, - "", - is_input, - _app->conf()->get_port_color(PortType::alsa_midi), - _app->show_human_names()); - - dynamic_cast<PatchageCanvas*>(parent.canvas())->index_port(id, ret); - - _app->canvas()->index_port(id, ret); - _port_addrs.insert(std::make_pair(ret, id)); - return ret; -} - bool AlsaDriver::ignore(const snd_seq_addr_t& addr, bool add) { @@ -646,11 +467,6 @@ AlsaDriver::_refresh_main() PortDestructionEvent{addr_to_id(ev->data.addr, true)}); _events.emplace( PortDestructionEvent{addr_to_id(ev->data.addr, false)}); - - _port_addrs.erase(_app->canvas()->find_port( - addr_to_id(ev->data.addr, false))); - _port_addrs.erase( - _app->canvas()->find_port(addr_to_id(ev->data.addr, true))); } break; diff --git a/src/AlsaDriver.hpp b/src/AlsaDriver.hpp index caaaf39..15a5086 100644 --- a/src/AlsaDriver.hpp +++ b/src/AlsaDriver.hpp @@ -18,7 +18,6 @@ #define PATCHAGE_ALSADRIVER_HPP #include "Driver.hpp" -#include "PatchageModule.hpp" #include <alsa/asoundlib.h> #include <pthread.h> @@ -31,13 +30,12 @@ class ILog; class Patchage; -class PatchagePort; /// Driver for ALSA Sequencer ports class AlsaDriver : public Driver { public: - explicit AlsaDriver(Patchage* app, ILog& log); + explicit AlsaDriver(ILog& log); AlsaDriver(const AlsaDriver&) = delete; AlsaDriver& operator=(const AlsaDriver&) = delete; @@ -52,11 +50,7 @@ public: bool is_attached() const override { return (_seq != nullptr); } - void refresh() override; - void destroy_all() override; - - PatchagePort* - create_port_view(Patchage* patchage, const PortID& id) override; + void refresh(const EventSink& sink) override; bool connect(PortID tail_id, PortID head_id) override; bool disconnect(PortID tail_id, PortID head_id) override; @@ -70,23 +64,6 @@ private: static void* refresh_main(void* me); void _refresh_main(); - PatchageModule* find_module(uint8_t client_id, SignalDirection type); - - PatchageModule* find_or_create_module(Patchage* patchage, - uint8_t client_id, - const std::string& client_name, - SignalDirection type); - - void create_port_view_internal(snd_seq_addr_t addr, - PatchageModule*& parent, - PatchagePort*& port); - - PatchagePort* create_port(PatchageModule& parent, - const std::string& name, - bool is_input, - snd_seq_addr_t addr); - - Patchage* _app; ILog& _log; snd_seq_t* _seq; pthread_t _refresh_thread; @@ -103,13 +80,9 @@ private: } }; - using Ignored = std::set<snd_seq_addr_t, SeqAddrComparator>; - using Modules = std::multimap<uint8_t, PatchageModule*>; - using PortAddrs = std::map<PatchagePort*, PortID>; + using Ignored = std::set<snd_seq_addr_t, SeqAddrComparator>; - Ignored _ignored; - Modules _modules; - PortAddrs _port_addrs; + Ignored _ignored; bool ignore(const snd_seq_addr_t& addr, bool add = true); }; diff --git a/src/Driver.hpp b/src/Driver.hpp index 116b29f..d40f7b5 100644 --- a/src/Driver.hpp +++ b/src/Driver.hpp @@ -21,16 +21,16 @@ #include <sigc++/sigc++.h> -#include <string> +#include <functional> class Patchage; -class PatchagePort; -class PatchageCanvas; /// Base class for drivers that handle system clients and ports class Driver { public: + using EventSink = std::function<void(const PatchageEvent&)>; + Driver() = default; Driver(const Driver&) = delete; @@ -47,11 +47,7 @@ public: virtual void detach() = 0; virtual bool is_attached() const = 0; - virtual void refresh() = 0; - virtual void destroy_all() = 0; - - virtual PatchagePort* - create_port_view(Patchage* patchage, const PortID& id) = 0; + virtual void refresh(const EventSink& sink) = 0; virtual bool connect(PortID tail_id, PortID head_id) = 0; virtual bool disconnect(PortID tail_id, PortID head_id) = 0; diff --git a/src/JackDbusDriver.cpp b/src/JackDbusDriver.cpp index 81d6224..85dfae3 100644 --- a/src/JackDbusDriver.cpp +++ b/src/JackDbusDriver.cpp @@ -20,14 +20,12 @@ #include "patchage_config.h" #include "Driver.hpp" -#include "Patchage.hpp" -#include "PatchageCanvas.hpp" #include "PatchageEvent.hpp" -#include "PatchageModule.hpp" -#include "PatchagePort.hpp" #include "PortNames.hpp" #include "PortType.hpp" #include "SignalDirection.hpp" +#include "handle_event.hpp" +#include "warnings.hpp" PATCHAGE_DISABLE_FMT_WARNINGS #include <fmt/core.h> @@ -57,19 +55,8 @@ PATCHAGE_RESTORE_WARNINGS #define JACKDBUS_PORT_TYPE_AUDIO 0 #define JACKDBUS_PORT_TYPE_MIDI 1 -namespace { - -std::string -full_name(const std::string& client_name, const std::string& port_name) -{ - return client_name + ":" + port_name; -} - -} // namespace - -JackDriver::JackDriver(Patchage* app, ILog& log) - : _app(app) - , _log(log) +JackDriver::JackDriver(ILog& log) + : _log(log) , _dbus_error() , _dbus_connection(nullptr) , _max_dsp_load(0.0f) @@ -91,19 +78,6 @@ JackDriver::~JackDriver() } } -static bool -is_jack_port(const PatchagePort* port) -{ - return port->type() == PortType::jack_audio || - port->type() == PortType::jack_midi; -} - -void -JackDriver::destroy_all() -{ - _app->canvas()->remove_ports(is_jack_port); -} - void JackDriver::update_attached() { @@ -236,8 +210,9 @@ JackDriver::dbus_message_hook(DBusConnection* /*connection*/, me->signal_attached.emit(); } - me->add_port( - client_id, client_name, port_id, port_name, port_flags, port_type); + me->_events.emplace( + PortCreationEvent{PortID::jack(client_name, port_name), + me->port_info(port_name, port_type, port_flags)}); return DBUS_HANDLER_RESULT_HANDLED; } @@ -270,7 +245,8 @@ JackDriver::dbus_message_hook(DBusConnection* /*connection*/, me->signal_attached.emit(); } - me->remove_port(client_id, client_name, port_id, port_name); + me->_events.emplace( + PortDestructionEvent{PortID::jack(client_name, port_name)}); return DBUS_HANDLER_RESULT_HANDLED; } @@ -313,15 +289,9 @@ JackDriver::dbus_message_hook(DBusConnection* /*connection*/, me->signal_attached.emit(); } - me->connect_ports(connection_id, - client_id, - client_name, - port_id, - port_name, - client2_id, - client2_name, - port2_id, - port2_name); + me->_events.emplace( + ConnectionEvent{PortID::jack(client_name, port_name), + PortID::jack(client2_name, port2_name)}); return DBUS_HANDLER_RESULT_HANDLED; } @@ -364,15 +334,9 @@ JackDriver::dbus_message_hook(DBusConnection* /*connection*/, me->signal_attached.emit(); } - me->disconnect_ports(connection_id, - client_id, - client_name, - port_id, - port_name, - client2_id, - client2_name, - port2_id, - port2_name); + me->_events.emplace( + DisconnectionEvent{PortID::jack(client_name, port_name), + PortID::jack(client2_name, port2_name)}); return DBUS_HANDLER_RESULT_HANDLED; } @@ -488,15 +452,11 @@ JackDriver::stop_server() "StopServer", &reply_ptr, DBUS_TYPE_INVALID)) { - return; + error_msg("Error stopping JACK server"); } dbus_message_unref(reply_ptr); - - if (!_server_started) { - _server_started = false; - signal_detached.emit(); - } + signal_detached.emit(); } void @@ -563,179 +523,7 @@ JackDriver::is_attached() const } void -JackDriver::add_port(PatchageModule* module, - PortType type, - const PortID& id, - const std::string& name, - bool is_input) -{ - if (module->get_port(id)) { - return; - } - - auto* port = new PatchagePort(*module, - type, - id, - name, - "", // TODO: pretty name - is_input, - _app->conf()->get_port_color(type), - _app->show_human_names()); - - _app->canvas()->index_port(id, port); -} - -void -JackDriver::add_port(dbus_uint64_t /*client_id*/, - const char* client_name, - dbus_uint64_t /*port_id*/, - const char* port_name, - dbus_uint32_t port_flags, - dbus_uint32_t port_type) -{ - PortType local_port_type; - - switch (port_type) { - case JACKDBUS_PORT_TYPE_AUDIO: - local_port_type = PortType::jack_audio; - break; - case JACKDBUS_PORT_TYPE_MIDI: - local_port_type = PortType::jack_midi; - break; - default: - error_msg("Unknown JACK D-Bus port type"); - return; - } - - SignalDirection type = SignalDirection::duplex; - if (_app->conf()->get_module_split( - client_name, port_flags & JACKDBUS_PORT_FLAG_TERMINAL)) { - if (port_flags & JACKDBUS_PORT_FLAG_INPUT) { - type = SignalDirection::input; - } else { - type = SignalDirection::output; - } - } - - PatchageModule* module = find_or_create_module(type, client_name); - - add_port(module, - local_port_type, - PortID::jack(full_name(client_name, port_name)), - port_name, - port_flags & JACKDBUS_PORT_FLAG_INPUT); -} - -void -JackDriver::remove_port(dbus_uint64_t /*client_id*/, - const char* client_name, - dbus_uint64_t /*port_id*/, - const char* port_name) -{ - const auto port_id = PortID::jack(client_name, port_name); - PatchagePort* const port = _app->canvas()->find_port(port_id); - if (!port) { - error_msg("Unable to remove unknown port"); - return; - } - - auto* module = dynamic_cast<PatchageModule*>(port->get_module()); - - delete port; - - // No empty modules (for now) - if (module->num_ports() == 0) { - delete module; - } - - if (_app->canvas()->empty()) { - if (_server_started) { - signal_detached.emit(); - } - - _server_started = false; - } -} - -PatchageModule* -JackDriver::find_or_create_module(SignalDirection type, const std::string& name) -{ - const auto id = ClientID::jack(name); - PatchageModule* module = _app->canvas()->find_module(id, type); - - if (!module) { - module = new PatchageModule(_app, name, type, id); - module->load_location(); - _app->canvas()->add_module(id, module); - } - - return module; -} - -void -JackDriver::connect_ports(dbus_uint64_t /*connection_id*/, - dbus_uint64_t /*client1_id*/, - const char* client1_name, - dbus_uint64_t /*port1_id*/, - const char* port1_name, - dbus_uint64_t /*client2_id*/, - const char* client2_name, - dbus_uint64_t /*port2_id*/, - const char* port2_name) -{ - const auto tail_id = PortID::jack(client1_name, port1_name); - const auto head_id = PortID::jack(client2_name, port2_name); - - PatchagePort* const tail = _app->canvas()->find_port(tail_id); - if (!tail) { - error_msg( - fmt::format("Unable to connect unknown port \"{}\"", tail_id)); - return; - } - - PatchagePort* const head = _app->canvas()->find_port(head_id); - if (!head) { - error_msg( - fmt::format("Unable to connect unknown port \"{}\"", head_id)); - return; - } - - _app->canvas()->make_connection(tail, head); -} - -void -JackDriver::disconnect_ports(dbus_uint64_t /*connection_id*/, - dbus_uint64_t /*client1_id*/, - const char* client1_name, - dbus_uint64_t /*port1_id*/, - const char* port1_name, - dbus_uint64_t /*client2_id*/, - const char* client2_name, - dbus_uint64_t /*port2_id*/, - const char* port2_name) -{ - const auto tail_id = PortID::jack(client1_name, port1_name); - const auto head_id = PortID::jack(client2_name, port2_name); - - PatchagePort* const tail = _app->canvas()->find_port(tail_id); - if (!tail) { - error_msg( - fmt::format("Unable to disconnect unknown port \"{}\"", tail_id)); - return; - } - - PatchagePort* const head = _app->canvas()->find_port(head_id); - if (!head) { - error_msg( - fmt::format("Unable to disconnect unknown port \"{}\"", head_id)); - return; - } - - _app->canvas()->remove_edge_between(tail, head); -} - -void -JackDriver::refresh() +JackDriver::refresh(const EventSink& sink) { DBusMessage* reply_ptr = nullptr; DBusMessageIter iter = {}; @@ -784,10 +572,9 @@ JackDriver::refresh() dbus_message_iter_get_basic(&iter, &version); dbus_message_iter_next(&iter); - destroy_all(); - _graph_version = version; + // Emit all clients and ports for (dbus_message_iter_recurse(&iter, &clients_array_iter); dbus_message_iter_get_arg_type(&clients_array_iter) != DBUS_TYPE_INVALID; @@ -800,6 +587,9 @@ JackDriver::refresh() dbus_message_iter_get_basic(&client_struct_iter, &client_name); dbus_message_iter_next(&client_struct_iter); + // TODO: Pretty name? + sink({ClientCreationEvent{ClientID::jack(client_name), {client_name}}}); + for (dbus_message_iter_recurse(&client_struct_iter, &ports_array_iter); dbus_message_iter_get_arg_type(&ports_array_iter) != DBUS_TYPE_INVALID; @@ -818,12 +608,9 @@ JackDriver::refresh() dbus_message_iter_get_basic(&port_struct_iter, &port_type); dbus_message_iter_next(&port_struct_iter); - add_port(client_id, - client_name, - port_id, - port_name, - port_flags, - port_type); + sink({PortCreationEvent{ + PortID::jack(client_name, port_name), + port_info(port_name, port_type, port_flags)}}); } dbus_message_iter_next(&client_struct_iter); @@ -831,6 +618,7 @@ JackDriver::refresh() dbus_message_iter_next(&iter); + // Emit all connections for (dbus_message_iter_recurse(&iter, &connections_array_iter); dbus_message_iter_get_arg_type(&connections_array_iter) != DBUS_TYPE_INVALID; @@ -865,15 +653,8 @@ JackDriver::refresh() dbus_message_iter_get_basic(&connection_struct_iter, &connection_id); dbus_message_iter_next(&connection_struct_iter); - connect_ports(connection_id, - client_id, - client_name, - port_id, - port_name, - client2_id, - client2_name, - port2_id, - port2_name); + sink({ConnectionEvent{PortID::jack(client_name, port_name), + PortID::jack(client2_name, port2_name)}}); } } @@ -1155,11 +936,37 @@ JackDriver::reset_max_dsp_load() _max_dsp_load = 0.0; } -PatchagePort* -JackDriver::create_port_view(Patchage*, const PortID&) +PortType +JackDriver::patchage_port_type(const dbus_uint32_t dbus_port_type) const +{ + switch (dbus_port_type) { + case JACKDBUS_PORT_TYPE_AUDIO: + return PortType::jack_audio; + case JACKDBUS_PORT_TYPE_MIDI: + return PortType::jack_midi; + default: + break; + } + + error_msg(fmt::format("Unknown JACK D-Bus port type {}", dbus_port_type)); + return PortType::jack_audio; +} + +PortInfo +JackDriver::port_info(const std::string& port_name, + const dbus_uint32_t port_type, + const dbus_uint32_t port_flags) const { - assert(false); // we dont use events at all - return nullptr; + const SignalDirection direction = + ((port_flags & JACKDBUS_PORT_FLAG_INPUT) ? SignalDirection::input + : SignalDirection::output); + + // TODO: Metadata? + return {port_name, + patchage_port_type(port_type), + direction, + {}, + bool(port_flags & JACKDBUS_PORT_FLAG_TERMINAL)}; } void @@ -1173,3 +980,13 @@ JackDriver::info_msg(const std::string& msg) const { _log.info(std::string{"[JACK] "} + msg); } + +void +JackDriver::process_events(Patchage* app) +{ + while (!_events.empty()) { + PatchageEvent& ev = _events.front(); + handle_event(*app, ev); + _events.pop(); + } +} diff --git a/src/JackDbusDriver.hpp b/src/JackDbusDriver.hpp index 37a7877..db77658 100644 --- a/src/JackDbusDriver.hpp +++ b/src/JackDbusDriver.hpp @@ -20,23 +20,21 @@ #include "Driver.hpp" #include "Patchage.hpp" -#include "PatchageModule.hpp" #include <dbus/dbus.h> #include <glibmm/thread.h> #include <jack/jack.h> #include <jack/statistics.h> +#include <queue> #include <string> class ILog; -class PatchageCanvas; -class PatchagePort; class JackDriver : public Driver { public: - explicit JackDriver(Patchage* app, ILog& log); + explicit JackDriver(ILog& log); JackDriver(const JackDriver&) = delete; JackDriver& operator=(const JackDriver&) = delete; @@ -52,8 +50,7 @@ public: bool is_attached() const override; bool is_realtime() const; - void refresh() override; - void destroy_all() override; + void refresh(const EventSink& sink) override; bool connect(PortID tail_id, PortID head_id) override; bool disconnect(PortID tail_id, PortID head_id) override; @@ -67,56 +64,18 @@ public: jack_nframes_t buffer_size(); bool set_buffer_size(jack_nframes_t size); - void process_events(Patchage*) override {} - - PatchagePort* - create_port_view(Patchage* patchage, const PortID& ref) override; + void process_events(Patchage* app) override; private: + PortType patchage_port_type(dbus_uint32_t dbus_port_type) const; + + PortInfo port_info(const std::string& port_name, + dbus_uint32_t port_type, + dbus_uint32_t port_flags) const; + void error_msg(const std::string& msg) const; void info_msg(const std::string& msg) const; - PatchageModule* - find_or_create_module(SignalDirection type, const std::string& name); - - void add_port(PatchageModule* module, - PortType type, - const PortID& id, - const std::string& name, - bool is_input); - - void add_port(dbus_uint64_t client_id, - const char* client_name, - dbus_uint64_t port_id, - const char* port_name, - dbus_uint32_t port_flags, - dbus_uint32_t port_type); - - void remove_port(dbus_uint64_t client_id, - const char* client_name, - dbus_uint64_t port_id, - const char* port_name); - - void connect_ports(dbus_uint64_t connection_id, - dbus_uint64_t client1_id, - const char* client1_name, - dbus_uint64_t port1_id, - const char* port1_name, - dbus_uint64_t client2_id, - const char* client2_name, - dbus_uint64_t port2_id, - const char* port2_name); - - void disconnect_ports(dbus_uint64_t connection_id, - dbus_uint64_t client1_id, - const char* client1_name, - dbus_uint64_t port1_id, - const char* port1_name, - dbus_uint64_t client2_id, - const char* client2_name, - dbus_uint64_t port2_id, - const char* port2_name); - bool call(bool response_expected, const char* iface, const char* method, @@ -140,12 +99,13 @@ private: void on_jack_disappeared(); - Patchage* _app; ILog& _log; DBusError _dbus_error; DBusConnection* _dbus_connection; float _max_dsp_load; + std::queue<PatchageEvent> _events; + bool _server_responding; bool _server_started; diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp index a491453..cfcc541 100644 --- a/src/JackDriver.cpp +++ b/src/JackDriver.cpp @@ -21,8 +21,6 @@ #include "Patchage.hpp" #include "PatchageCanvas.hpp" #include "PatchageEvent.hpp" -#include "PatchageModule.hpp" -#include "PatchagePort.hpp" #include "PortNames.hpp" #include "PortType.hpp" #include "SignalDirection.hpp" @@ -45,10 +43,10 @@ PATCHAGE_RESTORE_WARNINGS #include <cstring> #include <set> #include <string> +#include <unordered_set> -JackDriver::JackDriver(Patchage* app, ILog& log) - : _app(app) - , _log(log) +JackDriver::JackDriver(ILog& log) + : _log(log) , _client(nullptr) , _last_pos{} , _buffer_size(0) @@ -119,78 +117,6 @@ JackDriver::detach() _log.info("[JACK] Detached"); } -static bool -is_jack_port(const PatchagePort* port) -{ - return (port->type() == PortType::jack_audio || - port->type() == PortType::jack_midi || - port->type() == PortType::jack_osc || - port->type() == PortType::jack_cv); -} - -void -JackDriver::destroy_all() -{ - if (_app->canvas()) { - _app->canvas()->remove_ports(is_jack_port); - } -} - -PatchagePort* -JackDriver::create_port_view(Patchage* patchage, const PortID& id) -{ - assert(id.type() == PortID::Type::jack); - - const auto client_id = id.client(); - - jack_port_t* const jack_port = - jack_port_by_name(_client, id.jack_name().c_str()); - if (!jack_port) { - _log.error(fmt::format("[JACK] Failed to find port with name \"{}\"", - id.jack_name())); - return nullptr; - } - - const int jack_flags = jack_port_flags(jack_port); - - std::string module_name; - std::string port_name; - port_names(id, module_name, port_name); - - SignalDirection type = SignalDirection::duplex; - if (_app->conf()->get_module_split(module_name, - (jack_flags & JackPortIsTerminal))) { - if (jack_flags & JackPortIsInput) { - type = SignalDirection::input; - } else { - type = SignalDirection::output; - } - } - - PatchageModule* parent = _app->canvas()->find_module(client_id, type); - if (!parent) { - parent = new PatchageModule( - patchage, module_name, type, ClientID::jack(module_name)); - parent->load_location(); - patchage->canvas()->add_module(client_id, parent); - } - - if (parent->get_port(id)) { - _log.error(fmt::format("[JACK] Module \"{}\" already has port \"{}\"", - module_name, - port_name)); - return nullptr; - } - - PatchagePort* port = create_port(*parent, jack_port, id); - port->show(); - if (port->is_input()) { - parent->set_is_source(false); - } - - return port; -} - static std::string get_property(const jack_uuid_t subject, const char* const key) { @@ -266,32 +192,6 @@ JackDriver::get_port_info(const jack_port_t* const port) return {label, type, direction, order, bool(flags & JackPortIsTerminal)}; } -PatchagePort* -JackDriver::create_port(PatchageModule& parent, - jack_port_t* port, - const PortID& id) -{ - if (!port) { - return nullptr; - } - - const auto info = get_port_info(port); - - auto* ret = new PatchagePort(parent, - info.type, - id, - jack_port_short_name(port), - info.label, - (jack_port_flags(port) & JackPortIsInput), - _app->conf()->get_port_color(info.type), - _app->show_human_names(), - info.order); - - _app->canvas()->index_port(id, ret); - - return ret; -} - void JackDriver::shutdown() { @@ -299,133 +199,67 @@ JackDriver::shutdown() } void -JackDriver::refresh() +JackDriver::refresh(const EventSink& sink) { - jack_port_t* port = nullptr; - - // Jack can take _client away from us at any time throughout here :/ - // Shortest locks possible is the best solution I can figure out - std::lock_guard<std::mutex> lock{_shutdown_mutex}; - if (_client == nullptr) { + if (!_client) { shutdown(); return; } // Get all existing ports - const char** ports = jack_get_ports(_client, nullptr, nullptr, 0); - + const char** const ports = jack_get_ports(_client, nullptr, nullptr, 0); if (!ports) { return; } - std::string client1_name; - std::string port1_name; - std::string client2_name; - std::string port2_name; - size_t colon = std::string::npos; - - // Add all ports - for (int i = 0; ports[i]; ++i) { - port = jack_port_by_name(_client, ports[i]); - - client1_name = ports[i]; - client1_name = client1_name.substr(0, client1_name.find(':')); - - SignalDirection type = SignalDirection::duplex; - if (_app->conf()->get_module_split( - client1_name, (jack_port_flags(port) & JackPortIsTerminal))) { - if (jack_port_flags(port) & JackPortIsInput) { - type = SignalDirection::input; - } else { - type = SignalDirection::output; - } - } - - const auto port1_id = PortID::jack(ports[i]); - const auto client1_id = ClientID::jack(client1_name); - - PatchageModule* m = _app->canvas()->find_module(client1_id, type); - - if (!m) { - m = new PatchageModule(_app, client1_name, type, client1_id); - m->load_location(); - _app->canvas()->add_module(client1_id, m); - } - - if (!m->get_port(port1_id)) { - create_port(*m, port, PortID::jack(ports[i])); - } + // Get all client names (to only send a creation event once for each) + std::unordered_set<std::string> client_names; + for (auto i = 0u; ports[i]; ++i) { + client_names.insert(PortID::jack(ports[i]).client().jack_name()); } - // Add all connections - for (int i = 0; ports[i]; ++i) { - port = jack_port_by_name(_client, ports[i]); - const char** connected_ports = - jack_port_get_all_connections(_client, port); - - client1_name = ports[i]; - colon = client1_name.find(':'); - port1_name = client1_name.substr(colon + 1); - client1_name = client1_name.substr(0, colon); - - const SignalDirection port1_type = - (jack_port_flags(port) & JackPortIsInput) ? SignalDirection::input - : SignalDirection::output; - - const auto port1_id = PortID::jack(ports[i]); - const auto client1_id = ClientID::jack(client1_name); - - PatchageModule* client1_module = - _app->canvas()->find_module(client1_id, port1_type); - - if (connected_ports) { - for (int j = 0; connected_ports[j]; ++j) { - - client2_name = connected_ports[j]; - colon = client2_name.find(':'); - port2_name = client2_name.substr(colon + 1); - client2_name = client2_name.substr(0, colon); - - const auto port2_id = PortID::jack(connected_ports[j]); - const auto client2_id = ClientID::jack(client2_name); - - const SignalDirection port2_type = - (port1_type == SignalDirection::input) - ? SignalDirection::output - : SignalDirection::input; - - PatchageModule* client2_module = - _app->canvas()->find_module(client2_id, port2_type); + // Emit all clients + for (const auto& client_name : client_names) { + sink({ClientCreationEvent{ClientID::jack(client_name), + get_client_info(client_name.c_str())}}); + } - Ganv::Port* port1 = client1_module->get_port(port1_id); - Ganv::Port* port2 = client2_module->get_port(port2_id); + // Emit all ports + for (auto i = 0u; ports[i]; ++i) { + const jack_port_t* const port = jack_port_by_name(_client, ports[i]); - if (!port1 || !port2) { - continue; - } + sink({PortCreationEvent{PortID::jack(ports[i]), get_port_info(port)}}); + } - Ganv::Port* src = nullptr; - Ganv::Port* dst = nullptr; + // Get all connections (again to only create them once) + std::set<std::pair<std::string, std::string>> connections; + for (auto i = 0u; ports[i]; ++i) { + const jack_port_t* const port = jack_port_by_name(_client, ports[i]); + const char** const peers = jack_port_get_all_connections(_client, port); - if (port1->is_output() && port2->is_input()) { - src = port1; - dst = port2; - } else { - src = port2; - dst = port1; + if (peers) { + if (jack_port_flags(port) & JackPortIsInput) { + for (auto j = 0u; peers[j]; ++j) { + connections.emplace(peers[j], ports[i]); } - - if (src && dst && !_app->canvas()->get_edge(src, dst)) { - _app->canvas()->make_connection(src, dst); + } else { + for (auto j = 0u; peers[j]; ++j) { + connections.emplace(ports[i], peers[j]); } } - jack_free(connected_ports); + jack_free(peers); } } + // Emit all connections + for (const auto& connection : connections) { + sink({ConnectionEvent{PortID::jack(connection.first), + PortID::jack(connection.second)}}); + } + jack_free(ports); } diff --git a/src/JackDriver.hpp b/src/JackDriver.hpp index 6e41fbb..1e680a0 100644 --- a/src/JackDriver.hpp +++ b/src/JackDriver.hpp @@ -31,15 +31,12 @@ class ILog; class Patchage; -class PatchageCanvas; -class PatchageModule; -class PatchagePort; /// Driver for JACK audio and midi ports class JackDriver : public Driver { public: - explicit JackDriver(Patchage* app, ILog& log); + explicit JackDriver(ILog& log); JackDriver(const JackDriver&) = delete; JackDriver& operator=(const JackDriver&) = delete; @@ -56,16 +53,12 @@ public: bool is_realtime() const { return _client && jack_is_realtime(_client); } - void refresh() override; - void destroy_all() override; + void refresh(const EventSink& sink) override; bool port_names(const PortID& id, std::string& module_name, std::string& port_name); - PatchagePort* - create_port_view(Patchage* patchage, const PortID& id) override; - bool connect(PortID tail_id, PortID head_id) override; bool disconnect(PortID tail_id, PortID head_id) override; @@ -86,9 +79,6 @@ private: ClientInfo get_client_info(const char* name); PortInfo get_port_info(const jack_port_t* port); - PatchagePort* - create_port(PatchageModule& parent, jack_port_t* port, const PortID& id); - void shutdown(); static void jack_client_registration_cb(const char* name, @@ -108,7 +98,6 @@ private: static void jack_shutdown_cb(void* jack_driver); - Patchage* _app; ILog& _log; jack_client_t* _client; diff --git a/src/Patchage.cpp b/src/Patchage.cpp index 7613935..e0b5dc7 100644 --- a/src/Patchage.cpp +++ b/src/Patchage.cpp @@ -22,6 +22,8 @@ #include "PatchageEvent.hpp" #include "PatchagePort.hpp" #include "UIFile.hpp" +#include "event_to_string.hpp" +#include "handle_event.hpp" #include "patchage_config.h" #include "warnings.hpp" @@ -342,7 +344,7 @@ Patchage::Patchage(int argc, char** argv) #endif #if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) - _jack_driver = new JackDriver(this, _log); + _jack_driver = new JackDriver(_log); _connector.add_driver(PortID::Type::jack, _jack_driver); _jack_driver->signal_detached.connect( @@ -355,7 +357,7 @@ Patchage::Patchage(int argc, char** argv) #endif #ifdef HAVE_ALSA - _alsa_driver = new AlsaDriver(this, _log); + _alsa_driver = new AlsaDriver(_log); _connector.add_driver(PortID::Type::alsa, _alsa_driver); #endif @@ -461,12 +463,19 @@ Patchage::idle_callback() } else if (_driver_detached) { #if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) if (_jack_driver && !_jack_driver->is_attached()) { - _jack_driver->destroy_all(); + _canvas->remove_ports([](const PatchagePort* port) { + return (port->type() == PortType::jack_audio || + port->type() == PortType::jack_midi || + port->type() == PortType::jack_osc || + port->type() == PortType::jack_cv); + }); } #endif #ifdef HAVE_ALSA if (_alsa_driver && !_alsa_driver->is_attached()) { - _alsa_driver->destroy_all(); + _canvas->remove_ports([](const PatchagePort* port) { + return port->type() == PortType::alsa_midi; + }); } #endif } @@ -542,18 +551,22 @@ Patchage::zoom(double z) void Patchage::refresh() { + auto sink = [this](const PatchageEvent& event) { + handle_event(*this, event); + }; + if (_canvas && _enable_refresh) { _canvas->clear(); #if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) if (_jack_driver) { - _jack_driver->refresh(); + _jack_driver->refresh(sink); } #endif #ifdef HAVE_ALSA if (_alsa_driver) { - _alsa_driver->refresh(); + _alsa_driver->refresh(sink); } #endif } @@ -762,7 +775,8 @@ void Patchage::menu_alsa_connect() { _alsa_driver->attach(false); - _alsa_driver->refresh(); + _alsa_driver->refresh( + [this](const PatchageEvent& event) { handle_event(*this, event); }); } void diff --git a/src/PatchageCanvas.cpp b/src/PatchageCanvas.cpp index b94806d..bb9c484 100644 --- a/src/PatchageCanvas.cpp +++ b/src/PatchageCanvas.cpp @@ -19,8 +19,10 @@ #include "patchage_config.h" #include "Connector.hpp" +#include "Patchage.hpp" #include "PatchageModule.hpp" #include "PatchagePort.hpp" +#include "PortNames.hpp" #include "SignalDirection.hpp" #include "warnings.hpp" @@ -30,6 +32,11 @@ PATCHAGE_DISABLE_GANV_WARNINGS #include "ganv/Edge.hpp" PATCHAGE_RESTORE_WARNINGS +PATCHAGE_DISABLE_FMT_WARNINGS +#include <fmt/core.h> +#include <fmt/ostream.h> +PATCHAGE_RESTORE_WARNINGS + PatchageCanvas::PatchageCanvas(Connector& connector, int width, int height) : Ganv::Canvas(width, height) , _connector(connector) @@ -41,6 +48,84 @@ PatchageCanvas::PatchageCanvas(Connector& connector, int width, int height) } PatchageModule* +PatchageCanvas::create_module(Patchage& patchage, + const ClientID& id, + const ClientInfo& info) +{ + (void)patchage; + (void)id; + (void)info; + return nullptr; +} + +PatchagePort* +PatchageCanvas::create_port(Patchage& patchage, + const PortID& id, + const PortInfo& info) +{ + const auto client_id = id.client(); + + const auto port_name = + ((id.type() == PortID::Type::alsa) ? info.label : PortNames(id).port()); + + // Figure out the client name, for ALSA we need the metadata cache + std::string client_name; + if (id.type() == PortID::Type::alsa) { + const auto client_info = patchage.metadata().client(client_id); + if (!client_info.has_value()) { + patchage.log().error(fmt::format( + "Unable to add port \"{}\", client \"{}\" is unknown", + id, + client_id)); + + return nullptr; + } + + client_name = client_info->label; + } else { + client_name = PortNames(id).client(); + } + + // Determine the module type to place the port on in case of splitting + SignalDirection module_type = SignalDirection::duplex; + if (patchage.conf()->get_module_split(client_name, info.is_terminal)) { + module_type = info.direction; + } + + // Find or create parent module + PatchageModule* parent = find_module(client_id, module_type); + if (!parent) { + parent = + new PatchageModule(&patchage, client_name, module_type, client_id); + + parent->load_location(); + add_module(client_id, parent); + } + + if (parent->get_port(id)) { + // TODO: Update existing port? + patchage.log().error(fmt::format( + "Module \"{}\" already has port \"{}\"", client_name, port_name)); + return nullptr; + } + + auto* const port = + new PatchagePort(*parent, + info.type, + id, + port_name, + info.label, + info.direction == SignalDirection::input, + patchage.conf()->get_port_color(info.type), + patchage.show_human_names(), + info.order); + + index_port(id, port); + + return port; +} + +PatchageModule* PatchageCanvas::find_module(const ClientID& id, const SignalDirection type) { auto i = _module_index.find(id); @@ -199,7 +284,7 @@ PatchageCanvas::add_module(const ClientID& id, PatchageModule* module) in_module = module; out_module = find_module(id, SignalDirection::output); } else if (module->type() == SignalDirection::output) { - in_module = find_module(id, SignalDirection::output); + in_module = find_module(id, SignalDirection::input); out_module = module; } diff --git a/src/PatchageCanvas.hpp b/src/PatchageCanvas.hpp index 0debcf8..073b3fb 100644 --- a/src/PatchageCanvas.hpp +++ b/src/PatchageCanvas.hpp @@ -36,6 +36,7 @@ PATCHAGE_RESTORE_WARNINGS #include <string> #include <utility> +class Patchage; class PatchageModule; class PatchagePort; class Connector; @@ -45,6 +46,13 @@ class PatchageCanvas : public Ganv::Canvas public: PatchageCanvas(Connector& connector, int width, int height); + PatchageModule* create_module(Patchage& patchage, + const ClientID& id, + const ClientInfo& info); + + PatchagePort* + create_port(Patchage& patchage, const PortID& id, const PortInfo& info); + PatchageModule* find_module(const ClientID& id, SignalDirection type); PatchagePort* find_port(const PortID& id); diff --git a/src/handle_event.cpp b/src/handle_event.cpp index 2bd3609..200f819 100644 --- a/src/handle_event.cpp +++ b/src/handle_event.cpp @@ -15,26 +15,19 @@ */ #include "handle_event.hpp" +#include "event_to_string.hpp" #include "PatchageEvent.hpp" #include "patchage_config.h" -#include "Driver.hpp" +#include "event_to_string.hpp" + #include "Patchage.hpp" #include "PatchageCanvas.hpp" #include "PatchageModule.hpp" #include "PatchagePort.hpp" -#if defined(HAVE_JACK_DBUS) -# include "JackDbusDriver.hpp" -#elif defined(PATCHAGE_LIBJACK) -# include "JackDriver.hpp" -#endif -#ifdef HAVE_ALSA -# include "AlsaDriver.hpp" -#endif - PATCHAGE_DISABLE_FMT_WARNINGS #include <fmt/core.h> #include <fmt/ostream.h> @@ -65,27 +58,14 @@ public: void operator()(const PortCreationEvent& event) { - Driver* driver = nullptr; - if (event.id.type() == PortID::Type::jack) { -#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) - driver = _patchage.jack_driver(); -#endif -#ifdef HAVE_ALSA - } else if (event.id.type() == PortID::Type::alsa) { - driver = _patchage.alsa_driver(); -#endif - } _patchage.metadata().set_port(event.id, event.info); - if (driver) { - PatchagePort* port = driver->create_port_view(&_patchage, event.id); - if (!port) { - _patchage.log().error(fmt::format( - "Unable to create view for port \"{}\"", event.id)); - } - } else { + auto* const port = + _patchage.canvas()->create_port(_patchage, event.id, event.info); + + if (!port) { _patchage.log().error( - fmt::format("Unknown type for port \"{}\"", event.id)); + fmt::format("Unable to create view for port \"{}\"", event.id)); } } |