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