1/*
2 * Copyright 2006, Haiku.
3 *
4 * Copyright (c) 2002-2004 Matthijs Hollemans
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 *		Matthijs Hollemans
9 */
10
11#include "debug.h"
12#include <MidiConsumer.h>
13#include <MidiRoster.h>
14#include "MidiRosterLooper.h"
15#include "protocol.h"
16
17using namespace BPrivate;
18
19// The midi_debug_level and midi_dispatcher_priority symbols
20// were exported by Be's libmidi2, and even though they do not
21// appear in the headers, some apps may still be using them.
22// For backwards compatibility's sake, we export those symbols
23// as well, even though we do not use them for anything.
24
25// Not used. For backwards compatibility only.
26int32 midi_debug_level = 0;
27
28// Not used. For backwards compatibility only.
29int32 midi_dispatcher_priority = B_REAL_TIME_PRIORITY;
30
31//------------------------------------------------------------------------------
32
33// The one and only BMidiRoster instance, which is created
34// the first time the client app calls MidiRoster(). It is
35// destroyed by the BMidiRosterKiller when the app quits.
36static BMidiRoster* roster = NULL;
37
38// Destroys the BMidiRoster instance when the app quits.
39namespace BPrivate
40{
41	static struct BMidiRosterKiller
42	{
43		~BMidiRosterKiller()
44		{
45			delete roster;
46		}
47	}
48	midi_roster_killer;
49}
50
51
52BMidiEndpoint*
53BMidiRoster::NextEndpoint(int32* id)
54{
55	BMidiEndpoint* endp = NULL;
56
57	if (id != NULL) {
58		BMidiRosterLooper* looper = MidiRoster()->fLooper;
59		if (looper->Lock()) {
60			endp = looper->NextEndpoint(id);
61			if (endp != NULL) {
62				endp->Acquire();
63			}
64			looper->Unlock();
65		}
66	}
67
68	return endp;
69}
70
71
72BMidiProducer*
73BMidiRoster::NextProducer(int32* id)
74{
75	BMidiEndpoint* endp;
76
77	while ((endp = NextEndpoint(id)) != NULL) {
78		if (endp->IsProducer()) {
79			return (BMidiProducer*) endp;
80		}
81		endp->Release();
82	}
83
84	return NULL;
85}
86
87
88BMidiConsumer*
89BMidiRoster::NextConsumer(int32* id)
90{
91	BMidiEndpoint* endp;
92
93	while ((endp = NextEndpoint(id)) != NULL) {
94		if (endp->IsConsumer()) {
95			return (BMidiConsumer*) endp;
96		}
97		endp->Release();
98	}
99
100	return NULL;
101}
102
103
104BMidiEndpoint*
105BMidiRoster::FindEndpoint(int32 id, bool localOnly)
106{
107	BMidiEndpoint* endp = NULL;
108
109	BMidiRosterLooper* looper = MidiRoster()->fLooper;
110	if (looper->Lock()) {
111		endp = looper->FindEndpoint(id);
112
113		if ((endp != NULL) && endp->IsRemote()) {
114			if (localOnly || !endp->IsRegistered()) {
115				endp = NULL;
116			}
117		}
118
119		if (endp != NULL) {
120			endp->Acquire();
121		}
122
123		looper->Unlock();
124	}
125
126	return endp;
127}
128
129
130BMidiProducer*
131BMidiRoster::FindProducer(int32 id, bool localOnly)
132{
133	BMidiEndpoint* endp = FindEndpoint(id, localOnly);
134
135	if ((endp != NULL) && !endp->IsProducer()) {
136		endp->Release();
137		endp = NULL;
138	}
139
140	return (BMidiProducer*) endp;
141}
142
143
144BMidiConsumer*
145BMidiRoster::FindConsumer(int32 id, bool localOnly)
146{
147	BMidiEndpoint* endp = FindEndpoint(id, localOnly);
148
149	if ((endp != NULL) && !endp->IsConsumer()) {
150		endp->Release();
151		endp = NULL;
152	}
153
154	return (BMidiConsumer*) endp;
155}
156
157
158void
159BMidiRoster::StartWatching(const BMessenger* msngr)
160{
161	if (msngr == NULL) {
162		WARN("StartWatching does not accept a NULL messenger")
163	} else {
164		BMidiRosterLooper* looper = MidiRoster()->fLooper;
165		if (looper->Lock()) {
166			looper->StartWatching(msngr);
167			looper->Unlock();
168		}
169	}
170}
171
172
173void
174BMidiRoster::StopWatching()
175{
176	BMidiRosterLooper* looper = MidiRoster()->fLooper;
177	if (looper->Lock()) {
178		looper->StopWatching();
179		looper->Unlock();
180	}
181}
182
183
184status_t
185BMidiRoster::Register(BMidiEndpoint* endp)
186{
187	if (endp != NULL) {
188		return endp->Register();
189	}
190
191	return B_BAD_VALUE;
192}
193
194
195status_t
196BMidiRoster::Unregister(BMidiEndpoint* endp)
197{
198	if (endp != NULL) {
199		return endp->Unregister();
200	}
201
202	return B_BAD_VALUE;
203}
204
205
206BMidiRoster*
207BMidiRoster::MidiRoster()
208{
209	if (roster == NULL) {
210		new BMidiRoster();
211	}
212
213	return roster;
214}
215
216
217BMidiRoster::BMidiRoster()
218{
219	TRACE(("BMidiRoster::BMidiRoster"))
220
221	// While our constructor is executing, some function may
222	// call MidiRoster() again, which causes an endless loop.
223	// To prevent this, we immediately fill in "roster"; now
224	// subsequent calls to MidiRoster() won't mess up things.
225
226	roster = this;
227
228	fLooper = new BMidiRosterLooper();
229
230	if (!fLooper->Init(this))
231		return;
232
233	BMessage msg;
234	msg.what = MSG_REGISTER_APP;
235	msg.AddMessenger("midi:messenger", BMessenger(fLooper));
236	fServer = new BMessenger(MIDI_SERVER_SIGNATURE);
237
238	if (fServer->SendMessage(&msg, fLooper, TIMEOUT) != B_OK) {
239		WARN("Cannot send request to midi_server");
240		return;
241	}
242
243	// Although unlikely, we may receive the midi_server's
244	// "app registered" reply before we lock the semaphore.
245	// In that case, BMidiRosterLooper's MessageReceived()
246	// will bump the semaphore count, and our acquire_sem()
247	// can grab the semaphore safely (without blocking).
248
249	acquire_sem(fLooper->fInitLock);
250}
251
252
253BMidiRoster::~BMidiRoster()
254{
255	TRACE(("BMidiRoster::~BMidiRoster"))
256
257	if (fLooper != NULL) {
258		fLooper->Lock();
259		fLooper->Quit();
260	}
261	delete fServer;
262}
263
264
265void BMidiRoster::_Reserved1() { }
266void BMidiRoster::_Reserved2() { }
267void BMidiRoster::_Reserved3() { }
268void BMidiRoster::_Reserved4() { }
269void BMidiRoster::_Reserved5() { }
270void BMidiRoster::_Reserved6() { }
271void BMidiRoster::_Reserved7() { }
272void BMidiRoster::_Reserved8() { }
273
274
275void
276BMidiRoster::CreateLocal(BMidiEndpoint* endp)
277{
278	ASSERT(endp != NULL)
279
280	// We are being called from the BMidiLocalConsumer or
281	// BMidiLocalProducer constructor, so there is no need
282	// to lock anything, because at this point there cannot
283	// be multiple threads accessing the endpoint's data.
284
285	BMessage msg;
286	msg.what = MSG_CREATE_ENDPOINT;
287	msg.AddBool("midi:consumer", endp->fIsConsumer);
288	msg.AddBool("midi:registered", endp->fIsRegistered);
289	msg.AddString("midi:name", endp->Name());
290	msg.AddMessage("midi:properties", endp->fProperties);
291
292	if (endp->IsConsumer()) {
293		BMidiConsumer* consumer = (BMidiConsumer*) endp;
294		msg.AddInt32("midi:port", consumer->fPort);
295		msg.AddInt64("midi:latency", consumer->fLatency);
296	}
297
298	BMessage reply;
299	if (SendRequest(&msg, &reply) == B_OK) {
300		status_t res;
301		if (reply.FindInt32("midi:result", &res) == B_OK) {
302			if (res == B_OK) {
303				int32 id;
304				if (reply.FindInt32("midi:id", &id) == B_OK) {
305					endp->fId = id;
306
307					if (fLooper->Lock()) {
308						fLooper->AddEndpoint(endp);
309						fLooper->Unlock();
310					}
311				}
312			}
313		}
314	}
315
316	// There are many things that can go wrong when creating
317	// a new endpoint, but BMidiEndpoint has no InitCheck()
318	// method to check for this. (You can, however, see if the
319	// endpoint's ID is 0 after the constructor returns, or
320	// call IsValid().) In any case, you should still Release()
321	// the endpoint to delete the object. (This is different
322	// from Be's implementation, which bumps the refcount only
323	// when creation succeeded. If you call Release(), you'll
324	// trip an assertion, so you can't delete these endpoints.)
325}
326
327
328void
329BMidiRoster::DeleteLocal(BMidiEndpoint* endp)
330{
331	ASSERT(endp != NULL)
332
333	BMessage msg;
334	msg.what = MSG_DELETE_ENDPOINT;
335	msg.AddInt32("midi:id", endp->ID());
336
337	// Note: this is always called from BMidiLocalConsumer's
338	// or BMidiLocalProducer's destructor, so we don't expect
339	// a reply from the server. If something went wrong, the
340	// object will be destroyed regardless.
341
342	fServer->SendMessage(&msg, (BHandler*) NULL, TIMEOUT);
343
344	// If the endpoint was successfully created, we must remove
345	// it from the list of endpoints. If creation failed, then
346	// we didn't put the endpoint on that list. If the endpoint
347	// was connected to anything, we must also disconnect it.
348
349	if (endp->ID() > 0) {
350		if (fLooper->Lock()) {
351			fLooper->RemoveEndpoint(endp);
352			fLooper->Unlock();
353		}
354	}
355}
356
357
358status_t
359BMidiRoster::SendRequest(BMessage* msg, BMessage* reply)
360{
361	ASSERT(msg != NULL)
362	ASSERT(reply != NULL)
363
364	status_t err = fServer->SendMessage(msg, reply, TIMEOUT, TIMEOUT);
365
366	if (err != B_OK) {
367		WARN("Cannot send request to midi_server");
368	}
369
370	#ifdef DEBUG
371	if (err == B_OK) {
372		printf("REPLY "); reply->PrintToStream();
373	}
374	#endif
375
376	return err;
377}
378
379