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