1 /* 2 * Copyright 2003-2008, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Jérôme Duval 7 * François Revol 8 * Marcus Overhagen 9 * Jonas Sundström 10 */ 11 12 //! VolumeControl and link items in Deskbar 13 14 #include "VolumeSlider.h" 15 #include "DeskButton.h" 16 #include "iconfile.h" 17 18 #include <Alert.h> 19 #include <Application.h> 20 #include <Bitmap.h> 21 #include <Debug.h> 22 #include <Deskbar.h> 23 #include <Dragger.h> 24 #include <File.h> 25 #include <FindDirectory.h> 26 #include <List.h> 27 #include <MenuItem.h> 28 #include <Message.h> 29 #include <MimeType.h> 30 #include <Path.h> 31 #include <PopUpMenu.h> 32 #include <Roster.h> 33 #include <String.h> 34 #include <View.h> 35 36 #include <stdio.h> 37 #include <strings.h> 38 39 #define MEDIA_SETTINGS 'mese' 40 #define SOUND_SETTINGS 'sose' 41 #define OPEN_MEDIA_PLAYER 'omep' 42 #define TOGGLE_DONT_BEEP 'tdbp' 43 #define SET_VOLUME_WHICH 'svwh' 44 45 #define VOLUME_CTL_NAME "MediaReplicant" 46 // R5 name needed, Media prefs manel removes by name 47 48 #define SETTINGS_FILE "x-vnd.Haiku-desklink" 49 50 const char *kAppSignature = "application/x-vnd.Haiku-desklink"; 51 // the application signature used by the replicant to find the 52 // supporting code 53 54 55 class _EXPORT MediaReplicant; 56 // the dragger part has to be exported 57 58 class MediaReplicant : public BView { 59 public: 60 MediaReplicant(BRect frame, const char *name, 61 uint32 resizeMask = B_FOLLOW_ALL, 62 uint32 flags = B_WILL_DRAW | B_NAVIGABLE); 63 MediaReplicant(BMessage *); 64 // BMessage * based constructor needed to support archiving 65 virtual ~MediaReplicant(); 66 67 // archiving overrides 68 static MediaReplicant *Instantiate(BMessage *data); 69 virtual status_t Archive(BMessage *data, bool deep = true) const; 70 71 // misc BView overrides 72 virtual void AttachedToWindow(); 73 virtual void MouseDown(BPoint); 74 virtual void MouseUp(BPoint); 75 virtual void Draw(BRect updateRect); 76 virtual void MessageReceived(BMessage* message); 77 78 private: 79 void _AlertFindDirectory(status_t status, const char *where); 80 status_t _LaunchByPath(const char *path); 81 status_t _LaunchBySig(const char *sig); 82 void _LoadSettings(); 83 void _SaveSettings(); 84 85 BBitmap* fSegments; 86 VolumeSlider* fVolumeSlider; 87 bool fDontBeep; 88 // don't beep on volume change 89 int32 fVolumeWhich; 90 // which volume parameter to act on (Mixer/Phys.Output) 91 }; 92 93 // 94 // This is the exported function that will be used by Deskbar 95 // to create and add the replicant 96 // 97 extern "C" _EXPORT BView* instantiate_deskbar_item(); 98 99 BView * 100 instantiate_deskbar_item() 101 { 102 return new MediaReplicant(BRect(0, 0, 16, 16), VOLUME_CTL_NAME); 103 } 104 105 106 MediaReplicant::MediaReplicant(BRect frame, const char *name, 107 uint32 resizeMask, uint32 flags) 108 : BView(frame, name, resizeMask, flags), 109 fVolumeSlider(NULL) 110 { 111 // Background Bitmap 112 fSegments = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1), B_CMAP8); 113 fSegments->SetBits(kSpeakerBits, kSpeakerWidth*kSpeakerHeight, 0, B_CMAP8); 114 _LoadSettings(); 115 } 116 117 118 MediaReplicant::MediaReplicant(BMessage *message) 119 : BView(message), 120 fVolumeSlider(NULL) 121 { 122 // Background Bitmap 123 fSegments = new BBitmap(BRect(0, 0, 16 - 1, 16 - 1), B_CMAP8); 124 fSegments->SetBits(kSpeakerBits, 16*16, 0, B_CMAP8); 125 _LoadSettings(); 126 } 127 128 129 MediaReplicant::~MediaReplicant() 130 { 131 delete fSegments; 132 _SaveSettings(); 133 } 134 135 136 MediaReplicant * 137 MediaReplicant::Instantiate(BMessage *data) 138 { 139 if (!validate_instantiation(data, VOLUME_CTL_NAME)) 140 return NULL; 141 142 return new MediaReplicant(data); 143 } 144 145 146 status_t 147 MediaReplicant::Archive(BMessage *data, bool deep) const 148 { 149 status_t status = BView::Archive(data, deep); 150 if (status < B_OK) 151 return status; 152 153 return data->AddString("add_on", kAppSignature); 154 } 155 156 157 void 158 MediaReplicant::MessageReceived(BMessage *message) 159 { 160 switch (message->what) { 161 case B_ABOUT_REQUESTED: 162 (new BAlert("About Volume Control", "Volume Control (Replicant)\n" 163 " Brought to you by Jérôme DUVAL.\n\n" 164 "Copyright " B_UTF8_COPYRIGHT "2003-2008, Haiku","OK"))->Go(NULL); 165 break; 166 167 case OPEN_MEDIA_PLAYER: 168 { 169 BPath mediaPlayerPath; 170 status_t status = find_directory(B_BEOS_APPS_DIRECTORY, &mediaPlayerPath); 171 if (status != B_OK) { 172 _AlertFindDirectory(status, __PRETTY_FUNCTION__); 173 break; 174 } 175 mediaPlayerPath.Append("MediaPlayer"); 176 177 // launch the media player app 178 if (_LaunchBySig("application/x-vnd.Haiku-MediaPlayer") == B_OK 179 || _LaunchBySig("application/x-vnd.Be.MediaPlayer") == B_OK 180 || _LaunchByPath(mediaPlayerPath.Path()) == B_OK) 181 break; 182 183 (new BAlert("desklink", "Couldn't launch MediaPlayer", "OK"))->Go(); 184 break; 185 } 186 187 case MEDIA_SETTINGS: 188 { 189 BPath mediaPrefsPath; 190 status_t status = find_directory(B_BEOS_PREFERENCES_DIRECTORY, 191 &mediaPrefsPath); 192 if (status != B_OK) { 193 _AlertFindDirectory(status, __PRETTY_FUNCTION__); 194 break; 195 } 196 mediaPrefsPath.Append("Media"); 197 198 // launch the media prefs app 199 if (_LaunchBySig("application/x-vnd.Haiku-Media") == B_OK 200 || _LaunchBySig("application/x-vnd.Be.MediaPrefs") == B_OK 201 || _LaunchByPath(mediaPrefsPath.Path()) == B_OK) 202 break; 203 204 (new BAlert("desklink", "Couldn't launch Media Preferences", "OK"))->Go(); 205 break; 206 } 207 208 case SOUND_SETTINGS: 209 { 210 BPath soundsPrefsPath; 211 status_t status = find_directory(B_BEOS_PREFERENCES_DIRECTORY, 212 &soundsPrefsPath); 213 if (status != B_OK) { 214 _AlertFindDirectory(status, __PRETTY_FUNCTION__); 215 break; 216 } 217 soundsPrefsPath.Append("Sounds"); 218 219 // launch the sounds prefs app 220 if (_LaunchBySig("application/x-vnd.Haiku-Sounds") == B_OK 221 || _LaunchBySig("application/x-vnd.Be.SoundsPrefs") == B_OK 222 || _LaunchByPath(soundsPrefsPath.Path()) == B_OK) 223 break; 224 225 (new BAlert("desklink", "Couldn't launch Sounds Preferences", "OK"))->Go(); 226 break; 227 } 228 229 case TOGGLE_DONT_BEEP: 230 fDontBeep = !fDontBeep; 231 break; 232 233 case SET_VOLUME_WHICH: 234 message->FindInt32("volwhich", &fVolumeWhich); 235 break; 236 237 default: 238 BView::MessageReceived(message); 239 break; 240 } 241 } 242 243 244 status_t 245 MediaReplicant::_LaunchByPath(const char *path) 246 { 247 BEntry ent; 248 entry_ref ref; 249 app_info appInfo; 250 status_t err; 251 252 err = ent.SetTo(path); 253 if (err) 254 return err; 255 err = ent.GetRef(&ref); 256 if (err) 257 return err; 258 err = be_roster->Launch(&ref); 259 if (err != B_ALREADY_RUNNING) 260 return err; // should be B_OK or fatal error 261 err = be_roster->GetAppInfo(&ref, &appInfo); 262 if (err) 263 return err; 264 return be_roster->ActivateApp(appInfo.team); 265 } 266 267 268 status_t 269 MediaReplicant::_LaunchBySig(const char *sig) 270 { 271 app_info appInfo; 272 status_t err; 273 274 err = be_roster->Launch(sig); 275 if (err != B_ALREADY_RUNNING) 276 return err; // should be B_OK or fatal error 277 err = be_roster->GetAppInfo(sig, &appInfo); 278 if (err) 279 return err; 280 return be_roster->ActivateApp(appInfo.team); 281 } 282 283 284 void 285 MediaReplicant::AttachedToWindow() 286 { 287 BView *parent = Parent(); 288 if (parent) 289 SetViewColor(parent->ViewColor()); 290 291 BView::AttachedToWindow(); 292 } 293 294 295 void 296 MediaReplicant::Draw(BRect rect) 297 { 298 BView::Draw(rect); 299 300 SetDrawingMode(B_OP_OVER); 301 DrawBitmap(fSegments); 302 } 303 304 305 void 306 MediaReplicant::MouseDown(BPoint point) 307 { 308 uint32 mouseButtons; 309 BPoint where; 310 GetMouse(&where, &mouseButtons, true); 311 312 where = ConvertToScreen(point); 313 314 if (mouseButtons & B_SECONDARY_MOUSE_BUTTON) { 315 BPopUpMenu *menu = new BPopUpMenu("", false, false); 316 menu->SetFont(be_plain_font); 317 menu->AddItem(new BMenuItem("Media Preferences" B_UTF8_ELLIPSIS, new BMessage(MEDIA_SETTINGS))); 318 menu->AddItem(new BMenuItem("Sound Preferences" B_UTF8_ELLIPSIS, new BMessage(SOUND_SETTINGS))); 319 menu->AddSeparatorItem(); 320 menu->AddItem(new BMenuItem("Open MediaPlayer", new BMessage(OPEN_MEDIA_PLAYER))); 321 menu->AddSeparatorItem(); 322 BMenuItem *tmpItem = new BMenuItem("Don't beep", new BMessage(TOGGLE_DONT_BEEP)); 323 menu->AddItem(tmpItem); 324 tmpItem->SetMarked(fDontBeep); 325 BMenu *volMenu = new BMenu("Act On"); 326 volMenu->SetFont(be_plain_font); 327 BMessage *msg; 328 msg = new BMessage(SET_VOLUME_WHICH); 329 msg->AddInt32("volwhich", VOLUME_USE_MIXER); 330 tmpItem = new BMenuItem("System Mixer", msg); 331 tmpItem->SetMarked(fVolumeWhich == VOLUME_USE_MIXER); 332 volMenu->AddItem(tmpItem); 333 msg = new BMessage(SET_VOLUME_WHICH); 334 msg->AddInt32("volwhich", VOLUME_USE_PHYS_OUTPUT); 335 tmpItem = new BMenuItem("Physical Output", msg); 336 volMenu->AddItem(tmpItem); 337 tmpItem->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT); 338 menu->AddItem(volMenu); 339 340 menu->AddSeparatorItem(); 341 menu->SetFont(be_plain_font); 342 menu->AddItem(new BMenuItem("About" B_UTF8_ELLIPSIS, 343 new BMessage(B_ABOUT_REQUESTED))); 344 345 menu->SetTargetForItems(this); 346 volMenu->SetTargetForItems(this); 347 menu->Go(where, true, true, BRect(where - BPoint(4, 4), 348 where + BPoint(4, 4))); 349 } else if (mouseButtons & B_PRIMARY_MOUSE_BUTTON) { 350 // Show VolumeSlider 351 fVolumeSlider = new VolumeSlider(BRect(where.x, where.y, where.x + 207, where.y + 19), 352 fDontBeep, fVolumeWhich); 353 fVolumeSlider->Show(); 354 } 355 } 356 357 358 void 359 MediaReplicant::MouseUp(BPoint point) 360 { 361 // don't Quit() ! thanks for FFM users 362 } 363 364 365 void 366 MediaReplicant::_LoadSettings() 367 { 368 fDontBeep = false; 369 fVolumeWhich = VOLUME_USE_MIXER; 370 371 BPath p; 372 if (find_directory(B_USER_SETTINGS_DIRECTORY, &p, false) < B_OK) 373 return; 374 p.SetTo(p.Path(), SETTINGS_FILE); 375 BFile settings(p.Path(), B_READ_ONLY); 376 if (settings.InitCheck() < B_OK) 377 return; 378 BMessage msg; 379 if (msg.Unflatten(&settings) < B_OK) 380 return; 381 msg.FindInt32("volwhich", &fVolumeWhich); 382 msg.FindBool("dontbeep", &fDontBeep); 383 } 384 385 386 void 387 MediaReplicant::_SaveSettings() 388 { 389 BPath p; 390 if (find_directory(B_USER_SETTINGS_DIRECTORY, &p, false) < B_OK) 391 return; 392 p.SetTo(p.Path(), SETTINGS_FILE); 393 BFile settings(p.Path(), B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE); 394 if (settings.InitCheck() < B_OK) 395 return; 396 BMessage msg('CNFG'); 397 msg.AddInt32("volwhich", fVolumeWhich); 398 msg.AddBool("dontbeep", fDontBeep); 399 ssize_t len=0; 400 if (msg.Flatten(&settings, &len) < B_OK) 401 return; 402 } 403 404 405 void 406 MediaReplicant::_AlertFindDirectory(status_t status, const char *where) 407 { 408 BString errorMessage; 409 errorMessage << "At " << where << "\n"; 410 errorMessage << "find_directory() failed. \nReason: "; 411 errorMessage << strerror(status); 412 (new BAlert("AlertError", errorMessage.String(), "OK", NULL, NULL, 413 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 414 } 415 416 417 int 418 main(int, char **argv) 419 { 420 BApplication app(kAppSignature); 421 bool atLeastOnePath = false; 422 BList titleList; 423 BList actionList; 424 BDeskbar deskbar; 425 status_t err = B_OK; 426 427 for (int32 i = 1; argv[i]!=NULL; i++) { 428 if (strcmp(argv[i], "--help") == 0) 429 break; 430 431 if (strcmp(argv[i], "--list") == 0) { 432 int32 i, found = 0, count; 433 count = deskbar.CountItems(); 434 printf("Deskbar items:\n"); 435 // the API is doomed, so don't try to enum for too long 436 for (i = 0; (found < count) && (i >= 0) && (i < 5000); i++) { 437 const char scratch[2] = ""; // BDeskbar is buggy 438 const char *name=scratch; 439 if (deskbar.GetItemInfo(i, &name) >= B_OK) { 440 found++; 441 printf("Item %ld: '%s'\n", i, name); 442 free((void *)name); // INTENDED 443 } 444 } 445 return 0; 446 } 447 448 if (strncmp(argv[i], "--remove", 8) == 0) { 449 BString replicant = "DeskButton"; 450 if (strncmp(argv[i] + 8, "=", 1) == 0) { 451 if (strlen(argv[i] + 9) > 0) { 452 replicant = argv[i] + 9; 453 } else { 454 printf("desklink: Missing replicant name.\n"); 455 return 1; 456 } 457 } 458 int32 found = 0; 459 int32 found_id; 460 while (deskbar.GetItemInfo(replicant.String(), &found_id) == B_OK) { 461 err = deskbar.RemoveItem(found_id); 462 if (err != B_OK) { 463 printf("desklink: Error removing replicant id %ld: %s\n", 464 found_id, strerror(err)); 465 break; 466 } 467 found++; 468 } 469 printf("Removed %ld items.\n", found); 470 return err; 471 } 472 473 if (strncmp(argv[i], "cmd=", 4) == 0) { 474 BString *title = new BString(argv[i] + 4); 475 int32 index = title->FindFirst(':'); 476 if (index <= 0) { 477 printf("desklink: usage: cmd=title:action\n"); 478 } else { 479 title->Truncate(index); 480 BString *action = new BString(argv[i] + 4); 481 action->Remove(0, index+1); 482 titleList.AddItem(title); 483 actionList.AddItem(action); 484 } 485 continue; 486 } 487 488 atLeastOnePath = true; 489 490 BEntry entry(argv[i], true); 491 entry_ref ref; 492 493 if (entry.Exists()) { 494 entry.GetRef(&ref); 495 } else if (BMimeType::IsValid(argv[i])) { 496 if (be_roster->FindApp(argv[i], &ref) != B_OK) { 497 printf("desklink: cannot find '%s'\n", argv[i]); 498 return 1; 499 } 500 } else { 501 printf("desklink: cannot find '%s'\n", argv[i]); 502 return 1; 503 } 504 505 err = deskbar.AddItem(&ref); 506 if (err != B_OK) { 507 err = deskbar.AddItem(new DeskButton(BRect(0, 0, 15, 15), 508 &ref, "DeskButton", titleList, actionList)); 509 if (err != B_OK) { 510 printf("desklink: Deskbar refuses link to '%s': %s\n", argv[i], strerror(err)); 511 return 1; 512 } 513 } 514 515 titleList.MakeEmpty(); 516 actionList.MakeEmpty(); 517 } 518 519 if (!atLeastOnePath) { 520 printf( "usage: desklink { [ --list|--remove|[cmd=title:action ... ] [ path|signature ] } ...\n" 521 "--list: list all Deskbar addons.\n" 522 "--remove: remove all desklink addons.\n" 523 "--remove=name: remove all 'name' addons.\n"); 524 return 1; 525 } 526 527 return 0; 528 } 529