/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, 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. ** ** ** $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; QLinuxFbScreen::DriverTypes driverType; 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) { #ifndef QT_NO_QWS_SIGNALHANDLER QWSSignalHandler::instance()->addObject(this); #endif } 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; } /*! \enum QLinuxFbScreen::DriverTypes This enum describes the driver type. \value GenericDriver Generic Linux framebuffer driver \value EInk8Track e-Ink framebuffer driver using the 8Track chipset */ /*! \fn QLinuxFbScreen::fixupScreenInfo(fb_fix_screeninfo &finfo, fb_var_screeninfo &vinfo) Adjust the values returned by the framebuffer driver, to work around driver bugs or nonstandard behavior in certain drivers. \a finfo and \a vinfo specify the fixed and variable screen info returned by the driver. */ void QLinuxFbScreen::fixupScreenInfo(fb_fix_screeninfo &finfo, fb_var_screeninfo &vinfo) { // 8Track e-ink devices (as found in Sony PRS-505) lie // about their bit depth -- they claim they're 1 bit per // pixel while the only supported mode is 8 bit per pixel // grayscale. // Caused by this, they also miscalculate their line length. if(!strcmp(finfo.id, "8TRACKFB") && vinfo.bits_per_pixel == 1) { vinfo.bits_per_pixel = 8; finfo.line_length = vinfo.xres; } } /*! \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() { #ifdef QT_NO_QWS_SIGNALHANDLER delete d_ptr; #endif } /*! \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; } d_ptr->driverType = strcmp(finfo.id, "8TRACKFB") ? GenericDriver : EInk8Track; 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; } fixupScreenInfo(finfo, vinfo); 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 > (int)vinfo.xres) xxoff = vinfo.xres - w; if (yyoff < 0 || yyoff + h > (int)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 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; } fixupScreenInfo(finfo, vinfo); d_ptr->startupw=vinfo.xres; d_ptr->startuph=vinfo.yres; d_ptr->startupd=vinfo.bits_per_pixel; grayscale = vinfo.grayscale; #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"); } fixupScreenInfo(finfo, vinfo); 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::setDirty(const QRect &r) { if(d_ptr->driverType == EInk8Track) { // e-Ink displays need a trigger to actually show what is // in their framebuffer memory. The 8-Track driver does this // by adding custom IOCTLs - FBIO_EINK_DISP_PIC (0x46a2) takes // an argument specifying whether or not to flash the screen // while updating. // There doesn't seem to be a way to tell it to just update // a subset of the screen. if(r.left() == 0 && r.top() == 0 && r.width() == dw && r.height() == dh) ioctl(d_ptr->fd, 0x46a2, 1); else ioctl(d_ptr->fd, 0x46a2, 0); } } /*! \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() { // Not done for 8Track because on e-Ink displays, // everything is offscreen anyway if (d_ptr->driverType == EInk8Track || ((mapsize - size) < 16*1024)) return false; return true; } QT_END_NAMESPACE #endif // QT_NO_QWS_LINUXFB