From f0922c9bafce6a565af020851012dd3cbb609888 Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Fri, 6 Jul 2012 05:40:57 +0530 Subject: linuxfb: Rework screen code Move the screen code from integration. The design philosophy is that QFbScreen takes care of generic framebuffer composition. QLinuxFbScreen is just an linux framebuffer adaptation layer. Change-Id: I8456c13826f06621037dd77fe0d0bd8873806c96 Reviewed-by: Thomas Senyk Reviewed-by: Girish Ramakrishnan --- src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp | 400 ++++++++++++++++++++--- 1 file changed, 360 insertions(+), 40 deletions(-) (limited to 'src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp') diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp index 729fd87a31..7b9eacf59f 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp @@ -43,63 +43,383 @@ #include #include +#include // overrides QT_OPEN +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + QT_BEGIN_NAMESPACE -QLinuxFbScreen::QLinuxFbScreen(uchar * d, int w, - int h, int lstep, QImage::Format screenFormat) : compositePainter(0) +static int openFramebufferDevice(const QString &dev) { - data = d; - mGeometry = QRect(0,0,w,h); - bytesPerLine = lstep; - mFormat = screenFormat; - mDepth = 16; - mScreenImage = new QImage(mGeometry.width(), mGeometry.height(), - mFormat); - mFbScreenImage = new QImage(data, mGeometry.width(), mGeometry.height(), - bytesPerLine, mFormat); - cursor = new QFbCursor(this); + int fd = -1; + + if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0) + fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR); + + if (fd == -1) { + if (access(dev.toLatin1().constData(), R_OK) == 0) + fd = QT_OPEN(dev.toLatin1().constData(), O_RDONLY); + } + + return fd; } -void QLinuxFbScreen::setGeometry(QRect rect) +static int determineDepth(const fb_var_screeninfo &vinfo) { - mGeometry = rect; - delete mFbScreenImage; - mFbScreenImage = new QImage(data, mGeometry.width(), mGeometry.height(), - bytesPerLine, mFormat); - delete compositePainter; - compositePainter = 0; - - delete mScreenImage; - mScreenImage = new QImage(mGeometry.width(), mGeometry.height(), - mFormat); + int depth = vinfo.bits_per_pixel; + if (depth== 24) { + depth = vinfo.red.length + vinfo.green.length + vinfo.blue.length; + if (depth <= 0) + depth = 24; // reset if color component lengths are not reported + } else if (depth == 16) { + depth = vinfo.red.length + vinfo.green.length + vinfo.blue.length; + if (depth <= 0) + depth = 16; + } + return depth; } -void QLinuxFbScreen::setFormat(QImage::Format format) +static QRect determineGeometry(const fb_var_screeninfo &vinfo, const QRect &userGeometry) { - mFormat = format; - delete mFbScreenImage; - mFbScreenImage = new QImage(data, mGeometry.width(), mGeometry.height(), - bytesPerLine, mFormat); - delete compositePainter; - compositePainter = 0; - - delete mScreenImage; - mScreenImage = new QImage(mGeometry.width(), mGeometry.height(), - mFormat); + int xoff = vinfo.xoffset; + int yoff = vinfo.yoffset; + int w, h; + if (userGeometry.isValid()) { + w = userGeometry.width(); + h = userGeometry.height(); + if ((uint)w > vinfo.xres) + w = vinfo.xres; + if ((uint)h > vinfo.yres) + h = vinfo.yres; + + int xxoff = userGeometry.x(), yyoff = userGeometry.y(); + if (xxoff != 0 || yyoff != 0) { + 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 { + w = vinfo.xres; + h = vinfo.yres; + } + + if (w == 0 || h == 0) { + qWarning("Unable to find screen geometry, using 320x240"); + w = 320; + h = 240; + } + + return QRect(xoff, yoff, w, h); } -QRegion QLinuxFbScreen::doRedraw() +static QSizeF determinePhysicalSize(const fb_var_screeninfo &vinfo, const QSize &mmSize, const QSize &res) { - QRegion touched; - touched = QFbScreen::doRedraw(); + int mmWidth = mmSize.width(), mmHeight = mmSize.height(); + + if (mmWidth <= 0 && mmHeight <= 0) { + if (vinfo.width != 0 && vinfo.height != 0 + && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) { + mmWidth = vinfo.width; + mmHeight = vinfo.height; + } else { + const int dpi = 72; + mmWidth = qRound(res.width() * 25.4 / dpi); + mmHeight = qRound(res.height() * 25.4 / dpi); + } + } else if (mmWidth > 0 && mmHeight <= 0) { + mmHeight = res.height() * mmWidth/res.width(); + } else if (mmHeight > 0 && mmWidth <= 0) { + mmWidth = res.width() * mmHeight/res.height(); + } + + return QSize(mmWidth, mmHeight); +} - if (!compositePainter) { - compositePainter = new QPainter(mFbScreenImage); +static QImage::Format determineFormat(const fb_var_screeninfo &info, int depth) +{ + const fb_bitfield rgba[4] = { info.red, info.green, + info.blue, info.transp }; + + QImage::Format format = QImage::Format_Invalid; + + switch (depth) { + 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 = 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 = 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 = 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 = 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; + } + + return format; +} + +static void debug(const fb_var_screeninfo &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); +} + +static int openTtyDevice(const QString &device) +{ + const char *const devs[] = { "/dev/tty0", "/dev/tty", "/dev/console", 0 }; + + int fd = -1; + if (device.isEmpty()) { + for (const char * const *dev = devs; *dev; ++dev) { + fd = QT_OPEN(*dev, O_RDWR); + if (fd != -1) + break; + } + } else { + fd = QT_OPEN(QFile::encodeName(device).constData(), O_RDWR); + } + + return fd; +} + +static bool switchToGraphicsMode(int ttyfd, int *oldMode) +{ + ioctl(ttyfd, KDGETMODE, &oldMode); + if (*oldMode != KD_GRAPHICS) { + if (ioctl(ttyfd, KDSETMODE, KD_GRAPHICS) != 0) + return 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)); + return true; +} + +static void resetTty(int ttyfd, int oldMode) +{ + ioctl(ttyfd, KDSETMODE, oldMode); + + // 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); +} + +static void blankScreen(int fd, bool on) +{ + ioctl(fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING); +} + +QLinuxFbScreen::QLinuxFbScreen() + : mFbFd(-1), mBlitter(0) +{ +} + +QLinuxFbScreen::~QLinuxFbScreen() +{ + if (mFbFd != -1) { + munmap(mMmap.data - mMmap.offset, mMmap.size); + close(mFbFd); + } + + if (mTtyFd != -1) { + resetTty(mTtyFd, mOldTtyMode); + close(mTtyFd); + } + + delete mBlitter; +} + +bool QLinuxFbScreen::initialize(const QStringList &args) +{ + QRegExp ttyRx(QLatin1String("tty=(.*)")); + QRegExp fbRx(QLatin1String("fb=(.*)")); + QRegExp mmSizeRx(QLatin1String("mmsize=(\\d+)x(\\d+)")); + QRegExp sizeRx(QLatin1String("size=(\\d+)x(\\d+)")); + QRegExp offsetRx(QLatin1String("offset=(\\d+)x(\\d+)")); + + QString fbDevice, ttyDevice; + QSize userMmSize; + QRect userGeometry; + + // Parse arguments + foreach (const QString &arg, args) { + if (sizeRx.indexIn(arg) != -1) + userGeometry.setSize(QSize(sizeRx.cap(1).toInt(), sizeRx.cap(2).toInt())); + else if (offsetRx.indexIn(arg) != -1) + userGeometry.setTopLeft(QPoint(offsetRx.cap(1).toInt(), offsetRx.cap(2).toInt())); + else if (ttyRx.indexIn(arg) != -1) + ttyDevice = ttyRx.cap(1); + else if (fbRx.indexIn(arg) != -1) + fbDevice = fbRx.cap(1); + else if (mmSizeRx.indexIn(arg) != -1) + userMmSize = QSize(mmSizeRx.cap(1).toInt(), mmSizeRx.cap(2).toInt()); + } + + if (fbDevice.isEmpty()) + fbDevice = QLatin1String("/dev/fb0"); // ## auto-detect + + // Open the device + mFbFd = openFramebufferDevice(fbDevice); + if (mFbFd == -1) { + qWarning("Failed to open framebuffer %s : %s", qPrintable(fbDevice), strerror(errno)); + return false; + } + + // Read the fixed and variable screen information + fb_fix_screeninfo finfo; + fb_var_screeninfo vinfo; + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + + if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) { + qWarning("Error reading fixed information: %s", strerror(errno)); + return false; + } + + if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) { + qWarning("Error reading variable information: %s", strerror(errno)); + return false; + } + + mDepth = determineDepth(vinfo); + mBytesPerLine = finfo.line_length; + mGeometry = determineGeometry(vinfo, userGeometry); + mFormat = determineFormat(vinfo, mDepth); + mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, mGeometry.size()); + + // mmap the framebuffer + mMmap.size = finfo.smem_len; + uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0); + if ((long)data == -1) { + qWarning("Failed to mmap framebuffer: %s", strerror(errno)); + return false; + } + + mMmap.offset = mGeometry.y() * mBytesPerLine + mGeometry.x() * mDepth / 8; + mMmap.data = data + mMmap.offset; + + QFbScreen::initializeCompositor(); + mFbScreenImage = QImage(data, mGeometry.width(), mGeometry.height(), mBytesPerLine, mFormat); + mCursor = new QFbCursor(this); + + mTtyFd = openTtyDevice(ttyDevice); + if (mTtyFd == -1) + qWarning() << "Failed to open tty" << strerror(errno); + + if (!switchToGraphicsMode(mTtyFd, &mOldTtyMode)) + qWarning() << "Failed to set graphics mode" << strerror(errno); + + blankScreen(mFbFd, false); + + return true; +} + +QRegion QLinuxFbScreen::doRedraw() +{ + QRegion touched = QFbScreen::doRedraw(); + + if (touched.isEmpty()) + return touched; + + if (!mBlitter) + mBlitter = new QPainter(&mFbScreenImage); QVector rects = touched.rects(); for (int i = 0; i < rects.size(); i++) - compositePainter->drawImage(rects[i], *mScreenImage, rects[i]); + mBlitter->drawImage(rects[i], *mScreenImage, rects[i]); return touched; } -- cgit v1.2.3