1 /* 2 * MainApp.cpp - Media Player for the Haiku Operating System 3 * 4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de> 5 * Copyright (C) 2008 Stephan Aßmus <superstippi@gmx.de> (MIT Ok) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 * 20 */ 21 #include "MainApp.h" 22 23 #include <Alert.h> 24 #include <Autolock.h> 25 #include <Entry.h> 26 #include <FilePanel.h> 27 #include <MediaRoster.h> 28 #include <Path.h> 29 #include <Roster.h> 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 35 #include "EventQueue.h" 36 #include "Settings.h" 37 #include "SettingsWindow.h" 38 39 40 MainApp* gMainApp; 41 const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer"; 42 43 static const char* kMediaServerSig = "application/x-vnd.Be.media-server"; 44 static const char* kMediaServerAddOnSig = "application/x-vnd.Be.addon-host"; 45 46 47 MainApp::MainApp() 48 : BApplication(kAppSig), 49 fPlayerCount(0), 50 fFirstWindow(NULL), 51 fSettingsWindow(NULL), 52 53 fOpenFilePanel(NULL), 54 fSaveFilePanel(NULL), 55 fLastFilePanelFolder(), 56 57 fMediaServerRunning(false), 58 fMediaAddOnServerRunning(false) 59 { 60 mpSettings settings = Settings::CurrentSettings(); 61 fLastFilePanelFolder = settings.filePanelFolder; 62 } 63 64 65 MainApp::~MainApp() 66 { 67 delete fOpenFilePanel; 68 delete fSaveFilePanel; 69 } 70 71 72 bool 73 MainApp::QuitRequested() 74 { 75 // Note: This needs to be done here, SettingsWindow::QuitRequested() 76 // returns "false" always. (Standard BApplication quit procedure will 77 // hang otherwise.) 78 if (fSettingsWindow && fSettingsWindow->Lock()) 79 fSettingsWindow->Quit(); 80 fSettingsWindow = NULL; 81 82 // store the current file panel ref in the global settings 83 mpSettings settings = Settings::CurrentSettings(); 84 settings.filePanelFolder = fLastFilePanelFolder; 85 Settings::Default()->SaveSettings(settings); 86 87 return BApplication::QuitRequested(); 88 } 89 90 91 BWindow* 92 MainApp::FirstWindow() 93 { 94 BAutolock _(this); 95 if (fFirstWindow != NULL) 96 return fFirstWindow; 97 return NewWindow(); 98 } 99 100 101 BWindow* 102 MainApp::NewWindow() 103 { 104 BAutolock _(this); 105 fPlayerCount++; 106 BWindow* window = new MainWin(); 107 if (fFirstWindow == NULL) 108 fFirstWindow = window; 109 return window; 110 } 111 112 113 int32 114 MainApp::PlayerCount() const 115 { 116 BAutolock _(const_cast<MainApp*>(this)); 117 return fPlayerCount; 118 } 119 120 121 // #pragma mark - 122 123 124 void 125 MainApp::ReadyToRun() 126 { 127 // Now tell the application roster, that we're interested 128 // in getting notifications of apps being launched or quit. 129 // In this way we are going to detect a media_server restart. 130 be_roster->StartWatching(BMessenger(this, this), 131 B_REQUEST_LAUNCHED | B_REQUEST_QUIT); 132 // we will keep track of the status of media_server 133 // and media_addon_server 134 fMediaServerRunning = be_roster->IsRunning(kMediaServerSig); 135 fMediaAddOnServerRunning = be_roster->IsRunning(kMediaServerAddOnSig); 136 137 if (!fMediaServerRunning || !fMediaAddOnServerRunning) { 138 BAlert* alert = new BAlert("start_media_server", 139 "It appears the Media Server is not running.\n" 140 "Would you like to start it ?", "Quit", "Start Media Server", NULL, 141 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 142 if (alert->Go() == 0) { 143 PostMessage(B_QUIT_REQUESTED); 144 return; 145 } 146 147 launch_media_server(); 148 149 fMediaServerRunning = be_roster->IsRunning(kMediaServerSig); 150 fMediaAddOnServerRunning = be_roster->IsRunning(kMediaServerAddOnSig); 151 } 152 153 // make sure we have at least one window open 154 FirstWindow(); 155 156 // setup the settings window now, we need to have it 157 fSettingsWindow = new SettingsWindow(BRect(150, 150, 450, 520)); 158 fSettingsWindow->Hide(); 159 fSettingsWindow->Show(); 160 } 161 162 163 void 164 MainApp::RefsReceived(BMessage* message) 165 { 166 // The user dropped a file (or files) on this app's icon, 167 // or double clicked a file that's handled by this app. 168 // Command line arguments are also redirected to here by 169 // ArgvReceived() but without MIME type check. 170 // For each file we create a new window and send it a 171 // B_REFS_RECEIVED message with a single file. 172 // If IsLaunching() is true, we use fFirstWindow as first 173 // window. 174 printf("MainApp::RefsReceived\n"); 175 176 BWindow* window = NewWindow(); 177 if (window) 178 window->PostMessage(message); 179 } 180 181 182 void 183 MainApp::ArgvReceived(int32 argc, char **argv) 184 { 185 char cwd[B_PATH_NAME_LENGTH]; 186 getcwd(cwd, sizeof(cwd)); 187 188 BMessage m(B_REFS_RECEIVED); 189 190 for (int i = 1; i < argc; i++) { 191 printf("MainApp::ArgvReceived %s\n", argv[i]); 192 BPath path; 193 if (argv[i][0] != '/') 194 path.SetTo(cwd, argv[i]); 195 else 196 path.SetTo(argv[i]); 197 BEntry entry(path.Path(), true); 198 if (!entry.Exists() || !entry.IsFile()) 199 continue; 200 201 entry_ref ref; 202 if (B_OK == entry.GetRef(&ref)) 203 m.AddRef("refs", &ref); 204 } 205 206 if (m.HasRef("refs")) { 207 printf("MainApp::ArgvReceived calling RefsReceived\n"); 208 RefsReceived(&m); 209 } 210 } 211 212 213 void 214 MainApp::MessageReceived(BMessage* message) 215 { 216 switch (message->what) { 217 case M_PLAYER_QUIT: 218 fPlayerCount--; 219 if (fPlayerCount == 0) 220 PostMessage(B_QUIT_REQUESTED); 221 break; 222 223 case B_SOME_APP_LAUNCHED: 224 case B_SOME_APP_QUIT: 225 { 226 const char* mimeSig; 227 if (message->FindString("be:signature", &mimeSig) < B_OK) 228 break; 229 230 bool isMediaServer = strcmp(mimeSig, kMediaServerSig) == 0; 231 bool isAddonServer = strcmp(mimeSig, kMediaServerAddOnSig) == 0; 232 if (!isMediaServer && !isAddonServer) 233 break; 234 235 bool running = (message->what == B_SOME_APP_LAUNCHED); 236 if (isMediaServer) 237 fMediaServerRunning = running; 238 if (isAddonServer) 239 fMediaAddOnServerRunning = running; 240 241 if (!fMediaServerRunning && !fMediaAddOnServerRunning) { 242 fprintf(stderr, "media server has quit.\n"); 243 // trigger closing of media nodes 244 BMessage broadcast(M_MEDIA_SERVER_QUIT); 245 _BroadcastMessage(broadcast); 246 } else if (fMediaServerRunning && fMediaAddOnServerRunning) { 247 fprintf(stderr, "media server has launched.\n"); 248 // HACK! 249 // quit our now invalid instance of the media roster 250 // so that before new nodes are created, 251 // we get a new roster (it is a normal looper) 252 // TODO: This functionality could become part of 253 // BMediaRoster. It could detect the start/quit of 254 // the servers like it is done here, and either quit 255 // itself, or re-establish the connection, and send some 256 // notification to the app... something along those lines. 257 BMediaRoster* roster = BMediaRoster::CurrentRoster(); 258 if (roster) { 259 roster->Lock(); 260 roster->Quit(); 261 } 262 // give the servers some time to init... 263 snooze(3000000); 264 // trigger re-init of media nodes 265 BMessage broadcast(M_MEDIA_SERVER_STARTED); 266 _BroadcastMessage(broadcast); 267 } 268 break; 269 } 270 case M_SETTINGS: 271 _ShowSettingsWindow(); 272 break; 273 274 case M_SHOW_OPEN_PANEL: 275 _ShowOpenFilePanel(message); 276 break; 277 case M_SHOW_SAVE_PANEL: 278 _ShowSaveFilePanel(message); 279 break; 280 281 case M_OPEN_PANEL_RESULT: 282 _HandleOpenPanelResult(message); 283 break; 284 case M_SAVE_PANEL_RESULT: 285 _HandleSavePanelResult(message); 286 break; 287 case B_CANCEL: { 288 // The user canceled a file panel, but store at least the current 289 // file panel folder. 290 uint32 oldWhat; 291 if (message->FindInt32("old_what", (int32*)&oldWhat) != B_OK) 292 break; 293 if (oldWhat == M_OPEN_PANEL_RESULT && fOpenFilePanel != NULL) 294 fOpenFilePanel->GetPanelDirectory(&fLastFilePanelFolder); 295 else if (oldWhat == M_SAVE_PANEL_RESULT && fSaveFilePanel != NULL) 296 fSaveFilePanel->GetPanelDirectory(&fLastFilePanelFolder); 297 break; 298 } 299 300 default: 301 BApplication::MessageReceived(message); 302 break; 303 } 304 } 305 306 307 void 308 MainApp::AboutRequested() 309 { 310 FirstWindow()->PostMessage(B_ABOUT_REQUESTED); 311 } 312 313 314 // #pragma mark - 315 316 317 void 318 MainApp::_BroadcastMessage(const BMessage& _message) 319 { 320 for (int32 i = 0; BWindow* window = WindowAt(i); i++) { 321 BMessage message(_message); 322 window->PostMessage(&message); 323 } 324 } 325 326 327 void 328 MainApp::_ShowSettingsWindow() 329 { 330 BAutolock lock(fSettingsWindow); 331 if (!lock.IsLocked()) 332 return; 333 334 // If the window is already showing, don't jerk the workspaces around, 335 // just pull it to the current one. 336 uint32 workspace = 1UL << (uint32)current_workspace(); 337 uint32 windowWorkspaces = fSettingsWindow->Workspaces(); 338 if ((windowWorkspaces & workspace) == 0) { 339 // window in a different workspace, reopen in current 340 fSettingsWindow->SetWorkspaces(workspace); 341 } 342 343 if (fSettingsWindow->IsHidden()) 344 fSettingsWindow->Show(); 345 else 346 fSettingsWindow->Activate(); 347 } 348 349 350 // #pragma mark - file panels 351 352 353 void 354 MainApp::_ShowOpenFilePanel(const BMessage* message) 355 { 356 if (fOpenFilePanel == NULL) { 357 BMessenger target(this); 358 fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, &target); 359 } 360 361 _ShowFilePanel(fOpenFilePanel, M_OPEN_PANEL_RESULT, message, 362 "Open", "Open"); 363 } 364 365 366 void 367 MainApp::_ShowSaveFilePanel(const BMessage* message) 368 { 369 if (fSaveFilePanel == NULL) { 370 BMessenger target(this); 371 fSaveFilePanel = new BFilePanel(B_SAVE_PANEL, &target); 372 } 373 374 _ShowFilePanel(fSaveFilePanel, M_SAVE_PANEL_RESULT, message, 375 "Save", "Save"); 376 } 377 378 379 void 380 MainApp::_ShowFilePanel(BFilePanel* panel, uint32 command, 381 const BMessage* message, const char* defaultTitle, 382 const char* defaultLabel) 383 { 384 // printf("_ShowFilePanel()\n"); 385 // message->PrintToStream(); 386 387 BMessage panelMessage(command); 388 389 if (message != NULL) { 390 BMessage targetMessage; 391 if (message->FindMessage("message", &targetMessage) == B_OK) 392 panelMessage.AddMessage("message", &targetMessage); 393 394 BMessenger target; 395 if (message->FindMessenger("target", &target) == B_OK) 396 panelMessage.AddMessenger("target", target); 397 398 const char* panelTitle; 399 if (message->FindString("title", &panelTitle) != B_OK) 400 panelTitle = defaultTitle; 401 { 402 BString finalPanelTitle = "MediaPlayer: "; 403 finalPanelTitle << panelTitle; 404 BAutolock lock(panel->Window()); 405 panel->Window()->SetTitle(finalPanelTitle.String()); 406 } 407 const char* buttonLabel; 408 if (message->FindString("label", &buttonLabel) != B_OK) 409 buttonLabel = defaultLabel; 410 panel->SetButtonLabel(B_DEFAULT_BUTTON, buttonLabel); 411 } 412 413 // panelMessage.PrintToStream(); 414 panel->SetMessage(&panelMessage); 415 416 if (fLastFilePanelFolder != entry_ref()) { 417 panel->SetPanelDirectory(&fLastFilePanelFolder); 418 } 419 420 panel->Show(); 421 } 422 423 424 void 425 MainApp::_HandleOpenPanelResult(const BMessage* message) 426 { 427 _HandleFilePanelResult(fOpenFilePanel, message); 428 } 429 430 431 void 432 MainApp::_HandleSavePanelResult(const BMessage* message) 433 { 434 _HandleFilePanelResult(fSaveFilePanel, message); 435 } 436 437 438 void 439 MainApp::_HandleFilePanelResult(BFilePanel* panel, const BMessage* message) 440 { 441 // printf("_HandleFilePanelResult()\n"); 442 // message->PrintToStream(); 443 444 panel->GetPanelDirectory(&fLastFilePanelFolder); 445 446 BMessage targetMessage; 447 if (message->FindMessage("message", &targetMessage) != B_OK) 448 targetMessage.what = message->what; 449 450 BMessenger target; 451 if (message->FindMessenger("target", &target) != B_OK) { 452 if (targetMessage.what == M_OPEN_PANEL_RESULT 453 || targetMessage.what == M_SAVE_PANEL_RESULT) { 454 // prevent endless message cycle 455 return; 456 } 457 // send result message to ourselves 458 target = BMessenger(this); 459 } 460 461 // copy the important contents of the message 462 // save panel 463 entry_ref directory; 464 if (message->FindRef("directory", &directory) == B_OK) 465 targetMessage.AddRef("directory", &directory); 466 const char* name; 467 if (message->FindString("name", &name) == B_OK) 468 targetMessage.AddString("name", name); 469 // open panel 470 entry_ref ref; 471 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) 472 targetMessage.AddRef("refs", &ref); 473 474 target.SendMessage(&targetMessage); 475 } 476 477 478 // #pragma mark - main 479 480 481 int 482 main() 483 { 484 EventQueue::CreateDefault(); 485 486 srand(system_time()); 487 488 gMainApp = new MainApp; 489 gMainApp->Run(); 490 delete gMainApp; 491 492 EventQueue::DeleteDefault(); 493 494 return 0; 495 } 496