From 5128bfab7ddb9504abf17375e910e5bc94af291e Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 28 Nov 2020 21:41:26 +0100 Subject: Refresh by emitting events This decouples drivers from the rest of the application, in particular the horrible situation where they were working with the canvas directly, by having them always communicate changes by emitting events. --- src/AlsaDriver.cpp | 286 ++++++++++------------------------------------------- 1 file changed, 51 insertions(+), 235 deletions(-) (limited to 'src/AlsaDriver.cpp') 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 +#include #include #include #include @@ -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::max()); + sink({ClientCreationEvent{ + ClientID::alsa(static_cast(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(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; -- cgit v1.2.1