summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-11-28 21:41:26 +0100
committerDavid Robillard <d@drobilla.net>2020-11-28 22:49:10 +0100
commit5128bfab7ddb9504abf17375e910e5bc94af291e (patch)
tree79cc0953718e1f79ed47282b9f11f9f087edc3bc /src
parent0f25dd575f9c74cc34a54e64468f07e6c631750d (diff)
downloadpatchage-5128bfab7ddb9504abf17375e910e5bc94af291e.tar.gz
patchage-5128bfab7ddb9504abf17375e910e5bc94af291e.tar.bz2
patchage-5128bfab7ddb9504abf17375e910e5bc94af291e.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/AlsaDriver.cpp286
-rw-r--r--src/AlsaDriver.hpp35
-rw-r--r--src/Driver.hpp12
-rw-r--r--src/JackDbusDriver.cpp319
-rw-r--r--src/JackDbusDriver.hpp64
-rw-r--r--src/JackDriver.cpp244
-rw-r--r--src/JackDriver.hpp15
-rw-r--r--src/Patchage.cpp28
-rw-r--r--src/PatchageCanvas.cpp87
-rw-r--r--src/PatchageCanvas.hpp8
-rw-r--r--src/handle_event.cpp36
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));
}
}