tools/qvfb/qvfb.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the tools applications of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
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     // When compiling with Qt 3 we need to create the menu first to
00182     // avoid scroll bars in the main window
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                 // assumes same depth and rotation
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     // If we failed to get a skin or we were not supplied
00258     //      with one then fallback to a framebuffer without
00259     //      a skin
00260     if (!skin){
00261   // Default size
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       //unset fixed size:
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;          // avoid scrollbar
00282 #endif
00283   scroller->show();
00284   // delete defaultbuttons.conf if it was left behind...
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     // Resize QVFb to the new size
00294     QSize newSize = view->sizeHint();
00295 
00296     // ... fudge factor
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 &currentSkin)
00489 {
00490     skinnames.clear();
00491     skinfiles.clear();
00492     QDir dir(":/skins/","*.skin");
00493     const QFileInfoList l = dir.entryInfoList();
00494     int i = 1; // "None" is already in list at index 0
00495     for (QFileInfoList::const_iterator it = l.begin(); it != l.end(); ++it) {
00496   skinnames.append((*it).baseName()); // should perhaps be in file
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     // Need to block signals, because we connect to animateClick(),
00526     // since QCheckBox doesn't have setChecked(bool) in 2.x.
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(); // not settable yet
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); // unless changed by settings below
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) { // Browse... ?
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     // Create the animation record UI dialog
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     // clean up
00767     removeTemporaryFiles();
00768     delete animation;
00769 }
00770 
00771 // returns true if we have ppmtompeg command, else returns false
00772 bool AnimationSaveWidget::detectPpmtoMpegCommand()
00773 {
00774     // search the PATH for the ppmtompeg command to test we can record to mpeg
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     // Recording timer
00787     if ( te->timerId() == timerId ) {
00788 
00789   // Add a frame to the animation
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());//QPoint(0,0));
00794   imageNum++;
00795 
00796   // Update the display of number of seconds that have been recorded.
00797   int tmMsec = tm.elapsed();
00798   timeDpy->setText( str.sprintf("%02d:%02d", tmMsec/60000, (tmMsec%60000)/1000) );
00799   QObject::timerEvent( te );
00800 
00801   // Make the recording LED blink
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     // Saving progress timer
00816     if ( te->timerId() == progressTimerId ) {
00817   // Parse output log file to work out the encoding progress.
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       // example of the output log entries
00825       //   During each frame:
00826       //      "FRAME 764 (B):  I BLOCKS:  0......
00827       //   When complete:
00828       //      "======FRAMES READ:  766"
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   // Update the progress bar with the frame we are up to
00842   progressBar->setValue( largestNum );
00843 
00844   // Finished saving
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 // Takes the saved ppm files and converts them to a mpeg file named filename
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     // Build parameter file required by ppmtompeg
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";      // Quality
00893   t << "BUFFER_SIZE 2048\n";
00894     }
00895     file.close();
00896 
00897     // ### can't use QProcess, not in Qt 2.3
00898     // Execute the ppmtompeg command as a seperate process to do the encoding
00899     pid_t pid = ::fork();
00900     if ( !pid ) {
00901   // Child process
00902   // redirect stdout to log file
00903   freopen("/tmp/qvfb_tmp_output.log", "w", stdout);
00904   // ppmtompeg tool is from the netpbm package
00905   ::execlp("ppmtompeg", "ppmtompeg", "/tmp/qvfb_tmp_ppmtompeg.params", (void *)0);
00906   exit(0);
00907     }
00908 
00909     // Update the saving progress bar every 200ms
00910     progressTimerId = startTimer( 200 );
00911 }
00912 
00913 // Cleanup temporary files created during creating a mpeg file
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 // toggles between recording and paused (usually when record button clicked)
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 // Reset everything to initial state of not recording
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 // Prompt for filename to save to and put animation in that file
00973 void AnimationSaveWidget::save()
00974 {
00975     if ( recording )
00976   toggleRecord(); // pauses
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"

Generated on Thu Mar 15 12:03:19 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1