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