00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "qvfb.h"
00025 #include "qvfbview.h"
00026 #include "qvfbratedlg.h"
00027 #include "ui_config.h"
00028 #include "skin.h"
00029 #include "qanimationwriter.h"
00030
00031 #include <QMenuBar>
00032 #include <QMenu>
00033 #include <QApplication>
00034 #include <QMessageBox>
00035 #include <QComboBox>
00036 #include <QLabel>
00037 #include <QFileDialog>
00038 #include <QSlider>
00039 #include <QSpinBox>
00040 #include <QLayout>
00041 #include <QRadioButton>
00042 #include <QImage>
00043 #include <QPixmap>
00044 #include <QCheckBox>
00045 #include <QCursor>
00046 #include <QTime>
00047 #include <QScrollArea>
00048 #include <QProgressBar>
00049 #include <QPushButton>
00050 #include <QTextStream>
00051 #include <QFile>
00052 #include <QDebug>
00053
00054 #include <unistd.h>
00055 #include <stdlib.h>
00056 #include <sys/types.h>
00057
00058
00059
00060 static const char *red_on_led_xpm[] = {
00061 "11 11 10 1",
00062 " c None",
00063 ". c #FF0000",
00064 "+ c #FF4C4C",
00065 "@ c #FF7A7A",
00066 "# c #D30000",
00067 "$ c #FF9393",
00068 "% c #BA0000",
00069 "& c #FFFFFF",
00070 "* c #7F0000",
00071 "= c #000000",
00072 " ",
00073 " .++@@ ",
00074 " .....+@ ",
00075 " ##...$.+@ ",
00076 " %#..$&$.+ ",
00077 " *#...$..+ ",
00078 " *%#...... ",
00079 " =*%#..... ",
00080 " =*%###. ",
00081 " ===*. ",
00082 " "};
00083
00084 static const char *red_off_led_xpm[] = {
00085 "11 11 12 1",
00086 " c None",
00087 ". c #CDB7B4",
00088 "+ c #D2BFBD",
00089 "@ c #DBCBCA",
00090 "# c #E5D9D8",
00091 "$ c #BC9E9B",
00092 "% c #E2D6D5",
00093 "& c #AD8986",
00094 "* c #FFFFFF",
00095 "= c #A8817D",
00096 "- c #B2908D",
00097 "; c #6F4D4A",
00098 " ",
00099 " .++@# ",
00100 " .....@# ",
00101 " $$...%.@# ",
00102 " &$..%*%.@ ",
00103 " =-...%..+ ",
00104 " =&-...... ",
00105 " ;==-..... ",
00106 " ;=&-$$. ",
00107 " ;==&$ ",
00108 " "};
00109
00110
00111
00112 class AnimationSaveWidget : public QWidget {
00113 Q_OBJECT
00114 public:
00115 AnimationSaveWidget(QVFbView *v);
00116 ~AnimationSaveWidget();
00117 bool detectPpmtoMpegCommand();
00118 void timerEvent(QTimerEvent *te);
00119 void convertToMpeg(QString filename);
00120 void removeTemporaryFiles();
00121 protected slots:
00122 void toggleRecord();
00123 void reset();
00124 void save();
00125 private:
00126 QVFbView *view;
00127 QProgressBar *progressBar;
00128 QLabel *statusText;
00129 bool haveMpeg, savingAsMpeg, recording;
00130 QCheckBox *mpegSave;
00131 QAnimationWriter *animation;
00132 QPushButton *recBt, *resetBt, *saveBt;
00133 QLabel *timeDpy, *recLED;
00134 int timerId, progressTimerId;
00135 QPixmap recOn, recOff;
00136 QTime tm;
00137 int elapsed, imageNum;
00138 };
00139
00140
00141
00142 Zoomer::Zoomer(QVFb* target) :
00143 qvfb(target)
00144 {
00145 QVBoxLayout *layout = new QVBoxLayout(this);
00146 QSlider *sl = new QSlider(Qt::Horizontal);
00147 sl->setMinimum(10);
00148 sl->setMaximum(64);
00149 sl->setPageStep(1);
00150 sl->setValue(32);
00151 layout->addWidget(sl);
00152 connect(sl,SIGNAL(valueChanged(int)),this,SLOT(zoom(int)));
00153 label = new QLabel();
00154 layout->addWidget(label);
00155 }
00156
00157 void Zoomer::zoom(int z)
00158 {
00159 double d = (double)z/32.0;
00160 qvfb->setZoom(d);
00161 label->setText(QString::number(d,'g',2));
00162 }
00163
00164
00165
00166 QVFb::QVFb( int display_id, int w, int h, int d, int r, const QString &skin, QWidget *parent, Qt::WindowFlags flags )
00167 : QMainWindow( parent, flags )
00168 {
00169 view = 0;
00170 secondaryView = 0;
00171 scroller = 0;
00172 this->skin = 0;
00173 currentSkinIndex = -1;
00174 findSkins(skin);
00175 zoomer = 0;
00176 QPixmap pix(":/res/images/logo.png");
00177 setWindowIcon( pix );
00178 rateDlg = 0;
00179 refreshRate = 30;
00180 #if QT_VERSION >= 0x030000
00181
00182
00183 createMenu( menuBar() );
00184 init( display_id, w, h, d, r, skin );
00185 enableCursor( true );
00186 #else
00187 init( display_id, w, h, d, r, skin );
00188 createMenu( menuBar() );
00189 #endif
00190 }
00191
00192 QVFb::~QVFb()
00193 {
00194 }
00195
00196 void QVFb::popupMenu()
00197 {
00198 QMenu *pm = new QMenu( this );
00199 createMenu( pm );
00200 pm->exec(QCursor::pos());
00201 }
00202
00203 void QVFb::init( int display_id, int pw, int ph, int d, int r, const QString& skin_name )
00204 {
00205 delete view;
00206 view = 0;
00207 delete secondaryView;
00208 secondaryView = 0;
00209 delete scroller;
00210 scroller = 0;
00211 delete skin;
00212 skin = 0;
00213
00214 skinscaleH = skinscaleV = 1.0;
00215 QVFbView::Rotation rot = ((r == 90) ? QVFbView::Rot90 :
00216 ((r == 180) ? QVFbView::Rot180 :
00217 ((r == 270) ? QVFbView::Rot270 :
00218 QVFbView::Rot0 )));
00219 if ( !skin_name.isEmpty() ) {
00220 bool vis = isVisible();
00221 int sw, sh;
00222 skin = new Skin( this, skin_name, sw, sh );
00223 if (skin && skin->isValid()){
00224 if ( !pw ) pw = sw;
00225 if ( !ph ) ph = sh;
00226 if ( vis ) hide();
00227 menuBar()->hide();
00228 scroller = 0;
00229 view = new QVFbView( display_id, pw, ph, d, rot, skin );
00230 skin->setView( view );
00231 view->setContentsMargins( 0, 0, 0, 0 );
00232 view->setFixedSize( sw, sh );
00233
00234 setCentralWidget( skin );
00235 adjustSize();
00236 skinscaleH = (double)sw/pw;
00237 skinscaleV = (double)sh/ph;
00238 if ( skinscaleH != 1.0 || skinscaleH != 1.0 )
00239 setZoom(skinscaleH);
00240 view->show();
00241
00242 if (Skin::hasSecondaryScreen(skin_name)) {
00243 QSize ssize = Skin::secondaryScreenSize(skin_name);
00244
00245 secondaryView = new QVFbView( display_id+1, ssize.width(), ssize.height(), d, rot, skin );
00246 skin->setSecondaryView(secondaryView);
00247 secondaryView->show();
00248 }
00249
00250 if ( vis ) show();
00251 } else {
00252 delete skin;
00253 skin = 0;
00254 }
00255 }
00256
00257
00258
00259
00260 if (!skin){
00261
00262 if ( !pw ) pw = 240;
00263 if ( !ph ) ph = 320;
00264
00265 if ( currentSkinIndex!=-1 ) {
00266 clearMask();
00267 setParent( 0, 0 );
00268 move( pos() );
00269 show();
00270
00271 setMinimumSize(0,0);
00272 setMaximumSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX);
00273 }
00274 menuBar()->show();
00275 scroller = new QScrollArea(this);
00276 view = new QVFbView( display_id, pw, ph, d, rot, scroller );
00277 scroller->setWidget(view);
00278 view->setContentsMargins( 0, 0, 0, 0 );
00279 setCentralWidget(scroller);
00280 #if QT_VERSION >= 0x030000
00281 ph += 2;
00282 #endif
00283 scroller->show();
00284
00285 unlink(QFileInfo(QString("/tmp/qtembedded-%1/defaultbuttons.conf").arg(view->displayId())).absoluteFilePath().toLatin1().constData());
00286 if (secondaryView)
00287 unlink(QFileInfo(QString("/tmp/qtembedded-%1/defaultbuttons.conf").arg(view->displayId()+1)).absoluteFilePath().toLatin1().constData());
00288 }
00289 view->setRate(refreshRate);
00290 if (secondaryView) {
00291 secondaryView->setRate(refreshRate);
00292 }
00293
00294 QSize newSize = view->sizeHint();
00295
00296
00297 newSize += QSize(20, 35);
00298
00299 resize(newSize);
00300
00301 setWindowTitle(QString("Virtual framebuffer %1x%2 %3bpp Display :%4 Rotate %5")
00302 .arg(view->displayWidth()).arg(view->displayHeight())
00303 .arg(d).arg(display_id).arg(r));
00304 }
00305
00306 void QVFb::enableCursor( bool e )
00307 {
00308 if ( skin && skin->hasCursor() ) {
00309 view->setCursor( Qt::BlankCursor );
00310 if (secondaryView)
00311 secondaryView->setCursor( Qt::BlankCursor );
00312 } else {
00313 view->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor );
00314 if (secondaryView)
00315 secondaryView->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor );
00316 }
00317 cursorAction->setChecked( e );
00318 }
00319
00320 template <typename T>
00321 void QVFb::createMenu(T *menu)
00322 {
00323 menu->addMenu( createFileMenu() );
00324 menu->addMenu( createViewMenu() );
00325 menu->addSeparator();
00326 menu->addMenu( createHelpMenu() );
00327 }
00328
00329 QMenu* QVFb::createFileMenu()
00330 {
00331 QMenu *file = new QMenu( "&File", this );
00332 file->addAction( "Configure...", this, SLOT(configure()), 0 );
00333 file->addSeparator();
00334 file->addAction( "Save image...", this, SLOT(saveImage()), 0 );
00335 file->addAction( "Animation...", this, SLOT(toggleAnimation()), 0 );
00336 file->addSeparator();
00337 file->addAction( "Quit", qApp, SLOT(quit()) );
00338 return file;
00339 }
00340
00341 QMenu* QVFb::createViewMenu()
00342 {
00343 viewMenu = new QMenu( "&View", this );
00344 cursorAction = viewMenu->addAction( "Show &Cursor", this,
00345 SLOT(toggleCursor()) );
00346 cursorAction->setCheckable(true);
00347 if ( view )
00348 enableCursor(true);
00349 viewMenu->addAction( "&Refresh Rate...", this, SLOT(changeRate()) );
00350 viewMenu->addSeparator();
00351 viewMenu->addAction( "Zoom scale &0.5", this, SLOT(setZoomHalf()) );
00352 viewMenu->addAction( "Zoom scale 0.75", this, SLOT(setZoom075()) );
00353 viewMenu->addAction( "Zoom scale &1", this, SLOT(setZoom1()) );
00354 viewMenu->addAction( "Zoom scale &2", this, SLOT(setZoom2()) );
00355 viewMenu->addAction( "Zoom scale &3", this, SLOT(setZoom3()) );
00356 viewMenu->addAction( "Zoom scale &4", this, SLOT(setZoom4()) );
00357 viewMenu->addSeparator();
00358 viewMenu->addAction( "Zoom scale...", this, SLOT(setZoom()) );
00359 return viewMenu;
00360 }
00361
00362
00363 QMenu* QVFb::createHelpMenu()
00364 {
00365 QMenu *help = new QMenu( "&Help", this );
00366 help->addAction("About...", this, SLOT(about()));
00367 return help;
00368 }
00369
00370 void QVFb::setZoom(double z)
00371 {
00372 view->setZoom(z,z*skinscaleV/skinscaleH);
00373 if (secondaryView)
00374 secondaryView->setZoom(z,z*skinscaleV/skinscaleH);
00375
00376 if (skin) {
00377 skin->setZoom(z/skinscaleH);
00378 view->setFixedSize(
00379 int(view->displayWidth()*z),
00380 int(view->displayHeight()*z*skinscaleV/skinscaleH));
00381 if (secondaryView)
00382 secondaryView->setFixedSize(
00383 int(secondaryView->displayWidth()*z),
00384 int(secondaryView->displayHeight()*z*skinscaleV/skinscaleH));
00385 }
00386 }
00387
00388 void QVFb::setZoomHalf()
00389 {
00390 setZoom(0.5);
00391 }
00392
00393 void QVFb::setZoom075()
00394 {
00395 setZoom(0.75);
00396 }
00397
00398 void QVFb::setZoom1()
00399 {
00400 setZoom(1);
00401 }
00402
00403 void QVFb::setZoom()
00404 {
00405 if ( !zoomer )
00406 zoomer = new Zoomer(this);
00407 zoomer->show();
00408 }
00409
00410 void QVFb::setZoom2()
00411 {
00412 setZoom(2);
00413 }
00414
00415 void QVFb::setZoom3()
00416 {
00417 setZoom(3);
00418 }
00419
00420 void QVFb::setZoom4()
00421 {
00422 setZoom(4);
00423 }
00424
00425 void QVFb::saveImage()
00426 {
00427 QImage img = view->image();
00428 QString filename = QFileDialog::getSaveFileName(this, "Save Main Screen image", "snapshot.png", "Portable Network Graphics (*.png)");
00429 if (!filename.isEmpty())
00430 img.save(filename,"PNG");
00431 if (secondaryView) {
00432 QImage img = view->image();
00433 QString filename = QFileDialog::getSaveFileName(this, "Save Second Screen image", "snapshot.png", "Portable Network Graphics (*.png)");
00434 if (!filename.isEmpty())
00435 img.save(filename,"PNG");
00436 }
00437 }
00438
00439 void QVFb::toggleAnimation()
00440 {
00441 static AnimationSaveWidget *animWidget = 0;
00442 if ( !animWidget )
00443 animWidget = new AnimationSaveWidget(view);
00444 if ( animWidget->isVisible() )
00445 animWidget->hide();
00446 else
00447 animWidget->show();
00448 }
00449
00450 void QVFb::toggleCursor()
00451 {
00452 enableCursor(cursorAction->isChecked());
00453 }
00454
00455 void QVFb::changeRate()
00456 {
00457 if ( !rateDlg ) {
00458 rateDlg = new QVFbRateDialog( refreshRate, this );
00459 connect( rateDlg, SIGNAL(updateRate(int)), this, SLOT(setRate(int)) );
00460 }
00461
00462 rateDlg->show();
00463 }
00464
00465 void QVFb::setRate(int i)
00466 {
00467 refreshRate = i;
00468 view->setRate(i);
00469 if (secondaryView)
00470 secondaryView->setRate(i);
00471 }
00472
00473
00474 void QVFb::about()
00475 {
00476 QMessageBox::about(this, "About QVFB",
00477 "<h2>The Qtopia Core Virtual X11 Framebuffer</h2>"
00478 "<p>This application runs under Qt/X11, emulating a simple framebuffer, "
00479 "which the Qtopia Core server and clients can attach to just as if "
00480 "it was a hardware Linux framebuffer. "
00481 "<p>With the aid of this development tool, you can develop Qtopia Core "
00482 "applications under X11 without having to switch to a virtual console. "
00483 "This means you can comfortably use your other development tools such "
00484 "as GUI profilers and debuggers."
00485 );
00486 }
00487
00488 void QVFb::findSkins(const QString ¤tSkin)
00489 {
00490 skinnames.clear();
00491 skinfiles.clear();
00492 QDir dir(":/skins/","*.skin");
00493 const QFileInfoList l = dir.entryInfoList();
00494 int i = 1;
00495 for (QFileInfoList::const_iterator it = l.begin(); it != l.end(); ++it) {
00496 skinnames.append((*it).baseName());
00497 skinfiles.append((*it).filePath());
00498 if (((*it).baseName() + ".skin") == currentSkin)
00499 currentSkinIndex = i;
00500 i++;
00501 }
00502 }
00503
00504 class Config : public QDialog, public Ui::Config
00505 {
00506 public:
00507 Config(QWidget *parent)
00508 : QDialog(parent)
00509 {
00510 setupUi(this);
00511 setModal(true);
00512
00513 connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
00514 connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
00515 }
00516 };
00517
00518 void QVFb::configure()
00519 {
00520 config = new Config(this);
00521
00522 int w = view->displayWidth();
00523 int h = view->displayHeight();
00524
00525
00526
00527 chooseSize(QSize(w,h));
00528 config->skin->insertItems(config->skin->count(), skinnames);
00529 if (currentSkinIndex > 0)
00530 config->skin->setCurrentIndex(currentSkinIndex);
00531 config->skin->addItem(tr("Browse..."));
00532 config->touchScreen->setChecked(view->touchScreenEmulation());
00533 config->lcdScreen->setChecked(view->lcdScreenEmulation());
00534 config->depth_1->setChecked(view->displayDepth()==1);
00535 config->depth_4gray->setChecked(view->displayDepth()==4);
00536 config->depth_8->setChecked(view->displayDepth()==8);
00537 config->depth_12->setChecked(view->displayDepth()==12);
00538 config->depth_16->setChecked(view->displayDepth()==16);
00539 config->depth_18->setChecked(view->displayDepth()==18);
00540 config->depth_24->setChecked(view->displayDepth()==24);
00541 config->depth_32->setChecked(view->displayDepth()==32);
00542 connect(config->skin, SIGNAL(activated(int)), this, SLOT(skinConfigChosen(int)));
00543 if ( view->gammaRed() == view->gammaGreen() && view->gammaGreen() == view->gammaBlue() ) {
00544 config->gammaslider->setValue(int(view->gammaRed()*400));
00545 config->rslider->setValue(100);
00546 config->gslider->setValue(100);
00547 config->bslider->setValue(100);
00548 } else {
00549 config->gammaslider->setValue(100);
00550 config->rslider->setValue(int(view->gammaRed()*400));
00551 config->gslider->setValue(int(view->gammaGreen()*400));
00552 config->bslider->setValue(int(view->gammaBlue()*400));
00553 }
00554 connect(config->gammaslider, SIGNAL(valueChanged(int)), this, SLOT(setGamma400(int)));
00555 connect(config->rslider, SIGNAL(valueChanged(int)), this, SLOT(setR400(int)));
00556 connect(config->gslider, SIGNAL(valueChanged(int)), this, SLOT(setG400(int)));
00557 connect(config->bslider, SIGNAL(valueChanged(int)), this, SLOT(setB400(int)));
00558 updateGammaLabels();
00559
00560 double ogr=view->gammaRed(), ogg=view->gammaGreen(), ogb=view->gammaBlue();
00561 qApp->setQuitOnLastWindowClosed(false);
00562
00563 hide();
00564 if ( config->exec() ) {
00565 int id = view->displayId();
00566 if ( config->size_176_220->isChecked() ) {
00567 w=176; h=220;
00568 } else if ( config->size_240_320->isChecked() ) {
00569 w=240; h=320;
00570 } else if ( config->size_320_240->isChecked() ) {
00571 w=320; h=240;
00572 } else if ( config->size_640_480->isChecked() ) {
00573 w=640; h=480;
00574 } else if ( config->size_800_600->isChecked() ) {
00575 w=800; h=600;
00576 } else if ( config->size_1024_768->isChecked() ) {
00577 w=1024; h=768;
00578 } else {
00579 w=config->size_width->value();
00580 h=config->size_height->value();
00581 }
00582 int d;
00583 if ( config->depth_1->isChecked() )
00584 d=1;
00585 else if ( config->depth_4gray->isChecked() )
00586 d=4;
00587 else if ( config->depth_8->isChecked() )
00588 d=8;
00589 else if ( config->depth_12->isChecked() )
00590 d=12;
00591 else if ( config->depth_16->isChecked() )
00592 d=16;
00593 else if ( config->depth_18->isChecked() )
00594 d=18;
00595 else if ( config->depth_24->isChecked() )
00596 d=24;
00597 else
00598 d=32;
00599 int skinIndex = config->skin->currentIndex();
00600 if ( w != view->displayWidth() || h != view->displayHeight()
00601 || d != view->displayDepth() || skinIndex != currentSkinIndex ) {
00602 QVFbView::Rotation rot = view->displayRotation();
00603 int r = ((rot == QVFbView::Rot90) ? 90 :
00604 ((rot == QVFbView::Rot180) ? 180 :
00605 ((rot == QVFbView::Rot270) ? 270 : 0 )));
00606 currentSkinIndex = skinIndex;
00607 init( id, w, h, d, r, skinIndex > 0 ? skinfiles[skinIndex-1] : QString::null );
00608 }
00609 view->setTouchscreenEmulation( config->touchScreen->isChecked() );
00610 bool lcdEmulation = config->lcdScreen->isChecked();
00611 view->setLcdScreenEmulation( lcdEmulation );
00612 if ( lcdEmulation )
00613 setZoom3();
00614 } else {
00615 view->setGamma(ogr, ogg, ogb);
00616 }
00617 show();
00618 qApp->setQuitOnLastWindowClosed(true);
00619 delete config;
00620 config=0;
00621 }
00622
00623 void QVFb::chooseSize(const QSize& sz)
00624 {
00625 config->size_width->blockSignals(true);
00626 config->size_height->blockSignals(true);
00627 config->size_width->setValue(sz.width());
00628 config->size_height->setValue(sz.height());
00629 config->size_width->blockSignals(false);
00630 config->size_height->blockSignals(false);
00631 config->size_custom->setChecked(true);
00632 config->size_176_220->setChecked(sz == QSize(176,220));
00633 config->size_240_320->setChecked(sz == QSize(240,320));
00634 config->size_320_240->setChecked(sz == QSize(320,240));
00635 config->size_640_480->setChecked(sz == QSize(640,480));
00636 config->size_800_600->setChecked(sz == QSize(800,600));
00637 config->size_1024_768->setChecked(sz == QSize(1024,768));
00638 }
00639
00640 void QVFb::skinConfigChosen(int i)
00641 {
00642 if (i == config->skin->count() - 1) {
00643 QFileDialog dlg(this);
00644 dlg.setFileMode(QFileDialog::DirectoryOnly);
00645 dlg.setWindowTitle(tr("Load Custom Skin..."));
00646 dlg.setFilter(tr("All QVFB Skins (*.skin)"));
00647 dlg.setDirectory(QDir::current());
00648 if (dlg.exec() && dlg.selectedFiles().count() == 1) {
00649 skinfiles.append(dlg.selectedFiles().first());
00650 i = skinfiles.count();
00651 config->skin->insertItem(i, QFileInfo(skinfiles.last()).baseName());
00652 config->skin->setCurrentIndex(i);
00653 } else {
00654 i = 0;
00655 }
00656 }
00657 if ( i ) {
00658 chooseSize(Skin::screenSize(skinfiles[i-1]));
00659 }
00660 }
00661
00662 void QVFb::setGamma400(int n)
00663 {
00664 double g = n/100.0;
00665 view->setGamma(config->rslider->value()/100.0*g,
00666 config->gslider->value()/100.0*g,
00667 config->bslider->value()/100.0*g);
00668 updateGammaLabels();
00669 }
00670
00671 void QVFb::setR400(int n)
00672 {
00673 double g = n/100.0;
00674 view->setGamma(config->rslider->value()/100.0*g,
00675 view->gammaGreen(),
00676 view->gammaBlue());
00677 updateGammaLabels();
00678 }
00679
00680 void QVFb::setG400(int n)
00681 {
00682 double g = n/100.0;
00683 view->setGamma(view->gammaRed(),
00684 config->gslider->value()/100.0*g,
00685 view->gammaBlue());
00686 updateGammaLabels();
00687 }
00688
00689 void QVFb::setB400(int n)
00690 {
00691 double g = n/100.0;
00692 view->setGamma(view->gammaRed(),
00693 view->gammaGreen(),
00694 config->bslider->value()/100.0*g);
00695 updateGammaLabels();
00696 }
00697
00698 void QVFb::updateGammaLabels()
00699 {
00700 config->rlabel->setText(QString::number(view->gammaRed(),'g',2));
00701 config->glabel->setText(QString::number(view->gammaGreen(),'g',2));
00702 config->blabel->setText(QString::number(view->gammaBlue(),'g',2));
00703 }
00704
00705 QSize QVFb::sizeHint() const
00706 {
00707 return QSize(int(view->displayWidth()*view->zoomH()),
00708 int(menuBar()->height()+view->displayHeight()*view->zoomV()));
00709 }
00710
00711
00712
00713 AnimationSaveWidget::AnimationSaveWidget(QVFbView *v) :
00714 QWidget((QWidget*)0,0),
00715 view(v), recording(false), animation(0),
00716 timerId(-1), progressTimerId(-1),
00717 recOn(red_on_led_xpm), recOff(red_off_led_xpm),
00718 imageNum(0)
00719 {
00720
00721 QVBoxLayout *vlayout = new QVBoxLayout( this );
00722
00723 QWidget *hbox = new QWidget( this );
00724 vlayout->addWidget(hbox);
00725 QHBoxLayout *hlayout = new QHBoxLayout(hbox);
00726 recBt = new QPushButton( tr("Record"), hbox );
00727 hlayout->addWidget(recBt);
00728 resetBt = new QPushButton( tr("Reset"), hbox );
00729 hlayout->addWidget(resetBt);
00730 saveBt = new QPushButton( tr("Save"), hbox );
00731 hlayout->addWidget(saveBt);
00732 recBt->setFixedWidth( 100 );
00733 resetBt->setFixedWidth( 100 );
00734 saveBt->setFixedWidth( 100 );
00735 timeDpy = new QLabel( "00:00", hbox );
00736 hlayout->addWidget(timeDpy);
00737 recLED = new QLabel( hbox );
00738 hlayout->addWidget(recLED);
00739 recLED->setPixmap( recOff );
00740 timeDpy->setMargin( 5 );
00741 connect( recBt, SIGNAL(clicked()), this, SLOT(toggleRecord()) );
00742 connect( resetBt, SIGNAL(clicked()), this, SLOT(reset()) );
00743 connect( saveBt, SIGNAL(clicked()), this, SLOT(save()) );
00744 elapsed = 0;
00745 vlayout->setMargin( 5 );
00746 vlayout->setSpacing( 5 );
00747 haveMpeg = detectPpmtoMpegCommand();
00748 mpegSave = new QCheckBox( tr("Save in MPEG format (requires netpbm package installed)"), this );
00749 vlayout->addWidget(mpegSave);
00750 mpegSave->setChecked( haveMpeg );
00751 mpegSave->setEnabled( haveMpeg );
00752 savingAsMpeg = haveMpeg;
00753 QWidget *hbox2 = new QWidget( this );
00754 vlayout->addWidget(hbox2);
00755 QHBoxLayout *hlayout2 = new QHBoxLayout( hbox2 );
00756 statusText = new QLabel( tr("Click record to begin recording."), hbox2 );
00757 hlayout2->addWidget(statusText);
00758 progressBar = new QProgressBar( hbox2 );
00759 progressBar->setValue( 0 );
00760 hlayout2->addWidget(progressBar);
00761 progressBar->hide();
00762 }
00763
00764 AnimationSaveWidget::~AnimationSaveWidget()
00765 {
00766
00767 removeTemporaryFiles();
00768 delete animation;
00769 }
00770
00771
00772 bool AnimationSaveWidget::detectPpmtoMpegCommand()
00773 {
00774
00775 QStringList paths = QString(::getenv("PATH")).split(":");
00776 for ( int i = 0; i < paths.count(); i++ )
00777 if ( QFile::exists( paths[i] + "/" + "ppmtompeg" ) )
00778 return true;
00779 return false;
00780 }
00781
00782 void AnimationSaveWidget::timerEvent( QTimerEvent *te )
00783 {
00784 QString str;
00785
00786
00787 if ( te->timerId() == timerId ) {
00788
00789
00790 if ( savingAsMpeg && view )
00791 view->image().save( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", imageNum), "PPM");
00792 else if ( animation && view )
00793 animation->appendFrame(view->image());
00794 imageNum++;
00795
00796
00797 int tmMsec = tm.elapsed();
00798 timeDpy->setText( str.sprintf("%02d:%02d", tmMsec/60000, (tmMsec%60000)/1000) );
00799 QObject::timerEvent( te );
00800
00801
00802 static int tick = 0;
00803 static bool on = false;
00804 if ( tick > 10 ) {
00805 tick = 0;
00806 if ( on )
00807 recLED->setPixmap( recOff );
00808 else
00809 recLED->setPixmap( recOn );
00810 on = !on;
00811 }
00812 tick++;
00813 }
00814
00815
00816 if ( te->timerId() == progressTimerId ) {
00817
00818 QFile f("/tmp/qvfb_tmp_output.log");
00819 f.open(QIODevice::ReadOnly);
00820 int largestNum = 0;
00821 bool done = false;
00822 char buffer[1024];
00823 while ( !f.atEnd() ) {
00824
00825
00826
00827
00828
00829 f.readLine(buffer, 1024);
00830 str = QString(buffer);
00831 if ( str.left(6) == "FRAME " ) {
00832 int num = str.mid(6, str.indexOf(QChar(' '), 6) - 6).toInt();
00833 if ( num > largestNum )
00834 largestNum = num;
00835 } else if ( str.left(18) == "======FRAMES READ:" ) {
00836 done = true;
00837 }
00838 }
00839 f.close();
00840
00841
00842 progressBar->setValue( largestNum );
00843
00844
00845 if ( done ) {
00846 progressBar->hide();
00847 statusText->setText( tr("Finished saving."));
00848 removeTemporaryFiles();
00849 killTimer( progressTimerId );
00850 progressTimerId = -1;
00851 reset();
00852 }
00853 }
00854 }
00855
00856
00857 void AnimationSaveWidget::convertToMpeg(QString filename)
00858 {
00859 recLED->setPixmap( recOff );
00860 killTimer( timerId );
00861
00862 progressBar->show();
00863 progressBar->setRange( 0, imageNum );
00864 progressBar->setValue( 0 );
00865
00866
00867 QFile file("/tmp/qvfb_tmp_ppmtompeg.params");
00868 if ( file.open( QIODevice::WriteOnly ) ) {
00869 QTextStream t( &file );
00870 t << "PATTERN IBBPBBPBBPBBPBB\n";
00871 t << "OUTPUT " << filename << "\n";
00872 t << "INPUT_DIR /tmp\n";
00873 t << "INPUT\n";
00874 QString str;
00875 str = str.sprintf("%04d", imageNum - 1);
00876 t << "qvfb_tmp_image_*.ppm [0000-" << str << "]\n";
00877 t << "END_INPUT\n";
00878 t << "BASE_FILE_FORMAT PPM\n";
00879 t << "INPUT_CONVERT *\n";
00880 t << "GOP_SIZE 15\n";
00881 t << "SLICES_PER_FRAME 1\n";
00882 t << "PIXEL HALF\n";
00883 t << "RANGE 5\n";
00884 t << "PSEARCH_ALG LOGARITHMIC\n";
00885 t << "BSEARCH_ALG SIMPLE\n";
00886 t << "IQSCALE 1\n";
00887 t << "PQSCALE 1\n";
00888 t << "BQSCALE 1\n";
00889 t << "REFERENCE_FRAME DECODED\n";
00890 t << "ASPECT_RATIO 1\n";
00891 t << "FRAME_RATE 24\n";
00892 t << "BIT_RATE 64000\n";
00893 t << "BUFFER_SIZE 2048\n";
00894 }
00895 file.close();
00896
00897
00898
00899 pid_t pid = ::fork();
00900 if ( !pid ) {
00901
00902
00903 freopen("/tmp/qvfb_tmp_output.log", "w", stdout);
00904
00905 ::execlp("ppmtompeg", "ppmtompeg", "/tmp/qvfb_tmp_ppmtompeg.params", (void *)0);
00906 exit(0);
00907 }
00908
00909
00910 progressTimerId = startTimer( 200 );
00911 }
00912
00913
00914 void AnimationSaveWidget::removeTemporaryFiles()
00915 {
00916 QString str;
00917 for ( int i = 0; i < imageNum; i++ )
00918 QFile::remove( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", i) );
00919 QFile::remove("/tmp/qvfb_tmp_ppmtompeg.params");
00920 QFile::remove("/tmp/qvfb_tmp_output.log");
00921 imageNum = 0;
00922 }
00923
00924
00925 void AnimationSaveWidget::toggleRecord()
00926 {
00927 if ( recording ) {
00928 recLED->setPixmap( recOff );
00929 recBt->setText( tr("Record") );
00930 statusText->setText( tr("Paused. Click record to resume, or save if done."));
00931 killTimer( timerId );
00932 timerId = -1;
00933 elapsed = tm.elapsed();
00934 } else {
00935 recLED->setPixmap( recOn );
00936 recBt->setText( tr("Pause") );
00937 statusText->setText( tr("Recording..."));
00938 tm.start();
00939 if ( elapsed == 0 ) {
00940 savingAsMpeg = mpegSave->isChecked();
00941 if ( !savingAsMpeg ) {
00942 delete animation;
00943 animation = new QAnimationWriter("/tmp/qvfb_tmp_animation.mng","MNG");
00944 animation->setFrameRate(24);
00945 if ( view )
00946 animation->appendFrame(view->image());
00947 }
00948 }
00949 tm = tm.addMSecs(-elapsed);
00950 elapsed = 0;
00951 timerId = startTimer(1000 / 24);
00952 }
00953 recording = !recording;
00954 }
00955
00956
00957 void AnimationSaveWidget::reset()
00958 {
00959 if ( recording ) {
00960 toggleRecord();
00961 statusText->setText( tr("Click record to begin recording."));
00962 removeTemporaryFiles();
00963 }
00964 progressBar->setValue( 0 );
00965 timeDpy->setText( "00:00" );
00966 elapsed = 0;
00967 imageNum = 0;
00968 delete animation;
00969 animation = 0;
00970 }
00971
00972
00973 void AnimationSaveWidget::save()
00974 {
00975 if ( recording )
00976 toggleRecord();
00977 statusText->setText( tr("Saving... "));
00978
00979 QString filename;
00980 if ( savingAsMpeg ) {
00981 filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mpg");
00982 if ( !filename.isNull() )
00983 convertToMpeg(filename);
00984 } else {
00985 filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mng");
00986 if (filename.isNull()) {
00987 statusText->setText(tr("Save canceled."));
00988 } else {
00989 QFile::remove(filename);
00990 bool success = QFile::rename(QLatin1String("/tmp/qvfb_tmp_animation.mng"),
00991 filename);
00992 if (success) {
00993 statusText->setText(tr("Finished saving."));
00994 reset();
00995 } else {
00996 statusText->setText(tr("Save failed!"));
00997 }
00998 }
00999 }
01000 }
01001
01002 #include "qvfb.moc"