source: trunk/tools/qvfb/qvfb.cpp@ 356

Last change on this file since 356 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 33.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qvfb.h"
43#include "qvfbview.h"
44#ifdef Q_WS_X11
45#include "qvfbx11view.h"
46#endif
47#include "qvfbratedlg.h"
48#include "ui_config.h"
49#include "qanimationwriter.h"
50
51#include <deviceskin.h>
52
53#include <QMenuBar>
54#include <QMenu>
55#include <QApplication>
56#include <QMessageBox>
57#include <QComboBox>
58#include <QLabel>
59#include <QFileDialog>
60#include <QSlider>
61#include <QSpinBox>
62#include <QLayout>
63#include <QRadioButton>
64#include <QImage>
65#include <QPixmap>
66#include <QCheckBox>
67#include <QCursor>
68#include <QTime>
69#include <QScrollArea>
70#include <QProgressBar>
71#include <QPushButton>
72#include <QTextStream>
73#include <QFile>
74#include <QFileInfo>
75#include <QDebug>
76
77#include <unistd.h>
78#include <stdlib.h>
79#include <sys/types.h>
80
81QT_BEGIN_NAMESPACE
82
83// =====================================================================
84
85static const char *red_on_led_xpm[] = {
86"11 11 10 1",
87" c None",
88". c #FF0000",
89"+ c #FF4C4C",
90"@ c #FF7A7A",
91"# c #D30000",
92"$ c #FF9393",
93"% c #BA0000",
94"& c #FFFFFF",
95"* c #7F0000",
96"= c #000000",
97" ",
98" .++@@ ",
99" .....+@ ",
100" ##...$.+@ ",
101" %#..$&$.+ ",
102" *#...$..+ ",
103" *%#...... ",
104" =*%#..... ",
105" =*%###. ",
106" ===*. ",
107" "};
108
109static const char *red_off_led_xpm[] = {
110"11 11 12 1",
111" c None",
112". c #CDB7B4",
113"+ c #D2BFBD",
114"@ c #DBCBCA",
115"# c #E5D9D8",
116"$ c #BC9E9B",
117"% c #E2D6D5",
118"& c #AD8986",
119"* c #FFFFFF",
120"= c #A8817D",
121"- c #B2908D",
122"; c #6F4D4A",
123" ",
124" .++@# ",
125" .....@# ",
126" $$...%.@# ",
127" &$..%*%.@ ",
128" =-...%..+ ",
129" =&-...... ",
130" ;==-..... ",
131" ;=&-$$. ",
132" ;==&$ ",
133" "};
134
135static bool copyButtonConfiguration(const QString &prefix, int displayId)
136{
137 const QString destDir = QString(QLatin1String("/tmp/qtembedded-%1/")).arg(displayId);
138 const QFileInfo src(prefix + QLatin1String("defaultbuttons.conf"));
139 const QFileInfo dst(destDir + QLatin1String("defaultbuttons.conf"));
140 unlink(dst.absoluteFilePath().toLatin1().constData());
141 if (!src.exists())
142 return false;
143 const bool rc = QFile::copy(src.absoluteFilePath(), dst.absoluteFilePath());
144 if (!rc)
145 qWarning() << "Failed to copy the button configuration file " << src.absoluteFilePath() << " to " << dst.absoluteFilePath() << '.';
146 return rc;
147}
148
149// =====================================================================
150
151class AnimationSaveWidget : public QWidget {
152 Q_OBJECT
153public:
154 AnimationSaveWidget(QVFbAbstractView *v);
155 ~AnimationSaveWidget();
156 bool detectPpmtoMpegCommand();
157 void timerEvent(QTimerEvent *te);
158 void convertToMpeg(QString filename);
159 void removeTemporaryFiles();
160protected slots:
161 void toggleRecord();
162 void reset();
163 void save();
164private:
165 QVFbAbstractView *view;
166 QProgressBar *progressBar;
167 QLabel *statusText;
168 bool haveMpeg, savingAsMpeg, recording;
169 QCheckBox *mpegSave;
170 QAnimationWriter *animation;
171 QPushButton *recBt, *resetBt, *saveBt;
172 QLabel *timeDpy, *recLED;
173 int timerId, progressTimerId;
174 QPixmap recOn, recOff;
175 QTime tm;
176 int elapsed, imageNum;
177};
178
179// =====================================================================
180
181Zoomer::Zoomer(QVFb* target) :
182 qvfb(target)
183{
184 QVBoxLayout *layout = new QVBoxLayout(this);
185 QSlider *sl = new QSlider(Qt::Horizontal);
186 sl->setMinimum(10);
187 sl->setMaximum(64);
188 sl->setPageStep(1);
189 sl->setValue(32);
190 layout->addWidget(sl);
191 connect(sl,SIGNAL(valueChanged(int)),this,SLOT(zoom(int)));
192 label = new QLabel();
193 layout->addWidget(label);
194}
195
196void Zoomer::zoom(int z)
197{
198 double d = (double)z/32.0;
199 qvfb->setZoom(d);
200 label->setText(QString::number(d,'g',2));
201}
202
203// =====================================================================
204
205QVFb::QVFb( int display_id, int w, int h, int d, int r, const QString &skin, DisplayType displayType, QWidget *parent, Qt::WindowFlags flags )
206 : QMainWindow( parent, flags )
207{
208 this->displayType = displayType;
209 view = 0;
210 secondaryView = 0;
211 scroller = 0;
212 this->skin = 0;
213 currentSkinIndex = -1;
214 findSkins(skin);
215 zoomer = 0;
216 QPixmap pix(":/res/images/logo.png");
217 setWindowIcon( pix );
218 rateDlg = 0;
219 refreshRate = 30;
220#if QT_VERSION >= 0x030000
221 // When compiling with Qt 3 we need to create the menu first to
222 // avoid scroll bars in the main window
223 createMenu( menuBar() );
224 init( display_id, w, h, d, r, skin );
225 enableCursor( true );
226#else
227 init( display_id, w, h, d, r, skin );
228 createMenu( menuBar() );
229#endif
230}
231
232QVFb::~QVFb()
233{
234}
235
236void QVFb::popupMenu()
237{
238 QMenu *pm = new QMenu( this );
239 createMenu( pm );
240 pm->exec(QCursor::pos());
241}
242
243void QVFb::init( int display_id, int pw, int ph, int d, int r, const QString& skin_name )
244{
245 delete view;
246 view = 0;
247 delete secondaryView;
248 secondaryView = 0;
249 delete scroller;
250 scroller = 0;
251 delete skin;
252 skin = 0;
253
254 skinscaleH = skinscaleV = 1.0;
255 QVFbView::Rotation rot = ((r == 90) ? QVFbView::Rot90 :
256 ((r == 180) ? QVFbView::Rot180 :
257 ((r == 270) ? QVFbView::Rot270 :
258 QVFbView::Rot0 )));
259 if ( !skin_name.isEmpty() ) {
260 const bool vis = isVisible();
261 DeviceSkinParameters parameters;
262 QString readError;
263 if (parameters.read(skin_name,DeviceSkinParameters::ReadAll, &readError)) {
264 skin = new DeviceSkin(parameters, this);
265 connect(skin, SIGNAL(popupMenu()), this, SLOT(popupMenu()));
266 const int sw = parameters.screenSize().width();
267 const int sh = parameters.screenSize().height();
268 const int sd = parameters.screenDepth;
269 if (!pw) pw = sw;
270 if (!ph) ph = sh;
271 if (d < 0) {
272 if (sd)
273 d = sd;
274 else
275 d = -d;
276 }
277 if (vis)
278 hide();
279 menuBar()->hide();
280 scroller = 0;
281#ifdef Q_WS_X11
282 if (displayType == X11)
283 view = new QVFbX11View( display_id, pw, ph, d, rot, skin );
284 else
285#endif
286 view = new QVFbView( display_id, pw, ph, d, rot, skin );
287 skin->setView( view );
288 view->setContentsMargins( 0, 0, 0, 0 );
289 view->setTouchscreenEmulation(!parameters.hasMouseHover);
290 connect(skin, SIGNAL(skinKeyPressEvent(int,QString,bool)), view, SLOT(skinKeyPressEvent(int,QString,bool)));
291 connect(skin, SIGNAL(skinKeyReleaseEvent(int,QString,bool)), view, SLOT(skinKeyReleaseEvent(int,QString,bool)));
292
293 copyButtonConfiguration(skin->prefix(), view->displayId());
294
295 setCentralWidget( skin );
296 adjustSize();
297 skinscaleH = (double)sw/pw;
298 skinscaleV = (double)sh/ph;
299 if ( skinscaleH != 1.0 || skinscaleH != 1.0 )
300 setZoom(skinscaleH);
301 view->show();
302
303 if (parameters.hasSecondaryScreen()) {
304 const QSize ssize = parameters.secondaryScreenSize();
305 // assumes same depth and rotation
306#ifdef Q_WS_X11
307 if (displayType == X11)
308 secondaryView = new QVFbX11View( display_id+1, ssize.width(), ssize.height(), d, rot, skin );
309 else
310#endif
311 secondaryView = new QVFbView( display_id+1, ssize.width(), ssize.height(), d, rot, skin );
312 skin->setSecondaryView(secondaryView);
313 secondaryView->show();
314 }
315
316 if ( vis ) show();
317 } else {
318 qWarning("%s", qPrintable(readError));
319 }
320 }
321
322 // If we failed to get a skin or we were not supplied
323 // with one then fallback to a framebuffer without
324 // a skin
325 if (!skin){
326 // Default values
327 if (!pw)
328 pw = 240;
329 if (!ph)
330 ph = 320;
331 if (!d)
332 d = 32;
333 else if (d < 0)
334 d = -d;
335
336 if (currentSkinIndex != -1) {
337 clearMask();
338 setParent( 0, 0 );
339 move( pos() );
340 show();
341 //unset fixed size:
342 setMinimumSize(0,0);
343 setMaximumSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX);
344 }
345 menuBar()->show();
346 scroller = new QScrollArea(this);
347 scroller->setFocusPolicy(Qt::NoFocus); // don't steal key events from the embedded app
348#ifdef Q_WS_X11
349 if (displayType == X11)
350 view = new QVFbX11View( display_id, pw, ph, d, rot, scroller );
351 else
352#endif
353 view = new QVFbView( display_id, pw, ph, d, rot, scroller );
354 scroller->setWidget(view);
355 view->setContentsMargins( 0, 0, 0, 0 );
356 setCentralWidget(scroller);
357#if QT_VERSION >= 0x030000
358 ph += 2; // avoid scrollbar
359#endif
360 scroller->show();
361 // delete defaultbuttons.conf if it was left behind...
362 unlink(QFileInfo(QString("/tmp/qtembedded-%1/defaultbuttons.conf").arg(view->displayId())).absoluteFilePath().toLatin1().constData());
363 if (secondaryView)
364 unlink(QFileInfo(QString("/tmp/qtembedded-%1/defaultbuttons.conf").arg(view->displayId()+1)).absoluteFilePath().toLatin1().constData());
365 }
366 view->setRate(refreshRate);
367 if (secondaryView) {
368 secondaryView->setRate(refreshRate);
369 }
370 // Resize QVFb to the new size
371 QSize newSize = view->sizeHint();
372
373 // ... fudge factor
374 newSize += QSize(20, 35);
375
376 resize(newSize);
377
378 setWindowTitle(QString("Virtual framebuffer %1x%2 %3bpp Display :%4 Rotate %5")
379 .arg(view->displayWidth()).arg(view->displayHeight())
380 .arg(d).arg(display_id).arg(r));
381}
382
383void QVFb::enableCursor( bool e )
384{
385 if ( skin && skin->hasCursor() ) {
386 view->setCursor( Qt::BlankCursor );
387 if (secondaryView)
388 secondaryView->setCursor( Qt::BlankCursor );
389 } else {
390 view->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor );
391 if (secondaryView)
392 secondaryView->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor );
393 }
394 cursorAction->setChecked( e );
395}
396
397template <typename T>
398void QVFb::createMenu(T *menu)
399{
400 menu->addMenu( createFileMenu() );
401 menu->addMenu( createViewMenu() );
402 menu->addSeparator();
403 menu->addMenu( createHelpMenu() );
404}
405
406QMenu* QVFb::createFileMenu()
407{
408 QMenu *file = new QMenu( "File", this );
409 file->addAction( "Configure...", this, SLOT(configure()), 0 );
410 file->addSeparator();
411 file->addAction( "&Save image...", this, SLOT(saveImage()), 0 );
412 file->addAction( "&Animation...", this, SLOT(toggleAnimation()), 0 );
413 file->addSeparator();
414 file->addAction( "&Quit", qApp, SLOT(quit()) );
415 return file;
416}
417
418QMenu* QVFb::createViewMenu()
419{
420 viewMenu = new QMenu( "View", this );
421 cursorAction = viewMenu->addAction( "Show &Cursor", this,
422 SLOT(toggleCursor()) );
423 cursorAction->setCheckable(true);
424 if ( view )
425 enableCursor(true);
426 viewMenu->addAction( "&Refresh Rate...", this, SLOT(changeRate()) );
427 viewMenu->addSeparator();
428 viewMenu->addAction( "No rotation", this, SLOT(setRot0()) );
429 viewMenu->addAction( "90\260 rotation", this, SLOT(setRot90()) );
430 viewMenu->addAction( "180\260 rotation", this, SLOT(setRot180()) );
431 viewMenu->addAction( "270\260 rotation", this, SLOT(setRot270()) );
432 viewMenu->addSeparator();
433 viewMenu->addAction( "Zoom scale &0.5", this, SLOT(setZoomHalf()) );
434 viewMenu->addAction( "Zoom scale 0.75", this, SLOT(setZoom075()) );
435 viewMenu->addAction( "Zoom scale &1", this, SLOT(setZoom1()) );
436 viewMenu->addAction( "Zoom scale &2", this, SLOT(setZoom2()) );
437 viewMenu->addAction( "Zoom scale &3", this, SLOT(setZoom3()) );
438 viewMenu->addAction( "Zoom scale &4", this, SLOT(setZoom4()) );
439 viewMenu->addSeparator();
440 viewMenu->addAction( "Zoom scale...", this, SLOT(setZoom()) );
441 return viewMenu;
442}
443
444
445QMenu* QVFb::createHelpMenu()
446{
447 QMenu *help = new QMenu( "Help", this );
448 help->addAction("About...", this, SLOT(about()));
449 return help;
450}
451
452void QVFb::setZoom(double z)
453{
454 view->setZoom(z,z*skinscaleV/skinscaleH);
455 if (secondaryView)
456 secondaryView->setZoom(z,z*skinscaleV/skinscaleH);
457
458 if (skin) {
459 skin->setTransform(QMatrix().scale(z/skinscaleH,z/skinscaleV).rotate(90*view->displayRotation()));
460 if (secondaryView)
461 secondaryView->setFixedSize(
462 int(secondaryView->displayWidth()*z),
463 int(secondaryView->displayHeight()*z*skinscaleV/skinscaleH));
464 }
465}
466
467void QVFb::setRotation(QVFbView::Rotation r)
468{
469 view->setRotation(r);
470 if (secondaryView)
471 secondaryView->setRotation(r);
472 setZoom(view->zoomH());
473}
474
475void QVFb::setRot0()
476{
477 setRotation(QVFbView::Rot0);
478}
479
480void QVFb::setRot90()
481{
482 setRotation(QVFbView::Rot90);
483}
484
485void QVFb::setRot180()
486{
487 setRotation(QVFbView::Rot180);
488}
489
490void QVFb::setRot270()
491{
492 setRotation(QVFbView::Rot270);
493}
494
495void QVFb::setZoomHalf()
496{
497 setZoom(0.5);
498}
499
500void QVFb::setZoom075()
501{
502 setZoom(0.75);
503}
504
505void QVFb::setZoom1()
506{
507 setZoom(1);
508}
509
510void QVFb::setZoom()
511{
512 if ( !zoomer )
513 zoomer = new Zoomer(this);
514 zoomer->show();
515}
516
517void QVFb::setZoom2()
518{
519 setZoom(2);
520}
521
522void QVFb::setZoom3()
523{
524 setZoom(3);
525}
526
527void QVFb::setZoom4()
528{
529 setZoom(4);
530}
531
532void QVFb::saveImage()
533{
534 QImage img = view->image();
535 QString filename = QFileDialog::getSaveFileName(this, "Save Main Screen image", "snapshot.png", "Portable Network Graphics (*.png)");
536 if (!filename.isEmpty()){
537 if(!img.save(filename,"PNG"))
538 QMessageBox::critical(this, "Save Main Screen Image", "Save failed. Check that you have permission to write to the target directory.");
539 }
540 if (secondaryView) {
541 QImage img = view->image();
542 QString filename = QFileDialog::getSaveFileName(this, "Save Second Screen image", "snapshot.png", "Portable Network Graphics (*.png)");
543 if (!filename.isEmpty()) {
544 if(!img.save(filename,"PNG"))
545 QMessageBox::critical(this, "Save Second Screen Image", "Save failed. Check that you have permission to write to the target directory.");
546 }
547 }
548}
549
550void QVFb::toggleAnimation()
551{
552 static AnimationSaveWidget *animWidget = 0;
553 if ( !animWidget )
554 animWidget = new AnimationSaveWidget(view);
555 if ( animWidget->isVisible() )
556 animWidget->hide();
557 else
558 animWidget->show();
559}
560
561void QVFb::toggleCursor()
562{
563 enableCursor(cursorAction->isChecked());
564}
565
566void QVFb::changeRate()
567{
568 if ( !rateDlg ) {
569 rateDlg = new QVFbRateDialog( refreshRate, this );
570 connect( rateDlg, SIGNAL(updateRate(int)), this, SLOT(setRate(int)) );
571 }
572
573 rateDlg->show();
574}
575
576void QVFb::setRate(int i)
577{
578 refreshRate = i;
579 view->setRate(i);
580 if (secondaryView)
581 secondaryView->setRate(i);
582}
583
584
585void QVFb::about()
586{
587 QMessageBox::about(this, "About QVFB",
588 "<h2>The Qt for Embedded Linux Virtual X11 Framebuffer</h2>"
589 "<p>This application runs under Qt for X11, emulating a simple framebuffer, "
590 "which the Qt for Embedded Linux server and clients can attach to just as if "
591 "it was a hardware Linux framebuffer. "
592 "<p>With the aid of this development tool, you can develop Qt for Embedded "
593 "Linux applications under X11 without having to switch to a virtual console. "
594 "This means you can comfortably use your other development tools such "
595 "as GUI profilers and debuggers."
596 );
597}
598
599void QVFb::findSkins(const QString &currentSkin)
600{
601 skinnames.clear();
602 skinfiles.clear();
603 QDir dir(":/skins/","*.skin");
604 const QFileInfoList l = dir.entryInfoList();
605 int i = 1; // "None" is already in list at index 0
606 for (QFileInfoList::const_iterator it = l.begin(); it != l.end(); ++it) {
607 skinnames.append((*it).baseName()); // should perhaps be in file
608 skinfiles.append((*it).filePath());
609 if (((*it).baseName() + ".skin") == currentSkin)
610 currentSkinIndex = i;
611 i++;
612 }
613}
614
615class Config : public QDialog, public Ui::Config
616{
617public:
618 Config(QWidget *parent)
619 : QDialog(parent)
620 {
621 setupUi(this);
622 setModal(true);
623
624 connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
625 connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
626 }
627};
628
629void QVFb::configure()
630{
631 config = new Config(this);
632
633 int w = view->displayWidth();
634 int h = view->displayHeight();
635
636 // Need to block signals, because we connect to animateClick(),
637 // since QCheckBox doesn't have setChecked(bool) in 2.x.
638 chooseSize(QSize(w,h));
639 config->skin->insertItems(config->skin->count(), skinnames);
640 if (currentSkinIndex > 0)
641 config->skin->setCurrentIndex(currentSkinIndex);
642 config->skin->addItem(tr("Browse..."));
643 config->touchScreen->setChecked(view->touchScreenEmulation());
644 config->lcdScreen->setChecked(view->lcdScreenEmulation());
645 chooseDepth(view->displayDepth(), view->displayFormat());
646 connect(config->skin, SIGNAL(activated(int)), this, SLOT(skinConfigChosen(int)));
647 if ( view->gammaRed() == view->gammaGreen() && view->gammaGreen() == view->gammaBlue() ) {
648 config->gammaslider->setValue(int(view->gammaRed()*400));
649 config->rslider->setValue(100);
650 config->gslider->setValue(100);
651 config->bslider->setValue(100);
652 } else {
653 config->gammaslider->setValue(100);
654 config->rslider->setValue(int(view->gammaRed()*400));
655 config->gslider->setValue(int(view->gammaGreen()*400));
656 config->bslider->setValue(int(view->gammaBlue()*400));
657 }
658 connect(config->gammaslider, SIGNAL(valueChanged(int)), this, SLOT(setGamma400(int)));
659 connect(config->rslider, SIGNAL(valueChanged(int)), this, SLOT(setR400(int)));
660 connect(config->gslider, SIGNAL(valueChanged(int)), this, SLOT(setG400(int)));
661 connect(config->bslider, SIGNAL(valueChanged(int)), this, SLOT(setB400(int)));
662 updateGammaLabels();
663
664 double ogr=view->gammaRed(), ogg=view->gammaGreen(), ogb=view->gammaBlue();
665 qApp->setQuitOnLastWindowClosed(false);
666
667 hide();
668 if ( config->exec() ) {
669 int id = view->displayId(); // not settable yet
670 if ( config->size_176_220->isChecked() ) {
671 w=176; h=220;
672 } else if ( config->size_240_320->isChecked() ) {
673 w=240; h=320;
674 } else if ( config->size_320_240->isChecked() ) {
675 w=320; h=240;
676 } else if ( config->size_640_480->isChecked() ) {
677 w=640; h=480;
678 } else if ( config->size_800_600->isChecked() ) {
679 w=800; h=600;
680 } else if ( config->size_1024_768->isChecked() ) {
681 w=1024; h=768;
682 } else {
683 w=config->size_width->value();
684 h=config->size_height->value();
685 }
686 int d;
687 if ( config->depth_1->isChecked() )
688 d=1;
689 else if ( config->depth_4gray->isChecked() )
690 d=4;
691 else if ( config->depth_8->isChecked() )
692 d=8;
693 else if ( config->depth_12->isChecked() )
694 d=12;
695 else if ( config->depth_15->isChecked() )
696 d = 15;
697 else if ( config->depth_16->isChecked() )
698 d=16;
699 else if ( config->depth_18->isChecked() )
700 d=18;
701 else if ( config->depth_24->isChecked() )
702 d=24;
703 else
704 d=32;
705 QVFbView::PixelFormat displayFormat = config->depth_32_argb->isChecked()
706 ? QVFbView::ARGBFormat : QVFbView::DefaultFormat;
707 int skinIndex = config->skin->currentIndex();
708 if ( w != view->displayWidth() || h != view->displayHeight()
709 || d != view->displayDepth() || skinIndex != currentSkinIndex ) {
710 QVFbView::Rotation rot = view->displayRotation();
711 int r = ((rot == QVFbView::Rot90) ? 90 :
712 ((rot == QVFbView::Rot180) ? 180 :
713 ((rot == QVFbView::Rot270) ? 270 : 0 )));
714 currentSkinIndex = skinIndex;
715 init( id, w, h, d, r, skinIndex > 0 ? skinfiles[skinIndex-1] : QString::null );
716 }
717 view->setViewFormat(displayFormat);
718 view->setTouchscreenEmulation( config->touchScreen->isChecked() );
719 bool lcdEmulation = config->lcdScreen->isChecked();
720 view->setLcdScreenEmulation( lcdEmulation );
721 if ( lcdEmulation )
722 setZoom3();
723 } else {
724 view->setGamma(ogr, ogg, ogb);
725 }
726 show();
727 qApp->setQuitOnLastWindowClosed(true);
728 delete config;
729 config=0;
730}
731
732void QVFb::chooseSize(const QSize& sz)
733{
734 config->size_width->blockSignals(true);
735 config->size_height->blockSignals(true);
736 config->size_width->setValue(sz.width());
737 config->size_height->setValue(sz.height());
738 config->size_width->blockSignals(false);
739 config->size_height->blockSignals(false);
740 config->size_custom->setChecked(true); // unless changed by settings below
741 config->size_176_220->setChecked(sz == QSize(176,220));
742 config->size_240_320->setChecked(sz == QSize(240,320));
743 config->size_320_240->setChecked(sz == QSize(320,240));
744 config->size_640_480->setChecked(sz == QSize(640,480));
745 config->size_800_600->setChecked(sz == QSize(800,600));
746 config->size_1024_768->setChecked(sz == QSize(1024,768));
747}
748
749void QVFb::chooseDepth(int depth, QVFbView::PixelFormat displayFormat)
750{
751 config->depth_1->setChecked(depth==1);
752 config->depth_4gray->setChecked(depth==4);
753 config->depth_8->setChecked(depth==8);
754 config->depth_12->setChecked(depth==12);
755 config->depth_15->setChecked(depth==15);
756 config->depth_16->setChecked(depth==16);
757 config->depth_18->setChecked(depth==18);
758 config->depth_24->setChecked(depth==24);
759 config->depth_32->setChecked(depth==32 && displayFormat != QVFbView::ARGBFormat);
760 config->depth_32_argb->setChecked(depth==32 && displayFormat == QVFbView::ARGBFormat);
761}
762
763void QVFb::skinConfigChosen(int i)
764{
765 if (i == config->skin->count() - 1) { // Browse... ?
766 QFileDialog dlg(this);
767 dlg.setFileMode(QFileDialog::DirectoryOnly);
768 dlg.setWindowTitle(tr("Load Custom Skin..."));
769 dlg.setFilter(tr("All QVFB Skins (*.skin)"));
770 dlg.setDirectory(QDir::current());
771 if (dlg.exec() && dlg.selectedFiles().count() == 1) {
772 skinfiles.append(dlg.selectedFiles().first());
773 i = skinfiles.count();
774 config->skin->insertItem(i, QFileInfo(skinfiles.last()).baseName());
775 config->skin->setCurrentIndex(i);
776 } else {
777 i = 0;
778 }
779 }
780 if ( i ) {
781 DeviceSkinParameters parameters;
782 QString readError;
783 if (parameters.read(skinfiles[i-1], DeviceSkinParameters::ReadSizeOnly, &readError)) {
784 chooseSize(parameters.screenSize());
785 if (parameters.screenDepth)
786 chooseDepth(parameters.screenDepth,QVFbView::ARGBFormat);
787 config->touchScreen->setChecked(!parameters.hasMouseHover);
788 } else {
789 qWarning("%s", qPrintable(readError));
790 }
791 }
792}
793
794void QVFb::setGamma400(int n)
795{
796 double g = n/100.0;
797 view->setGamma(config->rslider->value()/100.0*g,
798 config->gslider->value()/100.0*g,
799 config->bslider->value()/100.0*g);
800 updateGammaLabels();
801}
802
803void QVFb::setR400(int n)
804{
805 double g = n/100.0;
806 view->setGamma(config->rslider->value()/100.0*g,
807 view->gammaGreen(),
808 view->gammaBlue());
809 updateGammaLabels();
810}
811
812void QVFb::setG400(int n)
813{
814 double g = n/100.0;
815 view->setGamma(view->gammaRed(),
816 config->gslider->value()/100.0*g,
817 view->gammaBlue());
818 updateGammaLabels();
819}
820
821void QVFb::setB400(int n)
822{
823 double g = n/100.0;
824 view->setGamma(view->gammaRed(),
825 view->gammaGreen(),
826 config->bslider->value()/100.0*g);
827 updateGammaLabels();
828}
829
830void QVFb::updateGammaLabels()
831{
832 config->rlabel->setText(QString::number(view->gammaRed(),'g',2));
833 config->glabel->setText(QString::number(view->gammaGreen(),'g',2));
834 config->blabel->setText(QString::number(view->gammaBlue(),'g',2));
835}
836
837QSize QVFb::sizeHint() const
838{
839 return QSize(int(view->displayWidth()*view->zoomH()),
840 int(menuBar()->height()+view->displayHeight()*view->zoomV()));
841}
842
843// =====================================================================
844
845AnimationSaveWidget::AnimationSaveWidget(QVFbAbstractView *v) :
846 QWidget((QWidget*)0,0),
847 view(v), recording(false), animation(0),
848 timerId(-1), progressTimerId(-1),
849 recOn(red_on_led_xpm), recOff(red_off_led_xpm),
850 imageNum(0)
851{
852 // Create the animation record UI dialog
853 QVBoxLayout *vlayout = new QVBoxLayout( this );
854
855 QWidget *hbox = new QWidget( this );
856 vlayout->addWidget(hbox);
857 QHBoxLayout *hlayout = new QHBoxLayout(hbox);
858 recBt = new QPushButton( tr("Record"), hbox );
859 hlayout->addWidget(recBt);
860 resetBt = new QPushButton( tr("Reset"), hbox );
861 hlayout->addWidget(resetBt);
862 saveBt = new QPushButton( tr("Save"), hbox );
863 hlayout->addWidget(saveBt);
864 recBt->setFixedWidth( 100 );
865 resetBt->setFixedWidth( 100 );
866 saveBt->setFixedWidth( 100 );
867 timeDpy = new QLabel( "00:00", hbox );
868 hlayout->addWidget(timeDpy);
869 recLED = new QLabel( hbox );
870 hlayout->addWidget(recLED);
871 recLED->setPixmap( recOff );
872 timeDpy->setMargin( 5 );
873 connect( recBt, SIGNAL(clicked()), this, SLOT(toggleRecord()) );
874 connect( resetBt, SIGNAL(clicked()), this, SLOT(reset()) );
875 connect( saveBt, SIGNAL(clicked()), this, SLOT(save()) );
876 elapsed = 0;
877 vlayout->setMargin( 5 );
878 vlayout->setSpacing( 5 );
879 haveMpeg = detectPpmtoMpegCommand();
880 mpegSave = new QCheckBox( tr("Save in MPEG format (requires netpbm package installed)"), this );
881 vlayout->addWidget(mpegSave);
882 mpegSave->setChecked( haveMpeg );
883 mpegSave->setEnabled( haveMpeg );
884 savingAsMpeg = haveMpeg;
885 QWidget *hbox2 = new QWidget( this );
886 vlayout->addWidget(hbox2);
887 QHBoxLayout *hlayout2 = new QHBoxLayout( hbox2 );
888 statusText = new QLabel( tr("Click record to begin recording."), hbox2 );
889 hlayout2->addWidget(statusText);
890 progressBar = new QProgressBar( hbox2 );
891 progressBar->setValue( 0 );
892 hlayout2->addWidget(progressBar);
893 progressBar->hide();
894}
895
896AnimationSaveWidget::~AnimationSaveWidget()
897{
898 // clean up
899 removeTemporaryFiles();
900 delete animation;
901}
902
903// returns true if we have ppmtompeg command, else returns false
904bool AnimationSaveWidget::detectPpmtoMpegCommand()
905{
906 // search the PATH for the ppmtompeg command to test we can record to mpeg
907 QStringList paths = QString(::getenv("PATH")).split(":");
908 for ( int i = 0; i < paths.count(); i++ )
909 if ( QFile::exists( paths[i] + "/" + "ppmtompeg" ) )
910 return true;
911 return false;
912}
913
914void AnimationSaveWidget::timerEvent( QTimerEvent *te )
915{
916 QString str;
917
918 // Recording timer
919 if ( te->timerId() == timerId ) {
920
921 // Add a frame to the animation
922 if ( savingAsMpeg && view )
923 view->image().save( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", imageNum), "PPM");
924 else if ( animation && view )
925 animation->appendFrame(view->image());//QPoint(0,0));
926 imageNum++;
927
928 // Update the display of number of seconds that have been recorded.
929 int tmMsec = tm.elapsed();
930 timeDpy->setText( str.sprintf("%02d:%02d", tmMsec/60000, (tmMsec%60000)/1000) );
931 QObject::timerEvent( te );
932
933 // Make the recording LED blink
934 static int tick = 0;
935 static bool on = false;
936 if ( tick > 10 ) {
937 tick = 0;
938 if ( on )
939 recLED->setPixmap( recOff );
940 else
941 recLED->setPixmap( recOn );
942 on = !on;
943 }
944 tick++;
945 }
946
947 // Saving progress timer
948 if ( te->timerId() == progressTimerId ) {
949 // Parse output log file to work out the encoding progress.
950 QFile f("/tmp/qvfb_tmp_output.log");
951 f.open(QIODevice::ReadOnly);
952 int largestNum = 0;
953 bool done = false;
954 char buffer[1024];
955 while ( !f.atEnd() ) {
956 // example of the output log entries
957 // During each frame:
958 // "FRAME 764 (B): I BLOCKS: 0......
959 // When complete:
960 // "======FRAMES READ: 766"
961 f.readLine(buffer, 1024);
962 str = QString(buffer);
963 if ( str.left(6) == "FRAME " ) {
964 int num = str.mid(6, str.indexOf(QChar(' '), 6) - 6).toInt();
965 if ( num > largestNum )
966 largestNum = num;
967 } else if ( str.left(18) == "======FRAMES READ:" ) {
968 done = true;
969 }
970 }
971 f.close();
972
973 // Update the progress bar with the frame we are up to
974 progressBar->setValue( largestNum );
975
976 // Finished saving
977 if ( done ) {
978 progressBar->hide();
979 statusText->setText( tr("Finished saving."));
980 removeTemporaryFiles();
981 killTimer( progressTimerId );
982 progressTimerId = -1;
983 reset();
984 }
985 }
986}
987
988// Takes the saved ppm files and converts them to a mpeg file named filename
989void AnimationSaveWidget::convertToMpeg(QString filename)
990{
991 recLED->setPixmap( recOff );
992 killTimer( timerId );
993
994 progressBar->show();
995 progressBar->setRange( 0, imageNum );
996 progressBar->setValue( 0 );
997
998 // Build parameter file required by ppmtompeg
999 QFile file("/tmp/qvfb_tmp_ppmtompeg.params");
1000 if ( file.open( QIODevice::WriteOnly ) ) {
1001 QTextStream t( &file );
1002 t << "PATTERN IBBPBBPBBPBBPBB\n";
1003 t << "OUTPUT " << filename << "\n";
1004 t << "INPUT_DIR /tmp\n";
1005 t << "INPUT\n";
1006 QString str;
1007 str = str.sprintf("%04d", imageNum - 1);
1008 t << "qvfb_tmp_image_*.ppm [0000-" << str << "]\n";
1009 t << "END_INPUT\n";
1010 t << "BASE_FILE_FORMAT PPM\n";
1011 t << "INPUT_CONVERT *\n";
1012 t << "GOP_SIZE 15\n";
1013 t << "SLICES_PER_FRAME 1\n";
1014 t << "PIXEL HALF\n";
1015 t << "RANGE 5\n";
1016 t << "PSEARCH_ALG LOGARITHMIC\n";
1017 t << "BSEARCH_ALG SIMPLE\n";
1018 t << "IQSCALE 1\n";
1019 t << "PQSCALE 1\n";
1020 t << "BQSCALE 1\n";
1021 t << "REFERENCE_FRAME DECODED\n";
1022 t << "ASPECT_RATIO 1\n";
1023 t << "FRAME_RATE 24\n";
1024 t << "BIT_RATE 64000\n"; // Quality
1025 t << "BUFFER_SIZE 2048\n";
1026 }
1027 file.close();
1028
1029 // ### can't use QProcess, not in Qt 2.3
1030 // ### but it's certainly in Qt 4! use it?
1031 // Execute the ppmtompeg command as a seperate process to do the encoding
1032 pid_t pid = ::fork();
1033 if ( !pid ) {
1034 // Child process
1035 // redirect stdout to log file
1036 freopen("/tmp/qvfb_tmp_output.log", "w", stdout);
1037 // ppmtompeg tool is from the netpbm package
1038 ::execlp("ppmtompeg", "ppmtompeg", "/tmp/qvfb_tmp_ppmtompeg.params", (void *)0);
1039 exit(0);
1040 }
1041
1042 // Update the saving progress bar every 200ms
1043 progressTimerId = startTimer( 200 );
1044}
1045
1046// Cleanup temporary files created during creating a mpeg file
1047void AnimationSaveWidget::removeTemporaryFiles()
1048{
1049 QString str;
1050 for ( int i = 0; i < imageNum; i++ )
1051 QFile::remove( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", i) );
1052 QFile::remove("/tmp/qvfb_tmp_ppmtompeg.params");
1053 QFile::remove("/tmp/qvfb_tmp_output.log");
1054 imageNum = 0;
1055}
1056
1057// toggles between recording and paused (usually when record button clicked)
1058void AnimationSaveWidget::toggleRecord()
1059{
1060 if ( recording ) {
1061 recLED->setPixmap( recOff );
1062 recBt->setText( tr("Record") );
1063 statusText->setText( tr("Paused. Click record to resume, or save if done."));
1064 killTimer( timerId );
1065 timerId = -1;
1066 elapsed = tm.elapsed();
1067 } else {
1068 recLED->setPixmap( recOn );
1069 recBt->setText( tr("Pause") );
1070 statusText->setText( tr("Recording..."));
1071 tm.start();
1072 if ( elapsed == 0 ) {
1073 savingAsMpeg = mpegSave->isChecked();
1074 if ( !savingAsMpeg ) {
1075 delete animation;
1076 animation = new QAnimationWriter("/tmp/qvfb_tmp_animation.mng","MNG");
1077 animation->setFrameRate(24);
1078 if ( view )
1079 animation->appendFrame(view->image());
1080 }
1081 }
1082 tm = tm.addMSecs(-elapsed);
1083 elapsed = 0;
1084 timerId = startTimer(1000 / 24);
1085 }
1086 recording = !recording;
1087}
1088
1089// Reset everything to initial state of not recording
1090void AnimationSaveWidget::reset()
1091{
1092 if ( recording ) {
1093 toggleRecord();
1094 statusText->setText( tr("Click record to begin recording."));
1095 removeTemporaryFiles();
1096 }
1097 progressBar->setValue( 0 );
1098 timeDpy->setText( "00:00" );
1099 elapsed = 0;
1100 imageNum = 0;
1101 delete animation;
1102 animation = 0;
1103}
1104
1105// Prompt for filename to save to and put animation in that file
1106void AnimationSaveWidget::save()
1107{
1108 if ( recording )
1109 toggleRecord(); // pauses
1110 statusText->setText( tr("Saving... "));
1111
1112 QString filename;
1113 if ( savingAsMpeg ) {
1114 filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mpg");
1115 if ( !filename.isNull() )
1116 convertToMpeg(filename);
1117 } else {
1118 filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mng");
1119 if (filename.isNull()) {
1120 statusText->setText(tr("Save canceled."));
1121 } else {
1122 QFile::remove(filename);
1123 bool success = QFile::rename(QLatin1String("/tmp/qvfb_tmp_animation.mng"),
1124 filename);
1125 if (success) {
1126 statusText->setText(tr("Finished saving."));
1127 reset();
1128 } else {
1129 statusText->setText(tr("Save failed!"));
1130 }
1131 }
1132 }
1133}
1134
1135QT_END_NAMESPACE
1136
1137#include "qvfb.moc"
Note: See TracBrowser for help on using the repository browser.