xref: /haiku/src/kits/media/Controllable.cpp (revision 5e7964b0a929555415798dea3373db9ac4611caa)
1 /*
2  * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT license.
4  */
5 
6 /*
7  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files or portions
11  * thereof (the "Software"), to deal in the Software without restriction,
12  * including without limitation the rights to use, copy, modify, merge,
13  * publish, distribute, sublicense, and/or sell copies of the Software,
14  * and to permit persons to whom the Software is furnished to do so, subject
15  * to the following conditions:
16  *
17  *  * Redistributions of source code must retain the above copyright notice,
18  *    this list of conditions and the following disclaimer.
19  *
20  *  * Redistributions in binary form must reproduce the above copyright notice
21  *    in the  binary, as well as this list of conditions and the following
22  *    disclaimer in the documentation and/or other materials provided with
23  *    the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31  * THE SOFTWARE.
32  *
33  */
34 
35 #include <Controllable.h>
36 
37 #include <string.h>
38 
39 #include <OS.h>
40 #include <ParameterWeb.h>
41 #include <Roster.h>
42 #include <TimeSource.h>
43 
44 #include <debug.h>
45 #include <DataExchange.h>
46 #include <Notifications.h>
47 
48 
49 namespace BPrivate { namespace media {
50 
51 /*!	A helper class for the communication with the media server that
52 	takes care of large buffers that need an area.
53 */
54 class ReceiveTransfer {
55 public:
56 	ReceiveTransfer(const area_request_data& request, const void* smallBuffer)
57 	{
58 		if (request.area == -1 && smallBuffer != NULL) {
59 			// small data transfer uses buffer in reply
60 			fArea = -1;
61 			fData = const_cast<void*>(smallBuffer);
62 				// The caller is actually responsible to enforce the const;
63 				// we don't touch the data.
64 		} else {
65 			// large data transfer, clone area
66 			fArea = clone_area("get parameter data clone", &fData,
67 				B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
68 			if (fArea < B_OK) {
69 				ERROR("BControllabe: cloning area failed: %s\n",
70 					strerror(fArea));
71 				fData = NULL;
72 			}
73 		}
74 	}
75 
76 	~ReceiveTransfer()
77 	{
78 		if (fArea >= B_OK)
79 			delete_area(fArea);
80 	}
81 
82 	status_t InitCheck() const
83 	{
84 		return fData != NULL ? B_OK : fArea;
85 	}
86 
87 	void* Data() const
88 	{
89 		return fData;
90 	}
91 
92 private:
93 	area_id				fArea;
94 	void*				fData;
95 };
96 
97 } // namespace media
98 } // namespace BPrivate
99 
100 using BPrivate::media::ReceiveTransfer;
101 
102 
103 //	#pragma mark - protected
104 
105 
106 BControllable::~BControllable()
107 {
108 	CALLED();
109 	if (fSem > 0)
110 		delete_sem(fSem);
111 
112 	delete fWeb;
113 }
114 
115 
116 //	#pragma mark - public
117 
118 
119 BParameterWeb*
120 BControllable::Web()
121 {
122 	CALLED();
123 	return fWeb;
124 }
125 
126 
127 bool
128 BControllable::LockParameterWeb()
129 {
130 	CALLED();
131 	if (fSem <= 0)
132 		return false;
133 
134 	if (atomic_add(&fBen, 1) > 0) {
135 		status_t status;
136 		do {
137 			status = acquire_sem(fSem);
138 		} while (status == B_INTERRUPTED);
139 
140 		return status == B_OK;
141 	}
142 
143 	return true;
144 }
145 
146 
147 void
148 BControllable::UnlockParameterWeb()
149 {
150 	CALLED();
151 	if (fSem <= 0)
152 		return;
153 
154 	if (atomic_add(&fBen, -1) > 1)
155 		release_sem(fSem);
156 }
157 
158 
159 //	#pragma mark - protected
160 
161 
162 BControllable::BControllable()
163 	: BMediaNode("this one is never called"),
164 	fWeb(NULL),
165 	fSem(create_sem(0, "BControllable lock")),
166 	fBen(0)
167 {
168 	CALLED();
169 
170 	AddNodeKind(B_CONTROLLABLE);
171 }
172 
173 
174 status_t
175 BControllable::SetParameterWeb(BParameterWeb* web)
176 {
177 	CALLED();
178 
179 	LockParameterWeb();
180 	BParameterWeb* old = fWeb;
181 	fWeb = web;
182 
183 	if (fWeb != NULL) {
184 		// initialize BParameterWeb member variable
185 		fWeb->fNode = Node();
186 	}
187 
188 	UnlockParameterWeb();
189 
190 	if (old != web && web != NULL)
191 		BPrivate::media::notifications::WebChanged(Node());
192 	delete old;
193 	return B_OK;
194 }
195 
196 
197 status_t
198 BControllable::HandleMessage(int32 message, const void* data, size_t size)
199 {
200 	PRINT(4, "BControllable::HandleMessage %#lx, node %ld\n", message, ID());
201 
202 	switch (message) {
203 		case CONTROLLABLE_GET_PARAMETER_DATA:
204 		{
205 			const controllable_get_parameter_data_request& request
206 				= *static_cast<const controllable_get_parameter_data_request*>(
207 					data);
208 			controllable_get_parameter_data_reply reply;
209 
210 			ReceiveTransfer transfer(request, reply.raw_data);
211 			if (transfer.InitCheck() != B_OK) {
212 				request.SendReply(transfer.InitCheck(), &reply, sizeof(reply));
213 				return B_OK;
214 			}
215 
216 			reply.size = request.request_size;
217 			status_t status = GetParameterValue(request.parameter_id,
218 				&reply.last_change, transfer.Data(), &reply.size);
219 
220 			request.SendReply(status, &reply, sizeof(reply));
221 			return B_OK;
222 		}
223 
224 		case CONTROLLABLE_SET_PARAMETER_DATA:
225 		{
226 			const controllable_set_parameter_data_request& request
227 				= *static_cast<const controllable_set_parameter_data_request*>(
228 					data);
229 			controllable_set_parameter_data_reply reply;
230 
231 			ReceiveTransfer transfer(request, request.raw_data);
232 			if (transfer.InitCheck() != B_OK) {
233 				request.SendReply(transfer.InitCheck(), &reply, sizeof(reply));
234 				return B_OK;
235 			}
236 
237 			// NOTE: This is not very fair, but the alternative
238 			// would have been to mess with friends classes and
239 			// member variables.
240 			bigtime_t perfTime = 0;
241 			if (request.when == -1)
242 				perfTime = TimeSource()->Now();
243 			else
244 				perfTime = request.when;
245 
246 			SetParameterValue(request.parameter_id, perfTime,
247 				transfer.Data(), request.size);
248 			request.SendReply(B_OK, &reply, sizeof(reply));
249 			return B_OK;
250 		}
251 
252 		case CONTROLLABLE_GET_PARAMETER_WEB:
253 		{
254 			const controllable_get_parameter_web_request& request
255 				= *static_cast<const controllable_get_parameter_web_request*>(
256 					data);
257 			controllable_get_parameter_web_reply reply;
258 
259 			status_t status = B_OK;
260 			bool wasLocked = true;
261 			if (!LockParameterWeb()) {
262 				status = B_ERROR;
263 				wasLocked = false;
264 			}
265 
266 			if (status == B_OK && fWeb != NULL) {
267 				if (fWeb->FlattenedSize() > request.max_size) {
268 					// parameter web too large
269 					reply.code = 0;
270 					reply.size = -1;
271 					status = B_OK;
272 				} else {
273 					ReceiveTransfer transfer(request, NULL);
274 					status = transfer.InitCheck();
275 					if (status == B_OK) {
276 						reply.code = fWeb->TypeCode();
277 						reply.size = fWeb->FlattenedSize();
278 						status = fWeb->Flatten(transfer.Data(), reply.size);
279 						if (status != B_OK) {
280 							ERROR("BControllable::HandleMessage "
281 								"CONTROLLABLE_GET_PARAMETER_WEB Flatten failed\n");
282 #if 0
283 						} else {
284 							printf("BControllable::HandleMessage CONTROLLABLE_GET_PARAMETER_WEB %ld bytes, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n",
285 								reply.size, ((uint32*)buffer)[0], ((uint32*)buffer)[1], ((uint32*)buffer)[2], ((uint32*)buffer)[3]);
286 #endif
287 						}
288 					}
289 				}
290 			} else {
291 				// no parameter web
292 				reply.code = 0;
293 				reply.size = 0;
294 			}
295 			if (wasLocked)
296 				UnlockParameterWeb();
297 
298 			request.SendReply(status, &reply, sizeof(reply));
299 			return B_OK;
300 		}
301 
302 		case CONTROLLABLE_START_CONTROL_PANEL:
303 		{
304 			const controllable_start_control_panel_request* request
305 				= static_cast<const controllable_start_control_panel_request*>(
306 					data);
307 			controllable_start_control_panel_reply reply;
308 			BMessenger targetMessenger;
309 			status_t status = StartControlPanel(&targetMessenger);
310 			if (status != B_OK) {
311 				ERROR("BControllable::HandleMessage "
312 					"CONTROLLABLE_START_CONTROL_PANEL failed\n");
313 			}
314 			reply.result = status;
315 			reply.team = targetMessenger.Team();
316 			request->SendReply(status, &reply, sizeof(reply));
317 			return B_OK;
318 		}
319 
320 		default:
321 			return B_ERROR;
322 	}
323 
324 	return B_OK;
325 }
326 
327 
328 status_t
329 BControllable::BroadcastChangedParameter(int32 id)
330 {
331 	CALLED();
332 	return BPrivate::media::notifications::ParameterChanged(Node(), id);
333 }
334 
335 
336 status_t
337 BControllable::BroadcastNewParameterValue(bigtime_t when, int32 id,
338 	void* newValue, size_t valueSize)
339 {
340 	CALLED();
341 	return BPrivate::media::notifications::NewParameterValue(Node(), id, when,
342 		newValue, valueSize);
343 }
344 
345 
346 status_t
347 BControllable::StartControlPanel(BMessenger* _messenger)
348 {
349 	CALLED();
350 
351 	int32 internalId;
352 	BMediaAddOn* addon = AddOn(&internalId);
353 	if (!addon) {
354 		ERROR("BControllable::StartControlPanel not instantiated per AddOn\n");
355 		return B_ERROR;
356 	}
357 
358 	image_id imageID = addon->ImageID();
359 	image_info info;
360 	if (imageID <= 0 || get_image_info(imageID, &info) != B_OK) {
361 		ERROR("BControllable::StartControlPanel Error accessing image\n");
362 		return B_BAD_VALUE;
363 	}
364 
365 	entry_ref ref;
366 	if (get_ref_for_path(info.name, &ref) != B_OK) {
367 		ERROR("BControllable::StartControlPanel Error getting ref\n");
368 		return B_BAD_VALUE;
369 	}
370 
371 	// The first argument is "node=id" with id meaning the media_node_id
372 	char arg[32];
373 	snprintf(arg, sizeof(arg), "node=%d", (int)ID());
374 
375 	team_id team;
376 	if (be_roster->Launch(&ref, 1, (const char* const*)&arg, &team) != B_OK) {
377 		ERROR("BControllable::StartControlPanel Error launching application\n");
378 		return B_BAD_VALUE;
379 	}
380 	printf("BControllable::StartControlPanel done with id: %" B_PRId32 "\n",
381 		team);
382 
383 	if (_messenger)
384 		*_messenger = BMessenger(NULL, team);
385 
386 	return B_OK;
387 }
388 
389 
390 status_t
391 BControllable::ApplyParameterData(const void* value, size_t size)
392 {
393 	UNIMPLEMENTED();
394 
395 	return B_ERROR;
396 }
397 
398 
399 status_t
400 BControllable::MakeParameterData(const int32* controls, int32 count,
401 	void* buffer, size_t* ioSize)
402 {
403 	UNIMPLEMENTED();
404 
405 	return B_ERROR;
406 }
407 
408 
409 //	#pragma mark - private
410 
411 
412 status_t BControllable::_Reserved_Controllable_0(void *) { return B_ERROR; }
413 status_t BControllable::_Reserved_Controllable_1(void *) { return B_ERROR; }
414 status_t BControllable::_Reserved_Controllable_2(void *) { return B_ERROR; }
415 status_t BControllable::_Reserved_Controllable_3(void *) { return B_ERROR; }
416 status_t BControllable::_Reserved_Controllable_4(void *) { return B_ERROR; }
417 status_t BControllable::_Reserved_Controllable_5(void *) { return B_ERROR; }
418 status_t BControllable::_Reserved_Controllable_6(void *) { return B_ERROR; }
419 status_t BControllable::_Reserved_Controllable_7(void *) { return B_ERROR; }
420 status_t BControllable::_Reserved_Controllable_8(void *) { return B_ERROR; }
421 status_t BControllable::_Reserved_Controllable_9(void *) { return B_ERROR; }
422 status_t BControllable::_Reserved_Controllable_10(void *) { return B_ERROR; }
423 status_t BControllable::_Reserved_Controllable_11(void *) { return B_ERROR; }
424 status_t BControllable::_Reserved_Controllable_12(void *) { return B_ERROR; }
425 status_t BControllable::_Reserved_Controllable_13(void *) { return B_ERROR; }
426 status_t BControllable::_Reserved_Controllable_14(void *) { return B_ERROR; }
427 status_t BControllable::_Reserved_Controllable_15(void *) { return B_ERROR; }
428