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