xref: /haiku/src/apps/cortex/NodeManager/NodeManager.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 status_t NodeManager::findGroup(uint32 id, NodeGroup** outGroup) const
786 {
787 	Autolock _l(this);
788 	D_METHOD(("NodeManager::findGroup(id)\n"));
789 
790 	node_group_set::const_iterator it;
791 	for (it = m_nodeGroupSet.begin(); it != m_nodeGroupSet.end(); it++)
792 	{
793 		if ((*it)->id() == id) {
794 			*outGroup = *it;
795 			return B_OK;
796 		}
797 	}
798 
799 	*outGroup = 0;
800 	return B_BAD_VALUE;
801 }
802 
803 // look up a group by name; returns B_NAME_NOT_FOUND if
804 // no group matching the name was found.
805 
806 status_t NodeManager::findGroup(const char* name, NodeGroup** outGroup) const
807 {
808 	Autolock _l(this);
809 	D_METHOD(("NodeManager::findGroup(name)\n"));
810 
811 	node_group_set::const_iterator it;
812 	for (it = m_nodeGroupSet.begin(); it != m_nodeGroupSet.end(); it++)
813 	{
814 		if (strcmp((*it)->name(), name) == 0) {
815 			*outGroup = *it;
816 				return B_OK;
817 		}
818 	}
819 
820 	*outGroup = 0;
821 	return B_BAD_VALUE;
822 }
823 
824 // merge the given source group to the given destination;
825 // empties and releases the source group
826 
827 status_t NodeManager::mergeGroups(
828 	NodeGroup*										sourceGroup,
829 	NodeGroup*										destinationGroup) {
830 	Autolock _l(this);
831 	D_METHOD((
832 		"NodeManager::mergeGroups(name)\n"));
833 
834 	// [5feb00 c.lenz] already merged
835 	if(sourceGroup->id() == destinationGroup->id())
836 		return B_OK;
837 
838 	if(sourceGroup->isReleased() || destinationGroup->isReleased())
839 		return B_NOT_ALLOWED;
840 
841 	for(uint32 n = sourceGroup->countNodes(); n; --n) {
842 		NodeRef* node = sourceGroup->nodeAt(n-1);
843 		ASSERT(node);
844 		status_t err __attribute__((unused)) = sourceGroup->removeNode(n-1);
845 		ASSERT(err == B_OK);
846 		err = destinationGroup->addNode(node);
847 		ASSERT(err == B_OK);
848 	}
849 
850 	// [7nov99 e.moon] delete the source group
851 	_removeGroup(sourceGroup);
852 	sourceGroup->release();
853 
854 	return B_OK;
855 }
856 
857 // [e.moon 28sep99]
858 // split group: given two nodes currently in the same group
859 // that are not connected (directly OR indirectly),
860 // this method removes outsideNode, and all nodes connected
861 // to outsideNode, from the common group.  These nodes are
862 // then added to a new group, returned in 'outGroup'.  The
863 // new group has " split" appended to the end of the original
864 // group's name.
865 //
866 // Returns B_NOT_ALLOWED if any of the above conditions aren't
867 // met (ie. the nodes are in different groups or an indirect
868 // route exists from one to the other), or B_OK if the group
869 // was split successfully.
870 
871 
872 class _changeNodeGroupFn {
873 public:
874 	NodeGroup*										newGroup;
875 
876 	_changeNodeGroupFn(
877 		NodeGroup*									_newGroup) : newGroup(_newGroup) {
878 		ASSERT(newGroup);
879 	}
880 
881 	void operator()(
882 		NodeRef*										node) {
883 
884 		PRINT((
885 			"_changeNodeGroupFn(): '%s'\n", node->name()));
886 
887 		status_t err __attribute__((unused));
888 		NodeGroup* oldGroup = node->group();
889 		if(oldGroup) {
890 			err = oldGroup->removeNode(node);
891 			ASSERT(err == B_OK);
892 		}
893 
894 		err = newGroup->addNode(node);
895 		ASSERT(err == B_OK);
896 	}
897 };
898 
899 status_t NodeManager::splitGroup(
900 	NodeRef*											insideNode,
901 	NodeRef*											outsideNode,
902 	NodeGroup**										outGroup) {
903 
904 	ASSERT(insideNode);
905 	ASSERT(outsideNode);
906 
907 	Autolock _l(this);
908 
909 	// ensure that no route exists from insideNode to outsideNode
910 	if(findRoute(insideNode->id(), outsideNode->id())) {
911 		PRINT((
912 			"!!! NodeManager::splitGroup(): route exists from %" B_PRId32
913 			" to %" B_PRId32 "\n", insideNode->id(), outsideNode->id()));
914 		return B_NOT_ALLOWED;
915 	}
916 
917 	// make sure the nodes share a common group
918 	NodeGroup* oldGroup = insideNode->group();
919 	if(!oldGroup) {
920 		PRINT(("!!! NodeManager::splitGroup(): invalid group\n"));
921 		return B_NOT_ALLOWED;
922 	}
923 	if(oldGroup != outsideNode->group()) {
924 		PRINT((
925 			"!!! NodeManager::splitGroup(): mismatched groups for %" B_PRId32
926 			" and %" B_PRId32 "\n", insideNode->id(), outsideNode->id()));
927 		return B_NOT_ALLOWED;
928 	}
929 
930 	Autolock _l_old_group(oldGroup);
931 
932 	// create the new group
933 	BString nameBuffer = oldGroup->name();
934 	nameBuffer << " split";
935 
936 	NodeGroup* newGroup = createGroup(
937 		nameBuffer.String(),
938 		oldGroup->runMode());
939 	*outGroup = newGroup;
940 
941 	// move nodes connected to outsideNode from old to new group
942 	_changeNodeGroupFn fn(newGroup);
943 	fn(outsideNode);
944 
945 	_for_each_state st;
946 	_do_for_each_connected(
947 		this,
948 		outsideNode,
949 		oldGroup,
950 		true,
951 		fn,
952 		&st);
953 
954 	// [e.moon 1dec99] a single-node group takes that node's name
955 	if(newGroup->countNodes() == 1)
956 		newGroup->setName(newGroup->nodeAt(0)->name());
957 
958 	if(oldGroup->countNodes() == 1)
959 		oldGroup->setName(oldGroup->nodeAt(0)->name());
960 
961 	return B_OK;
962 }
963 
964 
965 // * INSTANTIATION & CONNECTION
966 //   Use these calls rather than the associated BMediaRoster()
967 //   methods to assure that the nodes and connections you set up
968 //   can be properly serialized & reconstituted.
969 
970 // basic BMediaRoster::InstantiateDormantNode() wrapper
971 
972 status_t NodeManager::instantiate(
973 	const dormant_node_info&			info,
974 	NodeRef**											outRef,
975 	bigtime_t											timeout,
976 	uint32												nodeFlags) {
977 
978 	Autolock _l(this);
979 	status_t err;
980 	D_METHOD((
981 		"NodeManager::instantiate()\n"));
982 
983 	// * instantiate
984 
985 	media_node node;
986 
987 	if(m_useAddOnHost) {
988 		err = AddOnHost::InstantiateDormantNode(
989 			info, &node, timeout);
990 
991 		if(err < B_OK) {
992 			node = media_node::null;
993 
994 			// attempt to relaunch
995 			BMessenger mess;
996 			err = AddOnHost::Launch(&mess);
997 			if(err < B_OK) {
998 				PRINT((
999 					"!!! NodeManager::instantiate(): giving up on AddOnHost\n"));
1000 
1001 				m_useAddOnHost = false;
1002 			}
1003 			else {
1004 				err = AddOnHost::InstantiateDormantNode(
1005 					info, &node, timeout);
1006 			}
1007 		}
1008 	}
1009 
1010 	if(!m_useAddOnHost || node == media_node::null) {
1011 		D_ROSTER((
1012 			"# roster->InstantiateDormantNode()\n"));
1013 		err = roster->InstantiateDormantNode(info, &node);
1014 	}
1015 
1016 	if(err < B_OK) {
1017 		*outRef = 0;
1018 		return err;
1019 	}
1020 
1021 	if(node == media_node::null) {
1022 		// [e.moon 23oct99] +++++
1023 		// instantiating a soundcard input/output (emu10k or sonic_vibes)
1024 		// produces a node ID of -1 (R4.5.2 x86)
1025 		//
1026 		PRINT((
1027 			"! InstantiateDormantNode(): invalid media node\n"));
1028 
1029 		// bail out
1030 		*outRef = 0;
1031 		return B_BAD_INDEX;
1032 	}
1033 
1034 	// * create NodeRef
1035 	NodeRef* ref = new NodeRef(
1036 		node,
1037 		this,
1038 		nodeFlags, // | NodeRef::NO_ROSTER_WATCH, // +++++ e.moon 11oct99
1039 		NodeRef::_INTERNAL);
1040 
1041 	ref->_setAddonHint(&info);
1042 	_addRef(ref);
1043 
1044 	// * return it
1045 	*outRef = ref;
1046 	return B_OK;
1047 }
1048 
1049 // SniffRef/Instantiate.../SetRefFor: a one-call interface
1050 // to create a node capable of playing a given media file.
1051 
1052 status_t NodeManager::instantiate(
1053 	const entry_ref&							file,
1054 	uint64												requireNodeKinds,
1055 	NodeRef**											outRef,
1056 	bigtime_t											timeout,
1057 	uint32												nodeFlags,
1058 	bigtime_t*										outDuration) {
1059 
1060 	D_METHOD((
1061 		"NodeManager::instantiate(ref)\n"));
1062 
1063 	// [no lock needed; calls the full form of instantiate()]
1064 
1065 	status_t err;
1066 
1067 	// * Find matching add-on
1068 	dormant_node_info info;
1069 
1070 	D_ROSTER((
1071 		"# roster->SniffRef()\n"));
1072 	err = roster->SniffRef(
1073 		file,
1074 		requireNodeKinds,
1075 		&info);
1076 	if(err < B_OK) {
1077 		*outRef = 0;
1078 		return err;
1079 	}
1080 
1081 	// * Instantiate
1082 	err = instantiate(info, outRef, timeout, nodeFlags);
1083 
1084 	if(err < B_OK)
1085 		return err;
1086 
1087 	ASSERT(*outRef);
1088 
1089 	// * Set file to play
1090 	bigtime_t dur;
1091 	D_ROSTER(("# roster->SetRefFor()\n"));
1092 	err = roster->SetRefFor(
1093 		(*outRef)->node(),
1094 		file,
1095 		false,
1096 		&dur);
1097 
1098 	if(err < B_OK) {
1099 		PRINT((
1100 			"* SetRefFor() failed: %s\n", strerror(err)));
1101 	}
1102 	else if(outDuration)
1103 		*outDuration = dur;
1104 
1105 	// * update info [e.moon 29sep99]
1106 	Autolock _l(*outRef);
1107 	(*outRef)->_setAddonHint(&info, &file);
1108 
1109 	return err;
1110 }
1111 
1112 // use this method to reference nodes internal to your
1113 // application.
1114 
1115 status_t NodeManager::reference(
1116 	BMediaNode*										node,
1117 	NodeRef**											outRef,
1118 	uint32												nodeFlags) {
1119 
1120 	Autolock _l(this);
1121 	D_METHOD((
1122 		"NodeManager::reference()\n"));
1123 
1124 	// should this node be marked _NO_RELEASE?
1125 	NodeRef* ref = new NodeRef(node->Node(), this, nodeFlags, 0);
1126 	_addRef(ref);
1127 
1128 	*outRef = ref;
1129 	return B_OK;
1130 }
1131 
1132 // the most flexible form of connect(): set the template
1133 // format as you would for BMediaRoster::Connect().
1134 
1135 status_t NodeManager::connect(
1136 	const media_output&						output,
1137 	const media_input&						input,
1138 	const media_format&						templateFormat,
1139 	Connection*										outConnection /*=0*/) {
1140 
1141 	Autolock _l(this);
1142 	status_t err;
1143 	D_METHOD((
1144 		"NodeManager::connect()\n"));
1145 
1146 	// * Find (& create if necessary) NodeRefs
1147 
1148 	NodeRef* outputRef;
1149 	if(getNodeRef(output.node.node, &outputRef) < B_OK)
1150 		outputRef = _addRefFor(output.node, 0);
1151 
1152 	NodeRef* inputRef;
1153 	if(getNodeRef(input.node.node, &inputRef) < B_OK)
1154 		inputRef = _addRefFor(input.node, 0);
1155 
1156 	// * Connect the nodes
1157 
1158 	media_output finalOutput;
1159 	media_input finalInput;
1160 	media_format finalFormat = templateFormat;
1161 
1162 	D_ROSTER(("# roster->Connect()\n"));
1163 	err = roster->Connect(
1164 		output.source,
1165 		input.destination,
1166 		&finalFormat,
1167 		&finalOutput,
1168 		&finalInput);
1169 
1170 	if(err < B_OK) {
1171 		if(outConnection)
1172 			*outConnection = Connection();
1173 		connectionFailed(output, input, templateFormat, err);
1174 		return err;
1175 	}
1176 
1177 	// * Create Connection instance; mark it INTERNAL
1178 	//   to automatically remove it upon shutdown.
1179 
1180 	D_METHOD((
1181 		"! creating connection:\n"
1182 		"  source id:    %" B_PRId32 "\n"
1183 		"  source port:  %" B_PRId32 "\n"
1184 		"  dest id:      %" B_PRId32 "\n"
1185 		"  dest port:    %" B_PRId32 "\n",
1186 		finalOutput.source.id, finalOutput.source.port,
1187 		finalInput.destination.id, finalInput.destination.port));
1188 
1189 	uint32 cflags = Connection::INTERNAL;
1190 	if(outputRef->m_info.node.kind & B_FILE_INTERFACE) {
1191 		// 3aug99:
1192 		// workaround for bug 19990802-12798:
1193 		//   connections involving a file-interface node aren't removed
1194 		cflags |= Connection::LOCKED;
1195 	}
1196 
1197 	Connection* con = new Connection(
1198 		m_nextConID++,
1199 		output.node,
1200 		finalOutput.source,
1201 		finalOutput.name,
1202 		input.node,
1203 		finalInput.destination,
1204 		finalInput.name,
1205 		finalFormat,
1206 		cflags);
1207 
1208 	con->setOutputHint(
1209 		output.name,
1210 		output.format);
1211 
1212 	con->setInputHint(
1213 		input.name,
1214 		input.format);
1215 
1216 	con->setRequestedFormat(
1217 		templateFormat);
1218 
1219 	_addConnection(con);
1220 
1221 	// [e.moon 10aug99]
1222 	// fetch updated latencies;
1223 	// [e.moon 28sep99]
1224 	// scan all nodes connected directly OR indirectly to the
1225 	// newly-connected nodes and update their latencies -- this includes
1226 	// recalculating the node's 'producer delay' if in B_RECORDING mode.
1227 
1228 	_updateLatenciesFrom(inputRef, true);
1229 
1230 	// copy connection
1231 	if(outConnection) {
1232 		*outConnection = *con;
1233 	}
1234 	return B_OK;
1235 }
1236 
1237 // format-guessing form of connect(): tries to find
1238 // a common format between output & input before connection;
1239 // returns B_MEDIA_BAD_FORMAT if no common format appears
1240 // possible.
1241 //
1242 // NOTE: the specifics of the input and output formats are ignored;
1243 //       this method only looks at the format type, and properly
1244 //       handles wildcards at that level (B_MEDIA_NO_TYPE).
1245 
1246 status_t NodeManager::connect(
1247 	const media_output&						output,
1248 	const media_input&						input,
1249 	Connection*										outConnection /*=0*/) {
1250 
1251 	D_METHOD((
1252 		"NodeManager::connect(guess)\n"));
1253 
1254 	// [no lock needed; calls the full form of connect()]
1255 
1256 	// defer to the pickier endpoint
1257 	media_format f;
1258 
1259 	if(output.format.type > B_MEDIA_UNKNOWN_TYPE) {
1260 		f = output.format;
1261 		if ((input.format.type > B_MEDIA_UNKNOWN_TYPE) &&
1262 			(f.type != input.format.type)) {
1263 			connectionFailed(output, input, f, B_MEDIA_BAD_FORMAT);
1264 			return B_MEDIA_BAD_FORMAT;
1265 		}
1266 	}
1267 	else if(input.format.type > B_MEDIA_UNKNOWN_TYPE) {
1268 		f = input.format;
1269 		// output node doesn't care
1270 	}
1271 	else {
1272 		// about as non-picky as two nodes can possibly be
1273 		f.type = B_MEDIA_UNKNOWN_TYPE;
1274 	}
1275 
1276 	// +++++ ? revert to wildcard ?
1277 
1278 	// let the nodes try to work out a common format from here
1279 	return connect(
1280 		output,
1281 		input,
1282 		f,
1283 		outConnection);
1284 }
1285 
1286 // disconnects the connection represented by the provided
1287 // Connection object.  if successful, returns B_OK.
1288 
1289 status_t NodeManager::disconnect(
1290 	const Connection&								connection) {
1291 
1292 	Autolock _l(this);
1293 	status_t err;
1294 	D_METHOD((
1295 		"NodeManager::disconnect()\n"));
1296 
1297 	// don't bother trying to disconnect an invalid connection
1298 	if(!connection.isValid())
1299 		return B_NOT_ALLOWED;
1300 
1301 	// make sure connection can be released
1302 	if(connection.flags() & Connection::LOCKED) {
1303 		PRINT((
1304 			"NodeManager::disconnect(): connection locked:\n"
1305 			"  %" B_PRId32 ":%s -> %" B_PRId32 ":%s\n",
1306 			connection.sourceNode(),
1307 			connection.outputName(),
1308 			connection.destinationNode(),
1309 			connection.inputName()));
1310 		return B_NOT_ALLOWED;
1311 	}
1312 
1313 	D_METHOD((
1314 		"! breaking connection:\n"
1315 		"  source node:  %" B_PRId32 "\n"
1316 		"  source id:    %" B_PRId32 "\n"
1317 		"  source port:  %" B_PRId32 "\n"
1318 		"  dest node:    %" B_PRId32 "\n"
1319 		"  dest id:      %" B_PRId32 "\n"
1320 		"  dest port:    %" B_PRId32 "\n",
1321 		connection.sourceNode(),
1322 		connection.source().id, connection.source().port,
1323 		connection.destinationNode(),
1324 		connection.destination().id, connection.destination().port));
1325 
1326 	// do it
1327 	D_ROSTER(("# roster->Disconnect()\n"));
1328 	err = roster->Disconnect(
1329 		connection.sourceNode(),
1330 		connection.source(),
1331 		connection.destinationNode(),
1332 		connection.destination());
1333 
1334 	// mark disconnected
1335 	if(err == B_OK) {
1336 		con_map::iterator it = m_conSourceMap.lower_bound(connection.sourceNode());
1337 		con_map::iterator itEnd = m_conSourceMap.upper_bound(connection.sourceNode());
1338 		for(; it != itEnd; ++it)
1339 			if((*it).second->id() == connection.id()) {
1340 				(*it).second->m_disconnected = true;
1341 				break;
1342 			}
1343 		ASSERT(it != itEnd);
1344 
1345 		// [e.moon 28sep99]
1346 		// fetch updated latencies;
1347 		// scan all nodes connected directly OR indirectly to the
1348 		// newly-connected nodes and update their latencies -- this includes
1349 		// recalculating the node's 'producer delay' if in B_RECORDING mode.
1350 
1351 		NodeRef* ref;
1352 		if(getNodeRef(connection.sourceNode(), &ref) == B_OK)
1353 			_updateLatenciesFrom(ref, true);
1354 		if(getNodeRef(connection.destinationNode(), &ref) == B_OK)
1355 			_updateLatenciesFrom(ref, true);
1356 
1357 	} else {
1358 		// +++++ if this failed, somebody somewhere is mighty confused
1359 		PRINT((
1360 			"NodeManager::disconnect(): Disconnect() failed:\n  %s\n",
1361 			strerror(err)));
1362 	}
1363 
1364 	return err;
1365 }
1366 
1367 // * GROUP CREATION
1368 
1369 NodeGroup* NodeManager::createGroup(
1370 	const char*										name,
1371 	BMediaNode::run_mode					runMode) {
1372 
1373 	Autolock _l(this);
1374 	D_METHOD((
1375 		"NodeManager::createGroup()\n"));
1376 
1377 	NodeGroup* g = new NodeGroup(name, this, runMode);
1378 	_addGroup(g);
1379 
1380 	return g;
1381 }
1382 
1383 // -------------------------------------------------------- //
1384 // *** node/connection iteration
1385 // *** MUST BE LOCKED for any of these calls
1386 // -------------------------------------------------------- //
1387 
1388 // usage:
1389 //   For the first call, pass 'cookie' a pointer to a void* set to 0.
1390 //   Returns B_BAD_INDEX when the set of nodes has been exhausted (and
1391 //   re-zeroes the cookie, cleaning up any unused memory.)
1392 
1393 status_t NodeManager::getNextRef(
1394 	NodeRef**											ref,
1395 	void**												cookie) {
1396 	ASSERT(IsLocked());
1397 	ASSERT(cookie);
1398 
1399 	if(!*cookie)
1400 		*cookie = new node_ref_map::iterator(m_nodeRefMap.begin());
1401 
1402 	node_ref_map::iterator* pit = (node_ref_map::iterator*)*cookie;
1403 
1404 	// at end of set?
1405 	if(*pit == m_nodeRefMap.end()) {
1406 		delete pit;
1407 		*cookie = 0;
1408 		return B_BAD_INDEX;
1409 	}
1410 
1411 	// return next entry
1412 	*ref = (*(*pit)).second;
1413 	++(*pit);
1414 	return B_OK;
1415 }
1416 
1417 // +++++ reworked 13sep99: dtors wouldn't have been called with 'delete *cookie'! +++++
1418 void NodeManager::disposeRefCookie(
1419 	void**												cookie) {
1420 
1421 	if(!cookie)
1422 		return;
1423 
1424 	node_ref_map::iterator* it =
1425 		reinterpret_cast<node_ref_map::iterator*>(*cookie);
1426 	ASSERT(it);
1427 	if(it)
1428 		delete it;
1429 }
1430 
1431 status_t NodeManager::getNextConnection(
1432 	Connection*										connection,
1433 	void**												cookie) {
1434 	ASSERT(IsLocked());
1435 	ASSERT(cookie);
1436 
1437 	if(!*cookie)
1438 		*cookie = new con_map::iterator(m_conSourceMap.begin());
1439 
1440 	con_map::iterator* pit = (con_map::iterator*)*cookie;
1441 
1442 	// at end of set?
1443 	if(*pit == m_conSourceMap.end()) {
1444 		delete pit;
1445 		*cookie = 0;
1446 		return B_BAD_INDEX;
1447 	}
1448 
1449 	// return next entry (ewww)
1450 	*connection = *((*(*pit)).second);
1451 	++(*pit);
1452 	return B_OK;
1453 }
1454 
1455 // +++++ reworked 13sep99: dtors wouldn't have been called with 'delete *cookie'! +++++
1456 void NodeManager::disposeConnectionCookie(
1457 	void**												cookie) {
1458 
1459 	if(!cookie)
1460 		return;
1461 
1462 	con_map::iterator* it =
1463 		reinterpret_cast<con_map::iterator*>(*cookie);
1464 	ASSERT(it);
1465 	if(it)
1466 		delete it;
1467 }
1468 
1469 
1470 // -------------------------------------------------------- //
1471 // *** BHandler impl
1472 // -------------------------------------------------------- //
1473 
1474 // +++++ support all Media Roster messages! +++++
1475 
1476 // [e.moon 7nov99] implemented observer pattern for NodeGroup
1477 void NodeManager::MessageReceived(BMessage* message) {
1478 
1479 	D_MESSAGE((
1480 		"NodeManager::MessageReceived(): %c%c%c%c\n",
1481 		 message->what >> 24,
1482 		(message->what >> 16)	& 0xff,
1483 		(message->what >> 8)	& 0xff,
1484 		(message->what) 			& 0xff));
1485 
1486 	switch(message->what) {
1487 
1488 		// *** Media Roster messages ***
1489 
1490 		case B_MEDIA_NODE_CREATED:
1491 			if(_handleNodesCreated(message) == B_OK)
1492 				notify(message);
1493 			break;
1494 
1495 		case B_MEDIA_NODE_DELETED:
1496 			_handleNodesDeleted(message);
1497 			notify(message);
1498 			break;
1499 
1500 		case B_MEDIA_CONNECTION_MADE:
1501 			_handleConnectionMade(message);
1502 			notify(message);
1503 			break;
1504 
1505 		case B_MEDIA_CONNECTION_BROKEN:
1506 			_handleConnectionBroken(message); // augments message!
1507 			notify(message);
1508 			break;
1509 
1510 		case B_MEDIA_FORMAT_CHANGED:
1511 			_handleFormatChanged(message);
1512 			notify(message);
1513 			break;
1514 
1515 		default:
1516 			_inherited::MessageReceived(message);
1517 			break;
1518 	}
1519 }
1520 
1521 // -------------------------------------------------------- //
1522 // *** IObservable hooks
1523 // -------------------------------------------------------- //
1524 
1525 void NodeManager::observerAdded(
1526 	const BMessenger&				observer) {
1527 
1528 	BMessage m(M_OBSERVER_ADDED);
1529 	m.AddMessenger("target", BMessenger(this));
1530 	observer.SendMessage(&m);
1531 }
1532 
1533 
1534 void NodeManager::observerRemoved(
1535 	const BMessenger&				observer) {
1536 
1537 	BMessage m(M_OBSERVER_REMOVED);
1538 	m.AddMessenger("target", BMessenger(this));
1539 	observer.SendMessage(&m);
1540 }
1541 
1542 void NodeManager::notifyRelease() {
1543 
1544 	BMessage m(M_RELEASED);
1545 	m.AddMessenger("target", BMessenger(this));
1546 	notify(&m);
1547 }
1548 
1549 void NodeManager::releaseComplete() {
1550 	// tear down media roster connection
1551 	D_ROSTER(("# roster->StopWatching()\n"));
1552 	status_t err = roster->StopWatching(
1553 		BMessenger(this));
1554 	if(err < B_OK) {
1555 		PRINT((
1556 			"* roster->StopWatching() failed: %s\n", strerror(err)));
1557 	}
1558 }
1559 
1560 
1561 // -------------------------------------------------------- //
1562 // *** ILockable impl.
1563 // -------------------------------------------------------- //
1564 
1565 bool NodeManager::lock(
1566 	lock_t												type,
1567 	bigtime_t											timeout) {
1568 
1569 	D_LOCK(("*** NodeManager::lock(): %ld\n", find_thread(0)));
1570 
1571 	status_t err = LockWithTimeout(timeout);
1572 
1573 
1574 	D_LOCK(("*** NodeManager::lock() ACQUIRED: %ld\n", find_thread(0)));
1575 
1576 	return err == B_OK;
1577 }
1578 
1579 bool NodeManager::unlock(
1580 	lock_t												type) {
1581 
1582 	D_LOCK(("*** NodeManager::unlock(): %ld\n", find_thread(0)));
1583 
1584 	Unlock();
1585 
1586 	D_LOCK(("*** NodeManager::unlock() RELEASED: %ld\n", find_thread(0)));
1587 
1588 	return true;
1589 }
1590 
1591 bool NodeManager::isLocked(
1592 	lock_t												type) const {
1593 
1594 	return IsLocked();
1595 }
1596 
1597 // -------------------------------------------------------- //
1598 // *** internal operations (LOCK REQUIRED)
1599 // -------------------------------------------------------- //
1600 
1601 void NodeManager::_initCommonNodes() {
1602 
1603 	ASSERT(IsLocked());
1604 	status_t err;
1605 	media_node node;
1606 
1607 	D_METHOD((
1608 		"NodeManager::_initCommonNodes()\n"));
1609 
1610 	uint32 disableTransport =
1611 		(NodeRef::NO_START_STOP | NodeRef::NO_SEEK | NodeRef::NO_PREROLL);
1612 
1613 	// video input
1614 	D_ROSTER(("# roster->GetVideoInput()\n"));
1615 	err = roster->GetVideoInput(&node);
1616 	if(err == B_OK)
1617 		m_videoInputNode = _addRefFor(
1618 			node,
1619 			_userFlagsForKind(node.kind),
1620 			_implFlagsForKind(node.kind));
1621 
1622 	// video output
1623 	D_ROSTER(("# roster->GetVideoOutput()\n"));
1624 	err = roster->GetVideoOutput(&node);
1625 	if(err == B_OK) {
1626 		if(m_videoInputNode && node.node == m_videoInputNode->id()) {
1627 			// input and output nodes identical
1628 			// [e.moon 20dec99]
1629 			m_videoOutputNode = m_videoInputNode;
1630 		}
1631 		else {
1632 			m_videoOutputNode = _addRefFor(
1633 				node,
1634 				_userFlagsForKind(node.kind) & ~NodeRef::NO_START_STOP,
1635 				_implFlagsForKind(node.kind));
1636 		}
1637 	}
1638 
1639 	// audio mixer
1640 	D_ROSTER(("# roster->GetAudioMixer()\n"));
1641 	err = roster->GetAudioMixer(&node);
1642 	if(err == B_OK)
1643 		m_audioMixerNode = _addRefFor(
1644 			node,
1645 			_userFlagsForKind(node.kind) | disableTransport,
1646 			_implFlagsForKind(node.kind));
1647 
1648 	// audio input
1649 	D_ROSTER(("# roster->GetAudioInput()\n"));
1650 	err = roster->GetAudioInput(&node);
1651 	if(err == B_OK)
1652 		m_audioInputNode = _addRefFor(
1653 			node,
1654 			_userFlagsForKind(node.kind),
1655 			_implFlagsForKind(node.kind));
1656 
1657 	// audio output
1658 	D_ROSTER(("# roster->GetAudioOutput()\n"));
1659 	err = roster->GetAudioOutput(&node);
1660 	if(err == B_OK) {
1661 		if(m_audioInputNode && node.node == m_audioInputNode->id()) {
1662 			// input and output nodes identical
1663 			// [e.moon 20dec99]
1664 			m_audioOutputNode = m_audioInputNode;
1665 
1666 			// disable transport controls (the default audio output must always
1667 			// be running, and can't be seeked.)
1668 
1669 			m_audioOutputNode->setFlags(
1670 				m_audioOutputNode->flags() | disableTransport);
1671 		}
1672 		else {
1673 			m_audioOutputNode = _addRefFor(
1674 				node,
1675 				_userFlagsForKind(node.kind) | disableTransport,
1676 				_implFlagsForKind(node.kind));
1677 		}
1678 	}
1679 }
1680 
1681 void NodeManager::_addRef(
1682 	NodeRef*											ref) {
1683 
1684 	ASSERT(ref);
1685 	ASSERT(IsLocked());
1686 
1687 	D_METHOD((
1688 		"NodeManager::_addRef()\n"));
1689 
1690 	// precondition: no NodeRef yet exists for this node
1691 	// +++++
1692 	// [e.moon 21oct99]
1693 	// <markjan@xs4all.nl> sez this fails on startup w/ MediaKit 10.5
1694 	ASSERT(
1695 		m_nodeRefMap.find(ref->id()) == m_nodeRefMap.end());
1696 
1697 	// add it
1698 	// [e.moon 13oct99] PPC-friendly
1699 	m_nodeRefMap.insert(node_ref_map::value_type(ref->id(), ref));
1700 
1701 	// [e.moon 8nov99] call hook
1702 	nodeCreated(ref);
1703 }
1704 
1705 void NodeManager::_removeRef(
1706 	media_node_id									id) {
1707 	ASSERT(IsLocked());
1708 
1709 	D_METHOD((
1710 		"NodeManager::_removeRef()\n"));
1711 
1712 	node_ref_map::iterator it = m_nodeRefMap.find(id);
1713 
1714 	// precondition: a NodeRef w/ matching ID is in the map
1715 	ASSERT(it != m_nodeRefMap.end());
1716 
1717 	// [e.moon 8nov99] call hook
1718 	nodeDeleted((*it).second);
1719 
1720 	// remove it
1721 	m_nodeRefMap.erase(it);
1722 }
1723 
1724 // create, add, return a NodeRef for the given external node;
1725 // must not already exist
1726 NodeRef* NodeManager::_addRefFor(
1727 	const media_node&							node,
1728 	uint32												nodeFlags,
1729 	uint32												nodeImplFlags) {
1730 
1731 	ASSERT(IsLocked());
1732 
1733 	D_METHOD((
1734 		"NodeManager::_addRefFor()\n"));
1735 
1736 	// precondition: no NodeRef yet exists for this node
1737 	ASSERT(
1738 		m_nodeRefMap.find(node.node) == m_nodeRefMap.end());
1739 
1740 //	// precondition: the node actually exists
1741 //	live_node_info info;
1742 //	D_ROSTER(("# roster->GetLiveNodeInfo()\n"));
1743 //	ASSERT(roster->GetLiveNodeInfo(node, &info) == B_OK);
1744 
1745 	// * create & add ref
1746 	NodeRef* ref = new NodeRef(node, this, nodeFlags, nodeImplFlags);
1747 	_addRef(ref);
1748 
1749 	return ref;
1750 }
1751 
1752 void NodeManager::_addConnection(
1753 	Connection*										connection) {
1754 
1755 	ASSERT(connection);
1756 	ASSERT(IsLocked());
1757 
1758 	D_METHOD((
1759 		"NodeManager::_addConnection()\n"));
1760 
1761 	// precondition: not already entered in either source or dest map
1762 	// +++++ a more rigorous test would be a very good thing
1763 #ifdef DEBUG
1764 	for(con_map::iterator it = m_conSourceMap.lower_bound(connection->sourceNode());
1765 		it != m_conSourceMap.upper_bound(connection->sourceNode()); ++it) {
1766 		ASSERT((*it).second->id() != connection->id());
1767 	}
1768 	for(con_map::iterator it = m_conDestinationMap.lower_bound(connection->destinationNode());
1769 		it != m_conDestinationMap.upper_bound(connection->destinationNode()); ++it) {
1770 		ASSERT((*it).second->id() != connection->id());
1771 	}
1772 #endif
1773 
1774 	// add to both maps
1775 	// [e.moon 13oct99] PPC-friendly
1776 	m_conSourceMap.insert(
1777 		con_map::value_type(
1778 			connection->sourceNode(),
1779 			connection));
1780 	m_conDestinationMap.insert(
1781 		con_map::value_type(
1782 			connection->destinationNode(),
1783 			connection));
1784 
1785 	// [e.moon 8nov99] call hook
1786 	connectionMade(connection);
1787 }
1788 
1789 void NodeManager::_removeConnection(
1790 	const Connection&							connection) {
1791 
1792 	ASSERT(IsLocked());
1793 	con_map::iterator itSource, itDestination;
1794 
1795 	D_METHOD((
1796 		"NodeManager::_removeConnection()\n"));
1797 
1798 	// [e.moon 8nov99] call hook
1799 	connectionBroken(&connection);
1800 
1801 	// precondition: one entry in both source & dest maps
1802 	// +++++ a more rigorous test would be a very good thing
1803 
1804 	for(
1805 		itSource = m_conSourceMap.lower_bound(connection.sourceNode());
1806 		itSource != m_conSourceMap.upper_bound(connection.sourceNode());
1807 		++itSource)
1808 		if((*itSource).second->id() == connection.id())
1809 			break;
1810 
1811 	ASSERT(itSource != m_conSourceMap.end());
1812 
1813 	for(
1814 		itDestination = m_conDestinationMap.lower_bound(connection.destinationNode());
1815 		itDestination != m_conDestinationMap.upper_bound(connection.destinationNode());
1816 		++itDestination)
1817 		if((*itDestination).second->id() == connection.id())
1818 			break;
1819 
1820 	ASSERT(itDestination != m_conDestinationMap.end());
1821 
1822 	// delete & remove from both maps
1823 	delete (*itSource).second;
1824 	m_conSourceMap.erase(itSource);
1825 	m_conDestinationMap.erase(itDestination);
1826 }
1827 
1828 void NodeManager::_addGroup(
1829 	NodeGroup*										group) {
1830 
1831 	ASSERT(group);
1832 	ASSERT(IsLocked());
1833 
1834 	D_METHOD((
1835 		"NodeManager::_addGroup()\n"));
1836 
1837 	// precondition: group not already in set
1838 	ASSERT(
1839 		find(
1840 			m_nodeGroupSet.begin(),
1841 			m_nodeGroupSet.end(),
1842 			group) == m_nodeGroupSet.end());
1843 
1844 	// add
1845 	m_nodeGroupSet.push_back(group);
1846 
1847 //	// [e.moon 7nov99] observe
1848 //	add_observer(this, group);
1849 }
1850 
1851 void NodeManager::_removeGroup(
1852 	NodeGroup*										group) {
1853 
1854 	ASSERT(group);
1855 	ASSERT(IsLocked());
1856 
1857 	D_METHOD((
1858 		"NodeManager::_removeGroup()\n"));
1859 
1860 	node_group_set::iterator it = find(
1861 		m_nodeGroupSet.begin(),
1862 		m_nodeGroupSet.end(),
1863 		group);
1864 
1865 	// precondition: group in set
1866 	if(it == m_nodeGroupSet.end()) {
1867 		PRINT((
1868 			"* NodeManager::_removeGroup(%" B_PRId32 "): group not in set.\n",
1869 			group->id()));
1870 		return;
1871 	}
1872 
1873 	// remove it
1874 	m_nodeGroupSet.erase(it);
1875 }
1876 
1877 // -------------------------------------------------------- //
1878 // *** Message Handlers ***
1879 // -------------------------------------------------------- //
1880 
1881 
1882 // now returns B_OK iff the message should be relayed to observers
1883 // [e.moon 11oct99]
1884 
1885 inline status_t NodeManager::_handleNodesCreated(
1886 	BMessage*											message) {
1887 	ASSERT(IsLocked());
1888 
1889 	status_t err = B_OK;
1890 
1891 	// fetch number of new nodes
1892 	type_code type;
1893 	int32 count;
1894 	err = message->GetInfo("media_node_id", &type, &count);
1895 	if(err < B_OK) {
1896 		PRINT((
1897 			"* NodeManager::_handleNodesCreated(): GetInfo() failed:\n"
1898 			"  %s\n",
1899 			strerror(err)));
1900 		return err;
1901 	}
1902 	if(!count) {
1903 		PRINT((
1904 			"* NodeManager::_handleNodesCreated(): no node IDs in message.\n"));
1905 		return err;
1906 	}
1907 
1908 	D_METHOD((
1909 		"NodeManager::_handleNodesCreated(): %" B_PRId32 " nodes\n",
1910 		count));
1911 
1912 	// * Create NodeRef instances for the listed nodes.
1913 	//   If this is the first message received from the Media Roster,
1914 	//   no connection info will be received for these nodes; store them
1915 	//   for now (indexed by service-thread port) and figure the connections
1916 	//   afterwards.
1917 	//   These nodes are mapped by port ID because that's the only criterion
1918 	//   available for matching a media_source to a node.
1919 
1920 	typedef map<port_id, NodeRef*> port_ref_map;
1921 	port_ref_map* initialNodes = m_existingNodesInit ? 0 : new port_ref_map;
1922 
1923 	bool refsCreated = false;
1924 
1925 	for(int32 n = 0; n < count; ++n) {
1926 		// fetch ID of next node
1927 		int32 id;
1928 		err = message->FindInt32("media_node_id", n, &id);
1929 		if(err < B_OK) {
1930 			PRINT((
1931 				"* NodeManager::_handleNodesCreated(): FindInt32() failed:\n"
1932 				"  %s", strerror(err)));
1933 			continue;
1934 		}
1935 
1936 		// look up the node
1937 		media_node node;
1938 		err = roster->GetNodeFor(id, &node);
1939 		if(err < B_OK) {
1940 			PRINT((
1941 				"* NodeManager::_handleNodesCreated(): roster->GetNodeFor(%"
1942 					B_PRId32 ") failed:\n"
1943 				"  %s\n",
1944 				id, strerror(err)));
1945 			continue;
1946 		}
1947 
1948 		// look for an existing NodeRef; if not found, create one:
1949 		NodeRef* ref = 0;
1950 		if(getNodeRef(node.node, &ref) < B_OK) {
1951 			// create one
1952 			ref = _addRefFor(
1953 				node,
1954 				_userFlagsForKind(node.kind), // | NodeRef::NO_ROSTER_WATCH, // +++++ e.moon 11oct99
1955 				_implFlagsForKind(node.kind) | NodeRef::_CREATE_NOTIFIED);
1956 
1957 			refsCreated = true;
1958 
1959 //			// [e.moon 7nov99] call hook
1960 //			nodeCreated(ref);
1961 
1962 		} else {
1963 //			PRINT((
1964 //				"* NodeManager::_handleNodesCreated():\n"
1965 //				"  found existing ref for '%s' (%ld)\n",
1966 //				ref->name(), id));
1967 
1968 
1969 			// set _CREATE_NOTIFIED to prevent notification from being passed on
1970 			// twice [e.moon 11oct99]
1971 			if(!(ref->m_implFlags & NodeRef::_CREATE_NOTIFIED)) {
1972 				ref->m_implFlags |= NodeRef::_CREATE_NOTIFIED;
1973 				refsCreated = true;
1974 			}
1975 
1976 			// release the (duplicate) media_node reference
1977 			err = roster->ReleaseNode(node);
1978 			if(err < B_OK) {
1979 				PRINT((
1980 					"* NodeManager::_handleNodesCreated(): roster->ReleaseNode(%"
1981 						B_PRId32 ") failed:\n"
1982 					"  %s\n",
1983 					id, strerror(err)));
1984 			}
1985 		}
1986 
1987 		// add to the 'initial nodes' set if necessary
1988 		// [e.moon 13oct99] PPC-friendly
1989 		if(initialNodes)
1990 			initialNodes->insert(
1991 				port_ref_map::value_type(
1992 					node.port, ref));
1993 	}
1994 
1995 	if(initialNodes) {
1996 		// populate current connections from each node in the set
1997 //		PRINT((
1998 //			"* NodeManager::_handleNodesCreated(): POPULATING CONNECTIONS (%ld)\n",
1999 //			initialNodes->size()));
2000 
2001 		for(port_ref_map::const_iterator itDest = initialNodes->begin();
2002 			itDest != initialNodes->end(); ++itDest) {
2003 
2004 			// walk each connected input for this node; find corresponding
2005 			// output & fill in a new Connection instance.
2006 
2007 			NodeRef* destRef = (*itDest).second;
2008 			ASSERT(destRef);
2009 			if(!(destRef->kind() & B_BUFFER_CONSUMER))
2010 				// no inputs
2011 				continue;
2012 
2013 			vector<media_input> inputs;
2014 			err = destRef->getConnectedInputs(inputs);
2015 
2016 			// +++++ FAILED ON STARTUP [e.moon 28sep99]; haven't reproduced yet
2017 			//       [21oct99] failed again
2018 			//ASSERT(err == B_OK);
2019 			if(err < B_OK) {
2020 				PRINT((
2021 					"!!! NodeManager::_handleNodesCreated():\n"
2022 					"    NodeRef('%s')::getConnectedInputs() failed:\n"
2023 					"    %s\n",
2024 					destRef->name(), strerror(err)));
2025 
2026 				continue;
2027 			}
2028 
2029 
2030 //			PRINT((" - %s: %ld inputs\n", destRef->name(), inputs.size()));
2031 
2032 			for(vector<media_input>::const_iterator itInput = inputs.begin();
2033 				itInput != inputs.end(); ++itInput) {
2034 
2035 				// look for a matching source node by port ID:
2036 				const media_input& input = *itInput;
2037 				port_ref_map::const_iterator itSource = initialNodes->find(
2038 					input.source.port);
2039 
2040 				if(itSource == initialNodes->end()) {
2041 					// source not found!
2042 					PRINT((
2043 						"* NodeManager::_handleNodesCreated():\n"
2044 						"  Building initial Connection set: couldn't find source node\n"
2045 						"  connected to input '%s' of '%s' (source port %" B_PRId32 ").\n",
2046 						input.name, destRef->name(), input.source.port));
2047 					continue;
2048 				}
2049 
2050 				// found it; fetch matching output
2051 				NodeRef* sourceRef = (*itSource).second;
2052 				ASSERT(sourceRef);
2053 				media_output output;
2054 				err = sourceRef->findOutput(input.source, &output);
2055 				if(err < B_OK) {
2056 					PRINT((
2057 						"* NodeManager::_handleNodesCreated():\n"
2058 						"  Building initial Connection set: couldn't find output\n"
2059 						"  in node '%s' connected to input '%s' of '%s'.\n",
2060 						sourceRef->name(),
2061 						input.name, destRef->name()));
2062 					continue;
2063 				}
2064 
2065 				// sanity check
2066 //				ASSERT(input.source == output.source);
2067 //				ASSERT(input.destination == output.destination);
2068 				// diagnostics [e.moon 11jan00]
2069 				if(input.source != output.source ||
2070 					input.destination != output.destination) {
2071 					PRINT((
2072 						"!!! NodeManager::_handleNodesCreated():\n"
2073 						"    input/output mismatch for connection\n"
2074 						"    '%s' (%s) -> '%s' (%s)\n"
2075 						"    input.source:        port %" B_PRId32 ", ID %" B_PRId32 "\n"
2076 						"    output.source:       port %" B_PRId32 ", ID %" B_PRId32 "\n"
2077 						"    input.destination:   port %" B_PRId32 ", ID %" B_PRId32 "\n"
2078 						"    output.destination:  port %" B_PRId32 ", ID %" B_PRId32 "\n\n",
2079 						sourceRef->name(), output.name,
2080 						destRef->name(), input.name,
2081 						input.source.port, input.source.id,
2082 						output.source.port, output.source.id,
2083 						input.destination.port, input.destination.id,
2084 						output.destination.port, output.destination.id));
2085 					continue;
2086 				}
2087 
2088 				// instantiate & add connection
2089 
2090 				Connection* con = new Connection(
2091 					m_nextConID++,
2092 					output.node,
2093 					output.source,
2094 					output.name,
2095 					input.node,
2096 					input.destination,
2097 					input.name,
2098 					input.format,
2099 					0);
2100 
2101 				_addConnection(con);
2102 
2103 //				// [e.moon 7nov99] call hook
2104 //				connectionMade(con);
2105 
2106 //				PRINT((
2107 //					"* NodeManager::_handleNodesCreated(): Found initial connection:\n"
2108 //					"  %s:%s -> %s:%s\n",
2109 //					sourceRef->name(), con->outputName(),
2110 //					destRef->name(), con->inputName()));
2111 
2112 			} // for(vector<media_input> ...
2113 
2114 		} // for(port_ref_map ...
2115 
2116 		// mark the ordeal as over & done with
2117 		m_existingNodesInit = true;
2118 		// clean up
2119 		delete initialNodes;
2120 	}
2121 
2122 	// don't relay message if no new create notifications were received [e.moon 11oct99]
2123 	return refsCreated ? B_OK : B_ERROR;
2124 }
2125 
2126 inline void NodeManager::_handleNodesDeleted(
2127 	BMessage*											message) {
2128 	ASSERT(IsLocked());
2129 
2130 	D_METHOD((
2131 		"NodeManager::_handleNodesDeleted()\n"));
2132 
2133 	// walk the list of deleted nodes, removing & cleaning up refs
2134 	// (and any straggler connections)
2135 
2136 	type_code type;
2137 	int32 count;
2138 	status_t err = message->GetInfo("media_node_id", &type, &count);
2139 	if(err < B_OK) {
2140 		PRINT((
2141 			"* NodeManager::_handleNodesDeleted(): GetInfo() failed:\n"
2142 			"  %s\n",
2143 			strerror(err)));
2144 		return;
2145 	}
2146 	if(!count)
2147 		return;
2148 
2149 	for(int32 n = 0; n < count; n++) {
2150 
2151 		int32 id;
2152 		err = message->FindInt32("media_node_id", n, &id);
2153 		if(err < B_OK) {
2154 			PRINT((
2155 				"* NodeManager::_handleNodesDeleted(): FindInt32() failed\n"
2156 				"  %s\n",
2157 				strerror(err)));
2158 			continue;
2159 		}
2160 
2161 		// fetch ref
2162 		NodeRef* ref;
2163 		err = getNodeRef(id, &ref);
2164 		if(err < B_OK) {
2165 			PRINT((
2166 				"* NodeManager::_handleNodesDeleted(): getNodeRef(%" B_PRId32
2167 					") failed\n"
2168 				"  %s\n",
2169 				id, strerror(err)));
2170 			continue;
2171 		}
2172 
2173 		// find & remove any 'stuck' connections; notify any observers
2174 		// that the connections have been removed
2175 		vector<Connection> stuckConnections;
2176 		ref->getInputConnections(stuckConnections);
2177 		ref->getOutputConnections(stuckConnections);
2178 
2179 		BMessage message(B_MEDIA_CONNECTION_BROKEN);
2180 
2181 		for(uint32 n = 0; n < stuckConnections.size(); ++n) {
2182 			Connection& c = stuckConnections[n];
2183 
2184 			// generate a complete B_MEDIA_CONNECTION_BROKEN message
2185 			// +++++ the message format may be extended in the future -- ick
2186 
2187 			message.AddData("source", B_RAW_TYPE, &c.source(), sizeof(media_source));
2188 			message.AddData("destination", B_RAW_TYPE, &c.destination(), sizeof(media_destination));
2189 			message.AddInt32(_connectionField, c.id());
2190 			message.AddInt32(_sourceNodeField, c.sourceNode());
2191 			message.AddInt32(_destNodeField, c.destinationNode());
2192 
2193 			_removeConnection(c);
2194 		}
2195 
2196 		// +++++ don't notify if no stuck connections were found
2197 		notify(&message);
2198 
2199 		// ungroup if necessary
2200 		if(ref->m_group) {
2201 			ASSERT(!ref->isReleased());
2202 			ref->m_group->removeNode(ref);
2203 		}
2204 
2205 		// [e.moon 23oct99] mark the node released!
2206 		ref->m_nodeReleased = true;
2207 
2208 //		// [e.moon 7nov99] call hook
2209 //		nodeDeleted(ref);
2210 
2211 		// release it
2212 		ref->release();
2213 
2214 	} // for(int32 n ...
2215 }
2216 
2217 inline void NodeManager::_handleConnectionMade(
2218 	BMessage*											message) {
2219 	ASSERT(IsLocked());
2220 	D_METHOD((
2221 		"NodeManager::_handleConnectionMade()\n"));
2222 	status_t err;
2223 
2224 	for(int32 n = 0;;++n) {
2225 		media_input input;
2226 		media_output output;
2227 		const void* data;
2228 		ssize_t dataSize;
2229 
2230 		// fetch output
2231 		err = message->FindData("output", B_RAW_TYPE, n, &data, &dataSize);
2232 		if(err < B_OK) {
2233 			if(!n) {
2234 				PRINT((
2235 					"* NodeManager::_handleConnectionMade(): no entries in message.\n"));
2236 			}
2237 			break;
2238 		}
2239 		if(dataSize < ssize_t(sizeof(media_output))) {
2240 			PRINT((
2241 				"* NodeManager::_handleConnectionMade(): not enough data for output.\n"));
2242 			break;
2243 		}
2244 		output = *(media_output*)data;
2245 
2246 		// fetch input
2247 		err = message->FindData("input", B_RAW_TYPE, n, &data, &dataSize);
2248 		if(err < B_OK) {
2249 			if(!n) {
2250 				PRINT((
2251 					"* NodeManager::_handleConnectionMade(): no complete entries in message.\n"));
2252 			}
2253 			break;
2254 		}
2255 		if(dataSize < ssize_t(sizeof(media_input))) {
2256 			PRINT((
2257 				"* NodeManager::_handleConnectionMade(): not enough data for input.\n"));
2258 			break;
2259 		}
2260 		input = *(media_input*)data;
2261 
2262 		// look for existing Connection instance
2263 		Connection found;
2264 		err = findConnection(
2265 			output.node.node,
2266 			output.source,
2267 			&found);
2268 		if(err == B_OK) {
2269 			PRINT((
2270 				"  - existing connection for %s -> %s found\n",
2271 				found.outputName(), found.inputName()));
2272 			continue;
2273 		}
2274 
2275 		// instantiate & add Connection
2276 		Connection* con = new Connection(
2277 			m_nextConID++,
2278 			output.node,
2279 			output.source,
2280 			output.name,
2281 			input.node,
2282 			input.destination,
2283 			input.name,
2284 			input.format,
2285 			0);
2286 
2287 		_addConnection(con);
2288 	}
2289 }
2290 
2291 // augments message with source and destination node ID's
2292 inline void NodeManager::_handleConnectionBroken(
2293 	BMessage*											message) {
2294 
2295 	D_METHOD((
2296 		"NodeManager::_handleConnectionBroken()\n"));
2297 	status_t err;
2298 
2299 	// walk the listed connections
2300 	for(int32 n=0;;n++) {
2301 		media_source source;
2302 
2303 		const void* data;
2304 		ssize_t dataSize;
2305 
2306 		// fetch source
2307 		err = message->FindData("source", B_RAW_TYPE, n, &data, &dataSize);
2308 		if(err < B_OK) {
2309 			if(!n) {
2310 				PRINT((
2311 					"* NodeManager::_handleConnectionBroken(): incomplete entry in message.\n"));
2312 			}
2313 			break;
2314 		}
2315 		if(dataSize < ssize_t(sizeof(media_source))) {
2316 			PRINT((
2317 				"* NodeManager::_handleConnectionBroken(): not enough data for source.\n"));
2318 			continue;
2319 		}
2320 		source = *(media_source*)data;
2321 
2322 		// look up the connection +++++ SLOW +++++
2323 		Connection con;
2324 		err = findConnection(source, &con);
2325 		if(err < B_OK) {
2326 			PRINT((
2327 				"* NodeManager::_handleConnectionBroken(): connection not found:\n"
2328 				"  %" B_PRId32 ":%" B_PRId32 "\n",
2329 				source.port, source.id));
2330 
2331 			// add empty entry to message
2332 			message->AddInt32(_connectionField, 0);
2333 			message->AddInt32(_sourceNodeField, 0);
2334 			message->AddInt32(_destNodeField, 0);
2335 			continue;
2336 		}
2337 
2338 		// add entry to the message
2339 		message->AddInt32(_connectionField, con.id());
2340 		message->AddInt32(_sourceNodeField, con.sourceNode());
2341 		message->AddInt32(_destNodeField, con.destinationNode());
2342 
2343 //		// [e.moon 7nov99] call hook
2344 //		connectionBroken(&con);
2345 
2346 		// home free; delete the connection
2347 		_removeConnection(con);
2348 
2349 	} // for(int32 n ...
2350 }
2351 
2352 inline void
2353 NodeManager::_handleFormatChanged(BMessage *message)
2354 {
2355 	D_METHOD((
2356 		"NodeManager::_handleFormatChanged()\n"));
2357 	status_t err;
2358 
2359 	ssize_t dataSize;
2360 
2361 	// fetch source
2362 	media_source* source;
2363 	err = message->FindData("be:source", B_RAW_TYPE, (const void**)&source, &dataSize);
2364 	if(err < B_OK) {
2365 		PRINT((
2366 			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2367 		return;
2368 	}
2369 
2370 	// fetch destination
2371 	media_destination* destination;
2372 	err = message->FindData("be:destination", B_RAW_TYPE, (const void**)&destination, &dataSize);
2373 	if(err < B_OK) {
2374 		PRINT((
2375 			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2376 		return;
2377 	}
2378 
2379 	// fetch format
2380 	media_format* format;
2381 	err = message->FindData("be:format", B_RAW_TYPE, (const void**)&format, &dataSize);
2382 	if(err < B_OK) {
2383 		PRINT((
2384 			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2385 		return;
2386 	}
2387 
2388 	// find matching connection
2389 	for(con_map::const_iterator it = m_conSourceMap.begin();
2390 		it != m_conSourceMap.end(); ++it) {
2391 		if((*it).second->source() == *source) {
2392 			if((*it).second->destination() != *destination) {
2393 				// connection defunct
2394 				return;
2395 			}
2396 
2397 			// found
2398 			(*it).second->m_format = *format;
2399 
2400 			// attach node IDs to message
2401 			message->AddInt32(_connectionField, (*it).second->id());
2402 			message->AddInt32(_sourceNodeField, (*it).second->sourceNode());
2403 			message->AddInt32(_destNodeField, (*it).second->destinationNode());
2404 
2405 			break;
2406 		}
2407 	}
2408 }
2409 
2410 
2411 // return flags appropriate for an external
2412 // node with the given 'kind'
2413 
2414 inline uint32 NodeManager::_userFlagsForKind(
2415 	uint32												kind) {
2416 
2417 	uint32 f = 0;
2418 	if(
2419 //		kind & B_TIME_SOURCE ||
2420 		kind & B_PHYSICAL_OUTPUT
2421 		// || kind & B_SYSTEM_MIXER [now in initCommonNodes() e.moon 17nov99]
2422 		)
2423 		f |= (NodeRef::NO_START_STOP | NodeRef::NO_SEEK | NodeRef::NO_PREROLL);
2424 
2425 //	// [28sep99 e.moon] physical inputs may not be stopped for now; at
2426 //	// least one sound input node (for emu10k) stops working when requested
2427 //	// to stop.
2428 //	// +++++ should this logic be in initCommonNodes()?
2429 //	if(
2430 //		kind & B_PHYSICAL_INPUT)
2431 //		f |= NodeRef::NO_STOP;
2432 
2433 	return f;
2434 }
2435 
2436 inline uint32 NodeManager::_implFlagsForKind(
2437 	uint32												kind) {
2438 
2439 	return 0;
2440 }
2441 
2442 // [e.moon 28sep99] latency updating
2443 // These methods must set the recording-mode delay for
2444 // any B_RECORDING nodes they handle.
2445 
2446 // +++++ abstract to 'for each' and 'for each from'
2447 //       methods (template or callback?)
2448 
2449 
2450 // refresh cached latency for every node in the given group
2451 // (or all nodes if no group given.)
2452 
2453 inline void NodeManager::_updateLatencies(
2454 	NodeGroup*										group) {
2455 
2456 	ASSERT(IsLocked());
2457 	if(group) {
2458 		ASSERT(group->isLocked());
2459 	}
2460 
2461 	if(group) {
2462 		for(NodeGroup::node_set::iterator it = group->m_nodes.begin();
2463 			it != group->m_nodes.end(); ++it) {
2464 
2465 			(*it)->_updateLatency();
2466 		}
2467 	}
2468 	else {
2469 		for(node_ref_map::iterator it = m_nodeRefMap.begin();
2470 			it != m_nodeRefMap.end(); ++it) {
2471 
2472 			(*it).second->_updateLatency();
2473 		}
2474 	}
2475 }
2476 
2477 // refresh cached latency for every node attached to
2478 // AND INCLUDING the given origin node.
2479 // if 'recurse' is true, affects indirectly attached
2480 // nodes as well.
2481 
2482 
2483 inline void NodeManager::_updateLatenciesFrom(
2484 	NodeRef*											origin,
2485 	bool													recurse) {
2486 
2487 	ASSERT(IsLocked());
2488 
2489 //	PRINT(("### NodeManager::_updateLatenciesFrom()\n"));
2490 
2491 	origin->lock();
2492 	origin->_updateLatency();
2493 	origin->unlock();
2494 
2495 	_lockAllGroups(); // [e.moon 13oct99]
2496 
2497 	_for_each_state st;
2498 	_do_for_each_connected(
2499 		this,
2500 		origin,
2501 		0, // all groups
2502 		recurse,
2503 #if __GNUC__ <= 2
2504 		mem_fun(&NodeRef::_updateLatency),
2505 #else
2506 		[](NodeRef* node) { return node->_updateLatency(); },
2507 #endif
2508 		&st);
2509 
2510 	_unlockAllGroups(); // [e.moon 13oct99]
2511 }
2512 
2513 // a bit of unpleasantness [e.moon 13oct99]
2514 void NodeManager::_lockAllGroups() {
2515 
2516 	ASSERT(IsLocked());
2517 	for(node_group_set::iterator it = m_nodeGroupSet.begin();
2518 		it != m_nodeGroupSet.end(); ++it) {
2519 		(*it)->lock();
2520 	}
2521 }
2522 
2523 void NodeManager::_unlockAllGroups() {
2524 	ASSERT(IsLocked());
2525 	for(node_group_set::iterator it = m_nodeGroupSet.begin();
2526 		it != m_nodeGroupSet.end(); ++it) {
2527 		(*it)->unlock();
2528 	}
2529 }
2530 
2531 
2532 // END -- NodeManager.cpp --
2533