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 // RouteAppNodeManager.cpp 33 34 #include "RouteAppNodeManager.h" 35 36 #include "MediaIcon.h" 37 #include "NodeGroup.h" 38 #include "NodeRef.h" 39 #include "Connection.h" 40 41 #include "route_app_io.h" 42 #include "ConnectionIO.h" 43 #include "DormantNodeIO.h" 44 #include "LiveNodeIO.h" 45 #include "MediaFormatIO.h" 46 #include "MessageIO.h" 47 #include "NodeSetIOContext.h" 48 #include "StringContent.h" 49 #include "MediaString.h" 50 51 #include <Autolock.h> 52 #include <Debug.h> 53 #include <Entry.h> 54 #include <Path.h> 55 56 #include <TimeSource.h> 57 58 #include <cstring> 59 #include <cstdlib> 60 #include <cstdio> 61 #include <typeinfo> 62 63 #include "set_tools.h" 64 65 // Locale Kit 66 #include <Catalog.h> 67 68 #undef B_TRANSLATION_CONTEXT 69 #define B_TRANSLATION_CONTEXT "CortexRouteApp" 70 71 using namespace std; 72 73 __USE_CORTEX_NAMESPACE 74 75 #define D_METHOD(x) //PRINT (x) 76 #define D_HOOK(x) //PRINT (x) 77 #define D_SETTINGS(x) //PRINT (x) 78 79 // -------------------------------------------------------- // 80 // *** ctor/dtor 81 // -------------------------------------------------------- // 82 83 RouteAppNodeManager::~RouteAppNodeManager() { 84 85 _freeIcons(); 86 } 87 88 RouteAppNodeManager::RouteAppNodeManager( 89 bool useAddOnHost) : 90 NodeManager(useAddOnHost), 91 m_nextGroupNumber(1) { 92 93 // pre-cache icons? +++++ 94 95 } 96 97 // -------------------------------------------------------- // 98 // *** group management 99 // -------------------------------------------------------- // 100 101 // -------------------------------------------------------- // 102 // *** icon management 103 // -------------------------------------------------------- // 104 105 // fetch cached icon for the given live node; the MediaIcon 106 // instance is guaranteed to last as long as this object. 107 // Returns 0 if the node doesn't exist. 108 109 const MediaIcon* RouteAppNodeManager::mediaIconFor( 110 media_node_id nodeID, 111 icon_size iconSize) { 112 113 BAutolock _l(this); 114 115 uint64 key = _makeIconKey(nodeID, iconSize); 116 117 icon_map::const_iterator it = m_iconMap.find(key); 118 if(it != m_iconMap.end()) { 119 // already cached 120 return (*it).second; 121 } 122 123 // look up live_node_info 124 NodeRef* ref; 125 status_t err = getNodeRef(nodeID, &ref); 126 if(err < B_OK) 127 return 0; 128 129 return mediaIconFor(ref->nodeInfo(), iconSize); 130 } 131 132 const MediaIcon* RouteAppNodeManager::mediaIconFor( 133 live_node_info nodeInfo, 134 icon_size iconSize) { 135 136 uint64 key = _makeIconKey(nodeInfo.node.node, iconSize); 137 138 icon_map::const_iterator it = m_iconMap.find(key); 139 if(it != m_iconMap.end()) { 140 // already cached 141 return (*it).second; 142 } 143 144 // create & cache icon 145 MediaIcon* icon = new MediaIcon( 146 nodeInfo, iconSize); 147 148 m_iconMap.insert( 149 icon_map::value_type(key, icon)); 150 151 return icon; 152 } 153 154 // -------------------------------------------------------- // 155 // *** error handling 156 // -------------------------------------------------------- // 157 158 status_t RouteAppNodeManager::setLogTarget( 159 const BMessenger& target) { 160 161 BAutolock _l(this); 162 163 if(!target.IsValid()) 164 return B_BAD_VALUE; 165 166 m_logTarget = target; 167 return B_OK; 168 } 169 170 // -------------------------------------------------------- // 171 // NodeManager hook implementations 172 // -------------------------------------------------------- // 173 174 void RouteAppNodeManager::nodeCreated( 175 NodeRef* ref) { 176 177 // prepare the log message 178 BMessage logMsg(M_LOG); 179 BString title = B_TRANSLATE("Node '%name%' created"); 180 title.ReplaceFirst("%name%", ref->name()); 181 logMsg.AddString("title", title); 182 183 // create a default group for the node 184 // [em 8feb00] 185 NodeGroup* g = createGroup(ref->name()); 186 187 if(ref->kind() & B_TIME_SOURCE) { 188 // notify observers 189 BMessage m(M_TIME_SOURCE_CREATED); 190 m.AddInt32("nodeID", ref->id()); 191 notify(&m); 192 } 193 194 // adopt node's time source if it's not the system clock (the default) 195 // [em 20mar00] 196 media_node systemClock; 197 status_t err = roster->GetSystemTimeSource(&systemClock); 198 if(err == B_OK) 199 { 200 BTimeSource* ts = roster->MakeTimeSourceFor(ref->node()); 201 if (ts == NULL) 202 return; 203 if(ts->Node() != systemClock) 204 { 205 g->setTimeSource(ts->Node()); 206 logMsg.AddString("line", "Synced to system clock"); 207 } 208 ts->Release(); 209 } 210 211 g->addNode(ref); 212 213 m_logTarget.SendMessage(&logMsg); 214 } 215 216 void RouteAppNodeManager::nodeDeleted( 217 const NodeRef* ref) { 218 219 // prepare the log message 220 BMessage logMsg(M_LOG); 221 BString title = B_TRANSLATE("Node '%name%' released"); 222 title.ReplaceFirst("%name%", ref->name()); 223 logMsg.AddString("title", title); 224 225 if(ref->kind() & B_TIME_SOURCE) { 226 // notify observers 227 BMessage m(M_TIME_SOURCE_DELETED); 228 m.AddInt32("nodeID", ref->id()); 229 notify(&m); 230 } 231 232 m_logTarget.SendMessage(&logMsg); 233 } 234 235 void RouteAppNodeManager::connectionMade( 236 Connection* connection) { 237 238 D_HOOK(( 239 "@ RouteAppNodeManager::connectionMade()\n")); 240 241 status_t err; 242 243 // prepare the log message 244 BMessage logMsg(M_LOG); 245 BString title = B_TRANSLATE("Connection made"); 246 if (strcmp(connection->outputName(), connection->inputName()) == 0) { 247 title = B_TRANSLATE("Connection '%name%' made"); 248 title.ReplaceFirst("%name%", connection->outputName()); 249 } 250 logMsg.AddString("title", title); 251 252 if(!(connection->flags() & Connection::INTERNAL)) 253 // don't react to connection Cortex didn't make 254 return; 255 256 // create or merge groups 257 NodeRef *producer, *consumer; 258 err = getNodeRef(connection->sourceNode(), &producer); 259 if(err < B_OK) { 260 D_HOOK(( 261 "!!! RouteAppNodeManager::connectionMade():\n" 262 " sourceNode (%ld) not found\n", 263 connection->sourceNode())); 264 return; 265 } 266 err = getNodeRef(connection->destinationNode(), &consumer); 267 if(err < B_OK) { 268 D_HOOK(( 269 "!!! RouteAppNodeManager::connectionMade():\n" 270 " destinationNode (%ld) not found\n", 271 connection->destinationNode())); 272 return; 273 } 274 275 // add node names to log messages 276 BString line = B_TRANSLATE("Between:"); 277 logMsg.AddString("line", line); 278 line = " "; 279 line += B_TRANSLATE("%producer% and %consumer%"); 280 line.ReplaceFirst("%producer%", producer->name()); 281 line.ReplaceFirst("%consumer%", consumer->name()); 282 logMsg.AddString("line", line); 283 284 // add format to log message 285 line = B_TRANSLATE("Negotiated format:"); 286 logMsg.AddString("line", line); 287 line = " "; 288 line << MediaString::getStringFor(connection->format(), false); 289 logMsg.AddString("line", line); 290 291 NodeGroup *group = 0; 292 BString groupName = B_TRANSLATE("Untitled group"); 293 groupName += " "; 294 if(_canGroup(producer) && _canGroup(consumer)) 295 { 296 if (producer->group() && consumer->group() && 297 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED) && 298 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 299 { 300 // merge into consumers group 301 group = consumer->group(); 302 mergeGroups(producer->group(), group); 303 } 304 else if (producer->group() && 305 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 306 { // add consumer to producers group 307 group = producer->group(); 308 group->addNode(consumer); 309 } 310 else if (consumer->group() && 311 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 312 { // add producer to consumers group 313 group = consumer->group(); 314 group->addNode(producer); 315 } 316 else 317 { // make new group for both 318 groupName << m_nextGroupNumber++; 319 group = createGroup(groupName.String()); 320 group->addNode(producer); 321 group->addNode(consumer); 322 } 323 } 324 else if(_canGroup(producer) && !producer->group()) 325 { // make new group for producer 326 groupName << m_nextGroupNumber++; 327 group = createGroup(groupName.String()); 328 group->addNode(producer); 329 } 330 else if(_canGroup(consumer) && !consumer->group()) 331 { // make new group for consumer 332 groupName << m_nextGroupNumber++; 333 group = createGroup(groupName.String()); 334 group->addNode(consumer); 335 } 336 337 m_logTarget.SendMessage(&logMsg); 338 } 339 340 void RouteAppNodeManager::connectionBroken( 341 const Connection* connection) { 342 343 D_HOOK(( 344 "@ RouteAppNodeManager::connectionBroken()\n")); 345 346 // prepare the log message 347 BMessage logMsg(M_LOG); 348 BString title = B_TRANSLATE("Connection broken"); 349 if (strcmp(connection->outputName(), connection->inputName()) == 0) { 350 title = B_TRANSLATE("Connection '%name%' broken"); 351 title.ReplaceFirst("%name%", connection->outputName()); 352 } 353 logMsg.AddString("title", title); 354 355 if(!(connection->flags() & Connection::INTERNAL)) 356 // don't react to connection Cortex didn't make 357 return; 358 359 status_t err; 360 361 // if the source and destination nodes belong to the same group, 362 // and if no direct or indirect connection remains between the 363 // source and destination nodes, split groups 364 365 NodeRef *producer, *consumer; 366 err = getNodeRef(connection->sourceNode(), &producer); 367 if(err < B_OK) { 368 D_HOOK(( 369 "!!! RouteAppNodeManager::connectionMade():\n" 370 " sourceNode (%ld) not found\n", 371 connection->sourceNode())); 372 return; 373 } 374 err = getNodeRef(connection->destinationNode(), &consumer); 375 if(err < B_OK) { 376 D_HOOK(( 377 "!!! RouteAppNodeManager::connectionMade():\n" 378 " destinationNode (%ld) not found\n", 379 connection->destinationNode())); 380 return; 381 } 382 383 // add node names to log messages 384 BString line = B_TRANSLATE("Between:"); 385 logMsg.AddString("line", line); 386 line = " "; 387 line += B_TRANSLATE("%producer% and %consumer%"); 388 line.ReplaceFirst("%producer%", producer->name()); 389 line.ReplaceFirst("%consumer%", consumer->name()); 390 logMsg.AddString("line", line); 391 392 if( 393 producer->group() && 394 producer->group() == consumer->group() && 395 !findRoute(producer->id(), consumer->id())) { 396 397 NodeGroup *newGroup; 398 splitGroup(producer, consumer, &newGroup); 399 } 400 401 m_logTarget.SendMessage(&logMsg); 402 } 403 404 void RouteAppNodeManager::connectionFailed( 405 const media_output & output, 406 const media_input & input, 407 const media_format & format, 408 status_t error) { 409 D_HOOK(( 410 "@ RouteAppNodeManager::connectionFailed()\n")); 411 412 status_t err; 413 414 // prepare the log message 415 BMessage logMsg(M_LOG); 416 BString title = B_TRANSLATE("Connection failed"); 417 logMsg.AddString("title", title); 418 logMsg.AddInt32("error", error); 419 420 NodeRef *producer, *consumer; 421 err = getNodeRef(output.node.node, &producer); 422 if(err < B_OK) { 423 return; 424 } 425 err = getNodeRef(input.node.node, &consumer); 426 if(err < B_OK) { 427 return; 428 } 429 430 // add node names to log messages 431 BString line = B_TRANSLATE("Between:"); 432 logMsg.AddString("line", line); 433 line = " "; 434 line += B_TRANSLATE("%producer% and %consumer%"); 435 line.ReplaceFirst("%producer%", producer->name()); 436 line.ReplaceFirst("%consumer%", consumer->name()); 437 logMsg.AddString("line", line); 438 439 // add format to log message 440 line = B_TRANSLATE("Tried format:"); 441 logMsg.AddString("line", line); 442 line = " "; 443 line << MediaString::getStringFor(format, true); 444 logMsg.AddString("line", line); 445 446 // and send it 447 m_logTarget.SendMessage(&logMsg); 448 } 449 450 // -------------------------------------------------------- // 451 // *** IPersistent 452 // -------------------------------------------------------- // 453 454 void RouteAppNodeManager::xmlExportBegin( 455 ExportContext& context) const { 456 457 status_t err; 458 459 try { 460 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 461 context.beginElement(_NODE_SET_ELEMENT); 462 463 // validate the node set 464 for(int n = set.countNodes()-1; n >= 0; --n) { 465 media_node_id id = set.nodeAt(n); 466 ASSERT(id != media_node::null.node); 467 468 // fetch node 469 NodeRef* ref; 470 err = getNodeRef(id, &ref); 471 if(err < B_OK) { 472 D_SETTINGS(( 473 "! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id)); 474 475 set.removeNodeAt(n); 476 continue; 477 } 478 // skip unless internal 479 if(!ref->isInternal()) { 480 D_SETTINGS(( 481 "! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id)); 482 483 set.removeNodeAt(n); 484 continue; 485 } 486 } 487 } 488 catch(bad_cast& e) { 489 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 490 } 491 } 492 493 void RouteAppNodeManager::xmlExportAttributes( 494 ExportContext& context) const {} 495 496 void RouteAppNodeManager::xmlExportContent( 497 ExportContext& context) const { 498 499 status_t err; 500 501 try { 502 NodeSetIOContext& nodeSet = dynamic_cast<NodeSetIOContext&>(context); 503 context.beginContent(); 504 505 // write nodes; enumerate connections 506 typedef map<uint32,Connection> connection_map; 507 connection_map connections; 508 int count = nodeSet.countNodes(); 509 for(int n = 0; n < count; ++n) { 510 media_node_id id = nodeSet.nodeAt(n); 511 ASSERT(id != media_node::null.node); 512 513 // fetch node 514 NodeRef* ref; 515 err = getNodeRef(id, &ref); 516 if(err < B_OK) { 517 D_SETTINGS(( 518 "! RouteAppNodeManager::xmlExportContent():\n" 519 " getNodeRef(%ld) failed: '%s'\n", 520 id, strerror(err))); 521 continue; 522 } 523 524 // fetch connections 525 vector<Connection> conSet; 526 ref->getInputConnections(conSet); 527 ref->getOutputConnections(conSet); 528 for(uint32 c = 0; c < conSet.size(); ++c) 529 // non-unique connections will be rejected: 530 connections.insert( 531 connection_map::value_type(conSet[c].id(), conSet[c])); 532 533 // create an IO object for the node & write it 534 DormantNodeIO io(ref, nodeSet.keyAt(n)); 535 if(context.writeObject(&io) < B_OK) 536 // abort 537 return; 538 } 539 540 // write connections 541 for(connection_map::const_iterator it = connections.begin(); 542 it != connections.end(); ++it) { 543 544 ConnectionIO io( 545 &(*it).second, 546 this, 547 &nodeSet); 548 if(context.writeObject(&io) < B_OK) 549 // abort 550 return; 551 552 } 553 554 // +++++ write groups 555 556 // write UI state 557 { 558 BMessage m; 559 nodeSet.exportUIState(&m); 560 context.beginElement(_UI_STATE_ELEMENT); 561 context.beginContent(); 562 MessageIO io(&m); 563 context.writeObject(&io); 564 context.endElement(); 565 } 566 } 567 catch(bad_cast& e) { 568 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 569 } 570 } 571 572 void RouteAppNodeManager::xmlExportEnd( 573 ExportContext& context) const { 574 575 context.endElement(); 576 } 577 578 // -------------------------------------------------------- // 579 // IMPORT 580 // -------------------------------------------------------- // 581 582 void RouteAppNodeManager::xmlImportBegin( 583 ImportContext& context) { 584 585 } 586 587 void RouteAppNodeManager::xmlImportAttribute( 588 const char* key, 589 const char* value, 590 ImportContext& context) {} 591 592 void RouteAppNodeManager::xmlImportContent( 593 const char* data, 594 uint32 length, 595 ImportContext& context) {} 596 597 void RouteAppNodeManager::xmlImportChild( 598 IPersistent* child, 599 ImportContext& context) { 600 601 status_t err; 602 603 if(!strcmp(context.element(), _DORMANT_NODE_ELEMENT)) { 604 DormantNodeIO* io = dynamic_cast<DormantNodeIO*>(child); 605 ASSERT(io); 606 607 NodeRef* newRef; 608 err = io->instantiate(this, &newRef); 609 if(err == B_OK) { 610 // created node; add an entry to the set stored in the 611 // ImportContext for later reference 612 try { 613 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 614 set.addNode(newRef->id(), io->nodeKey()); 615 } 616 catch(bad_cast& e) { 617 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 618 } 619 } 620 else { 621 D_SETTINGS(( 622 "!!! RouteAppNodeManager::xmlImportChild():\n" 623 " DormantNodeIO::instantiate() failed:\n" 624 " '%s'\n", 625 strerror(err))); 626 } 627 } 628 else if(!strcmp(context.element(), _CONNECTION_ELEMENT)) { 629 ConnectionIO* io = dynamic_cast<ConnectionIO*>(child); 630 ASSERT(io); 631 632 // instantiate the connection 633 Connection con; 634 err = io->instantiate( 635 this, 636 dynamic_cast<NodeSetIOContext*>(&context), 637 &con); 638 if(err < B_OK) { 639 D_SETTINGS(( 640 "!!! ConnectionIO::instantiate() failed:\n" 641 " '%s'\n", strerror(err))); 642 } 643 644 // +++++ group magic? 645 646 } 647 else if(!strcmp(context.element(), _NODE_GROUP_ELEMENT)) { 648 // +++++ 649 } 650 else if( 651 context.parentElement() && 652 !strcmp(context.parentElement(), _UI_STATE_ELEMENT)) { 653 654 // expect a nested message 655 MessageIO* io = dynamic_cast<MessageIO*>(child); 656 if(!io) { 657 BString err; 658 err << 659 "RouteAppNodeManager: unexpected child '" << 660 context.element() << "'\n"; 661 context.reportError(err.String()); 662 } 663 664 // hand it off via the extended context object 665 try { 666 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 667 set.importUIState(io->message()); 668 } 669 catch(bad_cast& e) { 670 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 671 } 672 } 673 674 } 675 676 void RouteAppNodeManager::xmlImportComplete( 677 ImportContext& context) { 678 679 } 680 681 // -------------------------------------------------------- // 682 683 /*static*/ 684 void RouteAppNodeManager::AddTo( 685 XML::DocumentType* docType) { 686 687 // set up the document type 688 689 MessageIO::AddTo(docType); 690 MediaFormatIO::AddTo(docType); 691 ConnectionIO::AddTo(docType); 692 DormantNodeIO::AddTo(docType); 693 LiveNodeIO::AddTo(docType); 694 _add_string_elements(docType); 695 } 696 697 // -------------------------------------------------------- // 698 // implementation 699 // -------------------------------------------------------- // 700 701 uint64 RouteAppNodeManager::_makeIconKey( 702 media_node_id nodeID, icon_size iconSize) { 703 704 return ((uint64)nodeID) << 32 | iconSize; 705 } 706 707 void RouteAppNodeManager::_readIconKey( 708 uint64 key, media_node_id& nodeID, icon_size& iconSize) { 709 710 nodeID = key >> 32; 711 iconSize = icon_size(key & 0xffffffff); 712 } 713 714 void RouteAppNodeManager::_freeIcons() { 715 716 ptr_map_delete( 717 m_iconMap.begin(), 718 m_iconMap.end()); 719 } 720 721 bool RouteAppNodeManager::_canGroup(NodeRef* ref) const { 722 723 // sanity check & easy cases 724 ASSERT(ref); 725 if(ref->isInternal()) 726 return true; 727 728 // bar 'touchy' system nodes 729 if(ref == audioMixerNode() || ref == audioOutputNode()) 730 return false; 731 732 return true; 733 } 734 735 // END -- RouteAppNodeManager.cpp -- 736