xref: /haiku/src/apps/cortex/NodeManager/NodeManager.cpp (revision e39da397f5ff79f2db9f9a3ddf1852b6710578af)
1 // NodeManager.cpp
2 
3 #include "NodeManager.h"
4 
5 #include "AddOnHost.h"
6 #include "Connection.h"
7 #include "NodeGroup.h"
8 #include "NodeRef.h"
9 
10 #include <Debug.h>
11 #include <MediaRoster.h>
12 
13 #include <algorithm>
14 #include <cstring>
15 #include <functional>
16 #include <list>
17 #include <set>
18 
19 #include "set_tools.h"
20 #include "functional_tools.h"
21 
22 #include "node_manager_impl.h"
23 
24 __USE_CORTEX_NAMESPACE
25 
26 #define D_METHOD(x) //PRINT (x)
27 #define D_MESSAGE(x) //PRINT (x)
28 #define D_ROSTER(x) //PRINT (x)
29 #define D_LOCK(x) //PRINT (x)
30 
31 // -------------------------------------------------------- //
32 // messaging constants
33 // -------------------------------------------------------- //
34 
35 // B_MEDIA_CONNECTION_BROKEN
36 const char* const _connectionField = "__connection_id";
37 const char* const _sourceNodeField = "__source_node_id";
38 const char* const _destNodeField = "__destination_node_id";
39 
40 
41 
42 
43 // -------------------------------------------------------- //
44 // *** hooks
45 // -------------------------------------------------------- //
46 
47 // [e.moon 7nov99] these hooks are called during processing of
48 // BMediaRoster messages, before any notification is sent to
49 // observers.  For example, if a B_MEDIA_NODES_CREATED message
50 // were received describing 3 new nodes, nodeCreated() would be
51 // called 3 times before the notification was sent.
52 
53 void NodeManager::nodeCreated(
54 	NodeRef*											ref) {}
55 
56 void NodeManager::nodeDeleted(
57 	const NodeRef*								ref) {}
58 
59 void NodeManager::connectionMade(
60 	Connection*										connection) {}
61 
62 void NodeManager::connectionBroken(
63 	const Connection*							connection) {}
64 
65 void NodeManager::connectionFailed(
66 	const media_output &							output,
67 	const media_input &								input,
68 	const media_format &							format,
69 	status_t error) {}
70 
71 // -------------------------------------------------------- //
72 // helpers
73 // -------------------------------------------------------- //
74 
75 class _for_each_state {
76 public:
77 	// marks nodes visited
78 	set<media_node_id>		visited;
79 };
80 
81 // [e.moon 28sep99] graph crawling
82 // - does NOT apply operation to origin node.
83 // - if inGroup is non-0, visits only nodes in that group.
84 //
85 // [e.moon 13oct99]: no longer supports locking (use _lockAllGroups() to
86 // be sure all nodes are locked, if necessary.)
87 
88 template<class Op>
89 void _do_for_each_connected(
90 	NodeManager*											manager,
91 	NodeRef*													origin,
92 	NodeGroup*												inGroup,
93 	bool															recurse,
94 	Op																operation,
95 	_for_each_state*									state) {
96 
97 //	PRINT(("### _do_for_each_connected()\n"));
98 
99 	ASSERT(manager->IsLocked());
100 
101 	ASSERT(origin);
102 	ASSERT(state);
103 	status_t err;
104 
105 	if(state->visited.find(origin->id()) != state->visited.end()) {
106 //		PRINT(("### already visited\n"));
107 		// already visited
108 		return;
109 	}
110 
111 	// used to walk connections
112 	vector<Connection>		connections;
113 
114 	// mark visited
115 	state->visited.insert(origin->id());
116 
117 	// walk input connections
118 	origin->getInputConnections(connections);
119 	for(uint32 n = 0; n < connections.size(); ++n) {
120 
121 		if(!connections[n].isValid())
122 			continue;
123 
124 //		PRINT(("# source: %ld\n", connections[n].sourceNode()));
125 
126 		NodeRef* targetRef;
127 		err = manager->getNodeRef(
128 			connections[n].sourceNode(),
129 			&targetRef);
130 		ASSERT(err == B_OK);
131 		ASSERT(targetRef);
132 
133 		if(inGroup && targetRef->group() != inGroup) {
134 //			PRINT(("# .group mismatch\n"));
135 			// don't need to visit
136 			return;
137 		}
138 
139 		// invoke operation
140 //		if(lockRef)
141 //			targetRef->lock();
142 		operation(targetRef);
143 //		if(lockRef)
144 //			targetRef->unlock();
145 
146 		// recurse?
147 		if(recurse)
148 			_do_for_each_connected(
149 				manager,
150 				targetRef,
151 				inGroup,
152 				true,
153 				operation,
154 				state);
155 	}
156 
157 	// walk output connections
158 	connections.clear();
159 	origin->getOutputConnections(connections);
160 	for(uint32 n = 0; n < connections.size(); ++n) {
161 //		PRINT(("# dest: %ld\n", connections[n].destinationNode()));
162 
163 		if(!connections[n].isValid())
164 			continue;
165 
166 		NodeRef* targetRef;
167 		err = manager->getNodeRef(
168 			connections[n].destinationNode(),
169 			&targetRef);
170 		ASSERT(err == B_OK);
171 		ASSERT(targetRef);
172 
173 		if(inGroup && targetRef->group() != inGroup) {
174 //			PRINT(("# .group mismatch\n"));
175 			// don't need to visit
176 			return;
177 		}
178 
179 		// invoke operation
180 //		if(lockRef)
181 //			targetRef->lock();
182 		operation(targetRef);
183 //		if(lockRef)
184 //			targetRef->unlock();
185 
186 		// recurse?
187 		if(recurse)
188 			_do_for_each_connected(
189 				manager,
190 				targetRef,
191 				inGroup,
192 				true,
193 				operation,
194 				state);
195 	}
196 }
197 
198 // dtor helpers
199 inline void NodeManager::_clearGroup(
200 	NodeGroup*										group) {
201 	Autolock _l(group);
202 	D_METHOD((
203 		"NodeManager::_clearGroup()\n"));
204 
205 	// stop group before clearing [10aug99]
206 	group->_stop();
207 
208 	int32 n;
209 	while((n = group->countNodes()) > 0) {
210 		group->removeNode(n-1);
211 	}
212 
213 //	// [e.moon 7nov99] release the group
214 //	status_t err = remove_observer(this, group);
215 //	if(err < B_OK) {
216 //		// spew diagnostics
217 //		PRINT((
218 //			"!!! NodeManager::_clearGroup(): remove_observer(group %ld):\n"
219 //			"    %s\n",
220 //			group->id(),
221 //			strerror(err)));
222 //	}
223 }
224 
225 inline void NodeManager::_freeConnection(
226 	Connection*										connection) {
227 	ASSERT(connection);
228 
229 	D_METHOD((
230 		"NodeManager::_freeConnection(%ld)\n", connection->id()));
231 	status_t err;
232 
233 	// break if internal & still valid
234 	if(
235 		connection->isValid() &&
236 		connection->flags() & Connection::INTERNAL &&
237 		!(connection->flags() & Connection::LOCKED)) {
238 
239 		D_METHOD((
240 			"! breaking connection:\n"
241 			"  source node:  %ld\n"
242 			"  source id:    %ld\n"
243 			"  source port:  %ld\n"
244 			"  dest node:    %ld\n"
245 			"  dest id:      %ld\n"
246 			"  dest port:    %ld\n",
247 			connection->sourceNode(),
248 			connection->source().id, connection->source().port,
249 			connection->destinationNode(),
250 			connection->destination().id, connection->destination().port));
251 
252 		// do it
253 		D_ROSTER(("# roster->Disconnect()\n"));
254 		err = roster->Disconnect(
255 			connection->sourceNode(),
256 			connection->source(),
257 			connection->destinationNode(),
258 			connection->destination());
259 
260 		if(err < B_OK) {
261 			D_METHOD((
262 				"!!! BMediaRoster::Disconnect('%s' -> '%s') failed:\n"
263 				"    %s\n",
264 				connection->outputName(), connection->inputName(),
265 				strerror(err)));
266 		}
267 	}
268 
269 	// delete
270 	delete connection;
271 }
272 
273 // -------------------------------------------------------- //
274 // *** ctor/dtor
275 // -------------------------------------------------------- //
276 
277 
278 NodeManager::~NodeManager() {
279 	D_METHOD((
280 		"~NodeManager()\n"));
281 	ASSERT(IsLocked());
282 
283 	// make a list of nodes to be released
284 	list<NodeRef*> deadNodes;
285 
286 	for(node_ref_map::const_iterator it = m_nodeRefMap.begin();
287 		it != m_nodeRefMap.end(); ++it) {
288 		deadNodes.push_back((*it).second);
289 	}
290 
291 	// ungroup all nodes
292 
293 // [e.moon 13oct99] making PPC compiler happy
294 //	for_each(
295 //		m_nodeGroupSet.begin(),
296 //		m_nodeGroupSet.end(),
297 //		bound_method(
298 //			*this,
299 //			&NodeManager::_clearGroup
300 //		)
301 //	);
302 	for(node_group_set::iterator it = m_nodeGroupSet.begin();
303 		it != m_nodeGroupSet.end(); ++it) {
304 		_clearGroup(*it);
305 	}
306 
307 	// delete groups
308 	ptr_set_delete(
309 		m_nodeGroupSet.begin(),
310 		m_nodeGroupSet.end());
311 	m_nodeGroupSet.clear();
312 
313 
314 	// deallocate all connections; disconnect internal nodes
315 // [e.moon 13oct99] making PPC compiler happy
316 //	for_each(
317 //		m_conSourceMap.begin(),
318 //		m_conSourceMap.end(),
319 //		unary_map_function(
320 //			m_conSourceMap,
321 //			bound_method(
322 //				*this,
323 //				&NodeManager::_freeConnection
324 //			)
325 //		)
326 //	);
327 	for(con_map::iterator it = m_conSourceMap.begin();
328 		it != m_conSourceMap.end(); ++it) {
329 		_freeConnection((*it).second);
330 	}
331 	m_conSourceMap.clear();
332 	m_conDestinationMap.clear();
333 
334 	// release all nodes
335 	for(list<NodeRef*>::const_iterator it = deadNodes.begin();
336 		it != deadNodes.end(); ++it) {
337 		(*it)->release();
338 	}
339 
340 	if(m_nodeRefMap.size()) {
341 		// +++++ nodes will only remain if they have observers; cope!
342 		PRINT(("*** %ld nodes remaining!\n", m_nodeRefMap.size()));
343 
344 		deadNodes.clear();
345 		for(node_ref_map::const_iterator it = m_nodeRefMap.begin();
346 			it != m_nodeRefMap.end(); ++it)
347 				deadNodes.push_back((*it).second);
348 
349 		ptr_set_delete(
350 			deadNodes.begin(),
351 			deadNodes.end());
352 	}
353 
354 //	for_each(
355 //		m_nodeRefMap.begin(),
356 //		m_nodeRefMap.end(),
357 //		unary_map_function(
358 //			m_nodeRefMap,
359 //			mem_fun(
360 //				&NodeRef::release
361 //			)
362 //		)
363 //	);
364 //
365 //	// delete all nodes
366 //	ptr_map_delete(
367 //		m_nodeRefMap.begin(),
368 //		m_nodeRefMap.end());
369 //
370 //
371 //	PRINT((
372 //		"~NodeManager() done\n"));
373 //
374 }
375 
376 const char* const			NodeManager::s_defaultGroupPrefix = "No Name";
377 const char* const			NodeManager::s_timeSourceGroup = "Time Sources";
378 const char* const			NodeManager::s_audioInputGroup = "System Audio Input";
379 const char* const			NodeManager::s_videoInputGroup = "System Video Input";
380 const char* const			NodeManager::s_audioMixerGroup = "System Audio Mixer";
381 const char* const			NodeManager::s_videoOutputGroup = "System Video Output";
382 
383 NodeManager::NodeManager(
384 	bool													useAddOnHost) :
385 
386 	ObservableLooper("NodeManager"),
387 	roster(BMediaRoster::Roster()),
388 	m_audioInputNode(0),
389 	m_videoInputNode(0),
390 	m_audioMixerNode(0),
391 	m_audioOutputNode(0),
392 	m_videoOutputNode(0),
393 	m_nextConID(1),
394 	m_existingNodesInit(false),
395 	m_useAddOnHost(useAddOnHost) {
396 
397 	D_METHOD((
398 		"NodeManager()\n"));
399 
400 	ASSERT(roster);
401 
402 	// create refs for common nodes
403 	_initCommonNodes();
404 
405 	// start the looper
406 	Run();
407 
408 	// initialize connection to the media roster
409 	D_ROSTER((
410 		"# roster->StartWatching(%p)\n", this));
411 	status_t err = roster->StartWatching(
412 		BMessenger(this));
413 	ASSERT(err == B_OK);
414 }
415 
416 // -------------------------------------------------------- //
417 // *** operations
418 // -------------------------------------------------------- //
419 
420 // * ACCESS
421 
422 // fetches NodeRef corresponding to a given ID; returns
423 // B_BAD_VALUE if no matching entry was found (and writes
424 // a 0 into the provided pointer.)
425 
426 status_t NodeManager::getNodeRef(
427 	media_node_id									id,
428 	NodeRef**											outRef) const {
429 	Autolock _l(this);
430 
431 	D_METHOD((
432 		"NodeManager::getNodeRef(%ld)\n", id));
433 
434 	node_ref_map::const_iterator it = m_nodeRefMap.find(id);
435 	if(it == m_nodeRefMap.end()) {
436 		*outRef = 0;
437 		return B_BAD_VALUE;
438 	}
439 
440 	*outRef = (*it).second;
441 	return B_OK;
442 }
443 
444 // [13aug99]
445 // fetches Connection corresponding to a given source/destination
446 // on a given node.  Returns an invalid connection and B_BAD_VALUE
447 // if no matching connection was found.
448 
449 status_t NodeManager::findConnection(
450 	media_node_id									node,
451 	const media_source&						source,
452 	Connection*										outConnection) const {
453 	Autolock _l(this);
454 
455 	D_METHOD((
456 		"NodeManager::findConnection()\n"));
457 	ASSERT(source != media_source::null);
458 
459 	con_map::const_iterator it = m_conSourceMap.lower_bound(node);
460 	con_map::const_iterator itEnd = m_conSourceMap.upper_bound(node);
461 	for(; it != itEnd; ++it)
462 		if((*it).second->source() == source) {
463 			// copy connection
464 			*outConnection = *((*it).second);
465 			return B_OK;
466 		}
467 
468 	*outConnection = Connection();
469 	return B_BAD_VALUE;
470 }
471 
472 status_t NodeManager::findConnection(
473 	media_node_id									node,
474 	const media_destination&			destination,
475 	Connection*										outConnection) const {
476 	Autolock _l(this);
477 
478 	D_METHOD((
479 		"NodeManager::findConnection()\n"));
480 	ASSERT(destination != media_destination::null);
481 
482 	con_map::const_iterator it = m_conDestinationMap.lower_bound(node);
483 	con_map::const_iterator itEnd = m_conDestinationMap.upper_bound(node);
484 	for(; it != itEnd; ++it)
485 		if((*it).second->destination() == destination) {
486 			// copy connection
487 			*outConnection = *((*it).second);
488 			return B_OK;
489 		}
490 
491 	*outConnection = Connection();
492 	return B_BAD_VALUE;
493 }
494 
495 // [e.moon 28sep99]
496 // fetches a Connection matching the given source and destination
497 // nodes.  Returns an invalid connection and B_BAD_VALUE if
498 // no matching connection was found
499 
500 status_t NodeManager::findConnection(
501 	media_node_id									sourceNode,
502 	media_node_id									destinationNode,
503 	Connection*										outConnection) const {
504 	Autolock _l(this);
505 
506 	D_METHOD((
507 		"NodeManager::findConnection(source %ld, dest %ld)\n", sourceNode, destinationNode));
508 
509 	con_map::const_iterator it = m_conSourceMap.lower_bound(sourceNode);
510 	con_map::const_iterator itEnd = m_conSourceMap.upper_bound(sourceNode);
511 	for(; it != itEnd; ++it) {
512 		if((*it).second->destinationNode() == destinationNode) {
513 			*outConnection = *((*it).second);
514 			return B_OK;
515 		}
516 	}
517 
518 	*outConnection = Connection();
519 	return B_BAD_VALUE;
520 }
521 
522 // [e.moon 28sep99]
523 // tries to find a route from 'nodeA' to 'nodeB'; returns
524 // true if one exists, false if not.
525 
526 class NodeManager::_find_route_state {
527 public:
528 	set<media_node_id>						visited;
529 };
530 
531 bool NodeManager::findRoute(
532 	media_node_id									nodeA,
533 	media_node_id									nodeB) {
534 	Autolock _l(this);
535 
536 	D_METHOD((
537 		"NodeManager::findRoute(%ld, %ld)\n", nodeA, nodeB));
538 	status_t err;
539 
540 	NodeRef* ref;
541 	err = getNodeRef(nodeA, &ref);
542 	if(err < B_OK) {
543 		PRINT((
544 			"!!! NodeManager::findRoute(%ld, %ld): no ref for node %ld\n",
545 			nodeA, nodeB, nodeA));
546 		return false;
547 	}
548 
549 	_find_route_state st;
550 	return _find_route_recurse(ref, nodeB, &st);
551 }
552 
553 // implementation of above
554 
555 bool NodeManager::_find_route_recurse(
556 	NodeRef*													origin,
557 	media_node_id											target,
558 	_find_route_state*								state) {
559 
560 	ASSERT(IsLocked());
561 	ASSERT(origin);
562 	ASSERT(state);
563 	status_t err;
564 
565 	// node already visited?
566 	if(state->visited.find(origin->id()) != state->visited.end()) {
567 		return false;
568 	}
569 
570 	// mark node visited
571 	state->visited.insert(origin->id());
572 
573 	vector<Connection> connections;
574 
575 	// walk input connections
576 	origin->getInputConnections(connections);
577 	for(uint32 n = 0; n < connections.size(); ++n) {
578 
579 		if(!connections[n].isValid())
580 			continue;
581 
582 		// test against target
583 		if(connections[n].sourceNode() == target)
584 			return true; // SUCCESS
585 
586 		// recurse
587 		NodeRef* ref;
588 		err = getNodeRef(
589 			connections[n].sourceNode(),
590 			&ref);
591 		ASSERT(err == B_OK);
592 		ASSERT(ref);
593 
594 		if(_find_route_recurse(
595 			ref,
596 			target,
597 			state))
598 			return true; // SUCCESS
599 	}
600 
601 	// walk output connections
602 	connections.clear();
603 	origin->getOutputConnections(connections);
604 	for(uint32 n = 0; n < connections.size(); ++n) {
605 
606 		if(!connections[n].isValid())
607 			continue;
608 
609 		// test against target
610 		if(connections[n].destinationNode() == target)
611 			return true; // SUCCESS
612 
613 		// recurse
614 		NodeRef* ref;
615 		err = getNodeRef(
616 			connections[n].destinationNode(),
617 			&ref);
618 		ASSERT(err == B_OK);
619 		ASSERT(ref);
620 
621 		if(_find_route_recurse(
622 			ref,
623 			target,
624 			state))
625 			return true; // SUCCESS
626 	}
627 
628 	return false; // FAILED
629 }
630 
631 
632 
633 // fetches Connection corresponding to a given source or
634 // destination; Returns an invalid connection and B_BAD_VALUE if
635 // none found.
636 // * Note: this is the slowest possible way to look up a
637 //   connection.  Since the low-level source/destination
638 //   structures don't include a node ID, and a destination
639 //   port can differ from its node's control port, a linear
640 //   search of all known connections is performed.  Only
641 //   use these methods if you have no clue what node the
642 //   connection corresponds to.
643 
644 status_t NodeManager::findConnection(
645 	const media_source&						source,
646 	Connection*										outConnection) const {
647 	Autolock _l(this);
648 
649 	D_METHOD((
650 		"NodeManager::findConnection()\n"));
651 	ASSERT(source != media_source::null);
652 
653 	for(con_map::const_iterator it = m_conSourceMap.begin();
654 		it != m_conSourceMap.end(); ++it) {
655 		if((*it).second->source() == source) {
656 			// copy connection
657 			*outConnection = *((*it).second);
658 			return B_OK;
659 		}
660 	}
661 
662 	*outConnection = Connection();
663 	return B_BAD_VALUE;
664 }
665 
666 status_t NodeManager::findConnection(
667 	const media_destination&			destination,
668 	Connection*										outConnection) const {
669 	Autolock _l(this);
670 
671 	D_METHOD((
672 		"NodeManager::findConnection()\n"));
673 	ASSERT(destination != media_destination::null);
674 
675 	for(con_map::const_iterator it = m_conDestinationMap.begin();
676 		it != m_conDestinationMap.end(); ++it) {
677 		if((*it).second->destination() == destination) {
678 			// copy connection
679 			*outConnection = *((*it).second);
680 			return B_OK;
681 		}
682 	}
683 
684 	*outConnection = Connection();
685 	return B_BAD_VALUE;
686 }
687 
688 
689 // fetch NodeRefs for system nodes (if a particular node doesn't
690 // exist, these methods return 0)
691 
692 NodeRef* NodeManager::audioInputNode() const {
693 	Autolock _l(this);
694 	return m_audioInputNode;
695 }
696 NodeRef* NodeManager::videoInputNode() const {
697 	Autolock _l(this);
698 	return m_videoInputNode;
699 }
700 NodeRef* NodeManager::audioMixerNode() const {
701 	Autolock _l(this);
702 	return m_audioMixerNode;
703 }
704 NodeRef* NodeManager::audioOutputNode() const {
705 	Autolock _l(this);
706 	return m_audioOutputNode;
707 }
708 NodeRef* NodeManager::videoOutputNode() const {
709 	Autolock _l(this);
710 	return m_videoOutputNode;
711 }
712 
713 // fetch groups by index
714 // - you can write-lock the manager during sets of calls to these methods;
715 //   this ensures that the group set won't change.  The methods do lock
716 //   the group internally, so locking isn't explicitly required.
717 
718 uint32 NodeManager::countGroups() const {
719 	Autolock _l(this);
720 	D_METHOD((
721 		"NodeManager::countGroups()\n"));
722 
723 	return m_nodeGroupSet.size();
724 }
725 
726 NodeGroup* NodeManager::groupAt(
727 	uint32												index) const {
728 	Autolock _l(this);
729 	D_METHOD((
730 		"NodeManager::groupAt()\n"));
731 
732 	return (index < m_nodeGroupSet.size()) ?
733 		m_nodeGroupSet[index] :
734 		0;
735 }
736 
737 // look up a group by unique ID; returns B_BAD_VALUE if no
738 // matching group was found
739 
740 class match_group_by_id :
741 	public binary_function<const NodeGroup*, uint32, bool> {
742 public:
743 	bool operator()(const NodeGroup* group, uint32 id) const {
744 		return group->id() == id;
745 	}
746 };
747 
748 status_t NodeManager::findGroup(
749 	uint32												id,
750 	NodeGroup**										outGroup) const {
751 	Autolock _l(this);
752 	D_METHOD((
753 		"NodeManager::findGroup(id)\n"));
754 
755 	node_group_set::const_iterator it =
756 		find_if(
757 			m_nodeGroupSet.begin(),
758 			m_nodeGroupSet.end(),
759 			bind2nd(match_group_by_id(), id)
760 		);
761 
762 	if(it == m_nodeGroupSet.end()) {
763 		*outGroup = 0;
764 		return B_BAD_VALUE;
765 	}
766 
767 	*outGroup = *it;
768 	return B_OK;
769 }
770 
771 // look up a group by name; returns B_NAME_NOT_FOUND if
772 // no group matching the name was found.
773 
774 class match_group_by_name :
775 	public binary_function<const NodeGroup*, const char*, bool> {
776 public:
777 	bool operator()(const NodeGroup* group, const char* name) const {
778 		return !strcmp(group->name(), name);
779 	}
780 };
781 
782 status_t NodeManager::findGroup(
783 	const char*										name,
784 	NodeGroup**										outGroup) const {
785 	Autolock _l(this);
786 	D_METHOD((
787 		"NodeManager::findGroup(name)\n"));
788 
789 	node_group_set::const_iterator it =
790 		find_if(
791 			m_nodeGroupSet.begin(),
792 			m_nodeGroupSet.end(),
793 			bind2nd(match_group_by_name(), name)
794 		);
795 
796 	if(it == m_nodeGroupSet.end()) {
797 		*outGroup = 0;
798 		return B_BAD_VALUE;
799 	}
800 
801 	*outGroup = *it;
802 	return B_OK;
803 }
804 
805 // merge the given source group to the given destination;
806 // empties and releases the source group
807 
808 status_t NodeManager::mergeGroups(
809 	NodeGroup*										sourceGroup,
810 	NodeGroup*										destinationGroup) {
811 	Autolock _l(this);
812 	D_METHOD((
813 		"NodeManager::mergeGroups(name)\n"));
814 
815 	status_t err;
816 
817 	// [5feb00 c.lenz] already merged
818 	if(sourceGroup->id() == destinationGroup->id())
819 		return B_OK;
820 
821 	if(sourceGroup->isReleased() || destinationGroup->isReleased())
822 		return B_NOT_ALLOWED;
823 
824 	for(uint32 n = sourceGroup->countNodes(); n; --n) {
825 		NodeRef* node = sourceGroup->nodeAt(n-1);
826 		ASSERT(node);
827 		err = sourceGroup->removeNode(n-1);
828 		ASSERT(err == B_OK);
829 		err = destinationGroup->addNode(node);
830 		ASSERT(err == B_OK);
831 	}
832 
833 	// [7nov99 e.moon] delete the source group
834 	_removeGroup(sourceGroup);
835 	sourceGroup->release();
836 
837 	return B_OK;
838 }
839 
840 // [e.moon 28sep99]
841 // split group: given two nodes currently in the same group
842 // that are not connected (directly OR indirectly),
843 // this method removes outsideNode, and all nodes connected
844 // to outsideNode, from the common group.  These nodes are
845 // then added to a new group, returned in 'outGroup'.  The
846 // new group has " split" appended to the end of the original
847 // group's name.
848 //
849 // Returns B_NOT_ALLOWED if any of the above conditions aren't
850 // met (ie. the nodes are in different groups or an indirect
851 // route exists from one to the other), or B_OK if the group
852 // was split successfully.
853 
854 
855 class _changeNodeGroupFn :
856 	public	unary_function<NodeRef*, void> {
857 public:
858 	NodeGroup*										newGroup;
859 
860 	_changeNodeGroupFn(
861 		NodeGroup*									_newGroup) : newGroup(_newGroup) {
862 		ASSERT(newGroup);
863 	}
864 
865 	void operator()(
866 		NodeRef*										node) {
867 
868 		PRINT((
869 			"_changeNodeGroupFn(): '%s'\n", node->name()));
870 
871 		status_t err;
872 		NodeGroup* oldGroup = node->group();
873 		if(oldGroup) {
874 			err = oldGroup->removeNode(node);
875 			ASSERT(err == B_OK);
876 		}
877 
878 		err = newGroup->addNode(node);
879 		ASSERT(err == B_OK);
880 	}
881 };
882 
883 status_t NodeManager::splitGroup(
884 	NodeRef*											insideNode,
885 	NodeRef*											outsideNode,
886 	NodeGroup**										outGroup) {
887 
888 	ASSERT(insideNode);
889 	ASSERT(outsideNode);
890 
891 	Autolock _l(this);
892 
893 	// ensure that no route exists from insideNode to outsideNode
894 	if(findRoute(insideNode->id(), outsideNode->id())) {
895 		PRINT((
896 			"!!! NodeManager::splitGroup(): route exists from %ld to %ld\n",
897 			insideNode->id(), outsideNode->id()));
898 		return B_NOT_ALLOWED;
899 	}
900 
901 	// make sure the nodes share a common group
902 	NodeGroup* oldGroup = insideNode->group();
903 	if(!oldGroup) {
904 		PRINT((
905 			"!!! NodeManager::splitGroup(): invalid group\n",
906 			insideNode->id(), outsideNode->id()));
907 		return B_NOT_ALLOWED;
908 	}
909 	if(oldGroup != outsideNode->group()) {
910 		PRINT((
911 			"!!! NodeManager::splitGroup(): mismatched groups for %ld and %ld\n",
912 			insideNode->id(), outsideNode->id()));
913 		return B_NOT_ALLOWED;
914 	}
915 
916 	Autolock _l_old_group(oldGroup);
917 
918 	// create the new group
919 	BString nameBuffer = oldGroup->name();
920 	nameBuffer << " split";
921 
922 	NodeGroup* newGroup = createGroup(
923 		nameBuffer.String(),
924 		oldGroup->runMode());
925 	*outGroup = newGroup;
926 
927 	// move nodes connected to outsideNode from old to new group
928 	_changeNodeGroupFn fn(newGroup);
929 	fn(outsideNode);
930 
931 	_for_each_state st;
932 	_do_for_each_connected(
933 		this,
934 		outsideNode,
935 		oldGroup,
936 		true,
937 		fn,
938 		&st);
939 
940 	// [e.moon 1dec99] a single-node group takes that node's name
941 	if(newGroup->countNodes() == 1)
942 		newGroup->setName(newGroup->nodeAt(0)->name());
943 
944 	if(oldGroup->countNodes() == 1)
945 		oldGroup->setName(oldGroup->nodeAt(0)->name());
946 
947 	return B_OK;
948 }
949 
950 
951 // * INSTANTIATION & CONNECTION
952 //   Use these calls rather than the associated BMediaRoster()
953 //   methods to assure that the nodes and connections you set up
954 //   can be properly serialized & reconstituted.
955 
956 // basic BMediaRoster::InstantiateDormantNode() wrapper
957 
958 status_t NodeManager::instantiate(
959 	const dormant_node_info&			info,
960 	NodeRef**											outRef,
961 	bigtime_t											timeout,
962 	uint32												nodeFlags) {
963 
964 	Autolock _l(this);
965 	status_t err;
966 	D_METHOD((
967 		"NodeManager::instantiate()\n"));
968 
969 	// * instantiate
970 
971 	media_node node;
972 
973 	if(m_useAddOnHost) {
974 		err = AddOnHost::InstantiateDormantNode(
975 			info, &node, timeout);
976 
977 		if(err < B_OK) {
978 			node = media_node::null;
979 
980 			// attempt to relaunch
981 			BMessenger mess;
982 			err = AddOnHost::Launch(&mess);
983 			if(err < B_OK) {
984 				PRINT((
985 					"!!! NodeManager::instantiate(): giving up on AddOnHost\n"));
986 
987 				m_useAddOnHost = false;
988 			}
989 			else {
990 				err = AddOnHost::InstantiateDormantNode(
991 					info, &node, timeout);
992 			}
993 		}
994 	}
995 
996 	if(!m_useAddOnHost || node == media_node::null) {
997 		D_ROSTER((
998 			"# roster->InstantiateDormantNode()\n"));
999 		err = roster->InstantiateDormantNode(info, &node);
1000 	}
1001 
1002 	if(err < B_OK) {
1003 		*outRef = 0;
1004 		return err;
1005 	}
1006 
1007 	if(node == media_node::null) {
1008 		// [e.moon 23oct99] +++++
1009 		// instantiating a soundcard input/output (emu10k or sonic_vibes)
1010 		// produces a node ID of -1 (R4.5.2 x86)
1011 		//
1012 		PRINT((
1013 			"! InstantiateDormantNode(): invalid media node\n"));
1014 
1015 		// bail out
1016 		*outRef = 0;
1017 		return B_BAD_INDEX;
1018 	}
1019 
1020 	// * create NodeRef
1021 	NodeRef* ref = new NodeRef(
1022 		node,
1023 		this,
1024 		nodeFlags, // | NodeRef::NO_ROSTER_WATCH, // +++++ e.moon 11oct99
1025 		NodeRef::_INTERNAL);
1026 
1027 	ref->_setAddonHint(&info);
1028 	_addRef(ref);
1029 
1030 	// * return it
1031 	*outRef = ref;
1032 	return B_OK;
1033 }
1034 
1035 // SniffRef/Instantiate.../SetRefFor: a one-call interface
1036 // to create a node capable of playing a given media file.
1037 
1038 status_t NodeManager::instantiate(
1039 	const entry_ref&							file,
1040 	uint64												requireNodeKinds,
1041 	NodeRef**											outRef,
1042 	bigtime_t											timeout,
1043 	uint32												nodeFlags,
1044 	bigtime_t*										outDuration) {
1045 
1046 	D_METHOD((
1047 		"NodeManager::instantiate(ref)\n"));
1048 
1049 	// [no lock needed; calls the full form of instantiate()]
1050 
1051 	status_t err;
1052 
1053 	// * Find matching add-on
1054 	dormant_node_info info;
1055 
1056 	D_ROSTER((
1057 		"# roster->SniffRef()\n"));
1058 	err = roster->SniffRef(
1059 		file,
1060 		requireNodeKinds,
1061 		&info);
1062 	if(err < B_OK) {
1063 		*outRef = 0;
1064 		return err;
1065 	}
1066 
1067 	// * Instantiate
1068 	err = instantiate(info, outRef, timeout, nodeFlags);
1069 
1070 	if(err < B_OK)
1071 		return err;
1072 
1073 	ASSERT(*outRef);
1074 
1075 	// * Set file to play
1076 	bigtime_t dur;
1077 	D_ROSTER(("# roster->SetRefFor()\n"));
1078 	err = roster->SetRefFor(
1079 		(*outRef)->node(),
1080 		file,
1081 		false,
1082 		&dur);
1083 
1084 	if(err < B_OK) {
1085 		PRINT((
1086 			"* SetRefFor() failed: %s\n", strerror(err)));
1087 	}
1088 	else if(outDuration)
1089 		*outDuration = dur;
1090 
1091 	// * update info [e.moon 29sep99]
1092 	Autolock _l(*outRef);
1093 	(*outRef)->_setAddonHint(&info, &file);
1094 
1095 	return err;
1096 }
1097 
1098 // use this method to reference nodes internal to your
1099 // application.
1100 
1101 status_t NodeManager::reference(
1102 	BMediaNode*										node,
1103 	NodeRef**											outRef,
1104 	uint32												nodeFlags) {
1105 
1106 	Autolock _l(this);
1107 	D_METHOD((
1108 		"NodeManager::reference()\n"));
1109 
1110 	// should this node be marked _NO_RELEASE?
1111 	NodeRef* ref = new NodeRef(node->Node(), this, nodeFlags, 0);
1112 	_addRef(ref);
1113 
1114 	*outRef = ref;
1115 	return B_OK;
1116 }
1117 
1118 // the most flexible form of connect(): set the template
1119 // format as you would for BMediaRoster::Connect().
1120 
1121 status_t NodeManager::connect(
1122 	const media_output&						output,
1123 	const media_input&						input,
1124 	const media_format&						templateFormat,
1125 	Connection*										outConnection /*=0*/) {
1126 
1127 	Autolock _l(this);
1128 	status_t err;
1129 	D_METHOD((
1130 		"NodeManager::connect()\n"));
1131 
1132 	// * Find (& create if necessary) NodeRefs
1133 
1134 	NodeRef* outputRef;
1135 	if(getNodeRef(output.node.node, &outputRef) < B_OK)
1136 		outputRef = _addRefFor(output.node, 0);
1137 
1138 	NodeRef* inputRef;
1139 	if(getNodeRef(input.node.node, &inputRef) < B_OK)
1140 		inputRef = _addRefFor(input.node, 0);
1141 
1142 	// * Connect the nodes
1143 
1144 	media_output finalOutput;
1145 	media_input finalInput;
1146 	media_format finalFormat = templateFormat;
1147 
1148 	D_ROSTER(("# roster->Connect()\n"));
1149 	err = roster->Connect(
1150 		output.source,
1151 		input.destination,
1152 		&finalFormat,
1153 		&finalOutput,
1154 		&finalInput);
1155 
1156 	if(err < B_OK) {
1157 		if(outConnection)
1158 			*outConnection = Connection();
1159 		connectionFailed(output, input, templateFormat, err);
1160 		return err;
1161 	}
1162 
1163 	// * Create Connection instance; mark it INTERNAL
1164 	//   to automatically remove it upon shutdown.
1165 
1166 	D_METHOD((
1167 		"! creating connection:\n"
1168 		"  source id:    %ld\n"
1169 		"  source port:  %ld\n"
1170 		"  dest id:      %ld\n"
1171 		"  dest port:    %ld\n",
1172 		finalOutput.source.id, finalOutput.source.port,
1173 		finalInput.destination.id, finalInput.destination.port));
1174 
1175 	uint32 cflags = Connection::INTERNAL;
1176 	if(outputRef->m_info.node.kind & B_FILE_INTERFACE) {
1177 		// 3aug99:
1178 		// workaround for bug 19990802-12798:
1179 		//   connections involving a file-interface node aren't removed
1180 		cflags |= Connection::LOCKED;
1181 	}
1182 
1183 	Connection* con = new Connection(
1184 		m_nextConID++,
1185 		output.node,
1186 		finalOutput.source,
1187 		finalOutput.name,
1188 		input.node,
1189 		finalInput.destination,
1190 		finalInput.name,
1191 		finalFormat,
1192 		cflags);
1193 
1194 	con->setOutputHint(
1195 		output.name,
1196 		output.format);
1197 
1198 	con->setInputHint(
1199 		input.name,
1200 		input.format);
1201 
1202 	con->setRequestedFormat(
1203 		templateFormat);
1204 
1205 	_addConnection(con);
1206 
1207 	// [e.moon 10aug99]
1208 	// fetch updated latencies;
1209 	// [e.moon 28sep99]
1210 	// scan all nodes connected directly OR indirectly to the
1211 	// newly-connected nodes and update their latencies -- this includes
1212 	// recalculating the node's 'producer delay' if in B_RECORDING mode.
1213 
1214 	_updateLatenciesFrom(inputRef, true);
1215 
1216 	// copy connection
1217 	if(outConnection) {
1218 		*outConnection = *con;
1219 	}
1220 	return B_OK;
1221 }
1222 
1223 // format-guessing form of connect(): tries to find
1224 // a common format between output & input before connection;
1225 // returns B_MEDIA_BAD_FORMAT if no common format appears
1226 // possible.
1227 //
1228 // NOTE: the specifics of the input and output formats are ignored;
1229 //       this method only looks at the format type, and properly
1230 //       handles wildcards at that level (B_MEDIA_NO_TYPE).
1231 
1232 status_t NodeManager::connect(
1233 	const media_output&						output,
1234 	const media_input&						input,
1235 	Connection*										outConnection /*=0*/) {
1236 
1237 	D_METHOD((
1238 		"NodeManager::connect(guess)\n"));
1239 
1240 	// [no lock needed; calls the full form of connect()]
1241 
1242 	// defer to the pickier endpoint
1243 	media_format f;
1244 
1245 	if(output.format.type > B_MEDIA_UNKNOWN_TYPE) {
1246 		f = output.format;
1247 		if ((input.format.type > B_MEDIA_UNKNOWN_TYPE) &&
1248 			(f.type != input.format.type)) {
1249 			connectionFailed(output, input, f, B_MEDIA_BAD_FORMAT);
1250 			return B_MEDIA_BAD_FORMAT;
1251 		}
1252 	}
1253 	else if(input.format.type > B_MEDIA_UNKNOWN_TYPE) {
1254 		f = input.format;
1255 		// output node doesn't care
1256 	}
1257 	else {
1258 		// about as non-picky as two nodes can possibly be
1259 		f.type = B_MEDIA_UNKNOWN_TYPE;
1260 	}
1261 
1262 	// +++++ ? revert to wildcard ?
1263 
1264 	// let the nodes try to work out a common format from here
1265 	return connect(
1266 		output,
1267 		input,
1268 		f,
1269 		outConnection);
1270 }
1271 
1272 // disconnects the connection represented by the provided
1273 // Connection object.  if successful, returns B_OK.
1274 
1275 status_t NodeManager::disconnect(
1276 	const Connection&								connection) {
1277 
1278 	Autolock _l(this);
1279 	status_t err;
1280 	D_METHOD((
1281 		"NodeManager::disconnect()\n"));
1282 
1283 	// don't bother trying to disconnect an invalid connection
1284 	if(!connection.isValid())
1285 		return B_NOT_ALLOWED;
1286 
1287 	// make sure connection can be released
1288 	if(connection.flags() & Connection::LOCKED) {
1289 		PRINT((
1290 			"NodeManager::disconnect(): connection locked:\n"
1291 			"  %ld:%s -> %ld:%s\n",
1292 			connection.sourceNode(),
1293 			connection.outputName(),
1294 			connection.destinationNode(),
1295 			connection.inputName()));
1296 		return B_NOT_ALLOWED;
1297 	}
1298 
1299 	D_METHOD((
1300 		"! breaking connection:\n"
1301 		"  source node:  %ld\n"
1302 		"  source id:    %ld\n"
1303 		"  source port:  %ld\n"
1304 		"  dest node:    %ld\n"
1305 		"  dest id:      %ld\n"
1306 		"  dest port:    %ld\n",
1307 		connection.sourceNode(),
1308 		connection.source().id, connection.source().port,
1309 		connection.destinationNode(),
1310 		connection.destination().id, connection.destination().port));
1311 
1312 	// do it
1313 	D_ROSTER(("# roster->Disconnect()\n"));
1314 	err = roster->Disconnect(
1315 		connection.sourceNode(),
1316 		connection.source(),
1317 		connection.destinationNode(),
1318 		connection.destination());
1319 
1320 	// mark disconnected
1321 	if(err == B_OK) {
1322 		con_map::iterator it = m_conSourceMap.lower_bound(connection.sourceNode());
1323 		con_map::iterator itEnd = m_conSourceMap.upper_bound(connection.sourceNode());
1324 		for(; it != itEnd; ++it)
1325 			if((*it).second->id() == connection.id()) {
1326 				(*it).second->m_disconnected = true;
1327 				break;
1328 			}
1329 		ASSERT(it != itEnd);
1330 
1331 		// [e.moon 28sep99]
1332 		// fetch updated latencies;
1333 		// scan all nodes connected directly OR indirectly to the
1334 		// newly-connected nodes and update their latencies -- this includes
1335 		// recalculating the node's 'producer delay' if in B_RECORDING mode.
1336 
1337 		NodeRef* ref;
1338 		if(getNodeRef(connection.sourceNode(), &ref) == B_OK)
1339 			_updateLatenciesFrom(ref, true);
1340 		if(getNodeRef(connection.destinationNode(), &ref) == B_OK)
1341 			_updateLatenciesFrom(ref, true);
1342 
1343 	} else {
1344 		// +++++ if this failed, somebody somewhere is mighty confused
1345 		PRINT((
1346 			"NodeManager::disconnect(): Disconnect() failed:\n  %s\n",
1347 			strerror(err)));
1348 	}
1349 
1350 	return err;
1351 }
1352 
1353 // * GROUP CREATION
1354 
1355 NodeGroup* NodeManager::createGroup(
1356 	const char*										name,
1357 	BMediaNode::run_mode					runMode) {
1358 
1359 	Autolock _l(this);
1360 	D_METHOD((
1361 		"NodeManager::createGroup()\n"));
1362 
1363 	NodeGroup* g = new NodeGroup(name, this, runMode);
1364 	_addGroup(g);
1365 
1366 	return g;
1367 }
1368 
1369 // -------------------------------------------------------- //
1370 // *** node/connection iteration
1371 // *** MUST BE LOCKED for any of these calls
1372 // -------------------------------------------------------- //
1373 
1374 // usage:
1375 //   For the first call, pass 'cookie' a pointer to a void* set to 0.
1376 //   Returns B_BAD_INDEX when the set of nodes has been exhausted (and
1377 //   re-zeroes the cookie, cleaning up any unused memory.)
1378 
1379 status_t NodeManager::getNextRef(
1380 	NodeRef**											ref,
1381 	void**												cookie) {
1382 	ASSERT(IsLocked());
1383 	ASSERT(cookie);
1384 
1385 	if(!*cookie)
1386 		*cookie = new node_ref_map::iterator(m_nodeRefMap.begin());
1387 
1388 	node_ref_map::iterator* pit = (node_ref_map::iterator*)*cookie;
1389 
1390 	// at end of set?
1391 	if(*pit == m_nodeRefMap.end()) {
1392 		delete pit;
1393 		*cookie = 0;
1394 		return B_BAD_INDEX;
1395 	}
1396 
1397 	// return next entry
1398 	*ref = (*(*pit)).second;
1399 	++(*pit);
1400 	return B_OK;
1401 }
1402 
1403 // +++++ reworked 13sep99: dtors wouldn't have been called with 'delete *cookie'! +++++
1404 void NodeManager::disposeRefCookie(
1405 	void**												cookie) {
1406 
1407 	if(!cookie)
1408 		return;
1409 
1410 	node_ref_map::iterator* it =
1411 		reinterpret_cast<node_ref_map::iterator*>(*cookie);
1412 	ASSERT(it);
1413 	if(it)
1414 		delete it;
1415 }
1416 
1417 status_t NodeManager::getNextConnection(
1418 	Connection*										connection,
1419 	void**												cookie) {
1420 	ASSERT(IsLocked());
1421 	ASSERT(cookie);
1422 
1423 	if(!*cookie)
1424 		*cookie = new con_map::iterator(m_conSourceMap.begin());
1425 
1426 	con_map::iterator* pit = (con_map::iterator*)*cookie;
1427 
1428 	// at end of set?
1429 	if(*pit == m_conSourceMap.end()) {
1430 		delete pit;
1431 		*cookie = 0;
1432 		return B_BAD_INDEX;
1433 	}
1434 
1435 	// return next entry (ewww)
1436 	*connection = *((*(*pit)).second);
1437 	++(*pit);
1438 	return B_OK;
1439 }
1440 
1441 // +++++ reworked 13sep99: dtors wouldn't have been called with 'delete *cookie'! +++++
1442 void NodeManager::disposeConnectionCookie(
1443 	void**												cookie) {
1444 
1445 	if(!cookie)
1446 		return;
1447 
1448 	con_map::iterator* it =
1449 		reinterpret_cast<con_map::iterator*>(*cookie);
1450 	ASSERT(it);
1451 	if(it)
1452 		delete it;
1453 }
1454 
1455 
1456 // -------------------------------------------------------- //
1457 // *** BHandler impl
1458 // -------------------------------------------------------- //
1459 
1460 // +++++ support all Media Roster messages! +++++
1461 
1462 // [e.moon 7nov99] implemented observer pattern for NodeGroup
1463 void NodeManager::MessageReceived(BMessage* message) {
1464 
1465 	D_MESSAGE((
1466 		"NodeManager::MessageReceived(): %c%c%c%c\n",
1467 		 message->what >> 24,
1468 		(message->what >> 16)	& 0xff,
1469 		(message->what >> 8)	& 0xff,
1470 		(message->what) 			& 0xff));
1471 
1472 	switch(message->what) {
1473 
1474 		// *** Media Roster messages ***
1475 
1476 		case B_MEDIA_NODE_CREATED:
1477 			if(_handleNodesCreated(message) == B_OK)
1478 				notify(message);
1479 			break;
1480 
1481 		case B_MEDIA_NODE_DELETED:
1482 			_handleNodesDeleted(message);
1483 			notify(message);
1484 			break;
1485 
1486 		case B_MEDIA_CONNECTION_MADE:
1487 			_handleConnectionMade(message);
1488 			notify(message);
1489 			break;
1490 
1491 		case B_MEDIA_CONNECTION_BROKEN:
1492 			_handleConnectionBroken(message); // augments message!
1493 			notify(message);
1494 			break;
1495 
1496 		case B_MEDIA_FORMAT_CHANGED:
1497 			_handleFormatChanged(message);
1498 			notify(message);
1499 			break;
1500 
1501 		default:
1502 			_inherited::MessageReceived(message);
1503 			break;
1504 	}
1505 }
1506 
1507 // -------------------------------------------------------- //
1508 // *** IObservable hooks
1509 // -------------------------------------------------------- //
1510 
1511 void NodeManager::observerAdded(
1512 	const BMessenger&				observer) {
1513 
1514 	BMessage m(M_OBSERVER_ADDED);
1515 	m.AddMessenger("target", BMessenger(this));
1516 	observer.SendMessage(&m);
1517 }
1518 
1519 
1520 void NodeManager::observerRemoved(
1521 	const BMessenger&				observer) {
1522 
1523 	BMessage m(M_OBSERVER_REMOVED);
1524 	m.AddMessenger("target", BMessenger(this));
1525 	observer.SendMessage(&m);
1526 }
1527 
1528 void NodeManager::notifyRelease() {
1529 
1530 	BMessage m(M_RELEASED);
1531 	m.AddMessenger("target", BMessenger(this));
1532 	notify(&m);
1533 }
1534 
1535 void NodeManager::releaseComplete() {
1536 	// tear down media roster connection
1537 	D_ROSTER(("# roster->StopWatching()\n"));
1538 	status_t err = roster->StopWatching(
1539 		BMessenger(this));
1540 	if(err < B_OK) {
1541 		PRINT((
1542 			"* roster->StopWatching() failed: %s\n", strerror(err)));
1543 	}
1544 }
1545 
1546 
1547 // -------------------------------------------------------- //
1548 // *** ILockable impl.
1549 // -------------------------------------------------------- //
1550 
1551 bool NodeManager::lock(
1552 	lock_t												type,
1553 	bigtime_t											timeout) {
1554 
1555 	D_LOCK(("*** NodeManager::lock(): %ld\n", find_thread(0)));
1556 
1557 	status_t err = LockWithTimeout(timeout);
1558 
1559 
1560 	D_LOCK(("*** NodeManager::lock() ACQUIRED: %ld\n", find_thread(0)));
1561 
1562 	return err == B_OK;
1563 }
1564 
1565 bool NodeManager::unlock(
1566 	lock_t												type) {
1567 
1568 	D_LOCK(("*** NodeManager::unlock(): %ld\n", find_thread(0)));
1569 
1570 	Unlock();
1571 
1572 	D_LOCK(("*** NodeManager::unlock() RELEASED: %ld\n", find_thread(0)));
1573 
1574 	return true;
1575 }
1576 
1577 bool NodeManager::isLocked(
1578 	lock_t												type) const {
1579 
1580 	return IsLocked();
1581 }
1582 
1583 // -------------------------------------------------------- //
1584 // *** internal operations (LOCK REQUIRED)
1585 // -------------------------------------------------------- //
1586 
1587 void NodeManager::_initCommonNodes() {
1588 
1589 	ASSERT(IsLocked());
1590 	status_t err;
1591 	media_node node;
1592 
1593 	D_METHOD((
1594 		"NodeManager::_initCommonNodes()\n"));
1595 
1596 	uint32 disableTransport =
1597 		(NodeRef::NO_START_STOP | NodeRef::NO_SEEK | NodeRef::NO_PREROLL);
1598 
1599 	// video input
1600 	D_ROSTER(("# roster->GetVideoInput()\n"));
1601 	err = roster->GetVideoInput(&node);
1602 	if(err == B_OK)
1603 		m_videoInputNode = _addRefFor(
1604 			node,
1605 			_userFlagsForKind(node.kind),
1606 			_implFlagsForKind(node.kind));
1607 
1608 	// video output
1609 	D_ROSTER(("# roster->GetVideoOutput()\n"));
1610 	err = roster->GetVideoOutput(&node);
1611 	if(err == B_OK) {
1612 		if(m_videoInputNode && node.node == m_videoInputNode->id()) {
1613 			// input and output nodes identical
1614 			// [e.moon 20dec99]
1615 			m_videoOutputNode = m_videoInputNode;
1616 		}
1617 		else {
1618 			m_videoOutputNode = _addRefFor(
1619 				node,
1620 				_userFlagsForKind(node.kind) & ~NodeRef::NO_START_STOP,
1621 				_implFlagsForKind(node.kind));
1622 		}
1623 	}
1624 
1625 	// audio mixer
1626 	D_ROSTER(("# roster->GetAudioMixer()\n"));
1627 	err = roster->GetAudioMixer(&node);
1628 	if(err == B_OK)
1629 		m_audioMixerNode = _addRefFor(
1630 			node,
1631 			_userFlagsForKind(node.kind) | disableTransport,
1632 			_implFlagsForKind(node.kind));
1633 
1634 	// audio input
1635 	D_ROSTER(("# roster->GetAudioInput()\n"));
1636 	err = roster->GetAudioInput(&node);
1637 	if(err == B_OK)
1638 		m_audioInputNode = _addRefFor(
1639 			node,
1640 			_userFlagsForKind(node.kind),
1641 			_implFlagsForKind(node.kind));
1642 
1643 	// audio output
1644 	D_ROSTER(("# roster->GetAudioOutput()\n"));
1645 	err = roster->GetAudioOutput(&node);
1646 	if(err == B_OK) {
1647 		if(m_audioInputNode && node.node == m_audioInputNode->id()) {
1648 			// input and output nodes identical
1649 			// [e.moon 20dec99]
1650 			m_audioOutputNode = m_audioInputNode;
1651 
1652 			// disable transport controls (the default audio output must always
1653 			// be running, and can't be seeked.)
1654 
1655 			m_audioOutputNode->setFlags(
1656 				m_audioOutputNode->flags() | disableTransport);
1657 		}
1658 		else {
1659 			m_audioOutputNode = _addRefFor(
1660 				node,
1661 				_userFlagsForKind(node.kind) | disableTransport,
1662 				_implFlagsForKind(node.kind));
1663 		}
1664 	}
1665 }
1666 
1667 void NodeManager::_addRef(
1668 	NodeRef*											ref) {
1669 
1670 	ASSERT(ref);
1671 	ASSERT(IsLocked());
1672 
1673 	D_METHOD((
1674 		"NodeManager::_addRef()\n"));
1675 
1676 	// precondition: no NodeRef yet exists for this node
1677 	// +++++
1678 	// [e.moon 21oct99]
1679 	// <markjan@xs4all.nl> sez this fails on startup w/ MediaKit 10.5
1680 	ASSERT(
1681 		m_nodeRefMap.find(ref->id()) == m_nodeRefMap.end());
1682 
1683 	// add it
1684 	// [e.moon 13oct99] PPC-friendly
1685 	m_nodeRefMap.insert(node_ref_map::value_type(ref->id(), ref));
1686 
1687 	// [e.moon 8nov99] call hook
1688 	nodeCreated(ref);
1689 }
1690 
1691 void NodeManager::_removeRef(
1692 	media_node_id									id) {
1693 	ASSERT(IsLocked());
1694 
1695 	D_METHOD((
1696 		"NodeManager::_removeRef()\n"));
1697 
1698 	node_ref_map::iterator it = m_nodeRefMap.find(id);
1699 
1700 	// precondition: a NodeRef w/ matching ID is in the map
1701 	ASSERT(it != m_nodeRefMap.end());
1702 
1703 	// [e.moon 8nov99] call hook
1704 	nodeDeleted((*it).second);
1705 
1706 	// remove it
1707 	m_nodeRefMap.erase(it);
1708 }
1709 
1710 // create, add, return a NodeRef for the given external node;
1711 // must not already exist
1712 NodeRef* NodeManager::_addRefFor(
1713 	const media_node&							node,
1714 	uint32												nodeFlags,
1715 	uint32												nodeImplFlags) {
1716 
1717 	ASSERT(IsLocked());
1718 
1719 	D_METHOD((
1720 		"NodeManager::_addRefFor()\n"));
1721 
1722 	// precondition: no NodeRef yet exists for this node
1723 	ASSERT(
1724 		m_nodeRefMap.find(node.node) == m_nodeRefMap.end());
1725 
1726 //	// precondition: the node actually exists
1727 //	live_node_info info;
1728 //	D_ROSTER(("# roster->GetLiveNodeInfo()\n"));
1729 //	ASSERT(roster->GetLiveNodeInfo(node, &info) == B_OK);
1730 
1731 	// * create & add ref
1732 	NodeRef* ref = new NodeRef(node, this, nodeFlags, nodeImplFlags);
1733 	_addRef(ref);
1734 
1735 	return ref;
1736 }
1737 
1738 void NodeManager::_addConnection(
1739 	Connection*										connection) {
1740 
1741 	ASSERT(connection);
1742 	ASSERT(IsLocked());
1743 
1744 	D_METHOD((
1745 		"NodeManager::_addConnection()\n"));
1746 
1747 	// precondition: not already entered in either source or dest map
1748 	// +++++ a more rigorous test would be a very good thing
1749 #ifdef DEBUG
1750 	for(con_map::iterator it = m_conSourceMap.lower_bound(connection->sourceNode());
1751 		it != m_conSourceMap.upper_bound(connection->sourceNode()); ++it) {
1752 		ASSERT((*it).second->id() != connection->id());
1753 	}
1754 	for(con_map::iterator it = m_conDestinationMap.lower_bound(connection->destinationNode());
1755 		it != m_conDestinationMap.upper_bound(connection->destinationNode()); ++it) {
1756 		ASSERT((*it).second->id() != connection->id());
1757 	}
1758 #endif
1759 
1760 	// add to both maps
1761 	// [e.moon 13oct99] PPC-friendly
1762 	m_conSourceMap.insert(
1763 		con_map::value_type(
1764 			connection->sourceNode(),
1765 			connection));
1766 	m_conDestinationMap.insert(
1767 		con_map::value_type(
1768 			connection->destinationNode(),
1769 			connection));
1770 
1771 	// [e.moon 8nov99] call hook
1772 	connectionMade(connection);
1773 }
1774 
1775 void NodeManager::_removeConnection(
1776 	const Connection&							connection) {
1777 
1778 	ASSERT(IsLocked());
1779 	con_map::iterator itSource, itDestination;
1780 
1781 	D_METHOD((
1782 		"NodeManager::_removeConnection()\n"));
1783 
1784 	// [e.moon 8nov99] call hook
1785 	connectionBroken(&connection);
1786 
1787 	// precondition: one entry in both source & dest maps
1788 	// +++++ a more rigorous test would be a very good thing
1789 
1790 	for(
1791 		itSource = m_conSourceMap.lower_bound(connection.sourceNode());
1792 		itSource != m_conSourceMap.upper_bound(connection.sourceNode());
1793 		++itSource)
1794 		if((*itSource).second->id() == connection.id())
1795 			break;
1796 
1797 	ASSERT(itSource != m_conSourceMap.end());
1798 
1799 	for(
1800 		itDestination = m_conDestinationMap.lower_bound(connection.destinationNode());
1801 		itDestination != m_conDestinationMap.upper_bound(connection.destinationNode());
1802 		++itDestination)
1803 		if((*itDestination).second->id() == connection.id())
1804 			break;
1805 
1806 	ASSERT(itDestination != m_conDestinationMap.end());
1807 
1808 	// delete & remove from both maps
1809 	delete (*itSource).second;
1810 	m_conSourceMap.erase(itSource);
1811 	m_conDestinationMap.erase(itDestination);
1812 }
1813 
1814 void NodeManager::_addGroup(
1815 	NodeGroup*										group) {
1816 
1817 	ASSERT(group);
1818 	ASSERT(IsLocked());
1819 
1820 	D_METHOD((
1821 		"NodeManager::_addGroup()\n"));
1822 
1823 	// precondition: group not already in set
1824 	ASSERT(
1825 		find(
1826 			m_nodeGroupSet.begin(),
1827 			m_nodeGroupSet.end(),
1828 			group) == m_nodeGroupSet.end());
1829 
1830 	// add
1831 	m_nodeGroupSet.push_back(group);
1832 
1833 //	// [e.moon 7nov99] observe
1834 //	add_observer(this, group);
1835 }
1836 
1837 void NodeManager::_removeGroup(
1838 	NodeGroup*										group) {
1839 
1840 	ASSERT(group);
1841 	ASSERT(IsLocked());
1842 
1843 	D_METHOD((
1844 		"NodeManager::_removeGroup()\n"));
1845 
1846 	node_group_set::iterator it = find(
1847 		m_nodeGroupSet.begin(),
1848 		m_nodeGroupSet.end(),
1849 		group);
1850 
1851 	// precondition: group in set
1852 	if(it == m_nodeGroupSet.end()) {
1853 		PRINT((
1854 			"* NodeManager::_removeGroup(%ld): group not in set.\n",
1855 			group->id()));
1856 		return;
1857 	}
1858 
1859 	// remove it
1860 	m_nodeGroupSet.erase(it);
1861 }
1862 
1863 // -------------------------------------------------------- //
1864 // *** Message Handlers ***
1865 // -------------------------------------------------------- //
1866 
1867 
1868 // now returns B_OK iff the message should be relayed to observers
1869 // [e.moon 11oct99]
1870 
1871 inline status_t NodeManager::_handleNodesCreated(
1872 	BMessage*											message) {
1873 	ASSERT(IsLocked());
1874 
1875 	status_t err = B_OK;
1876 
1877 	// fetch number of new nodes
1878 	type_code type;
1879 	int32 count;
1880 	err = message->GetInfo("media_node_id", &type, &count);
1881 	if(err < B_OK) {
1882 		PRINT((
1883 			"* NodeManager::_handleNodesCreated(): GetInfo() failed:\n"
1884 			"  %s\n",
1885 			strerror(err)));
1886 		return err;
1887 	}
1888 	if(!count) {
1889 		PRINT((
1890 			"* NodeManager::_handleNodesCreated(): no node IDs in message.\n"));
1891 		return err;
1892 	}
1893 
1894 	D_METHOD((
1895 		"NodeManager::_handleNodesCreated(): %d nodes\n",
1896 		count));
1897 
1898 	// * Create NodeRef instances for the listed nodes.
1899 	//   If this is the first message received from the Media Roster,
1900 	//   no connection info will be received for these nodes; store them
1901 	//   for now (indexed by service-thread port) and figure the connections
1902 	//   afterwards.
1903 	//   These nodes are mapped by port ID because that's the only criterion
1904 	//   available for matching a media_source to a node.
1905 
1906 	typedef map<port_id, NodeRef*> port_ref_map;
1907 	port_ref_map* initialNodes = m_existingNodesInit ? 0 : new port_ref_map;
1908 
1909 	bool refsCreated = false;
1910 
1911 	for(int32 n = 0; n < count; ++n) {
1912 		// fetch ID of next node
1913 		int32 id;
1914 		err = message->FindInt32("media_node_id", n, &id);
1915 		if(err < B_OK) {
1916 			PRINT((
1917 				"* NodeManager::_handleNodesCreated(): FindInt32() failed:\n"
1918 				"  %s", strerror(err)));
1919 			continue;
1920 		}
1921 
1922 		// look up the node
1923 		media_node node;
1924 		err = roster->GetNodeFor(id, &node);
1925 		if(err < B_OK) {
1926 			PRINT((
1927 				"* NodeManager::_handleNodesCreated(): roster->GetNodeFor(%ld) failed:\n"
1928 				"  %s\n",
1929 				id, strerror(err)));
1930 			continue;
1931 		}
1932 
1933 		// look for an existing NodeRef; if not found, create one:
1934 		NodeRef* ref = 0;
1935 		if(getNodeRef(node.node, &ref) < B_OK) {
1936 			// create one
1937 			ref = _addRefFor(
1938 				node,
1939 				_userFlagsForKind(node.kind), // | NodeRef::NO_ROSTER_WATCH, // +++++ e.moon 11oct99
1940 				_implFlagsForKind(node.kind) | NodeRef::_CREATE_NOTIFIED);
1941 
1942 			refsCreated = true;
1943 
1944 //			// [e.moon 7nov99] call hook
1945 //			nodeCreated(ref);
1946 
1947 		} else {
1948 //			PRINT((
1949 //				"* NodeManager::_handleNodesCreated():\n"
1950 //				"  found existing ref for '%s' (%ld)\n",
1951 //				ref->name(), id));
1952 
1953 
1954 			// set _CREATE_NOTIFIED to prevent notification from being passed on
1955 			// twice [e.moon 11oct99]
1956 			if(!(ref->m_implFlags & NodeRef::_CREATE_NOTIFIED)) {
1957 				ref->m_implFlags |= NodeRef::_CREATE_NOTIFIED;
1958 				refsCreated = true;
1959 			}
1960 
1961 			// release the (duplicate) media_node reference
1962 			err = roster->ReleaseNode(node);
1963 			if(err < B_OK) {
1964 				PRINT((
1965 					"* NodeManager::_handleNodesCreated(): roster->ReleaseNode(%ld) failed:\n"
1966 					"  %s\n",
1967 					id, strerror(err)));
1968 			}
1969 		}
1970 
1971 		// add to the 'initial nodes' set if necessary
1972 		// [e.moon 13oct99] PPC-friendly
1973 		if(initialNodes)
1974 			initialNodes->insert(
1975 				port_ref_map::value_type(
1976 					node.port, ref));
1977 	}
1978 
1979 	if(initialNodes) {
1980 		// populate current connections from each node in the set
1981 //		PRINT((
1982 //			"* NodeManager::_handleNodesCreated(): POPULATING CONNECTIONS (%ld)\n",
1983 //			initialNodes->size()));
1984 
1985 		for(port_ref_map::const_iterator itDest = initialNodes->begin();
1986 			itDest != initialNodes->end(); ++itDest) {
1987 
1988 			// walk each connected input for this node; find corresponding
1989 			// output & fill in a new Connection instance.
1990 
1991 			NodeRef* destRef = (*itDest).second;
1992 			ASSERT(destRef);
1993 			if(!(destRef->kind() & B_BUFFER_CONSUMER))
1994 				// no inputs
1995 				continue;
1996 
1997 			vector<media_input> inputs;
1998 			err = destRef->getConnectedInputs(inputs);
1999 
2000 			// +++++ FAILED ON STARTUP [e.moon 28sep99]; haven't reproduced yet
2001 			//       [21oct99] failed again
2002 			//ASSERT(err == B_OK);
2003 			if(err < B_OK) {
2004 				PRINT((
2005 					"!!! NodeManager::_handleNodesCreated():\n"
2006 					"    NodeRef('%s')::getConnectedInputs() failed:\n"
2007 					"    %s\n",
2008 					destRef->name(), strerror(err)));
2009 
2010 				continue;
2011 			}
2012 
2013 
2014 //			PRINT((" - %s: %ld inputs\n", destRef->name(), inputs.size()));
2015 
2016 			for(vector<media_input>::const_iterator itInput = inputs.begin();
2017 				itInput != inputs.end(); ++itInput) {
2018 
2019 				// look for a matching source node by port ID:
2020 				const media_input& input = *itInput;
2021 				port_ref_map::const_iterator itSource = initialNodes->find(
2022 					input.source.port);
2023 
2024 				if(itSource == initialNodes->end()) {
2025 					// source not found!
2026 					PRINT((
2027 						"* NodeManager::_handleNodesCreated():\n"
2028 						"  Building initial Connection set: couldn't find source node\n"
2029 						"  connected to input '%s' of '%s' (source port %d).\n",
2030 						input.name, destRef->name(), input.source.port));
2031 					continue;
2032 				}
2033 
2034 				// found it; fetch matching output
2035 				NodeRef* sourceRef = (*itSource).second;
2036 				ASSERT(sourceRef);
2037 				media_output output;
2038 				err = sourceRef->findOutput(input.source, &output);
2039 				if(err < B_OK) {
2040 					PRINT((
2041 						"* NodeManager::_handleNodesCreated():\n"
2042 						"  Building initial Connection set: couldn't find output\n"
2043 						"  in node '%s' connected to input '%s' of '%s'.\n",
2044 						sourceRef->name(),
2045 						input.name, destRef->name()));
2046 					continue;
2047 				}
2048 
2049 				// sanity check
2050 //				ASSERT(input.source == output.source);
2051 //				ASSERT(input.destination == output.destination);
2052 				// diagnostics [e.moon 11jan00]
2053 				if(input.source != output.source ||
2054 					input.destination != output.destination) {
2055 					PRINT((
2056 						"!!! NodeManager::_handleNodesCreated():\n"
2057 						"    input/output mismatch for connection\n"
2058 						"    '%s' (%s) -> '%s' (%s)\n"
2059 						"    input.source:        port %ld, ID %ld\n"
2060 						"    output.source:       port %ld, ID %ld\n"
2061 						"    input.destination:   port %ld, ID %ld\n"
2062 						"    output.destination:  port %ld, ID %ld\n\n",
2063 						sourceRef->name(), output.name,
2064 						destRef->name(), input.name,
2065 						input.source.port, input.source.id,
2066 						output.source.port, output.source.id,
2067 						input.destination.port, input.destination.id,
2068 						output.destination.port, output.destination.id));
2069 					continue;
2070 				}
2071 
2072 				// instantiate & add connection
2073 
2074 				uint32 flags = 0;
2075 
2076 				// 22sep99: lock audio-mixer connection
2077 				if(
2078 					sourceRef == m_audioMixerNode &&
2079 					destRef == m_audioOutputNode)
2080 					flags |= Connection::LOCKED;
2081 
2082 				Connection* con = new Connection(
2083 					m_nextConID++,
2084 					output.node,
2085 					output.source,
2086 					output.name,
2087 					input.node,
2088 					input.destination,
2089 					input.name,
2090 					input.format,
2091 					flags);
2092 
2093 				_addConnection(con);
2094 
2095 //				// [e.moon 7nov99] call hook
2096 //				connectionMade(con);
2097 
2098 //				PRINT((
2099 //					"* NodeManager::_handleNodesCreated(): Found initial connection:\n"
2100 //					"  %s:%s -> %s:%s\n",
2101 //					sourceRef->name(), con->outputName(),
2102 //					destRef->name(), con->inputName()));
2103 
2104 			} // for(vector<media_input> ...
2105 
2106 		} // for(port_ref_map ...
2107 
2108 		// mark the ordeal as over & done with
2109 		m_existingNodesInit = true;
2110 		// clean up
2111 		delete initialNodes;
2112 	}
2113 
2114 	// don't relay message if no new create notifications were received [e.moon 11oct99]
2115 	return refsCreated ? B_OK : B_ERROR;
2116 }
2117 
2118 inline void NodeManager::_handleNodesDeleted(
2119 	BMessage*											message) {
2120 	ASSERT(IsLocked());
2121 
2122 	D_METHOD((
2123 		"NodeManager::_handleNodesDeleted()\n"));
2124 
2125 	// walk the list of deleted nodes, removing & cleaning up refs
2126 	// (and any straggler connections)
2127 
2128 	type_code type;
2129 	int32 count;
2130 	status_t err = message->GetInfo("media_node_id", &type, &count);
2131 	if(err < B_OK) {
2132 		PRINT((
2133 			"* NodeManager::_handleNodesDeleted(): GetInfo() failed:\n"
2134 			"  %s\n",
2135 			strerror(err)));
2136 		return;
2137 	}
2138 	if(!count)
2139 		return;
2140 
2141 	for(int32 n = 0; n < count; n++) {
2142 
2143 		int32 id;
2144 		err = message->FindInt32("media_node_id", n, &id);
2145 		if(err < B_OK) {
2146 			PRINT((
2147 				"* NodeManager::_handleNodesDeleted(): FindInt32() failed\n"
2148 				"  %s\n",
2149 				strerror(err)));
2150 			continue;
2151 		}
2152 
2153 		// fetch ref
2154 		NodeRef* ref;
2155 		err = getNodeRef(id, &ref);
2156 		if(err < B_OK) {
2157 			PRINT((
2158 				"* NodeManager::_handleNodesDeleted(): getNodeRef(%ld) failed\n"
2159 				"  %s\n",
2160 				id, strerror(err)));
2161 			continue;
2162 		}
2163 
2164 		// find & remove any 'stuck' connections; notify any observers
2165 		// that the connections have been removed
2166 		vector<Connection> stuckConnections;
2167 		ref->getInputConnections(stuckConnections);
2168 		ref->getOutputConnections(stuckConnections);
2169 
2170 		BMessage message(B_MEDIA_CONNECTION_BROKEN);
2171 
2172 		for(uint32 n = 0; n < stuckConnections.size(); ++n) {
2173 			Connection& c = stuckConnections[n];
2174 
2175 			// generate a complete B_MEDIA_CONNECTION_BROKEN message
2176 			// +++++ the message format may be extended in the future -- ick
2177 
2178 			message.AddData("source", B_RAW_TYPE, &c.source(), sizeof(media_source));
2179 			message.AddData("destination", B_RAW_TYPE, &c.destination(), sizeof(media_destination));
2180 			message.AddInt32(_connectionField, c.id());
2181 			message.AddInt32(_sourceNodeField, c.sourceNode());
2182 			message.AddInt32(_destNodeField, c.destinationNode());
2183 
2184 			_removeConnection(c);
2185 		}
2186 
2187 		// +++++ don't notify if no stuck connections were found
2188 		notify(&message);
2189 
2190 		// ungroup if necessary
2191 		if(ref->m_group) {
2192 			ASSERT(!ref->isReleased());
2193 			ref->m_group->removeNode(ref);
2194 		}
2195 
2196 		// [e.moon 23oct99] mark the node released!
2197 		ref->m_nodeReleased = true;
2198 
2199 //		// [e.moon 7nov99] call hook
2200 //		nodeDeleted(ref);
2201 
2202 		// release it
2203 		ref->release();
2204 
2205 	} // for(int32 n ...
2206 }
2207 
2208 inline void NodeManager::_handleConnectionMade(
2209 	BMessage*											message) {
2210 	ASSERT(IsLocked());
2211 	D_METHOD((
2212 		"NodeManager::_handleConnectionMade()\n"));
2213 	status_t err;
2214 
2215 	for(int32 n = 0;;++n) {
2216 		media_input input;
2217 		media_output output;
2218 		const void* data;
2219 		ssize_t dataSize;
2220 
2221 		// fetch output
2222 		err = message->FindData("output", B_RAW_TYPE, n, &data, &dataSize);
2223 		if(err < B_OK) {
2224 			if(!n) {
2225 				PRINT((
2226 					"* NodeManager::_handleConnectionMade(): no entries in message.\n"));
2227 			}
2228 			break;
2229 		}
2230 		if(dataSize < ssize_t(sizeof(media_output))) {
2231 			PRINT((
2232 				"* NodeManager::_handleConnectionMade(): not enough data for output.\n"));
2233 			break;
2234 		}
2235 		output = *(media_output*)data;
2236 
2237 		// fetch input
2238 		err = message->FindData("input", B_RAW_TYPE, n, &data, &dataSize);
2239 		if(err < B_OK) {
2240 			if(!n) {
2241 				PRINT((
2242 					"* NodeManager::_handleConnectionMade(): no complete entries in message.\n"));
2243 			}
2244 			break;
2245 		}
2246 		if(dataSize < ssize_t(sizeof(media_input))) {
2247 			PRINT((
2248 				"* NodeManager::_handleConnectionMade(): not enough data for input.\n"));
2249 			break;
2250 		}
2251 		input = *(media_input*)data;
2252 
2253 		// look for existing Connection instance
2254 		Connection found;
2255 		err = findConnection(
2256 			output.node.node,
2257 			output.source,
2258 			&found);
2259 		if(err == B_OK) {
2260 			PRINT((
2261 				"  - existing connection for %s -> %s found\n",
2262 				found.outputName(), found.inputName()));
2263 			continue;
2264 		}
2265 
2266 		// instantiate & add Connection
2267 		Connection* con = new Connection(
2268 			m_nextConID++,
2269 			output.node,
2270 			output.source,
2271 			output.name,
2272 			input.node,
2273 			input.destination,
2274 			input.name,
2275 			input.format,
2276 			0);
2277 
2278 		_addConnection(con);
2279 	}
2280 }
2281 
2282 // augments message with source and destination node ID's
2283 inline void NodeManager::_handleConnectionBroken(
2284 	BMessage*											message) {
2285 
2286 	D_METHOD((
2287 		"NodeManager::_handleConnectionBroken()\n"));
2288 	status_t err;
2289 
2290 	// walk the listed connections
2291 	for(int32 n=0;;n++) {
2292 		media_source source;
2293 
2294 		const void* data;
2295 		ssize_t dataSize;
2296 
2297 		// fetch source
2298 		err = message->FindData("source", B_RAW_TYPE, n, &data, &dataSize);
2299 		if(err < B_OK) {
2300 			if(!n) {
2301 				PRINT((
2302 					"* NodeManager::_handleConnectionBroken(): incomplete entry in message.\n"));
2303 			}
2304 			break;
2305 		}
2306 		if(dataSize < ssize_t(sizeof(media_source))) {
2307 			PRINT((
2308 				"* NodeManager::_handleConnectionBroken(): not enough data for source.\n"));
2309 			continue;
2310 		}
2311 		source = *(media_source*)data;
2312 
2313 		// look up the connection +++++ SLOW +++++
2314 		Connection con;
2315 		err = findConnection(source, &con);
2316 		if(err < B_OK) {
2317 			PRINT((
2318 				"* NodeManager::_handleConnectionBroken(): connection not found:\n"
2319 				"  %ld:%ld\n",
2320 				source.port, source.id));
2321 
2322 			// add empty entry to message
2323 			message->AddInt32(_connectionField, 0);
2324 			message->AddInt32(_sourceNodeField, 0);
2325 			message->AddInt32(_destNodeField, 0);
2326 			continue;
2327 		}
2328 
2329 		// add entry to the message
2330 		message->AddInt32(_connectionField, con.id());
2331 		message->AddInt32(_sourceNodeField, con.sourceNode());
2332 		message->AddInt32(_destNodeField, con.destinationNode());
2333 
2334 //		// [e.moon 7nov99] call hook
2335 //		connectionBroken(&con);
2336 
2337 		// home free; delete the connection
2338 		_removeConnection(con);
2339 
2340 	} // for(int32 n ...
2341 }
2342 
2343 inline void
2344 NodeManager::_handleFormatChanged(BMessage *message)
2345 {
2346 	D_METHOD((
2347 		"NodeManager::_handleFormatChanged()\n"));
2348 	status_t err;
2349 
2350 	ssize_t dataSize;
2351 
2352 	// fetch source
2353 	media_source* source;
2354 	err = message->FindData("be:source", B_RAW_TYPE, (const void**)&source, &dataSize);
2355 	if(err < B_OK) {
2356 		PRINT((
2357 			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2358 		return;
2359 	}
2360 
2361 	// fetch destination
2362 	media_destination* destination;
2363 	err = message->FindData("be:destination", B_RAW_TYPE, (const void**)&destination, &dataSize);
2364 	if(err < B_OK) {
2365 		PRINT((
2366 			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2367 		return;
2368 	}
2369 
2370 	// fetch format
2371 	media_format* format;
2372 	err = message->FindData("be:format", B_RAW_TYPE, (const void**)&format, &dataSize);
2373 	if(err < B_OK) {
2374 		PRINT((
2375 			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2376 		return;
2377 	}
2378 
2379 	// find matching connection
2380 	for(con_map::const_iterator it = m_conSourceMap.begin();
2381 		it != m_conSourceMap.end(); ++it) {
2382 		if((*it).second->source() == *source) {
2383 			if((*it).second->destination() != *destination) {
2384 				// connection defunct
2385 				return;
2386 			}
2387 
2388 			// found
2389 			(*it).second->m_format = *format;
2390 
2391 			// attach node IDs to message
2392 			message->AddInt32(_connectionField, (*it).second->id());
2393 			message->AddInt32(_sourceNodeField, (*it).second->sourceNode());
2394 			message->AddInt32(_destNodeField, (*it).second->destinationNode());
2395 
2396 			break;
2397 		}
2398 	}
2399 }
2400 
2401 
2402 // return flags appropriate for an external
2403 // node with the given 'kind'
2404 
2405 inline uint32 NodeManager::_userFlagsForKind(
2406 	uint32												kind) {
2407 
2408 	uint32 f = 0;
2409 	if(
2410 //		kind & B_TIME_SOURCE ||
2411 		kind & B_PHYSICAL_OUTPUT
2412 		// || kind & B_SYSTEM_MIXER [now in initCommonNodes() e.moon 17nov99]
2413 		)
2414 		f |= (NodeRef::NO_START_STOP | NodeRef::NO_SEEK | NodeRef::NO_PREROLL);
2415 
2416 //	// [28sep99 e.moon] physical inputs may not be stopped for now; at
2417 //	// least one sound input node (for emu10k) stops working when requested
2418 //	// to stop.
2419 //	// +++++ should this logic be in initCommonNodes()?
2420 //	if(
2421 //		kind & B_PHYSICAL_INPUT)
2422 //		f |= NodeRef::NO_STOP;
2423 
2424 	return f;
2425 }
2426 
2427 inline uint32 NodeManager::_implFlagsForKind(
2428 	uint32												kind) {
2429 
2430 	return 0;
2431 }
2432 
2433 // [e.moon 28sep99] latency updating
2434 // These methods must set the recording-mode delay for
2435 // any B_RECORDING nodes they handle.
2436 
2437 // +++++ abstract to 'for each' and 'for each from'
2438 //       methods (template or callback?)
2439 
2440 
2441 // refresh cached latency for every node in the given group
2442 // (or all nodes if no group given.)
2443 
2444 inline void NodeManager::_updateLatencies(
2445 	NodeGroup*										group) {
2446 
2447 	ASSERT(IsLocked());
2448 	if(group) {
2449 		ASSERT(group->isLocked());
2450 	}
2451 
2452 	if(group) {
2453 		for(NodeGroup::node_set::iterator it = group->m_nodes.begin();
2454 			it != group->m_nodes.end(); ++it) {
2455 
2456 			(*it)->_updateLatency();
2457 		}
2458 	}
2459 	else {
2460 		for(node_ref_map::iterator it = m_nodeRefMap.begin();
2461 			it != m_nodeRefMap.end(); ++it) {
2462 
2463 			(*it).second->_updateLatency();
2464 		}
2465 	}
2466 }
2467 
2468 // refresh cached latency for every node attached to
2469 // AND INCLUDING the given origin node.
2470 // if 'recurse' is true, affects indirectly attached
2471 // nodes as well.
2472 
2473 
2474 inline void NodeManager::_updateLatenciesFrom(
2475 	NodeRef*											origin,
2476 	bool													recurse) {
2477 
2478 	ASSERT(IsLocked());
2479 
2480 //	PRINT(("### NodeManager::_updateLatenciesFrom()\n"));
2481 
2482 	origin->lock();
2483 	origin->_updateLatency();
2484 	origin->unlock();
2485 
2486 	_lockAllGroups(); // [e.moon 13oct99]
2487 
2488 	_for_each_state st;
2489 	_do_for_each_connected(
2490 		this,
2491 		origin,
2492 		0, // all groups
2493 		recurse,
2494 		mem_fun(&NodeRef::_updateLatency),
2495 		&st);
2496 
2497 	_unlockAllGroups(); // [e.moon 13oct99]
2498 }
2499 
2500 // a bit of unpleasantness [e.moon 13oct99]
2501 void NodeManager::_lockAllGroups() {
2502 
2503 	ASSERT(IsLocked());
2504 	for(node_group_set::iterator it = m_nodeGroupSet.begin();
2505 		it != m_nodeGroupSet.end(); ++it) {
2506 		(*it)->lock();
2507 	}
2508 }
2509 
2510 void NodeManager::_unlockAllGroups() {
2511 	ASSERT(IsLocked());
2512 	for(node_group_set::iterator it = m_nodeGroupSet.begin();
2513 		it != m_nodeGroupSet.end(); ++it) {
2514 		(*it)->unlock();
2515 	}
2516 }
2517 
2518 
2519 // END -- NodeManager.cpp --
2520