1 #include "CodyCam.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 #include <unistd.h> 6 7 #include <Alert.h> 8 #include <Button.h> 9 #include <Catalog.h> 10 #include <FindDirectory.h> 11 #include <LayoutBuilder.h> 12 #include <MediaDefs.h> 13 #include <MediaNode.h> 14 #include <MediaRoster.h> 15 #include <MediaTheme.h> 16 #include <Menu.h> 17 #include <MenuBar.h> 18 #include <MenuItem.h> 19 #include <Path.h> 20 #include <PopUpMenu.h> 21 #include <scheduler.h> 22 #include <TabView.h> 23 #include <TextControl.h> 24 #include <TimeSource.h> 25 #include <TranslationUtils.h> 26 27 #undef B_TRANSLATION_CONTEXT 28 #define B_TRANSLATION_CONTEXT "CodyCam" 29 30 #define VIDEO_SIZE_X 320 31 #define VIDEO_SIZE_Y 240 32 33 #define WINDOW_SIZE_X (VIDEO_SIZE_X + 80) 34 #define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230) 35 36 #define WINDOW_OFFSET_X 28 37 #define WINDOW_OFFSET_Y 28 38 39 #define CALL printf 40 #define ERROR printf 41 #define FTPINFO printf 42 #define INFO printf 43 44 45 // Utility functions 46 47 namespace { 48 49 // functions for EnumeratedStringValueSettings 50 51 const char* 52 CaptureRateAt(int32 i) 53 { 54 return (i >= 0 && i < kCaptureRatesCount) ? kCaptureRates[i].name : NULL; 55 } 56 57 58 const char* 59 UploadClientAt(int32 i) 60 { 61 return (i >= 0 && i < kUploadClientsCount) ? kUploadClients[i] : NULL; 62 } 63 64 65 }; // end anonymous namespace 66 67 68 69 // #pragma mark - 70 71 72 CodyCam::CodyCam() 73 : 74 BApplication("application/x-vnd.Haiku-CodyCam"), 75 fMediaRoster(NULL), 76 fVideoConsumer(NULL), 77 fWindow(NULL), 78 fPort(0), 79 fVideoControlWindow(NULL) 80 { 81 int32 index = 0; 82 kCaptureRates[index++].name = B_TRANSLATE("Every 15 seconds"); 83 kCaptureRates[index++].name = B_TRANSLATE("Every 30 seconds"); 84 kCaptureRates[index++].name = B_TRANSLATE("Every minute"); 85 kCaptureRates[index++].name = B_TRANSLATE("Every 5 minutes"); 86 kCaptureRates[index++].name = B_TRANSLATE("Every 10 minutes"); 87 kCaptureRates[index++].name = B_TRANSLATE("Every 15 minutes"); 88 kCaptureRates[index++].name = B_TRANSLATE("Every 30 minutes"); 89 kCaptureRates[index++].name = B_TRANSLATE("Every hour"); 90 kCaptureRates[index++].name = B_TRANSLATE("Every 2 hours"); 91 kCaptureRates[index++].name = B_TRANSLATE("Every 4 hours"); 92 kCaptureRates[index++].name = B_TRANSLATE("Every 8 hours"); 93 kCaptureRates[index++].name = B_TRANSLATE("Every 24 hours"); 94 kCaptureRates[index++].name = B_TRANSLATE("Never"); 95 96 index = 0; 97 kUploadClients[index++] = B_TRANSLATE("FTP"); 98 kUploadClients[index++] = B_TRANSLATE("SFTP"); 99 kUploadClients[index++] = B_TRANSLATE("Local"); 100 101 BPath homeDir; 102 if (find_directory(B_USER_DIRECTORY, &homeDir) != B_OK) 103 homeDir.SetTo("/boot/home"); 104 105 chdir(homeDir.Path()); 106 } 107 108 109 CodyCam::~CodyCam() 110 { 111 CALL("CodyCam::~CodyCam\n"); 112 113 // release the video consumer node 114 // the consumer node cleans up the window 115 if (fVideoConsumer) { 116 fVideoConsumer->Release(); 117 fVideoConsumer = NULL; 118 } 119 120 CALL("CodyCam::~CodyCam - EXIT\n"); 121 } 122 123 124 void 125 CodyCam::ReadyToRun() 126 { 127 fWindow = new VideoWindow( 128 (const char*) B_TRANSLATE_SYSTEM_NAME("CodyCam"), B_TITLED_WINDOW, 129 B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS, &fPort); 130 131 if (_SetUpNodes() != B_OK) 132 fWindow->ToggleMenuOnOff(); 133 134 ((VideoWindow*)fWindow)->ApplyControls(); 135 } 136 137 138 bool 139 CodyCam::QuitRequested() 140 { 141 _TearDownNodes(); 142 snooze(100000); 143 144 return true; 145 } 146 147 148 void 149 CodyCam::MessageReceived(BMessage *message) 150 { 151 switch (message->what) { 152 case msg_start: 153 { 154 BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor( 155 fTimeSourceNode); 156 bigtime_t real = BTimeSource::RealTime(); 157 bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000; 158 status_t status = fMediaRoster->StartNode(fProducerNode, perf); 159 if (status != B_OK) 160 ERROR("error starting producer!"); 161 timeSource->Release(); 162 break; 163 } 164 165 case msg_stop: 166 fMediaRoster->StopNode(fProducerNode, 0, true); 167 break; 168 169 case msg_video: 170 { 171 if (fVideoControlWindow) { 172 fVideoControlWindow->Activate(); 173 break; 174 } 175 BParameterWeb* web = NULL; 176 BView* view = NULL; 177 media_node node = fProducerNode; 178 status_t err = fMediaRoster->GetParameterWebFor(node, &web); 179 if (err >= B_OK && web != NULL) { 180 view = BMediaTheme::ViewFor(web); 181 fVideoControlWindow = new ControlWindow(view, node); 182 fMediaRoster->StartWatching(BMessenger(NULL, 183 fVideoControlWindow), node, B_MEDIA_WEB_CHANGED); 184 fVideoControlWindow->Show(); 185 } 186 break; 187 } 188 189 case msg_control_win: 190 // our control window is being asked to go away 191 // set our pointer to NULL 192 fVideoControlWindow = NULL; 193 break; 194 195 default: 196 BApplication::MessageReceived(message); 197 break; 198 } 199 } 200 201 202 status_t 203 CodyCam::_SetUpNodes() 204 { 205 status_t status = B_OK; 206 207 /* find the media roster */ 208 fMediaRoster = BMediaRoster::Roster(&status); 209 if (status != B_OK) { 210 fWindow->ErrorAlert(B_TRANSLATE("Cannot find the media roster"), 211 status); 212 return status; 213 } 214 215 /* find the time source */ 216 status = fMediaRoster->GetTimeSource(&fTimeSourceNode); 217 if (status != B_OK) { 218 fWindow->ErrorAlert(B_TRANSLATE("Cannot get a time source"), status); 219 return status; 220 } 221 222 /* find a video producer node */ 223 INFO("CodyCam acquiring VideoInput node\n"); 224 status = fMediaRoster->GetVideoInput(&fProducerNode); 225 if (status != B_OK) { 226 fWindow->ErrorAlert(B_TRANSLATE("Cannot find a video source. You need " 227 "a webcam to use CodyCam."), status); 228 return status; 229 } 230 231 /* create the video consumer node */ 232 fVideoConsumer = new VideoConsumer("CodyCam", 233 ((VideoWindow*)fWindow)->VideoView(), 234 ((VideoWindow*)fWindow)->StatusLine(), NULL, 0); 235 if (!fVideoConsumer) { 236 fWindow->ErrorAlert(B_TRANSLATE("Cannot create a video window"), 237 B_ERROR); 238 return B_ERROR; 239 } 240 241 /* register the node */ 242 status = fMediaRoster->RegisterNode(fVideoConsumer); 243 if (status != B_OK) { 244 fWindow->ErrorAlert(B_TRANSLATE("Cannot register the video window"), 245 status); 246 return status; 247 } 248 fPort = fVideoConsumer->ControlPort(); 249 250 /* find free producer output */ 251 int32 cnt = 0; 252 status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1, 253 &cnt, B_MEDIA_RAW_VIDEO); 254 if (status != B_OK || cnt < 1) { 255 status = B_RESOURCE_UNAVAILABLE; 256 fWindow->ErrorAlert(B_TRANSLATE("Cannot find an available video stream"), 257 status); 258 return status; 259 } 260 261 /* find free consumer input */ 262 cnt = 0; 263 status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(), 264 &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO); 265 if (status != B_OK || cnt < 1) { 266 status = B_RESOURCE_UNAVAILABLE; 267 fWindow->ErrorAlert(B_TRANSLATE("Can't find an available connection to " 268 "the video window"), status); 269 return status; 270 } 271 272 /* Connect The Nodes!!! */ 273 media_format format; 274 format.type = B_MEDIA_RAW_VIDEO; 275 media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT, 276 1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}}; 277 format.u.raw_video = vid_format; 278 279 /* connect producer to consumer */ 280 status = fMediaRoster->Connect(fProducerOut.source, 281 fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn); 282 if (status != B_OK) { 283 fWindow->ErrorAlert(B_TRANSLATE("Cannot connect the video source to " 284 "the video window"), status); 285 return status; 286 } 287 288 289 /* set time sources */ 290 status = fMediaRoster->SetTimeSourceFor(fProducerNode.node, 291 fTimeSourceNode.node); 292 if (status != B_OK) { 293 fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the " 294 "video source"), status); 295 return status; 296 } 297 298 status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(), 299 fTimeSourceNode.node); 300 if (status != B_OK) { 301 fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the " 302 "video window"), status); 303 return status; 304 } 305 306 /* figure out what recording delay to use */ 307 bigtime_t latency = 0; 308 status = fMediaRoster->GetLatencyFor(fProducerNode, &latency); 309 status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency); 310 311 /* start the nodes */ 312 bigtime_t initLatency = 0; 313 status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency); 314 if (status < B_OK) { 315 fWindow->ErrorAlert(B_TRANSLATE("Error getting initial latency for the " 316 "capture node"), status); 317 return status; 318 } 319 320 initLatency += estimate_max_scheduling_latency(); 321 322 BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode); 323 bool running = timeSource->IsRunning(); 324 325 /* workaround for people without sound cards */ 326 /* because the system time source won't be running */ 327 bigtime_t real = BTimeSource::RealTime(); 328 if (!running) { 329 status = fMediaRoster->StartTimeSource(fTimeSourceNode, real); 330 if (status != B_OK) { 331 timeSource->Release(); 332 fWindow->ErrorAlert(B_TRANSLATE("Cannot start time source!"), 333 status); 334 return status; 335 } 336 status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real); 337 if (status != B_OK) { 338 timeSource->Release(); 339 fWindow->ErrorAlert(B_TRANSLATE("Cannot seek time source!"), 340 status); 341 return status; 342 } 343 } 344 345 bigtime_t perf = timeSource->PerformanceTimeFor(real + latency 346 + initLatency); 347 timeSource->Release(); 348 349 /* start the nodes */ 350 status = fMediaRoster->StartNode(fProducerNode, perf); 351 if (status != B_OK) { 352 fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video source"), 353 status); 354 return status; 355 } 356 status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf); 357 if (status != B_OK) { 358 fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video window"), 359 status); 360 return status; 361 } 362 363 return status; 364 } 365 366 367 void 368 CodyCam::_TearDownNodes() 369 { 370 CALL("CodyCam::_TearDownNodes\n"); 371 if (!fMediaRoster) 372 return; 373 374 if (fVideoConsumer) { 375 /* stop */ 376 INFO("stopping nodes!\n"); 377 // fMediaRoster->StopNode(fProducerNode, 0, true); 378 fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true); 379 380 /* disconnect */ 381 fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source, 382 fConsumerIn.node.node, fConsumerIn.destination); 383 384 if (fProducerNode != media_node::null) { 385 INFO("CodyCam releasing fProducerNode\n"); 386 fMediaRoster->ReleaseNode(fProducerNode); 387 fProducerNode = media_node::null; 388 } 389 fMediaRoster->ReleaseNode(fVideoConsumer->Node()); 390 fVideoConsumer = NULL; 391 } 392 } 393 394 395 // #pragma mark - Video Window Class 396 397 398 VideoWindow::VideoWindow(const char* title, window_type type, 399 uint32 flags, port_id* consumerPort) 400 : 401 BWindow(BRect(50, 50, 50, 50), title, type, flags), 402 fPortPtr(consumerPort), 403 fVideoView(NULL) 404 { 405 fFtpInfo.port = 0; 406 fFtpInfo.rate = 0x7fffffff; 407 fFtpInfo.imageFormat = 0; 408 fFtpInfo.translator = 0; 409 fFtpInfo.passiveFtp = true; 410 fFtpInfo.uploadClient = 0; 411 strcpy(fFtpInfo.fileNameText, "filename"); 412 strcpy(fFtpInfo.serverText, "server"); 413 strcpy(fFtpInfo.loginText, "login"); 414 strcpy(fFtpInfo.passwordText, "password"); 415 strcpy(fFtpInfo.directoryText, "directory"); 416 417 _SetUpSettings("codycam", ""); 418 419 BMenuBar* menuBar = new BMenuBar("menu bar"); 420 421 BMenuItem* menuItem; 422 fMenu = new BMenu(B_TRANSLATE("File")); 423 424 menuItem = new BMenuItem(B_TRANSLATE("Video settings"), 425 new BMessage(msg_video), 'P'); 426 menuItem->SetTarget(be_app); 427 fMenu->AddItem(menuItem); 428 429 fMenu->AddSeparatorItem(); 430 431 menuItem = new BMenuItem(B_TRANSLATE("Start video"), 432 new BMessage(msg_start), 'A'); 433 menuItem->SetTarget(be_app); 434 fMenu->AddItem(menuItem); 435 436 menuItem = new BMenuItem(B_TRANSLATE("Stop video"), 437 new BMessage(msg_stop), 'O'); 438 menuItem->SetTarget(be_app); 439 fMenu->AddItem(menuItem); 440 441 fMenu->AddSeparatorItem(); 442 443 menuItem = new BMenuItem(B_TRANSLATE("Quit"), 444 new BMessage(B_QUIT_REQUESTED), 'Q'); 445 menuItem->SetTarget(be_app); 446 fMenu->AddItem(menuItem); 447 448 menuBar->AddItem(fMenu); 449 450 /* add some controls */ 451 _BuildCaptureControls(); 452 453 BBox* box = new BBox("box"); 454 box->AddChild(fVideoView); 455 456 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 457 .Add(menuBar) 458 .AddGroup(B_VERTICAL) 459 .SetInsets(B_USE_WINDOW_SPACING) 460 .Add(box) 461 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) 462 .Add(fCaptureSetupBox) 463 .Add(fFtpSetupBox) 464 .End() 465 .Add(fStatusLine) 466 .End() 467 .AddGlue(); 468 469 Show(); 470 } 471 472 473 VideoWindow::~VideoWindow() 474 { 475 _QuitSettings(); 476 } 477 478 479 bool 480 VideoWindow::QuitRequested() 481 { 482 be_app->PostMessage(B_QUIT_REQUESTED); 483 return false; 484 } 485 486 487 void 488 VideoWindow::MessageReceived(BMessage* message) 489 { 490 BControl* control = NULL; 491 message->FindPointer((const char*)"source", (void **)&control); 492 493 switch (message->what) { 494 case msg_filename: 495 if (control != NULL) { 496 strlcpy(fFtpInfo.fileNameText, 497 ((BTextControl*)control)->Text(), 64); 498 FTPINFO("file is '%s'\n", fFtpInfo.fileNameText); 499 } 500 break; 501 502 case msg_rate_changed: { 503 int32 seconds; 504 message->FindInt32("seconds", &seconds); 505 if (seconds == 0) { 506 FTPINFO("never\n"); 507 fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT); 508 } else { 509 FTPINFO("%ld seconds\n", (long)seconds); 510 fFtpInfo.rate = (bigtime_t)(seconds * 1000000LL); 511 } 512 break; 513 } 514 515 case msg_translate: 516 message->FindInt32("be:type", (int32*)&(fFtpInfo.imageFormat)); 517 message->FindInt32("be:translator", &(fFtpInfo.translator)); 518 break; 519 520 case msg_upl_client: 521 message->FindInt32("client", &(fFtpInfo.uploadClient)); 522 FTPINFO("upl client = %" B_PRId32 "\n", fFtpInfo.uploadClient); 523 _UploadClientChanged(); 524 break; 525 526 case msg_server: 527 if (control != NULL) { 528 strlcpy(fFtpInfo.serverText, 529 ((BTextControl*)control)->Text(), 64); 530 FTPINFO("server = '%s'\n", fFtpInfo.serverText); 531 } 532 break; 533 534 case msg_login: 535 if (control != NULL) { 536 strlcpy(fFtpInfo.loginText, 537 ((BTextControl*)control)->Text(), 64); 538 FTPINFO("login = '%s'\n", fFtpInfo.loginText); 539 } 540 break; 541 542 case msg_password: 543 if (control != NULL) { 544 strlcpy(fFtpInfo.passwordText, 545 ((BTextControl*)control)->Text(), 64); 546 FTPINFO("password = '%s'\n", fFtpInfo.passwordText); 547 } 548 break; 549 550 case msg_directory: 551 if (control != NULL) { 552 strlcpy(fFtpInfo.directoryText, 553 ((BTextControl*)control)->Text(), 64); 554 FTPINFO("directory = '%s'\n", fFtpInfo.directoryText); 555 } 556 break; 557 558 case msg_passiveftp: 559 if (control != NULL) { 560 fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value(); 561 if (fFtpInfo.passiveFtp) 562 FTPINFO("using passive ftp\n"); 563 } 564 break; 565 566 default: 567 BWindow::MessageReceived(message); 568 return; 569 } 570 571 if (*fPortPtr) 572 write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info)); 573 574 } 575 576 577 BView* 578 VideoWindow::VideoView() 579 { 580 return fVideoView; 581 } 582 583 584 BStringView* 585 VideoWindow::StatusLine() 586 { 587 return fStatusLine; 588 } 589 590 591 void 592 VideoWindow::_BuildCaptureControls() 593 { 594 // a view to hold the video image 595 fVideoView = new BTextView(""); 596 fVideoView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); 597 fVideoView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); 598 fVideoView->MakeEditable(false); 599 fVideoView->MakeResizable(false); 600 fVideoView->MakeSelectable(false); 601 fVideoView->SetAlignment(B_ALIGN_CENTER); 602 fVideoView->SetInsets(0, VIDEO_SIZE_Y / 3, 0 , 0); 603 604 // Capture controls 605 fCaptureSetupBox = new BBox("Capture Controls", B_WILL_DRAW); 606 fCaptureSetupBox->SetLabel(B_TRANSLATE("Capture controls")); 607 608 BGridLayout *controlsLayout = new BGridLayout(B_USE_DEFAULT_SPACING, 0); 609 controlsLayout->SetInsets(10, 15, 5, 5); 610 fCaptureSetupBox->SetLayout(controlsLayout); 611 612 // file name 613 fFileName = new BTextControl("File Name", B_TRANSLATE("File name:"), 614 fFilenameSetting->Value(), new BMessage(msg_filename)); 615 fFileName->SetTarget(BMessenger(NULL, this)); 616 617 // format menu 618 fImageFormatMenu = new BPopUpMenu(B_TRANSLATE("Image Format Menu")); 619 BTranslationUtils::AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP); 620 fImageFormatMenu->SetTargetForItems(this); 621 622 if (fImageFormatSettings->Value() 623 && fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) { 624 fImageFormatMenu->FindItem( 625 fImageFormatSettings->Value())->SetMarked(true); 626 } else if (fImageFormatMenu->FindItem("JPEG image") != NULL) 627 fImageFormatMenu->FindItem("JPEG image")->SetMarked(true); 628 else 629 fImageFormatMenu->ItemAt(0)->SetMarked(true); 630 631 fImageFormatSelector = new BMenuField("Format", B_TRANSLATE("Format:"), 632 fImageFormatMenu); 633 634 // capture rate 635 fCaptureRateMenu = new BPopUpMenu(B_TRANSLATE("Capture Rate Menu")); 636 for (int32 i = 0; i < kCaptureRatesCount; i++) { 637 BMessage* itemMessage = new BMessage(msg_rate_changed); 638 itemMessage->AddInt32("seconds", kCaptureRates[i].seconds); 639 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRates[i].name, 640 itemMessage)); 641 } 642 fCaptureRateMenu->SetTargetForItems(this); 643 fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true); 644 fCaptureRateSelector = new BMenuField("Rate", B_TRANSLATE("Rate:"), 645 fCaptureRateMenu); 646 647 BLayoutBuilder::Grid<>(controlsLayout) 648 .AddTextControl(fFileName, 0, 0) 649 .AddMenuField(fImageFormatSelector, 0, 1) 650 .AddMenuField(fCaptureRateSelector, 0, 2) 651 .Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1); 652 653 // FTP setup box 654 fFtpSetupBox = new BBox("FTP Setup", B_WILL_DRAW); 655 fFtpSetupBox->SetLabel(B_TRANSLATE("Output")); 656 657 fUploadClientMenu = new BPopUpMenu(B_TRANSLATE("Send to" B_UTF8_ELLIPSIS)); 658 for (int i = 0; i < kUploadClientsCount; i++) { 659 BMessage *m = new BMessage(msg_upl_client); 660 m->AddInt32("client", i); 661 fUploadClientMenu->AddItem(new BMenuItem(kUploadClients[i], m)); 662 } 663 664 fUploadClientMenu->SetTargetForItems(this); 665 fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true); 666 fUploadClientSelector = new BMenuField("UploadClient", NULL, 667 fUploadClientMenu); 668 669 fUploadClientSelector->SetLabel(B_TRANSLATE("Type:")); 670 671 BGridLayout *ftpLayout = new BGridLayout(B_USE_DEFAULT_SPACING, 0); 672 ftpLayout->SetInsets(10, 15, 5, 5); 673 fFtpSetupBox->SetLayout(ftpLayout); 674 675 fServerName = new BTextControl("Server", B_TRANSLATE("Server:"), 676 fServerSetting->Value(), new BMessage(msg_server)); 677 fServerName->SetTarget(this); 678 679 fLoginId = new BTextControl("Login", B_TRANSLATE("Login:"), 680 fLoginSetting->Value(), new BMessage(msg_login)); 681 fLoginId->SetTarget(this); 682 683 fPassword = new BTextControl("Password", B_TRANSLATE("Password:"), 684 fPasswordSetting->Value(), new BMessage(msg_password)); 685 fPassword->SetTarget(this); 686 fPassword->TextView()->HideTyping(true); 687 // BeOS HideTyping() seems broken, it empties the text 688 fPassword->SetText(fPasswordSetting->Value()); 689 690 fDirectory = new BTextControl("Directory", B_TRANSLATE("Directory:"), 691 fDirectorySetting->Value(), new BMessage(msg_directory)); 692 fDirectory->SetTarget(this); 693 694 fPassiveFtp = new BCheckBox("Passive FTP", B_TRANSLATE("Passive FTP"), 695 new BMessage(msg_passiveftp)); 696 fPassiveFtp->SetTarget(this); 697 fPassiveFtp->SetValue(fPassiveFtpSetting->Value()); 698 699 BLayoutBuilder::Grid<>(ftpLayout) 700 .AddMenuField(fUploadClientSelector, 0, 0) 701 .AddTextControl(fServerName, 0, 1) 702 .AddTextControl(fLoginId, 0, 2) 703 .AddTextControl(fPassword, 0, 3) 704 .AddTextControl(fDirectory, 0, 4) 705 .Add(fPassiveFtp, 0, 5, 2, 1); 706 707 fStatusLine = new BStringView("Status Line", 708 B_TRANSLATE("Waiting" B_UTF8_ELLIPSIS)); 709 } 710 711 712 void 713 VideoWindow::ApplyControls() 714 { 715 if (!Lock()) 716 return; 717 718 // apply controls 719 fFileName->Invoke(); 720 PostMessage(fImageFormatMenu->FindMarked()->Message()); 721 PostMessage(fCaptureRateMenu->FindMarked()->Message()); 722 PostMessage(fUploadClientMenu->FindMarked()->Message()); 723 fServerName->Invoke(); 724 fLoginId->Invoke(); 725 fPassword->Invoke(); 726 fDirectory->Invoke(); 727 fPassiveFtp->Invoke(); 728 729 Unlock(); 730 } 731 732 733 void 734 VideoWindow::ErrorAlert(const char* message, status_t err) 735 { 736 Lock(); 737 fVideoView->SetText(message); 738 Unlock(); 739 740 printf("%s\n%s [%" B_PRIx32 "]", message, strerror(err), err); 741 } 742 743 744 void 745 VideoWindow::_SetUpSettings(const char* filename, const char* dirname) 746 { 747 fSettings = new Settings(filename, dirname); 748 749 fServerSetting = new StringValueSetting("Server", "ftp.my.server", 750 B_TRANSLATE("server address expected")); 751 752 fLoginSetting = new StringValueSetting("Login", "loginID", 753 B_TRANSLATE("login ID expected")); 754 755 fPasswordSetting = new StringValueSetting("Password", 756 B_TRANSLATE("password"), B_TRANSLATE("password expected")); 757 758 fDirectorySetting = new StringValueSetting("Directory", "web/images", 759 B_TRANSLATE("destination directory expected")); 760 761 fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1); 762 763 fFilenameSetting = new StringValueSetting("StillImageFilename", 764 "codycam.jpg", B_TRANSLATE("still image filename expected")); 765 766 fImageFormatSettings = new StringValueSetting("ImageFileFormat", 767 B_TRANSLATE("JPEG image"), B_TRANSLATE("image file format expected")); 768 769 fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate", 770 kCaptureRates[3].name, &CaptureRateAt, 771 B_TRANSLATE("capture rate expected"), 772 "unrecognized capture rate specified"); 773 774 fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient", 775 B_TRANSLATE("FTP"), &UploadClientAt, 776 B_TRANSLATE("upload client name expected"), 777 B_TRANSLATE("unrecognized upload client specified")); 778 779 fSettings->Add(fServerSetting); 780 fSettings->Add(fLoginSetting); 781 fSettings->Add(fPasswordSetting); 782 fSettings->Add(fDirectorySetting); 783 fSettings->Add(fPassiveFtpSetting); 784 fSettings->Add(fFilenameSetting); 785 fSettings->Add(fImageFormatSettings); 786 fSettings->Add(fCaptureRateSetting); 787 fSettings->Add(fUploadClientSetting); 788 789 fSettings->TryReadingSettings(); 790 } 791 792 793 void 794 VideoWindow::_UploadClientChanged() 795 { 796 bool enableServerControls = fFtpInfo.uploadClient < 2; 797 fServerName->SetEnabled(enableServerControls); 798 fLoginId->SetEnabled(enableServerControls); 799 fPassword->SetEnabled(enableServerControls); 800 fDirectory->SetEnabled(enableServerControls); 801 fPassiveFtp->SetEnabled(enableServerControls); 802 } 803 804 805 void 806 VideoWindow::_QuitSettings() 807 { 808 fServerSetting->ValueChanged(fServerName->Text()); 809 fLoginSetting->ValueChanged(fLoginId->Text()); 810 fPasswordSetting->ValueChanged(fFtpInfo.passwordText); 811 fDirectorySetting->ValueChanged(fDirectory->Text()); 812 fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value()); 813 fFilenameSetting->ValueChanged(fFileName->Text()); 814 fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label()); 815 fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label()); 816 fUploadClientSetting->ValueChanged(fUploadClientMenu->FindMarked()->Label()); 817 818 fSettings->SaveSettings(); 819 delete fSettings; 820 } 821 822 823 void 824 VideoWindow::ToggleMenuOnOff() 825 { 826 BMenuItem* item = fMenu->FindItem(msg_video); 827 item->SetEnabled(!item->IsEnabled()); 828 829 item = fMenu->FindItem(msg_start); 830 item->SetEnabled(!item->IsEnabled()); 831 832 item = fMenu->FindItem(msg_stop); 833 item->SetEnabled(!item->IsEnabled()); 834 } 835 836 837 // #pragma mark - 838 839 840 ControlWindow::ControlWindow(BView* controls, 841 media_node node) 842 : 843 BWindow(BRect(), B_TRANSLATE("Video settings"), B_TITLED_WINDOW, 844 B_ASYNCHRONOUS_CONTROLS) 845 { 846 fView = controls; 847 fNode = node; 848 849 AddChild(fView); 850 } 851 852 853 void 854 ControlWindow::MessageReceived(BMessage* message) 855 { 856 BParameterWeb* web = NULL; 857 status_t err; 858 859 switch (message->what) { 860 case B_MEDIA_WEB_CHANGED: 861 { 862 // If this is a tab view, find out which tab 863 // is selected 864 BTabView* tabView = dynamic_cast<BTabView*>(fView); 865 int32 tabNum = -1; 866 if (tabView) 867 tabNum = tabView->Selection(); 868 869 RemoveChild(fView); 870 delete fView; 871 872 err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web); 873 874 if (err >= B_OK && web != NULL) { 875 fView = BMediaTheme::ViewFor(web); 876 AddChild(fView); 877 878 // Another tab view? Restore previous selection 879 if (tabNum > 0) { 880 BTabView* newTabView = dynamic_cast<BTabView*>(fView); 881 if (newTabView) 882 newTabView->Select(tabNum); 883 } 884 } 885 break; 886 } 887 888 default: 889 BWindow::MessageReceived(message); 890 } 891 } 892 893 894 bool 895 ControlWindow::QuitRequested() 896 { 897 be_app->PostMessage(msg_control_win); 898 return true; 899 } 900 901 902 // #pragma mark - 903 904 905 int main() { 906 CodyCam app; 907 app.Run(); 908 return 0; 909 } 910 911