xref: /haiku/src/kits/midi2/MidiEndpoint.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2  * Copyright (c) 2002-2003 Matthijs Hollemans
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "debug.h"
24 #include "MidiEndpoint.h"
25 #include "MidiRoster.h"
26 #include "MidiRosterLooper.h"
27 #include "protocol.h"
28 
29 
30 const char*
31 BMidiEndpoint::Name() const
32 {
33 	const char* str = NULL;
34 
35 	// It seems reasonable to assume that the pointer returned by
36 	// BString::String() can change when the string is modified,
37 	// e.g. to allocate more space. That's why we lock here too.
38 
39 	if (LockLooper()) {
40 		str = name.String();
41 		UnlockLooper();
42 	}
43 
44 	return str;
45 }
46 
47 
48 void
49 BMidiEndpoint::SetName(const char* newName)
50 {
51 	if (newName == NULL) {
52 		WARN("SetName() does not accept a NULL name");
53 		return;
54 	}
55 	if (IsRemote()) {
56 		WARN("SetName() is not allowed on remote endpoints");
57 		return;
58 	}
59 	if (!IsValid())
60 		return;
61 
62 	if (name != newName) {
63 		BMessage msg;
64 		msg.AddString("midi:name", newName);
65 
66 		if (SendChangeRequest(&msg) == B_OK) {
67 			if (LockLooper()) {
68 				name.SetTo(newName);
69 				UnlockLooper();
70 			}
71 		}
72 	}
73 }
74 
75 
76 int32
77 BMidiEndpoint::ID() const
78 {
79 	return id;
80 }
81 
82 
83 bool
84 BMidiEndpoint::IsProducer() const
85 {
86 	return !isConsumer;
87 }
88 
89 
90 bool
91 BMidiEndpoint::IsConsumer() const
92 {
93 	return isConsumer;
94 }
95 
96 
97 bool
98 BMidiEndpoint::IsRemote() const
99 {
100 	return !isLocal;
101 }
102 
103 
104 bool
105 BMidiEndpoint::IsLocal() const
106 {
107 	return isLocal;
108 }
109 
110 
111 bool
112 BMidiEndpoint::IsPersistent() const
113 {
114 	return false;
115 }
116 
117 
118 bool
119 BMidiEndpoint::IsValid() const
120 {
121 	if (IsLocal())
122 		return (ID() > 0);
123 
124 	// remote endpoint
125 	return IsRegistered();
126 }
127 
128 
129 status_t
130 BMidiEndpoint::Release()
131 {
132 	int32 old = atomic_add(&refCount, -1);
133 
134 	TRACE(("BMidiEndpoint::Release refCount is now %ld", old - 1))
135 
136 	if (old == 1) {
137 		// If the reference count of a local endpoint drops to zero,
138 		// we must delete it. The destructor of BMidiLocalXXX calls
139 		// BMidiRoster::DeleteLocal(), which does all the hard work.
140 		// If we are a proxy for a remote endpoint, we must only be
141 		// deleted if that remote endpoint no longer exists.
142 
143 		if (IsLocal() || !isAlive)
144 			delete this;
145 	} else if (old <= 0) {
146 		debugger("too many calls to Release()");
147 	}
148 
149 	return B_OK;
150 }
151 
152 
153 status_t
154 BMidiEndpoint::Acquire()
155 {
156 #ifdef DEBUG
157 	int32 old =
158 #endif
159 	atomic_add(&refCount, 1);
160 
161 	TRACE(("BMidiEndpoint::Acquire refCount is now %ld", old + 1))
162 
163 	return B_OK;
164 }
165 
166 
167 status_t
168 BMidiEndpoint::SetProperties(const BMessage* properties_)
169 {
170 	if (properties_ == NULL) {
171 		WARN("SetProperties() does not accept a NULL message")
172 		return B_BAD_VALUE;
173 	} else if (IsRemote()) {
174 		WARN("SetProperties() is not allowed on remote endpoints");
175 		return B_ERROR;
176 	} else if (!IsValid())  {
177 		return B_ERROR;
178 	} else {
179 		BMessage msg;
180 		msg.AddMessage("midi:properties", properties_);
181 
182 		status_t err = SendChangeRequest(&msg);
183 		if (err == B_OK) {
184 			if (LockLooper()) {
185 				*properties = *properties_;
186 				UnlockLooper();
187 			}
188 		}
189 
190 		return err;
191 	}
192 }
193 
194 
195 status_t
196 BMidiEndpoint::GetProperties(BMessage* _properties) const
197 {
198 	if (_properties == NULL) {
199 		WARN("GetProperties() does not accept NULL properties")
200 		return B_BAD_VALUE;
201 	}
202 
203 	if (LockLooper()) {
204 		*_properties = *properties;
205 		UnlockLooper();
206 	}
207 
208 	return B_OK;
209 }
210 
211 
212 status_t
213 BMidiEndpoint::Register(void)
214 {
215 	if (IsRemote()) {
216 		WARN("You cannot Register() remote endpoints");
217 		return B_ERROR;
218 	}
219 	if (IsRegistered()) {
220 		WARN("This endpoint is already registered");
221 		return B_OK;
222 	}
223 	if (!IsValid())
224 		return B_ERROR;
225 
226 	return SendRegisterRequest(true);
227 }
228 
229 
230 status_t
231 BMidiEndpoint::Unregister(void)
232 {
233 	if (IsRemote()) {
234 		WARN("You cannot Unregister() remote endpoints");
235 		return B_ERROR;
236 	}
237 	if (!IsRegistered()) {
238 		WARN("This endpoint is already unregistered");
239 		return B_OK;
240 	}
241 	if (!IsValid())
242 		return B_ERROR;
243 
244 	return SendRegisterRequest(false);
245 }
246 
247 
248 BMidiEndpoint::BMidiEndpoint(const char* name_)
249 {
250 	TRACE(("BMidiEndpoint::BMidiEndpoint"))
251 
252 	if (name_ != NULL)
253 		name.SetTo(name_);
254 
255 	id           = 0;
256 	refCount     = 0;
257 	isLocal      = false;
258 	isRegistered = false;
259 	isAlive      = true;
260 
261 	properties = new BMessage;
262 }
263 
264 
265 BMidiEndpoint::~BMidiEndpoint()
266 {
267 	TRACE(("BMidiEndpoint::~BMidiEndpoint (%p)", this))
268 
269 	if (refCount > 0) {
270 		debugger(
271 			"you should use Release() to dispose of endpoints; "
272 			"do not \"delete\" them or allocate them on the stack!");
273 	}
274 
275 	delete properties;
276 }
277 
278 //------------------------------------------------------------------------------
279 
280 void BMidiEndpoint::_Reserved1() { }
281 void BMidiEndpoint::_Reserved2() { }
282 void BMidiEndpoint::_Reserved3() { }
283 void BMidiEndpoint::_Reserved4() { }
284 void BMidiEndpoint::_Reserved5() { }
285 void BMidiEndpoint::_Reserved6() { }
286 void BMidiEndpoint::_Reserved7() { }
287 void BMidiEndpoint::_Reserved8() { }
288 
289 //------------------------------------------------------------------------------
290 
291 status_t
292 BMidiEndpoint::SendRegisterRequest(bool registered)
293 {
294 	BMessage msg;
295 	msg.AddBool("midi:registered", registered);
296 
297 	status_t err = SendChangeRequest(&msg);
298 	if (err == B_OK)  {
299 		if (LockLooper()) {
300 			isRegistered = registered;
301 			UnlockLooper();
302 		}
303 	}
304 
305 	return err;
306 }
307 
308 
309 status_t
310 BMidiEndpoint::SendChangeRequest(BMessage* msg)
311 {
312 	ASSERT(msg != NULL)
313 
314 	msg->what = MSG_CHANGE_ENDPOINT;
315 	msg->AddInt32("midi:id", ID());
316 
317 	BMessage reply;
318 	status_t err = BMidiRoster::MidiRoster()->SendRequest(msg, &reply);
319 	if (err != B_OK)
320 		return err;
321 
322 	status_t res;
323 	if (reply.FindInt32("midi:result", &res) == B_OK)
324 		return res;
325 
326 	return B_ERROR;
327 }
328 
329 
330 bool
331 BMidiEndpoint::IsRegistered() const
332 {
333 	// No need to protect this with a lock, because reading
334 	// and writing a bool is always an atomic operation.
335 
336 	return isRegistered;
337 }
338 
339 
340 bool
341 BMidiEndpoint::LockLooper() const
342 {
343 	return BMidiRoster::MidiRoster()->looper->Lock();
344 }
345 
346 
347 void
348 BMidiEndpoint::UnlockLooper() const
349 {
350 	BMidiRoster::MidiRoster()->looper->Unlock();
351 }
352 
353