463 lines
10 KiB
C++
463 lines
10 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <pcrecpp.h>
|
|
#include "jack.h"
|
|
#include "portmanager.h"
|
|
#include "config.h"
|
|
|
|
Alias::Alias(std::string match, std::string replace)
|
|
{
|
|
m_match = match;
|
|
m_replace = replace;
|
|
}
|
|
|
|
bool Alias::Match(std::string name)
|
|
{
|
|
pcrecpp::RE re(m_match);
|
|
return re.FullMatch(name);
|
|
}
|
|
|
|
std::string Alias::Replace(std::string name)
|
|
{
|
|
pcrecpp::RE re(m_match);
|
|
re.Replace(m_replace, &name);
|
|
return name;
|
|
}
|
|
|
|
bool Connection::Match(std::string port1, std::string port2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void PortManager::Refresh()
|
|
{
|
|
// Clear all information
|
|
m_clients.clear();
|
|
m_ports.clear();
|
|
|
|
if (!jack.IsConnected()) return;
|
|
|
|
jack_client_t *client = jack.GetClient();
|
|
|
|
const char **ports = jack_get_ports(client, NULL, NULL, 0);
|
|
|
|
if (ports != NULL) {
|
|
for (int i = 0; ports[i] != NULL; ++i) {
|
|
Add(jack_port_by_name(client, ports[i]));
|
|
}
|
|
|
|
jack_free(ports);
|
|
}
|
|
|
|
RefreshConnections();
|
|
}
|
|
|
|
void PortManager::RefreshConnections()
|
|
{
|
|
jack_client_t *client = jack.GetClient();
|
|
|
|
PortList::iterator it;
|
|
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
|
|
Port *p = *it;
|
|
p->m_connections.clear();
|
|
}
|
|
|
|
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
|
|
Port *p = *it;
|
|
if (p->m_is_input) {
|
|
const char **connections = jack_port_get_all_connections(client, p->m_port);
|
|
if (connections != NULL) {
|
|
for (int j = 0; connections[j] != NULL; ++j) {
|
|
Connect(p->m_port, jack_port_by_name(client, connections[j]));
|
|
}
|
|
free(connections);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortManager::Add(jack_port_t *port)
|
|
{
|
|
std::string jack_name = jack_port_name(port);
|
|
std::string client_name;
|
|
std::string group_name;
|
|
std::string port_name;
|
|
|
|
if (cfg.UseAliases) {
|
|
if (cfg.LocalAliases) {
|
|
jack_name = GetAlias(jack_name);
|
|
} else {
|
|
char *aliases[2];
|
|
aliases[0] = new char[jack_port_name_size()];
|
|
aliases[1] = new char[jack_port_name_size()];
|
|
|
|
int num_aliases = jack_port_get_aliases(port, aliases);
|
|
|
|
if (num_aliases < 2) {
|
|
AliasBay(port, num_aliases, aliases);
|
|
num_aliases = jack_port_get_aliases(port, aliases);
|
|
}
|
|
|
|
if (num_aliases == 2) {
|
|
jack_name = aliases[1];
|
|
} else if (num_aliases == 1) {
|
|
jack_name = aliases[0];
|
|
}
|
|
|
|
delete aliases[0];
|
|
delete aliases[1];
|
|
}
|
|
}
|
|
|
|
size_t pos1 = jack_name.find_first_of(':');
|
|
size_t pos2 = jack_name.find_last_of('.');
|
|
|
|
// All ports must have a client part
|
|
if (pos1 == std::string::npos) return;
|
|
|
|
Port *p = new Port();
|
|
p->m_port = port;
|
|
p->m_is_input = (JackPortFlags)jack_port_flags(port) & JackPortIsInput;
|
|
|
|
p->m_real_name = jack_port_name(port);
|
|
|
|
std::string type = jack_port_type(port);
|
|
p->m_is_midi = type == JACK_DEFAULT_MIDI_TYPE;
|
|
|
|
// Client
|
|
client_name = jack_name.substr(0, pos1);
|
|
p->m_client = FindOrMakeClient(client_name, p->m_is_input, p->m_is_midi);
|
|
|
|
if (pos2 != std::string::npos) {
|
|
// Group
|
|
group_name = jack_name.substr(pos1 + 1, pos2 - pos1 - 1);
|
|
port_name = jack_name.substr(pos2 + 1);
|
|
} else {
|
|
group_name = "";
|
|
port_name = jack_name.substr(pos1 + 1);
|
|
}
|
|
|
|
p->m_group = FindOrMakeGroup(p->m_client, group_name);
|
|
p->m_group->m_ports.push_back(p);
|
|
|
|
p->m_name = port_name;
|
|
|
|
//std::clog << "New port (" << client_name << ") " << port_name << std::endl;
|
|
|
|
p->m_client->m_ports.push_back(p);
|
|
|
|
m_ports.push_back(p);
|
|
}
|
|
|
|
void PortManager::Delete(jack_port_t *port)
|
|
{
|
|
Port *p = FindPort(port);
|
|
if (p == NULL) {
|
|
std::cerr << "Deleting unknown port!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
if (p->m_group != NULL) {
|
|
// Clean up groups
|
|
p->m_group->m_ports.remove(p);
|
|
if (p->m_group->m_ports.size() == 0) {
|
|
p->m_client->m_groups.remove(p->m_group);
|
|
delete p->m_group;
|
|
}
|
|
}
|
|
|
|
// Clean up clients
|
|
p->m_client->m_ports.remove(p);
|
|
if (p->m_client->m_ports.size() == 0) {
|
|
m_clients.remove(p->m_client);
|
|
delete p->m_client;
|
|
}
|
|
|
|
// Remove port
|
|
m_ports.remove(p);
|
|
|
|
//std::clog << "Port '" << p->m_name << "' removed" << std::endl;
|
|
|
|
delete p;
|
|
}
|
|
|
|
void PortManager::Connect(jack_port_t *port_a, jack_port_t *port_b)
|
|
{
|
|
Port *p1 = FindPort(port_a);
|
|
Port *p2 = FindPort(port_b);
|
|
|
|
if (p1 == NULL || p2 == NULL) {
|
|
std::cerr << "Connecting unknown ports!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
if (p1->m_is_input) {
|
|
p1->m_connections.push_back(p2);
|
|
} else {
|
|
p2->m_connections.push_back(p1);
|
|
}
|
|
|
|
//std::clog << "Connecting '" << jack_port_name(port_a) << "' to '" << jack_port_name(port_b) << "'" << std::endl;
|
|
}
|
|
|
|
void PortManager::Disconnect(jack_port_t *port_a, jack_port_t *port_b)
|
|
{
|
|
Port *p1 = FindPort(port_a);
|
|
Port *p2 = FindPort(port_b);
|
|
|
|
if (p1 == NULL || p2 == NULL) {
|
|
std::cerr << "Disconnecting unknown ports!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
if (p1->m_is_input) {
|
|
p1->m_connections.remove(p2);
|
|
} else {
|
|
p2->m_connections.remove(p1);
|
|
}
|
|
|
|
//std::clog << "Disconnecting '" << jack_port_name(port_a) << "' from '" << jack_port_name(port_b) << "'" << std::endl;
|
|
}
|
|
|
|
void PortManager::Rename(jack_port_t *port)
|
|
{
|
|
Delete(port);
|
|
Add(port);
|
|
RefreshConnections();
|
|
}
|
|
|
|
bool PortManager::PollPortRenames()
|
|
{
|
|
// Queue for storing ports to be renamed, to avoid messing up the
|
|
// port iterator.
|
|
PortList queue;
|
|
|
|
PortList::iterator it;
|
|
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
|
|
Port *p = *it;
|
|
if (strcmp(p->m_real_name.c_str(), jack_port_name(p->m_port))) {
|
|
// Queue for rename.
|
|
queue.push_back(p);
|
|
}
|
|
}
|
|
|
|
// Now push the renames through.
|
|
for (it = queue.begin(); it != queue.end(); ++it) {
|
|
Port *p = *it;
|
|
Rename(p->m_port);
|
|
}
|
|
|
|
return queue.size() > 0;
|
|
}
|
|
|
|
Client *PortManager::FindOrMakeClient(std::string name, bool is_input, bool is_midi)
|
|
{
|
|
ClientList::iterator it;
|
|
for (it = m_clients.begin(); it != m_clients.end(); ++it) {
|
|
Client *c = *it;
|
|
if (c->m_is_input == is_input && c->m_is_midi == is_midi && c->m_name == name) return c;
|
|
}
|
|
|
|
Client *c = new Client();
|
|
c->m_name = name;
|
|
c->m_is_input = is_input;
|
|
c->m_is_midi = is_midi;
|
|
c->m_expanded = cfg.ExpandClients;
|
|
|
|
m_clients.push_back(c);
|
|
|
|
return c;
|
|
}
|
|
|
|
PortGroup *PortManager::FindOrMakeGroup(Client *client, std::string name)
|
|
{
|
|
PortGroupList::iterator it;
|
|
for (it = client->m_groups.begin(); it != client->m_groups.end(); ++it) {
|
|
PortGroup *pg = *it;
|
|
if (pg->m_name == name) return pg;
|
|
}
|
|
|
|
PortGroup *pg = new PortGroup();
|
|
pg->m_name = name;
|
|
pg->m_is_input = client->m_is_input;
|
|
pg->m_is_midi = client->m_is_midi;
|
|
pg->m_expanded = cfg.ExpandGroups;
|
|
|
|
client->m_groups.push_back(pg);
|
|
|
|
return pg;
|
|
}
|
|
|
|
Port *PortManager::FindPort(jack_port_t *port)
|
|
{
|
|
PortList::iterator it;
|
|
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
|
|
Port *p = *it;
|
|
if (p->m_port == port) return p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool BaseSortPredicate(const Base *lhs, const Base *rhs)
|
|
{
|
|
const char *a = lhs->m_name.c_str();
|
|
const char *b = rhs->m_name.c_str();
|
|
size_t len = std::max(lhs->m_name.length(), rhs->m_name.length());
|
|
|
|
for (size_t p = 0; p < len; p++) {
|
|
char ac = (p < lhs->m_name.length()) ? a[p] : '\0';
|
|
char bc = (p < rhs->m_name.length()) ? b[p] : '\0';
|
|
|
|
if (ac >= '0' && ac <= '9' && bc >= '0' && bc <= '9') {
|
|
// Numeric sort
|
|
long int av = strtol(&a[p], NULL, 0);
|
|
long int bv = strtol(&b[p], NULL, 0);
|
|
if (av != bv) {
|
|
return av < bv;
|
|
}
|
|
} else {
|
|
// Alphabetic sort
|
|
if (ac >= 'a' && ac <= 'z') ac -= 'a' - 'A';
|
|
if (bc >= 'a' && bc <= 'z') bc -= 'a' - 'A';
|
|
if (ac != bc) {
|
|
return ac < bc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void PortManager::Sort()
|
|
{
|
|
m_clients.sort(&BaseSortPredicate);
|
|
|
|
ClientList::iterator clit;
|
|
for (clit = m_clients.begin(); clit != m_clients.end(); ++clit) {
|
|
Client *c = *clit;
|
|
|
|
c->m_groups.sort(&BaseSortPredicate);
|
|
|
|
PortGroupList::iterator pglit;
|
|
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
|
|
PortGroup *pg = *pglit;
|
|
|
|
pg->m_ports.sort(&BaseSortPredicate);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortManager::ToggleConnect(Port *a, Port *b)
|
|
{
|
|
ConnectionMode cm = a->ConnectedTo(b);
|
|
|
|
if (cm == CM_NONE) {
|
|
jack.Connect(b->m_port, a->m_port);
|
|
} else {
|
|
jack.Disconnect(b->m_port, a->m_port);
|
|
}
|
|
}
|
|
|
|
void PortManager::ToggleConnect(Port *a, PortGroup *b)
|
|
{
|
|
PortList::iterator plitb;
|
|
ConnectionMode cm = a->ConnectedTo(b);
|
|
|
|
for (plitb = b->m_ports.begin(); plitb != b->m_ports.end(); ++plitb) {
|
|
Port *pb = *plitb;
|
|
|
|
// if (connect == 0) {
|
|
// connect = a->ConnectedTo(pb) ? -1 : 1;
|
|
// }
|
|
if (cm == CM_NONE) {
|
|
jack.Connect(pb->m_port, a->m_port);
|
|
} else {
|
|
jack.Disconnect(pb->m_port, a->m_port);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortManager::ToggleConnect(PortGroup *a, Port *b)
|
|
{
|
|
PortList::iterator plita;
|
|
ConnectionMode cm = a->ConnectedTo(b);
|
|
|
|
for (plita = a->m_ports.begin(); plita != a->m_ports.end(); ++plita) {
|
|
Port *pa = *plita;
|
|
|
|
// if (connect == 0) {
|
|
// connect = pa->ConnectedTo(b) ? -1 : 1;
|
|
// }
|
|
if (cm == CM_NONE) {
|
|
jack.Connect(b->m_port, pa->m_port);
|
|
} else {
|
|
jack.Disconnect(b->m_port, pa->m_port);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortManager::ToggleConnect(PortGroup *a, PortGroup *b)
|
|
{
|
|
PortList::iterator plita;
|
|
PortList::iterator plitb;
|
|
ConnectionMode cm = a->ConnectedTo(b);
|
|
|
|
for (plita = a->m_ports.begin(), plitb = b->m_ports.begin(); plita != a->m_ports.end() && plitb != b->m_ports.end(); ++plita, ++plitb) {
|
|
Port *pa = *plita;
|
|
Port *pb = *plitb;
|
|
|
|
if (cm == CM_NONE) {
|
|
jack.Connect(pb->m_port, pa->m_port);
|
|
} else {
|
|
jack.Disconnect(pb->m_port, pa->m_port);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string PortManager::GetAlias(std::string port_name)
|
|
{
|
|
AliasList::iterator it;
|
|
for (it = m_aliases.begin(); it != m_aliases.end(); ++it) {
|
|
Alias &a = *it;
|
|
|
|
if (a.Match(port_name)) {
|
|
return a.Replace(port_name);
|
|
}
|
|
}
|
|
|
|
return port_name;
|
|
}
|
|
|
|
void PortManager::AliasBay(jack_port_t *port, int num_aliases, char *aliases[2])
|
|
{
|
|
std::string port_name = jack_port_name(port);
|
|
|
|
AliasList::iterator it;
|
|
for (it = m_aliases.begin(); it != m_aliases.end(); ++it) {
|
|
Alias &a = *it;
|
|
if (a.Match(port_name)) {
|
|
std::string alias = a.Replace(port_name);
|
|
bool got = false;
|
|
for (int i = 0; i < num_aliases; i++) {
|
|
if (alias == aliases[i]) got = true;
|
|
}
|
|
if (!got) {
|
|
jack_port_set_alias(port, alias.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortManager::AliasClear()
|
|
{
|
|
m_aliases.clear();
|
|
}
|
|
|
|
void PortManager::AliasAdd(std::string source, std::string target)
|
|
{
|
|
m_aliases.push_back(Alias(source, target));
|
|
}
|
|
|