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