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