diff -r 000000000000 -r 1918ee327afb src/gui/embedded/qscreenlinuxfb_qws.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gui/embedded/qscreenlinuxfb_qws.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,1326 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscreenlinuxfb_qws.h" + +#ifndef QT_NO_QWS_LINUXFB +//#include "qmemorymanager_qws.h" +#include "qwsdisplay_qws.h" +#include "qpixmap.h" +#include +#include // overrides QT_OPEN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qwindowsystem_qws.h" + +#if !defined(Q_OS_DARWIN) && !defined(Q_OS_FREEBSD) +#include + +#ifdef __i386__ +#include +#endif +#endif + +QT_BEGIN_NAMESPACE + +extern int qws_client_id; + +//#define DEBUG_CACHE + +class QLinuxFbScreenPrivate : public QObject +{ +public: + QLinuxFbScreenPrivate(); + ~QLinuxFbScreenPrivate(); + + void openTty(); + void closeTty(); + + int fd; + int startupw; + int startuph; + int startupd; + bool blank; + + bool doGraphicsMode; +#ifdef QT_QWS_DEPTH_GENERIC + bool doGenericColors; +#endif + int ttyfd; + long oldKdMode; + QString ttyDevice; + QString displaySpec; +}; + +QLinuxFbScreenPrivate::QLinuxFbScreenPrivate() + : fd(-1), blank(true), doGraphicsMode(true), +#ifdef QT_QWS_DEPTH_GENERIC + doGenericColors(false), +#endif + ttyfd(-1), oldKdMode(KD_TEXT) +{ + QWSSignalHandler::instance()->addObject(this); +} + +QLinuxFbScreenPrivate::~QLinuxFbScreenPrivate() +{ + closeTty(); +} + +void QLinuxFbScreenPrivate::openTty() +{ + const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0}; + + if (ttyDevice.isEmpty()) { + for (const char * const *dev = devs; *dev; ++dev) { + ttyfd = QT_OPEN(*dev, O_RDWR); + if (ttyfd != -1) + break; + } + } else { + ttyfd = QT_OPEN(ttyDevice.toAscii().constData(), O_RDWR); + } + + if (ttyfd == -1) + return; + + if (doGraphicsMode) { + ioctl(ttyfd, KDGETMODE, &oldKdMode); + if (oldKdMode != KD_GRAPHICS) { + int ret = ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); + if (ret == -1) + doGraphicsMode = false; + } + } + + // No blankin' screen, no blinkin' cursor!, no cursor! + const char termctl[] = "\033[9;0]\033[?33l\033[?25l\033[?1c"; + QT_WRITE(ttyfd, termctl, sizeof(termctl)); +} + +void QLinuxFbScreenPrivate::closeTty() +{ + if (ttyfd == -1) + return; + + if (doGraphicsMode) + ioctl(ttyfd, KDSETMODE, oldKdMode); + + // Blankin' screen, blinkin' cursor! + const char termctl[] = "\033[9;15]\033[?33h\033[?25h\033[?0c"; + QT_WRITE(ttyfd, termctl, sizeof(termctl)); + + QT_CLOSE(ttyfd); + ttyfd = -1; +} + +/*! + \internal + + \class QLinuxFbScreen + \ingroup qws + + \brief The QLinuxFbScreen class implements a screen driver for the + Linux framebuffer. + + Note that this class is only available in \l{Qt for Embedded Linux}. + Custom screen drivers can be added by subclassing the + QScreenDriverPlugin class, using the QScreenDriverFactory class to + dynamically load the driver into the application, but there should + only be one screen object per application. + + The QLinuxFbScreen class provides the cache() function allocating + off-screen graphics memory, and the complementary uncache() + function releasing the allocated memory. The latter function will + first sync the graphics card to ensure the memory isn't still + being used by a command in the graphics card FIFO queue. The + deleteEntry() function deletes the given memory block without such + synchronization. Given the screen instance and client id, the + memory can also be released using the clearCache() function, but + this should only be necessary if a client exits abnormally. + + In addition, when in paletted graphics modes, the set() function + provides the possibility of setting a specified color index to a + given RGB value. + + The QLinuxFbScreen class also acts as a factory for the + unaccelerated screen cursor and the unaccelerated raster-based + implementation of QPaintEngine (\c QRasterPaintEngine); + accelerated drivers for Linux should derive from this class. + + \sa QScreen, QScreenDriverPlugin, {Running Applications} +*/ + +/*! + \fn bool QLinuxFbScreen::useOffscreen() + \internal +*/ + +// Unaccelerated screen/driver setup. Can be overridden by accelerated +// drivers + +/*! + \fn QLinuxFbScreen::QLinuxFbScreen(int displayId) + + Constructs a QLinuxFbScreen object. The \a displayId argument + identifies the Qt for Embedded Linux server to connect to. +*/ + +QLinuxFbScreen::QLinuxFbScreen(int display_id) + : QScreen(display_id, LinuxFBClass), d_ptr(new QLinuxFbScreenPrivate) +{ + canaccel=false; + clearCacheFunc = &clearCache; +#ifdef QT_QWS_CLIENTBLIT + setSupportsBlitInClients(true); +#endif +} + +/*! + Destroys this QLinuxFbScreen object. +*/ + +QLinuxFbScreen::~QLinuxFbScreen() +{ +} + +/*! + \reimp + + This is called by \l{Qt for Embedded Linux} clients to map in the framebuffer. + It should be reimplemented by accelerated drivers to map in + graphics card registers; those drivers should then call this + function in order to set up offscreen memory management. The + device is specified in \a displaySpec; e.g. "/dev/fb". + + \sa disconnect() +*/ + +bool QLinuxFbScreen::connect(const QString &displaySpec) +{ + d_ptr->displaySpec = displaySpec; + + const QStringList args = displaySpec.split(QLatin1Char(':')); + + if (args.contains(QLatin1String("nographicsmodeswitch"))) + d_ptr->doGraphicsMode = false; + +#ifdef QT_QWS_DEPTH_GENERIC + if (args.contains(QLatin1String("genericcolors"))) + d_ptr->doGenericColors = true; +#endif + + QRegExp ttyRegExp(QLatin1String("tty=(.*)")); + if (args.indexOf(ttyRegExp) != -1) + d_ptr->ttyDevice = ttyRegExp.cap(1); + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +#ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN + if (args.contains(QLatin1String("littleendian"))) +#endif + QScreen::setFrameBufferLittleEndian(true); +#endif + + QString dev = QLatin1String("/dev/fb0"); + foreach(QString d, args) { + if (d.startsWith(QLatin1Char('/'))) { + dev = d; + break; + } + } + + if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0) + d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR); + if (d_ptr->fd == -1) { + if (QApplication::type() == QApplication::GuiServer) { + perror("QScreenLinuxFb::connect"); + qCritical("Error opening framebuffer device %s", qPrintable(dev)); + return false; + } + if (access(dev.toLatin1().constData(), R_OK) == 0) + d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDONLY); + } + + ::fb_fix_screeninfo finfo; + ::fb_var_screeninfo vinfo; + //####################### + // Shut up Valgrind + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + //####################### + + /* Get fixed screen information */ + if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) { + perror("QLinuxFbScreen::connect"); + qWarning("Error reading fixed information"); + return false; + } + + if (finfo.type == FB_TYPE_VGA_PLANES) { + qWarning("VGA16 video mode not supported"); + return false; + } + + /* Get variable screen information */ + if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::connect"); + qWarning("Error reading variable information"); + return false; + } + + grayscale = vinfo.grayscale; + d = vinfo.bits_per_pixel; + if (d == 24) { + d = vinfo.red.length + vinfo.green.length + vinfo.blue.length; + if (d <= 0) + d = 24; // reset if color component lengths are not reported + } else if (d == 16) { + d = vinfo.red.length + vinfo.green.length + vinfo.blue.length; + if (d <= 0) + d = 16; + } + lstep = finfo.line_length; + + int xoff = vinfo.xoffset; + int yoff = vinfo.yoffset; + const char* qwssize; + if((qwssize=::getenv("QWS_SIZE")) && sscanf(qwssize,"%dx%d",&w,&h)==2) { + if (d_ptr->fd != -1) { + if ((uint)w > vinfo.xres) w = vinfo.xres; + if ((uint)h > vinfo.yres) h = vinfo.yres; + } + dw=w; + dh=h; + int xxoff, yyoff; + if (sscanf(qwssize, "%*dx%*d+%d+%d", &xxoff, &yyoff) == 2) { + if (xxoff < 0 || xxoff + w > vinfo.xres) + xxoff = vinfo.xres - w; + if (yyoff < 0 || yyoff + h > vinfo.yres) + yyoff = vinfo.yres - h; + xoff += xxoff; + yoff += yyoff; + } else { + xoff += (vinfo.xres - w)/2; + yoff += (vinfo.yres - h)/2; + } + } else { + dw=w=vinfo.xres; + dh=h=vinfo.yres; + } + + if (w == 0 || h == 0) { + qWarning("QScreenLinuxFb::connect(): Unable to find screen geometry, " + "will use 320x240."); + dw = w = 320; + dh = h = 240; + } + + setPixelFormat(vinfo); + + // Handle display physical size spec. + QStringList displayArgs = displaySpec.split(QLatin1Char(':')); + QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)")); + int dimIdxW = displayArgs.indexOf(mmWidthRx); + QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)")); + int dimIdxH = displayArgs.indexOf(mmHeightRx); + if (dimIdxW >= 0) { + mmWidthRx.exactMatch(displayArgs.at(dimIdxW)); + physWidth = mmWidthRx.cap(1).toInt(); + if (dimIdxH < 0) + physHeight = dh*physWidth/dw; + } + if (dimIdxH >= 0) { + mmHeightRx.exactMatch(displayArgs.at(dimIdxH)); + physHeight = mmHeightRx.cap(1).toInt(); + if (dimIdxW < 0) + physWidth = dw*physHeight/dh; + } + if (dimIdxW < 0 && dimIdxH < 0) { + if (vinfo.width != 0 && vinfo.height != 0 + && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) { + physWidth = vinfo.width; + physHeight = vinfo.height; + } else { + const int dpi = 72; + physWidth = qRound(dw * 25.4 / dpi); + physHeight = qRound(dh * 25.4 / dpi); + } + } + + dataoffset = yoff * lstep + xoff * d / 8; + //qDebug("Using %dx%dx%d screen",w,h,d); + + /* Figure out the size of the screen in bytes */ + size = h * lstep; + + mapsize = finfo.smem_len; + + data = (unsigned char *)-1; + if (d_ptr->fd != -1) + data = (unsigned char *)mmap(0, mapsize, PROT_READ | PROT_WRITE, + MAP_SHARED, d_ptr->fd, 0); + + if ((long)data == -1) { + if (QApplication::type() == QApplication::GuiServer) { + perror("QLinuxFbScreen::connect"); + qWarning("Error: failed to map framebuffer device to memory."); + return false; + } + data = 0; + } else { + data += dataoffset; + } + + canaccel = useOffscreen(); + if(canaccel) + setupOffScreen(); + + // Now read in palette + if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) { + screencols= (vinfo.bits_per_pixel==8) ? 256 : 16; + int loopc; + ::fb_cmap startcmap; + startcmap.start=0; + startcmap.len=screencols; + startcmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + startcmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + startcmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + startcmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + if (d_ptr->fd == -1 || ioctl(d_ptr->fd, FBIOGETCMAP, &startcmap)) { + perror("QLinuxFbScreen::connect"); + qWarning("Error reading palette from framebuffer, using default palette"); + createPalette(startcmap, vinfo, finfo); + } + int bits_used = 0; + for(loopc=0;loopc> 8, + startcmap.green[loopc] >> 8, + startcmap.blue[loopc] >> 8); + bits_used |= startcmap.red[loopc] + | startcmap.green[loopc] + | startcmap.blue[loopc]; + } + // WORKAROUND: Some framebuffer drivers only return 8 bit + // color values, so we need to not bit shift them.. + if ((bits_used & 0x00ff) && !(bits_used & 0xff00)) { + for(loopc=0;loopcfd); +} + +// #define DEBUG_VINFO + +void QLinuxFbScreen::createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo) +{ + if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) { + screencols= (vinfo.bits_per_pixel==8) ? 256 : 16; + cmap.start=0; + cmap.len=screencols; + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + + if (screencols==16) { + if (finfo.type == FB_TYPE_PACKED_PIXELS) { + // We'll setup a grayscale cmap for 4bpp linear + int val = 0; + for (int idx = 0; idx < 16; ++idx, val += 17) { + cmap.red[idx] = (val<<8)|val; + cmap.green[idx] = (val<<8)|val; + cmap.blue[idx] = (val<<8)|val; + screenclut[idx]=qRgb(val, val, val); + } + } else { + // Default 16 colour palette + // Green is now trolltech green so certain images look nicer + // black d_gray l_gray white red green blue cyan magenta yellow + unsigned char reds[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0xFF, 0xA2, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x82 }; + unsigned char greens[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0xC5, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F }; + unsigned char blues[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0x11, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x00 }; + + for (int idx = 0; idx < 16; ++idx) { + cmap.red[idx] = ((reds[idx]) << 8)|reds[idx]; + cmap.green[idx] = ((greens[idx]) << 8)|greens[idx]; + cmap.blue[idx] = ((blues[idx]) << 8)|blues[idx]; + cmap.transp[idx] = 0; + screenclut[idx]=qRgb(reds[idx], greens[idx], blues[idx]); + } + } + } else { + if (grayscale) { + // Build grayscale palette + int i; + for(i=0;iopenTty(); + + // Grab current mode so we can reset it + fb_var_screeninfo vinfo; + fb_fix_screeninfo finfo; + //####################### + // Shut up Valgrind + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + //####################### + + if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::initDevice"); + qFatal("Error reading variable information in card init"); + return false; + } + +#ifdef DEBUG_VINFO + qDebug("Greyscale %d",vinfo.grayscale); + qDebug("Nonstd %d",vinfo.nonstd); + qDebug("Red %d %d %d",vinfo.red.offset,vinfo.red.length, + vinfo.red.msb_right); + qDebug("Green %d %d %d",vinfo.green.offset,vinfo.green.length, + vinfo.green.msb_right); + qDebug("Blue %d %d %d",vinfo.blue.offset,vinfo.blue.length, + vinfo.blue.msb_right); + qDebug("Transparent %d %d %d",vinfo.transp.offset,vinfo.transp.length, + vinfo.transp.msb_right); +#endif + + d_ptr->startupw=vinfo.xres; + d_ptr->startuph=vinfo.yres; + d_ptr->startupd=vinfo.bits_per_pixel; + grayscale = vinfo.grayscale; + + if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) { + perror("QLinuxFbScreen::initDevice"); + qCritical("Error reading fixed information in card init"); + // It's not an /error/ as such, though definitely a bad sign + // so we return true + return true; + } + +#ifdef __i386__ + // Now init mtrr + if(!::getenv("QWS_NOMTRR")) { + int mfd=QT_OPEN("/proc/mtrr",O_WRONLY,0); + // MTRR entry goes away when file is closed - i.e. + // hopefully when QWS is killed + if(mfd != -1) { + mtrr_sentry sentry; + sentry.base=(unsigned long int)finfo.smem_start; + //qDebug("Physical framebuffer address %p",(void*)finfo.smem_start); + // Size needs to be in 4k chunks, but that's not always + // what we get thanks to graphics card registers. Write combining + // these is Not Good, so we write combine what we can + // (which is not much - 4 megs on an 8 meg card, it seems) + unsigned int size=finfo.smem_len; + size=size >> 22; + size=size << 22; + sentry.size=size; + sentry.type=MTRR_TYPE_WRCOMB; + if(ioctl(mfd,MTRRIOC_ADD_ENTRY,&sentry)==-1) { + //printf("Couldn't add mtrr entry for %lx %lx, %s\n", + //sentry.base,sentry.size,strerror(errno)); + } + } + + // Should we close mfd here? + //QT_CLOSE(mfd); + } +#endif + if ((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4) || (finfo.visual==FB_VISUAL_DIRECTCOLOR)) + { + fb_cmap cmap; + createPalette(cmap, vinfo, finfo); + if (ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap)) { + perror("QLinuxFbScreen::initDevice"); + qWarning("Error writing palette to framebuffer"); + } + free(cmap.red); + free(cmap.green); + free(cmap.blue); + free(cmap.transp); + } + + if (canaccel) { + *entryp=0; + *lowest = mapsize; + insert_entry(*entryp, *lowest, *lowest); // dummy entry to mark start + } + + shared->fifocount = 0; + shared->buffer_offset = 0xffffffff; // 0 would be a sensible offset (screen) + shared->linestep = 0; + shared->cliptop = 0xffffffff; + shared->clipleft = 0xffffffff; + shared->clipright = 0xffffffff; + shared->clipbottom = 0xffffffff; + shared->rop = 0xffffffff; + +#ifdef QT_QWS_DEPTH_GENERIC + if (pixelFormat() == QImage::Format_Invalid && screencols == 0 + && d_ptr->doGenericColors) + { + qt_set_generic_blit(this, vinfo.bits_per_pixel, + vinfo.red.length, vinfo.green.length, + vinfo.blue.length, vinfo.transp.length, + vinfo.red.offset, vinfo.green.offset, + vinfo.blue.offset, vinfo.transp.offset); + } +#endif + +#ifndef QT_NO_QWS_CURSOR + QScreenCursor::initSoftwareCursor(); +#endif + blank(false); + + return true; +} + +/* + The offscreen memory manager's list of entries is stored at the bottom + of the offscreen memory area and consistes of a series of QPoolEntry's, + each of which keep track of a block of allocated memory. Unallocated memory + is implicitly indicated by the gap between blocks indicated by QPoolEntry's. + The memory manager looks through any unallocated memory before the end + of currently-allocated memory to see if a new block will fit in the gap; + if it doesn't it allocated it from the end of currently-allocated memory. + Memory is allocated from the top of the framebuffer downwards; if it hits + the list of entries then offscreen memory is full and further allocations + are made from main RAM (and hence unaccelerated). Allocated memory can + be seen as a sort of upside-down stack; lowest keeps track of the + bottom of the stack. +*/ + +void QLinuxFbScreen::delete_entry(int pos) +{ + if (pos > *entryp || pos < 0) { + qWarning("Attempt to delete odd pos! %d %d", pos, *entryp); + return; + } + +#ifdef DEBUG_CACHE + qDebug("Remove entry: %d", pos); +#endif + + QPoolEntry *qpe = &entries[pos]; + if (qpe->start <= *lowest) { + // Lowest goes up again + *lowest = entries[pos-1].start; +#ifdef DEBUG_CACHE + qDebug(" moved lowest to %d", *lowest); +#endif + } + + (*entryp)--; + if (pos == *entryp) + return; + + int size = (*entryp)-pos; + memmove(&entries[pos], &entries[pos+1], size*sizeof(QPoolEntry)); +} + +void QLinuxFbScreen::insert_entry(int pos, int start, int end) +{ + if (pos > *entryp) { + qWarning("Attempt to insert odd pos! %d %d",pos,*entryp); + return; + } + +#ifdef DEBUG_CACHE + qDebug("Insert entry: %d, %d -> %d", pos, start, end); +#endif + + if (start < (int)*lowest) { + *lowest = start; +#ifdef DEBUG_CACHE + qDebug(" moved lowest to %d", *lowest); +#endif + } + + if (pos == *entryp) { + entries[pos].start = start; + entries[pos].end = end; + entries[pos].clientId = qws_client_id; + (*entryp)++; + return; + } + + int size=(*entryp)-pos; + memmove(&entries[pos+1],&entries[pos],size*sizeof(QPoolEntry)); + entries[pos].start=start; + entries[pos].end=end; + entries[pos].clientId=qws_client_id; + (*entryp)++; +} + +/*! + \fn uchar * QLinuxFbScreen::cache(int amount) + + Requests the specified \a amount of offscreen graphics card memory + from the memory manager, and returns a pointer to the data within + the framebuffer (or 0 if there is no free memory). + + Note that the display is locked while memory is allocated in order to + preserve the memory pool's integrity. + + Use the QScreen::onCard() function to retrieve an offset (in + bytes) from the start of graphics card memory for the returned + pointer. + + \sa uncache(), clearCache(), deleteEntry() +*/ + +uchar * QLinuxFbScreen::cache(int amount) +{ + if (!canaccel || entryp == 0) + return 0; + + qt_fbdpy->grab(); + + int startp = cacheStart + (*entryp+1) * sizeof(QPoolEntry); + if (startp >= (int)*lowest) { + // We don't have room for another cache QPoolEntry. +#ifdef DEBUG_CACHE + qDebug("No room for pool entry in VRAM"); +#endif + qt_fbdpy->ungrab(); + return 0; + } + + int align = pixmapOffsetAlignment(); + + if (*entryp > 1) { + // Try to find a gap in the allocated blocks. + for (int loopc = 0; loopc < *entryp-1; loopc++) { + int freestart = entries[loopc+1].end; + int freeend = entries[loopc].start; + if (freestart != freeend) { + while (freestart % align) { + freestart++; + } + int len=freeend-freestart; + if (len >= amount) { + insert_entry(loopc+1, freestart, freestart+amount); + qt_fbdpy->ungrab(); + return data+freestart; + } + } + } + } + + // No free blocks in already-taken memory; get some more + // if we can + int newlowest = (*lowest)-amount; + if (newlowest % align) { + newlowest -= align; + while (newlowest % align) { + newlowest++; + } + } + if (startp >= newlowest) { + qt_fbdpy->ungrab(); +#ifdef DEBUG_CACHE + qDebug("No VRAM available for %d bytes", amount); +#endif + return 0; + } + insert_entry(*entryp, newlowest, *lowest); + qt_fbdpy->ungrab(); + + return data + newlowest; +} + +/*! + \fn void QLinuxFbScreen::uncache(uchar * memoryBlock) + + Deletes the specified \a memoryBlock allocated from the graphics + card memory. + + Note that the display is locked while memory is unallocated in + order to preserve the memory pool's integrity. + + This function will first sync the graphics card to ensure the + memory isn't still being used by a command in the graphics card + FIFO queue. It is possible to speed up a driver by overriding this + function to avoid syncing. For example, the driver might delay + deleting the memory until it detects that all commands dealing + with the memory are no longer in the queue. Note that it will then + be up to the driver to ensure that the specified \a memoryBlock no + longer is being used. + + \sa cache(), deleteEntry(), clearCache() + */ +void QLinuxFbScreen::uncache(uchar * c) +{ + // need to sync graphics card + + deleteEntry(c); +} + +/*! + \fn void QLinuxFbScreen::deleteEntry(uchar * memoryBlock) + + Deletes the specified \a memoryBlock allocated from the graphics + card memory. + + \sa uncache(), cache(), clearCache() +*/ +void QLinuxFbScreen::deleteEntry(uchar * c) +{ + qt_fbdpy->grab(); + unsigned long pos=(unsigned long)c; + pos-=((unsigned long)data); + unsigned int hold=(*entryp); + for(unsigned int loopc=1;loopcungrab(); + return; + } + } + qt_fbdpy->ungrab(); + qWarning("Attempt to delete unknown offset %ld",pos); +} + +/*! + Removes all entries from the cache for the specified screen \a + instance and client identified by the given \a clientId. + + Calling this function should only be necessary if a client exits + abnormally. + + \sa cache(), uncache(), deleteEntry() +*/ +void QLinuxFbScreen::clearCache(QScreen *instance, int clientId) +{ + QLinuxFbScreen *screen = (QLinuxFbScreen *)instance; + if (!screen->canaccel || !screen->entryp) + return; + qt_fbdpy->grab(); + for (int loopc = 0; loopc < *(screen->entryp); loopc++) { + if (screen->entries[loopc].clientId == clientId) { + screen->delete_entry(loopc); + loopc--; + } + } + qt_fbdpy->ungrab(); +} + + +void QLinuxFbScreen::setupOffScreen() +{ + // Figure out position of offscreen memory + // Set up pool entries pointer table and 64-bit align it + int psize = size; + + // hw: this causes the limitation of cursors to 64x64 + // the cursor should rather use the normal pixmap mechanism + psize += 4096; // cursor data + psize += 8; // for alignment + psize &= ~0x7; // align + + unsigned long pos = (unsigned long)data; + pos += psize; + entryp = ((int *)pos); + lowest = ((unsigned int *)pos)+1; + pos += (sizeof(int))*4; + entries = (QPoolEntry *)pos; + + // beginning of offscreen memory available for pixmaps. + cacheStart = psize + 4*sizeof(int) + sizeof(QPoolEntry); +} + +/*! + \reimp + + This is called by the \l{Qt for Embedded Linux} server when it shuts + down, and should be inherited if you need to do any card-specific cleanup. + The default version hides the screen cursor and reenables the blinking + cursor and screen blanking. +*/ + +void QLinuxFbScreen::shutdownDevice() +{ + // Causing crashes. Not needed. + //setMode(startupw,startuph,startupd); +/* + if (startupd == 8) { + ioctl(fd,FBIOPUTCMAP,startcmap); + free(startcmap->red); + free(startcmap->green); + free(startcmap->blue); + free(startcmap->transp); + delete startcmap; + startcmap = 0; + } +*/ + d_ptr->closeTty(); +} + +/*! + \fn void QLinuxFbScreen::set(unsigned int index,unsigned int red,unsigned int green,unsigned int blue) + + Sets the specified color \a index to the specified RGB value, (\a + red, \a green, \a blue), when in paletted graphics modes. +*/ + +void QLinuxFbScreen::set(unsigned int i,unsigned int r,unsigned int g,unsigned int b) +{ + if (d_ptr->fd != -1) { + fb_cmap cmap; + cmap.start=i; + cmap.len=1; + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.red[0]=r << 8; + cmap.green[0]=g << 8; + cmap.blue[0]=b << 8; + cmap.transp[0]=0; + ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap); + free(cmap.red); + free(cmap.green); + free(cmap.blue); + free(cmap.transp); + } + screenclut[i] = qRgb(r, g, b); +} + +/*! + \reimp + + Sets the framebuffer to a new resolution and bit depth. The width is + in \a nw, the height is in \a nh, and the depth is in \a nd. After + doing this any currently-existing paint engines will be invalid and the + screen should be completely redrawn. In a multiple-process + Embedded Qt situation you must signal all other applications to + call setMode() to the same mode and redraw. +*/ + +void QLinuxFbScreen::setMode(int nw,int nh,int nd) +{ + if (d_ptr->fd == -1) + return; + + fb_fix_screeninfo finfo; + fb_var_screeninfo vinfo; + //####################### + // Shut up Valgrind + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + //####################### + + if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::setMode"); + qFatal("Error reading variable information in mode change"); + } + + vinfo.xres=nw; + vinfo.yres=nh; + vinfo.bits_per_pixel=nd; + + if (ioctl(d_ptr->fd, FBIOPUT_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::setMode"); + qCritical("Error writing variable information in mode change"); + } + + if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::setMode"); + qFatal("Error reading changed variable information in mode change"); + } + + if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) { + perror("QLinuxFbScreen::setMode"); + qFatal("Error reading fixed information"); + } + + disconnect(); + connect(d_ptr->displaySpec); + exposeRegion(region(), 0); +} + +// save the state of the graphics card +// This is needed so that e.g. we can restore the palette when switching +// between linux virtual consoles. + +/*! + \reimp + + This doesn't do anything; accelerated drivers may wish to reimplement + it to save graphics cards registers. It's called by the + \l{Qt for Embedded Linux} server when the virtual console is switched. +*/ + +void QLinuxFbScreen::save() +{ + // nothing to do. +} + + +// restore the state of the graphics card. +/*! + \reimp + + This is called when the virtual console is switched back to + \l{Qt for Embedded Linux} and restores the palette. +*/ +void QLinuxFbScreen::restore() +{ + if (d_ptr->fd == -1) + return; + + if ((d == 8) || (d == 4)) { + fb_cmap cmap; + cmap.start=0; + cmap.len=screencols; + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + for (int loopc = 0; loopc < screencols; loopc++) { + cmap.red[loopc] = qRed(screenclut[loopc]) << 8; + cmap.green[loopc] = qGreen(screenclut[loopc]) << 8; + cmap.blue[loopc] = qBlue(screenclut[loopc]) << 8; + cmap.transp[loopc] = 0; + } + ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap); + free(cmap.red); + free(cmap.green); + free(cmap.blue); + free(cmap.transp); + } +} + +/*! + \fn int QLinuxFbScreen::sharedRamSize(void * end) + \internal +*/ + +// This works like the QScreenCursor code. end points to the end +// of our shared structure, we return the amount of memory we reserved +int QLinuxFbScreen::sharedRamSize(void * end) +{ + shared=(QLinuxFb_Shared *)end; + shared--; + return sizeof(QLinuxFb_Shared); +} + +/*! + \reimp +*/ +void QLinuxFbScreen::blank(bool on) +{ + if (d_ptr->blank == on) + return; + +#if defined(QT_QWS_IPAQ) + if (on) + system("apm -suspend"); +#else + if (d_ptr->fd == -1) + return; +// Some old kernel versions don't have this. These defines should go +// away eventually +#if defined(FBIOBLANK) +#if defined(VESA_POWERDOWN) && defined(VESA_NO_BLANKING) + ioctl(d_ptr->fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING); +#else + ioctl(d_ptr->fd, FBIOBLANK, on ? 1 : 0); +#endif +#endif +#endif + + d_ptr->blank = on; +} + +void QLinuxFbScreen::setPixelFormat(struct fb_var_screeninfo info) +{ + const fb_bitfield rgba[4] = { info.red, info.green, + info.blue, info.transp }; + + QImage::Format format = QImage::Format_Invalid; + + switch (d) { + case 32: { + const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0}, + {0, 8, 0}, {24, 8, 0}}; + const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0}, + {16, 8, 0}, {24, 8, 0}}; + if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_ARGB32; + } else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB32; + } else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB32; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 24: { + const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0}, + {0, 8, 0}, {0, 0, 0}}; + const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0}, + {16, 8, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB888; + } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB888; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 18: { + const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0}, + {0, 6, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0) + format = QImage::Format_RGB666; + break; + } + case 16: { + const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0}, + {0, 5, 0}, {0, 0, 0}}; + const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0}, + {11, 5, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB16; + } else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB16; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 15: { + const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0}, + {0, 5, 0}, {15, 1, 0}}; + const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0}, + {10, 5, 0}, {15, 1, 0}}; + if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB555; + } else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB555; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 12: { + const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0}, + {0, 4, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0) + format = QImage::Format_RGB444; + break; + } + case 8: + break; + case 1: + format = QImage::Format_Mono; //###: LSB??? + break; + default: + break; + } + + QScreen::setPixelFormat(format); +} + +bool QLinuxFbScreen::useOffscreen() +{ + if ((mapsize - size) < 16*1024) + return false; + + return true; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_LINUXFB