vfs: check userland buffers before reading them.
[haiku.git] / src / apps / codycam / CodyCam.cpp
blob77596698637cc1c36814bc7dc5fb6b1345c38761
1 #include "CodyCam.h"
3 #include <stdio.h>
4 #include <string.h>
5 #include <unistd.h>
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>
27 #undef B_TRANSLATION_CONTEXT
28 #define B_TRANSLATION_CONTEXT "CodyCam"
30 #define VIDEO_SIZE_X 320
31 #define VIDEO_SIZE_Y 240
33 #define WINDOW_SIZE_X (VIDEO_SIZE_X + 80)
34 #define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230)
36 #define WINDOW_OFFSET_X 28
37 #define WINDOW_OFFSET_Y 28
39 #define CALL printf
40 #define ERROR printf
41 #define FTPINFO printf
42 #define INFO printf
45 // Utility functions
47 namespace {
49 // functions for EnumeratedStringValueSettings
51 const char*
52 CaptureRateAt(int32 i)
54 return (i >= 0 && i < kCaptureRatesCount) ? kCaptureRates[i].name : NULL;
58 const char*
59 UploadClientAt(int32 i)
61 return (i >= 0 && i < kUploadClientsCount) ? kUploadClients[i] : NULL;
65 }; // end anonymous namespace
69 // #pragma mark -
72 CodyCam::CodyCam()
74 BApplication("application/x-vnd.Haiku-CodyCam"),
75 fMediaRoster(NULL),
76 fVideoConsumer(NULL),
77 fWindow(NULL),
78 fPort(0),
79 fVideoControlWindow(NULL)
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");
96 index = 0;
97 kUploadClients[index++] = B_TRANSLATE("FTP");
98 kUploadClients[index++] = B_TRANSLATE("SFTP");
99 kUploadClients[index++] = B_TRANSLATE("Local");
101 BPath homeDir;
102 if (find_directory(B_USER_DIRECTORY, &homeDir) != B_OK)
103 homeDir.SetTo("/boot/home");
105 chdir(homeDir.Path());
109 CodyCam::~CodyCam()
111 CALL("CodyCam::~CodyCam\n");
113 // release the video consumer node
114 // the consumer node cleans up the window
115 if (fVideoConsumer) {
116 fVideoConsumer->Release();
117 fVideoConsumer = NULL;
120 CALL("CodyCam::~CodyCam - EXIT\n");
124 void
125 CodyCam::ReadyToRun()
127 fWindow = new VideoWindow(
128 (const char*) B_TRANSLATE_SYSTEM_NAME("CodyCam"), B_TITLED_WINDOW,
129 B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE
130 | B_AUTO_UPDATE_SIZE_LIMITS, &fPort);
132 if (_SetUpNodes() != B_OK)
133 fWindow->ToggleMenuOnOff();
135 ((VideoWindow*)fWindow)->ApplyControls();
139 bool
140 CodyCam::QuitRequested()
142 _TearDownNodes();
143 snooze(100000);
145 return true;
149 void
150 CodyCam::MessageReceived(BMessage *message)
152 switch (message->what) {
153 case msg_start:
155 BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(
156 fTimeSourceNode);
157 bigtime_t real = BTimeSource::RealTime();
158 bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000;
159 status_t status = fMediaRoster->StartNode(fProducerNode, perf);
160 if (status != B_OK)
161 ERROR("error starting producer!");
162 timeSource->Release();
163 break;
166 case msg_stop:
167 fMediaRoster->StopNode(fProducerNode, 0, true);
168 break;
170 case msg_video:
172 if (fVideoControlWindow) {
173 fVideoControlWindow->Activate();
174 break;
176 BParameterWeb* web = NULL;
177 BView* view = NULL;
178 media_node node = fProducerNode;
179 status_t err = fMediaRoster->GetParameterWebFor(node, &web);
180 if (err >= B_OK && web != NULL) {
181 view = BMediaTheme::ViewFor(web);
182 fVideoControlWindow = new ControlWindow(view, node);
183 fMediaRoster->StartWatching(BMessenger(NULL,
184 fVideoControlWindow), node, B_MEDIA_WEB_CHANGED);
185 fVideoControlWindow->Show();
187 break;
190 case msg_control_win:
191 // our control window is being asked to go away
192 // set our pointer to NULL
193 fVideoControlWindow = NULL;
194 break;
196 default:
197 BApplication::MessageReceived(message);
198 break;
203 status_t
204 CodyCam::_SetUpNodes()
206 status_t status = B_OK;
208 /* find the media roster */
209 fMediaRoster = BMediaRoster::Roster(&status);
210 if (status != B_OK) {
211 fWindow->ErrorAlert(B_TRANSLATE("Cannot find the media roster"),
212 status);
213 return status;
216 /* find the time source */
217 status = fMediaRoster->GetTimeSource(&fTimeSourceNode);
218 if (status != B_OK) {
219 fWindow->ErrorAlert(B_TRANSLATE("Cannot get a time source"), status);
220 return status;
223 /* find a video producer node */
224 INFO("CodyCam acquiring VideoInput node\n");
225 status = fMediaRoster->GetVideoInput(&fProducerNode);
226 if (status != B_OK) {
227 fWindow->ErrorAlert(B_TRANSLATE("Cannot find a video source.\n"
228 "You need a webcam to use CodyCam."), status);
229 return status;
232 /* create the video consumer node */
233 fVideoConsumer = new VideoConsumer("CodyCam",
234 ((VideoWindow*)fWindow)->VideoView(),
235 ((VideoWindow*)fWindow)->StatusLine(), NULL, 0);
236 if (!fVideoConsumer) {
237 fWindow->ErrorAlert(B_TRANSLATE("Cannot create a video window"),
238 B_ERROR);
239 return B_ERROR;
242 /* register the node */
243 status = fMediaRoster->RegisterNode(fVideoConsumer);
244 if (status != B_OK) {
245 fWindow->ErrorAlert(B_TRANSLATE("Cannot register the video window"),
246 status);
247 return status;
249 fPort = fVideoConsumer->ControlPort();
251 /* find free producer output */
252 int32 cnt = 0;
253 status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1,
254 &cnt, B_MEDIA_RAW_VIDEO);
255 if (status != B_OK || cnt < 1) {
256 status = B_RESOURCE_UNAVAILABLE;
257 fWindow->ErrorAlert(B_TRANSLATE("Cannot find an available video stream"),
258 status);
259 return status;
262 /* find free consumer input */
263 cnt = 0;
264 status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(),
265 &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO);
266 if (status != B_OK || cnt < 1) {
267 status = B_RESOURCE_UNAVAILABLE;
268 fWindow->ErrorAlert(B_TRANSLATE("Can't find an available connection to "
269 "the video window"), status);
270 return status;
273 /* Connect The Nodes!!! */
274 media_format format;
275 format.type = B_MEDIA_RAW_VIDEO;
276 media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT,
277 1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}};
278 format.u.raw_video = vid_format;
280 /* connect producer to consumer */
281 status = fMediaRoster->Connect(fProducerOut.source,
282 fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn);
283 if (status != B_OK) {
284 fWindow->ErrorAlert(B_TRANSLATE("Cannot connect the video source to "
285 "the video window"), status);
286 return status;
290 /* set time sources */
291 status = fMediaRoster->SetTimeSourceFor(fProducerNode.node,
292 fTimeSourceNode.node);
293 if (status != B_OK) {
294 fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the "
295 "video source"), status);
296 return status;
299 status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
300 fTimeSourceNode.node);
301 if (status != B_OK) {
302 fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the "
303 "video window"), status);
304 return status;
307 /* figure out what recording delay to use */
308 bigtime_t latency = 0;
309 status = fMediaRoster->GetLatencyFor(fProducerNode, &latency);
310 status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency);
312 /* start the nodes */
313 bigtime_t initLatency = 0;
314 status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency);
315 if (status < B_OK) {
316 fWindow->ErrorAlert(B_TRANSLATE("Error getting initial latency for the "
317 "capture node"), status);
318 return status;
321 initLatency += estimate_max_scheduling_latency();
323 BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode);
324 bool running = timeSource->IsRunning();
326 /* workaround for people without sound cards */
327 /* because the system time source won't be running */
328 bigtime_t real = BTimeSource::RealTime();
329 if (!running) {
330 status = fMediaRoster->StartTimeSource(fTimeSourceNode, real);
331 if (status != B_OK) {
332 timeSource->Release();
333 fWindow->ErrorAlert(B_TRANSLATE("Cannot start time source!"),
334 status);
335 return status;
337 status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real);
338 if (status != B_OK) {
339 timeSource->Release();
340 fWindow->ErrorAlert(B_TRANSLATE("Cannot seek time source!"),
341 status);
342 return status;
346 bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
347 + initLatency);
348 timeSource->Release();
350 /* start the nodes */
351 status = fMediaRoster->StartNode(fProducerNode, perf);
352 if (status != B_OK) {
353 fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video source"),
354 status);
355 return status;
357 status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf);
358 if (status != B_OK) {
359 fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video window"),
360 status);
361 return status;
364 return status;
368 void
369 CodyCam::_TearDownNodes()
371 CALL("CodyCam::_TearDownNodes\n");
372 if (!fMediaRoster)
373 return;
375 if (fVideoConsumer) {
376 /* stop */
377 INFO("stopping nodes!\n");
378 // fMediaRoster->StopNode(fProducerNode, 0, true);
379 fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true);
381 /* disconnect */
382 fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source,
383 fConsumerIn.node.node, fConsumerIn.destination);
385 if (fProducerNode != media_node::null) {
386 INFO("CodyCam releasing fProducerNode\n");
387 fMediaRoster->ReleaseNode(fProducerNode);
388 fProducerNode = media_node::null;
390 fMediaRoster->ReleaseNode(fVideoConsumer->Node());
391 fVideoConsumer = NULL;
396 // #pragma mark - Video Window Class
399 VideoWindow::VideoWindow(const char* title, window_type type,
400 uint32 flags, port_id* consumerPort)
402 BWindow(BRect(50, 50, 50, 50), title, type, flags),
403 fPortPtr(consumerPort),
404 fVideoView(NULL)
406 fFtpInfo.port = 0;
407 fFtpInfo.rate = 0x7fffffff;
408 fFtpInfo.imageFormat = 0;
409 fFtpInfo.translator = 0;
410 fFtpInfo.passiveFtp = true;
411 fFtpInfo.uploadClient = 0;
412 strcpy(fFtpInfo.fileNameText, "filename");
413 strcpy(fFtpInfo.serverText, "server");
414 strcpy(fFtpInfo.loginText, "login");
415 strcpy(fFtpInfo.passwordText, "password");
416 strcpy(fFtpInfo.directoryText, "directory");
418 _SetUpSettings("codycam", "");
420 BMenuBar* menuBar = new BMenuBar("menu bar");
422 BMenuItem* menuItem;
423 fMenu = new BMenu(B_TRANSLATE("File"));
425 menuItem = new BMenuItem(B_TRANSLATE("Video settings"),
426 new BMessage(msg_video), 'P');
427 menuItem->SetTarget(be_app);
428 fMenu->AddItem(menuItem);
430 fMenu->AddSeparatorItem();
432 menuItem = new BMenuItem(B_TRANSLATE("Start video"),
433 new BMessage(msg_start), 'A');
434 menuItem->SetTarget(be_app);
435 fMenu->AddItem(menuItem);
437 menuItem = new BMenuItem(B_TRANSLATE("Stop video"),
438 new BMessage(msg_stop), 'O');
439 menuItem->SetTarget(be_app);
440 fMenu->AddItem(menuItem);
442 fMenu->AddSeparatorItem();
444 menuItem = new BMenuItem(B_TRANSLATE("Quit"),
445 new BMessage(B_QUIT_REQUESTED), 'Q');
446 menuItem->SetTarget(be_app);
447 fMenu->AddItem(menuItem);
449 menuBar->AddItem(fMenu);
451 /* add some controls */
452 _BuildCaptureControls();
454 BBox* box = new BBox("box");
455 BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
456 box->SetLayout(layout);
457 layout->SetInsets(2, 2, 2, 2);
458 box->AddChild(fVideoView);
459 box->AddChild(fErrorView);
461 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
462 .Add(menuBar)
463 .AddGroup(B_VERTICAL)
464 .SetInsets(B_USE_WINDOW_SPACING)
465 .AddGroup(B_HORIZONTAL)
466 .AddGlue()
467 .Add(box)
468 .AddGlue()
469 .End()
470 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
471 .Add(fCaptureSetupBox)
472 .Add(fFtpSetupBox)
473 .End()
474 .Add(fStatusLine)
475 .End()
476 .AddGlue();
478 Show();
482 VideoWindow::~VideoWindow()
484 _QuitSettings();
488 bool
489 VideoWindow::QuitRequested()
491 be_app->PostMessage(B_QUIT_REQUESTED);
492 return false;
496 void
497 VideoWindow::MessageReceived(BMessage* message)
499 BControl* control = NULL;
500 message->FindPointer((const char*)"source", (void **)&control);
502 switch (message->what) {
503 case msg_filename:
504 if (control != NULL) {
505 strlcpy(fFtpInfo.fileNameText,
506 ((BTextControl*)control)->Text(), 64);
507 FTPINFO("file is '%s'\n", fFtpInfo.fileNameText);
509 break;
511 case msg_rate_changed: {
512 int32 seconds;
513 message->FindInt32("seconds", &seconds);
514 if (seconds == 0) {
515 FTPINFO("never\n");
516 fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT);
517 } else {
518 FTPINFO("%ld seconds\n", (long)seconds);
519 fFtpInfo.rate = (bigtime_t)(seconds * 1000000LL);
521 break;
524 case msg_translate:
525 message->FindInt32("be:type", (int32*)&(fFtpInfo.imageFormat));
526 message->FindInt32("be:translator", &(fFtpInfo.translator));
527 break;
529 case msg_upl_client:
530 message->FindInt32("client", &(fFtpInfo.uploadClient));
531 FTPINFO("upl client = %" B_PRId32 "\n", fFtpInfo.uploadClient);
532 _UploadClientChanged();
533 break;
535 case msg_server:
536 if (control != NULL) {
537 strlcpy(fFtpInfo.serverText,
538 ((BTextControl*)control)->Text(), 64);
539 FTPINFO("server = '%s'\n", fFtpInfo.serverText);
541 break;
543 case msg_login:
544 if (control != NULL) {
545 strlcpy(fFtpInfo.loginText,
546 ((BTextControl*)control)->Text(), 64);
547 FTPINFO("login = '%s'\n", fFtpInfo.loginText);
549 break;
551 case msg_password:
552 if (control != NULL) {
553 strlcpy(fFtpInfo.passwordText,
554 ((BTextControl*)control)->Text(), 64);
555 FTPINFO("password = '%s'\n", fFtpInfo.passwordText);
557 break;
559 case msg_directory:
560 if (control != NULL) {
561 strlcpy(fFtpInfo.directoryText,
562 ((BTextControl*)control)->Text(), 64);
563 FTPINFO("directory = '%s'\n", fFtpInfo.directoryText);
565 break;
567 case msg_passiveftp:
568 if (control != NULL) {
569 fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value();
570 if (fFtpInfo.passiveFtp)
571 FTPINFO("using passive ftp\n");
573 break;
575 default:
576 BWindow::MessageReceived(message);
577 return;
580 if (*fPortPtr)
581 write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info));
586 BView*
587 VideoWindow::VideoView()
589 return fVideoView;
593 BStringView*
594 VideoWindow::StatusLine()
596 return fStatusLine;
600 void
601 VideoWindow::_BuildCaptureControls()
603 // a view to hold the video image
604 fVideoView = new BView("Video preview", B_WILL_DRAW);
605 fVideoView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
606 fVideoView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
608 fErrorView = new BTextView("error");
609 fErrorView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
611 // Capture controls
612 BGridLayout *controlsLayout = new BGridLayout(B_USE_DEFAULT_SPACING,
613 B_USE_SMALL_SPACING);
614 controlsLayout->SetInsets(B_USE_SMALL_SPACING);
616 BView* controlView = new BView("Controls", B_SUPPORTS_LAYOUT, NULL);
617 controlView->SetLayout(controlsLayout);
619 fCaptureSetupBox = new BBox("Capture Controls", B_WILL_DRAW);
620 fCaptureSetupBox->SetLabel(B_TRANSLATE("Capture controls"));
621 fCaptureSetupBox->AddChild(controlView);
623 // file name
624 fFileName = new BTextControl("File Name", B_TRANSLATE("File name:"),
625 fFilenameSetting->Value(), new BMessage(msg_filename));
626 fFileName->SetTarget(BMessenger(NULL, this));
628 // format menu
629 fImageFormatMenu = new BPopUpMenu(B_TRANSLATE("Image Format Menu"));
630 BTranslationUtils::AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP);
631 fImageFormatMenu->SetTargetForItems(this);
633 if (fImageFormatSettings->Value()
634 && fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) {
635 fImageFormatMenu->FindItem(
636 fImageFormatSettings->Value())->SetMarked(true);
637 } else if (fImageFormatMenu->FindItem("JPEG image") != NULL)
638 fImageFormatMenu->FindItem("JPEG image")->SetMarked(true);
639 else
640 fImageFormatMenu->ItemAt(0)->SetMarked(true);
642 fImageFormatSelector = new BMenuField("Format", B_TRANSLATE("Format:"),
643 fImageFormatMenu);
645 // capture rate
646 fCaptureRateMenu = new BPopUpMenu(B_TRANSLATE("Capture Rate Menu"));
647 for (int32 i = 0; i < kCaptureRatesCount; i++) {
648 BMessage* itemMessage = new BMessage(msg_rate_changed);
649 itemMessage->AddInt32("seconds", kCaptureRates[i].seconds);
650 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRates[i].name,
651 itemMessage));
653 fCaptureRateMenu->SetTargetForItems(this);
654 fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true);
655 fCaptureRateSelector = new BMenuField("Rate", B_TRANSLATE("Rate:"),
656 fCaptureRateMenu);
658 BLayoutBuilder::Grid<>(controlsLayout)
659 .AddTextControl(fFileName, 0, 0)
660 .AddMenuField(fImageFormatSelector, 0, 1)
661 .AddMenuField(fCaptureRateSelector, 0, 2)
662 .Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1);
664 // FTP setup box
665 BGridLayout* ftpLayout = new BGridLayout(B_USE_DEFAULT_SPACING,
666 B_USE_SMALL_SPACING);
667 ftpLayout->SetInsets(B_USE_SMALL_SPACING);
669 BView* outputView = new BView("Output", B_SUPPORTS_LAYOUT, NULL);
670 outputView->SetLayout(ftpLayout);
672 fFtpSetupBox = new BBox("FTP Setup", B_WILL_DRAW);
673 fFtpSetupBox->SetLabel(B_TRANSLATE("Output"));
674 fFtpSetupBox->AddChild(outputView);
675 float minWidth = be_plain_font->StringWidth(
676 "The server label plus ftp.reasonably.com");
677 fFtpSetupBox->SetExplicitMinSize(BSize(minWidth, B_SIZE_UNSET));
678 fFtpSetupBox->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
680 fUploadClientMenu = new BPopUpMenu(B_TRANSLATE("Send to" B_UTF8_ELLIPSIS));
681 for (int i = 0; i < kUploadClientsCount; i++) {
682 BMessage *m = new BMessage(msg_upl_client);
683 m->AddInt32("client", i);
684 fUploadClientMenu->AddItem(new BMenuItem(kUploadClients[i], m));
687 fUploadClientMenu->SetTargetForItems(this);
688 fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true);
690 fUploadClientSelector = new BMenuField("UploadClient", NULL,
691 fUploadClientMenu);
693 fUploadClientSelector->SetLabel(B_TRANSLATE("Type:"));
695 fServerName = new BTextControl("Server", B_TRANSLATE("Server:"),
696 fServerSetting->Value(), new BMessage(msg_server));
697 fServerName->SetTarget(this);
699 fLoginId = new BTextControl("Login", B_TRANSLATE("Login:"),
700 fLoginSetting->Value(), new BMessage(msg_login));
701 fLoginId->SetTarget(this);
703 fPassword = new BTextControl("Password", B_TRANSLATE("Password:"),
704 fPasswordSetting->Value(), new BMessage(msg_password));
705 fPassword->SetTarget(this);
706 fPassword->TextView()->HideTyping(true);
707 // BeOS HideTyping() seems broken, it empties the text
708 fPassword->SetText(fPasswordSetting->Value());
710 fDirectory = new BTextControl("Directory", B_TRANSLATE("Directory:"),
711 fDirectorySetting->Value(), new BMessage(msg_directory));
712 fDirectory->SetTarget(this);
714 fPassiveFtp = new BCheckBox("Passive FTP", B_TRANSLATE("Passive FTP"),
715 new BMessage(msg_passiveftp));
716 fPassiveFtp->SetTarget(this);
717 fPassiveFtp->SetValue(fPassiveFtpSetting->Value());
718 fPassiveFtp->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
720 BLayoutBuilder::Grid<>(ftpLayout)
721 .AddMenuField(fUploadClientSelector, 0, 0)
722 .AddTextControl(fServerName, 0, 1)
723 .AddTextControl(fLoginId, 0, 2)
724 .AddTextControl(fPassword, 0, 3)
725 .AddTextControl(fDirectory, 0, 4)
726 .Add(fPassiveFtp, 0, 5, 2, 1);
728 fStatusLine = new BStringView("Status Line",
729 B_TRANSLATE("Waiting" B_UTF8_ELLIPSIS));
730 fStatusLine->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
734 void
735 VideoWindow::ApplyControls()
737 if (!Lock())
738 return;
740 // apply controls
741 fFileName->Invoke();
742 PostMessage(fImageFormatMenu->FindMarked()->Message());
743 PostMessage(fCaptureRateMenu->FindMarked()->Message());
744 PostMessage(fUploadClientMenu->FindMarked()->Message());
745 fServerName->Invoke();
746 fLoginId->Invoke();
747 fPassword->Invoke();
748 fDirectory->Invoke();
749 fPassiveFtp->Invoke();
751 Unlock();
755 void
756 VideoWindow::ErrorAlert(const char* message, status_t err)
758 Lock();
759 fErrorView->SetText(message);
760 fErrorView->MakeEditable(false);
761 fErrorView->MakeSelectable(false);
762 fErrorView->SetWordWrap(true);
763 fErrorView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
764 fErrorView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
765 fErrorView->Show();
766 fVideoView->Hide();
767 Unlock();
769 printf("%s\n%s [%" B_PRIx32 "]", message, strerror(err), err);
773 void
774 VideoWindow::_SetUpSettings(const char* filename, const char* dirname)
776 fSettings = new Settings(filename, dirname);
778 fServerSetting = new StringValueSetting("Server", "ftp.my.server",
779 B_TRANSLATE("server address expected"));
781 fLoginSetting = new StringValueSetting("Login", "loginID",
782 B_TRANSLATE("login ID expected"));
784 fPasswordSetting = new StringValueSetting("Password",
785 B_TRANSLATE("password"), B_TRANSLATE("password expected"));
787 fDirectorySetting = new StringValueSetting("Directory", "web/images",
788 B_TRANSLATE("destination directory expected"));
790 fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1);
792 fFilenameSetting = new StringValueSetting("StillImageFilename",
793 "codycam.jpg", B_TRANSLATE("still image filename expected"));
795 fImageFormatSettings = new StringValueSetting("ImageFileFormat",
796 B_TRANSLATE("JPEG image"), B_TRANSLATE("image file format expected"));
798 fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate",
799 kCaptureRates[3].name, &CaptureRateAt,
800 B_TRANSLATE("capture rate expected"),
801 "unrecognized capture rate specified");
803 fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient",
804 B_TRANSLATE("FTP"), &UploadClientAt,
805 B_TRANSLATE("upload client name expected"),
806 B_TRANSLATE("unrecognized upload client specified"));
808 fSettings->Add(fServerSetting);
809 fSettings->Add(fLoginSetting);
810 fSettings->Add(fPasswordSetting);
811 fSettings->Add(fDirectorySetting);
812 fSettings->Add(fPassiveFtpSetting);
813 fSettings->Add(fFilenameSetting);
814 fSettings->Add(fImageFormatSettings);
815 fSettings->Add(fCaptureRateSetting);
816 fSettings->Add(fUploadClientSetting);
818 fSettings->TryReadingSettings();
822 void
823 VideoWindow::_UploadClientChanged()
825 bool enableServerControls = fFtpInfo.uploadClient < 2;
826 fServerName->SetEnabled(enableServerControls);
827 fLoginId->SetEnabled(enableServerControls);
828 fPassword->SetEnabled(enableServerControls);
829 fDirectory->SetEnabled(enableServerControls);
830 fPassiveFtp->SetEnabled(enableServerControls);
834 void
835 VideoWindow::_QuitSettings()
837 fServerSetting->ValueChanged(fServerName->Text());
838 fLoginSetting->ValueChanged(fLoginId->Text());
839 fPasswordSetting->ValueChanged(fFtpInfo.passwordText);
840 fDirectorySetting->ValueChanged(fDirectory->Text());
841 fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value());
842 fFilenameSetting->ValueChanged(fFileName->Text());
843 fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label());
844 fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label());
845 fUploadClientSetting->ValueChanged(fUploadClientMenu->FindMarked()->Label());
847 fSettings->SaveSettings();
848 delete fSettings;
852 void
853 VideoWindow::ToggleMenuOnOff()
855 BMenuItem* item = fMenu->FindItem(msg_video);
856 item->SetEnabled(!item->IsEnabled());
858 item = fMenu->FindItem(msg_start);
859 item->SetEnabled(!item->IsEnabled());
861 item = fMenu->FindItem(msg_stop);
862 item->SetEnabled(!item->IsEnabled());
866 // #pragma mark -
869 ControlWindow::ControlWindow(BView* controls,
870 media_node node)
872 BWindow(BRect(), B_TRANSLATE("Video settings"), B_TITLED_WINDOW,
873 B_ASYNCHRONOUS_CONTROLS)
875 fView = controls;
876 fNode = node;
878 AddChild(fView);
882 void
883 ControlWindow::MessageReceived(BMessage* message)
885 BParameterWeb* web = NULL;
886 status_t err;
888 switch (message->what) {
889 case B_MEDIA_WEB_CHANGED:
891 // If this is a tab view, find out which tab
892 // is selected
893 BTabView* tabView = dynamic_cast<BTabView*>(fView);
894 int32 tabNum = -1;
895 if (tabView)
896 tabNum = tabView->Selection();
898 RemoveChild(fView);
899 delete fView;
901 err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web);
903 if (err >= B_OK && web != NULL) {
904 fView = BMediaTheme::ViewFor(web);
905 AddChild(fView);
907 // Another tab view? Restore previous selection
908 if (tabNum > 0) {
909 BTabView* newTabView = dynamic_cast<BTabView*>(fView);
910 if (newTabView)
911 newTabView->Select(tabNum);
914 break;
917 default:
918 BWindow::MessageReceived(message);
923 bool
924 ControlWindow::QuitRequested()
926 be_app->PostMessage(msg_control_win);
927 return true;
931 // #pragma mark -
934 int main() {
935 CodyCam app;
936 app.Run();
937 return 0;