1 // qjackctlAlsaGraph.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2003-2020, rncbc aka Rui Nuno Capela. All rights reserved.
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 *****************************************************************************/
21
22 #include "qjackctlAlsaGraph.h"
23
24 #include "qjackctlMainForm.h"
25
26
27 #ifdef CONFIG_ALSA_SEQ
28
29 #include <QMutexLocker>
30
31
32 //----------------------------------------------------------------------------
33 // qjackctlAlsaGraph -- ALSA graph driver
34
35 QMutex qjackctlAlsaGraph::g_mutex;
36
37
38 // Constructor.
qjackctlAlsaGraph(qjackctlGraphCanvas * canvas)39 qjackctlAlsaGraph::qjackctlAlsaGraph ( qjackctlGraphCanvas *canvas )
40 : qjackctlGraphSect(canvas)
41 {
42 resetPortTypeColors();
43 }
44
45
46 // ALSA port (dis)connection.
connectPorts(qjackctlGraphPort * port1,qjackctlGraphPort * port2,bool connect)47 void qjackctlAlsaGraph::connectPorts (
48 qjackctlGraphPort *port1, qjackctlGraphPort *port2, bool connect )
49 {
50 qjackctlMainForm *pMainForm = qjackctlMainForm::getInstance();
51 if (pMainForm == nullptr)
52 return;
53
54 snd_seq_t *seq = pMainForm->alsaSeq();
55 if (seq == nullptr)
56 return;
57
58 if (port1 == nullptr || port2 == nullptr)
59 return;
60
61 const qjackctlGraphNode *node1 = port1->portNode();
62 const qjackctlGraphNode *node2 = port2->portNode();
63
64 if (node1 == nullptr || node2 == nullptr)
65 return;
66
67 QMutexLocker locker(&g_mutex);
68
69 const int client_id1
70 = node1->nodeName().section(':', 0, 0).toInt();
71 const int port_id1
72 = port1->portName().section(':', 0, 0).toInt();
73
74 const int client_id2
75 = node2->nodeName().section(':', 0, 0).toInt();
76 const int port_id2
77 = port2->portName().section(':', 0, 0).toInt();
78
79 #ifdef CONFIG_DEBUG
80 qDebug("qjackctlAlsaGraph::connectPorts(%d:%d, %d:%d, %d)",
81 client_id1, port_id1, client_id2, port_id2, connect);
82 #endif
83
84 snd_seq_port_subscribe_t *seq_subs;
85 snd_seq_addr_t seq_addr;
86
87 snd_seq_port_subscribe_alloca(&seq_subs);
88
89 seq_addr.client = client_id1;
90 seq_addr.port = port_id1;
91 snd_seq_port_subscribe_set_sender(seq_subs, &seq_addr);
92
93 seq_addr.client = client_id2;
94 seq_addr.port = port_id2;
95 snd_seq_port_subscribe_set_dest(seq_subs, &seq_addr);
96
97 if (connect) {
98 snd_seq_subscribe_port(seq, seq_subs);
99 } else {
100 snd_seq_unsubscribe_port(seq, seq_subs);
101 }
102 }
103
104
105 // ALSA node type inquirer. (static)
isNodeType(uint node_type)106 bool qjackctlAlsaGraph::isNodeType ( uint node_type )
107 {
108 return (node_type == qjackctlAlsaGraph::nodeType());
109 }
110
111
112 // ALSA node type.
nodeType(void)113 uint qjackctlAlsaGraph::nodeType (void)
114 {
115 static
116 const uint AlsaNodeType
117 = qjackctlGraphItem::itemType("ALSA_NODE_TYPE");
118
119 return AlsaNodeType;
120 }
121
122
123 // ALSA port type inquirer. (static)
isPortType(uint port_type)124 bool qjackctlAlsaGraph::isPortType ( uint port_type )
125 {
126 return (port_type == qjackctlAlsaGraph::midiPortType());
127 }
128
129
130 // ALSA port type.
midiPortType(void)131 uint qjackctlAlsaGraph::midiPortType (void)
132 {
133 static
134 const uint AlsaMidiPortType
135 = qjackctlGraphItem::itemType("ALSA_PORT_TYPE");
136
137 return AlsaMidiPortType;
138 }
139
140
141 // ALSA client:port finder and creator if not existing.
findClientPort(snd_seq_client_info_t * client_info,snd_seq_port_info_t * port_info,qjackctlGraphItem::Mode port_mode,qjackctlGraphNode ** node,qjackctlGraphPort ** port,bool add_new)142 bool qjackctlAlsaGraph::findClientPort (
143 snd_seq_client_info_t *client_info,
144 snd_seq_port_info_t *port_info,
145 qjackctlGraphItem::Mode port_mode,
146 qjackctlGraphNode **node,
147 qjackctlGraphPort **port,
148 bool add_new )
149 {
150 const int client_id
151 = snd_seq_client_info_get_client(client_info);
152 const int port_id
153 = snd_seq_port_info_get_port(port_info);
154
155 const QString& client_name
156 = QString::number(client_id) + ':'
157 + QString::fromUtf8(snd_seq_client_info_get_name(client_info));
158 const QString& port_name
159 = QString::number(port_id) + ':'
160 + QString::fromUtf8(snd_seq_port_info_get_name(port_info));
161
162 const uint node_type
163 = qjackctlAlsaGraph::nodeType();
164 const uint port_type
165 = qjackctlAlsaGraph::midiPortType();
166
167 qjackctlGraphItem::Mode node_mode = port_mode;
168
169 *node = qjackctlGraphSect::findNode(client_name, node_mode, node_type);
170 *port = nullptr;
171
172 if (*node == nullptr && client_id >= 128) {
173 node_mode = qjackctlGraphItem::Duplex;
174 *node = qjackctlGraphSect::findNode(client_name, node_mode, node_type);
175 }
176
177 if (*node)
178 *port = (*node)->findPort(port_name, port_mode, port_type);
179
180 if (add_new && *node == nullptr) {
181 *node = new qjackctlGraphNode(client_name, node_mode, node_type);
182 (*node)->setNodeIcon(QIcon(":/images/graphAlsa.png"));
183 qjackctlGraphSect::addItem(*node);
184 }
185
186 if (add_new && *port == nullptr && *node) {
187 *port = (*node)->addPort(port_name, port_mode, port_type);
188 (*port)->updatePortTypeColors(canvas());
189 }
190
191 if (add_new && *node) {
192 int nchanged = 0;
193 QString node_title = (*node)->nodeTitle();
194 foreach (qjackctlAliasList *node_aliases, item_aliases(*node))
195 node_title = node_aliases->clientAlias(client_name);
196 if ((*node)->nodeTitle() != node_title) {
197 (*node)->setNodeTitle(node_title);
198 ++nchanged;
199 }
200 if (*port) {
201 QString port_title = (*port)->portTitle();
202 foreach (qjackctlAliasList *port_aliases, item_aliases(*port))
203 port_title = port_aliases->portAlias(client_name, port_name);
204 if ((*port)->portTitle() != port_title) {
205 (*port)->setPortTitle(port_title);
206 ++nchanged;
207 }
208 }
209 if (nchanged > 0)
210 (*node)->updatePath();
211 }
212
213 return (*node && *port);
214 }
215
216
217 // ALSA graph updater.
updateItems(void)218 void qjackctlAlsaGraph::updateItems (void)
219 {
220 QMutexLocker locker(&g_mutex);
221
222 qjackctlMainForm *pMainForm = qjackctlMainForm::getInstance();
223 if (pMainForm == nullptr)
224 return;
225
226 snd_seq_t *seq = pMainForm->alsaSeq();
227 if (seq == nullptr)
228 return;
229
230 #ifdef CONFIG_DEBUG_0
231 qDebug("qjackctlAlsaGraph::updateItems()");
232 #endif
233
234 // 1. Client/ports inventory...
235 //
236 snd_seq_client_info_t *client_info1;
237 snd_seq_port_info_t *port_info1;
238
239 snd_seq_client_info_alloca(&client_info1);
240 snd_seq_port_info_alloca(&port_info1);
241
242 snd_seq_client_info_set_client(client_info1, -1);
243
244 while (snd_seq_query_next_client(seq, client_info1) >= 0) {
245 const int client_id
246 = snd_seq_client_info_get_client(client_info1);
247 if (0 >= client_id) // Skip 0:System client...
248 continue;
249 snd_seq_port_info_set_client(port_info1, client_id);
250 snd_seq_port_info_set_port(port_info1, -1);
251 while (snd_seq_query_next_port(seq, port_info1) >= 0) {
252 const unsigned int port_caps1
253 = snd_seq_port_info_get_capability(port_info1);
254 if (port_caps1 & SND_SEQ_PORT_CAP_NO_EXPORT)
255 continue;
256 qjackctlGraphItem::Mode port_mode1 = qjackctlGraphItem::None;
257 const unsigned int port_is_input
258 = (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE);
259 if ((port_caps1 & port_is_input) == port_is_input) {
260 port_mode1 = qjackctlGraphItem::Input;
261 qjackctlGraphNode *node1 = nullptr;
262 qjackctlGraphPort *port1 = nullptr;
263 if (findClientPort(client_info1, port_info1,
264 port_mode1, &node1, &port1, true)) {
265 node1->setMarked(true);
266 port1->setMarked(true);
267 }
268 }
269 const unsigned int port_is_output
270 = (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ);
271 if ((port_caps1 & port_is_output) == port_is_output) {
272 port_mode1 = qjackctlGraphItem::Output;
273 qjackctlGraphNode *node1 = nullptr;
274 qjackctlGraphPort *port1 = nullptr;
275 if (findClientPort(client_info1, port_info1,
276 port_mode1, &node1, &port1, true)) {
277 node1->setMarked(true);
278 port1->setMarked(true);
279 }
280 }
281 }
282 }
283
284 // 2. Connections inventory...
285 //
286 snd_seq_client_info_t *client_info2;
287 snd_seq_port_info_t *port_info2;
288
289 snd_seq_client_info_alloca(&client_info2);
290 snd_seq_port_info_alloca(&port_info2);
291
292 snd_seq_query_subscribe_t *seq_subs;
293 snd_seq_addr_t seq_addr;
294
295 snd_seq_query_subscribe_alloca(&seq_subs);
296
297 snd_seq_client_info_set_client(client_info1, -1);
298
299 while (snd_seq_query_next_client(seq, client_info1) >= 0) {
300 const int client_id
301 = snd_seq_client_info_get_client(client_info1);
302 if (0 >= client_id) // Skip 0:system client...
303 continue;
304 snd_seq_port_info_set_client(port_info1, client_id);
305 snd_seq_port_info_set_port(port_info1, -1);
306 while (snd_seq_query_next_port(seq, port_info1) >= 0) {
307 const unsigned int port_caps1
308 = snd_seq_port_info_get_capability(port_info1);
309 if (port_caps1 & SND_SEQ_PORT_CAP_NO_EXPORT)
310 continue;
311 if (port_caps1 & (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) {
312 const qjackctlGraphItem::Mode port_mode1
313 = qjackctlGraphItem::Output;
314 qjackctlGraphNode *node1 = nullptr;
315 qjackctlGraphPort *port1 = nullptr;
316 if (!findClientPort(client_info1, port_info1,
317 port_mode1, &node1, &port1, false))
318 continue;
319 snd_seq_query_subscribe_set_type(seq_subs, SND_SEQ_QUERY_SUBS_READ);
320 snd_seq_query_subscribe_set_index(seq_subs, 0);
321 seq_addr.client = client_id;
322 seq_addr.port = snd_seq_port_info_get_port(port_info1);
323 snd_seq_query_subscribe_set_root(seq_subs, &seq_addr);
324 while (snd_seq_query_port_subscribers(seq, seq_subs) >= 0) {
325 seq_addr = *snd_seq_query_subscribe_get_addr(seq_subs);
326 if (snd_seq_get_any_client_info(seq,
327 seq_addr.client, client_info2) >= 0 &&
328 snd_seq_get_any_port_info(seq,
329 seq_addr.client, seq_addr.port, port_info2) >= 0) {
330 const qjackctlGraphItem::Mode port_mode2
331 = qjackctlGraphItem::Input;
332 qjackctlGraphNode *node2 = nullptr;
333 qjackctlGraphPort *port2 = nullptr;
334 if (findClientPort(client_info2, port_info2,
335 port_mode2, &node2, &port2, false)) {
336 qjackctlGraphConnect *connect = port1->findConnect(port2);
337 if (connect == nullptr) {
338 connect = new qjackctlGraphConnect();
339 connect->setPort1(port1);
340 connect->setPort2(port2);
341 connect->updatePortTypeColors();
342 connect->updatePath();
343 qjackctlGraphSect::addItem(connect);
344 }
345 if (connect)
346 connect->setMarked(true);
347 }
348 }
349 snd_seq_query_subscribe_set_index(seq_subs,
350 snd_seq_query_subscribe_get_index(seq_subs) + 1);
351 }
352 }
353 }
354 }
355
356 // 3. Clean-up all un-marked items...
357 //
358 qjackctlGraphSect::resetItems(qjackctlAlsaGraph::nodeType());
359 }
360
361
clearItems(void)362 void qjackctlAlsaGraph::clearItems (void)
363 {
364 QMutexLocker locker(&g_mutex);
365
366 #ifdef CONFIG_DEBUG_0
367 qDebug("qjackctlAlsaGraph::clearItems()");
368 #endif
369
370 qjackctlGraphSect::clearItems(qjackctlAlsaGraph::nodeType());
371 }
372
373
374 // Special port-type colors defaults (virtual).
resetPortTypeColors(void)375 void qjackctlAlsaGraph::resetPortTypeColors (void)
376 {
377 qjackctlGraphCanvas *canvas = qjackctlGraphSect::canvas();
378 if (canvas) {
379 canvas->setPortTypeColor(
380 qjackctlAlsaGraph::midiPortType(),
381 QColor(Qt::darkMagenta).darker(120));
382 }
383 }
384
385
386 // Client/port item aliases accessor.
item_aliases(qjackctlGraphItem * item) const387 QList<qjackctlAliasList *> qjackctlAlsaGraph::item_aliases (
388 qjackctlGraphItem *item ) const
389 {
390 QList<qjackctlAliasList *> alist;
391
392 qjackctlAliases *aliases = nullptr;
393 qjackctlGraphCanvas *canvas = qjackctlGraphSect::canvas();
394 if (canvas)
395 aliases = canvas->aliases();
396 if (aliases == nullptr)
397 return alist; // empty!
398
399 uint item_type = 0;
400 qjackctlGraphItem::Mode item_mode = qjackctlGraphItem::None;
401 bool is_node = false;
402
403 if (item->type() == qjackctlGraphNode::Type) {
404 qjackctlGraphNode *node = static_cast<qjackctlGraphNode *> (item);
405 if (node && qjackctlAlsaGraph::isNodeType(node->nodeType())) {
406 item_type = node->nodeType();
407 item_mode = node->nodeMode();
408 is_node = true;
409 }
410 }
411 else
412 if (item->type() == qjackctlGraphPort::Type) {
413 qjackctlGraphPort *port = static_cast<qjackctlGraphPort *> (item);
414 if (port) {
415 item_type = port->portType();
416 item_mode = port->portMode();
417 }
418 }
419
420 if (!item_type || !item_mode)
421 return alist; // empty again!
422
423 if (is_node || item_type == qjackctlAlsaGraph::midiPortType()) {
424 // ALSA MIDI type...
425 if (item_mode & qjackctlGraphItem::Input)
426 alist.append(&(aliases->alsaInputs));
427 if (item_mode & qjackctlGraphItem::Output)
428 alist.append(&(aliases->alsaOutputs));
429 }
430
431 return alist; // hopefully non empty!
432 }
433
434
435 #endif // CONFIG_ALSA_SEQ
436
437
438 // end of qjackctlAlsaGraph.cpp
439