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