xref: /haiku/src/servers/media/MediaFilesManager.cpp (revision 8f3a684551ef612f4b2dee43979f4a20f887c082)
1 /*
2  * Copyright 2003, Jérôme Duval. All rights reserved.
3  * Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "MediaFilesManager.h"
9 
10 #include <string.h>
11 
12 #include <Application.h>
13 #include <Autolock.h>
14 #include <Directory.h>
15 #include <FindDirectory.h>
16 #include <MediaFiles.h>
17 #include <Path.h>
18 
19 #include <MediaDebug.h>
20 #include <MediaSounds.h>
21 
22 
23 static const char* kSettingsDirectory = "Media";
24 static const char* kSettingsFile = "MediaFiles";
25 static const uint32 kSettingsWhat = 'mfil';
26 
27 
MediaFilesManager()28 MediaFilesManager::MediaFilesManager()
29 	:
30 	BLocker("media files manager"),
31 	fSaveTimerRunner(NULL)
32 {
33 	CALLED();
34 
35 	static const struct {
36 		const char* type;
37 		const char* item;
38 	} kInitialItems[] = {
39 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_BEEP},
40 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_STARTUP},
41 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_KEY_DOWN},
42 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_KEY_REPEAT},
43 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_KEY_UP},
44 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_MOUSE_DOWN},
45 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_MOUSE_UP},
46 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_WINDOW_ACTIVATED},
47 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_WINDOW_CLOSE},
48 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_WINDOW_MINIMIZED},
49 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_WINDOW_OPEN},
50 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_WINDOW_RESTORED},
51 		{MEDIA_TYPE_SOUNDS, MEDIA_SOUNDS_WINDOW_ZOOMED}
52 	};
53 
54 	for (size_t i = 0; i < sizeof(kInitialItems) / sizeof(kInitialItems[0]);
55 			i++) {
56 		_SetItem(kInitialItems[i].type, kInitialItems[i].item);
57 	}
58 
59 	_LoadState();
60 #if DEBUG >=3
61 	Dump();
62 #endif
63 }
64 
65 
~MediaFilesManager()66 MediaFilesManager::~MediaFilesManager()
67 {
68 	CALLED();
69 	delete fSaveTimerRunner;
70 }
71 
72 
73 status_t
SaveState()74 MediaFilesManager::SaveState()
75 {
76 	CALLED();
77 	BMessage settings(kSettingsWhat);
78 	status_t status;
79 
80 	TypeMap::iterator iterator = fMap.begin();
81 	for (; iterator != fMap.end(); iterator++) {
82 		const BString& type = iterator->first;
83 		ItemMap& itemMap = iterator->second;
84 
85 		BMessage items;
86 		status = items.AddString("type", type.String());
87 		if (status != B_OK)
88 			return status;
89 
90 		ItemMap::iterator itemIterator = itemMap.begin();
91 		for (; itemIterator != itemMap.end(); itemIterator++) {
92 			const BString& item = itemIterator->first;
93 			item_info& info = itemIterator->second;
94 
95 			status = items.AddString("item", item.String());
96 			if (status == B_OK) {
97 				BPath path(&info.ref);
98 				status = items.AddString("path",
99 					path.Path() ? path.Path() : "");
100 			}
101 			if (status == B_OK)
102 				status = items.AddFloat("gain", info.gain);
103 			if (status != B_OK)
104 				return status;
105 		}
106 
107 		status = settings.AddMessage("type items", &items);
108 		if (status != B_OK)
109 			return status;
110 	}
111 
112 	BFile file;
113 	status = _OpenSettingsFile(file,
114 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
115 	if (status != B_OK)
116 		return status;
117 
118 	return settings.Flatten(&file);
119 }
120 
121 
122 void
Dump()123 MediaFilesManager::Dump()
124 {
125 	BAutolock _(this);
126 
127 	printf("MediaFilesManager: types follow\n");
128 
129 	TypeMap::iterator iterator = fMap.begin();
130 	for (; iterator != fMap.end(); iterator++) {
131 		const BString& type = iterator->first;
132 		ItemMap& itemMap = iterator->second;
133 
134 		ItemMap::iterator fileIterator = itemMap.begin();
135 		for (; fileIterator != itemMap.end(); fileIterator++) {
136 			const BString& item = fileIterator->first;
137 			const item_info& info = fileIterator->second;
138 
139 			BPath path(&info.ref);
140 
141 			printf(" type \"%s\", item \"%s\", path \"%s\", gain %g\n",
142 				type.String(), item.String(),
143 				path.InitCheck() == B_OK ? path.Path() : "INVALID",
144 				info.gain);
145 		}
146 	}
147 
148 	printf("MediaFilesManager: list end\n");
149 }
150 
151 
152 area_id
GetTypesArea(int32 & count)153 MediaFilesManager::GetTypesArea(int32& count)
154 {
155 	CALLED();
156 	BAutolock _(this);
157 
158 	count = fMap.size();
159 
160 	size_t size = (count * B_MEDIA_NAME_LENGTH + B_PAGE_SIZE - 1)
161 		& ~(B_PAGE_SIZE - 1);
162 
163 	char* start;
164 	area_id area = create_area("media types", (void**)&start, B_ANY_ADDRESS,
165 		size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
166 	if (area < 0) {
167 		ERROR("MediaFilesManager::GetTypesArea(): failed to create area: %s\n",
168 			strerror(area));
169 		count = 0;
170 		return area;
171 	}
172 
173 	TypeMap::iterator iterator = fMap.begin();
174 	for (; iterator != fMap.end(); iterator++, start += B_MEDIA_NAME_LENGTH) {
175 		const BString& type = iterator->first;
176 		strncpy(start, type.String(), B_MEDIA_NAME_LENGTH);
177 	}
178 
179 	return area;
180 }
181 
182 
183 area_id
GetItemsArea(const char * type,int32 & count)184 MediaFilesManager::GetItemsArea(const char* type, int32& count)
185 {
186 	CALLED();
187 	if (type == NULL)
188 		return B_BAD_VALUE;
189 
190 	BAutolock _(this);
191 
192 	TypeMap::iterator found = fMap.find(BString(type));
193 	if (found == fMap.end()) {
194 		count = 0;
195 		return B_NAME_NOT_FOUND;
196 	}
197 
198 	ItemMap& itemMap = found->second;
199 	count = itemMap.size();
200 
201 	size_t size = (count * B_MEDIA_NAME_LENGTH + B_PAGE_SIZE - 1)
202 		& ~(B_PAGE_SIZE - 1);
203 
204 	char* start;
205 	area_id area = create_area("media refs", (void**)&start, B_ANY_ADDRESS,
206 		size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
207 	if (area < 0) {
208 		ERROR("MediaFilesManager::GetRefsArea(): failed to create area: %s\n",
209 			strerror(area));
210 		count = 0;
211 		return area;
212 	}
213 
214 	ItemMap::iterator iterator = itemMap.begin();
215 	for (; iterator != itemMap.end();
216 			iterator++, start += B_MEDIA_NAME_LENGTH) {
217 		const BString& item = iterator->first;
218 		strncpy(start, item.String(), B_MEDIA_NAME_LENGTH);
219 	}
220 
221 	return area;
222 }
223 
224 
225 status_t
GetRefFor(const char * type,const char * item,entry_ref ** _ref)226 MediaFilesManager::GetRefFor(const char* type, const char* item,
227 	entry_ref** _ref)
228 {
229 	CALLED();
230 	BAutolock _(this);
231 
232 	item_info* info;
233 	status_t status = _GetItem(type, item, info);
234 	if (status == B_OK)
235 		*_ref = &info->ref;
236 
237 	return status;
238 }
239 
240 
241 status_t
GetAudioGainFor(const char * type,const char * item,float * _gain)242 MediaFilesManager::GetAudioGainFor(const char* type, const char* item,
243 	float* _gain)
244 {
245 	CALLED();
246 	BAutolock _(this);
247 
248 	item_info* info;
249 	status_t status = _GetItem(type, item, info);
250 	if (status == B_OK)
251 		*_gain = info->gain;
252 
253 	return status;
254 }
255 
256 
257 status_t
SetRefFor(const char * type,const char * item,const entry_ref & ref)258 MediaFilesManager::SetRefFor(const char* type, const char* item,
259 	const entry_ref& ref)
260 {
261 	CALLED();
262 	TRACE("MediaFilesManager::SetRefFor %s %s\n", type, item);
263 
264 	BAutolock _(this);
265 
266 	status_t status = _SetItem(type, item, &ref);
267 	if (status == B_OK)
268 		_LaunchTimer();
269 
270 	return status;
271 }
272 
273 
274 status_t
SetAudioGainFor(const char * type,const char * item,float gain)275 MediaFilesManager::SetAudioGainFor(const char* type, const char* item,
276 	float gain)
277 {
278 	CALLED();
279 	TRACE("MediaFilesManager::SetAudioGainFor %s %s %g\n", type, item, gain);
280 
281 	BAutolock _(this);
282 
283 	status_t status = _SetItem(type, item, NULL, &gain);
284 	if (status == B_OK)
285 		_LaunchTimer();
286 
287 	return status;
288 }
289 
290 
291 status_t
InvalidateItem(const char * type,const char * item)292 MediaFilesManager::InvalidateItem(const char* type, const char* item)
293 {
294 	CALLED();
295 	BAutolock _(this);
296 
297 	TypeMap::iterator found = fMap.find(type);
298 	if (found == fMap.end())
299 		return B_NAME_NOT_FOUND;
300 
301 	ItemMap& itemMap = found->second;
302 	itemMap[item] = item_info();
303 
304 	_LaunchTimer();
305 	return B_OK;
306 }
307 
308 
309 status_t
RemoveItem(const char * type,const char * item)310 MediaFilesManager::RemoveItem(const char *type, const char *item)
311 {
312 	CALLED();
313 	BAutolock _(this);
314 
315 	TypeMap::iterator found = fMap.find(type);
316 	if (found == fMap.end())
317 		return B_NAME_NOT_FOUND;
318 
319 	found->second.erase(item);
320 	if (found->second.empty())
321 		fMap.erase(found);
322 
323 	_LaunchTimer();
324 	return B_OK;
325 }
326 
327 
328 void
TimerMessage()329 MediaFilesManager::TimerMessage()
330 {
331 	SaveState();
332 
333 	delete fSaveTimerRunner;
334 	fSaveTimerRunner = NULL;
335 }
336 
337 
338 void
HandleAddSystemBeepEvent(BMessage * message)339 MediaFilesManager::HandleAddSystemBeepEvent(BMessage* message)
340 {
341 	const char* name;
342 	const char* type;
343 	uint32 flags;
344 	if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK
345 		|| message->FindString(MEDIA_TYPE_KEY, &type) != B_OK
346 		|| message->FindInt32(MEDIA_FLAGS_KEY, (int32 *)&flags) != B_OK) {
347 		message->SendReply(B_BAD_VALUE);
348 		return;
349 	}
350 
351 	entry_ref* ref = NULL;
352 	if (GetRefFor(type, name, &ref) != B_OK) {
353 		entry_ref newRef;
354 		SetRefFor(type, name, newRef);
355 	}
356 }
357 
358 
359 void
_LaunchTimer()360 MediaFilesManager::_LaunchTimer()
361 {
362 	if (fSaveTimerRunner == NULL) {
363 		BMessage timer(MEDIA_FILES_MANAGER_SAVE_TIMER);
364 		fSaveTimerRunner = new BMessageRunner(be_app, &timer, 3 * 1000000LL, 1);
365 	}
366 }
367 
368 
369 /*!	You need to have the manager locked when calling this method.
370 */
371 status_t
_GetItem(const char * type,const char * item,item_info * & info)372 MediaFilesManager::_GetItem(const char* type, const char* item,
373 	item_info*& info)
374 {
375 	ASSERT(IsLocked());
376 
377 	TypeMap::iterator found = fMap.find(type);
378 	if (found == fMap.end())
379 		return B_NAME_NOT_FOUND;
380 
381 	ItemMap::iterator foundFile = found->second.find(item);
382 	if (foundFile == found->second.end())
383 		return B_NAME_NOT_FOUND;
384 
385 	info = &foundFile->second;
386 	return B_OK;
387 }
388 
389 
390 /*!	You need to have the manager locked when calling this method after
391 	launch.
392 */
393 status_t
_SetItem(const char * _type,const char * _item,const entry_ref * ref,const float * gain)394 MediaFilesManager::_SetItem(const char* _type, const char* _item,
395 	const entry_ref* ref, const float* gain)
396 {
397 	CALLED();
398 	TRACE("MediaFilesManager::_SetItem(%s, %s)\n", _type, _item);
399 
400 	BString type(_type);
401 	type.Truncate(B_MEDIA_NAME_LENGTH);
402 	BString item(_item);
403 	item.Truncate(B_MEDIA_NAME_LENGTH);
404 
405 	try {
406 		TypeMap::iterator found = fMap.find(type);
407 		if (found == fMap.end()) {
408 			// add new type
409 			ItemMap itemMap;
410 			// TODO: For some reason, this does not work:
411 			//found = fMap.insert(TypeMap::value_type(type, itemMap));
412 			fMap[type] = itemMap;
413 			found = fMap.find(type);
414 		}
415 
416 		ItemMap& itemMap = found->second;
417 		item_info info = itemMap[item];
418 
419 		// only update what we've got
420 		if (gain != NULL)
421 			info.gain = *gain;
422 		if (ref != NULL)
423 			info.ref = *ref;
424 
425 		itemMap[item] = info;
426 	} catch (std::bad_alloc& exception) {
427 		return B_NO_MEMORY;
428 	}
429 
430 	return B_OK;
431 }
432 
433 
434 status_t
_OpenSettingsFile(BFile & file,int mode)435 MediaFilesManager::_OpenSettingsFile(BFile& file, int mode)
436 {
437 	bool createFile = (mode & O_ACCMODE) != O_RDONLY;
438 	BPath path;
439 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path,
440 		createFile);
441 	if (status == B_OK)
442 		status = path.Append(kSettingsDirectory);
443 	if (status == B_OK && createFile) {
444 		status = create_directory(path.Path(),
445 			S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
446 	}
447 	if (status == B_OK)
448 		status = path.Append(kSettingsFile);
449 	if (status != B_OK)
450 		return status;
451 
452 	return file.SetTo(path.Path(), mode);
453 }
454 
455 
456 //! This is called by the media_server *before* any add-ons have been loaded.
457 status_t
_LoadState()458 MediaFilesManager::_LoadState()
459 {
460 	CALLED();
461 
462 	BFile file;
463 	status_t status = _OpenSettingsFile(file, B_READ_ONLY);
464 	if (status != B_OK)
465 		return status;
466 
467 	BMessage settings;
468 	status = settings.Unflatten(&file);
469 	if (status != B_OK)
470 		return status;
471 
472 	if (settings.what != kSettingsWhat)
473 		return B_BAD_TYPE;
474 
475 	BMessage items;
476 	for (int32 i = 0; settings.FindMessage("type items", i, &items) == B_OK;
477 			i++) {
478 		const char* type;
479 		if (items.FindString("type", &type) != B_OK)
480 			continue;
481 
482 		const char* item;
483 		for (int32 j = 0; items.FindString("item", j, &item) == B_OK; j++) {
484 			const char* path;
485 			if (items.FindString("path", j, &path) != B_OK)
486 				return B_BAD_DATA;
487 
488 			float gain;
489 			if (items.FindFloat("gain", j, &gain) != B_OK)
490 				gain = 1.0f;
491 
492 			entry_ref ref;
493 			get_ref_for_path(path, &ref);
494 				// it's okay for this to fail
495 
496 			_SetItem(type, item, &ref, &gain);
497 		}
498 	}
499 
500 	return B_OK;
501 }
502