xref: /haiku/src/apps/cortex/RouteApp/RouteAppNodeManager.cpp (revision 323b65468e5836bb27a5e373b14027d902349437)
1 /*
2  * Copyright (c) 1999-2000, Eric Moon.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions, and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 // RouteAppNodeManager.cpp
33 
34 #include "RouteAppNodeManager.h"
35 
36 #include "MediaIcon.h"
37 #include "NodeGroup.h"
38 #include "NodeRef.h"
39 #include "Connection.h"
40 
41 #include "route_app_io.h"
42 #include "ConnectionIO.h"
43 #include "DormantNodeIO.h"
44 #include "LiveNodeIO.h"
45 #include "MediaFormatIO.h"
46 #include "MessageIO.h"
47 #include "NodeSetIOContext.h"
48 #include "StringContent.h"
49 #include "MediaString.h"
50 
51 #include <Autolock.h>
52 #include <Debug.h>
53 #include <Entry.h>
54 #include <Path.h>
55 
56 #include <TimeSource.h>
57 
58 #include <cstring>
59 #include <cstdlib>
60 #include <cstdio>
61 #include <typeinfo>
62 
63 #include "set_tools.h"
64 
65 using namespace std;
66 
67 __USE_CORTEX_NAMESPACE
68 
69 #define D_METHOD(x) //PRINT (x)
70 #define D_HOOK(x) //PRINT (x)
71 #define D_SETTINGS(x) //PRINT (x)
72 
73 // -------------------------------------------------------- //
74 // *** ctor/dtor
75 // -------------------------------------------------------- //
76 
77 RouteAppNodeManager::~RouteAppNodeManager() {
78 
79 	_freeIcons();
80 }
81 
82 RouteAppNodeManager::RouteAppNodeManager(
83 	bool													useAddOnHost) :
84 	NodeManager(useAddOnHost),
85 	m_nextGroupNumber(1) {
86 
87 	// pre-cache icons? +++++
88 
89 }
90 
91 // -------------------------------------------------------- //
92 // *** group management
93 // -------------------------------------------------------- //
94 
95 // -------------------------------------------------------- //
96 // *** icon management
97 // -------------------------------------------------------- //
98 
99 // fetch cached icon for the given live node; the MediaIcon
100 // instance is guaranteed to last as long as this object.
101 // Returns 0 if the node doesn't exist.
102 
103 const MediaIcon* RouteAppNodeManager::mediaIconFor(
104 	media_node_id									nodeID,
105 	icon_size											iconSize) {
106 
107 	BAutolock _l(this);
108 
109 	uint64 key = _makeIconKey(nodeID, iconSize);
110 
111 	icon_map::const_iterator it = m_iconMap.find(key);
112 	if(it != m_iconMap.end()) {
113 		// already cached
114 		return (*it).second;
115 	}
116 
117 	// look up live_node_info
118 	NodeRef* ref;
119 	status_t err = getNodeRef(nodeID, &ref);
120 	if(err < B_OK)
121 		return 0;
122 
123 	return mediaIconFor(ref->nodeInfo(), iconSize);
124 }
125 
126 const MediaIcon* RouteAppNodeManager::mediaIconFor(
127 	live_node_info								nodeInfo,
128 	icon_size											iconSize) {
129 
130 	uint64 key = _makeIconKey(nodeInfo.node.node, iconSize);
131 
132 	icon_map::const_iterator it = m_iconMap.find(key);
133 	if(it != m_iconMap.end()) {
134 		// already cached
135 		return (*it).second;
136 	}
137 
138 	// create & cache icon
139 	MediaIcon* icon = new MediaIcon(
140 		nodeInfo, iconSize);
141 
142 	m_iconMap.insert(
143 		icon_map::value_type(key, icon));
144 
145 	return icon;
146 }
147 
148 // -------------------------------------------------------- //
149 // *** error handling
150 // -------------------------------------------------------- //
151 
152 status_t RouteAppNodeManager::setLogTarget(
153 	const BMessenger&							target) {
154 
155 	BAutolock _l(this);
156 
157 	if(!target.IsValid())
158 		return B_BAD_VALUE;
159 
160 	m_logTarget = target;
161 	return B_OK;
162 }
163 
164 // -------------------------------------------------------- //
165 // NodeManager hook implementations
166 // -------------------------------------------------------- //
167 
168 void RouteAppNodeManager::nodeCreated(
169 	NodeRef*											ref) {
170 
171 	// prepare the log message
172 	BMessage logMsg(M_LOG);
173 	BString title = "Node '";
174 	title << ref->name() << "' created";
175 	logMsg.AddString("title", title);
176 
177 	// create a default group for the node
178 	// [em 8feb00]
179 	NodeGroup* g = createGroup(ref->name());
180 
181 	if(ref->kind() & B_TIME_SOURCE) {
182 		// notify observers
183 		BMessage m(M_TIME_SOURCE_CREATED);
184 		m.AddInt32("nodeID", ref->id());
185 		notify(&m);
186 	}
187 
188 	// adopt node's time source if it's not the system clock (the default)
189 	// [em 20mar00]
190 	media_node systemClock;
191 	status_t err = roster->GetSystemTimeSource(&systemClock);
192 	if(err == B_OK)
193 	{
194 		BTimeSource* ts = roster->MakeTimeSourceFor(ref->node());
195 		if(ts->Node() != systemClock)
196 		{
197 			g->setTimeSource(ts->Node());
198 			logMsg.AddString("line", "Synced to system clock");
199 		}
200 		ts->Release();
201 	}
202 
203 	g->addNode(ref);
204 
205 	m_logTarget.SendMessage(&logMsg);
206 }
207 
208 void RouteAppNodeManager::nodeDeleted(
209 	const NodeRef*								ref) {
210 
211 	// prepare the log message
212 	BMessage logMsg(M_LOG);
213 	BString title = "Node '";
214 	title << ref->name() << "' released";
215 	logMsg.AddString("title", title);
216 
217 	if(ref->kind() & B_TIME_SOURCE) {
218 		// notify observers
219 		BMessage m(M_TIME_SOURCE_DELETED);
220 		m.AddInt32("nodeID", ref->id());
221 		notify(&m);
222 	}
223 
224 	m_logTarget.SendMessage(&logMsg);
225 }
226 
227 void RouteAppNodeManager::connectionMade(
228 	Connection*										connection) {
229 
230 	D_HOOK((
231 		"@ RouteAppNodeManager::connectionMade()\n"));
232 
233 	status_t err;
234 
235 	// prepare the log message
236 	BMessage logMsg(M_LOG);
237 	BString title = "Connection ";
238 	if (strcmp(connection->outputName(), connection->inputName()) == 0) {
239 		title << "'" << connection->outputName() << "' ";
240 	}
241 	title << "made";
242 	logMsg.AddString("title", title);
243 
244 	if(!(connection->flags() & Connection::INTERNAL))
245 		// don't react to connection Cortex didn't make
246 		return;
247 
248 	// create or merge groups
249 	NodeRef *producer, *consumer;
250 	err = getNodeRef(connection->sourceNode(), &producer);
251 	if(err < B_OK) {
252 		D_HOOK((
253 			"!!! RouteAppNodeManager::connectionMade():\n"
254 			"    sourceNode (%ld) not found\n",
255 			connection->sourceNode()));
256 		return;
257 	}
258 	err = getNodeRef(connection->destinationNode(), &consumer);
259 	if(err < B_OK) {
260 		D_HOOK((
261 			"!!! RouteAppNodeManager::connectionMade():\n"
262 			"    destinationNode (%ld) not found\n",
263 			connection->destinationNode()));
264 		return;
265 	}
266 
267 	// add node names to log messages
268 	BString line = "Between:";
269 	logMsg.AddString("line", line);
270 	line = "    ";
271 	line << producer->name() << " and ";
272 	line << consumer->name();
273 	logMsg.AddString("line", line);
274 
275 	// add format to log message
276 	line = "Negotiated format:";
277 	logMsg.AddString("line", line);
278 	line = "    ";
279 	line << MediaString::getStringFor(connection->format(), false);
280 	logMsg.AddString("line", line);
281 
282 	NodeGroup *group = 0;
283 	BString groupName = "Untitled group ";
284 	if(_canGroup(producer) && _canGroup(consumer))
285 	{
286 		if (producer->group() && consumer->group() &&
287 			!(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED) &&
288 			!(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
289 		{
290 			// merge into consumers group
291 			group = consumer->group();
292 			mergeGroups(producer->group(), group);
293 		}
294 		else if (producer->group() &&
295 			!(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
296 		{ // add consumer to producers group
297 			group = producer->group();
298 			group->addNode(consumer);
299 		}
300 		else if (consumer->group() &&
301 			!(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
302 		{ // add producer to consumers group
303 			group = consumer->group();
304 			group->addNode(producer);
305 		}
306 		else
307 		{ // make new group for both
308 			groupName << m_nextGroupNumber++;
309 			group = createGroup(groupName.String());
310 			group->addNode(producer);
311 			group->addNode(consumer);
312 		}
313 	}
314 	else if(_canGroup(producer) && !producer->group())
315 	{ // make new group for producer
316 		groupName << m_nextGroupNumber++;
317 		group = createGroup(groupName.String());
318 		group->addNode(producer);
319 	}
320 	else if(_canGroup(consumer) && !consumer->group())
321 	{ // make new group for consumer
322 		groupName << m_nextGroupNumber++;
323 		group = createGroup(groupName.String());
324 		group->addNode(consumer);
325 	}
326 
327 	m_logTarget.SendMessage(&logMsg);
328 }
329 
330 void RouteAppNodeManager::connectionBroken(
331 	const Connection*									connection) {
332 
333 	D_HOOK((
334 		"@ RouteAppNodeManager::connectionBroken()\n"));
335 
336 	// prepare the log message
337 	BMessage logMsg(M_LOG);
338 	BString title = "Connection ";
339 	if (strcmp(connection->outputName(), connection->inputName()) == 0) {
340 		title << "'" << connection->outputName() << "' ";
341 	}
342 	title << "broken";
343 	logMsg.AddString("title", title);
344 
345 	if(!(connection->flags() & Connection::INTERNAL))
346 		// don't react to connection Cortex didn't make
347 		return;
348 
349 	status_t err;
350 
351 	// if the source and destination nodes belong to the same group,
352 	// and if no direct or indirect connection remains between the
353 	// source and destination nodes, split groups
354 
355 	NodeRef *producer, *consumer;
356 	err = getNodeRef(connection->sourceNode(), &producer);
357 	if(err < B_OK) {
358 		D_HOOK((
359 			"!!! RouteAppNodeManager::connectionMade():\n"
360 			"    sourceNode (%ld) not found\n",
361 			connection->sourceNode()));
362 		return;
363 	}
364 	err = getNodeRef(connection->destinationNode(), &consumer);
365 	if(err < B_OK) {
366 		D_HOOK((
367 			"!!! RouteAppNodeManager::connectionMade():\n"
368 			"    destinationNode (%ld) not found\n",
369 			connection->destinationNode()));
370 		return;
371 	}
372 
373 	// add node names to log messages
374 	BString line = "Between:";
375 	logMsg.AddString("line", line);
376 	line = "    ";
377 	line << producer->name() << " and ";
378 	line << consumer->name();
379 	logMsg.AddString("line", line);
380 
381 	if(
382 		producer->group() &&
383 		producer->group() == consumer->group() &&
384 		!findRoute(producer->id(), consumer->id())) {
385 
386 		NodeGroup *newGroup;
387 		splitGroup(producer, consumer, &newGroup);
388 	}
389 
390 	m_logTarget.SendMessage(&logMsg);
391 }
392 
393 void RouteAppNodeManager::connectionFailed(
394 	const media_output &							output,
395 	const media_input &								input,
396 	const media_format &							format,
397 	status_t										error) {
398 	D_HOOK((
399 		"@ RouteAppNodeManager::connectionFailed()\n"));
400 
401 	status_t err;
402 
403 	// prepare the log message
404 	BMessage logMsg(M_LOG);
405 	BString title = "Connection failed";
406 	logMsg.AddString("title", title);
407 	logMsg.AddInt32("error", error);
408 
409 	NodeRef *producer, *consumer;
410 	err = getNodeRef(output.node.node, &producer);
411 	if(err < B_OK) {
412 		return;
413 	}
414 	err = getNodeRef(input.node.node, &consumer);
415 	if(err < B_OK) {
416 		return;
417 	}
418 
419 	// add node names to log messages
420 	BString line = "Between:";
421 	logMsg.AddString("line", line);
422 	line = "    ";
423 	line << producer->name() << " and " << consumer->name();
424 	logMsg.AddString("line", line);
425 
426 	// add format to log message
427 	line = "Tried format:";
428 	logMsg.AddString("line", line);
429 	line = "    ";
430 	line << MediaString::getStringFor(format, true);
431 	logMsg.AddString("line", line);
432 
433 	// and send it
434 	m_logTarget.SendMessage(&logMsg);
435 }
436 
437 // -------------------------------------------------------- //
438 // *** IPersistent
439 // -------------------------------------------------------- //
440 
441 void RouteAppNodeManager::xmlExportBegin(
442 	ExportContext&								context) const {
443 
444 	status_t err;
445 
446 	try {
447 		NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
448 		context.beginElement(_NODE_SET_ELEMENT);
449 
450 		// validate the node set
451 		for(int n = set.countNodes()-1; n >= 0; --n) {
452 			media_node_id id = set.nodeAt(n);
453 			ASSERT(id != media_node::null.node);
454 
455 			// fetch node
456 			NodeRef* ref;
457 			err = getNodeRef(id, &ref);
458 			if(err < B_OK) {
459 				D_SETTINGS((
460 					"! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id));
461 
462 				set.removeNodeAt(n);
463 				continue;
464 			}
465 			// skip unless internal
466 			if(!ref->isInternal()) {
467 				D_SETTINGS((
468 					"! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id));
469 
470 				set.removeNodeAt(n);
471 				continue;
472 			}
473 		}
474 	}
475 	catch(bad_cast& e) {
476 		context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
477 	}
478 }
479 
480 void RouteAppNodeManager::xmlExportAttributes(
481 	ExportContext&								context) const {}
482 
483 void RouteAppNodeManager::xmlExportContent(
484 	ExportContext&								context) const {
485 
486 	status_t err;
487 
488 	try {
489 		NodeSetIOContext& nodeSet = dynamic_cast<NodeSetIOContext&>(context);
490 		context.beginContent();
491 
492 		// write nodes; enumerate connections
493 		typedef map<uint32,Connection> connection_map;
494 		connection_map connections;
495 		int count = nodeSet.countNodes();
496 		for(int n = 0; n < count; ++n) {
497 			media_node_id id = nodeSet.nodeAt(n);
498 			ASSERT(id != media_node::null.node);
499 
500 			// fetch node
501 			NodeRef* ref;
502 			err = getNodeRef(id, &ref);
503 			if(err < B_OK) {
504 				D_SETTINGS((
505 					"! RouteAppNodeManager::xmlExportContent():\n"
506 					"  getNodeRef(%ld) failed: '%s'\n",
507 					id, strerror(err)));
508 				continue;
509 			}
510 
511 			// fetch connections
512 			vector<Connection> conSet;
513 			ref->getInputConnections(conSet);
514 			ref->getOutputConnections(conSet);
515 			for(uint32 c = 0; c < conSet.size(); ++c)
516 				// non-unique connections will be rejected:
517 				connections.insert(
518 					connection_map::value_type(conSet[c].id(), conSet[c]));
519 
520 			// create an IO object for the node & write it
521 			DormantNodeIO io(ref, nodeSet.keyAt(n));
522 			if(context.writeObject(&io) < B_OK)
523 				// abort
524 				return;
525 		}
526 
527 		// write connections
528 		for(connection_map::const_iterator it = connections.begin();
529 			it != connections.end(); ++it) {
530 
531 			ConnectionIO io(
532 				&(*it).second,
533 				this,
534 				&nodeSet);
535 			if(context.writeObject(&io) < B_OK)
536 				// abort
537 				return;
538 
539 		}
540 
541 		// +++++ write groups
542 
543 		// write UI state
544 		{
545 			BMessage m;
546 			nodeSet.exportUIState(&m);
547 			context.beginElement(_UI_STATE_ELEMENT);
548 			context.beginContent();
549 			MessageIO io(&m);
550 			context.writeObject(&io);
551 			context.endElement();
552 		}
553 	}
554 	catch(bad_cast& e) {
555 		context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
556 	}
557 }
558 
559 void RouteAppNodeManager::xmlExportEnd(
560 	ExportContext&								context) const {
561 
562 	context.endElement();
563 }
564 
565 // -------------------------------------------------------- //
566 // IMPORT
567 // -------------------------------------------------------- //
568 
569 void RouteAppNodeManager::xmlImportBegin(
570 	ImportContext&								context) {
571 
572 }
573 
574 void RouteAppNodeManager::xmlImportAttribute(
575 	const char*										key,
576 	const char*										value,
577 	ImportContext&								context) {}
578 
579 void RouteAppNodeManager::xmlImportContent(
580 	const char*										data,
581 	uint32												length,
582 	ImportContext&								context) {}
583 
584 void RouteAppNodeManager::xmlImportChild(
585 	IPersistent*									child,
586 	ImportContext&								context) {
587 
588 	status_t err;
589 
590 	if(!strcmp(context.element(), _DORMANT_NODE_ELEMENT)) {
591 		DormantNodeIO* io = dynamic_cast<DormantNodeIO*>(child);
592 		ASSERT(io);
593 
594 		NodeRef* newRef;
595 		err = io->instantiate(this, &newRef);
596 		if(err == B_OK) {
597 			// created node; add an entry to the set stored in the
598 			// ImportContext for later reference
599 			try {
600 				NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
601 				set.addNode(newRef->id(), io->nodeKey());
602 			}
603 			catch(bad_cast& e) {
604 				context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
605 			}
606 		}
607 		else {
608 			D_SETTINGS((
609 				"!!! RouteAppNodeManager::xmlImportChild():\n"
610 				"    DormantNodeIO::instantiate() failed:\n"
611 				"    '%s'\n",
612 				strerror(err)));
613 		}
614 	}
615 	else if(!strcmp(context.element(), _CONNECTION_ELEMENT)) {
616 		ConnectionIO* io = dynamic_cast<ConnectionIO*>(child);
617 		ASSERT(io);
618 
619 		// instantiate the connection
620 		Connection con;
621 		err = io->instantiate(
622 			this,
623 			dynamic_cast<NodeSetIOContext*>(&context),
624 			&con);
625 		if(err < B_OK) {
626 			D_SETTINGS((
627 				"!!! ConnectionIO::instantiate() failed:\n"
628 				"    '%s'\n", strerror(err)));
629 		}
630 
631 		// +++++ group magic?
632 
633 	}
634 	else if(!strcmp(context.element(), _NODE_GROUP_ELEMENT)) {
635 		// +++++
636 	}
637 	else if(
638 		context.parentElement() &&
639 		!strcmp(context.parentElement(), _UI_STATE_ELEMENT)) {
640 
641 		// expect a nested message
642 		MessageIO* io = dynamic_cast<MessageIO*>(child);
643 		if(!io) {
644 			BString err;
645 			err <<
646 				"RouteAppNodeManager: unexpected child '" <<
647 				context.element() << "'\n";
648 			context.reportError(err.String());
649 		}
650 
651 		// hand it off via the extended context object
652 		try {
653 			NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
654 			set.importUIState(io->message());
655 		}
656 		catch(bad_cast& e) {
657 			context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
658 		}
659 	}
660 
661 }
662 
663 void RouteAppNodeManager::xmlImportComplete(
664 	ImportContext&								context) {
665 
666 }
667 
668 // -------------------------------------------------------- //
669 
670 /*static*/
671 void RouteAppNodeManager::AddTo(
672 	XML::DocumentType*				docType) {
673 
674 	// set up the document type
675 
676 	MessageIO::AddTo(docType);
677 	MediaFormatIO::AddTo(docType);
678 	ConnectionIO::AddTo(docType);
679 	DormantNodeIO::AddTo(docType);
680 	LiveNodeIO::AddTo(docType);
681 	_add_string_elements(docType);
682 }
683 
684 // -------------------------------------------------------- //
685 // implementation
686 // -------------------------------------------------------- //
687 
688 uint64 RouteAppNodeManager::_makeIconKey(
689 	media_node_id nodeID, icon_size iconSize) {
690 
691 	return ((uint64)nodeID) << 32 | iconSize;
692 }
693 
694 void RouteAppNodeManager::_readIconKey(
695 	uint64 key, media_node_id& nodeID, icon_size& iconSize) {
696 
697 	nodeID = key >> 32;
698 	iconSize = icon_size(key & 0xffffffff);
699 }
700 
701 void RouteAppNodeManager::_freeIcons() {
702 
703 	ptr_map_delete(
704 		m_iconMap.begin(),
705 		m_iconMap.end());
706 }
707 
708 bool RouteAppNodeManager::_canGroup(NodeRef* ref) const {
709 
710 	// sanity check & easy cases
711 	ASSERT(ref);
712 	if(ref->isInternal())
713 		return true;
714 
715 	// bar 'touchy' system nodes
716 	if(ref == audioMixerNode() || ref == audioOutputNode())
717 		return false;
718 
719 	return true;
720 }
721 
722 // END -- RouteAppNodeManager.cpp --
723