#include #include #include #include #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)); }