headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / midi2 / MidiRosterLooper.cpp
blob186e2faed1bd07d1489d6d81c21535a3f1dcf276
1 /*
2 * Copyright 2006, Haiku.
4 * Copyright (c) 2002-2004 Matthijs Hollemans
5 * Distributed under the terms of the MIT License.
7 * Authors:
8 * Matthijs Hollemans
9 */
11 #include "debug.h"
12 #include <MidiConsumer.h>
13 #include <MidiProducer.h>
14 #include <MidiRoster.h>
15 #include "MidiRosterLooper.h"
16 #include "protocol.h"
18 using namespace BPrivate;
21 BMidiRosterLooper::BMidiRosterLooper()
22 : BLooper("MidiRosterLooper")
24 fInitLock = -1;
25 fRoster = NULL;
26 fWatcher = NULL;
30 BMidiRosterLooper::~BMidiRosterLooper()
32 StopWatching();
34 if (fInitLock >= B_OK) {
35 delete_sem(fInitLock);
38 // At this point, our list may still contain endpoints with a
39 // zero reference count. These objects are proxies for remote
40 // endpoints, so we can safely delete them. If the list also
41 // contains endpoints with a non-zero refcount (which can be
42 // either remote or local), we will output a warning message.
43 // It would have been better to jump into the debugger, but I
44 // did not want to risk breaking any (misbehaving) old apps.
46 for (int32 t = 0; t < CountEndpoints(); ++t) {
47 BMidiEndpoint* endp = EndpointAt(t);
48 if (endp->fRefCount > 0) {
49 fprintf(
50 stderr, "[midi] WARNING: Endpoint %" B_PRId32 " (%p) has "
51 "not been Release()d properly (refcount = %" B_PRId32 ")\n",
52 endp->ID(), endp, endp->fRefCount);
53 } else {
54 delete endp;
60 bool
61 BMidiRosterLooper::Init(BMidiRoster* roster_)
63 ASSERT(roster_ != NULL)
65 fRoster = roster_;
67 // We create a semaphore with a zero count. BMidiRoster's
68 // MidiRoster() method will try to acquire this semaphore,
69 // but blocks because the count is 0. When we receive the
70 // "app registered" message in our MessageReceived() hook,
71 // we release the semaphore and MidiRoster() will unblock.
73 fInitLock = create_sem(0, "InitLock");
75 if (fInitLock < B_OK) {
76 WARN("Could not create semaphore")
77 return false;
80 thread_id threadId = Run();
82 if (threadId < B_OK) {
83 WARN("Could not start looper thread")
84 return false;
87 return true;
91 BMidiEndpoint*
92 BMidiRosterLooper::NextEndpoint(int32* id)
94 ASSERT(id != NULL)
96 for (int32 t = 0; t < CountEndpoints(); ++t) {
97 BMidiEndpoint* endp = EndpointAt(t);
98 if (endp->ID() > *id) {
99 if (endp->IsRemote() && endp->IsRegistered()) {
100 *id = endp->ID();
101 return endp;
106 return NULL;
110 BMidiEndpoint*
111 BMidiRosterLooper::FindEndpoint(int32 id)
113 for (int32 t = 0; t < CountEndpoints(); ++t) {
114 BMidiEndpoint* endp = EndpointAt(t);
115 if (endp->ID() == id) {
116 return endp;
120 return NULL;
124 void
125 BMidiRosterLooper::AddEndpoint(BMidiEndpoint* endp)
127 ASSERT(endp != NULL)
128 ASSERT(!fEndpoints.HasItem(endp))
130 // We store the endpoints sorted by ID, because that
131 // simplifies the implementation of NextEndpoint().
132 // Although the midi_server assigns IDs in ascending
133 // order, we can't assume that the mNEW messages also
134 // are delivered in this order (mostly they will be).
136 int32 t;
137 for (t = CountEndpoints(); t > 0; --t) {
138 BMidiEndpoint* other = EndpointAt(t - 1);
139 if (endp->ID() > other->ID()) {
140 break;
143 fEndpoints.AddItem(endp, t);
145 #ifdef DEBUG
146 DumpEndpoints();
147 #endif
151 void
152 BMidiRosterLooper::RemoveEndpoint(BMidiEndpoint* endp)
154 ASSERT(endp != NULL)
155 ASSERT(fEndpoints.HasItem(endp))
157 fEndpoints.RemoveItem(endp);
159 if (endp->IsConsumer()) {
160 DisconnectDeadConsumer((BMidiConsumer*) endp);
161 } else {
162 DisconnectDeadProducer((BMidiProducer*) endp);
165 #ifdef DEBUG
166 DumpEndpoints();
167 #endif
171 void
172 BMidiRosterLooper::StartWatching(const BMessenger* watcher_)
174 ASSERT(watcher_ != NULL)
176 StopWatching();
177 fWatcher = new BMessenger(*watcher_);
179 AllEndpoints();
180 AllConnections();
184 void
185 BMidiRosterLooper::StopWatching()
187 delete fWatcher;
188 fWatcher = NULL;
192 void
193 BMidiRosterLooper::MessageReceived(BMessage* msg)
195 #ifdef DEBUG
196 printf("IN "); msg->PrintToStream();
197 #endif
199 switch (msg->what) {
200 case MSG_APP_REGISTERED: OnAppRegistered(msg); break;
201 case MSG_ENDPOINT_CREATED: OnEndpointCreated(msg); break;
202 case MSG_ENDPOINT_DELETED: OnEndpointDeleted(msg); break;
203 case MSG_ENDPOINT_CHANGED: OnEndpointChanged(msg); break;
204 case MSG_ENDPOINTS_CONNECTED: OnConnectedDisconnected(msg); break;
205 case MSG_ENDPOINTS_DISCONNECTED: OnConnectedDisconnected(msg); break;
207 default: super::MessageReceived(msg); break;
212 void
213 BMidiRosterLooper::OnAppRegistered(BMessage* msg)
215 release_sem(fInitLock);
219 void
220 BMidiRosterLooper::OnEndpointCreated(BMessage* msg)
222 int32 id;
223 bool isRegistered;
224 BString name;
225 BMessage properties;
226 bool isConsumer;
228 if ((msg->FindInt32("midi:id", &id) == B_OK)
229 && (msg->FindBool("midi:registered", &isRegistered) == B_OK)
230 && (msg->FindString("midi:name", &name) == B_OK)
231 && (msg->FindMessage("midi:properties", &properties) == B_OK)
232 && (msg->FindBool("midi:consumer", &isConsumer) == B_OK)) {
233 if (isConsumer) {
234 int32 port;
235 bigtime_t latency;
237 if ((msg->FindInt32("midi:port", &port) == B_OK)
238 && (msg->FindInt64("midi:latency", &latency) == B_OK)) {
239 BMidiConsumer* cons = new BMidiConsumer();
240 cons->fName = name;
241 cons->fId = id;
242 cons->fIsRegistered = isRegistered;
243 cons->fPort = port;
244 cons->fLatency = latency;
245 *(cons->fProperties) = properties;
246 AddEndpoint(cons);
247 return;
249 } else { // producer
250 BMidiProducer* prod = new BMidiProducer();
251 prod->fName = name;
252 prod->fId = id;
253 prod->fIsRegistered = isRegistered;
254 *(prod->fProperties) = properties;
255 AddEndpoint(prod);
256 return;
260 WARN("Could not create proxy for remote endpoint")
264 void
265 BMidiRosterLooper::OnEndpointDeleted(BMessage* msg)
267 int32 id;
268 if (msg->FindInt32("midi:id", &id) == B_OK) {
269 BMidiEndpoint* endp = FindEndpoint(id);
270 if (endp != NULL) {
271 RemoveEndpoint(endp);
273 // If the client is watching, and the endpoint is
274 // registered remote, we need to let it know that
275 // the endpoint is now unregistered.
277 if (endp->IsRemote() && endp->IsRegistered()) {
278 if (fWatcher != NULL) {
279 BMessage notify;
280 notify.AddInt32("be:op", B_MIDI_UNREGISTERED);
281 ChangeEvent(&notify, endp);
285 // If the proxy object for this endpoint is no
286 // longer being used, we can delete it. However,
287 // if the refcount is not zero, we must defer
288 // destruction until the client Release()'s the
289 // object. We clear the "isRegistered" flag to
290 // let the client know the object is now invalid.
292 if (endp->fRefCount == 0) {
293 delete endp;
294 } else { // still being used
295 endp->fIsRegistered = false;
296 endp->fIsAlive = false;
299 return;
303 WARN("Could not delete proxy for remote endpoint")
307 void
308 BMidiRosterLooper::OnEndpointChanged(BMessage* msg)
310 int32 id;
311 if (msg->FindInt32("midi:id", &id) == B_OK) {
312 BMidiEndpoint* endp = FindEndpoint(id);
313 if ((endp != NULL) && endp->IsRemote()) {
314 ChangeRegistered(msg, endp);
315 ChangeName(msg, endp);
316 ChangeProperties(msg, endp);
317 ChangeLatency(msg, endp);
319 #ifdef DEBUG
320 DumpEndpoints();
321 #endif
323 return;
327 WARN("Could not change endpoint attributes")
331 void
332 BMidiRosterLooper::OnConnectedDisconnected(BMessage* msg)
334 int32 prodId, consId;
335 if ((msg->FindInt32("midi:producer", &prodId) == B_OK)
336 && (msg->FindInt32("midi:consumer", &consId) == B_OK)) {
337 BMidiEndpoint* endp1 = FindEndpoint(prodId);
338 BMidiEndpoint* endp2 = FindEndpoint(consId);
340 if ((endp1 != NULL) && endp1->IsProducer()) {
341 if ((endp2 != NULL) && endp2->IsConsumer()) {
342 BMidiProducer* prod = (BMidiProducer*) endp1;
343 BMidiConsumer* cons = (BMidiConsumer*) endp2;
345 bool mustConnect = (msg->what == MSG_ENDPOINTS_CONNECTED);
347 if (mustConnect) {
348 prod->ConnectionMade(cons);
349 } else {
350 prod->ConnectionBroken(cons);
353 if (fWatcher != NULL) {
354 ConnectionEvent(prod, cons, mustConnect);
357 #ifdef DEBUG
358 DumpEndpoints();
359 #endif
361 return;
366 WARN("Could not connect/disconnect endpoints")
370 void
371 BMidiRosterLooper::ChangeRegistered(BMessage* msg, BMidiEndpoint* endp)
373 ASSERT(msg != NULL)
374 ASSERT(endp != NULL)
376 bool isRegistered;
377 if (msg->FindBool("midi:registered", &isRegistered) == B_OK) {
378 if (endp->fIsRegistered != isRegistered) {
379 endp->fIsRegistered = isRegistered;
381 if (fWatcher != NULL) {
382 BMessage notify;
383 if (isRegistered) {
384 notify.AddInt32("be:op", B_MIDI_REGISTERED);
385 } else {
386 notify.AddInt32("be:op", B_MIDI_UNREGISTERED);
388 ChangeEvent(&notify, endp);
395 void
396 BMidiRosterLooper::ChangeName(BMessage* msg, BMidiEndpoint* endp)
398 ASSERT(msg != NULL)
399 ASSERT(endp != NULL)
401 BString name;
402 if (msg->FindString("midi:name", &name) == B_OK) {
403 if (endp->fName != name) {
404 endp->fName = name;
406 if ((fWatcher != NULL) && endp->IsRegistered()) {
407 BMessage notify;
408 notify.AddInt32("be:op", B_MIDI_CHANGED_NAME);
409 notify.AddString("be:name", name);
410 ChangeEvent(&notify, endp);
417 void
418 BMidiRosterLooper::ChangeProperties(BMessage* msg, BMidiEndpoint* endp)
420 ASSERT(msg != NULL)
421 ASSERT(endp != NULL)
423 BMessage properties;
424 if (msg->FindMessage("midi:properties", &properties) == B_OK) {
425 *(endp->fProperties) = properties;
427 if ((fWatcher != NULL) && endp->IsRegistered()) {
428 BMessage notify;
429 notify.AddInt32("be:op", B_MIDI_CHANGED_PROPERTIES);
430 notify.AddMessage("be:properties", &properties);
431 ChangeEvent(&notify, endp);
437 void
438 BMidiRosterLooper::ChangeLatency(BMessage* msg, BMidiEndpoint* endp)
440 ASSERT(msg != NULL)
441 ASSERT(endp != NULL)
443 bigtime_t latency;
444 if (msg->FindInt64("midi:latency", &latency) == B_OK) {
445 if (endp->IsConsumer()) {
446 BMidiConsumer* cons = (BMidiConsumer*) endp;
447 if (cons->fLatency != latency) {
448 cons->fLatency = latency;
450 if ((fWatcher != NULL) && cons->IsRegistered()) {
451 BMessage notify;
452 notify.AddInt32("be:op", B_MIDI_CHANGED_LATENCY);
453 notify.AddInt64("be:latency", latency);
454 ChangeEvent(&notify, endp);
462 void
463 BMidiRosterLooper::AllEndpoints()
465 BMessage notify;
466 for (int32 t = 0; t < CountEndpoints(); ++t) {
467 BMidiEndpoint* endp = EndpointAt(t);
468 if (endp->IsRemote() && endp->IsRegistered()) {
469 notify.MakeEmpty();
470 notify.AddInt32("be:op", B_MIDI_REGISTERED);
471 ChangeEvent(&notify, endp);
477 void
478 BMidiRosterLooper::AllConnections()
480 for (int32 t = 0; t < CountEndpoints(); ++t) {
481 BMidiEndpoint* endp = EndpointAt(t);
482 if (endp->IsRemote() && endp->IsRegistered()) {
483 if (endp->IsProducer()) {
484 BMidiProducer* prod = (BMidiProducer*) endp;
485 if (prod->LockProducer()) {
486 for (int32 k = 0; k < prod->CountConsumers(); ++k) {
487 ConnectionEvent(prod, prod->ConsumerAt(k), true);
489 prod->UnlockProducer();
497 void
498 BMidiRosterLooper::ChangeEvent(BMessage* msg, BMidiEndpoint* endp)
500 ASSERT(fWatcher != NULL)
501 ASSERT(msg != NULL)
502 ASSERT(endp != NULL)
504 msg->what = B_MIDI_EVENT;
505 msg->AddInt32("be:id", endp->ID());
507 if (endp->IsConsumer()) {
508 msg->AddString("be:type", "consumer");
509 } else {
510 msg->AddString("be:type", "producer");
513 fWatcher->SendMessage(msg);
517 void
518 BMidiRosterLooper::ConnectionEvent(
519 BMidiProducer* prod, BMidiConsumer* cons, bool mustConnect)
521 ASSERT(fWatcher != NULL)
522 ASSERT(prod != NULL)
523 ASSERT(cons != NULL)
525 BMessage notify;
526 notify.what = B_MIDI_EVENT;
527 notify.AddInt32("be:producer", prod->ID());
528 notify.AddInt32("be:consumer", cons->ID());
530 if (mustConnect) {
531 notify.AddInt32("be:op", B_MIDI_CONNECTED);
532 } else {
533 notify.AddInt32("be:op", B_MIDI_DISCONNECTED);
536 fWatcher->SendMessage(&notify);
540 void
541 BMidiRosterLooper::DisconnectDeadConsumer(BMidiConsumer* cons)
543 ASSERT(cons != NULL)
545 // Note: Rather than looping through each producer's list
546 // of connected consumers, we let ConnectionBroken() tell
547 // us whether the consumer really was connected.
549 for (int32 t = 0; t < CountEndpoints(); ++t) {
550 BMidiEndpoint* endp = EndpointAt(t);
551 if (endp->IsProducer()) {
552 BMidiProducer* prod = (BMidiProducer*) endp;
553 if (prod->ConnectionBroken(cons)) {
554 if (cons->IsRemote() && (fWatcher != NULL)) {
555 ConnectionEvent(prod, cons, false);
563 void
564 BMidiRosterLooper::DisconnectDeadProducer(BMidiProducer* prod)
566 ASSERT(prod != NULL)
568 // We don't need to lock or remove the consumers from
569 // the producer's list of connections, because when this
570 // function is called, we're destroying the object.
572 if (prod->IsRemote() && (fWatcher != NULL)) {
573 for (int32 t = 0; t < prod->CountConsumers(); ++t) {
574 ConnectionEvent(prod, prod->ConsumerAt(t), false);
580 int32
581 BMidiRosterLooper::CountEndpoints()
583 return fEndpoints.CountItems();
587 BMidiEndpoint*
588 BMidiRosterLooper::EndpointAt(int32 index)
590 ASSERT(index >= 0 && index < CountEndpoints())
592 return (BMidiEndpoint*) fEndpoints.ItemAt(index);
596 #ifdef DEBUG
597 void
598 BMidiRosterLooper::DumpEndpoints()
600 if (Lock()) {
601 printf("*** START DumpEndpoints\n");
603 for (int32 t = 0; t < CountEndpoints(); ++t) {
604 BMidiEndpoint* endp = EndpointAt(t);
606 printf("\tendpoint %" B_PRId32 " (%p):\n", t, endp);
608 printf(
609 "\t\tid %" B_PRId32 ", name '%s', %s, %s, %s, %s, refcount %"
610 B_PRId32 "\n", endp->ID(), endp->Name(),
611 endp->IsConsumer() ? "consumer" : "producer",
612 endp->IsRegistered() ? "registered" : "unregistered",
613 endp->IsLocal() ? "local" : "remote",
614 endp->IsValid() ? "valid" : "invalid", endp->fRefCount);
616 printf("\t\tproperties: ");
617 endp->fProperties->PrintToStream();
619 if (endp->IsConsumer()) {
620 BMidiConsumer* cons = (BMidiConsumer*) endp;
621 printf("\t\tport %" B_PRId32 ", latency %" B_PRIdBIGTIME "\n",
622 cons->fPort, cons->fLatency);
623 } else {
624 BMidiProducer* prod = (BMidiProducer*) endp;
625 if (prod->LockProducer()) {
626 printf("\t\tconnections:\n");
627 for (int32 k = 0; k < prod->CountConsumers(); ++k) {
628 BMidiConsumer* cons = prod->ConsumerAt(k);
629 printf("\t\t\tid %" B_PRId32 " (%p)\n", cons->ID(),
630 cons);
632 prod->UnlockProducer();
637 printf("*** END DumpEndpoints\n");
638 Unlock();
641 #endif