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 <debug.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 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 66 MediaFilesManager::~MediaFilesManager() 67 { 68 CALLED(); 69 delete fSaveTimerRunner; 70 } 71 72 73 status_t 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 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 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 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 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 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 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 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 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 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 329 MediaFilesManager::TimerMessage() 330 { 331 SaveState(); 332 333 delete fSaveTimerRunner; 334 fSaveTimerRunner = NULL; 335 } 336 337 338 void 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 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 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 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 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 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