xref: /haiku/src/apps/cortex/RouteApp/RouteAppNodeManager.cpp (revision 4e3137c085bae361922078f123dceb92da700640)
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 == NULL)
196 			return;
197 		if(ts->Node() != systemClock)
198 		{
199 			g->setTimeSource(ts->Node());
200 			logMsg.AddString("line", "Synced to system clock");
201 		}
202 		ts->Release();
203 	}
204 
205 	g->addNode(ref);
206 
207 	m_logTarget.SendMessage(&logMsg);
208 }
209 
210 void RouteAppNodeManager::nodeDeleted(
211 	const NodeRef*								ref) {
212 
213 	// prepare the log message
214 	BMessage logMsg(M_LOG);
215 	BString title = "Node '";
216 	title << ref->name() << "' released";
217 	logMsg.AddString("title", title);
218 
219 	if(ref->kind() & B_TIME_SOURCE) {
220 		// notify observers
221 		BMessage m(M_TIME_SOURCE_DELETED);
222 		m.AddInt32("nodeID", ref->id());
223 		notify(&m);
224 	}
225 
226 	m_logTarget.SendMessage(&logMsg);
227 }
228 
229 void RouteAppNodeManager::connectionMade(
230 	Connection*										connection) {
231 
232 	D_HOOK((
233 		"@ RouteAppNodeManager::connectionMade()\n"));
234 
235 	status_t err;
236 
237 	// prepare the log message
238 	BMessage logMsg(M_LOG);
239 	BString title = "Connection ";
240 	if (strcmp(connection->outputName(), connection->inputName()) == 0) {
241 		title << "'" << connection->outputName() << "' ";
242 	}
243 	title << "made";
244 	logMsg.AddString("title", title);
245 
246 	if(!(connection->flags() & Connection::INTERNAL))
247 		// don't react to connection Cortex didn't make
248 		return;
249 
250 	// create or merge groups
251 	NodeRef *producer, *consumer;
252 	err = getNodeRef(connection->sourceNode(), &producer);
253 	if(err < B_OK) {
254 		D_HOOK((
255 			"!!! RouteAppNodeManager::connectionMade():\n"
256 			"    sourceNode (%ld) not found\n",
257 			connection->sourceNode()));
258 		return;
259 	}
260 	err = getNodeRef(connection->destinationNode(), &consumer);
261 	if(err < B_OK) {
262 		D_HOOK((
263 			"!!! RouteAppNodeManager::connectionMade():\n"
264 			"    destinationNode (%ld) not found\n",
265 			connection->destinationNode()));
266 		return;
267 	}
268 
269 	// add node names to log messages
270 	BString line = "Between:";
271 	logMsg.AddString("line", line);
272 	line = "    ";
273 	line << producer->name() << " and ";
274 	line << consumer->name();
275 	logMsg.AddString("line", line);
276 
277 	// add format to log message
278 	line = "Negotiated format:";
279 	logMsg.AddString("line", line);
280 	line = "    ";
281 	line << MediaString::getStringFor(connection->format(), false);
282 	logMsg.AddString("line", line);
283 
284 	NodeGroup *group = 0;
285 	BString groupName = "Untitled group ";
286 	if(_canGroup(producer) && _canGroup(consumer))
287 	{
288 		if (producer->group() && consumer->group() &&
289 			!(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED) &&
290 			!(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
291 		{
292 			// merge into consumers group
293 			group = consumer->group();
294 			mergeGroups(producer->group(), group);
295 		}
296 		else if (producer->group() &&
297 			!(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
298 		{ // add consumer to producers group
299 			group = producer->group();
300 			group->addNode(consumer);
301 		}
302 		else if (consumer->group() &&
303 			!(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
304 		{ // add producer to consumers group
305 			group = consumer->group();
306 			group->addNode(producer);
307 		}
308 		else
309 		{ // make new group for both
310 			groupName << m_nextGroupNumber++;
311 			group = createGroup(groupName.String());
312 			group->addNode(producer);
313 			group->addNode(consumer);
314 		}
315 	}
316 	else if(_canGroup(producer) && !producer->group())
317 	{ // make new group for producer
318 		groupName << m_nextGroupNumber++;
319 		group = createGroup(groupName.String());
320 		group->addNode(producer);
321 	}
322 	else if(_canGroup(consumer) && !consumer->group())
323 	{ // make new group for consumer
324 		groupName << m_nextGroupNumber++;
325 		group = createGroup(groupName.String());
326 		group->addNode(consumer);
327 	}
328 
329 	m_logTarget.SendMessage(&logMsg);
330 }
331 
332 void RouteAppNodeManager::connectionBroken(
333 	const Connection*									connection) {
334 
335 	D_HOOK((
336 		"@ RouteAppNodeManager::connectionBroken()\n"));
337 
338 	// prepare the log message
339 	BMessage logMsg(M_LOG);
340 	BString title = "Connection ";
341 	if (strcmp(connection->outputName(), connection->inputName()) == 0) {
342 		title << "'" << connection->outputName() << "' ";
343 	}
344 	title << "broken";
345 	logMsg.AddString("title", title);
346 
347 	if(!(connection->flags() & Connection::INTERNAL))
348 		// don't react to connection Cortex didn't make
349 		return;
350 
351 	status_t err;
352 
353 	// if the source and destination nodes belong to the same group,
354 	// and if no direct or indirect connection remains between the
355 	// source and destination nodes, split groups
356 
357 	NodeRef *producer, *consumer;
358 	err = getNodeRef(connection->sourceNode(), &producer);
359 	if(err < B_OK) {
360 		D_HOOK((
361 			"!!! RouteAppNodeManager::connectionMade():\n"
362 			"    sourceNode (%ld) not found\n",
363 			connection->sourceNode()));
364 		return;
365 	}
366 	err = getNodeRef(connection->destinationNode(), &consumer);
367 	if(err < B_OK) {
368 		D_HOOK((
369 			"!!! RouteAppNodeManager::connectionMade():\n"
370 			"    destinationNode (%ld) not found\n",
371 			connection->destinationNode()));
372 		return;
373 	}
374 
375 	// add node names to log messages
376 	BString line = "Between:";
377 	logMsg.AddString("line", line);
378 	line = "    ";
379 	line << producer->name() << " and ";
380 	line << consumer->name();
381 	logMsg.AddString("line", line);
382 
383 	if(
384 		producer->group() &&
385 		producer->group() == consumer->group() &&
386 		!findRoute(producer->id(), consumer->id())) {
387 
388 		NodeGroup *newGroup;
389 		splitGroup(producer, consumer, &newGroup);
390 	}
391 
392 	m_logTarget.SendMessage(&logMsg);
393 }
394 
395 void RouteAppNodeManager::connectionFailed(
396 	const media_output &							output,
397 	const media_input &								input,
398 	const media_format &							format,
399 	status_t										error) {
400 	D_HOOK((
401 		"@ RouteAppNodeManager::connectionFailed()\n"));
402 
403 	status_t err;
404 
405 	// prepare the log message
406 	BMessage logMsg(M_LOG);
407 	BString title = "Connection failed";
408 	logMsg.AddString("title", title);
409 	logMsg.AddInt32("error", error);
410 
411 	NodeRef *producer, *consumer;
412 	err = getNodeRef(output.node.node, &producer);
413 	if(err < B_OK) {
414 		return;
415 	}
416 	err = getNodeRef(input.node.node, &consumer);
417 	if(err < B_OK) {
418 		return;
419 	}
420 
421 	// add node names to log messages
422 	BString line = "Between:";
423 	logMsg.AddString("line", line);
424 	line = "    ";
425 	line << producer->name() << " and " << consumer->name();
426 	logMsg.AddString("line", line);
427 
428 	// add format to log message
429 	line = "Tried format:";
430 	logMsg.AddString("line", line);
431 	line = "    ";
432 	line << MediaString::getStringFor(format, true);
433 	logMsg.AddString("line", line);
434 
435 	// and send it
436 	m_logTarget.SendMessage(&logMsg);
437 }
438 
439 // -------------------------------------------------------- //
440 // *** IPersistent
441 // -------------------------------------------------------- //
442 
443 void RouteAppNodeManager::xmlExportBegin(
444 	ExportContext&								context) const {
445 
446 	status_t err;
447 
448 	try {
449 		NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
450 		context.beginElement(_NODE_SET_ELEMENT);
451 
452 		// validate the node set
453 		for(int n = set.countNodes()-1; n >= 0; --n) {
454 			media_node_id id = set.nodeAt(n);
455 			ASSERT(id != media_node::null.node);
456 
457 			// fetch node
458 			NodeRef* ref;
459 			err = getNodeRef(id, &ref);
460 			if(err < B_OK) {
461 				D_SETTINGS((
462 					"! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id));
463 
464 				set.removeNodeAt(n);
465 				continue;
466 			}
467 			// skip unless internal
468 			if(!ref->isInternal()) {
469 				D_SETTINGS((
470 					"! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id));
471 
472 				set.removeNodeAt(n);
473 				continue;
474 			}
475 		}
476 	}
477 	catch(bad_cast& e) {
478 		context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
479 	}
480 }
481 
482 void RouteAppNodeManager::xmlExportAttributes(
483 	ExportContext&								context) const {}
484 
485 void RouteAppNodeManager::xmlExportContent(
486 	ExportContext&								context) const {
487 
488 	status_t err;
489 
490 	try {
491 		NodeSetIOContext& nodeSet = dynamic_cast<NodeSetIOContext&>(context);
492 		context.beginContent();
493 
494 		// write nodes; enumerate connections
495 		typedef map<uint32,Connection> connection_map;
496 		connection_map connections;
497 		int count = nodeSet.countNodes();
498 		for(int n = 0; n < count; ++n) {
499 			media_node_id id = nodeSet.nodeAt(n);
500 			ASSERT(id != media_node::null.node);
501 
502 			// fetch node
503 			NodeRef* ref;
504 			err = getNodeRef(id, &ref);
505 			if(err < B_OK) {
506 				D_SETTINGS((
507 					"! RouteAppNodeManager::xmlExportContent():\n"
508 					"  getNodeRef(%ld) failed: '%s'\n",
509 					id, strerror(err)));
510 				continue;
511 			}
512 
513 			// fetch connections
514 			vector<Connection> conSet;
515 			ref->getInputConnections(conSet);
516 			ref->getOutputConnections(conSet);
517 			for(uint32 c = 0; c < conSet.size(); ++c)
518 				// non-unique connections will be rejected:
519 				connections.insert(
520 					connection_map::value_type(conSet[c].id(), conSet[c]));
521 
522 			// create an IO object for the node & write it
523 			DormantNodeIO io(ref, nodeSet.keyAt(n));
524 			if(context.writeObject(&io) < B_OK)
525 				// abort
526 				return;
527 		}
528 
529 		// write connections
530 		for(connection_map::const_iterator it = connections.begin();
531 			it != connections.end(); ++it) {
532 
533 			ConnectionIO io(
534 				&(*it).second,
535 				this,
536 				&nodeSet);
537 			if(context.writeObject(&io) < B_OK)
538 				// abort
539 				return;
540 
541 		}
542 
543 		// +++++ write groups
544 
545 		// write UI state
546 		{
547 			BMessage m;
548 			nodeSet.exportUIState(&m);
549 			context.beginElement(_UI_STATE_ELEMENT);
550 			context.beginContent();
551 			MessageIO io(&m);
552 			context.writeObject(&io);
553 			context.endElement();
554 		}
555 	}
556 	catch(bad_cast& e) {
557 		context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
558 	}
559 }
560 
561 void RouteAppNodeManager::xmlExportEnd(
562 	ExportContext&								context) const {
563 
564 	context.endElement();
565 }
566 
567 // -------------------------------------------------------- //
568 // IMPORT
569 // -------------------------------------------------------- //
570 
571 void RouteAppNodeManager::xmlImportBegin(
572 	ImportContext&								context) {
573 
574 }
575 
576 void RouteAppNodeManager::xmlImportAttribute(
577 	const char*										key,
578 	const char*										value,
579 	ImportContext&								context) {}
580 
581 void RouteAppNodeManager::xmlImportContent(
582 	const char*										data,
583 	uint32												length,
584 	ImportContext&								context) {}
585 
586 void RouteAppNodeManager::xmlImportChild(
587 	IPersistent*									child,
588 	ImportContext&								context) {
589 
590 	status_t err;
591 
592 	if(!strcmp(context.element(), _DORMANT_NODE_ELEMENT)) {
593 		DormantNodeIO* io = dynamic_cast<DormantNodeIO*>(child);
594 		ASSERT(io);
595 
596 		NodeRef* newRef;
597 		err = io->instantiate(this, &newRef);
598 		if(err == B_OK) {
599 			// created node; add an entry to the set stored in the
600 			// ImportContext for later reference
601 			try {
602 				NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
603 				set.addNode(newRef->id(), io->nodeKey());
604 			}
605 			catch(bad_cast& e) {
606 				context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
607 			}
608 		}
609 		else {
610 			D_SETTINGS((
611 				"!!! RouteAppNodeManager::xmlImportChild():\n"
612 				"    DormantNodeIO::instantiate() failed:\n"
613 				"    '%s'\n",
614 				strerror(err)));
615 		}
616 	}
617 	else if(!strcmp(context.element(), _CONNECTION_ELEMENT)) {
618 		ConnectionIO* io = dynamic_cast<ConnectionIO*>(child);
619 		ASSERT(io);
620 
621 		// instantiate the connection
622 		Connection con;
623 		err = io->instantiate(
624 			this,
625 			dynamic_cast<NodeSetIOContext*>(&context),
626 			&con);
627 		if(err < B_OK) {
628 			D_SETTINGS((
629 				"!!! ConnectionIO::instantiate() failed:\n"
630 				"    '%s'\n", strerror(err)));
631 		}
632 
633 		// +++++ group magic?
634 
635 	}
636 	else if(!strcmp(context.element(), _NODE_GROUP_ELEMENT)) {
637 		// +++++
638 	}
639 	else if(
640 		context.parentElement() &&
641 		!strcmp(context.parentElement(), _UI_STATE_ELEMENT)) {
642 
643 		// expect a nested message
644 		MessageIO* io = dynamic_cast<MessageIO*>(child);
645 		if(!io) {
646 			BString err;
647 			err <<
648 				"RouteAppNodeManager: unexpected child '" <<
649 				context.element() << "'\n";
650 			context.reportError(err.String());
651 		}
652 
653 		// hand it off via the extended context object
654 		try {
655 			NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
656 			set.importUIState(io->message());
657 		}
658 		catch(bad_cast& e) {
659 			context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
660 		}
661 	}
662 
663 }
664 
665 void RouteAppNodeManager::xmlImportComplete(
666 	ImportContext&								context) {
667 
668 }
669 
670 // -------------------------------------------------------- //
671 
672 /*static*/
673 void RouteAppNodeManager::AddTo(
674 	XML::DocumentType*				docType) {
675 
676 	// set up the document type
677 
678 	MessageIO::AddTo(docType);
679 	MediaFormatIO::AddTo(docType);
680 	ConnectionIO::AddTo(docType);
681 	DormantNodeIO::AddTo(docType);
682 	LiveNodeIO::AddTo(docType);
683 	_add_string_elements(docType);
684 }
685 
686 // -------------------------------------------------------- //
687 // implementation
688 // -------------------------------------------------------- //
689 
690 uint64 RouteAppNodeManager::_makeIconKey(
691 	media_node_id nodeID, icon_size iconSize) {
692 
693 	return ((uint64)nodeID) << 32 | iconSize;
694 }
695 
696 void RouteAppNodeManager::_readIconKey(
697 	uint64 key, media_node_id& nodeID, icon_size& iconSize) {
698 
699 	nodeID = key >> 32;
700 	iconSize = icon_size(key & 0xffffffff);
701 }
702 
703 void RouteAppNodeManager::_freeIcons() {
704 
705 	ptr_map_delete(
706 		m_iconMap.begin(),
707 		m_iconMap.end());
708 }
709 
710 bool RouteAppNodeManager::_canGroup(NodeRef* ref) const {
711 
712 	// sanity check & easy cases
713 	ASSERT(ref);
714 	if(ref->isInternal())
715 		return true;
716 
717 	// bar 'touchy' system nodes
718 	if(ref == audioMixerNode() || ref == audioOutputNode())
719 		return false;
720 
721 	return true;
722 }
723 
724 // END -- RouteAppNodeManager.cpp --
725