summaryrefslogtreecommitdiffstats
path: root/tools/qvfb/qvfb.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/qvfb/qvfb.cpp
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'tools/qvfb/qvfb.cpp')
-rw-r--r--tools/qvfb/qvfb.cpp1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/tools/qvfb/qvfb.cpp b/tools/qvfb/qvfb.cpp
new file mode 100644
index 0000000..b6a715c
--- /dev/null
+++ b/tools/qvfb/qvfb.cpp
@@ -0,0 +1,1137 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvfb.h"
+#include "qvfbview.h"
+#ifdef Q_WS_X11
+#include "qvfbx11view.h"
+#endif
+#include "qvfbratedlg.h"
+#include "ui_config.h"
+#include "qanimationwriter.h"
+
+#include <deviceskin.h>
+
+#include <QMenuBar>
+#include <QMenu>
+#include <QApplication>
+#include <QMessageBox>
+#include <QComboBox>
+#include <QLabel>
+#include <QFileDialog>
+#include <QSlider>
+#include <QSpinBox>
+#include <QLayout>
+#include <QRadioButton>
+#include <QImage>
+#include <QPixmap>
+#include <QCheckBox>
+#include <QCursor>
+#include <QTime>
+#include <QScrollArea>
+#include <QProgressBar>
+#include <QPushButton>
+#include <QTextStream>
+#include <QFile>
+#include <QFileInfo>
+#include <QDebug>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+QT_BEGIN_NAMESPACE
+
+// =====================================================================
+
+static const char *red_on_led_xpm[] = {
+"11 11 10 1",
+" c None",
+". c #FF0000",
+"+ c #FF4C4C",
+"@ c #FF7A7A",
+"# c #D30000",
+"$ c #FF9393",
+"% c #BA0000",
+"& c #FFFFFF",
+"* c #7F0000",
+"= c #000000",
+" ",
+" .++@@ ",
+" .....+@ ",
+" ##...$.+@ ",
+" %#..$&$.+ ",
+" *#...$..+ ",
+" *%#...... ",
+" =*%#..... ",
+" =*%###. ",
+" ===*. ",
+" "};
+
+static const char *red_off_led_xpm[] = {
+"11 11 12 1",
+" c None",
+". c #CDB7B4",
+"+ c #D2BFBD",
+"@ c #DBCBCA",
+"# c #E5D9D8",
+"$ c #BC9E9B",
+"% c #E2D6D5",
+"& c #AD8986",
+"* c #FFFFFF",
+"= c #A8817D",
+"- c #B2908D",
+"; c #6F4D4A",
+" ",
+" .++@# ",
+" .....@# ",
+" $$...%.@# ",
+" &$..%*%.@ ",
+" =-...%..+ ",
+" =&-...... ",
+" ;==-..... ",
+" ;=&-$$. ",
+" ;==&$ ",
+" "};
+
+static bool copyButtonConfiguration(const QString &prefix, int displayId)
+{
+ const QString destDir = QString(QLatin1String("/tmp/qtembedded-%1/")).arg(displayId);
+ const QFileInfo src(prefix + QLatin1String("defaultbuttons.conf"));
+ const QFileInfo dst(destDir + QLatin1String("defaultbuttons.conf"));
+ unlink(dst.absoluteFilePath().toLatin1().constData());
+ if (!src.exists())
+ return false;
+ const bool rc = QFile::copy(src.absoluteFilePath(), dst.absoluteFilePath());
+ if (!rc)
+ qWarning() << "Failed to copy the button configuration file " << src.absoluteFilePath() << " to " << dst.absoluteFilePath() << '.';
+ return rc;
+}
+
+// =====================================================================
+
+class AnimationSaveWidget : public QWidget {
+ Q_OBJECT
+public:
+ AnimationSaveWidget(QVFbAbstractView *v);
+ ~AnimationSaveWidget();
+ bool detectPpmtoMpegCommand();
+ void timerEvent(QTimerEvent *te);
+ void convertToMpeg(QString filename);
+ void removeTemporaryFiles();
+protected slots:
+ void toggleRecord();
+ void reset();
+ void save();
+private:
+ QVFbAbstractView *view;
+ QProgressBar *progressBar;
+ QLabel *statusText;
+ bool haveMpeg, savingAsMpeg, recording;
+ QCheckBox *mpegSave;
+ QAnimationWriter *animation;
+ QPushButton *recBt, *resetBt, *saveBt;
+ QLabel *timeDpy, *recLED;
+ int timerId, progressTimerId;
+ QPixmap recOn, recOff;
+ QTime tm;
+ int elapsed, imageNum;
+};
+
+// =====================================================================
+
+Zoomer::Zoomer(QVFb* target) :
+ qvfb(target)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ QSlider *sl = new QSlider(Qt::Horizontal);
+ sl->setMinimum(10);
+ sl->setMaximum(64);
+ sl->setPageStep(1);
+ sl->setValue(32);
+ layout->addWidget(sl);
+ connect(sl,SIGNAL(valueChanged(int)),this,SLOT(zoom(int)));
+ label = new QLabel();
+ layout->addWidget(label);
+}
+
+void Zoomer::zoom(int z)
+{
+ double d = (double)z/32.0;
+ qvfb->setZoom(d);
+ label->setText(QString::number(d,'g',2));
+}
+
+// =====================================================================
+
+QVFb::QVFb( int display_id, int w, int h, int d, int r, const QString &skin, DisplayType displayType, QWidget *parent, Qt::WindowFlags flags )
+ : QMainWindow( parent, flags )
+{
+ this->displayType = displayType;
+ view = 0;
+ secondaryView = 0;
+ scroller = 0;
+ this->skin = 0;
+ currentSkinIndex = -1;
+ findSkins(skin);
+ zoomer = 0;
+ QPixmap pix(":/res/images/logo.png");
+ setWindowIcon( pix );
+ rateDlg = 0;
+ refreshRate = 30;
+#if QT_VERSION >= 0x030000
+ // When compiling with Qt 3 we need to create the menu first to
+ // avoid scroll bars in the main window
+ createMenu( menuBar() );
+ init( display_id, w, h, d, r, skin );
+ enableCursor( true );
+#else
+ init( display_id, w, h, d, r, skin );
+ createMenu( menuBar() );
+#endif
+}
+
+QVFb::~QVFb()
+{
+}
+
+void QVFb::popupMenu()
+{
+ QMenu *pm = new QMenu( this );
+ createMenu( pm );
+ pm->exec(QCursor::pos());
+}
+
+void QVFb::init( int display_id, int pw, int ph, int d, int r, const QString& skin_name )
+{
+ delete view;
+ view = 0;
+ delete secondaryView;
+ secondaryView = 0;
+ delete scroller;
+ scroller = 0;
+ delete skin;
+ skin = 0;
+
+ skinscaleH = skinscaleV = 1.0;
+ QVFbView::Rotation rot = ((r == 90) ? QVFbView::Rot90 :
+ ((r == 180) ? QVFbView::Rot180 :
+ ((r == 270) ? QVFbView::Rot270 :
+ QVFbView::Rot0 )));
+ if ( !skin_name.isEmpty() ) {
+ const bool vis = isVisible();
+ DeviceSkinParameters parameters;
+ QString readError;
+ if (parameters.read(skin_name,DeviceSkinParameters::ReadAll, &readError)) {
+ skin = new DeviceSkin(parameters, this);
+ connect(skin, SIGNAL(popupMenu()), this, SLOT(popupMenu()));
+ const int sw = parameters.screenSize().width();
+ const int sh = parameters.screenSize().height();
+ const int sd = parameters.screenDepth;
+ if (!pw) pw = sw;
+ if (!ph) ph = sh;
+ if (d < 0) {
+ if (sd)
+ d = sd;
+ else
+ d = -d;
+ }
+ if (vis)
+ hide();
+ menuBar()->hide();
+ scroller = 0;
+#ifdef Q_WS_X11
+ if (displayType == X11)
+ view = new QVFbX11View( display_id, pw, ph, d, rot, skin );
+ else
+#endif
+ view = new QVFbView( display_id, pw, ph, d, rot, skin );
+ skin->setView( view );
+ view->setContentsMargins( 0, 0, 0, 0 );
+ view->setTouchscreenEmulation(!parameters.hasMouseHover);
+ connect(skin, SIGNAL(skinKeyPressEvent(int,QString,bool)), view, SLOT(skinKeyPressEvent(int,QString,bool)));
+ connect(skin, SIGNAL(skinKeyReleaseEvent(int,QString,bool)), view, SLOT(skinKeyReleaseEvent(int,QString,bool)));
+
+ copyButtonConfiguration(skin->prefix(), view->displayId());
+
+ setCentralWidget( skin );
+ adjustSize();
+ skinscaleH = (double)sw/pw;
+ skinscaleV = (double)sh/ph;
+ if ( skinscaleH != 1.0 || skinscaleH != 1.0 )
+ setZoom(skinscaleH);
+ view->show();
+
+ if (parameters.hasSecondaryScreen()) {
+ const QSize ssize = parameters.secondaryScreenSize();
+ // assumes same depth and rotation
+#ifdef Q_WS_X11
+ if (displayType == X11)
+ secondaryView = new QVFbX11View( display_id+1, ssize.width(), ssize.height(), d, rot, skin );
+ else
+#endif
+ secondaryView = new QVFbView( display_id+1, ssize.width(), ssize.height(), d, rot, skin );
+ skin->setSecondaryView(secondaryView);
+ secondaryView->show();
+ }
+
+ if ( vis ) show();
+ } else {
+ qWarning("%s", qPrintable(readError));
+ }
+ }
+
+ // If we failed to get a skin or we were not supplied
+ // with one then fallback to a framebuffer without
+ // a skin
+ if (!skin){
+ // Default values
+ if (!pw)
+ pw = 240;
+ if (!ph)
+ ph = 320;
+ if (!d)
+ d = 32;
+ else if (d < 0)
+ d = -d;
+
+ if (currentSkinIndex != -1) {
+ clearMask();
+ setParent( 0, 0 );
+ move( pos() );
+ show();
+ //unset fixed size:
+ setMinimumSize(0,0);
+ setMaximumSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX);
+ }
+ menuBar()->show();
+ scroller = new QScrollArea(this);
+ scroller->setFocusPolicy(Qt::NoFocus); // don't steal key events from the embedded app
+#ifdef Q_WS_X11
+ if (displayType == X11)
+ view = new QVFbX11View( display_id, pw, ph, d, rot, scroller );
+ else
+#endif
+ view = new QVFbView( display_id, pw, ph, d, rot, scroller );
+ scroller->setWidget(view);
+ view->setContentsMargins( 0, 0, 0, 0 );
+ setCentralWidget(scroller);
+#if QT_VERSION >= 0x030000
+ ph += 2; // avoid scrollbar
+#endif
+ scroller->show();
+ // delete defaultbuttons.conf if it was left behind...
+ unlink(QFileInfo(QString("/tmp/qtembedded-%1/defaultbuttons.conf").arg(view->displayId())).absoluteFilePath().toLatin1().constData());
+ if (secondaryView)
+ unlink(QFileInfo(QString("/tmp/qtembedded-%1/defaultbuttons.conf").arg(view->displayId()+1)).absoluteFilePath().toLatin1().constData());
+ }
+ view->setRate(refreshRate);
+ if (secondaryView) {
+ secondaryView->setRate(refreshRate);
+ }
+ // Resize QVFb to the new size
+ QSize newSize = view->sizeHint();
+
+ // ... fudge factor
+ newSize += QSize(20, 35);
+
+ resize(newSize);
+
+ setWindowTitle(QString("Virtual framebuffer %1x%2 %3bpp Display :%4 Rotate %5")
+ .arg(view->displayWidth()).arg(view->displayHeight())
+ .arg(d).arg(display_id).arg(r));
+}
+
+void QVFb::enableCursor( bool e )
+{
+ if ( skin && skin->hasCursor() ) {
+ view->setCursor( Qt::BlankCursor );
+ if (secondaryView)
+ secondaryView->setCursor( Qt::BlankCursor );
+ } else {
+ view->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor );
+ if (secondaryView)
+ secondaryView->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor );
+ }
+ cursorAction->setChecked( e );
+}
+
+template <typename T>
+void QVFb::createMenu(T *menu)
+{
+ menu->addMenu( createFileMenu() );
+ menu->addMenu( createViewMenu() );
+ menu->addSeparator();
+ menu->addMenu( createHelpMenu() );
+}
+
+QMenu* QVFb::createFileMenu()
+{
+ QMenu *file = new QMenu( "File", this );
+ file->addAction( "Configure...", this, SLOT(configure()), 0 );
+ file->addSeparator();
+ file->addAction( "&Save image...", this, SLOT(saveImage()), 0 );
+ file->addAction( "&Animation...", this, SLOT(toggleAnimation()), 0 );
+ file->addSeparator();
+ file->addAction( "&Quit", qApp, SLOT(quit()) );
+ return file;
+}
+
+QMenu* QVFb::createViewMenu()
+{
+ viewMenu = new QMenu( "View", this );
+ cursorAction = viewMenu->addAction( "Show &Cursor", this,
+ SLOT(toggleCursor()) );
+ cursorAction->setCheckable(true);
+ if ( view )
+ enableCursor(true);
+ viewMenu->addAction( "&Refresh Rate...", this, SLOT(changeRate()) );
+ viewMenu->addSeparator();
+ viewMenu->addAction( "No rotation", this, SLOT(setRot0()) );
+ viewMenu->addAction( "90\260 rotation", this, SLOT(setRot90()) );
+ viewMenu->addAction( "180\260 rotation", this, SLOT(setRot180()) );
+ viewMenu->addAction( "270\260 rotation", this, SLOT(setRot270()) );
+ viewMenu->addSeparator();
+ viewMenu->addAction( "Zoom scale &0.5", this, SLOT(setZoomHalf()) );
+ viewMenu->addAction( "Zoom scale 0.75", this, SLOT(setZoom075()) );
+ viewMenu->addAction( "Zoom scale &1", this, SLOT(setZoom1()) );
+ viewMenu->addAction( "Zoom scale &2", this, SLOT(setZoom2()) );
+ viewMenu->addAction( "Zoom scale &3", this, SLOT(setZoom3()) );
+ viewMenu->addAction( "Zoom scale &4", this, SLOT(setZoom4()) );
+ viewMenu->addSeparator();
+ viewMenu->addAction( "Zoom scale...", this, SLOT(setZoom()) );
+ return viewMenu;
+}
+
+
+QMenu* QVFb::createHelpMenu()
+{
+ QMenu *help = new QMenu( "Help", this );
+ help->addAction("About...", this, SLOT(about()));
+ return help;
+}
+
+void QVFb::setZoom(double z)
+{
+ view->setZoom(z,z*skinscaleV/skinscaleH);
+ if (secondaryView)
+ secondaryView->setZoom(z,z*skinscaleV/skinscaleH);
+
+ if (skin) {
+ skin->setTransform(QMatrix().scale(z/skinscaleH,z/skinscaleV).rotate(90*view->displayRotation()));
+ if (secondaryView)
+ secondaryView->setFixedSize(
+ int(secondaryView->displayWidth()*z),
+ int(secondaryView->displayHeight()*z*skinscaleV/skinscaleH));
+ }
+}
+
+void QVFb::setRotation(QVFbView::Rotation r)
+{
+ view->setRotation(r);
+ if (secondaryView)
+ secondaryView->setRotation(r);
+ setZoom(view->zoomH());
+}
+
+void QVFb::setRot0()
+{
+ setRotation(QVFbView::Rot0);
+}
+
+void QVFb::setRot90()
+{
+ setRotation(QVFbView::Rot90);
+}
+
+void QVFb::setRot180()
+{
+ setRotation(QVFbView::Rot180);
+}
+
+void QVFb::setRot270()
+{
+ setRotation(QVFbView::Rot270);
+}
+
+void QVFb::setZoomHalf()
+{
+ setZoom(0.5);
+}
+
+void QVFb::setZoom075()
+{
+ setZoom(0.75);
+}
+
+void QVFb::setZoom1()
+{
+ setZoom(1);
+}
+
+void QVFb::setZoom()
+{
+ if ( !zoomer )
+ zoomer = new Zoomer(this);
+ zoomer->show();
+}
+
+void QVFb::setZoom2()
+{
+ setZoom(2);
+}
+
+void QVFb::setZoom3()
+{
+ setZoom(3);
+}
+
+void QVFb::setZoom4()
+{
+ setZoom(4);
+}
+
+void QVFb::saveImage()
+{
+ QImage img = view->image();
+ QString filename = QFileDialog::getSaveFileName(this, "Save Main Screen image", "snapshot.png", "Portable Network Graphics (*.png)");
+ if (!filename.isEmpty()){
+ if(!img.save(filename,"PNG"))
+ QMessageBox::critical(this, "Save Main Screen Image", "Save failed. Check that you have permission to write to the target directory.");
+ }
+ if (secondaryView) {
+ QImage img = view->image();
+ QString filename = QFileDialog::getSaveFileName(this, "Save Second Screen image", "snapshot.png", "Portable Network Graphics (*.png)");
+ if (!filename.isEmpty()) {
+ if(!img.save(filename,"PNG"))
+ QMessageBox::critical(this, "Save Second Screen Image", "Save failed. Check that you have permission to write to the target directory.");
+ }
+ }
+}
+
+void QVFb::toggleAnimation()
+{
+ static AnimationSaveWidget *animWidget = 0;
+ if ( !animWidget )
+ animWidget = new AnimationSaveWidget(view);
+ if ( animWidget->isVisible() )
+ animWidget->hide();
+ else
+ animWidget->show();
+}
+
+void QVFb::toggleCursor()
+{
+ enableCursor(cursorAction->isChecked());
+}
+
+void QVFb::changeRate()
+{
+ if ( !rateDlg ) {
+ rateDlg = new QVFbRateDialog( refreshRate, this );
+ connect( rateDlg, SIGNAL(updateRate(int)), this, SLOT(setRate(int)) );
+ }
+
+ rateDlg->show();
+}
+
+void QVFb::setRate(int i)
+{
+ refreshRate = i;
+ view->setRate(i);
+ if (secondaryView)
+ secondaryView->setRate(i);
+}
+
+
+void QVFb::about()
+{
+ QMessageBox::about(this, "About QVFB",
+ "<h2>The Qt for Embedded Linux Virtual X11 Framebuffer</h2>"
+ "<p>This application runs under Qt for X11, emulating a simple framebuffer, "
+ "which the Qt for Embedded Linux server and clients can attach to just as if "
+ "it was a hardware Linux framebuffer. "
+ "<p>With the aid of this development tool, you can develop Qt for Embedded "
+ "Linux applications under X11 without having to switch to a virtual console. "
+ "This means you can comfortably use your other development tools such "
+ "as GUI profilers and debuggers."
+ );
+}
+
+void QVFb::findSkins(const QString &currentSkin)
+{
+ skinnames.clear();
+ skinfiles.clear();
+ QDir dir(":/skins/","*.skin");
+ const QFileInfoList l = dir.entryInfoList();
+ int i = 1; // "None" is already in list at index 0
+ for (QFileInfoList::const_iterator it = l.begin(); it != l.end(); ++it) {
+ skinnames.append((*it).baseName()); // should perhaps be in file
+ skinfiles.append((*it).filePath());
+ if (((*it).baseName() + ".skin") == currentSkin)
+ currentSkinIndex = i;
+ i++;
+ }
+}
+
+class Config : public QDialog, public Ui::Config
+{
+public:
+ Config(QWidget *parent)
+ : QDialog(parent)
+ {
+ setupUi(this);
+ setModal(true);
+
+ connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
+ connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
+ }
+};
+
+void QVFb::configure()
+{
+ config = new Config(this);
+
+ int w = view->displayWidth();
+ int h = view->displayHeight();
+
+ // Need to block signals, because we connect to animateClick(),
+ // since QCheckBox doesn't have setChecked(bool) in 2.x.
+ chooseSize(QSize(w,h));
+ config->skin->insertItems(config->skin->count(), skinnames);
+ if (currentSkinIndex > 0)
+ config->skin->setCurrentIndex(currentSkinIndex);
+ config->skin->addItem(tr("Browse..."));
+ config->touchScreen->setChecked(view->touchScreenEmulation());
+ config->lcdScreen->setChecked(view->lcdScreenEmulation());
+ chooseDepth(view->displayDepth(), view->displayFormat());
+ connect(config->skin, SIGNAL(activated(int)), this, SLOT(skinConfigChosen(int)));
+ if ( view->gammaRed() == view->gammaGreen() && view->gammaGreen() == view->gammaBlue() ) {
+ config->gammaslider->setValue(int(view->gammaRed()*400));
+ config->rslider->setValue(100);
+ config->gslider->setValue(100);
+ config->bslider->setValue(100);
+ } else {
+ config->gammaslider->setValue(100);
+ config->rslider->setValue(int(view->gammaRed()*400));
+ config->gslider->setValue(int(view->gammaGreen()*400));
+ config->bslider->setValue(int(view->gammaBlue()*400));
+ }
+ connect(config->gammaslider, SIGNAL(valueChanged(int)), this, SLOT(setGamma400(int)));
+ connect(config->rslider, SIGNAL(valueChanged(int)), this, SLOT(setR400(int)));
+ connect(config->gslider, SIGNAL(valueChanged(int)), this, SLOT(setG400(int)));
+ connect(config->bslider, SIGNAL(valueChanged(int)), this, SLOT(setB400(int)));
+ updateGammaLabels();
+
+ double ogr=view->gammaRed(), ogg=view->gammaGreen(), ogb=view->gammaBlue();
+ qApp->setQuitOnLastWindowClosed(false);
+
+ hide();
+ if ( config->exec() ) {
+ int id = view->displayId(); // not settable yet
+ if ( config->size_176_220->isChecked() ) {
+ w=176; h=220;
+ } else if ( config->size_240_320->isChecked() ) {
+ w=240; h=320;
+ } else if ( config->size_320_240->isChecked() ) {
+ w=320; h=240;
+ } else if ( config->size_640_480->isChecked() ) {
+ w=640; h=480;
+ } else if ( config->size_800_600->isChecked() ) {
+ w=800; h=600;
+ } else if ( config->size_1024_768->isChecked() ) {
+ w=1024; h=768;
+ } else {
+ w=config->size_width->value();
+ h=config->size_height->value();
+ }
+ int d;
+ if ( config->depth_1->isChecked() )
+ d=1;
+ else if ( config->depth_4gray->isChecked() )
+ d=4;
+ else if ( config->depth_8->isChecked() )
+ d=8;
+ else if ( config->depth_12->isChecked() )
+ d=12;
+ else if ( config->depth_15->isChecked() )
+ d = 15;
+ else if ( config->depth_16->isChecked() )
+ d=16;
+ else if ( config->depth_18->isChecked() )
+ d=18;
+ else if ( config->depth_24->isChecked() )
+ d=24;
+ else
+ d=32;
+ QVFbView::PixelFormat displayFormat = config->depth_32_argb->isChecked()
+ ? QVFbView::ARGBFormat : QVFbView::DefaultFormat;
+ int skinIndex = config->skin->currentIndex();
+ if ( w != view->displayWidth() || h != view->displayHeight()
+ || d != view->displayDepth() || skinIndex != currentSkinIndex ) {
+ QVFbView::Rotation rot = view->displayRotation();
+ int r = ((rot == QVFbView::Rot90) ? 90 :
+ ((rot == QVFbView::Rot180) ? 180 :
+ ((rot == QVFbView::Rot270) ? 270 : 0 )));
+ currentSkinIndex = skinIndex;
+ init( id, w, h, d, r, skinIndex > 0 ? skinfiles[skinIndex-1] : QString::null );
+ }
+ view->setViewFormat(displayFormat);
+ view->setTouchscreenEmulation( config->touchScreen->isChecked() );
+ bool lcdEmulation = config->lcdScreen->isChecked();
+ view->setLcdScreenEmulation( lcdEmulation );
+ if ( lcdEmulation )
+ setZoom3();
+ } else {
+ view->setGamma(ogr, ogg, ogb);
+ }
+ show();
+ qApp->setQuitOnLastWindowClosed(true);
+ delete config;
+ config=0;
+}
+
+void QVFb::chooseSize(const QSize& sz)
+{
+ config->size_width->blockSignals(true);
+ config->size_height->blockSignals(true);
+ config->size_width->setValue(sz.width());
+ config->size_height->setValue(sz.height());
+ config->size_width->blockSignals(false);
+ config->size_height->blockSignals(false);
+ config->size_custom->setChecked(true); // unless changed by settings below
+ config->size_176_220->setChecked(sz == QSize(176,220));
+ config->size_240_320->setChecked(sz == QSize(240,320));
+ config->size_320_240->setChecked(sz == QSize(320,240));
+ config->size_640_480->setChecked(sz == QSize(640,480));
+ config->size_800_600->setChecked(sz == QSize(800,600));
+ config->size_1024_768->setChecked(sz == QSize(1024,768));
+}
+
+void QVFb::chooseDepth(int depth, QVFbView::PixelFormat displayFormat)
+{
+ config->depth_1->setChecked(depth==1);
+ config->depth_4gray->setChecked(depth==4);
+ config->depth_8->setChecked(depth==8);
+ config->depth_12->setChecked(depth==12);
+ config->depth_15->setChecked(depth==15);
+ config->depth_16->setChecked(depth==16);
+ config->depth_18->setChecked(depth==18);
+ config->depth_24->setChecked(depth==24);
+ config->depth_32->setChecked(depth==32 && displayFormat != QVFbView::ARGBFormat);
+ config->depth_32_argb->setChecked(depth==32 && displayFormat == QVFbView::ARGBFormat);
+}
+
+void QVFb::skinConfigChosen(int i)
+{
+ if (i == config->skin->count() - 1) { // Browse... ?
+ QFileDialog dlg(this);
+ dlg.setFileMode(QFileDialog::DirectoryOnly);
+ dlg.setWindowTitle(tr("Load Custom Skin..."));
+ dlg.setFilter(tr("All QVFB Skins (*.skin)"));
+ dlg.setDirectory(QDir::current());
+ if (dlg.exec() && dlg.selectedFiles().count() == 1) {
+ skinfiles.append(dlg.selectedFiles().first());
+ i = skinfiles.count();
+ config->skin->insertItem(i, QFileInfo(skinfiles.last()).baseName());
+ config->skin->setCurrentIndex(i);
+ } else {
+ i = 0;
+ }
+ }
+ if ( i ) {
+ DeviceSkinParameters parameters;
+ QString readError;
+ if (parameters.read(skinfiles[i-1], DeviceSkinParameters::ReadSizeOnly, &readError)) {
+ chooseSize(parameters.screenSize());
+ if (parameters.screenDepth)
+ chooseDepth(parameters.screenDepth,QVFbView::ARGBFormat);
+ config->touchScreen->setChecked(!parameters.hasMouseHover);
+ } else {
+ qWarning("%s", qPrintable(readError));
+ }
+ }
+}
+
+void QVFb::setGamma400(int n)
+{
+ double g = n/100.0;
+ view->setGamma(config->rslider->value()/100.0*g,
+ config->gslider->value()/100.0*g,
+ config->bslider->value()/100.0*g);
+ updateGammaLabels();
+}
+
+void QVFb::setR400(int n)
+{
+ double g = n/100.0;
+ view->setGamma(config->rslider->value()/100.0*g,
+ view->gammaGreen(),
+ view->gammaBlue());
+ updateGammaLabels();
+}
+
+void QVFb::setG400(int n)
+{
+ double g = n/100.0;
+ view->setGamma(view->gammaRed(),
+ config->gslider->value()/100.0*g,
+ view->gammaBlue());
+ updateGammaLabels();
+}
+
+void QVFb::setB400(int n)
+{
+ double g = n/100.0;
+ view->setGamma(view->gammaRed(),
+ view->gammaGreen(),
+ config->bslider->value()/100.0*g);
+ updateGammaLabels();
+}
+
+void QVFb::updateGammaLabels()
+{
+ config->rlabel->setText(QString::number(view->gammaRed(),'g',2));
+ config->glabel->setText(QString::number(view->gammaGreen(),'g',2));
+ config->blabel->setText(QString::number(view->gammaBlue(),'g',2));
+}
+
+QSize QVFb::sizeHint() const
+{
+ return QSize(int(view->displayWidth()*view->zoomH()),
+ int(menuBar()->height()+view->displayHeight()*view->zoomV()));
+}
+
+// =====================================================================
+
+AnimationSaveWidget::AnimationSaveWidget(QVFbAbstractView *v) :
+ QWidget((QWidget*)0,0),
+ view(v), recording(false), animation(0),
+ timerId(-1), progressTimerId(-1),
+ recOn(red_on_led_xpm), recOff(red_off_led_xpm),
+ imageNum(0)
+{
+ // Create the animation record UI dialog
+ QVBoxLayout *vlayout = new QVBoxLayout( this );
+
+ QWidget *hbox = new QWidget( this );
+ vlayout->addWidget(hbox);
+ QHBoxLayout *hlayout = new QHBoxLayout(hbox);
+ recBt = new QPushButton( tr("Record"), hbox );
+ hlayout->addWidget(recBt);
+ resetBt = new QPushButton( tr("Reset"), hbox );
+ hlayout->addWidget(resetBt);
+ saveBt = new QPushButton( tr("Save"), hbox );
+ hlayout->addWidget(saveBt);
+ recBt->setFixedWidth( 100 );
+ resetBt->setFixedWidth( 100 );
+ saveBt->setFixedWidth( 100 );
+ timeDpy = new QLabel( "00:00", hbox );
+ hlayout->addWidget(timeDpy);
+ recLED = new QLabel( hbox );
+ hlayout->addWidget(recLED);
+ recLED->setPixmap( recOff );
+ timeDpy->setMargin( 5 );
+ connect( recBt, SIGNAL(clicked()), this, SLOT(toggleRecord()) );
+ connect( resetBt, SIGNAL(clicked()), this, SLOT(reset()) );
+ connect( saveBt, SIGNAL(clicked()), this, SLOT(save()) );
+ elapsed = 0;
+ vlayout->setMargin( 5 );
+ vlayout->setSpacing( 5 );
+ haveMpeg = detectPpmtoMpegCommand();
+ mpegSave = new QCheckBox( tr("Save in MPEG format (requires netpbm package installed)"), this );
+ vlayout->addWidget(mpegSave);
+ mpegSave->setChecked( haveMpeg );
+ mpegSave->setEnabled( haveMpeg );
+ savingAsMpeg = haveMpeg;
+ QWidget *hbox2 = new QWidget( this );
+ vlayout->addWidget(hbox2);
+ QHBoxLayout *hlayout2 = new QHBoxLayout( hbox2 );
+ statusText = new QLabel( tr("Click record to begin recording."), hbox2 );
+ hlayout2->addWidget(statusText);
+ progressBar = new QProgressBar( hbox2 );
+ progressBar->setValue( 0 );
+ hlayout2->addWidget(progressBar);
+ progressBar->hide();
+}
+
+AnimationSaveWidget::~AnimationSaveWidget()
+{
+ // clean up
+ removeTemporaryFiles();
+ delete animation;
+}
+
+// returns true if we have ppmtompeg command, else returns false
+bool AnimationSaveWidget::detectPpmtoMpegCommand()
+{
+ // search the PATH for the ppmtompeg command to test we can record to mpeg
+ QStringList paths = QString(::getenv("PATH")).split(":");
+ for ( int i = 0; i < paths.count(); i++ )
+ if ( QFile::exists( paths[i] + "/" + "ppmtompeg" ) )
+ return true;
+ return false;
+}
+
+void AnimationSaveWidget::timerEvent( QTimerEvent *te )
+{
+ QString str;
+
+ // Recording timer
+ if ( te->timerId() == timerId ) {
+
+ // Add a frame to the animation
+ if ( savingAsMpeg && view )
+ view->image().save( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", imageNum), "PPM");
+ else if ( animation && view )
+ animation->appendFrame(view->image());//QPoint(0,0));
+ imageNum++;
+
+ // Update the display of number of seconds that have been recorded.
+ int tmMsec = tm.elapsed();
+ timeDpy->setText( str.sprintf("%02d:%02d", tmMsec/60000, (tmMsec%60000)/1000) );
+ QObject::timerEvent( te );
+
+ // Make the recording LED blink
+ static int tick = 0;
+ static bool on = false;
+ if ( tick > 10 ) {
+ tick = 0;
+ if ( on )
+ recLED->setPixmap( recOff );
+ else
+ recLED->setPixmap( recOn );
+ on = !on;
+ }
+ tick++;
+ }
+
+ // Saving progress timer
+ if ( te->timerId() == progressTimerId ) {
+ // Parse output log file to work out the encoding progress.
+ QFile f("/tmp/qvfb_tmp_output.log");
+ f.open(QIODevice::ReadOnly);
+ int largestNum = 0;
+ bool done = false;
+ char buffer[1024];
+ while ( !f.atEnd() ) {
+ // example of the output log entries
+ // During each frame:
+ // "FRAME 764 (B): I BLOCKS: 0......
+ // When complete:
+ // "======FRAMES READ: 766"
+ f.readLine(buffer, 1024);
+ str = QString(buffer);
+ if ( str.left(6) == "FRAME " ) {
+ int num = str.mid(6, str.indexOf(QChar(' '), 6) - 6).toInt();
+ if ( num > largestNum )
+ largestNum = num;
+ } else if ( str.left(18) == "======FRAMES READ:" ) {
+ done = true;
+ }
+ }
+ f.close();
+
+ // Update the progress bar with the frame we are up to
+ progressBar->setValue( largestNum );
+
+ // Finished saving
+ if ( done ) {
+ progressBar->hide();
+ statusText->setText( tr("Finished saving."));
+ removeTemporaryFiles();
+ killTimer( progressTimerId );
+ progressTimerId = -1;
+ reset();
+ }
+ }
+}
+
+// Takes the saved ppm files and converts them to a mpeg file named filename
+void AnimationSaveWidget::convertToMpeg(QString filename)
+{
+ recLED->setPixmap( recOff );
+ killTimer( timerId );
+
+ progressBar->show();
+ progressBar->setRange( 0, imageNum );
+ progressBar->setValue( 0 );
+
+ // Build parameter file required by ppmtompeg
+ QFile file("/tmp/qvfb_tmp_ppmtompeg.params");
+ if ( file.open( QIODevice::WriteOnly ) ) {
+ QTextStream t( &file );
+ t << "PATTERN IBBPBBPBBPBBPBB\n";
+ t << "OUTPUT " << filename << "\n";
+ t << "INPUT_DIR /tmp\n";
+ t << "INPUT\n";
+ QString str;
+ str = str.sprintf("%04d", imageNum - 1);
+ t << "qvfb_tmp_image_*.ppm [0000-" << str << "]\n";
+ t << "END_INPUT\n";
+ t << "BASE_FILE_FORMAT PPM\n";
+ t << "INPUT_CONVERT *\n";
+ t << "GOP_SIZE 15\n";
+ t << "SLICES_PER_FRAME 1\n";
+ t << "PIXEL HALF\n";
+ t << "RANGE 5\n";
+ t << "PSEARCH_ALG LOGARITHMIC\n";
+ t << "BSEARCH_ALG SIMPLE\n";
+ t << "IQSCALE 1\n";
+ t << "PQSCALE 1\n";
+ t << "BQSCALE 1\n";
+ t << "REFERENCE_FRAME DECODED\n";
+ t << "ASPECT_RATIO 1\n";
+ t << "FRAME_RATE 24\n";
+ t << "BIT_RATE 64000\n"; // Quality
+ t << "BUFFER_SIZE 2048\n";
+ }
+ file.close();
+
+ // ### can't use QProcess, not in Qt 2.3
+ // ### but it's certainly in Qt 4! use it?
+ // Execute the ppmtompeg command as a seperate process to do the encoding
+ pid_t pid = ::fork();
+ if ( !pid ) {
+ // Child process
+ // redirect stdout to log file
+ freopen("/tmp/qvfb_tmp_output.log", "w", stdout);
+ // ppmtompeg tool is from the netpbm package
+ ::execlp("ppmtompeg", "ppmtompeg", "/tmp/qvfb_tmp_ppmtompeg.params", (void *)0);
+ exit(0);
+ }
+
+ // Update the saving progress bar every 200ms
+ progressTimerId = startTimer( 200 );
+}
+
+// Cleanup temporary files created during creating a mpeg file
+void AnimationSaveWidget::removeTemporaryFiles()
+{
+ QString str;
+ for ( int i = 0; i < imageNum; i++ )
+ QFile::remove( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", i) );
+ QFile::remove("/tmp/qvfb_tmp_ppmtompeg.params");
+ QFile::remove("/tmp/qvfb_tmp_output.log");
+ imageNum = 0;
+}
+
+// toggles between recording and paused (usually when record button clicked)
+void AnimationSaveWidget::toggleRecord()
+{
+ if ( recording ) {
+ recLED->setPixmap( recOff );
+ recBt->setText( tr("Record") );
+ statusText->setText( tr("Paused. Click record to resume, or save if done."));
+ killTimer( timerId );
+ timerId = -1;
+ elapsed = tm.elapsed();
+ } else {
+ recLED->setPixmap( recOn );
+ recBt->setText( tr("Pause") );
+ statusText->setText( tr("Recording..."));
+ tm.start();
+ if ( elapsed == 0 ) {
+ savingAsMpeg = mpegSave->isChecked();
+ if ( !savingAsMpeg ) {
+ delete animation;
+ animation = new QAnimationWriter("/tmp/qvfb_tmp_animation.mng","MNG");
+ animation->setFrameRate(24);
+ if ( view )
+ animation->appendFrame(view->image());
+ }
+ }
+ tm = tm.addMSecs(-elapsed);
+ elapsed = 0;
+ timerId = startTimer(1000 / 24);
+ }
+ recording = !recording;
+}
+
+// Reset everything to initial state of not recording
+void AnimationSaveWidget::reset()
+{
+ if ( recording ) {
+ toggleRecord();
+ statusText->setText( tr("Click record to begin recording."));
+ removeTemporaryFiles();
+ }
+ progressBar->setValue( 0 );
+ timeDpy->setText( "00:00" );
+ elapsed = 0;
+ imageNum = 0;
+ delete animation;
+ animation = 0;
+}
+
+// Prompt for filename to save to and put animation in that file
+void AnimationSaveWidget::save()
+{
+ if ( recording )
+ toggleRecord(); // pauses
+ statusText->setText( tr("Saving... "));
+
+ QString filename;
+ if ( savingAsMpeg ) {
+ filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mpg");
+ if ( !filename.isNull() )
+ convertToMpeg(filename);
+ } else {
+ filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mng");
+ if (filename.isNull()) {
+ statusText->setText(tr("Save canceled."));
+ } else {
+ QFile::remove(filename);
+ bool success = QFile::rename(QLatin1String("/tmp/qvfb_tmp_animation.mng"),
+ filename);
+ if (success) {
+ statusText->setText(tr("Finished saving."));
+ reset();
+ } else {
+ statusText->setText(tr("Save failed!"));
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qvfb.moc"