diff options
Diffstat (limited to 'tools/qvfb/qvfbview.cpp')
-rw-r--r-- | tools/qvfb/qvfbview.cpp | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/tools/qvfb/qvfbview.cpp b/tools/qvfb/qvfbview.cpp new file mode 100644 index 000000000..5fb0f5e16 --- /dev/null +++ b/tools/qvfb/qvfbview.cpp @@ -0,0 +1,615 @@ +/********************************************************************** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of TQt/Embedded virtual framebuffer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** Licensees holding valid TQt Commercial licenses may use this file in +** accordance with the TQt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qglobal.h" +#if !defined( Q_WS_QWS ) || defined( QT_NO_QWS_MULTIPROCESS ) +#define TQLock TQWSSemaphore +#undef QT_NO_QWS_MULTIPROCESS +#include "../../src/kernel/qlock.cpp" +#else +#include "qlock_p.h" +#endif + +#include "qvfbview.h" +#include "qvfbhdr.h" + +#define TQTE_PIPE "TQtEmbedded-%1" + +#include <qapplication.h> +#include <qimage.h> +#include <qbitmap.h> +#include <qtimer.h> +#include <qwmatrix.h> +#include <qpainter.h> +#include "qanimationwriter.h" + +#include <unistd.h> +#include <sys/ipc.h> +#include <sys/types.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <math.h> + +//#define QT_QWS_EXPERIMENTAL_REVERSE_BIT_ENDIANNESS + +TQVFbView::TQVFbView( int display_id, int w, int h, int d, TQWidget *parent, + const char *name, uint flags ) + : TQScrollView( parent, name, flags ), emulateTouchscreen(FALSE), qwslock(NULL) +{ + displayid = display_id; + viewport()->setMouseTracking( TRUE ); + viewport()->setFocusPolicy( StrongFocus ); + zm = 1; + animation = 0; + int actualdepth=d; + + switch ( d ) { + case 12: + actualdepth=16; + break; + case 1: + case 4: + case 8: + case 16: + case 32: + break; + + default: + qFatal( "Unsupported bit depth %d\n", d ); + } + + mousePipe = TQString(QT_VFB_MOUSE_PIPE).arg(display_id); + keyboardPipe = TQString(QT_VFB_KEYBOARD_PIPE).arg(display_id); + + unlink( mousePipe.latin1() ); + mkfifo( mousePipe.latin1(), 0666 ); + + mouseFd = open( mousePipe.latin1(), O_RDWR | O_NDELAY ); + if ( mouseFd == -1 ) { + qFatal( "Cannot open mouse pipe" ); + } + + unlink( keyboardPipe ); + mkfifo( keyboardPipe, 0666 ); + keyboardFd = open( keyboardPipe, O_RDWR | O_NDELAY ); + if ( keyboardFd == -1 ) { + qFatal( "Cannot open keyboard pipe" ); + } + + key_t key = ftok( mousePipe.latin1(), 'b' ); + + int bpl; + if ( d == 1 ) + bpl = (w*d+7)/8; + else + bpl = ((w*actualdepth+31)/32)*4; + + int dataSize = bpl * h + sizeof( TQVFbHeader ); + shmId = shmget( key, dataSize, IPC_CREAT|0666); + if ( shmId != -1 ) + data = (unsigned char *)shmat( shmId, 0, 0 ); + else { + struct shmid_ds shm; + shmctl( shmId, IPC_RMID, &shm ); + shmId = shmget( key, dataSize, IPC_CREAT|0666); + data = (unsigned char *)shmat( shmId, 0, 0 ); + } + + if ( (long)data == -1 ) + qFatal( "Cannot attach to shared memory" ); + + hdr = (TQVFbHeader *)data; + hdr->width = w; + hdr->height = h; + viewdepth = d; + hdr->depth = actualdepth; + hdr->linestep = bpl; + hdr->numcols = 0; + hdr->dataoffset = sizeof( TQVFbHeader ); + hdr->update = TQRect(); + + resizeContents( w, h ); + + timer = new TQTimer( this ); + connect( timer, SIGNAL(timeout()), this, SLOT(timeout()) ); + + gammatable=0; + setGamma(1.0,1.0,1.0); + setRate( 30 ); +} + +TQVFbView::~TQVFbView() +{ + stopAnimation(); + sendKeyboardData( 0, 0, 0, TRUE, FALSE ); // magic die key + delete qwslock; + struct shmid_ds shm; + shmdt( (char*)data ); + shmctl( shmId, IPC_RMID, &shm ); + ::close( mouseFd ); + ::close( keyboardFd ); + unlink( mousePipe ); + unlink( keyboardPipe ); +} + +TQSize TQVFbView::sizeHint() const +{ + int f = 2 * frameWidth(); + return TQSize( contentsWidth() + f, contentsHeight() + f ); +} + +void TQVFbView::setGamma(double gr, double gg, double gb) +{ + if ( viewdepth < 12 ) + return; // not implemented + + gred=gr; ggreen=gg; gblue=gb; + + switch ( viewdepth ) { + case 12: + rsh = 12; + gsh = 7; + bsh = 1; + rmax = 15; + gmax = 15; + bmax = 15; + break; + case 16: + rsh = 11; + gsh = 5; + bsh = 0; + rmax = 31; + gmax = 63; + bmax = 31; + break; + case 32: + rsh = 16; + gsh = 8; + bsh = 0; + rmax = 255; + gmax = 255; + bmax = 255; + } + int mm = TQMAX(rmax,TQMAX(gmax,bmax))+1; + if ( gammatable ) + delete [] gammatable; + gammatable = new TQRgb[mm]; + for (int i=0; i<mm; i++) { + int r = int(pow(i,gr)*255/rmax); + int g = int(pow(i,gg)*255/gmax); + int b = int(pow(i,gb)*255/bmax); + if ( r > 255 ) r = 255; + if ( g > 255 ) g = 255; + if ( b > 255 ) b = 255; + gammatable[i] = qRgb(r,g,b); +//qDebug("%d: %d,%d,%d",i,r,g,b); + } + + setDirty(rect()); +} + +void TQVFbView::getGamma(int i, TQRgb& rgb) +{ + if ( i > 255 ) i = 255; + if ( i < 0 ) i = 0; + rgb = qRgb(qRed(gammatable[i*rmax/255]), + qGreen(gammatable[i*rmax/255]), + qBlue(gammatable[i*rmax/255])); +} + +int TQVFbView::displayId() const +{ + return displayid; +} + +int TQVFbView::displayWidth() const +{ + return hdr->width; +} + +int TQVFbView::displayHeight() const +{ + return hdr->height; +} + +int TQVFbView::displayDepth() const +{ + return viewdepth; +} + +void TQVFbView::setZoom( double z ) +{ + if ( zm != z ) { + zm = z; + setDirty(TQRect(0,0,hdr->width,hdr->height)); + resizeContents( int(hdr->width*z), int(hdr->height*z) ); + updateGeometry(); + qApp->sendPostedEvents(); + topLevelWidget()->adjustSize(); + drawScreen(); + } +} + +void TQVFbView::setRate( int r ) +{ + refreshRate = r; + timer->start( 1000/r ); +} + +#ifndef Q_WS_QWS +// Get the name of the directory where TQt/Embedded temporary data should +// live. +static TQString qws_dataDir() +{ + TQString username = "unknown"; + const char *logname = getenv("LOGNAME"); + if ( logname ) + username = logname; + + TQString dataDir = "/tmp/qtembedded-" + username; + if ( mkdir( dataDir.latin1(), 0700 ) ) { + if ( errno != EEXIST ) { + qFatal( TQString("Cannot create TQt/Embedded data directory: %1") + .arg( dataDir ) ); + } + } + + struct stat buf; + if ( lstat( dataDir.latin1(), &buf ) ) + qFatal( TQString( "stat failed for TQt/Embedded data directory: %1" ) + .arg( dataDir ) ); + + if ( !S_ISDIR( buf.st_mode ) ) + qFatal( TQString( "%1 is not a directory" ).arg( dataDir ) ); + + if ( buf.st_uid != getuid() ) + qFatal( TQString( "TQt/Embedded data directory is not owned by user %1" ) + .arg( getuid() ) ); + + if ( (buf.st_mode & 0677) != 0600 ) + qFatal( TQString( "TQt/Embedded data directory has incorrect permissions: %1" ) + .arg( dataDir ) ); + + dataDir += "/"; + + return dataDir; +} +#endif + +void TQVFbView::initLock() +{ + TQString username = "unknown"; + const char *logname = getenv("LOGNAME"); + if ( logname ) + username = logname; + qwslock = new TQLock(qws_dataDir() + TQString( TQTE_PIPE ).arg( displayid ), + 'd', TRUE); +} + +void TQVFbView::lock() +{ + if ( !qwslock ) + initLock(); + qwslock->lock(TQLock::Read); +} + +void TQVFbView::unlock() +{ + if ( qwslock ) + qwslock->unlock(); +} + +void TQVFbView::sendMouseData( const TQPoint &pos, int buttons ) +{ + write( mouseFd, &pos, sizeof( TQPoint ) ); + write( mouseFd, &buttons, sizeof( int ) ); +} + +void TQVFbView::sendKeyboardData( int unicode, int keycode, int modifiers, + bool press, bool repeat ) +{ + TQVFbKeyData kd; + kd.unicode = unicode | (keycode << 16); + kd.modifiers = modifiers; + kd.press = press; + kd.repeat = repeat; + write( keyboardFd, &kd, sizeof( TQVFbKeyData ) ); +} + +void TQVFbView::timeout() +{ + lock(); + if ( animation ) { + TQRect r( hdr->update ); + r = r.intersect( TQRect(0, 0, hdr->width, hdr->height ) ); + if ( r.isEmpty() ) { + animation->appendBlankFrame(); + } else { + int l; + TQImage img = getBuffer( r, l ); + animation->appendFrame(img,TQPoint(r.x(),r.y())); + } + } + if ( hdr->dirty ) { + drawScreen(); + } + unlock(); +} + +TQImage TQVFbView::getBuffer( const TQRect &r, int &leading ) const +{ + switch ( viewdepth ) { + case 12: + case 16: { + static unsigned char *imgData = 0; + if ( !imgData ) { + int bpl = ((hdr->width*32+31)/32)*4; + imgData = new unsigned char [ bpl * hdr->height ]; + } + TQImage img( imgData, r.width(), r.height(), 32, 0, 0, TQImage::IgnoreEndian ); + const int rsh = viewdepth == 12 ? 12 : 11; + const int gsh = viewdepth == 12 ? 7 : 5; + const int bsh = viewdepth == 12 ? 1 : 0; + const int rmax = viewdepth == 12 ? 15 : 31; + const int gmax = viewdepth == 12 ? 15 : 63; + const int bmax = viewdepth == 12 ? 15 : 31; + for ( int row = 0; row < r.height(); row++ ) { + TQRgb *dptr = (TQRgb*)img.scanLine( row ); + ushort *sptr = (ushort*)(data + hdr->dataoffset + (r.y()+row)*hdr->linestep); + sptr += r.x(); +#ifdef QT_QWS_REVERSE_BYTE_ENDIANNESS + for ( int col=0; col < r.width()/2; col++ ) { +#else + for ( int col=0; col < r.width(); col++ ) { +#endif + ushort s = *sptr++; +#ifdef QT_QWS_REVERSE_BYTE_ENDIANNESS + ushort s2 = *sptr++; + *dptr++ = qRgb(qRed(gammatable[(s2>>rsh)&rmax]),qGreen(gammatable[(s2>>gsh)&gmax]),qBlue(gammatable[(s2>>bsh)&bmax])); +#endif + *dptr++ = qRgb(qRed(gammatable[(s>>rsh)&rmax]),qGreen(gammatable[(s>>gsh)&gmax]),qBlue(gammatable[(s>>bsh)&bmax])); + //*dptr++ = qRgb(((s>>rsh)&rmax)*255/rmax,((s>>gsh)&gmax)*255/gmax,((s>>bsh)&bmax)*255/bmax); + } + } + leading = 0; + return img; + } + case 4: { + static unsigned char *imgData = 0; + if ( !imgData ) { + int bpl = ((hdr->width*8+31)/32)*4; + imgData = new unsigned char [ bpl * hdr->height ]; + } + TQImage img( imgData, r.width(), r.height(), 8, hdr->clut, 16, + TQImage::IgnoreEndian ); + for ( int row = 0; row < r.height(); row++ ) { + unsigned char *dptr = img.scanLine( row ); + unsigned char *sptr = data + hdr->dataoffset + (r.y()+row)*hdr->linestep; + sptr += r.x()/2; + int col = 0; +#ifdef QT_QWS_EXPERIMENTAL_REVERSE_BIT_ENDIANNESS + if ( r.x() & 1 ) { + *dptr++ = *sptr++ & 0x0f; + col++; + } + for ( ; col < r.width()-1; col+=2 ) { + unsigned char s = *sptr++; + *dptr++ = s >> 4; + *dptr++ = s & 0x0f; + } + if ( !(r.right() & 1) ) + *dptr = *sptr >> 4; +#else + if ( r.x() & 1 ) { + *dptr++ = *sptr++ >> 4; + col++; + } + for ( ; col < r.width()-1; col+=2 ) { + unsigned char s = *sptr++; + *dptr++ = s & 0x0f; + *dptr++ = s >> 4; + } + if ( !(r.right() & 1) ) + *dptr = *sptr & 0x0f; +#endif + } + leading = 0; + return img; + } + case 32: { + leading = r.x(); + return TQImage( data + hdr->dataoffset + r.y() * hdr->linestep, + hdr->width, r.height(), hdr->depth, 0, + 0, TQImage::LittleEndian ); + } + case 8: { + leading = r.x(); + return TQImage( data + hdr->dataoffset + r.y() * hdr->linestep, + hdr->width, r.height(), hdr->depth, hdr->clut, + 256, TQImage::LittleEndian ); + } + case 1: { + leading = r.x(); + return TQImage( data + hdr->dataoffset + r.y() * hdr->linestep, + hdr->width, r.height(), hdr->depth, hdr->clut, +#ifndef QT_QWS_EXPERIMENTAL_REVERSE_BIT_ENDIANNESS + 0, TQImage::LittleEndian ); +#else + 0, TQImage::BigEndian ); +#endif + } + } + return TQImage(); +} + +void TQVFbView::drawScreen() +{ + TQPainter p( viewport() ); + + p.translate( -contentsX(), -contentsY() ); + + lock(); + TQRect r( hdr->update ); + hdr->dirty = FALSE; + hdr->update = TQRect(); + // qDebug( "update %d, %d, %dx%d", r.y(), r.x(), r.width(), r.height() ); + r = r.intersect( TQRect(0, 0, hdr->width, hdr->height ) ); + if ( !r.isEmpty() ) { + if ( int(zm) != zm ) { + r.rLeft() = int(int(r.left()*zm)/zm); + r.rTop() = int(int(r.top()*zm)/zm); + r.rRight() = int(int(r.right()*zm+zm+0.0000001)/zm+1.9999); + r.rBottom() = int(int(r.bottom()*zm+zm+0.0000001)/zm+1.9999); + r.rRight() = TQMIN(r.right(),hdr->width-1); + r.rBottom() = TQMIN(r.bottom(),hdr->height-1); + } + int leading; + TQImage img( getBuffer( r, leading ) ); + TQPixmap pm; + if ( zm == 1 ) { + pm.convertFromImage( img ); + } else if ( int(zm) == zm ) { + TQWMatrix m; + m.scale(zm,zm); + pm.convertFromImage( img ); + pm = pm.xForm(m); + } else { + pm.convertFromImage( img.smoothScale(int(img.width()*zm),int(img.height()*zm)) ); + } + unlock(); + p.setPen( black ); + p.setBrush( white ); + p.drawPixmap( int(r.x()*zm), int(r.y()*zm), pm, + int(leading*zm), 0, pm.width(), pm.height() ); + } else { + unlock(); + } +} + +bool TQVFbView::eventFilter( TQObject *obj, TQEvent *e ) +{ + if ( obj == viewport() && + (e->type() == TQEvent::FocusIn || e->type() == TQEvent::FocusOut) ) + return TRUE; + + return TQScrollView::eventFilter( obj, e ); +} + +void TQVFbView::viewportPaintEvent( TQPaintEvent *pe ) +{ + TQRect r( pe->rect() ); + r.moveBy( contentsX(), contentsY() ); + r = TQRect(int(r.x()/zm),int(r.y()/zm), + int(r.width()/zm)+1,int(r.height()/zm)+1); + setDirty(r); + drawScreen(); +} + +void TQVFbView::setDirty( const TQRect& r ) +{ + lock(); + hdr->update |= r; + hdr->dirty = TRUE; + unlock(); +} + +void TQVFbView::contentsMousePressEvent( TQMouseEvent *e ) +{ + sendMouseData( e->pos()/zm, e->stateAfter() ); +} + +void TQVFbView::contentsMouseDoubleClickEvent( TQMouseEvent *e ) +{ + sendMouseData( e->pos()/zm, e->stateAfter() ); +} + +void TQVFbView::contentsMouseReleaseEvent( TQMouseEvent *e ) +{ + sendMouseData( e->pos()/zm, e->stateAfter() ); +} + +void TQVFbView::contentsMouseMoveEvent( TQMouseEvent *e ) +{ + if ( !emulateTouchscreen || (e->state() & MouseButtonMask ) ) + sendMouseData( e->pos()/zm, e->state() ); +} + + + +void TQVFbView::keyPressEvent( TQKeyEvent *e ) +{ + sendKeyboardData(e->text()[0].unicode(), e->key(), + e->stateAfter()&(ShiftButton|ControlButton|AltButton), + TRUE, e->isAutoRepeat()); +} + +void TQVFbView::keyReleaseEvent( TQKeyEvent *e ) +{ + sendKeyboardData(e->ascii(), e->key(), + e->stateAfter()&(ShiftButton|ControlButton|AltButton), + FALSE, e->isAutoRepeat()); +} + + +TQImage TQVFbView::image() const +{ + ((TQVFbView*)this)->lock(); + int l; + TQImage r = getBuffer( TQRect(0, 0, hdr->width, hdr->height), l ).copy(); + ((TQVFbView*)this)->unlock(); + return r; +} + +void TQVFbView::startAnimation( const TQString& filename ) +{ + delete animation; + animation = new TQAnimationWriter(filename,"MNG"); + animation->setFrameRate(refreshRate); + animation->appendFrame(TQImage(data + hdr->dataoffset, + hdr->width, hdr->height, hdr->depth, hdr->clut, + 256, TQImage::LittleEndian)); +} + +void TQVFbView::stopAnimation() +{ + delete animation; + animation = 0; +} + +void TQVFbView::setTouchscreenEmulation( bool b ) +{ + emulateTouchscreen = b; +} |