diff --git a/src/prefs/codycam/CodyCam.cpp b/src/prefs/codycam/CodyCam.cpp new file mode 100644 index 0000000000..36c9bc9b6c --- /dev/null +++ b/src/prefs/codycam/CodyCam.cpp @@ -0,0 +1,921 @@ +#include "CodyCam.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define VIDEO_SIZE_X 320 +#define VIDEO_SIZE_Y 240 + +#define WINDOW_SIZE_X (VIDEO_SIZE_X + 80) +#define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230) + +#define WINDOW_OFFSET_X 28 +#define WINDOW_OFFSET_Y 28 + +const int32 kBtnHeight = 20; +const int32 kBtnWidth = 60; +const int32 kBtnBuffer = 25; +const int32 kXBuffer = 10; +const int32 kYBuffer = 10; +const int32 kMenuHeight = 15; +const int32 kButtonHeight = 15; +const int32 kSliderViewRectHeight = 40; + +const rgb_color kViewGray = { 216, 216, 216, 255}; + +static void ErrorAlert(const char * message, status_t err); +static status_t AddTranslationItems( BMenu * intoMenu, uint32 from_type); + +#define CALL printf +#define ERROR printf +#define FTPINFO printf +#define INFO printf + +//--------------------------------------------------------------- +// The Application +//--------------------------------------------------------------- + +int main() { + chdir("/boot/home"); + CodyCam app; + app.Run(); + return 0; +} + +//--------------------------------------------------------------- + +CodyCam::CodyCam() : + BApplication("application/x-vnd.Be.CodyCam"), + fMediaRoster(NULL), + fVideoConsumer(NULL), + fWindow(NULL), + fPort(0), + mVideoControlWindow(NULL) +{ +} + +//--------------------------------------------------------------- + +CodyCam::~CodyCam() +{ + CALL("CodyCam::~CodyCam\n"); + + // release the video consumer node + // the consumer node cleans up the window + if (fVideoConsumer) { + fVideoConsumer->Release(); + fVideoConsumer = NULL; + } + + CALL("CodyCam::~CodyCam - EXIT\n"); +} + +//--------------------------------------------------------------- + +void +CodyCam::ReadyToRun() +{ + /* create the window for the app */ + uint32 x = WINDOW_SIZE_X; + uint32 y = WINDOW_SIZE_Y; + + fWindow = new VideoWindow(BRect(28, 28, 28 + (WINDOW_SIZE_X-1), 28 + (WINDOW_SIZE_Y-1)), + (const char *)"CodyCam", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE, &fPort); + + /* set up the node connections */ + status_t status = SetUpNodes(); + if (status != B_OK) + { + ErrorAlert("Error setting up nodes", status); + return; + } + + ((VideoWindow *)fWindow)->ApplyControls(); + +} + +//--------------------------------------------------------------- + +bool +CodyCam::QuitRequested() +{ + TearDownNodes(); + snooze(100000); + + return true; +} + +//--------------------------------------------------------------- + +void +CodyCam::MessageReceived(BMessage *message) +{ + status_t status; + + switch (message->what) + { + case msg_start: + { + BTimeSource *timeSource = fMediaRoster->MakeTimeSourceFor(fTimeSourceNode); + bigtime_t real = BTimeSource::RealTime(); + bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000; + status_t status = fMediaRoster->StartNode(fProducerNode, perf); + if (status != B_OK) + ERROR("error starting producer!"); + timeSource->Release(); + break; + } + case msg_stop: + fMediaRoster->StopNode(fProducerNode, 0, true); + break; + case msg_video: + { + if (mVideoControlWindow) { + mVideoControlWindow->Activate(); + break; + } + BParameterWeb * web = NULL; + BView * view = NULL; + media_node node = fProducerNode; + status_t err = fMediaRoster->GetParameterWebFor(node, &web); + if ((err >= B_OK) && + (web != NULL)) + { + view = BMediaTheme::ViewFor(web); + mVideoControlWindow = new ControlWindow( + BRect(2*WINDOW_OFFSET_X + WINDOW_SIZE_X, WINDOW_OFFSET_Y, + 2*WINDOW_OFFSET_X + WINDOW_SIZE_X + view->Bounds().right, WINDOW_OFFSET_Y + view->Bounds().bottom), + view, node); + fMediaRoster->StartWatching(BMessenger(NULL, mVideoControlWindow), node, B_MEDIA_WEB_CHANGED); + mVideoControlWindow->Show(); + } + break; + } + case msg_about: + { + (new BAlert("About CodyCam", "CodyCam\n\nThe Original BeOS WebCam", "Close"))->Go(); + break; + } + + case msg_control_win: + { + // our control window is being asked to go away + // set our pointer to NULL + mVideoControlWindow = NULL; + break; + + } + + default: + BApplication::MessageReceived(message); + break; + } +} + +//--------------------------------------------------------------- + +status_t +CodyCam::SetUpNodes() +{ + status_t status = B_OK; + + /* find the media roster */ + fMediaRoster = BMediaRoster::Roster(&status); + if (status != B_OK) { + ErrorAlert("Can't find the media roster", status); + return status; + } + /* find the time source */ + status = fMediaRoster->GetTimeSource(&fTimeSourceNode); + if (status != B_OK) { + ErrorAlert("Can't get a time source", status); + return status; + } + /* find a video producer node */ + INFO("CodyCam acquiring VideoInput node\n"); + status = fMediaRoster->GetVideoInput(&fProducerNode); + if (status != B_OK) { + ErrorAlert("Can't find a video input!", status); + return status; + } + + /* create the video consumer node */ + fVideoConsumer = new VideoConsumer("CodyCam", ((VideoWindow *)fWindow)->VideoView(), ((VideoWindow *)fWindow)->StatusLine(), NULL, 0); + if (!fVideoConsumer) { + ErrorAlert("Can't create a video window", B_ERROR); + return B_ERROR; + } + + /* register the node */ + status = fMediaRoster->RegisterNode(fVideoConsumer); + if (status != B_OK) { + ErrorAlert("Can't register the video window", status); + return status; + } + fPort = fVideoConsumer->ControlPort(); + + /* find free producer output */ + int32 cnt = 0; + status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1, &cnt, B_MEDIA_RAW_VIDEO); + if (status != B_OK || cnt < 1) { + status = B_RESOURCE_UNAVAILABLE; + ErrorAlert("Can't find an available video stream", status); + return status; + } + + /* find free consumer input */ + cnt = 0; + status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(), &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO); + if (status != B_OK || cnt < 1) { + status = B_RESOURCE_UNAVAILABLE; + ErrorAlert("Can't find an available connection to the video window", status); + return status; + } + + /* Connect The Nodes!!! */ + media_format format; + format.type = B_MEDIA_RAW_VIDEO; + media_raw_video_format vid_format = + { 0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT, 1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X*4, 0, 0}}; + format.u.raw_video = vid_format; + + /* connect producer to consumer */ + status = fMediaRoster->Connect(fProducerOut.source, fConsumerIn.destination, + &format, &fProducerOut, &fConsumerIn); + if (status != B_OK) { + ErrorAlert("Can't connect the video source to the video window", status); + return status; + } + + /* set time sources */ + status = fMediaRoster->SetTimeSourceFor(fProducerNode.node, fTimeSourceNode.node); + if (status != B_OK) { + ErrorAlert("Can't set the timesource for the video source", status); + return status; + } + + status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(), fTimeSourceNode.node); + if (status != B_OK) { + ErrorAlert("Can't set the timesource for the video window", status); + return status; + } + + /* figure out what recording delay to use */ + bigtime_t latency = 0; + status = fMediaRoster->GetLatencyFor(fProducerNode, &latency); + status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency); + + /* start the nodes */ + bigtime_t initLatency = 0; + status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency); + if (status < B_OK) { + ErrorAlert("error getting initial latency for fCaptureNode", status); + } + initLatency += estimate_max_scheduling_latency(); + + BTimeSource *timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode); + bool running = timeSource->IsRunning(); + + /* workaround for people without sound cards */ + /* because the system time source won't be running */ + bigtime_t real = BTimeSource::RealTime(); + if (!running) + { + status = fMediaRoster->StartTimeSource(fTimeSourceNode, real); + if (status != B_OK) { + timeSource->Release(); + ErrorAlert("cannot start time source!", status); + return status; + } + status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real); + if (status != B_OK) { + timeSource->Release(); + ErrorAlert("cannot seek time source!", status); + return status; + } + } + + bigtime_t perf = timeSource->PerformanceTimeFor(real + latency + initLatency); + timeSource->Release(); + + /* start the nodes */ + status = fMediaRoster->StartNode(fProducerNode, perf); + if (status != B_OK) { + ErrorAlert("Can't start the video source", status); + return status; + } + status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf); + if (status != B_OK) { + ErrorAlert("Can't start the video window", status); + return status; + } + + return status; +} + +//--------------------------------------------------------------- + +void +CodyCam::TearDownNodes() +{ + CALL("CodyCam::TearDownNodes\n"); + if (!fMediaRoster) + return; + + if (fVideoConsumer) + { + /* stop */ + INFO("stopping nodes!\n"); +// fMediaRoster->StopNode(fProducerNode, 0, true); + fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true); + + /* disconnect */ + fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source, + fConsumerIn.node.node, fConsumerIn.destination); + + if (fProducerNode != media_node::null) { + INFO("CodyCam releasing fProducerNode\n"); + fMediaRoster->ReleaseNode(fProducerNode); + fProducerNode = media_node::null; + } + fMediaRoster->ReleaseNode(fVideoConsumer->Node()); + fVideoConsumer = NULL; + } +} + +//--------------------------------------------------------------- +// Utility functions +//--------------------------------------------------------------- + +static void +ErrorAlert(const char * message, status_t err) +{ + char msg[256]; + sprintf(msg, "%s\n%s [%x]", message, strerror(err), err); + (new BAlert("", msg, "Quit"))->Go(); + be_app->PostMessage(B_QUIT_REQUESTED); +} + +//--------------------------------------------------------------- + +status_t +AddTranslationItems( BMenu * intoMenu, uint32 from_type) +{ + + BTranslatorRoster * use; + char * translator_type_name; + const char * translator_id_name; + + use = BTranslatorRoster::Default(); + translator_id_name = "be:translator"; + translator_type_name = "be:type"; + translator_id * ids = NULL; + int32 count = 0; + + status_t err = use->GetAllTranslators(&ids, &count); + if (err < B_OK) return err; + for (int tix=0; tixGetInputFormats(ids[tix], &formats, &num_formats); + if (err == B_OK) for (int iix=0; iixGetOutputFormats(ids[tix], &formats, &num_formats); + if (err == B_OK) for (int oix=0; oixAddInt32(translator_id_name, ids[tix]); + itemmsg->AddInt32(translator_type_name, formats[oix].type); + intoMenu->AddItem(new BMenuItem(formats[oix].name, itemmsg)); + } + } + } + delete[] ids; + return B_OK; +} + +//--------------------------------------------------------------- +// Video Window Class +//--------------------------------------------------------------- + +VideoWindow::VideoWindow (BRect frame, const char *title, window_type type, uint32 flags, port_id * consumerport) : + BWindow(frame,title,type,flags), + fView(NULL), + fVideoView(NULL), + fPortPtr(consumerport) +{ + fFtpInfo.port = 0; + fFtpInfo.rate = 0x7fffffff; + fFtpInfo.imageFormat = 0; + fFtpInfo.translator = 0; + fFtpInfo.passiveFtp = true; + strcpy(fFtpInfo.fileNameText, "filename"); + strcpy(fFtpInfo.serverText, "server"); + strcpy(fFtpInfo.loginText, "login"); + strcpy(fFtpInfo.passwordText, "password"); + strcpy(fFtpInfo.directoryText, "directory"); + + SetUpSettings("codycam", ""); + + BMenuBar* menuBar = new BMenuBar(BRect(0,0,0,0), "menu bar"); + AddChild(menuBar); + + BMenuItem* menuItem; + BMenu* menu = new BMenu("File"); + + menuItem = new BMenuItem("Video Preferences", new BMessage(msg_video), 'P'); + menuItem->SetTarget(be_app); + menu->AddItem(menuItem); + + menu->AddSeparatorItem(); + + menuItem = new BMenuItem("Start Video", new BMessage(msg_start), 'A'); + menuItem->SetTarget(be_app); + menu->AddItem(menuItem); + + menuItem = new BMenuItem("Stop Video", new BMessage(msg_stop), 'O'); + menuItem->SetTarget(be_app); + menu->AddItem(menuItem); + + menu->AddSeparatorItem(); + + menuItem = new BMenuItem("About Codycam", new BMessage(msg_about), 'B'); + menuItem->SetTarget(be_app); + menu->AddItem(menuItem); + + menu->AddSeparatorItem(); + + menuItem = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'); + menuItem->SetTarget(be_app); + menu->AddItem(menuItem); + + menuBar->AddItem(menu); + + /* give it a gray background view */ + BRect aRect; + aRect = Frame(); + aRect.OffsetTo(B_ORIGIN); + aRect.top += menuBar->Frame().Height() + 1; + fView = new BView(aRect, "Background View", B_FOLLOW_ALL, B_WILL_DRAW); + fView->SetViewColor(kViewGray); + AddChild(fView); + + /* add some controls */ + BuildCaptureControls(fView); + + /* add another view to hold the video image */ + aRect = BRect(0, 0, VIDEO_SIZE_X - 1, VIDEO_SIZE_Y - 1); + aRect.OffsetBy((WINDOW_SIZE_X - VIDEO_SIZE_X)/2, kYBuffer); + + fVideoView = new BView(aRect, "Video View", B_FOLLOW_ALL, B_WILL_DRAW); + fView->AddChild(fVideoView); + + Show(); +} + +//--------------------------------------------------------------- + +VideoWindow::~VideoWindow() +{ + QuitSettings(); +} + +//--------------------------------------------------------------- + +bool +VideoWindow::QuitRequested() +{ + be_app->PostMessage(B_QUIT_REQUESTED); + return false; +} + +//--------------------------------------------------------------- + +void +VideoWindow::MessageReceived(BMessage *message) +{ + BControl *p; + uint32 index; + + p = NULL; + message->FindPointer((const char *)"source",(void **)&p); + + switch (message->what) + { + case msg_filename: + if (p != NULL) + { + strncpy(fFtpInfo.fileNameText, ((BTextControl *)p)->Text(), 63); + FTPINFO("file is '%s'\n", fFtpInfo.fileNameText); + } + break; + case msg_rate_15s: + FTPINFO("fifteen seconds\n"); + fFtpInfo.rate = (bigtime_t)(15 * 1000000); + break; + case msg_rate_30s: + FTPINFO("thirty seconds\n"); + fFtpInfo.rate = (bigtime_t)(30 * 1000000); + break; + case msg_rate_1m: + FTPINFO("one minute\n"); + fFtpInfo.rate = (bigtime_t)(1 * 60 * 1000000); + break; + case msg_rate_5m: + FTPINFO("five minute\n"); + fFtpInfo.rate = (bigtime_t)(5 * 60 * 1000000); + break; + case msg_rate_10m: + FTPINFO("ten minute\n"); + fFtpInfo.rate = (bigtime_t)(10 * 60 * 1000000); + break; + case msg_rate_15m: + FTPINFO("fifteen minute\n"); + fFtpInfo.rate = (bigtime_t)(15 * 60 * 1000000); + break; + case msg_rate_30m: + FTPINFO("thirty minute\n"); + fFtpInfo.rate = (bigtime_t)(30 * 60 * 1000000); + break; + case msg_rate_1h: + FTPINFO("one hour\n"); + fFtpInfo.rate = (bigtime_t)(60LL * 60LL * 1000000LL); + break; + case msg_rate_2h: + FTPINFO("two hour\n"); + fFtpInfo.rate = (bigtime_t)(2LL * 60LL * 60LL * 1000000LL); + break; + case msg_rate_4h: + FTPINFO("four hour\n"); + fFtpInfo.rate = (bigtime_t)(4LL * 60LL * 60LL * 1000000LL); + break; + case msg_rate_8h: + FTPINFO("eight hour\n"); + fFtpInfo.rate = (bigtime_t)(8LL * 60LL * 60LL * 1000000LL); + break; + case msg_rate_24h: + FTPINFO("24 hour\n"); + fFtpInfo.rate = (bigtime_t)(24LL * 60LL * 60LL * 1000000LL); + break; + case msg_rate_never: + FTPINFO("never\n"); + fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT); + break; + case msg_translate: + message->FindInt32("be:type", (int32 *)&(fFtpInfo.imageFormat)); + message->FindInt32("be:translator", &(fFtpInfo.translator)); + break; + case msg_server: + if (p != NULL) + { + strncpy(fFtpInfo.serverText, ((BTextControl *)p)->Text(), 64); + FTPINFO("server = '%s'\n", fFtpInfo.serverText); + } + break; + case msg_login: + if (p != NULL) + { + strncpy(fFtpInfo.loginText, ((BTextControl *)p)->Text(), 64); + FTPINFO("login = '%s'\n", fFtpInfo.loginText); + } + break; + case msg_password: + if (p != NULL) + { + strncpy(fFtpInfo.passwordText, ((BTextControl *)p)->Text(), 64); + FTPINFO("password = '%s'\n", fFtpInfo.passwordText); + if (Lock()) + { + ((BTextControl *)p)->SetText(""); + Unlock(); + } + } + break; + case msg_directory: + if (p != NULL) + { + strncpy(fFtpInfo.directoryText, ((BTextControl *)p)->Text(), 64); + FTPINFO("directory = '%s'\n", fFtpInfo.directoryText); + } + break; + case msg_passiveftp: + if (p != NULL) + { + fFtpInfo.passiveFtp = ((BCheckBox *)p)->Value(); + if (fFtpInfo.passiveFtp) + FTPINFO("using passive ftp\n"); + } + break; + default: + BWindow::MessageReceived(message); + return; + } + + if (*fPortPtr) + write_port(*fPortPtr, FTP_INFO, (void *)&fFtpInfo, sizeof(ftp_msg_info)); + +} + +//--------------------------------------------------------------- + +BView * +VideoWindow::VideoView() +{ + return fVideoView; +} + +//--------------------------------------------------------------- + +BStringView * +VideoWindow::StatusLine() +{ + return fStatusLine; +} + +//--------------------------------------------------------------- + +void +VideoWindow::BuildCaptureControls(BView *theView) +{ + BRect aFrame, theFrame; + + theFrame = theView->Bounds(); + theFrame.top += VIDEO_SIZE_Y + 2*kYBuffer + 40; + theFrame.left += kXBuffer; + theFrame.right -= (WINDOW_SIZE_X/2 + 5); + theFrame.bottom -= kXBuffer; + + fCaptureSetupBox = new BBox( theFrame, "Capture Controls", B_FOLLOW_ALL, B_WILL_DRAW); + fCaptureSetupBox->SetLabel("Capture Controls"); + theView->AddChild(fCaptureSetupBox); + + aFrame = fCaptureSetupBox->Bounds(); + aFrame.InsetBy(kXBuffer,kYBuffer); + aFrame.top += kYBuffer/2; + aFrame.bottom = aFrame.top + kMenuHeight; + + fFileName = new BTextControl(aFrame, "File Name", "File Name:", fFilenameSetting->Value(), new BMessage(msg_filename)); + + fFileName->SetTarget(BMessenger(NULL, this)); + fFileName->SetDivider(fFileName->Divider() - 30); + fCaptureSetupBox->AddChild(fFileName); + + aFrame.top = aFrame.bottom + kYBuffer; + aFrame.bottom = aFrame.top + kMenuHeight; + + fImageFormatMenu = new BPopUpMenu("Image Format Menu"); + AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP); + fImageFormatMenu->SetTargetForItems(this); + if (fImageFormatMenu->FindItem("JPEG Image") != NULL) + fImageFormatMenu->FindItem("JPEG Image")->SetMarked(true); + else + fImageFormatMenu->ItemAt(0)->SetMarked(true); + fImageFormatSelector = new BMenuField(aFrame, "Format", "Format:", fImageFormatMenu); + fImageFormatSelector->SetDivider(fImageFormatSelector->Divider() - 30); + fCaptureSetupBox->AddChild(fImageFormatSelector); + + aFrame.top = aFrame.bottom + kYBuffer; + aFrame.bottom = aFrame.top + kMenuHeight; + + fCaptureRateMenu = new BPopUpMenu("Capture Rate Menu"); + fCaptureRateMenu->AddItem(new BMenuItem("Every 15 seconds",new BMessage(msg_rate_15s))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 30 seconds",new BMessage(msg_rate_30s))); + fCaptureRateMenu->AddItem(new BMenuItem("Every minute",new BMessage(msg_rate_1m))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 5 minutes",new BMessage(msg_rate_5m))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 10 minutes",new BMessage(msg_rate_10m))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 15 minutes",new BMessage(msg_rate_15m))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 30 minutes",new BMessage(msg_rate_30m))); + fCaptureRateMenu->AddItem(new BMenuItem("Every hour",new BMessage(msg_rate_1h))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 2 hours",new BMessage(msg_rate_2h))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 4 hours",new BMessage(msg_rate_4h))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 8 hours",new BMessage(msg_rate_8h))); + fCaptureRateMenu->AddItem(new BMenuItem("Every 24 hours",new BMessage(msg_rate_24h))); + fCaptureRateMenu->AddItem(new BMenuItem("Never",new BMessage(msg_rate_never))); + fCaptureRateMenu->SetTargetForItems(this); + fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true); + fCaptureRateSelector = new BMenuField(aFrame, "Rate", "Rate:", fCaptureRateMenu); + fCaptureRateSelector->SetDivider(fCaptureRateSelector->Divider() - 30); + fCaptureSetupBox->AddChild(fCaptureRateSelector); + + aFrame = theView->Bounds(); + aFrame.top += VIDEO_SIZE_Y + 2*kYBuffer + 40; + aFrame.left += WINDOW_SIZE_X/2 + 5; + aFrame.right -= kXBuffer; + aFrame.bottom -= kYBuffer; + + fFtpSetupBox = new BBox( aFrame, "Ftp Setup", B_FOLLOW_ALL, B_WILL_DRAW); + fFtpSetupBox->SetLabel("Ftp Setup"); + theView->AddChild(fFtpSetupBox); + + aFrame = fFtpSetupBox->Bounds(); + aFrame.InsetBy(kXBuffer,kYBuffer); + aFrame.top += kYBuffer/2; + aFrame.bottom = aFrame.top + kMenuHeight; + aFrame.right = aFrame.left + 160; + + fServerName = new BTextControl(aFrame, "Server", "Server:", fServerSetting->Value(), new BMessage(msg_server)); + fServerName->SetTarget(this); + fServerName->SetDivider(fServerName->Divider() - 30); + fFtpSetupBox->AddChild(fServerName); + + aFrame.top = aFrame.bottom + kYBuffer; + aFrame.bottom = aFrame.top + kMenuHeight; + + fLoginId = new BTextControl(aFrame, "Login", "Login:", fLoginSetting->Value(), new BMessage(msg_login)); + fLoginId->SetTarget(this); + fLoginId->SetDivider(fLoginId->Divider() - 30); + fFtpSetupBox->AddChild(fLoginId); + + aFrame.top = aFrame.bottom + kYBuffer; + aFrame.bottom = aFrame.top + kMenuHeight; + + fPassword = new BTextControl(aFrame, "Password", "Password:", fPasswordSetting->Value(), new BMessage(msg_password)); + fPassword->SetTarget(this); + fPassword->SetDivider(fPassword->Divider() - 30); + fFtpSetupBox->AddChild(fPassword); + + aFrame.top = aFrame.bottom + kYBuffer; + aFrame.bottom = aFrame.top + kMenuHeight; + + fDirectory = new BTextControl(aFrame, "Directory", "Directory:", fDirectorySetting->Value(), new BMessage(msg_directory)); + fDirectory->SetTarget(this); + fDirectory->SetDivider(fDirectory->Divider() - 30); + fFtpSetupBox->AddChild(fDirectory); + + aFrame.top = aFrame.bottom + kYBuffer; + aFrame.bottom = aFrame.top + kMenuHeight; + + fPassiveFtp = new BCheckBox(aFrame, "Passive ftp", "Passive ftp", new BMessage(msg_passiveftp)); + fPassiveFtp->SetTarget(this); + fPassiveFtp->SetValue(fPassiveFtpSetting->Value()); + fFtpSetupBox->AddChild(fPassiveFtp); + + aFrame = theView->Bounds(); + aFrame.top += VIDEO_SIZE_Y + 2*kYBuffer; + aFrame.left += kXBuffer; + aFrame.right -= kXBuffer; + aFrame.bottom = aFrame.top + kMenuHeight + 2*kYBuffer; + + fStatusBox = new BBox( aFrame, "Status", B_FOLLOW_ALL, B_WILL_DRAW); + fStatusBox->SetLabel("Status"); + theView->AddChild(fStatusBox); + + aFrame = fStatusBox->Bounds(); + aFrame.InsetBy(kXBuffer,kYBuffer); + + fStatusLine = new BStringView(aFrame,"Status Line","Waiting ..."); + fStatusBox->AddChild(fStatusLine); +} + +//--------------------------------------------------------------- + +void +VideoWindow::ApplyControls() +{ + // apply controls + fFileName->Invoke(); + PostMessage(fImageFormatMenu->FindMarked()->Message()); + PostMessage(fCaptureRateMenu->FindMarked()->Message()); + fServerName->Invoke(); + fLoginId->Invoke(); + fPassword->Invoke(); + fDirectory->Invoke(); + fPassiveFtp->Invoke(); +} + +//--------------------------------------------------------------- + +void +VideoWindow::SetUpSettings(const char *filename, const char *dirname) +{ + fSettings = new Settings(filename, dirname); + + fSettings->Add(fServerSetting = new StringValueSetting("Server", "ftp.my.server", + "server address expected", "")); + fSettings->Add(fLoginSetting = new StringValueSetting("Login", "loginID", + "login ID expected", "")); + fSettings->Add(fPasswordSetting = new StringValueSetting("Password", "password", + "password expected", "")); + fSettings->Add(fDirectorySetting = new StringValueSetting("Directory", "web/images", + "destination directory expected", "")); + fSettings->Add(fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1)); + fSettings->Add(fFilenameSetting = new StringValueSetting("StillImageFilename", "codycam.jpg", + "still image filename expected", "")); + fSettings->Add(fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate", "Every 5 minutes", kCaptureRate, + "capture rate expected", "unrecognized capture rate specified")); + + fSettings->TryReadingSettings(); +} + +//--------------------------------------------------------------- + +void +VideoWindow::QuitSettings() +{ + fServerSetting->ValueChanged(fServerName->Text()); + fLoginSetting->ValueChanged(fLoginId->Text()); + fPasswordSetting->ValueChanged(fFtpInfo.passwordText); + fDirectorySetting->ValueChanged(fDirectory->Text()); + fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value()); + fFilenameSetting->ValueChanged(fFileName->Text()); + fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label()); + + fSettings->SaveSettings(); + delete fSettings; +} + +//--------------------------------------------------------------- + +ControlWindow::ControlWindow( + const BRect & frame, + BView * controls, + media_node node) : + BWindow(frame, "Video Preferences", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS) +{ + fView = controls; + fNode = node; + + AddChild(fView); +} + +//--------------------------------------------------------------- + +void +ControlWindow::MessageReceived(BMessage * message) +{ + BParameterWeb * web = NULL; + BView * panel = NULL; + status_t err; + + switch (message->what) + { + case B_MEDIA_WEB_CHANGED: + { + // If this is a tab view, find out which tab + // is selected + BTabView *tabView = dynamic_cast(fView); + int32 tabNum = -1; + if (tabView) + tabNum = tabView->Selection(); + + RemoveChild(fView); + delete fView; + + err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web); + + if ((err >= B_OK) && + (web != NULL)) + { + fView = BMediaTheme::ViewFor(web); + AddChild(fView); + + // Another tab view? Restore previous selection + if (tabNum > 0) + { + BTabView *newTabView = dynamic_cast(fView); + if (newTabView) + newTabView->Select(tabNum); + } + } + break; + } + default: + BWindow::MessageReceived(message); + } +} + +//--------------------------------------------------------------- + +bool +ControlWindow::QuitRequested() +{ + be_app->PostMessage(msg_control_win); + return true; +} + + + diff --git a/src/prefs/codycam/CodyCam.h b/src/prefs/codycam/CodyCam.h new file mode 100644 index 0000000000..2baecb5e14 --- /dev/null +++ b/src/prefs/codycam/CodyCam.h @@ -0,0 +1,181 @@ +/* CodyCam.h */ + +#ifndef CODYCAM_H +#define CODYCAM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Settings.h" +#include"VideoConsumer.h" + +enum { + msg_filename = 'file', + + msg_rate_15s = 'r15s', + msg_rate_30s = 'r30s', + msg_rate_1m = 'r1m ', + msg_rate_5m = 'r5m ', + msg_rate_10m = 'r10m', + msg_rate_15m = 'r15m', + msg_rate_30m = 'r30m', + msg_rate_1h = 'r1h ', + msg_rate_2h = 'r2h ', + msg_rate_4h = 'r4h ', + msg_rate_8h = 'r8h ', + msg_rate_24h = 'r24h', + msg_rate_never = 'nevr', + + msg_translate = 'tran', + + msg_server = 'srvr', + msg_login = 'lgin', + msg_password = 'pswd', + msg_directory = 'drct', + msg_passiveftp = 'pasv', + msg_pswrd_text = 'pstx', + + msg_start = 'strt', + msg_stop = 'stop', + + msg_about = 'abut', + msg_setup = 'setp', + msg_video = 'vdeo', + + msg_control_win = 'ctlw' +}; + +const char *kCaptureRate[] = { + "Every 15 seconds", + "Every 30 seconds", + "Every minute", + "Every 5 minutes", + "Every 10 minutes", + "Every 15 minutes", + "Every 30 minutes", + "Every hour", + "Every 2 hours", + "Every 4 hours", + "Every 8 hours", + "Every 24 hours", + "Never", + 0 +}; + +class CodyCam : public BApplication { + public: + CodyCam(); + virtual ~CodyCam(); + + void ReadyToRun(); + virtual bool QuitRequested(); + virtual void MessageReceived( + BMessage *message); + + private: + status_t SetUpNodes(); + void TearDownNodes(); + + BMediaRoster * fMediaRoster; + + media_node fTimeSourceNode; + media_node fProducerNode; + + VideoConsumer * fVideoConsumer; + + media_output fProducerOut; + media_input fConsumerIn; + + BWindow * fWindow; + + port_id fPort; + + BWindow *mVideoControlWindow; +}; + +class VideoWindow : public BWindow +{ +public: + VideoWindow ( + BRect frame, + const char * title, + window_type type, + uint32 flags, + port_id * consumerport); + ~VideoWindow(); + + virtual bool QuitRequested(); + virtual void MessageReceived( + BMessage *message); + + void ApplyControls(); + + BView * VideoView(); + BStringView * StatusLine(); + +private: + void BuildCaptureControls( + BView *theView); + + void SetUpSettings( + const char *filename, + const char *dirname); + void QuitSettings(); + + +private: + media_node * fProducer; + port_id * fPortPtr; + + + BView * fView; + BView * fVideoView; + + BTextControl * fFileName; + BBox * fCaptureSetupBox; + BMenu * fCaptureRateMenu; + BMenuField * fCaptureRateSelector; + BMenu * fImageFormatMenu; + BMenuField * fImageFormatSelector; + BBox * fFtpSetupBox; + BTextControl * fServerName; + BTextControl * fLoginId; + BTextControl * fPassword; + BTextControl * fDirectory; + BCheckBox * fPassiveFtp; + BBox * fStatusBox; + BStringView * fStatusLine; + + ftp_msg_info fFtpInfo; + + + Settings * fSettings; + StringValueSetting * fServerSetting; + StringValueSetting * fLoginSetting; + StringValueSetting * fPasswordSetting; + StringValueSetting * fDirectorySetting; + BooleanValueSetting * fPassiveFtpSetting; + StringValueSetting * fFilenameSetting; + EnumeratedStringValueSetting *fCaptureRateSetting; +}; + +class ControlWindow : public BWindow { + +public: + ControlWindow(const BRect & frame, BView * controls, media_node node); + void MessageReceived(BMessage * message); + bool QuitRequested(); + +private: + BView * fView; + media_node fNode; +}; + +#endif \ No newline at end of file diff --git a/src/prefs/codycam/FtpClient.cpp b/src/prefs/codycam/FtpClient.cpp new file mode 100644 index 0000000000..640bd55031 --- /dev/null +++ b/src/prefs/codycam/FtpClient.cpp @@ -0,0 +1,739 @@ +#include "FtpClient.h" + +FtpClient::FtpClient() +{ + m_control = 0; + m_state = 0; + m_data = 0; +} + + + +FtpClient::~FtpClient() +{ + delete m_control; + delete m_data; +} + + +bool FtpClient::cd(const string &dir) +{ + bool rc = false; + int code, codetype; + string cmd = "CWD ", replystr; + + + cmd += dir; + + if(dir.length() == 0) + cmd += '/'; + + if(p_sendRequest(cmd) == true) + { + if(p_getReply(replystr, code, codetype) == true) + { + if(codetype == 2) + rc = true; + } + } + return rc; +} + + +bool FtpClient::ls(string &listing) +{ + bool rc = false; + string cmd, replystr; + int code, codetype, i, numread; + char buf[513]; + + cmd = "TYPE A"; + + if(p_sendRequest(cmd)) + p_getReply(replystr, code, codetype); + + if(p_openDataConnection()) + { + cmd = "LIST"; + + if(p_sendRequest(cmd)) + { + if(p_getReply(replystr, code, codetype)) + { + if(codetype <= 2) + { + if(p_acceptDataConnection()) + { + numread = 1; + while(numread > 0) + { + memset(buf, 0, sizeof(buf)); + numread = m_data->Receive(buf, sizeof(buf) - 1); + listing += buf; + printf(buf); + } + if(p_getReply(replystr, code, codetype)) + { + if(codetype <= 2) + { + rc = true; + } + } + } + } + } + } + } + + delete m_data; + m_data = 0; + + return rc; +} + + +bool FtpClient::pwd(string &dir) +{ + bool rc = false; + int code, codetype; + string cmd = "PWD", replystr; + long i; + + if(p_sendRequest(cmd) == true) + { + if(p_getReply(replystr, code, codetype) == true) + { + if(codetype == 2) + { + i = replystr.find('"'); + if(i != -1) + { + i++; + dir = replystr.substr(i, replystr.find('"') - i); + rc = true; + } + } + } + } + + return rc; +} + + +bool FtpClient::connect(const string &server, const string &login, const string &passwd) +{ + bool rc = false; + int code, codetype; + string cmd, replystr; + BNetAddress addr; + + delete m_control; + delete m_data; + + + m_control = new BNetEndpoint; + + if(m_control->InitCheck() != B_NO_ERROR) + return false; + + addr.SetTo(server.c_str(), "tcp", "ftp"); + if(m_control->Connect(addr) == B_NO_ERROR) + { + // + // read the welcome message, do the login + // + + if(p_getReply(replystr, code, codetype)) + { + if(code != 421 && codetype != 5) + { + cmd = "USER "; cmd += login; + p_sendRequest(cmd); + if(p_getReply(replystr, code, codetype)) + { + switch(code) + { + case 230: + case 202: + rc = true; + break; + + case 331: // password needed + cmd = "PASS "; cmd += passwd; + p_sendRequest(cmd); + if(p_getReply(replystr, code, codetype)) + { + if(codetype == 2) + { + rc = true; + } + } + break; + + default: + break; + + } + } + } + } + } + + if(rc == true) + { + p_setState(ftp_connected); + + } else { + delete m_control; + m_control = 0; + } + + return rc; +} + + + +bool FtpClient::putFile(const string &local, const string &remote, ftp_mode mode) +{ + bool rc = false; + string cmd, replystr; + int code, codetype, rlen, slen, i; + BFile infile(local.c_str(), B_READ_ONLY); + char buf[8192], sbuf[16384], *stmp; + + if(infile.InitCheck() != B_NO_ERROR) + return false; + + if(mode == binary_mode) + cmd = "TYPE I"; + else + cmd = "TYPE A"; + + if(p_sendRequest(cmd)) + p_getReply(replystr, code, codetype); + + try + { + if(p_openDataConnection()) + { + cmd = "STOR "; + cmd += remote; + + if(p_sendRequest(cmd)) + { + if(p_getReply(replystr, code, codetype)) + { + if(codetype <= 2) + { + if(p_acceptDataConnection()) + { + rlen = 1; + while(rlen > 0) + { + memset(buf, 0, sizeof(buf)); + memset(sbuf, 0, sizeof(sbuf)); + rlen = infile.Read((void *) buf, sizeof(buf)); + slen = rlen; + stmp = buf; + if(mode == ascii_mode) + { + stmp = sbuf; + slen = 0; + for(i=0;i 0) + { + if(m_data->Send(stmp, slen) < 0) + throw "bail"; + } + } + + rc = true; + } + } + } + } + } + } + + catch(const char *errstr) + { + + } + + delete m_data; + m_data = 0; + + if(rc == true) + { + p_getReply(replystr, code, codetype); + rc = (bool) codetype <= 2; + } + + return rc; +} + + + +bool FtpClient::getFile(const string &remote, const string &local, ftp_mode mode) +{ + bool rc = false; + string cmd, replystr; + int code, codetype, rlen, slen, i; + BFile outfile(local.c_str(), B_READ_WRITE | B_CREATE_FILE); + char buf[8192], sbuf[16384], *stmp; + bool writeerr = false; + + if(outfile.InitCheck() != B_NO_ERROR) + return false; + + if(mode == binary_mode) + cmd = "TYPE I"; + else + cmd = "TYPE A"; + + if(p_sendRequest(cmd)) + p_getReply(replystr, code, codetype); + + + if(p_openDataConnection()) + { + cmd = "RETR "; + cmd += remote; + + if(p_sendRequest(cmd)) + { + if(p_getReply(replystr, code, codetype)) + { + if(codetype <= 2) + { + if(p_acceptDataConnection()) + { + rlen = 1; + rc = true; + while(rlen > 0) + { + memset(buf, 0, sizeof(buf)); + memset(sbuf, 0, sizeof(sbuf)); + rlen = m_data->Receive(buf, sizeof(buf)); + + if(rlen > 0) + { + + slen = rlen; + stmp = buf; + if(mode == ascii_mode) + { + stmp = sbuf; + slen = 0; + for(i=0;i 0) + { + if(outfile.Write(stmp, slen) < 0) + { + writeerr = true; + } + } + } + } + + } + } + } + } + } + + delete m_data; + m_data = 0; + + if(rc == true) + { + p_getReply(replystr, code, codetype); + rc = (bool) ((codetype <= 2) && (writeerr == false)); + } + return rc; +} + +// +// Note: this only works for local remote moves, cross filesystem moves +// will not work +// +bool FtpClient::moveFile(const string &oldpath, const string &newpath) +{ + bool rc = false; + string from = "RNFR "; + string to = "RNTO "; + string replystr; + int code, codetype; + + from += oldpath; + to += newpath; + + if(p_sendRequest(from)) + { + if(p_getReply(replystr, code, codetype)) + { + if(codetype == 3) + { + if(p_sendRequest(to)) + { + if(p_getReply(replystr, code, codetype)) + { + if(codetype == 2) + rc = true; + } + } + } + } + } + return rc; +} + + +void FtpClient::setPassive(bool on) +{ + if(on) + p_setState(ftp_passive); + else + p_clearState(ftp_passive); +} + + + +bool FtpClient::p_testState(unsigned long state) +{ + return (bool) ((m_state & state) != 0); +} + + + +void FtpClient::p_setState(unsigned long state) +{ + m_state |= state; +} + + + +void FtpClient::p_clearState(unsigned long state) +{ + m_state &= ~state; +} + + + + +bool FtpClient::p_sendRequest(const string &cmd) +{ + bool rc = false; + string ccmd = cmd; + + if(m_control != 0) + { + if(cmd.find("PASS") != -1) + printf("PASS (real password sent)\n"); + else + printf("%s\n", ccmd.c_str()); + ccmd += "\r\n"; + if(m_control->Send(ccmd.c_str(), ccmd.length()) >= 0) + { + rc = true; + } + } + + return rc; +} + + + +bool FtpClient::p_getReplyLine(string &line) +{ + bool rc = false; + int c = 0; + bool done = false; + + line = ""; // Thanks to Stephen van Egmond for catching a bug here + + if(m_control != 0) + { + rc = true; + while(done == false && (m_control->Receive(&c, 1) > 0)) + { + if(c == EOF || c == xEOF || c == '\n') + { + done = true; + } else { + if(c == IAC) + { + m_control->Receive(&c, 1); + switch(c) + { + unsigned char treply[3]; + case WILL: + case WONT: + m_control->Receive(&c, 1); + treply[0] = IAC; + treply[1] = DONT; + treply[2] = c; + m_control->Send(treply, 3); + break; + + case DO: + case DONT: + m_control->Receive(&c, 1); + m_control->Receive(&c, 1); + treply[0] = IAC; + treply[1] = WONT; + treply[2] = c; + m_control->Send(treply, 3); + break; + + case EOF: + case xEOF: + done = true; + break; + + default: + line += c; + break; + } + } else { + // + // normal char + // + if(c != '\r') + line += c; + } + } + } + } + + return rc; +} + + +bool FtpClient::p_getReply(string &outstr, int &outcode, int &codetype) +{ + bool rc = false; + string line, tempstr; + + // + // comment from the ncftp source: + // + + /* RFC 959 states that a reply may span multiple lines. A single + * line message would have the 3-digit code then the msg. + * A multi-line message would have the code and the first + * line of the msg, then additional lines, until the last line, + * which has the code and last line of the msg. + * + * For example: + * 123-First line + * Second line + * 234 A line beginning with numbers + * 123 The last line + */ + + if((rc = p_getReplyLine(line)) == true) + { + outstr = line; + outstr += '\n'; + printf(outstr.c_str()); + tempstr = line.substr(0, 3); + outcode = atoi(tempstr.c_str()); + + if(line[3] == '-') + { + while((rc = p_getReplyLine(line)) == true) + { + outstr += line; + outstr += '\n'; + printf(outstr.c_str()); + // + // we're done with nnn when we get to a "nnn blahblahblah" + // + if((line.find(tempstr) == 0) && line[3] == ' ') + break; + } + } + } + + if(rc == false && outcode != 421) + { + outstr += "Remote host has closed the connection.\n"; + outcode = 421; + } + + if(outcode == 421) + { + delete m_control; + m_control = 0; + p_clearState(ftp_connected); + } + + codetype = outcode / 100; + + return rc; +} + + + +bool FtpClient::p_openDataConnection() +{ + string host, cmd, repstr; + unsigned short port; + BNetAddress addr; + int i, code, codetype; + char buf[32]; + bool rc = false; + struct sockaddr_in sa; + + delete m_data; + m_data = 0; + + m_data = new BNetEndpoint; + + if(p_testState(ftp_passive)) + { + // + // Here we send a "pasv" command and connect to the remote server + // on the port it sends back to us + // + cmd = "PASV"; + if(p_sendRequest(cmd)) + { + if(p_getReply(repstr, code, codetype)) + { + + if(codetype == 2) + { + // It should give us something like: + // "227 Entering Passive Mode (192,168,1,1,10,187)" + int paddr[6]; + unsigned char ucaddr[6]; + + i = repstr.find('('); + i++; + + repstr = repstr.substr(i, repstr.find(')') - i); + if (sscanf(repstr.c_str(), "%d,%d,%d,%d,%d,%d", + &paddr[0], &paddr[1], &paddr[2], &paddr[3], + &paddr[4], &paddr[5]) != 6) + { + // + // cannot do passive. Do a little harmless rercursion here + // + p_clearState(ftp_passive); + return p_openDataConnection(); + } + for(i=0;i<6;i++) + { + ucaddr[i] = (unsigned char) (paddr[i] & 0xff); + } + memcpy(&sa.sin_addr, &ucaddr[0], (size_t) 4); + memcpy(&sa.sin_port, &ucaddr[4], (size_t) 2); + addr.SetTo(sa); + if(m_data->Connect(addr) == B_NO_ERROR) + { + rc = true; + } + } + } + } else { + // + // cannot do passive. Do a little harmless rercursion here + // + p_clearState(ftp_passive); + rc = p_openDataConnection(); + } + + } else { + // + // Here we bind to a local port and send a PORT command + // + if(m_data->Bind() == B_NO_ERROR) + { + char buf[255]; + + m_data->Listen(); + addr = m_data->LocalAddr(); + addr.GetAddr(buf, &port); + host = buf; + + i=0; + while(i >= 0) + { + i = host.find('.', i); + if(i >= 0) + host[i] = ','; + } + + sprintf(buf, ",%d,%d", (port & 0xff00) >> 8, port & 0x00ff); + cmd = "PORT "; + cmd += host; cmd += buf; + p_sendRequest(cmd); + p_getReply(repstr, code, codetype); + // + // PORT failure is in the 500-range + // + if(codetype == 2) + rc = true; + } + } + + return rc; +} + + +bool FtpClient::p_acceptDataConnection() +{ + BNetEndpoint *ep; + bool rc = false; + + if(p_testState(ftp_passive) == false) + { + if(m_data != 0) + { + ep = m_data->Accept(); + if(ep != 0) + { + delete m_data; + m_data = ep; + rc = true; + } + } + + } else { + rc = true; + } + + return rc; +} + + + + + diff --git a/src/prefs/codycam/FtpClient.h b/src/prefs/codycam/FtpClient.h new file mode 100644 index 0000000000..1a0103bf9a --- /dev/null +++ b/src/prefs/codycam/FtpClient.h @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +class FtpClient +{ +public: + FtpClient(); + ~FtpClient(); + + enum ftp_mode + { + binary_mode, + ascii_mode + }; + + bool connect(const string &server, const string &login, const string &passwd); + bool putFile(const string &local, const string &remote, ftp_mode mode = binary_mode); + bool getFile(const string &remote, const string &local, ftp_mode mode = binary_mode); + bool moveFile(const string &oldpath, const string &newpath); + bool cd(const string &dir); + bool pwd(string &dir); + bool ls(string &listing); + + void setPassive(bool on); + +protected: + enum { + ftp_complete = 1UL, + ftp_connected = 2, + ftp_passive = 4 + }; + + unsigned long m_state; + bool p_testState(unsigned long state); + void p_setState(unsigned long state); + void p_clearState(unsigned long state); + + bool p_sendRequest(const string &cmd); + bool p_getReply(string &outstr, int &outcode, int &codetype); + bool p_getReplyLine(string &line); + bool p_openDataConnection(); + bool p_acceptDataConnection(); + + BNetEndpoint *m_control; + BNetEndpoint *m_data; + +}; + +/* + * Definitions for the TELNET protocol. Snarfed from the BSD source. + */ +#define IAC 255 +#define DONT 254 +#define DO 253 +#define WONT 252 +#define WILL 251 +#define xEOF 236 + diff --git a/src/prefs/codycam/Settings.cpp b/src/prefs/codycam/Settings.cpp new file mode 100644 index 0000000000..015ebc7103 --- /dev/null +++ b/src/prefs/codycam/Settings.cpp @@ -0,0 +1,213 @@ +#include +#include +#include +#include + +#include "Settings.h" + +Settings *settings = NULL; + +// generic setting handler classes + + +StringValueSetting::StringValueSetting(const char *name, const char *defaultValue, + const char *valueExpectedErrorString, const char *wrongValueErrorString) + : SettingsArgvDispatcher(name), + defaultValue(defaultValue), + valueExpectedErrorString(valueExpectedErrorString), + wrongValueErrorString(wrongValueErrorString), + value(strdup(defaultValue)) +{ +} + + +StringValueSetting::~StringValueSetting() +{ + free(value); +} + +void +StringValueSetting::ValueChanged(const char *newValue) +{ + if (newValue == value) + // guard against self assingment + return; + + free(value); + value = strdup(newValue); +} + +const char * +StringValueSetting::Value() const +{ + return value; +} + +void +StringValueSetting::SaveSettingValue(Settings *settings) +{ +printf("-------StringValueSetting::SaveSettingValue %s %s\n", Name(), value); + settings->Write("\"%s\"", value); +} + +bool +StringValueSetting::NeedsSaving() const +{ + // needs saving if different than default + return strcmp(value, defaultValue) != 0; +} + +const char * +StringValueSetting::Handle(const char *const *argv) +{ + if (!*++argv) + return valueExpectedErrorString; + + ValueChanged(*argv); + return 0; +} + +EnumeratedStringValueSetting::EnumeratedStringValueSetting(const char *name, + const char *defaultValue, const char *const *values, const char *valueExpectedErrorString, + const char *wrongValueErrorString) + : StringValueSetting(name, defaultValue, valueExpectedErrorString, wrongValueErrorString), + values(values) +{ +} + +void +EnumeratedStringValueSetting::ValueChanged(const char *newValue) +{ +#if DEBUG + // must be one of the enumerated values + bool found = false; + for (int32 index = 0; ; index++) { + if (!values[index]) + break; + if (strcmp(values[index], newValue) != 0) + continue; + found = true; + break; + } + ASSERT(found); +#endif + StringValueSetting::ValueChanged(newValue); +} + +const char * +EnumeratedStringValueSetting::Handle(const char *const *argv) +{ + if (!*++argv) + return valueExpectedErrorString; + +printf("-----EnumeratedStringValueSetting::Handle %s %s\n", *(argv-1), *argv); + bool found = false; + for (int32 index = 0; ; index++) { + if (!values[index]) + break; + if (strcmp(values[index], *argv) != 0) + continue; + found = true; + break; + } + + if (!found) + return wrongValueErrorString; + + ValueChanged(*argv); + return 0; +} + +ScalarValueSetting::ScalarValueSetting(const char *name, int32 defaultValue, + const char *valueExpectedErrorString, const char *wrongValueErrorString, + int32 min, int32 max) + : SettingsArgvDispatcher(name), + defaultValue(defaultValue), + value(defaultValue), + max(max), + min(min), + valueExpectedErrorString(valueExpectedErrorString), + wrongValueErrorString(wrongValueErrorString) +{ +} + +void +ScalarValueSetting::ValueChanged(int32 newValue) +{ + ASSERT(newValue > min); + ASSERT(newValue < max); + value = newValue; +} + +int32 +ScalarValueSetting::Value() const +{ + return value; +} + +void +ScalarValueSetting::GetValueAsString(char *buffer) const +{ + sprintf(buffer, "%ld", value); +} + +const char * +ScalarValueSetting::Handle(const char *const *argv) +{ + if (!*++argv) + return valueExpectedErrorString; + + int32 newValue = atoi(*argv); + if (newValue < min || newValue > max) + return wrongValueErrorString; + + value = newValue; + return 0; +} + +void +ScalarValueSetting::SaveSettingValue(Settings *settings) +{ + settings->Write("%d", value); +} + +bool +ScalarValueSetting::NeedsSaving() const +{ + return value != defaultValue; +} + + +BooleanValueSetting::BooleanValueSetting(const char *name, bool defaultValue ) + : ScalarValueSetting(name, defaultValue, 0, 0) +{ +} + +bool +BooleanValueSetting::Value() const +{ + return value; +} + +const char * +BooleanValueSetting::Handle(const char *const *argv) +{ + if (!*++argv) + return "or or off expected"; + + if (strcmp(*argv, "on") == 0) + value = true; + else if (strcmp(*argv, "off") == 0) + value = false; + else + return "or or off expected"; + + return 0; +} + +void +BooleanValueSetting::SaveSettingValue(Settings *settings) +{ + settings->Write(value ? "on" : "off"); +} + diff --git a/src/prefs/codycam/Settings.h b/src/prefs/codycam/Settings.h new file mode 100644 index 0000000000..0afa50afea --- /dev/null +++ b/src/prefs/codycam/Settings.h @@ -0,0 +1,84 @@ +#ifndef __SETTINGS__ +#define __SETTINGS__ + +#include "SettingsHandler.h" + +void SetUpSettings(char *filename); +void QuitSettings(); + +class StringValueSetting : public SettingsArgvDispatcher { + // simple string setting +public: + StringValueSetting(const char *name, const char *defaultValue, + const char *valueExpectedErrorString, + const char *wrongValueErrorString); + + virtual ~StringValueSetting(); + + void ValueChanged(const char *newValue); + const char *Value() const; + virtual const char *Handle(const char *const *argv); + +protected: + virtual void SaveSettingValue(Settings *); + virtual bool NeedsSaving() const; + + const char *defaultValue; + const char *valueExpectedErrorString; + const char *wrongValueErrorString; + char *value; +}; + +class EnumeratedStringValueSetting : public StringValueSetting { + // string setting, values that do not match string enumeration + // are rejected +public: + EnumeratedStringValueSetting(const char *name, const char *defaultValue, + const char *const *values, const char *valueExpectedErrorString, + const char *wrongValueErrorString); + + void ValueChanged(const char *newValue); + virtual const char *Handle(const char *const *argv); + +protected: + const char *const *values; + char *value; +}; + +class ScalarValueSetting : public SettingsArgvDispatcher { + // simple int32 setting +public: + ScalarValueSetting(const char *name, int32 defaultValue, + const char *valueExpectedErrorString, const char *wrongValueErrorString, + int32 min = LONG_MIN, int32 max = LONG_MAX); + + void ValueChanged(int32 newValue); + int32 Value() const; + void GetValueAsString(char *) const; + virtual const char *Handle(const char *const *argv); + +protected: + virtual void SaveSettingValue(Settings *); + virtual bool NeedsSaving() const; + + int32 defaultValue; + int32 value; + int32 max; + int32 min; + const char *valueExpectedErrorString; + const char *wrongValueErrorString; +}; + +class BooleanValueSetting : public ScalarValueSetting { + // on-off setting +public: + BooleanValueSetting(const char *name, bool defaultValue); + + bool Value() const; + virtual const char *Handle(const char *const *argv); + +protected: + virtual void SaveSettingValue(Settings *); +}; + +#endif diff --git a/src/prefs/codycam/SettingsHandler.cpp b/src/prefs/codycam/SettingsHandler.cpp new file mode 100644 index 0000000000..02e7379cdd --- /dev/null +++ b/src/prefs/codycam/SettingsHandler.cpp @@ -0,0 +1,427 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "SettingsHandler.h" + +ArgvParser::ArgvParser(const char *name) + : file(0), + buffer(0), + pos(-1), + argc(0), + currentArgv(0), + currentArgsPos(-1), + sawBackslash(false), + eatComment(false), + inDoubleQuote(false), + inSingleQuote(false), + lineNo(0), + fileName(name) +{ + file = fopen(fileName, "r"); + if (!file) { + PRINT(("Error opening %s\n", fileName)); + return; + } + buffer = new char [kBufferSize]; + currentArgv = new char * [1024]; +} + + +ArgvParser::~ArgvParser() +{ + delete [] buffer; + + MakeArgvEmpty(); + delete [] currentArgv; + + if (file) + fclose(file); +} + +void +ArgvParser::MakeArgvEmpty() +{ + // done with current argv, free it up + for (int32 index = 0; index < argc; index++) + delete currentArgv[index]; + + argc = 0; +} + +status_t +ArgvParser::SendArgv(ArgvHandler argvHandlerFunc, void *passThru) +{ + if (argc) { + NextArgv(); + currentArgv[argc] = 0; + const char *result = (argvHandlerFunc)(argc, currentArgv, passThru); + if (result) + printf("File %s; Line %ld # %s", fileName, lineNo, result); + MakeArgvEmpty(); + if (result) + return B_ERROR; + } + + return B_NO_ERROR; +} + +void +ArgvParser::NextArgv() +{ + if (sawBackslash) { + currentArgs[++currentArgsPos] = '\\'; + sawBackslash = false; + } + currentArgs[++currentArgsPos] = '\0'; + // terminate current arg pos + + // copy it as a string to the current argv slot + currentArgv[argc] = new char [strlen(currentArgs) + 1]; + strcpy(currentArgv[argc], currentArgs); + currentArgsPos = -1; + argc++; +} + +void +ArgvParser::NextArgvIfNotEmpty() +{ + if (!sawBackslash && currentArgsPos < 0) + return; + + NextArgv(); +} + +char +ArgvParser::GetCh() +{ + if (pos < 0 || buffer[pos] == 0) { + if (file == 0) + return EOF; + if (fgets(buffer, kBufferSize, file) == 0) + return EOF; + pos = 0; + } + return buffer[pos++]; +} + +status_t +ArgvParser::EachArgv(const char *name, ArgvHandler argvHandlerFunc, void *passThru) +{ + ArgvParser parser(name); + return parser.EachArgvPrivate(name, argvHandlerFunc, passThru); +} + +status_t +ArgvParser::EachArgvPrivate(const char *name, ArgvHandler argvHandlerFunc, void *passThru) +{ + status_t result; + + for (;;) { + char ch = GetCh(); + if (ch == EOF) { + // done with file + if (inDoubleQuote || inSingleQuote) { + printf("File %s # unterminated quote at end of file\n", name); + result = B_ERROR; + break; + } + result = SendArgv(argvHandlerFunc, passThru); + break; + } + + if (ch == '\n' || ch == '\r') { + // handle new line + eatComment = false; + if (!sawBackslash && (inDoubleQuote || inSingleQuote)) { + printf("File %s ; Line %ld # unterminated quote\n", name, lineNo); + result = B_ERROR; + break; + } + lineNo++; + if (sawBackslash) { + sawBackslash = false; + continue; + } + // end of line, flush all argv + result = SendArgv(argvHandlerFunc, passThru); + if (result != B_NO_ERROR) + break; + continue; + } + + if (eatComment) + continue; + + if (!sawBackslash) { + if (!inDoubleQuote && !inSingleQuote) { + if (ch == ';') { + // semicolon is a command separator, pass on the whole argv + result = SendArgv(argvHandlerFunc, passThru); + if (result != B_NO_ERROR) + break; + continue; + } else if (ch == '#') { + // ignore everything on this line after this character + eatComment = true; + continue; + } else if (ch == ' ' || ch == '\t') { + // space or tab separates the individual arg strings + NextArgvIfNotEmpty(); + continue; + } else if (!sawBackslash && ch == '\\') { + // the next character is escaped + sawBackslash = true; + continue; + } + } + if (!inSingleQuote && ch == '"') { + // enter/exit double quote handling + inDoubleQuote = !inDoubleQuote; + continue; + } + if (!inDoubleQuote && ch == '\'') { + // enter/exit single quote handling + inSingleQuote = !inSingleQuote; + continue; + } + } else { + // we just pass through the escape sequence as is + currentArgs[++currentArgsPos] = '\\'; + sawBackslash = false; + } + currentArgs[++currentArgsPos] = ch; + } + + return result; +} + + +SettingsArgvDispatcher::SettingsArgvDispatcher(const char *name) + : name(name) +{ +} + +void +SettingsArgvDispatcher::SaveSettings(Settings *settings, bool onlyIfNonDefault) +{ + if (!onlyIfNonDefault || NeedsSaving()) { + settings->Write("%s ", Name()); + SaveSettingValue(settings); + settings->Write("\n"); + } +} + +bool +SettingsArgvDispatcher::HandleRectValue(BRect &result, const char *const *argv, + bool printError) +{ + if (!*argv) { + if (printError) + printf("rect left expected"); + return false; + } + result.left = atoi(*argv); + if (!*++argv) { + if (printError) + printf("rect top expected"); + return false; + } + result.top = atoi(*argv); + if (!*++argv) { + if (printError) + printf("rect right expected"); + return false; + } + result.right = atoi(*argv); + if (!*++argv) { + if (printError) + printf("rect bottom expected"); + return false; + } + result.bottom = atoi(*argv); + return true; +} + +void +SettingsArgvDispatcher::WriteRectValue(Settings *setting, BRect rect) +{ + setting->Write("%d %d %d %d", (int32)rect.left, (int32)rect.top, + (int32)rect.right, (int32)rect.bottom); +} + +#if 0 +static int +CompareByNameOne(const SettingsArgvDispatcher *item1, const SettingsArgvDispatcher *item2) +{ + return strcmp(item1->Name(), item2->Name()); +} +#endif + +Settings::Settings(const char *filename, const char *settingsDirName) + : fileName(filename), + settingsDir(settingsDirName), + list(0), + count(0), + listSize(30), + currentSettings(0) +{ +#ifdef SINGLE_SETTING_FILE + settingsHandler = this; +#endif + list = (SettingsArgvDispatcher **)calloc(listSize, sizeof(SettingsArgvDispatcher *)); +} + + +Settings::~Settings() +{ + for (int32 index = 0; index < count; index++) + delete list[index]; + + free(list); +} + + +const char * +Settings::ParseUserSettings(int, const char *const *argv, void *castToThis) +{ + if (!*argv) + return 0; + +#ifdef SINGLE_SETTING_FILE + Settings *settings = settingsHandler; +#else + Settings *settings = (Settings *)castToThis; +#endif + + SettingsArgvDispatcher *handler = settings->Find(*argv); + if (!handler) + return "unknown command"; + return handler->Handle(argv); +} + + +#if 0 +static int +Compare(const SettingsArgvDispatcher *p1, const SettingsArgvDispatcher *p2) +{ + return strcmp(p1->Name(), p2->Name()); +} +#endif + +bool +Settings::Add(SettingsArgvDispatcher *setting) +{ + // check for uniqueness + if (Find(setting->Name())) + return false; + + if (count >= listSize) { + listSize += 30; + list = (SettingsArgvDispatcher **)realloc(list, + listSize * sizeof(SettingsArgvDispatcher *)); + } + list[count++] = setting; + return true; +} + +SettingsArgvDispatcher * +Settings::Find(const char *name) +{ + for (int32 index = 0; index < count; index++) + if (strcmp(name, list[index]->Name()) == 0) + return list[index]; + + return 0; +} + +void +Settings::TryReadingSettings() +{ + BPath prefsPath; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &prefsPath, true) == B_OK) { + prefsPath.Append(settingsDir); + + BPath path(prefsPath); + path.Append(fileName); + ArgvParser::EachArgv(path.Path(), Settings::ParseUserSettings, this); + } +} + +void +Settings::SaveSettings(bool onlyIfNonDefault) +{ + ASSERT(SettingsHandler()); + SettingsHandler()->SaveCurrentSettings(onlyIfNonDefault); +} + +void +Settings::MakeSettingsDirectory(BDirectory *resultingSettingsDir) +{ + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) + return; + + // make sure there is a directory + path.Append(settingsDir); + mkdir(path.Path(), 0777); + resultingSettingsDir->SetTo(path.Path()); +} + +void +Settings::SaveCurrentSettings(bool onlyIfNonDefault) +{ + BDirectory settingsDir; + MakeSettingsDirectory(&settingsDir); + + if (settingsDir.InitCheck() != B_OK) + return; +printf("+++++++++++ Settings::SaveCurrentSettings %s\n", fileName); + // nuke old settings + BEntry entry(&settingsDir, fileName); + entry.Remove(); + + BFile prefs(&entry, O_RDWR | O_CREAT); + if (prefs.InitCheck() != B_OK) + return; + + currentSettings = &prefs; + for (int32 index = 0; index < count; index++) { + list[index]->SaveSettings(this, onlyIfNonDefault); + } + + currentSettings = 0; +} + +void +Settings::Write(const char *format, ...) +{ + va_list args; + + va_start(args, format); + VSWrite(format, args); + va_end(args); +} + +void +Settings::VSWrite(const char *format, va_list arg) +{ + char buffer[2048]; + vsprintf(buffer, format, arg); + ASSERT(currentSettings && currentSettings->InitCheck() == B_OK); + currentSettings->Write(buffer, strlen(buffer)); +} + +#ifdef SINGLE_SETTING_FILE +Settings *Settings::settingsHandler = 0; +#endif diff --git a/src/prefs/codycam/SettingsHandler.h b/src/prefs/codycam/SettingsHandler.h new file mode 100644 index 0000000000..17bc186667 --- /dev/null +++ b/src/prefs/codycam/SettingsHandler.h @@ -0,0 +1,147 @@ +#ifndef __SETTINGS_FILE__ +#define __SETTINGS_FILE__ + +#include +#include +#include +#include +#include + +class BFile; +class BDirectory; +class Settings; + + +typedef const char *(*ArgvHandler)(int argc, const char *const *argv, void *params); + // return 0 or error string if parsing failed + + +const int32 kBufferSize = 1024; + +class ArgvParser { + // this class opens a text file and passes the context in argv + // format to a specified handler +public: + static status_t EachArgv(const char *name, + ArgvHandler argvHandlerFunc, void *passThru); + +private: + ArgvParser(const char *name); + ~ArgvParser(); + + status_t EachArgvPrivate(const char *name, + ArgvHandler argvHandlerFunc, void *passThru); + char GetCh(); + + status_t SendArgv(ArgvHandler argvHandlerFunc, void *passThru); + // done with a whole line of argv, send it off and get ready + // to build a new one + + void NextArgv(); + // done with current string, get ready to start building next + void NextArgvIfNotEmpty(); + // as above, don't commint current string if empty + + void MakeArgvEmpty(); + + FILE *file; + char *buffer; + int32 pos; + int32 numAvail; + + int argc; + char **currentArgv; + + int32 currentArgsPos; + char currentArgs [1024]; + + bool sawBackslash; + bool eatComment; + bool inDoubleQuote; + bool inSingleQuote; + + int32 lineNo; + const char *fileName; +}; + +class SettingsArgvDispatcher { + // base class for a single setting item +public: + SettingsArgvDispatcher(const char *name); + + void SaveSettings(Settings *settings, bool onlyIfNonDefault); + + const char *Name() const + { return name; } + // name as it appears in the settings file + + virtual const char *Handle(const char *const *argv) = 0; + // override this adding an argv parser that reads in the + // values in argv format for this setting + // return a pointer to an error message or null if parsed OK + + + // some handy reader/writer calls + bool HandleRectValue(BRect &, const char *const *argv, bool printError = true); + // static bool HandleColorValue(rgb_color &, const char *const *argv, bool printError = true); + void WriteRectValue(Settings *, BRect); + // void WriteColorValue(BRect); + +protected: + virtual void SaveSettingValue(Settings *settings) = 0; + // override this to save the current value of this setting in a + // text format + + virtual bool NeedsSaving() const + { return true; } + // override to return false if current value is equal to the default + // and does not need saving +private: + const char *name; +}; + +class Settings { + // this class is a list of all the settings handlers, reads and + // saves the settings file +public: + Settings(const char *filename, const char *settingsDirName); + ~Settings(); + void TryReadingSettings(); + void SaveSettings(bool onlyIfNonDefault = true); + +#ifdef SINGLE_SETTING_FILE + static Settings *SettingsHandler() + { return settingsHandler; } +#else + Settings *SettingsHandler() + { return this; } +#endif + + bool Add(SettingsArgvDispatcher *); + // return false if argv dispatcher with the same name already + // registered + + void Write(const char *format, ...); + void VSWrite(const char *, va_list); + +#ifdef SINGLE_SETTING_FILE + static Settings *settingsHandler; +#endif + +private: + void MakeSettingsDirectory(BDirectory *); + + SettingsArgvDispatcher *Find(const char *); + static const char *ParseUserSettings(int, const char *const *argv, void *); + void SaveCurrentSettings(bool onlyIfNonDefault); + + const char *fileName; + const char *settingsDir; + SettingsArgvDispatcher **list; + int32 count; + int32 listSize; + BFile *currentSettings; +}; + + +#endif \ No newline at end of file diff --git a/src/prefs/codycam/VideoConsumer.cpp b/src/prefs/codycam/VideoConsumer.cpp new file mode 100644 index 0000000000..46166d5766 --- /dev/null +++ b/src/prefs/codycam/VideoConsumer.cpp @@ -0,0 +1,808 @@ +// Copyright (c) 1998-99, Be Incorporated, All Rights Reserved. +// SMS +// VideoConsumer.cpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FtpClient.h" +#include "VideoConsumer.h" + +#define M1 ((double)1000000.0) +#define JITTER 20000 + +#define FUNCTION printf +#define ERROR printf +#define PROGRESS printf +#define LOOP printf + +static status_t SetFileType(BFile * file, int32 translator, uint32 type); + +const media_raw_video_format vid_format = { 29.97,1,0,239,B_VIDEO_TOP_LEFT_RIGHT,1,1,{B_RGB16,320,240,320*4,0,0}}; + +//--------------------------------------------------------------- + +VideoConsumer::VideoConsumer( + const char * name, + BView *view, + BStringView * statusLine, + BMediaAddOn *addon, + const uint32 internal_id) : + + BMediaNode(name), + BMediaEventLooper(), + BBufferConsumer(B_MEDIA_RAW_VIDEO), + mView(view), + mWindow(NULL), + mStatusLine(statusLine), + mInternalID(internal_id), + mAddOn(addon), + mTimeToFtp(false), + mFtpComplete(true), + mRate(1000000), + mImageFormat(0), + mTranslator(0), + mPassiveFtp(true), + mConnectionActive(false), + mMyLatency(20000), + mBuffers(NULL), + mOurBuffers(false) +{ + FUNCTION("VideoConsumer::VideoConsumer\n"); + + AddNodeKind(B_PHYSICAL_OUTPUT); + SetEventLatency(0); + mWindow = mView->Window(); + + for (uint32 j = 0; j < 3; j++) + { + mBitmap[j] = NULL; + mBufferMap[j] = 0; + } + + strcpy(mFileNameText, ""); + strcpy(mServerText, ""); + strcpy(mLoginText, ""); + strcpy(mPasswordText, ""); + strcpy(mDirectoryText, ""); + + SetPriority(B_DISPLAY_PRIORITY); +} + +//--------------------------------------------------------------- + +VideoConsumer::~VideoConsumer() +{ + FUNCTION("VideoConsumer::~VideoConsumer\n"); + status_t status; + + Quit(); + + if (mWindow) + { + printf("Locking the window\n"); + if (mWindow->Lock()) + { + printf("Closing the window\n"); + mWindow->Close(); + mWindow = 0; + } + } + + // clean up ftp thread + // wait up to 30 seconds if ftp is in progress + int32 count = 0; + while (!mFtpComplete && (count < 30)) + { + snooze(1000000); + count++; + } + + if(count == 30) + kill_thread(mFtpThread); + + DeleteBuffers(); + + +} + +/******************************** + From BMediaNode +********************************/ + +//--------------------------------------------------------------- + +BMediaAddOn * +VideoConsumer::AddOn(long *cookie) const +{ + FUNCTION("VideoConsumer::AddOn\n"); + // do the right thing if we're ever used with an add-on + *cookie = mInternalID; + return mAddOn; +} + +//--------------------------------------------------------------- + +// This implementation is required to get around a bug in +// the ppc compiler. + +void +VideoConsumer::Start(bigtime_t performance_time) +{ + BMediaEventLooper::Start(performance_time); +} + +void +VideoConsumer::Stop(bigtime_t performance_time, bool immediate) +{ + BMediaEventLooper::Stop(performance_time, immediate); +} + +void +VideoConsumer::Seek(bigtime_t media_time, bigtime_t performance_time) +{ + BMediaEventLooper::Seek(media_time, performance_time); +} + +void +VideoConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) +{ + BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); +} + +status_t +VideoConsumer::DeleteHook(BMediaNode *node) +{ + return BMediaEventLooper::DeleteHook(node); +} + +//--------------------------------------------------------------- + +void +VideoConsumer::NodeRegistered() +{ + FUNCTION("VideoConsumer::NodeRegistered\n"); + mIn.destination.port = ControlPort(); + mIn.destination.id = 0; + mIn.source = media_source::null; + mIn.format.type = B_MEDIA_RAW_VIDEO; + mIn.format.u.raw_video = vid_format; + + Run(); +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::RequestCompleted(const media_request_info & info) +{ + FUNCTION("VideoConsumer::RequestCompleted\n"); + switch(info.what) + { + case media_request_info::B_SET_OUTPUT_BUFFERS_FOR: + { + if (info.status != B_OK) + ERROR("VideoConsumer::RequestCompleted: Not using our buffers!\n"); + } + break; + } + return B_OK; +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::HandleMessage(int32 message, const void * data, size_t size) +{ + //FUNCTION("VideoConsumer::HandleMessage\n"); + ftp_msg_info *info = (ftp_msg_info *)data; + status_t status = B_OK; + + switch (message) + { + case FTP_INFO: + PROGRESS("VideoConsumer::HandleMessage - FTP_INFO message\n"); + mRate = info->rate; + mImageFormat = info->imageFormat; + mTranslator = info->translator; + mPassiveFtp = info->passiveFtp; + strcpy(mFileNameText, info->fileNameText); + strcpy(mServerText, info->serverText); + strcpy(mLoginText, info->loginText); + strcpy(mPasswordText, info->passwordText); + strcpy(mDirectoryText, info->directoryText); + // remove old user events + EventQueue()->FlushEvents(TimeSource()->Now(), BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_USER_EVENT); + if (mRate != B_INFINITE_TIMEOUT) { + // if rate is not "Never," push an event to restart captures 5 seconds from now + media_timed_event event(TimeSource()->Now() + 5000000, BTimedEventQueue::B_USER_EVENT); + EventQueue()->AddEvent(event); + } + break; + } + + return status; +} + +//--------------------------------------------------------------- + +void +VideoConsumer::BufferReceived(BBuffer * buffer) +{ + LOOP("VideoConsumer::Buffer #%d received\n", buffer->ID()); + + if (RunState() == B_STOPPED) + { + buffer->Recycle(); + return; + } + + media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER, + buffer, BTimedEventQueue::B_RECYCLE_BUFFER); + EventQueue()->AddEvent(event); +} + + +//--------------------------------------------------------------- + +void +VideoConsumer::ProducerDataStatus( + const media_destination &for_whom, + int32 status, + bigtime_t at_media_time) +{ + FUNCTION("VideoConsumer::ProducerDataStatus\n"); + + if (for_whom != mIn.destination) + return; +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::CreateBuffers( + const media_format & with_format) +{ + FUNCTION("VideoConsumer::CreateBuffers\n"); + + // delete any old buffers + DeleteBuffers(); + + status_t status = B_OK; + + // create a buffer group + uint32 mXSize = with_format.u.raw_video.display.line_width; + uint32 mYSize = with_format.u.raw_video.display.line_count; + uint32 mRowBytes = with_format.u.raw_video.display.bytes_per_row; + color_space mColorspace = with_format.u.raw_video.display.format; + PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", mColorspace); + + mBuffers = new BBufferGroup(); + status = mBuffers->InitCheck(); + if (B_OK != status) + { + ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n"); + return status; + } + // and attach the bitmaps to the buffer group + for (uint32 j=0; j < 3; j++) + { + mBitmap[j] = new BBitmap(BRect(0, 0, (mXSize-1), (mYSize - 1)), mColorspace, false, true); + if (mBitmap[j]->IsValid()) + { + buffer_clone_info info; + if ((info.area = area_for(mBitmap[j]->Bits())) == B_ERROR) + ERROR("VideoConsumer::CreateBuffers - ERROR IN AREA_FOR\n");; + info.offset = 0; + info.size = (size_t)mBitmap[j]->BitsLength(); + info.flags = j; + info.buffer = 0; + + if ((status = mBuffers->AddBuffer(info)) != B_OK) + { + ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n"); + return status; + } else PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n"); + } + else + { + ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING BUFFER: %08x\n", status); + return B_ERROR; + } + } + + BBuffer ** buffList = new BBuffer * [3]; + for (int j = 0; j < 3; j++) buffList[j] = 0; + + if ((status = mBuffers->GetBufferList(3, buffList)) == B_OK) + for (int j = 0; j < 3; j++) + if (buffList[j] != NULL) + { + mBufferMap[j] = (uint32) buffList[j]; + PROGRESS(" j = %d buffer = %08x\n", j, mBufferMap[j]); + } + else + { + ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n"); + return B_ERROR; + } + else + ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n"); + + FUNCTION("VideoConsumer::CreateBuffers - EXIT\n"); + return status; +} + +//--------------------------------------------------------------- + +void +VideoConsumer::DeleteBuffers() +{ + FUNCTION("VideoConsumer::DeleteBuffers\n"); + status_t status; + + if (mBuffers) + { + delete mBuffers; + mBuffers = NULL; + + for (uint32 j = 0; j < 3; j++) + if (mBitmap[j]->IsValid()) + { + delete mBitmap[j]; + mBitmap[j] = NULL; + } + } + FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n"); +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::Connected( + const media_source & producer, + const media_destination & where, + const media_format & with_format, + media_input * out_input) +{ + FUNCTION("VideoConsumer::Connected\n"); + + mIn.source = producer; + mIn.format = with_format; + mIn.node = Node(); + sprintf(mIn.name, "Video Consumer"); + *out_input = mIn; + + uint32 user_data = 0; + int32 change_tag = 1; + if (CreateBuffers(with_format) == B_OK) + BBufferConsumer::SetOutputBuffersFor(producer, mDestination, + mBuffers, (void *)&user_data, &change_tag, true); + else + { + ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n"); + return B_ERROR; + } + + mFtpBitmap = new BBitmap(BRect(0, 0, 320-1, 240-1), B_RGB32, false, false); + mConnectionActive = true; + + FUNCTION("VideoConsumer::Connected - EXIT\n"); + return B_OK; +} + +//--------------------------------------------------------------- + +void +VideoConsumer::Disconnected( + const media_source & producer, + const media_destination & where) +{ + FUNCTION("VideoConsumer::Disconnected\n"); + + if (where == mIn.destination && producer == mIn.source) + { + // disconnect the connection + mIn.source = media_source::null; + delete mFtpBitmap; + mConnectionActive = false; + } + +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::AcceptFormat( + const media_destination & dest, + media_format * format) +{ + FUNCTION("VideoConsumer::AcceptFormat\n"); + + if (dest != mIn.destination) + { + ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + + if (format->type == B_MEDIA_NO_TYPE) + format->type = B_MEDIA_RAW_VIDEO; + + if (format->type != B_MEDIA_RAW_VIDEO) + { + ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + + if (format->u.raw_video.display.format != B_RGB32 && + format->u.raw_video.display.format != B_RGB16 && + format->u.raw_video.display.format != B_RGB15 && + format->u.raw_video.display.format != B_GRAY8 && + format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format) + { + ERROR("AcceptFormat - not a format we know about!\n"); + return B_MEDIA_BAD_FORMAT; + } + + if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) + { + format->u.raw_video.display.format = B_RGB16; + } + + char format_string[256]; + string_for_format(*format, format_string, 256); + FUNCTION("VideoConsumer::AcceptFormat: %s\n", format_string); + + return B_OK; +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::GetNextInput( + int32 * cookie, + media_input * out_input) +{ + FUNCTION("VideoConsumer::GetNextInput\n"); + + // custom build a destination for this connection + // put connection number in id + + if (*cookie < 1) + { + mIn.node = Node(); + mIn.destination.id = *cookie; + sprintf(mIn.name, "Video Consumer"); + *out_input = mIn; + (*cookie)++; + return B_OK; + } + else + { + ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n"); + return B_MEDIA_BAD_DESTINATION; + } +} + +//--------------------------------------------------------------- + +void +VideoConsumer::DisposeInputCookie(int32 /*cookie*/) +{ +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::GetLatencyFor( + const media_destination &for_whom, + bigtime_t * out_latency, + media_node_id * out_timesource) +{ + FUNCTION("VideoConsumer::GetLatencyFor\n"); + + if (for_whom != mIn.destination) + return B_MEDIA_BAD_DESTINATION; + + *out_latency = mMyLatency; + *out_timesource = TimeSource()->ID(); + return B_OK; +} + + +//--------------------------------------------------------------- + +status_t +VideoConsumer::FormatChanged( + const media_source & producer, + const media_destination & consumer, + int32 from_change_count, + const media_format &format) +{ + FUNCTION("VideoConsumer::FormatChanged\n"); + + if (consumer != mIn.destination) + return B_MEDIA_BAD_DESTINATION; + + if (producer != mIn.source) + return B_MEDIA_BAD_SOURCE; + + mIn.format = format; + + return CreateBuffers(format); +} + +//--------------------------------------------------------------- + +void +VideoConsumer::HandleEvent( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) + +{ + LOOP("VideoConsumer::HandleEvent\n"); + + BBuffer *buffer; + + switch (event->type) + { + case BTimedEventQueue::B_START: + PROGRESS("VideoConsumer::HandleEvent - START\n"); + break; + case BTimedEventQueue::B_STOP: + PROGRESS("VideoConsumer::HandleEvent - STOP\n"); + EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); + break; + case BTimedEventQueue::B_USER_EVENT: + PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n"); + if (RunState() == B_STARTED) + { + mTimeToFtp = true; + PROGRESS("Pushing user event for %.4f, time now %.4f\n", (event->event_time + mRate)/M1, event->event_time/M1); + media_timed_event newEvent(event->event_time + mRate, BTimedEventQueue::B_USER_EVENT); + EventQueue()->AddEvent(newEvent); + } + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n"); + buffer = (BBuffer *) event->pointer; + if (RunState() == B_STARTED && mConnectionActive) + { + // see if this is one of our buffers + uint32 index = 0; + mOurBuffers = true; + while(index < 3) + if ((uint32)buffer == mBufferMap[index]) + break; + else + index++; + + if (index == 3) + { + // no, buffers belong to consumer + mOurBuffers = false; + index = 0; + } + + if (mFtpComplete && mTimeToFtp) + { + PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n"); + mTimeToFtp = false; + mFtpComplete = false; + memcpy(mFtpBitmap->Bits(), buffer->Data(),mFtpBitmap->BitsLength()); + mFtpThread = spawn_thread(FtpRun, "Video Window Ftp", B_NORMAL_PRIORITY, this); + resume_thread(mFtpThread); + } + + if ( (RunMode() == B_OFFLINE) || + ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER)) && + (TimeSource()->Now() < (buffer->Header()->start_time + JITTER))) ) + { + if (!mOurBuffers) + // not our buffers, so we need to copy + memcpy(mBitmap[index]->Bits(), buffer->Data(),mBitmap[index]->BitsLength()); + + if (mWindow->Lock()) + { + uint32 flags; + if ((mBitmap[index]->ColorSpace() == B_GRAY8) && + !bitmaps_support_space(mBitmap[index]->ColorSpace(), &flags)) + { + // handle mapping of GRAY8 until app server knows how + uint32 *start = (uint32 *)mBitmap[index]->Bits(); + int32 size = mBitmap[index]->BitsLength(); + uint32 *end = start + size/4; + for (uint32 *p = start; p < end; p++) + *p = (*p >> 3) & 0x1f1f1f1f; + } + + mView->DrawBitmap(mBitmap[index]); + mWindow->Unlock(); + } + } + else + PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n"); + buffer->Recycle(); + } + else + buffer->Recycle(); + break; + default: + ERROR("VideoConsumer::HandleEvent - BAD EVENT\n"); + break; + } +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::FtpRun(void * data) +{ + FUNCTION("VideoConsumer::FtpRun\n"); + + ((VideoConsumer *)data)->FtpThread(); + + return 0; +} + +//--------------------------------------------------------------- + + +void +VideoConsumer::FtpThread() +{ + FUNCTION("VideoConsumer::FtpThread\n"); + + if (LocalSave(mFileNameText, mFtpBitmap) == B_OK) + FtpSave(mFileNameText); + +#if 0 + // save a small version, too + BBitmap * b = new BBitmap(BRect(0,0,159,119), B_RGB32, true, false); + BView * v = new BView(BRect(0,0,159,119), "SmallView 1", 0, B_WILL_DRAW); + b->AddChild(v); + + b->Lock(); + v->DrawBitmap(mFtpBitmap, v->Frame()); + v->Sync(); + b->Unlock(); + + if (LocalSave("small.jpg", b) == B_OK) + FtpSave("small.jpg"); + + delete b; +#endif + + mFtpComplete = true; +} + +//--------------------------------------------------------------- + +void +VideoConsumer::UpdateFtpStatus(char *status) +{ + printf("FTP STATUS: %s\n",status); + if (mView->Window()->Lock()) + { + mStatusLine->SetText(status); + mView->Window()->Unlock(); + } +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::LocalSave(char *filename, BBitmap *bitmap) +{ + BFile *output; + + UpdateFtpStatus("Capturing Image ..."); + + /* save a local copy of the image in the requested format */ + output =new BFile(); + if (output->SetTo(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) == B_NO_ERROR) + { + BBitmapStream input(bitmap); + status_t err = BTranslatorRoster::Default()->Translate(&input, NULL, NULL, output, mImageFormat); + if (err == B_OK) + { + err = SetFileType(output, mTranslator, mImageFormat); + if (err != B_OK) + UpdateFtpStatus("Error setting type of output file"); + } + else + { + UpdateFtpStatus("Error writing output file"); + } + input.DetachBitmap(&bitmap); + output->Unset(); + delete output; + return B_OK; + } + else + { + UpdateFtpStatus("Error creating output file"); + return B_ERROR; + } +} + +//--------------------------------------------------------------- + +status_t +VideoConsumer::FtpSave(char *filename) +{ + FtpClient ftp; + + /* ftp the local file to our web site */ + ftp.setPassive(mPassiveFtp); + + UpdateFtpStatus("Logging in ..."); + if (ftp.connect((string)mServerText, (string)mLoginText, (string)mPasswordText)) // connect to server + { + UpdateFtpStatus("Connected ..."); + if (ftp.cd((string)mDirectoryText)) + { // cd to the desired directory + UpdateFtpStatus("Transmitting ..."); + if (ftp.putFile((string)filename, (string)"temp")) // send the file to the server + { + UpdateFtpStatus("Renaming ..."); + if (ftp.moveFile((string)"temp",(string)filename)) // change to the desired name + { + uint32 time = real_time_clock(); + char s[80]; + strcpy(s,"Last Capture: "); + strcat(s,ctime((const long *)&time)); + s[strlen(s)-1] = 0; + UpdateFtpStatus(s); + return B_OK; + } else UpdateFtpStatus("Rename failed"); + } else UpdateFtpStatus("File transmission failed"); + } else UpdateFtpStatus("Couldn't find requested directory on server"); + } else UpdateFtpStatus("Server login failed"); + return B_ERROR; +} + +//--------------------------------------------------------------- + +status_t +SetFileType(BFile * file, int32 translator, uint32 type) +{ + translation_format * formats; + int32 count; + + status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator, (const translation_format **) &formats, &count); + if (err < B_OK) return err; + const char * mime = NULL; + for (int ix=0; ix +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + port_id port; + bigtime_t rate; + uint32 imageFormat; + int32 translator; + bool passiveFtp; + char fileNameText[64]; + char serverText[64]; + char loginText[64]; + char passwordText[64]; + char directoryText[64]; +} ftp_msg_info; + +#define FTP_INFO 0x60000001 + +class VideoConsumer : + public BMediaEventLooper, + public BBufferConsumer +{ +public: + VideoConsumer( + const char * name, + BView * view, + BStringView * statusLine, + BMediaAddOn *addon, + const uint32 internal_id); + ~VideoConsumer(); + +/* BMediaNode */ +public: + + virtual BMediaAddOn *AddOn(long *cookie) const; + +protected: + + virtual void Start(bigtime_t performance_time); + virtual void Stop(bigtime_t performance_time, bool immediate); + virtual void Seek(bigtime_t media_time, bigtime_t performance_time); + virtual void TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time); + + virtual void NodeRegistered(); + virtual status_t RequestCompleted( + const media_request_info & info); + + virtual status_t HandleMessage( + int32 message, + const void * data, + size_t size); + + virtual status_t DeleteHook(BMediaNode * node); + +/* BMediaEventLooper */ +protected: + virtual void HandleEvent( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent); +/* BBufferConsumer */ +public: + + virtual status_t AcceptFormat( + const media_destination &dest, + media_format * format); + virtual status_t GetNextInput( + int32 * cookie, + media_input * out_input); + + virtual void DisposeInputCookie( + int32 cookie); + +protected: + + virtual void BufferReceived( + BBuffer * buffer); + +private: + + virtual void ProducerDataStatus( + const media_destination &for_whom, + int32 status, + bigtime_t at_media_time); + virtual status_t GetLatencyFor( + const media_destination &for_whom, + bigtime_t * out_latency, + media_node_id * out_id); + virtual status_t Connected( + const media_source &producer, + const media_destination &where, + const media_format & with_format, + media_input * out_input); + virtual void Disconnected( + const media_source &producer, + const media_destination &where); + virtual status_t FormatChanged( + const media_source & producer, + const media_destination & consumer, + int32 from_change_count, + const media_format & format); + +/* implementation */ + +public: + status_t CreateBuffers( + const media_format & with_format); + + void DeleteBuffers(); + + static status_t FtpRun( + void *data); + + void FtpThread( + void); + + void UpdateFtpStatus( + char *status); + + status_t LocalSave( + char *filename, + BBitmap *bitmap); + + status_t FtpSave( + char *filename); + +private: + + BStringView * mStatusLine; + uint32 mInternalID; + BMediaAddOn *mAddOn; + + thread_id mFtpThread; + + bool mConnectionActive; + media_input mIn; + media_destination mDestination; + bigtime_t mMyLatency; + + BWindow *mWindow; + BView *mView; + BBitmap *mBitmap[3]; + bool mOurBuffers; + BBufferGroup *mBuffers; + uint32 mBufferMap[3]; + + BBitmap *mFtpBitmap; + volatile bool mTimeToFtp; + volatile bool mFtpComplete; + + bigtime_t mRate; + uint32 mImageFormat; + int32 mTranslator; + bool mPassiveFtp; + char mFileNameText[64]; + char mServerText[64]; + char mLoginText[64]; + char mPasswordText[64]; + char mDirectoryText[64]; +}; + +#endif diff --git a/src/prefs/codycam/codycam.rsrc b/src/prefs/codycam/codycam.rsrc new file mode 100644 index 0000000000..75d2f1b6c3 Binary files /dev/null and b/src/prefs/codycam/codycam.rsrc differ diff --git a/src/prefs/codycam/makefile b/src/prefs/codycam/makefile new file mode 100644 index 0000000000..dd6a09278a --- /dev/null +++ b/src/prefs/codycam/makefile @@ -0,0 +1,120 @@ +## BeOS Generic Makefile v2.1 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME= CodyCam + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= APP + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS= \ + CodyCam.cpp \ + FtpClient.cpp \ + Settings.cpp \ + SettingsHandler.cpp \ + VideoConsumer.cpp + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= codycam.rsrc + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS= be media translation netapi +ifeq ($(shell uname -m), BePC) +LIBS += stdc++.r4 +else +LIBS += mslcpp_4_0 +endif + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= FULL + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES= + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = + +# specify debug settings +# if TRUE will allow application to be run from a source-level +# debugger. Note that this will disable all optimzation. +DEBUGGER = + +# specify additional compiler flags for all files +COMPILER_FLAGS = + +# specify additional linker flags +LINKER_FLAGS = + + +## include the makefile-engine +include /boot/develop/etc/makefile-engine +