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