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