1 /* 2 * Copyright 2002-2015, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Matthijs Hollemans 7 */ 8 9 /* 10 * Copyright (c) 2002-2004 Matthijs Hollemans 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining a 13 * copy of this software and associated documentation files (the "Software"), 14 * to deal in the Software without restriction, including without limitation 15 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 * and/or sell copies of the Software, and to permit persons to whom the 17 * Software is furnished to do so, subject to the following conditions: 18 * 19 * The above copyright notice and this permission notice shall be included in 20 * all copies or substantial portions of the Software. 21 * 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 * DEALINGS IN THE SOFTWARE. 29 */ 30 31 32 #include "MidiServerApp.h" 33 34 #include <new> 35 36 #include <Alert.h> 37 38 #include "debug.h" 39 #include "protocol.h" 40 #include "PortDrivers.h" 41 #include "ServerDefs.h" 42 43 44 using std::nothrow; 45 46 47 MidiServerApp::MidiServerApp(status_t& error) 48 : 49 BServer(MIDI_SERVER_SIGNATURE, true, &error) 50 { 51 TRACE(("Running Haiku MIDI server")) 52 53 fNextID = 1; 54 fDeviceWatcher = new(std::nothrow) DeviceWatcher(); 55 if (fDeviceWatcher != NULL) 56 fDeviceWatcher->Run(); 57 } 58 59 60 MidiServerApp::~MidiServerApp() 61 { 62 if (fDeviceWatcher && fDeviceWatcher->Lock()) 63 fDeviceWatcher->Quit(); 64 65 for (int32 t = 0; t < _CountApps(); ++t) { 66 delete _AppAt(t); 67 } 68 69 for (int32 t = 0; t < _CountEndpoints(); ++t) { 70 delete _EndpointAt(t); 71 } 72 } 73 74 75 void 76 MidiServerApp::AboutRequested() 77 { 78 BAlert* alert = new BAlert(0, 79 "Haiku midi_server 1.0.0 alpha\n\n" 80 "notes disguised as bytes\n" 81 "propagating to endpoints,\n" 82 "an aural delight", 83 "OK", 0, 0, B_WIDTH_AS_USUAL, 84 B_INFO_ALERT); 85 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 86 alert->Go(); 87 } 88 89 90 void 91 MidiServerApp::MessageReceived(BMessage* msg) 92 { 93 #ifdef DEBUG 94 printf("IN "); msg->PrintToStream(); 95 #endif 96 97 switch (msg->what) { 98 case MSG_REGISTER_APP: 99 _OnRegisterApp(msg); 100 break; 101 case MSG_CREATE_ENDPOINT: 102 _OnCreateEndpoint(msg); 103 break; 104 case MSG_DELETE_ENDPOINT: 105 _OnDeleteEndpoint(msg); 106 break; 107 case MSG_PURGE_ENDPOINT: 108 _OnPurgeEndpoint(msg); 109 break; 110 case MSG_CHANGE_ENDPOINT: 111 _OnChangeEndpoint(msg); 112 break; 113 case MSG_CONNECT_ENDPOINTS: 114 _OnConnectDisconnect(msg); 115 break; 116 case MSG_DISCONNECT_ENDPOINTS: 117 _OnConnectDisconnect(msg); 118 break; 119 120 default: 121 super::MessageReceived(msg); 122 break; 123 } 124 } 125 126 127 void 128 MidiServerApp::_OnRegisterApp(BMessage* msg) 129 { 130 TRACE(("MidiServerApp::_OnRegisterApp")) 131 132 // We only send the "app registered" message upon success, 133 // so if anything goes wrong here, we do not let the app 134 // know about it, and we consider it unregistered. (Most 135 // likely, the app is dead. If not, it freezes forever 136 // in anticipation of a message that will never arrive.) 137 138 app_t* app = new app_t; 139 140 if (msg->FindMessenger("midi:messenger", &app->messenger) == B_OK 141 && _SendAllEndpoints(app) 142 && _SendAllConnections(app)) { 143 BMessage reply; 144 reply.what = MSG_APP_REGISTERED; 145 146 if (_SendNotification(app, &reply)) { 147 fApps.AddItem(app); 148 #ifdef DEBUG 149 _DumpApps(); 150 #endif 151 return; 152 } 153 } 154 155 delete app; 156 } 157 158 159 void 160 MidiServerApp::_OnCreateEndpoint(BMessage* msg) 161 { 162 TRACE(("MidiServerApp::_OnCreateEndpoint")) 163 164 status_t status; 165 endpoint_t* endpoint = new endpoint_t; 166 167 endpoint->app = _WhichApp(msg); 168 if (endpoint->app == NULL) { 169 status = B_ERROR; 170 } else { 171 status = B_BAD_VALUE; 172 173 if (msg->FindBool("midi:consumer", &endpoint->consumer) == B_OK 174 && msg->FindBool("midi:registered", &endpoint->registered) == B_OK 175 && msg->FindString("midi:name", &endpoint->name) == B_OK 176 && msg->FindMessage("midi:properties", &endpoint->properties) 177 == B_OK) { 178 if (endpoint->consumer) { 179 if (msg->FindInt32("midi:port", &endpoint->port) == B_OK 180 && msg->FindInt64("midi:latency", &endpoint->latency) 181 == B_OK) 182 status = B_OK; 183 } else 184 status = B_OK; 185 } 186 } 187 188 BMessage reply; 189 190 if (status == B_OK) { 191 endpoint->id = fNextID++; 192 reply.AddInt32("midi:id", endpoint->id); 193 } 194 195 reply.AddInt32("midi:result", status); 196 197 if (_SendReply(endpoint->app, msg, &reply) && status == B_OK) 198 _AddEndpoint(msg, endpoint); 199 else 200 delete endpoint; 201 } 202 203 204 void 205 MidiServerApp::_OnDeleteEndpoint(BMessage* msg) 206 { 207 TRACE(("MidiServerApp::_OnDeleteEndpoint")) 208 209 // Clients send the "delete endpoint" message from 210 // the BMidiEndpoint destructor, so there is no point 211 // sending a reply, because the endpoint object will 212 // be destroyed no matter what. 213 214 app_t* app = _WhichApp(msg); 215 if (app != NULL) { 216 endpoint_t* endpoint = _WhichEndpoint(msg, app); 217 if (endpoint != NULL) 218 _RemoveEndpoint(app, endpoint); 219 } 220 } 221 222 223 void 224 MidiServerApp::_OnPurgeEndpoint(BMessage* msg) 225 { 226 TRACE(("MidiServerApp::_OnPurgeEndpoint")) 227 228 // This performs the same task as OnDeleteEndpoint(), 229 // except that this message was send by the midi_server 230 // itself, so we don't check that the app that made the 231 // request really is the owner of the endpoint. (But we 232 // _do_ check that the message came from the server.) 233 234 if (!msg->IsSourceRemote()) { 235 int32 id; 236 if (msg->FindInt32("midi:id", &id) == B_OK) { 237 endpoint_t* endpoint = _FindEndpoint(id); 238 if (endpoint != NULL) 239 _RemoveEndpoint(NULL, endpoint); 240 } 241 } 242 } 243 244 245 void 246 MidiServerApp::_OnChangeEndpoint(BMessage* msg) 247 { 248 TRACE(("MidiServerApp::_OnChangeEndpoint")) 249 250 endpoint_t* endpoint = NULL; 251 status_t status; 252 253 app_t* app = _WhichApp(msg); 254 if (app == NULL) 255 status = B_ERROR; 256 else { 257 endpoint = _WhichEndpoint(msg, app); 258 if (endpoint == NULL) 259 status = B_BAD_VALUE; 260 else 261 status = B_OK; 262 } 263 264 BMessage reply; 265 reply.AddInt32("midi:result", status); 266 267 if (_SendReply(app, msg, &reply) && status == B_OK) { 268 TRACE(("Endpoint %" B_PRId32 " (%p) changed", endpoint->id, endpoint)) 269 270 BMessage notify; 271 notify.what = MSG_ENDPOINT_CHANGED; 272 notify.AddInt32("midi:id", endpoint->id); 273 274 bool registered; 275 if (msg->FindBool("midi:registered", ®istered) == B_OK) { 276 notify.AddBool("midi:registered", registered); 277 endpoint->registered = registered; 278 } 279 280 BString name; 281 if (msg->FindString("midi:name", &name) == B_OK) { 282 notify.AddString("midi:name", name); 283 endpoint->name = name; 284 } 285 286 BMessage properties; 287 if (msg->FindMessage("midi:properties", &properties) == B_OK) { 288 notify.AddMessage("midi:properties", &properties); 289 endpoint->properties = properties; 290 } 291 292 bigtime_t latency; 293 if (msg->FindInt64("midi:latency", &latency) == B_OK) { 294 notify.AddInt64("midi:latency", latency); 295 endpoint->latency = latency; 296 } 297 298 _NotifyAll(¬ify, app); 299 300 #ifdef DEBUG 301 _DumpEndpoints(); 302 #endif 303 } 304 } 305 306 307 void 308 MidiServerApp::_OnConnectDisconnect(BMessage* msg) 309 { 310 TRACE(("MidiServerApp::_OnConnectDisconnect")) 311 312 bool mustConnect = msg->what == MSG_CONNECT_ENDPOINTS; 313 314 status_t status; 315 endpoint_t* producer = NULL; 316 endpoint_t* consumer = NULL; 317 318 app_t* app = _WhichApp(msg); 319 if (app == NULL) 320 status = B_ERROR; 321 else { 322 status = B_BAD_VALUE; 323 324 int32 producerID; 325 int32 consumerID; 326 if (msg->FindInt32("midi:producer", &producerID) == B_OK 327 && msg->FindInt32("midi:consumer", &consumerID) == B_OK) { 328 producer = _FindEndpoint(producerID); 329 consumer = _FindEndpoint(consumerID); 330 331 if (producer != NULL && !producer->consumer) { 332 if (consumer != NULL && consumer->consumer) { 333 // It is an error to connect two endpoints that 334 // are already connected, or to disconnect two 335 // endpoints that are not connected at all. 336 337 if (mustConnect == producer->connections.HasItem(consumer)) 338 status = B_ERROR; 339 else 340 status = B_OK; 341 } 342 } 343 } 344 } 345 346 BMessage reply; 347 reply.AddInt32("midi:result", status); 348 349 if (_SendReply(app, msg, &reply) && status == B_OK) { 350 if (mustConnect) { 351 TRACE(("Connection made: %" B_PRId32 " ---> %" B_PRId32, 352 producer->id, consumer->id)) 353 354 producer->connections.AddItem(consumer); 355 } else { 356 TRACE(("Connection broken: %" B_PRId32 " -X-> %" B_PRId32, 357 producer->id, consumer->id)) 358 359 producer->connections.RemoveItem(consumer); 360 } 361 362 BMessage notify; 363 _MakeConnectedNotification(¬ify, producer, consumer, mustConnect); 364 _NotifyAll(¬ify, app); 365 366 #ifdef DEBUG 367 _DumpEndpoints(); 368 #endif 369 } 370 } 371 372 373 /*! Sends an app MSG_ENDPOINT_CREATED notifications for 374 all current endpoints. Used when the app registers. 375 */ 376 bool 377 MidiServerApp::_SendAllEndpoints(app_t* app) 378 { 379 ASSERT(app != NULL) 380 381 BMessage notify; 382 383 for (int32 t = 0; t < _CountEndpoints(); ++t) { 384 endpoint_t* endpoint = _EndpointAt(t); 385 386 _MakeCreatedNotification(¬ify, endpoint); 387 388 if (!_SendNotification(app, ¬ify)) 389 return false; 390 } 391 392 return true; 393 } 394 395 396 /*! Sends an app MSG_ENDPOINTS_CONNECTED notifications for 397 all current connections. Used when the app registers. 398 */ 399 bool 400 MidiServerApp::_SendAllConnections(app_t* app) 401 { 402 ASSERT(app != NULL) 403 404 BMessage notify; 405 406 for (int32 t = 0; t < _CountEndpoints(); ++t) { 407 endpoint_t* producer = _EndpointAt(t); 408 if (!producer->consumer) { 409 for (int32 k = 0; k < _CountConnections(producer); ++k) { 410 endpoint_t* consumer = _ConnectionAt(producer, k); 411 412 _MakeConnectedNotification(¬ify, producer, consumer, true); 413 414 if (!_SendNotification(app, ¬ify)) 415 return false; 416 } 417 } 418 } 419 420 return true; 421 } 422 423 424 /*! Adds the specified endpoint to the roster, and notifies 425 all other applications about this event. 426 */ 427 void 428 MidiServerApp::_AddEndpoint(BMessage* msg, endpoint_t* endpoint) 429 { 430 ASSERT(msg != NULL) 431 ASSERT(endpoint != NULL) 432 ASSERT(!fEndpoints.HasItem(endpoint)) 433 434 TRACE(("Endpoint %" B_PRId32 " (%p) added", endpoint->id, endpoint)) 435 436 fEndpoints.AddItem(endpoint); 437 438 BMessage notify; 439 _MakeCreatedNotification(¬ify, endpoint); 440 _NotifyAll(¬ify, endpoint->app); 441 442 #ifdef DEBUG 443 _DumpEndpoints(); 444 #endif 445 } 446 447 448 /*! Removes an endpoint from the roster, and notifies all 449 other apps about this event. "app" is the application 450 that the endpoint belongs to; if it is NULL, the app 451 no longer exists and we're purging the endpoint. 452 */ 453 void 454 MidiServerApp::_RemoveEndpoint(app_t* app, endpoint_t* endpoint) 455 { 456 ASSERT(endpoint != NULL) 457 ASSERT(fEndpoints.HasItem(endpoint)) 458 459 TRACE(("Endpoint %" B_PRId32 " (%p) removed", endpoint->id, endpoint)) 460 461 fEndpoints.RemoveItem(endpoint); 462 463 if (endpoint->consumer) 464 _DisconnectDeadConsumer(endpoint); 465 466 BMessage notify; 467 notify.what = MSG_ENDPOINT_DELETED; 468 notify.AddInt32("midi:id", endpoint->id); 469 _NotifyAll(¬ify, app); 470 471 delete endpoint; 472 473 #ifdef DEBUG 474 _DumpEndpoints(); 475 #endif 476 } 477 478 479 /*! Removes a consumer from the list of connections of 480 all the producers it is connected to, just before 481 we remove it from the roster. 482 */ 483 void 484 MidiServerApp::_DisconnectDeadConsumer(endpoint_t* consumer) 485 { 486 ASSERT(consumer != NULL) 487 ASSERT(consumer->consumer) 488 489 for (int32 t = 0; t < _CountEndpoints(); ++t) { 490 endpoint_t* producer = _EndpointAt(t); 491 if (!producer->consumer) 492 producer->connections.RemoveItem(consumer); 493 } 494 } 495 496 497 //! Fills up a MSG_ENDPOINT_CREATED message. 498 void 499 MidiServerApp::_MakeCreatedNotification(BMessage* msg, endpoint_t* endpoint) 500 { 501 ASSERT(msg != NULL) 502 ASSERT(endpoint != NULL) 503 504 msg->MakeEmpty(); 505 msg->what = MSG_ENDPOINT_CREATED; 506 msg->AddInt32("midi:id", endpoint->id); 507 msg->AddBool("midi:consumer", endpoint->consumer); 508 msg->AddBool("midi:registered", endpoint->registered); 509 msg->AddString("midi:name", endpoint->name); 510 msg->AddMessage("midi:properties", &endpoint->properties); 511 512 if (endpoint->consumer) { 513 msg->AddInt32("midi:port", endpoint->port); 514 msg->AddInt64("midi:latency", endpoint->latency); 515 } 516 } 517 518 519 //! Fills up a MSG_ENDPOINTS_(DIS)CONNECTED message. 520 void 521 MidiServerApp::_MakeConnectedNotification(BMessage* msg, endpoint_t* producer, 522 endpoint_t* consumer, bool mustConnect) 523 { 524 ASSERT(msg != NULL) 525 ASSERT(producer != NULL) 526 ASSERT(consumer != NULL) 527 ASSERT(!producer->consumer) 528 ASSERT(consumer->consumer) 529 530 msg->MakeEmpty(); 531 532 if (mustConnect) 533 msg->what = MSG_ENDPOINTS_CONNECTED; 534 else 535 msg->what = MSG_ENDPOINTS_DISCONNECTED; 536 537 msg->AddInt32("midi:producer", producer->id); 538 msg->AddInt32("midi:consumer", consumer->id); 539 } 540 541 542 /*! Figures out which application a message came from. 543 Returns NULL if the application is not registered. 544 */ 545 app_t* 546 MidiServerApp::_WhichApp(BMessage* msg) 547 { 548 ASSERT(msg != NULL) 549 550 BMessenger retadr = msg->ReturnAddress(); 551 552 for (int32 t = 0; t < _CountApps(); ++t) { 553 app_t* app = _AppAt(t); 554 if (app->messenger.Team() == retadr.Team()) 555 return app; 556 } 557 558 TRACE(("Application %" B_PRId32 " is not registered", retadr.Team())) 559 560 return NULL; 561 } 562 563 564 /*! Looks at the "midi:id" field from a message, and returns 565 the endpoint object that corresponds to that ID. It also 566 checks whether the application specified by "app" really 567 owns the endpoint. Returns NULL on error. 568 */ 569 endpoint_t* 570 MidiServerApp::_WhichEndpoint(BMessage* msg, app_t* app) 571 { 572 ASSERT(msg != NULL) 573 ASSERT(app != NULL) 574 575 int32 id; 576 if (msg->FindInt32("midi:id", &id) == B_OK) { 577 endpoint_t* endpoint = _FindEndpoint(id); 578 if (endpoint != NULL && endpoint->app == app) 579 return endpoint; 580 } 581 582 TRACE(("Endpoint not found or wrong app")) 583 return NULL; 584 } 585 586 587 /*! Returns the endpoint with the specified ID, or 588 \c NULL if no such endpoint exists on the roster. 589 */ 590 endpoint_t* 591 MidiServerApp::_FindEndpoint(int32 id) 592 { 593 if (id > 0) { 594 for (int32 t = 0; t < _CountEndpoints(); ++t) { 595 endpoint_t* endpoint = _EndpointAt(t); 596 if (endpoint->id == id) 597 return endpoint; 598 } 599 } 600 601 TRACE(("Endpoint %" B_PRId32 " not found", id)) 602 return NULL; 603 } 604 605 606 /*! Sends notification messages to all registered apps, 607 except to the application that triggered the event. 608 The "except" app is allowed to be NULL. 609 */ 610 void 611 MidiServerApp::_NotifyAll(BMessage* msg, app_t* except) 612 { 613 ASSERT(msg != NULL) 614 615 for (int32 t = _CountApps() - 1; t >= 0; --t) { 616 app_t* app = _AppAt(t); 617 if (app != except && !_SendNotification(app, msg)) { 618 delete (app_t*)fApps.RemoveItem(t); 619 #ifdef DEBUG 620 _DumpApps(); 621 #endif 622 } 623 } 624 } 625 626 627 /*! Sends a notification message to an application, which is 628 not necessarily registered yet. Applications never reply 629 to such notification messages. 630 */ 631 bool 632 MidiServerApp::_SendNotification(app_t* app, BMessage* msg) 633 { 634 ASSERT(app != NULL) 635 ASSERT(msg != NULL) 636 637 status_t status = app->messenger.SendMessage(msg, (BHandler*) NULL, 638 TIMEOUT); 639 if (status != B_OK) 640 _DeliveryError(app); 641 642 return status == B_OK; 643 } 644 645 646 /*! Sends a reply to a request made by an application. 647 If "app" is NULL, the application is not registered 648 (and the reply should contain an error code). 649 */ 650 bool 651 MidiServerApp::_SendReply(app_t* app, BMessage* msg, BMessage* reply) 652 { 653 ASSERT(msg != NULL) 654 ASSERT(reply != NULL) 655 656 status_t status = msg->SendReply(reply, (BHandler*) NULL, TIMEOUT); 657 if (status != B_OK && app != NULL) { 658 _DeliveryError(app); 659 fApps.RemoveItem(app); 660 delete app; 661 662 #ifdef DEBUG 663 _DumpApps(); 664 #endif 665 } 666 667 return status == B_OK; 668 } 669 670 671 /*! Removes an app and all of its endpoints from the roster 672 if a reply or notification message cannot be delivered. 673 (Waiting for communications to fail is actually our only 674 way to get rid of stale endpoints.) 675 */ 676 void 677 MidiServerApp::_DeliveryError(app_t* app) 678 { 679 ASSERT(app != NULL) 680 681 // We cannot communicate with the app, so we assume it's 682 // dead. We need to remove its endpoints from the roster, 683 // but we cannot do that right away; removing endpoints 684 // triggers a bunch of new notifications and we don't want 685 // those to get in the way of the notifications we are 686 // currently sending out. Instead, we consider the death 687 // of an app as a separate event, and pretend that the 688 // now-dead app sent us delete requests for its endpoints. 689 690 TRACE(("Delivery error; unregistering app (%p)", app)) 691 692 BMessage msg; 693 694 for (int32 t = 0; t < _CountEndpoints(); ++t) { 695 endpoint_t* endpoint = _EndpointAt(t); 696 if (endpoint->app == app) { 697 msg.MakeEmpty(); 698 msg.what = MSG_PURGE_ENDPOINT; 699 msg.AddInt32("midi:id", endpoint->id); 700 701 // It is not safe to post a message to your own 702 // looper's message queue, because you risk a 703 // deadlock if the queue is full. The chance of 704 // that happening is fairly small, but just in 705 // case, we catch it with a timeout. Because this 706 // situation is so unlikely, I decided to simply 707 // forget about the whole "purge" message then. 708 709 if (be_app_messenger.SendMessage(&msg, (BHandler*)NULL, 710 TIMEOUT) != B_OK) { 711 WARN("Could not deliver purge message") 712 } 713 } 714 } 715 } 716 717 718 int32 719 MidiServerApp::_CountApps() 720 { 721 return fApps.CountItems(); 722 } 723 724 725 app_t* 726 MidiServerApp::_AppAt(int32 index) 727 { 728 ASSERT(index >= 0 && index < _CountApps()) 729 730 return (app_t*)fApps.ItemAt(index); 731 } 732 733 734 int32 735 MidiServerApp::_CountEndpoints() 736 { 737 return fEndpoints.CountItems(); 738 } 739 740 741 endpoint_t* 742 MidiServerApp::_EndpointAt(int32 index) 743 { 744 ASSERT(index >= 0 && index < _CountEndpoints()) 745 746 return (endpoint_t*)fEndpoints.ItemAt(index); 747 } 748 749 750 int32 751 MidiServerApp::_CountConnections(endpoint_t* producer) 752 { 753 ASSERT(producer != NULL) 754 ASSERT(!producer->consumer) 755 756 return producer->connections.CountItems(); 757 } 758 759 760 endpoint_t* 761 MidiServerApp::_ConnectionAt(endpoint_t* producer, int32 index) 762 { 763 ASSERT(producer != NULL) 764 ASSERT(!producer->consumer) 765 ASSERT(index >= 0 && index < _CountConnections(producer)) 766 767 return (endpoint_t*)producer->connections.ItemAt(index); 768 } 769 770 771 #ifdef DEBUG 772 void 773 MidiServerApp::_DumpApps() 774 { 775 printf("*** START DumpApps\n"); 776 777 for (int32 t = 0; t < _CountApps(); ++t) { 778 app_t* app = _AppAt(t); 779 780 printf("\tapp %" B_PRId32 " (%p): team %" B_PRId32 "\n", t, app, 781 app->messenger.Team()); 782 } 783 784 printf("*** END DumpApps\n"); 785 } 786 787 788 void 789 MidiServerApp::_DumpEndpoints() 790 { 791 printf("*** START DumpEndpoints\n"); 792 793 for (int32 t = 0; t < _CountEndpoints(); ++t) { 794 endpoint_t* endpoint = _EndpointAt(t); 795 796 printf("\tendpoint %" B_PRId32 " (%p):\n", t, endpoint); 797 printf("\t\tid %" B_PRId32 ", name '%s', %s, %s, app %p\n", 798 endpoint->id, endpoint->name.String(), 799 endpoint->consumer ? "consumer" : "producer", 800 endpoint->registered ? "registered" : "unregistered", 801 endpoint->app); 802 printf("\t\tproperties: "); endpoint->properties.PrintToStream(); 803 804 if (endpoint->consumer) 805 printf("\t\tport %" B_PRId32 ", latency %" B_PRIdBIGTIME "\n", 806 endpoint->port, endpoint->latency); 807 else { 808 printf("\t\tconnections:\n"); 809 for (int32 k = 0; k < _CountConnections(endpoint); ++k) { 810 endpoint_t* consumer = _ConnectionAt(endpoint, k); 811 printf("\t\t\tid %" B_PRId32 " (%p)\n", consumer->id, consumer); 812 } 813 } 814 } 815 816 printf("*** END DumpEndpoints\n"); 817 } 818 #endif // DEBUG 819 820 821 // #pragma mark - 822 823 824 int 825 main() 826 { 827 status_t status; 828 MidiServerApp app(status); 829 830 if (status == B_OK) 831 app.Run(); 832 833 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 834 } 835