From 38be0d13830efd2d98281c645c3a60afe05ffece Mon Sep 17 00:00:00 2001 From: Qt by Nokia Date: Wed, 27 Apr 2011 12:05:43 +0200 Subject: Initial import from the monolithic Qt. This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12 --- src/plugins/gfxdrivers/ahi/ahi.pro | 14 + src/plugins/gfxdrivers/ahi/qscreenahi_qws.cpp | 598 +++++ src/plugins/gfxdrivers/ahi/qscreenahi_qws.h | 84 + src/plugins/gfxdrivers/ahi/qscreenahiplugin.cpp | 74 + src/plugins/gfxdrivers/directfb/directfb.pro | 15 + .../gfxdrivers/directfb/qdirectfbkeyboard.cpp | 436 ++++ .../gfxdrivers/directfb/qdirectfbkeyboard.h | 74 + src/plugins/gfxdrivers/directfb/qdirectfbmouse.cpp | 294 +++ src/plugins/gfxdrivers/directfb/qdirectfbmouse.h | 75 + .../gfxdrivers/directfb/qdirectfbpaintdevice.cpp | 221 ++ .../gfxdrivers/directfb/qdirectfbpaintdevice.h | 108 + .../gfxdrivers/directfb/qdirectfbpaintengine.cpp | 1430 ++++++++++++ .../gfxdrivers/directfb/qdirectfbpaintengine.h | 123 + .../gfxdrivers/directfb/qdirectfbpixmap.cpp | 588 +++++ src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h | 105 + .../gfxdrivers/directfb/qdirectfbscreen.cpp | 1819 +++++++++++++++ src/plugins/gfxdrivers/directfb/qdirectfbscreen.h | 303 +++ .../gfxdrivers/directfb/qdirectfbscreenplugin.cpp | 78 + .../gfxdrivers/directfb/qdirectfbwindowsurface.cpp | 506 +++++ .../gfxdrivers/directfb/qdirectfbwindowsurface.h | 129 ++ src/plugins/gfxdrivers/eglnullws/README | 48 + src/plugins/gfxdrivers/eglnullws/eglnullws.pro | 18 + .../gfxdrivers/eglnullws/eglnullwsscreen.cpp | 181 ++ src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h | 69 + .../gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp | 66 + .../gfxdrivers/eglnullws/eglnullwsscreenplugin.h | 47 + .../eglnullws/eglnullwswindowsurface.cpp | 84 + .../gfxdrivers/eglnullws/eglnullwswindowsurface.h | 63 + src/plugins/gfxdrivers/gfxdrivers.pro | 10 + src/plugins/gfxdrivers/linuxfb/linuxfb.pro | 14 + src/plugins/gfxdrivers/linuxfb/main.cpp | 79 + .../gfxdrivers/powervr/QWSWSEGL/QWSWSEGL.pro | 26 + .../gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c | 830 +++++++ .../gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h | 169 ++ .../gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable_p.h | 132 ++ .../gfxdrivers/powervr/QWSWSEGL/pvrqwswsegl.c | 402 ++++ src/plugins/gfxdrivers/powervr/README | 66 + src/plugins/gfxdrivers/powervr/powervr.pri | 2 + src/plugins/gfxdrivers/powervr/powervr.pro | 3 + .../powervr/pvreglscreen/pvreglscreen.cpp | 351 +++ .../gfxdrivers/powervr/pvreglscreen/pvreglscreen.h | 99 + .../powervr/pvreglscreen/pvreglscreen.pro | 27 + .../powervr/pvreglscreen/pvreglscreenplugin.cpp | 74 + .../powervr/pvreglscreen/pvreglwindowsurface.cpp | 273 +++ .../powervr/pvreglscreen/pvreglwindowsurface.h | 85 + src/plugins/gfxdrivers/qvfb/main.cpp | 82 + src/plugins/gfxdrivers/qvfb/qvfb.pro | 19 + src/plugins/gfxdrivers/transformed/main.cpp | 84 + src/plugins/gfxdrivers/transformed/transformed.pro | 13 + src/plugins/gfxdrivers/vnc/main.cpp | 86 + src/plugins/gfxdrivers/vnc/qscreenvnc_p.h | 524 +++++ src/plugins/gfxdrivers/vnc/qscreenvnc_qws.cpp | 2338 ++++++++++++++++++++ src/plugins/gfxdrivers/vnc/qscreenvnc_qws.h | 88 + src/plugins/gfxdrivers/vnc/vnc.pro | 16 + 54 files changed, 13542 insertions(+) create mode 100644 src/plugins/gfxdrivers/ahi/ahi.pro create mode 100644 src/plugins/gfxdrivers/ahi/qscreenahi_qws.cpp create mode 100644 src/plugins/gfxdrivers/ahi/qscreenahi_qws.h create mode 100644 src/plugins/gfxdrivers/ahi/qscreenahiplugin.cpp create mode 100644 src/plugins/gfxdrivers/directfb/directfb.pro create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbmouse.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbmouse.h create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbscreen.h create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp create mode 100644 src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h create mode 100644 src/plugins/gfxdrivers/eglnullws/README create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullws.pro create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h create mode 100644 src/plugins/gfxdrivers/gfxdrivers.pro create mode 100644 src/plugins/gfxdrivers/linuxfb/linuxfb.pro create mode 100644 src/plugins/gfxdrivers/linuxfb/main.cpp create mode 100644 src/plugins/gfxdrivers/powervr/QWSWSEGL/QWSWSEGL.pro create mode 100644 src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c create mode 100644 src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h create mode 100644 src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable_p.h create mode 100644 src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwswsegl.c create mode 100644 src/plugins/gfxdrivers/powervr/README create mode 100644 src/plugins/gfxdrivers/powervr/powervr.pri create mode 100644 src/plugins/gfxdrivers/powervr/powervr.pro create mode 100644 src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp create mode 100644 src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h create mode 100644 src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro create mode 100644 src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp create mode 100644 src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.cpp create mode 100644 src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h create mode 100644 src/plugins/gfxdrivers/qvfb/main.cpp create mode 100644 src/plugins/gfxdrivers/qvfb/qvfb.pro create mode 100644 src/plugins/gfxdrivers/transformed/main.cpp create mode 100644 src/plugins/gfxdrivers/transformed/transformed.pro create mode 100644 src/plugins/gfxdrivers/vnc/main.cpp create mode 100644 src/plugins/gfxdrivers/vnc/qscreenvnc_p.h create mode 100644 src/plugins/gfxdrivers/vnc/qscreenvnc_qws.cpp create mode 100644 src/plugins/gfxdrivers/vnc/qscreenvnc_qws.h create mode 100644 src/plugins/gfxdrivers/vnc/vnc.pro (limited to 'src/plugins/gfxdrivers') diff --git a/src/plugins/gfxdrivers/ahi/ahi.pro b/src/plugins/gfxdrivers/ahi/ahi.pro new file mode 100644 index 0000000000..6fc8a5cc8d --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/ahi.pro @@ -0,0 +1,14 @@ +TARGET = qahiscreen +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +HEADERS = qscreenahi_qws.h + +SOURCES = qscreenahi_qws.cpp \ + qscreenahiplugin.cpp + +LIBS += -lahi diff --git a/src/plugins/gfxdrivers/ahi/qscreenahi_qws.cpp b/src/plugins/gfxdrivers/ahi/qscreenahi_qws.cpp new file mode 100644 index 0000000000..aa28b0eeec --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/qscreenahi_qws.cpp @@ -0,0 +1,598 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qscreenahi_qws.h" + +#ifndef QT_NO_QWS_AHI + +#include +#include +#include +#include +#include + +#include + +//#define QAHISCREEN_DEBUG + +static int depthForPixelFormat(const AhiPixelFormat_t format) +{ + switch (format) { + case AhiPix1bpp: + return 1; + case AhiPix2bpp: + return 2; + case AhiPix4bpp: + return 4; + case AhiPix8bpp_332RGB: + case AhiPix8bpp: + return 8; + case AhiPix16bpp_444RGB: + return 12; + case AhiPix16bpp_555RGB: + return 15; + case AhiPix16bpp_565RGB: + return 16; + case AhiPix32bpp_8888ARGB: + case AhiPix32bpp_8888BGRA: + return 32; + default: + return 0; + } +} + +static AhiPixelFormat_t pixelFormatForImageFormat(const QImage::Format format) +{ + switch (format) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + return AhiPix1bpp; + case QImage::Format_Indexed8: + return AhiPix8bpp; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + return AhiPix32bpp_8888ARGB; + case QImage::Format_RGB16: + return AhiPix16bpp_565RGB; + case QImage::Format_RGB555: + return AhiPix16bpp_555RGB; + case QImage::Format_ARGB4444_Premultiplied: + case QImage::Format_RGB444: + return AhiPix16bpp_444RGB; + default: + return AhiPixelFormatMax; + } +} + +class QAhiScreenCursor : public QScreenCursor +{ +public: + QAhiScreenCursor(QScreen *screen, AhiDevCtx_t context); + + void set(const QImage &image, int hotx, int hoty); + void move(int x, int y); + void show(); + void hide(); + +private: + QScreen *screen; + AhiDevCtx_t context; +}; + +QAhiScreenCursor::QAhiScreenCursor(QScreen *s, AhiDevCtx_t c) + : QScreenCursor(), screen(s), context(c) +{ + hwaccel = true; + supportsAlpha = true; + + if (enable) + show(); + else + hide(); +} + +void QAhiScreenCursor::set(const QImage &image, int hotx, int hoty) +{ + if (image.isNull()) { + QScreenCursor::set(image, hotx, hoty); + return; + } + + if (image.format() != QImage::Format_MonoLSB) { + set(image.convertToFormat(QImage::Format_MonoLSB), hotx, hoty); + return; + } + + AhiPixelFormat_t pixFmt = pixelFormatForImageFormat(image.format()); + + if (pixFmt >= AhiPixelFormatMax) { // generic fallback + QImage::Format toFormat = screen->pixelFormat(); + if (toFormat == QImage::Format_Invalid) + toFormat = QImage::Format_ARGB32; + set(image.convertToFormat(toFormat), hotx, hoty); + return; + } + + AhiPoint_t hotSpot = { hotx, hoty }; + AhiSize_t bitmapSize = { image.width(), image.height() }; + AhiBitmap_t bitmap = { bitmapSize, (void*)(image.bits()), + image.bytesPerLine(), pixFmt }; + + AhiSts_t status; + status = AhiDispCursorSet(context, AhiCursor1, &bitmap, &hotSpot, + image.serialNumber(), 0); + if (status != AhiStsOk) + qWarning("QAhiScreenCursor::set(): AhiDispCursorSet failed: %x", + status); + + QScreenCursor::set(image, hotx, hoty); +} + +void QAhiScreenCursor::move(int x, int y) +{ + AhiPoint_t pos = { x, y }; + AhiSts_t status = AhiDispCursorPos(context, AhiCursor1, &pos, 0); + if (status != AhiStsOk) + qWarning("QAhiScreenCursor::move(): error setting mouse position: %x", + status); + QScreenCursor::move(x, y); +} + +void QAhiScreenCursor::show() +{ + AhiSts_t status; + status = AhiDispCursorState(context, AhiCursor1, AhiCursorStateOn, 0); + if (status != AhiStsOk) + qWarning("QAhiScreenCursor::show(): error setting state: %x", status); + QScreenCursor::show(); +} + +void QAhiScreenCursor::hide() +{ + AhiDispCursorState(context, AhiCursor1, AhiCursorStateOff, 0); + QScreenCursor::hide(); +} + +class QAhiScreenPrivate : public QObject +{ +public: + QAhiScreenPrivate(); + ~QAhiScreenPrivate(); + + bool setMode(AhiDispMode_t mode); + + AhiDevCtx_t context; + AhiSurf_t surface; + QAhiScreenCursor *cursor; +}; + +QT_BEGIN_NAMESPACE + +QAhiScreenPrivate::QAhiScreenPrivate() + : context(0), surface(0), cursor(0) +{ +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addObject(this); +#endif +} + +QAhiScreenPrivate::~QAhiScreenPrivate() +{ + delete cursor; + + if (surface) { + AhiSurfFree(context, surface); + surface = 0; + } + if (context) { + AhiDevClose(context); + context = 0; + } + AhiTerm(); +} + +bool QAhiScreenPrivate::setMode(AhiDispMode_t mode) +{ + AhiSts_t status; + + status = AhiDispModeSet(context, &mode, 0); + if (status != AhiStsOk) { + qCritical("QAhiScreenPrivate::setMode(): AhiDispModeSet failed: %x", + status); + return false; + } + + if (surface) { + AhiSurfFree(context, surface); + surface = 0; + } + status = AhiSurfAlloc(context, &surface, &mode.size, mode.pixFmt, + AHIFLAG_SURFFIXED); + if (status != AhiStsOk) { + qCritical("QAhiScreenPrivate::setMode(): AhisurfAlloc failed: %x", + status); + return false; + } + + status = AhiDispSurfSet(context, surface, 0); + if (status != AhiStsOk) { + qCritical("QAhiScreenPrivate::setMode(): AhiDispSurfSet failed: %x", + status); + return false; + } + + return true; +} + +QAhiScreen::QAhiScreen(int displayId) + : QScreen(displayId), d_ptr(new QAhiScreenPrivate) +{ +} + +QAhiScreen::~QAhiScreen() +{ + delete d_ptr; +} + +bool QAhiScreen::configure() +{ + AhiSurfInfo_t surfaceInfo; + AhiSts_t status; + + status = AhiSurfInfo(d_ptr->context, d_ptr->surface, &surfaceInfo); + if (status != AhiStsOk) { + qCritical("QAhiScreen::configure(): AhiSurfInfo failed: %x", status); + return false; + } + + QScreen::data = 0; + QScreen::w = QScreen::dw = surfaceInfo.size.cx; + QScreen::h = QScreen::dh = surfaceInfo.size.cy; + QScreen::lstep = surfaceInfo.stride; + QScreen::size = surfaceInfo.sizeInBytes; + + switch (surfaceInfo.pixFmt) { + case AhiPix1bpp: + setPixelFormat(QImage::Format_Mono); + QScreen::d = 1; + break; + case AhiPix4bpp: + QScreen::d = 4; + break; + case AhiPix8bpp_332RGB: + case AhiPix8bpp: + QScreen::d = 8; + break; + case AhiPix16bpp_444RGB: + setPixelFormat(QImage::Format_RGB444); + QScreen::d = 12; + break; + case AhiPix16bpp_555RGB: + setPixelFormat(QImage::Format_RGB555); + QScreen::d = 15; + break; + case AhiPix16bpp_565RGB: + setPixelFormat(QImage::Format_RGB16); + QScreen::d = 16; + break; + case AhiPix2bpp: + QScreen::d = 2; + break; + case AhiPix32bpp_8888ARGB: + setPixelFormat(QImage::Format_ARGB32); + // fallthrough + case AhiPix32bpp_8888BGRA: + QScreen::d = 32; + break; + default: + qCritical("QAhiScreen::configure(): Unknown pixel format: %x", + surfaceInfo.pixFmt); + return false; + } + + const int dpi = 72; + QScreen::physWidth = qRound(QScreen::dw * 25.4 / dpi); + QScreen::physHeight = qRound(QScreen::dh * 25.4 / dpi); + + return true; +} + +bool QAhiScreen::connect(const QString &displaySpec) +{ + Q_UNUSED(displaySpec); + + AhiSts_t status; + + status = AhiInit(0); + if (status != AhiStsOk) { + qCritical("QAhiScreen::connect(): AhiInit failed: %x", status); + return false; + } + + AhiDev_t device; + AhiDevInfo_t info; + + status = AhiDevEnum(&device, &info, 0); + if (status != AhiStsOk) { + qCritical("QAhiScreen::connect(): AhiDevEnum failed: %x", status); + return false; + } +#ifdef QAHISCREEN_DEBUG + { + int displayNo = 0; + AhiDevInfo_t dispInfo = info; + qDebug("AHI supported devices:"); + do { + qDebug(" %2i: %s, sw version: %s (rev %u)\n" + " chip: 0x%x (rev %u), mem: %i (%i/%i), bus: 0x%x", + displayNo, dispInfo.name, + dispInfo.swVersion, uint(dispInfo.swRevision), + uint(dispInfo.chipId), uint(dispInfo.revisionId), + uint(dispInfo.totalMemory), + uint(dispInfo.internalMemSize), + uint(dispInfo.externalMemSize), + uint(dispInfo.cpuBusInterfaceMode)); + status = AhiDevEnum(&device, &info, ++displayNo); + } while (status == AhiStsOk); + } +#endif + + status = AhiDevOpen(&d_ptr->context, device, "qscreenahi", + AHIFLAG_USERLEVEL); + if (status != AhiStsOk) { + qCritical("QAhiScreen::connect(): AhiDevOpen failed: %x", status); + return false; + } + + AhiDispMode_t mode; + + status = AhiDispModeEnum(d_ptr->context, &mode, 0); + if (status != AhiStsOk) { + qCritical("QAhiScreen::connect(): AhiDispModeEnum failed: %x", status); + return false; + } + +#ifdef QAHISCREEN_DEBUG + { + int modeNo = 0; + AhiDispMode_t modeInfo = mode; + qDebug("AHI supported modes:"); + do { + qDebug(" %2i: %ux%u, fmt: %i, %u Hz, rot: %i, mirror: %i", + modeNo, uint(modeInfo.size.cx), uint(modeInfo.size.cy), + modeInfo.pixFmt, uint(modeInfo.frequency), + modeInfo.rotation, modeInfo.mirror); + status = AhiDispModeEnum(d_ptr->context, &modeInfo, ++modeNo); + } while (status == AhiStsOk); + } +#endif + + if (QApplication::type() == QApplication::GuiServer) { + if (!d_ptr->setMode(mode)) + return false; + } else { + status = AhiDispSurfGet(d_ptr->context, &d_ptr->surface); + if (status != AhiStsOk) { + qCritical("QAhiScreen::connect(): AhiDispSurfGet failed: %x", + status); + return false; + } + + status = AhiDispModeGet(d_ptr->context, &mode); + if (status != AhiStsOk) { + qCritical("QAhiScreen::context(): AhiDispModeGet failed: %x", + status); + return false; + } + } + + return configure(); +} + +void QAhiScreen::disconnect() +{ + AhiSurfFree(d_ptr->context, d_ptr->surface); + d_ptr->surface = 0; + AhiDevClose(d_ptr->context); + d_ptr->context = 0; + AhiTerm(); +} + +bool QAhiScreen::initDevice() +{ + QScreenCursor::initSoftwareCursor(); + + AhiSts_t status = AhiDispState(d_ptr->context, AhiDispStateOn, 0); + if (status != AhiStsOk) { + qCritical("QAhiScreen::connect(): AhiDispState failed: %x", status); + return false; + } + + return true; +} + +void QAhiScreen::shutdownDevice() +{ + AhiDispState(d_ptr->context, AhiDispStateOff, 0); +} + +void QAhiScreen::setMode(int width, int height, int depth) +{ + int modeNo = 0; + AhiDispMode_t mode; + AhiSts_t status = AhiStsOk; + + while (status == AhiStsOk) { + status = AhiDispModeEnum(d_ptr->context, &mode, modeNo); + if (mode.size.cx == uint(width) && + mode.size.cy == uint(height) && + depthForPixelFormat(mode.pixFmt) == depth) + { + d_ptr->setMode(mode); + configure(); + return; + } + } +} + +void QAhiScreen::blit(const QImage &image, const QPoint &topLeft, + const QRegion ®) +{ + AhiPixelFormat_t pixFmt = pixelFormatForImageFormat(image.format()); + + if (pixFmt >= AhiPixelFormatMax) { // generic fallback + QImage::Format toFormat = pixelFormat(); + if (toFormat == QImage::Format_Invalid) + toFormat = QImage::Format_ARGB32; + blit(image.convertToFormat(toFormat), topLeft, reg); + return; + } + + AhiSts_t status; + + status = AhiDrawSurfDstSet(d_ptr->context, d_ptr->surface, 0); + if (status != AhiStsOk) { + qWarning("QAhiScreen::blit(): AhiDrawSurfDstSet failed: %x", status); + return; + } + + const QVector rects = (reg & region()).rects(); + const int numRects = rects.size(); + QVarLengthArray src(numRects); + QVarLengthArray dest(numRects); + + for (int i = 0; i < numRects; ++i) { + const QRect rect = rects.at(i); + + src[i].x = rect.x() - topLeft.x(); + src[i].y = rect.y() - topLeft.y(); + dest[i].left = rect.left(); + dest[i].top = rect.top(); + dest[i].right = rect.x() + rect.width(); + dest[i].bottom = rect.y() + rect.height(); + } + + AhiSize_t bitmapSize = { image.width(), image.height() }; + AhiBitmap_t bitmap = { bitmapSize, (void*)(image.bits()), + image.bytesPerLine(), pixFmt }; + + status = AhiDrawRopSet(d_ptr->context, AHIMAKEROP3(AHIROPSRCCOPY)); + if (status != AhiStsOk) { + qWarning("QAhiScreen::blit(): AhiDrawRopSet failed: %x", status); + return; + } + + for (int i = 0; i < numRects; ++i) { + status = AhiDrawBitmapBlt(d_ptr->context, &dest[i], &src[i], + &bitmap, 0, 0); + if (status != AhiStsOk) { + qWarning("QAhiScreen::blit(): AhiDrawBitmapBlt failed: %x", + status); + break; + } + } +} + +void QAhiScreen::solidFill(const QColor &color, const QRegion ®) +{ + AhiSts_t status = AhiStsOk; + + switch (pixelFormat()) { + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + status = AhiDrawBrushFgColorSet(d_ptr->context, color.rgba()); + break; + case QImage::Format_RGB16: + status = AhiDrawBrushFgColorSet(d_ptr->context, qt_convRgbTo16(color.rgb())); + break; + default: + qFatal("QAhiScreen::solidFill(): Not implemented for pixel format %d", + int(pixelFormat())); + break; + } + + if (status != AhiStsOk) { + qWarning("QAhiScreen::solidFill(): AhiDrawBrushFgColorSet failed: %x", + status); + return; + } + + status = AhiDrawBrushSet(d_ptr->context, 0, 0, 0, AHIFLAG_BRUSHSOLID); + if (status != AhiStsOk) { + qWarning("QAhiScreen::solidFill(): AhiDrawBrushSet failed: %x", + status); + return; + } + + status = AhiDrawRopSet(d_ptr->context, AHIMAKEROP3(AHIROPPATCOPY)); + if (status != AhiStsOk) { + qWarning("QAhiScreen::solidFill(): AhiDrawRopSet failed: %x", status); + return; + } + + status = AhiDrawSurfDstSet(d_ptr->context, d_ptr->surface, 0); + if (status != AhiStsOk) { + qWarning("QAhiScreen::solidFill(): AhiDrawSurfDst failed: %x", status); + return; + } + + const QVector rects = (reg & region()).rects(); + QVarLengthArray ahiRects(rects.size()); + + for (int i = 0; i < rects.size(); ++i) { + const QRect rect = rects.at(i); + ahiRects[i].left = rect.left(); + ahiRects[i].top = rect.top(); + ahiRects[i].right = rect.x() + rect.width(); + ahiRects[i].bottom = rect.y() + rect.height(); + } + + status = AhiDrawBitBltMulti(d_ptr->context, ahiRects.data(), + 0, ahiRects.size()); + if (status != AhiStsOk) + qWarning("QAhiScreen::solidFill(): AhiDrawBitBlt failed: %x", status); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_AHI diff --git a/src/plugins/gfxdrivers/ahi/qscreenahi_qws.h b/src/plugins/gfxdrivers/ahi/qscreenahi_qws.h new file mode 100644 index 0000000000..ec947f4dc6 --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/qscreenahi_qws.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QAHISCREEN_H +#define QAHISCREEN_H + +#include + +#ifndef QT_NO_QWS_AHI + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QAhiScreenPrivate; + +class QAhiScreen : public QScreen +{ +public: + QAhiScreen(int displayId); + ~QAhiScreen(); + + bool connect(const QString &displaySpec); + void disconnect(); + bool initDevice(); + void shutdownDevice(); + void setMode(int width, int height, int depth); + + void blit(const QImage &image, const QPoint &topLeft, + const QRegion ®ion); + void solidFill(const QColor &color, const QRegion ®ion); + +private: + bool configure(); + + QAhiScreenPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_AHI +#endif // QAHISCREEN_H diff --git a/src/plugins/gfxdrivers/ahi/qscreenahiplugin.cpp b/src/plugins/gfxdrivers/ahi/qscreenahiplugin.cpp new file mode 100644 index 0000000000..7fdb7789b8 --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/qscreenahiplugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qscreenahi_qws.h" + +#include +#include + +class QAhiScreenPlugin : public QScreenDriverPlugin +{ +public: + QAhiScreenPlugin(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +QAhiScreenPlugin::QAhiScreenPlugin() + : QScreenDriverPlugin() +{ +} + +QStringList QAhiScreenPlugin::keys() const +{ + return (QStringList() << "ahi"); +} + +QScreen* QAhiScreenPlugin::create(const QString& driver, int displayId) +{ + if (driver.toLower() != "ahi") + return 0; + + return new QAhiScreen(displayId); +} + +Q_EXPORT_PLUGIN2(qahiscreen, QAhiScreenPlugin) diff --git a/src/plugins/gfxdrivers/directfb/directfb.pro b/src/plugins/gfxdrivers/directfb/directfb.pro new file mode 100644 index 0000000000..d397050bdc --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/directfb.pro @@ -0,0 +1,15 @@ +TARGET = qdirectfbscreen +include(../../qpluginbase.pri) +include($$QT_SOURCE_TREE/src/gui/embedded/directfb.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +SOURCES += qdirectfbscreenplugin.cpp + +QMAKE_CXXFLAGS += $$QT_CFLAGS_DIRECTFB +LIBS += $$QT_LIBS_DIRECTFB +DEFINES += $$QT_DEFINES_DIRECTFB +contains(gfx-plugins, directfb):DEFINES += QT_QWS_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp new file mode 100644 index 0000000000..5c6842edca --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp @@ -0,0 +1,436 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbkeyboard.h" + +#ifndef QT_NO_QWS_DIRECTFB + +#include "qdirectfbscreen.h" +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class KeyMap : public QHash +{ +public: + KeyMap(); +}; + +Q_GLOBAL_STATIC(KeyMap, keymap); + +class QDirectFBKeyboardHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QDirectFBKeyboardHandlerPrivate(QDirectFBKeyboardHandler *handler); + ~QDirectFBKeyboardHandlerPrivate(); + + void suspend(); + void resume(); + +private: + QDirectFBKeyboardHandler *handler; + IDirectFBEventBuffer *eventBuffer; + QSocketNotifier *keyboardNotifier; + DFBEvent event; + int bytesRead; + int lastUnicode, lastKeycode; + Qt::KeyboardModifiers lastModifiers; +private Q_SLOTS: + void readKeyboardData(); +}; + +QDirectFBKeyboardHandlerPrivate::QDirectFBKeyboardHandlerPrivate(QDirectFBKeyboardHandler *h) + : handler(h), eventBuffer(0), keyboardNotifier(0), bytesRead(0), + lastUnicode(0), lastKeycode(0), lastModifiers(0) +{ + Q_ASSERT(qt_screen); + + IDirectFB *fb = QDirectFBScreen::instance()->dfb(); + if (!fb) { + qCritical("QDirectFBKeyboardHandler: DirectFB not initialized"); + return; + } + + DFBResult result; + result = fb->CreateInputEventBuffer(fb, DICAPS_KEYS, DFB_TRUE, + &eventBuffer); + if (result != DFB_OK) { + DirectFBError("QDirectFBKeyboardHandler: " + "Unable to create input event buffer", result); + return; + } + + int fd; + result = eventBuffer->CreateFileDescriptor(eventBuffer, &fd); + if (result != DFB_OK) { + DirectFBError("QDirectFBKeyboardHandler: " + "Unable to create file descriptor", result); + return; + } + + int flags = ::fcntl(fd, F_GETFL, 0); + ::fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + memset(&event, 0, sizeof(event)); + + keyboardNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(keyboardNotifier, SIGNAL(activated(int)), + this, SLOT(readKeyboardData())); + resume(); +} + +void QDirectFBKeyboardHandlerPrivate::suspend() +{ + keyboardNotifier->setEnabled(false); +} + +void QDirectFBKeyboardHandlerPrivate::resume() +{ + eventBuffer->Reset(eventBuffer); + keyboardNotifier->setEnabled(true); +} + +QDirectFBKeyboardHandlerPrivate::~QDirectFBKeyboardHandlerPrivate() +{ + if (eventBuffer) + eventBuffer->Release(eventBuffer); +} + +void QDirectFBKeyboardHandlerPrivate::readKeyboardData() +{ + if(!qt_screen) + return; + + for (;;) { + // GetEvent returns DFB_UNSUPPORTED after CreateFileDescriptor(). + // This seems stupid and I really hope it's a bug which will be fixed. + + // DFBResult ret = eventBuffer->GetEvent(eventBuffer, &event); + + char *buf = reinterpret_cast(&event); + int ret = ::read(keyboardNotifier->socket(), + buf + bytesRead, sizeof(DFBEvent) - bytesRead); + if (ret == -1) { + if (errno != EAGAIN) + qWarning("QDirectFBKeyboardHandlerPrivate::readKeyboardData(): %s", + strerror(errno)); + return; + } + + Q_ASSERT(ret >= 0); + bytesRead += ret; + if (bytesRead < int(sizeof(DFBEvent))) + break; + bytesRead = 0; + + Q_ASSERT(event.clazz == DFEC_INPUT); + + const DFBInputEvent input = event.input; + + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + // Not implemented: + // if (input.modifiers & DIMM_SUPER) + // if (input.modifiers & DIMM_HYPER) + + if (!(input.flags & DIEF_KEYSYMBOL) || + !(input.flags & DIEF_KEYID) || + !(input.type & (DIET_KEYPRESS|DIET_KEYRELEASE))) + { + static bool first = true; + if (first) { + qWarning("QDirectFBKeyboardHandler - Getting unexpected non-keyboard related events"); + first = false; + } + break; + } + + if (input.flags & DIEF_MODIFIERS) { + if (input.modifiers & DIMM_SHIFT) + modifiers |= Qt::ShiftModifier; + if (input.modifiers & DIMM_CONTROL) + modifiers |= Qt::ControlModifier; + if (input.modifiers & DIMM_ALT) + modifiers |= Qt::AltModifier; + if (input.modifiers & DIMM_ALTGR) + modifiers |= Qt::AltModifier; + if (input.modifiers & DIMM_META) + modifiers |= Qt::MetaModifier; + } + + + const bool press = input.type & DIET_KEYPRESS; + DFBInputDeviceKeySymbol symbol = input.key_symbol; + int unicode = -1; + int keycode = 0; + + keycode = keymap()->value(symbol); + if (DFB_KEY_TYPE(symbol) == DIKT_UNICODE) + unicode = symbol; + + if (unicode != -1 || keycode != 0) { + bool autoRepeat = false; + if (press) { + if (unicode == lastUnicode && keycode == lastKeycode && modifiers == lastModifiers) { + autoRepeat = true; + } else { + lastUnicode = unicode; + lastKeycode = keycode; + lastModifiers = modifiers; + } + } else { + lastUnicode = lastKeycode = -1; + lastModifiers = 0; + } + if (autoRepeat) { + handler->processKeyEvent(unicode, keycode, + modifiers, false, autoRepeat); + + } + + handler->processKeyEvent(unicode, keycode, + modifiers, press, autoRepeat); + } + } +} + +QDirectFBKeyboardHandler::QDirectFBKeyboardHandler(const QString &device) + : QWSKeyboardHandler() +{ + Q_UNUSED(device); + d = new QDirectFBKeyboardHandlerPrivate(this); +} + +QDirectFBKeyboardHandler::~QDirectFBKeyboardHandler() +{ + delete d; +} + +KeyMap::KeyMap() +{ + insert(DIKS_BACKSPACE , Qt::Key_Backspace); + insert(DIKS_TAB , Qt::Key_Tab); + insert(DIKS_RETURN , Qt::Key_Return); + insert(DIKS_ESCAPE , Qt::Key_Escape); + insert(DIKS_DELETE , Qt::Key_Delete); + + insert(DIKS_CURSOR_LEFT , Qt::Key_Left); + insert(DIKS_CURSOR_RIGHT , Qt::Key_Right); + insert(DIKS_CURSOR_UP , Qt::Key_Up); + insert(DIKS_CURSOR_DOWN , Qt::Key_Down); + insert(DIKS_INSERT , Qt::Key_Insert); + insert(DIKS_HOME , Qt::Key_Home); + insert(DIKS_END , Qt::Key_End); + insert(DIKS_PAGE_UP , Qt::Key_PageUp); + insert(DIKS_PAGE_DOWN , Qt::Key_PageDown); + insert(DIKS_PRINT , Qt::Key_Print); + insert(DIKS_PAUSE , Qt::Key_Pause); + insert(DIKS_SELECT , Qt::Key_Select); + insert(DIKS_GOTO , Qt::Key_OpenUrl); + insert(DIKS_CLEAR , Qt::Key_Clear); + insert(DIKS_MENU , Qt::Key_Menu); + insert(DIKS_HELP , Qt::Key_Help); + + insert(DIKS_INTERNET , Qt::Key_HomePage); + insert(DIKS_MAIL , Qt::Key_LaunchMail); + insert(DIKS_FAVORITES , Qt::Key_Favorites); + + insert(DIKS_BACK , Qt::Key_Back); + insert(DIKS_FORWARD , Qt::Key_Forward); + insert(DIKS_VOLUME_UP , Qt::Key_VolumeUp); + insert(DIKS_VOLUME_DOWN , Qt::Key_VolumeDown); + insert(DIKS_MUTE , Qt::Key_VolumeMute); + insert(DIKS_PLAYPAUSE , Qt::Key_Pause); + insert(DIKS_PLAY , Qt::Key_MediaPlay); + insert(DIKS_STOP , Qt::Key_MediaStop); + insert(DIKS_RECORD , Qt::Key_MediaRecord); + insert(DIKS_PREVIOUS , Qt::Key_MediaPrevious); + insert(DIKS_NEXT , Qt::Key_MediaNext); + + insert(DIKS_F1 , Qt::Key_F1); + insert(DIKS_F2 , Qt::Key_F2); + insert(DIKS_F3 , Qt::Key_F3); + insert(DIKS_F4 , Qt::Key_F4); + insert(DIKS_F5 , Qt::Key_F5); + insert(DIKS_F6 , Qt::Key_F6); + insert(DIKS_F7 , Qt::Key_F7); + insert(DIKS_F8 , Qt::Key_F8); + insert(DIKS_F9 , Qt::Key_F9); + insert(DIKS_F10 , Qt::Key_F10); + insert(DIKS_F11 , Qt::Key_F11); + insert(DIKS_F12 , Qt::Key_F12); + + insert(DIKS_SHIFT , Qt::Key_Shift); + insert(DIKS_CONTROL , Qt::Key_Control); + insert(DIKS_ALT , Qt::Key_Alt); + insert(DIKS_ALTGR , Qt::Key_AltGr); + + insert(DIKS_META , Qt::Key_Meta); + insert(DIKS_SUPER , Qt::Key_Super_L); // ??? + insert(DIKS_HYPER , Qt::Key_Hyper_L); // ??? + + insert(DIKS_CAPS_LOCK , Qt::Key_CapsLock); + insert(DIKS_NUM_LOCK , Qt::Key_NumLock); + insert(DIKS_SCROLL_LOCK , Qt::Key_ScrollLock); + + insert(DIKS_DEAD_ABOVEDOT , Qt::Key_Dead_Abovedot); + insert(DIKS_DEAD_ABOVERING , Qt::Key_Dead_Abovering); + insert(DIKS_DEAD_ACUTE , Qt::Key_Dead_Acute); + insert(DIKS_DEAD_BREVE , Qt::Key_Dead_Breve); + insert(DIKS_DEAD_CARON , Qt::Key_Dead_Caron); + insert(DIKS_DEAD_CEDILLA , Qt::Key_Dead_Cedilla); + insert(DIKS_DEAD_CIRCUMFLEX , Qt::Key_Dead_Circumflex); + insert(DIKS_DEAD_DIAERESIS , Qt::Key_Dead_Diaeresis); + insert(DIKS_DEAD_DOUBLEACUTE , Qt::Key_Dead_Doubleacute); + insert(DIKS_DEAD_GRAVE , Qt::Key_Dead_Grave); + insert(DIKS_DEAD_IOTA , Qt::Key_Dead_Iota); + insert(DIKS_DEAD_MACRON , Qt::Key_Dead_Macron); + insert(DIKS_DEAD_OGONEK , Qt::Key_Dead_Ogonek); + insert(DIKS_DEAD_SEMIVOICED_SOUND , Qt::Key_Dead_Semivoiced_Sound); + insert(DIKS_DEAD_TILDE , Qt::Key_Dead_Tilde); + insert(DIKS_DEAD_VOICED_SOUND , Qt::Key_Dead_Voiced_Sound); + insert(DIKS_SPACE , Qt::Key_Space); + insert(DIKS_EXCLAMATION_MARK , Qt::Key_Exclam); + insert(DIKS_QUOTATION , Qt::Key_QuoteDbl); + insert(DIKS_NUMBER_SIGN , Qt::Key_NumberSign); + insert(DIKS_DOLLAR_SIGN , Qt::Key_Dollar); + insert(DIKS_PERCENT_SIGN , Qt::Key_Percent); + insert(DIKS_AMPERSAND , Qt::Key_Ampersand); + insert(DIKS_APOSTROPHE , Qt::Key_Apostrophe); + insert(DIKS_PARENTHESIS_LEFT , Qt::Key_ParenLeft); + insert(DIKS_PARENTHESIS_RIGHT , Qt::Key_ParenRight); + insert(DIKS_ASTERISK , Qt::Key_Asterisk); + insert(DIKS_PLUS_SIGN , Qt::Key_Plus); + insert(DIKS_COMMA , Qt::Key_Comma); + insert(DIKS_MINUS_SIGN , Qt::Key_Minus); + insert(DIKS_PERIOD , Qt::Key_Period); + insert(DIKS_SLASH , Qt::Key_Slash); + insert(DIKS_0 , Qt::Key_0); + insert(DIKS_1 , Qt::Key_1); + insert(DIKS_2 , Qt::Key_2); + insert(DIKS_3 , Qt::Key_3); + insert(DIKS_4 , Qt::Key_4); + insert(DIKS_5 , Qt::Key_5); + insert(DIKS_6 , Qt::Key_6); + insert(DIKS_7 , Qt::Key_7); + insert(DIKS_8 , Qt::Key_8); + insert(DIKS_9 , Qt::Key_9); + insert(DIKS_COLON , Qt::Key_Colon); + insert(DIKS_SEMICOLON , Qt::Key_Semicolon); + insert(DIKS_LESS_THAN_SIGN , Qt::Key_Less); + insert(DIKS_EQUALS_SIGN , Qt::Key_Equal); + insert(DIKS_GREATER_THAN_SIGN , Qt::Key_Greater); + insert(DIKS_QUESTION_MARK , Qt::Key_Question); + insert(DIKS_AT , Qt::Key_At); + insert(DIKS_CAPITAL_A , Qt::Key_A); + insert(DIKS_CAPITAL_B , Qt::Key_B); + insert(DIKS_CAPITAL_C , Qt::Key_C); + insert(DIKS_CAPITAL_D , Qt::Key_D); + insert(DIKS_CAPITAL_E , Qt::Key_E); + insert(DIKS_CAPITAL_F , Qt::Key_F); + insert(DIKS_CAPITAL_G , Qt::Key_G); + insert(DIKS_CAPITAL_H , Qt::Key_H); + insert(DIKS_CAPITAL_I , Qt::Key_I); + insert(DIKS_CAPITAL_J , Qt::Key_J); + insert(DIKS_CAPITAL_K , Qt::Key_K); + insert(DIKS_CAPITAL_L , Qt::Key_L); + insert(DIKS_CAPITAL_M , Qt::Key_M); + insert(DIKS_CAPITAL_N , Qt::Key_N); + insert(DIKS_CAPITAL_O , Qt::Key_O); + insert(DIKS_CAPITAL_P , Qt::Key_P); + insert(DIKS_CAPITAL_Q , Qt::Key_Q); + insert(DIKS_CAPITAL_R , Qt::Key_R); + insert(DIKS_CAPITAL_S , Qt::Key_S); + insert(DIKS_CAPITAL_T , Qt::Key_T); + insert(DIKS_CAPITAL_U , Qt::Key_U); + insert(DIKS_CAPITAL_V , Qt::Key_V); + insert(DIKS_CAPITAL_W , Qt::Key_W); + insert(DIKS_CAPITAL_X , Qt::Key_X); + insert(DIKS_CAPITAL_Y , Qt::Key_Y); + insert(DIKS_CAPITAL_Z , Qt::Key_Z); + insert(DIKS_SQUARE_BRACKET_LEFT , Qt::Key_BracketLeft); + insert(DIKS_BACKSLASH , Qt::Key_Backslash); + insert(DIKS_SQUARE_BRACKET_RIGHT , Qt::Key_BracketRight); + insert(DIKS_CIRCUMFLEX_ACCENT , Qt::Key_AsciiCircum); + insert(DIKS_UNDERSCORE , Qt::Key_Underscore); + insert(DIKS_SMALL_A , Qt::Key_A); + insert(DIKS_SMALL_B , Qt::Key_B); + insert(DIKS_SMALL_C , Qt::Key_C); + insert(DIKS_SMALL_D , Qt::Key_D); + insert(DIKS_SMALL_E , Qt::Key_E); + insert(DIKS_SMALL_F , Qt::Key_F); + insert(DIKS_SMALL_G , Qt::Key_G); + insert(DIKS_SMALL_H , Qt::Key_H); + insert(DIKS_SMALL_I , Qt::Key_I); + insert(DIKS_SMALL_J , Qt::Key_J); + insert(DIKS_SMALL_K , Qt::Key_K); + insert(DIKS_SMALL_L , Qt::Key_L); + insert(DIKS_SMALL_M , Qt::Key_M); + insert(DIKS_SMALL_N , Qt::Key_N); + insert(DIKS_SMALL_O , Qt::Key_O); + insert(DIKS_SMALL_P , Qt::Key_P); + insert(DIKS_SMALL_Q , Qt::Key_Q); + insert(DIKS_SMALL_R , Qt::Key_R); + insert(DIKS_SMALL_S , Qt::Key_S); + insert(DIKS_SMALL_T , Qt::Key_T); + insert(DIKS_SMALL_U , Qt::Key_U); + insert(DIKS_SMALL_V , Qt::Key_V); + insert(DIKS_SMALL_W , Qt::Key_W); + insert(DIKS_SMALL_X , Qt::Key_X); + insert(DIKS_SMALL_Y , Qt::Key_Y); + insert(DIKS_SMALL_Z , Qt::Key_Z); + insert(DIKS_CURLY_BRACKET_LEFT , Qt::Key_BraceLeft); + insert(DIKS_VERTICAL_BAR , Qt::Key_Bar); + insert(DIKS_CURLY_BRACKET_RIGHT , Qt::Key_BraceRight); + insert(DIKS_TILDE , Qt::Key_AsciiTilde); +} + +QT_END_NAMESPACE +#include "qdirectfbkeyboard.moc" +#endif // QT_NO_QWS_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h new file mode 100644 index 0000000000..49c1c18b22 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QDIRECTFBKEYBOARD_H +#define QDIRECTFBKEYBOARD_H + +#include +#include + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDirectFBKeyboardHandlerPrivate; + +class QDirectFBKeyboardHandler : public QWSKeyboardHandler +{ +public: + QDirectFBKeyboardHandler(const QString &device); + ~QDirectFBKeyboardHandler(); + +private: + QDirectFBKeyboardHandlerPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_DIRECTFB + +QT_END_HEADER + +#endif // QDIRECTFBKEYBOARD_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbmouse.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.cpp new file mode 100644 index 0000000000..3999b85afb --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbmouse.h" + +#ifndef QT_NO_QWS_DIRECTFB + +#include "qdirectfbscreen.h" +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDirectFBMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QDirectFBMouseHandlerPrivate(QDirectFBMouseHandler *h); + ~QDirectFBMouseHandlerPrivate(); + + void setEnabled(bool on); +private: + QDirectFBMouseHandler *handler; + IDirectFBEventBuffer *eventBuffer; +#ifndef QT_NO_DIRECTFB_LAYER + IDirectFBDisplayLayer *layer; +#endif + QSocketNotifier *mouseNotifier; + + QPoint prevPoint; + Qt::MouseButtons prevbuttons; + + DFBEvent event; + uint bytesRead; + +private Q_SLOTS: + void readMouseData(); +}; + +QDirectFBMouseHandlerPrivate::QDirectFBMouseHandlerPrivate(QDirectFBMouseHandler *h) + : handler(h), eventBuffer(0) +{ + DFBResult result; + + QScreen *screen = QScreen::instance(); + if (!screen) { + qCritical("QDirectFBMouseHandler: no screen instance found"); + return; + } + + IDirectFB *fb = QDirectFBScreen::instance()->dfb(); + if (!fb) { + qCritical("QDirectFBMouseHandler: DirectFB not initialized"); + return; + } + +#ifndef QT_NO_DIRECTFB_LAYER + layer = QDirectFBScreen::instance()->dfbDisplayLayer(); + if (!layer) { + qCritical("QDirectFBMouseHandler: Unable to get primary display layer"); + return; + } +#endif + + DFBInputDeviceCapabilities caps; + caps = DICAPS_BUTTONS | DICAPS_AXES; + result = fb->CreateInputEventBuffer(fb, caps, DFB_TRUE, &eventBuffer); + if (result != DFB_OK) { + DirectFBError("QDirectFBMouseHandler: " + "Unable to create input event buffer", result); + return; + } + + int fd; + result = eventBuffer->CreateFileDescriptor(eventBuffer, &fd); + if (result != DFB_OK) { + DirectFBError("QDirectFBMouseHandler: " + "Unable to create file descriptor", result); + return; + } + + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + // DirectFB seems to assume that the mouse always starts centered + prevPoint = QPoint(screen->deviceWidth() / 2, screen->deviceHeight() / 2); + prevbuttons = Qt::NoButton; + memset(&event, 0, sizeof(event)); + bytesRead = 0; + + mouseNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); + setEnabled(true); +} + +QDirectFBMouseHandlerPrivate::~QDirectFBMouseHandlerPrivate() +{ + if (eventBuffer) + eventBuffer->Release(eventBuffer); +} + +void QDirectFBMouseHandlerPrivate::setEnabled(bool on) +{ + if (mouseNotifier->isEnabled() != on) { +#ifndef QT_NO_DIRECTFB_LAYER + DFBResult result; + result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::QDirectFBScreenCursor: " + "Unable to set cooperative level", result); + } + result = layer->EnableCursor(layer, on ? 1 : 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::QDirectFBScreenCursor: " + "Unable to enable cursor", result); + } + + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cooperative level", result); + } + + layer->SetCooperativeLevel(layer, DLSCL_SHARED); +#endif + mouseNotifier->setEnabled(on); + } +} + +void QDirectFBMouseHandlerPrivate::readMouseData() +{ + if (!QScreen::instance()) + return; + + for (;;) { + // GetEvent returns DFB_UNSUPPORTED after CreateFileDescriptor(). + // This seems stupid and I really hope it's a bug which will be fixed. + + // DFBResult ret = eventBuffer->GetEvent(eventBuffer, &event); + + char *buf = reinterpret_cast(&event); + int ret = ::read(mouseNotifier->socket(), + buf + bytesRead, sizeof(DFBEvent) - bytesRead); + if (ret == -1) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + return; + qWarning("QDirectFBMouseHandlerPrivate::readMouseData(): %s", + strerror(errno)); + return; + } + + Q_ASSERT(ret >= 0); + bytesRead += ret; + if (bytesRead < sizeof(DFBEvent)) + break; + bytesRead = 0; + + Q_ASSERT(event.clazz == DFEC_INPUT); + + const DFBInputEvent input = event.input; + int x = prevPoint.x(); + int y = prevPoint.y(); + int wheel = 0; + + if (input.type == DIET_AXISMOTION) { +#if defined(QT_NO_DIRECTFB_LAYER) || defined(QT_DIRECTFB_WINDOW_AS_CURSOR) + if (input.flags & DIEF_AXISABS) { + switch (input.axis) { + case DIAI_X: x = input.axisabs; break; + case DIAI_Y: y = input.axisabs; break; + default: + qWarning("QDirectFBMouseHandlerPrivate::readMouseData: " + "unknown axis (absolute) %d", input.axis); + break; + } + } else if (input.flags & DIEF_AXISREL) { + switch (input.axis) { + case DIAI_X: x += input.axisrel; break; + case DIAI_Y: y += input.axisrel; break; + case DIAI_Z: wheel = -120 * input.axisrel; break; + default: + qWarning("QDirectFBMouseHandlerPrivate::readMouseData: " + "unknown axis (releative) %d", input.axis); + } + } +#else + if (input.axis == DIAI_X || input.axis == DIAI_Y) { + DFBResult result = layer->GetCursorPosition(layer, &x, &y); + if (result != DFB_OK) { + DirectFBError("QDirectFBMouseHandler::readMouseData", + result); + } + } else if (input.axis == DIAI_Z) { + Q_ASSERT(input.flags & DIEF_AXISREL); + wheel = input.axisrel; + wheel *= -120; + } +#endif + } + + Qt::MouseButtons buttons = Qt::NoButton; + if (input.flags & DIEF_BUTTONS) { + if (input.buttons & DIBM_LEFT) + buttons |= Qt::LeftButton; + if (input.buttons & DIBM_MIDDLE) + buttons |= Qt::MidButton; + if (input.buttons & DIBM_RIGHT) + buttons |= Qt::RightButton; + } + + QPoint p = QPoint(x, y); + handler->limitToScreen(p); + + if (p == prevPoint && wheel == 0 && buttons == prevbuttons) + continue; + + prevPoint = p; + prevbuttons = buttons; + + handler->mouseChanged(p, buttons, wheel); + } +} + +QDirectFBMouseHandler::QDirectFBMouseHandler(const QString &driver, + const QString &device) + : QWSMouseHandler(driver, device) +{ + d = new QDirectFBMouseHandlerPrivate(this); +} + +QDirectFBMouseHandler::~QDirectFBMouseHandler() +{ + delete d; +} + +void QDirectFBMouseHandler::suspend() +{ + d->setEnabled(false); +} + +void QDirectFBMouseHandler::resume() +{ + d->setEnabled(true); +} + +QT_END_NAMESPACE +#include "qdirectfbmouse.moc" +#endif // QT_NO_QWS_DIRECTFB + + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbmouse.h b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.h new file mode 100644 index 0000000000..ac0fcadfaa --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QDIRECTFBMOUSE_H +#define QDIRECTFBMOUSE_H + +#include +#include + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDirectFBMouseHandlerPrivate; + +class QDirectFBMouseHandler : public QWSMouseHandler +{ +public: + explicit QDirectFBMouseHandler(const QString &driver = QString(), + const QString &device = QString()); + ~QDirectFBMouseHandler(); + + void suspend(); + void resume(); +protected: + QDirectFBMouseHandlerPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QT_NO_QWS_DIRECTFB +#endif // QDIRECTFBMOUSE_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp new file mode 100644 index 0000000000..10f1bb3f46 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbscreen.h" +#include "qdirectfbpaintdevice.h" +#include "qdirectfbpaintengine.h" + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_NAMESPACE + +QDirectFBPaintDevice::QDirectFBPaintDevice(QDirectFBScreen *scr) + : QCustomRasterPaintDevice(0), dfbSurface(0), screen(scr), + bpl(-1), lockFlgs(DFBSurfaceLockFlags(0)), mem(0), engine(0), imageFormat(QImage::Format_Invalid) +{ +#ifdef QT_DIRECTFB_SUBSURFACE + subSurface = 0; + syncPending = false; +#endif +} + +QDirectFBPaintDevice::~QDirectFBPaintDevice() +{ + if (QDirectFBScreen::instance()) { + unlockSurface(); +#ifdef QT_DIRECTFB_SUBSURFACE + releaseSubSurface(); +#endif + if (dfbSurface) { + screen->releaseDFBSurface(dfbSurface); + } + } + delete engine; +} + +IDirectFBSurface *QDirectFBPaintDevice::directFBSurface() const +{ + return dfbSurface; +} + +bool QDirectFBPaintDevice::lockSurface(DFBSurfaceLockFlags lockFlags) +{ + if (lockFlgs && (lockFlags & ~lockFlgs)) + unlockSurface(); + if (!mem) { + Q_ASSERT(dfbSurface); +#ifdef QT_DIRECTFB_SUBSURFACE + if (!subSurface) { + DFBResult result; + subSurface = screen->getSubSurface(dfbSurface, QRect(), QDirectFBScreen::TrackSurface, &result); + if (result != DFB_OK || !subSurface) { + DirectFBError("Couldn't create sub surface", result); + return false; + } + } + IDirectFBSurface *surface = subSurface; +#else + IDirectFBSurface *surface = dfbSurface; +#endif + Q_ASSERT(surface); + mem = QDirectFBScreen::lockSurface(surface, lockFlags, &bpl); + lockFlgs = lockFlags; + Q_ASSERT(mem); + Q_ASSERT(bpl > 0); + const QSize s = size(); + lockedImage = QImage(mem, s.width(), s.height(), bpl, + QDirectFBScreen::getImageFormat(dfbSurface)); + return true; + } +#ifdef QT_DIRECTFB_SUBSURFACE + if (syncPending) { + syncPending = false; + screen->waitIdle(); + } +#endif + return false; +} + +void QDirectFBPaintDevice::unlockSurface() +{ + if (QDirectFBScreen::instance() && lockFlgs) { +#ifdef QT_DIRECTFB_SUBSURFACE + IDirectFBSurface *surface = subSurface; +#else + IDirectFBSurface *surface = dfbSurface; +#endif + if (surface) { + surface->Unlock(surface); + lockFlgs = static_cast(0); + mem = 0; + } + } +} + +void *QDirectFBPaintDevice::memory() const +{ + return mem; +} + +QImage::Format QDirectFBPaintDevice::format() const +{ + return imageFormat; +} + +int QDirectFBPaintDevice::bytesPerLine() const +{ + Q_ASSERT(!mem || bpl != -1); + return bpl; +} + +QSize QDirectFBPaintDevice::size() const +{ + int w, h; + dfbSurface->GetSize(dfbSurface, &w, &h); + return QSize(w, h); +} + +int QDirectFBPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + if (!dfbSurface) + return 0; + + switch (metric) { + case QPaintDevice::PdmWidth: + case QPaintDevice::PdmHeight: + return (metric == PdmWidth ? size().width() : size().height()); + case QPaintDevice::PdmWidthMM: + return (size().width() * 1000) / dotsPerMeterX(); + case QPaintDevice::PdmHeightMM: + return (size().height() * 1000) / dotsPerMeterY(); + case QPaintDevice::PdmPhysicalDpiX: + case QPaintDevice::PdmDpiX: + return (dotsPerMeterX() * 254) / 10000; // 0.0254 meters-per-inch + case QPaintDevice::PdmPhysicalDpiY: + case QPaintDevice::PdmDpiY: + return (dotsPerMeterY() * 254) / 10000; // 0.0254 meters-per-inch + case QPaintDevice::PdmDepth: + return QDirectFBScreen::depth(imageFormat); + case QPaintDevice::PdmNumColors: { + if (!lockedImage.isNull()) + return lockedImage.colorCount(); + + DFBResult result; + IDirectFBPalette *palette = 0; + unsigned int numColors = 0; + + result = dfbSurface->GetPalette(dfbSurface, &palette); + if ((result != DFB_OK) || !palette) + return 0; + + result = palette->GetSize(palette, &numColors); + palette->Release(palette); + if (result != DFB_OK) + return 0; + + return numColors; + } + default: + qCritical("QDirectFBPaintDevice::metric(): Unhandled metric!"); + return 0; + } +} + +QPaintEngine *QDirectFBPaintDevice::paintEngine() const +{ + return engine; +} + +#ifdef QT_DIRECTFB_SUBSURFACE +void QDirectFBPaintDevice::releaseSubSurface() +{ + Q_ASSERT(QDirectFBScreen::instance()); + if (subSurface) { + unlockSurface(); + screen->releaseDFBSurface(subSurface); + subSurface = 0; + } +} +#endif + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h new file mode 100644 index 0000000000..71a7a8e104 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QDIRECTFBPAINTDEVICE_H +#define QDIRECTFBPAINTDEVICE_H + +#include +#include "qdirectfbscreen.h" + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +// Inherited by both window surface and pixmap +class QDirectFBPaintEngine; +class QDirectFBPaintDevice : public QCustomRasterPaintDevice +{ +public: + ~QDirectFBPaintDevice(); + + virtual IDirectFBSurface *directFBSurface() const; + + bool lockSurface(DFBSurfaceLockFlags lockFlags); + void unlockSurface(); + + // Reimplemented from QCustomRasterPaintDevice: + void *memory() const; + QImage::Format format() const; + int bytesPerLine() const; + QSize size() const; + int metric(QPaintDevice::PaintDeviceMetric metric) const; + DFBSurfaceLockFlags lockFlags() const { return lockFlgs; } + QPaintEngine *paintEngine() const; +protected: + QDirectFBPaintDevice(QDirectFBScreen *scr); + inline int dotsPerMeterX() const + { + return (screen->deviceWidth() * 1000) / screen->physicalWidth(); + } + inline int dotsPerMeterY() const + { + return (screen->deviceHeight() * 1000) / screen->physicalHeight(); + } + + IDirectFBSurface *dfbSurface; +#ifdef QT_DIRECTFB_SUBSURFACE + void releaseSubSurface(); + IDirectFBSurface *subSurface; + friend class QDirectFBPaintEnginePrivate; + bool syncPending; +#endif + QImage lockedImage; + QDirectFBScreen *screen; + int bpl; + DFBSurfaceLockFlags lockFlgs; + uchar *mem; + QDirectFBPaintEngine *engine; + QImage::Format imageFormat; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_DIRECTFB +#endif //QDIRECTFBPAINTDEVICE_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp new file mode 100644 index 0000000000..6d6fb02a68 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp @@ -0,0 +1,1430 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbpaintengine.h" + +#ifndef QT_NO_QWS_DIRECTFB + +#include "qdirectfbwindowsurface.h" +#include "qdirectfbscreen.h" +#include "qdirectfbpixmap.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +class SurfaceCache; +class QDirectFBPaintEnginePrivate : public QRasterPaintEnginePrivate +{ +public: + enum TransformationTypeFlags { + Matrix_NegativeScale = 0x100, + Matrix_RectsUnsupported = (QTransform::TxRotate|QTransform::TxShear|QTransform::TxProject), + Matrix_BlitsUnsupported = (Matrix_NegativeScale|Matrix_RectsUnsupported) + }; + + inline static uint getTransformationType(const QTransform &transform) + { + int ret = transform.type(); + if (qMin(transform.m11(), transform.m22()) < 0) { + ret |= QDirectFBPaintEnginePrivate::Matrix_NegativeScale; + } + return ret; + } + + enum CompositionModeStatus { + PorterDuff_None = 0x0, + PorterDuff_Supported = 0x1, + PorterDuff_PremultiplyColors = 0x2, + PorterDuff_AlwaysBlend = 0x4 + }; + + enum ClipType { + ClipUnset, + NoClip, + RectClip, + RegionClip, + ComplexClip + }; + + QDirectFBPaintEnginePrivate(QDirectFBPaintEngine *p); + ~QDirectFBPaintEnginePrivate(); + + inline void setTransform(const QTransform &transforma); + inline void setPen(const QPen &pen); + inline void setCompositionMode(QPainter::CompositionMode mode); + inline void setRenderHints(QPainter::RenderHints hints); + + inline void setDFBColor(const QColor &color); + + inline void lock(); + inline void unlock(); + static inline void unlock(QDirectFBPaintDevice *device); + + inline bool isSimpleBrush(const QBrush &brush) const; + + void drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap, const QPointF &pos, const QTransform &pixmapTransform); + void blit(const QRectF &dest, IDirectFBSurface *surface, const QRectF &src); + + inline bool supportsStretchBlit() const; + + inline void updateClip(); + virtual void systemStateChanged(); + + static IDirectFBSurface *getSurface(const QImage &img, bool *release); + +#ifdef QT_DIRECTFB_IMAGECACHE + static inline int cacheCost(const QImage &img) { return img.width() * img.height() * img.depth() / 8; } +#endif + + enum BlitFlag { + HasAlpha = 0x1, + Premultiplied = 0x2 + }; + void prepareForBlit(uint blitFlags); + + IDirectFBSurface *surface; + + bool antialiased; + bool simplePen; + + uint transformationType; // this is QTransform::type() + Matrix_NegativeScale if qMin(transform.m11(), transform.m22()) < 0 + + SurfaceCache *surfaceCache; + IDirectFB *fb; + quint8 opacity; + + ClipType clipType; + QDirectFBPaintDevice *dfbDevice; + uint compositionModeStatus; + bool isPremultiplied; + + bool inClip; + QRect currentClip; + + QDirectFBPaintEngine *q; +}; + +class SurfaceCache +{ +public: + SurfaceCache() : surface(0), buffer(0), bufsize(0) {} + ~SurfaceCache() { clear(); } + IDirectFBSurface *getSurface(const uint *buf, int size); + void clear(); +private: + IDirectFBSurface *surface; + uint *buffer; + int bufsize; +}; + + +#ifdef QT_DIRECTFB_IMAGECACHE +QT_BEGIN_INCLUDE_NAMESPACE +#include +QT_END_INCLUDE_NAMESPACE +struct CachedImage +{ + IDirectFBSurface *surface; + ~CachedImage() + { + if (surface && QDirectFBScreen::instance()) { + QDirectFBScreen::instance()->releaseDFBSurface(surface); + } + } +}; +static QCache imageCache(4*1024*1024); // 4 MB +#endif + +#define VOID_ARG() static_cast(false) +enum PaintOperation { + DRAW_RECTS = 0x0001, DRAW_LINES = 0x0002, DRAW_IMAGE = 0x0004, + DRAW_PIXMAP = 0x0008, DRAW_TILED_PIXMAP = 0x0010, STROKE_PATH = 0x0020, + DRAW_PATH = 0x0040, DRAW_POINTS = 0x0080, DRAW_ELLIPSE = 0x0100, + DRAW_POLYGON = 0x0200, DRAW_TEXT = 0x0400, FILL_PATH = 0x0800, + FILL_RECT = 0x1000, DRAW_COLORSPANS = 0x2000, DRAW_ROUNDED_RECT = 0x4000, + DRAW_STATICTEXT = 0x8000, ALL = 0xffff +}; + +enum { RasterWarn = 1, RasterDisable = 2 }; +static inline uint rasterFallbacksMask(PaintOperation op) +{ + uint ret = 0; +#ifdef QT_DIRECTFB_WARN_ON_RASTERFALLBACKS + if (op & QT_DIRECTFB_WARN_ON_RASTERFALLBACKS) + ret |= RasterWarn; +#endif +#ifdef QT_DIRECTFB_DISABLE_RASTERFALLBACKS + if (op & QT_DIRECTFB_DISABLE_RASTERFALLBACKS) + ret |= RasterDisable; +#endif + static int warningMask = -1; + static int disableMask = -1; + if (warningMask < 0) { + struct { + const char *name; + PaintOperation operation; + } const operations[] = { + { "DRAW_RECTS", DRAW_RECTS }, + { "DRAW_LINES", DRAW_LINES }, + { "DRAW_IMAGE", DRAW_IMAGE }, + { "DRAW_PIXMAP", DRAW_PIXMAP }, + { "DRAW_TILED_PIXMAP", DRAW_TILED_PIXMAP }, + { "STROKE_PATH", STROKE_PATH }, + { "DRAW_PATH", DRAW_PATH }, + { "DRAW_POINTS", DRAW_POINTS }, + { "DRAW_ELLIPSE", DRAW_ELLIPSE }, + { "DRAW_POLYGON", DRAW_POLYGON }, + { "DRAW_TEXT", DRAW_TEXT }, + { "FILL_PATH", FILL_PATH }, + { "FILL_RECT", FILL_RECT }, + { "DRAW_COLORSPANS", DRAW_COLORSPANS }, + { "DRAW_ROUNDED_RECT", DRAW_ROUNDED_RECT }, + { "ALL", ALL }, + { 0, ALL } + }; + + QStringList warning = QString::fromLatin1(qgetenv("QT_DIRECTFB_WARN_ON_RASTERFALLBACKS")).toUpper().split(QLatin1Char('|'), + QString::SkipEmptyParts); + QStringList disable = QString::fromLatin1(qgetenv("QT_DIRECTFB_DISABLE_RASTERFALLBACKS")).toUpper().split(QLatin1Char('|'), + QString::SkipEmptyParts); + warningMask = 0; + disableMask = 0; + if (!warning.isEmpty() || !disable.isEmpty()) { + for (int i=0; operations[i].name; ++i) { + const QString name = QString::fromLatin1(operations[i].name); + int idx = warning.indexOf(name); + if (idx != -1) { + warningMask |= operations[i].operation; + warning.erase(warning.begin() + idx); + } + idx = disable.indexOf(name); + if (idx != -1) { + disableMask |= operations[i].operation; + disable.erase(disable.begin() + idx); + } + } + } + if (!warning.isEmpty()) { + qWarning("QDirectFBPaintEngine QT_DIRECTFB_WARN_ON_RASTERFALLBACKS Unknown operation(s): %s", + qPrintable(warning.join(QLatin1String("|")))); + } + if (!disable.isEmpty()) { + qWarning("QDirectFBPaintEngine QT_DIRECTFB_DISABLE_RASTERFALLBACKS Unknown operation(s): %s", + qPrintable(disable.join(QLatin1String("|")))); + } + } + if (op & warningMask) + ret |= RasterWarn; + if (op & disableMask) + ret |= RasterDisable; + return ret; +} + +template +static void rasterFallbackWarn(const char *msg, const char *func, const device *dev, + uint transformationType, bool simplePen, + uint clipType, uint compositionModeStatus, + const char *nameOne, const T1 &one, + const char *nameTwo, const T2 &two, + const char *nameThree, const T3 &three); + +#define RASTERFALLBACK(op, one, two, three) \ + { \ + static const uint rasterFallbacks = rasterFallbacksMask(op); \ + switch (rasterFallbacks) { \ + case 0: break; \ + case RasterWarn: \ + rasterFallbackWarn("Falling back to raster engine for", \ + __FUNCTION__, \ + state()->painter->device(), \ + d_func()->transformationType, \ + d_func()->simplePen, \ + d_func()->clipType, \ + d_func()->compositionModeStatus, \ + #one, one, #two, two, #three, three); \ + break; \ + case RasterDisable|RasterWarn: \ + rasterFallbackWarn("Disabled raster engine operation", \ + __FUNCTION__, \ + state()->painter->device(), \ + d_func()->transformationType, \ + d_func()->simplePen, \ + d_func()->clipType, \ + d_func()->compositionModeStatus, \ + #one, one, #two, two, #three, three); \ + case RasterDisable: \ + return; \ + } \ + } + +template +static inline void drawLines(const T *lines, int n, const QTransform &transform, IDirectFBSurface *surface); +template +static inline void fillRects(const T *rects, int n, const QTransform &transform, IDirectFBSurface *surface); +template +static inline void drawRects(const T *rects, int n, const QTransform &transform, IDirectFBSurface *surface); + +#define CLIPPED_PAINT(operation) { \ + d->unlock(); \ + DFBRegion clipRegion; \ + switch (d->clipType) { \ + case QDirectFBPaintEnginePrivate::NoClip: \ + case QDirectFBPaintEnginePrivate::RectClip: \ + operation; \ + break; \ + case QDirectFBPaintEnginePrivate::RegionClip: { \ + Q_ASSERT(d->clip()); \ + const QVector cr = d->clip()->clipRegion.rects(); \ + const int size = cr.size(); \ + for (int i=0; icurrentClip = cr.at(i); \ + clipRegion.x1 = d->currentClip.x(); \ + clipRegion.y1 = d->currentClip.y(); \ + clipRegion.x2 = d->currentClip.right(); \ + clipRegion.y2 = d->currentClip.bottom(); \ + d->surface->SetClip(d->surface, &clipRegion); \ + operation; \ + } \ + d->updateClip(); \ + break; } \ + case QDirectFBPaintEnginePrivate::ComplexClip: \ + case QDirectFBPaintEnginePrivate::ClipUnset: \ + qFatal("CLIPPED_PAINT internal error %d", d->clipType); \ + break; \ + } \ + } + + +QDirectFBPaintEngine::QDirectFBPaintEngine(QPaintDevice *device) + : QRasterPaintEngine(*(new QDirectFBPaintEnginePrivate(this)), device) +{ +} + +QDirectFBPaintEngine::~QDirectFBPaintEngine() +{ +} + +bool QDirectFBPaintEngine::begin(QPaintDevice *device) +{ + Q_D(QDirectFBPaintEngine); + if (device->devType() == QInternal::CustomRaster) { + d->dfbDevice = static_cast(device); + } else if (device->devType() == QInternal::Pixmap) { + QPixmapData *data = static_cast(device)->pixmapData(); + Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); + QDirectFBPixmapData *dfbPixmapData = static_cast(data); + QDirectFBPaintEnginePrivate::unlock(dfbPixmapData); + d->dfbDevice = static_cast(dfbPixmapData); + } + + if (d->dfbDevice) + d->surface = d->dfbDevice->directFBSurface(); + + if (!d->surface) { + qFatal("QDirectFBPaintEngine used on an invalid device: 0x%x", + device->devType()); + } + d->isPremultiplied = QDirectFBScreen::isPremultiplied(d->dfbDevice->format()); + + d->prepare(d->dfbDevice); + gccaps = AllFeatures; + d->setCompositionMode(state()->composition_mode); + + return QRasterPaintEngine::begin(device); +} + +bool QDirectFBPaintEngine::end() +{ + Q_D(QDirectFBPaintEngine); + d->unlock(); + d->dfbDevice = 0; +#if (Q_DIRECTFB_VERSION >= 0x010000) + d->surface->ReleaseSource(d->surface); +#endif + d->currentClip = QRect(); + d->surface->SetClip(d->surface, NULL); + d->surface = 0; + return QRasterPaintEngine::end(); +} + +void QDirectFBPaintEngine::clipEnabledChanged() +{ + Q_D(QDirectFBPaintEngine); + QRasterPaintEngine::clipEnabledChanged(); + d->updateClip(); +} + +void QDirectFBPaintEngine::penChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setPen(state()->pen); + + QRasterPaintEngine::penChanged(); +} + +void QDirectFBPaintEngine::opacityChanged() +{ + Q_D(QDirectFBPaintEngine); + d->opacity = quint8(state()->opacity * 255); + QRasterPaintEngine::opacityChanged(); +} + +void QDirectFBPaintEngine::compositionModeChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setCompositionMode(state()->compositionMode()); + QRasterPaintEngine::compositionModeChanged(); +} + +void QDirectFBPaintEngine::renderHintsChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setRenderHints(state()->renderHints); + QRasterPaintEngine::renderHintsChanged(); +} + +void QDirectFBPaintEngine::transformChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setTransform(state()->matrix); + QRasterPaintEngine::transformChanged(); +} + +void QDirectFBPaintEngine::setState(QPainterState *state) +{ + Q_D(QDirectFBPaintEngine); + QRasterPaintEngine::setState(state); + d->setPen(state->pen); + d->opacity = quint8(state->opacity * 255); + d->setCompositionMode(state->compositionMode()); + d->setTransform(state->transform()); + d->setRenderHints(state->renderHints); + if (d->surface) + d->updateClip(); +} + +void QDirectFBPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ + Q_D(QDirectFBPaintEngine); + const bool wasInClip = d->inClip; + d->inClip = true; + QRasterPaintEngine::clip(path, op); + if (!wasInClip) { + d->inClip = false; + d->updateClip(); + } +} + +void QDirectFBPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) +{ + Q_D(QDirectFBPaintEngine); + const bool wasInClip = d->inClip; + d->inClip = true; + QRasterPaintEngine::clip(region, op); + if (!wasInClip) { + d->inClip = false; + d->updateClip(); + } +} + +void QDirectFBPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) +{ + Q_D(QDirectFBPaintEngine); + const bool wasInClip = d->inClip; + d->inClip = true; + QRasterPaintEngine::clip(rect, op); + if (!wasInClip) { + d->inClip = false; + d->updateClip(); + } +} + +void QDirectFBPaintEngine::drawRects(const QRect *rects, int rectCount) +{ + Q_D(QDirectFBPaintEngine); + const QPen &pen = state()->pen; + const QBrush &brush = state()->brush; + if (brush.style() == Qt::NoBrush && pen.style() == Qt::NoPen) + return; + + if ((d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported) + || !d->simplePen + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip + || !d->isSimpleBrush(brush) + || !(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported)) { + RASTERFALLBACK(DRAW_RECTS, rectCount, VOID_ARG(), VOID_ARG()); + d->lock(); + QRasterPaintEngine::drawRects(rects, rectCount); + return; + } + + if (brush.style() != Qt::NoBrush) { + d->setDFBColor(brush.color()); + CLIPPED_PAINT(QT_PREPEND_NAMESPACE(fillRects)(rects, rectCount, state()->matrix, d->surface)); + } + + if (pen.style() != Qt::NoPen) { + d->setDFBColor(pen.color()); + CLIPPED_PAINT(QT_PREPEND_NAMESPACE(drawRects)(rects, rectCount, state()->matrix, d->surface)); + } +} + +void QDirectFBPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + Q_D(QDirectFBPaintEngine); + const QPen &pen = state()->pen; + const QBrush &brush = state()->brush; + if (brush.style() == Qt::NoBrush && pen.style() == Qt::NoPen) + return; + + if ((d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported) + || !d->simplePen + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip + || !d->isSimpleBrush(brush) + || !(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported)) { + RASTERFALLBACK(DRAW_RECTS, rectCount, VOID_ARG(), VOID_ARG()); + d->lock(); + QRasterPaintEngine::drawRects(rects, rectCount); + return; + } + + if (brush.style() != Qt::NoBrush) { + d->setDFBColor(brush.color()); + CLIPPED_PAINT(fillRects(rects, rectCount, state()->matrix, d->surface)); + } + + if (pen.style() != Qt::NoPen) { + d->setDFBColor(pen.color()); + CLIPPED_PAINT(QT_PREPEND_NAMESPACE(drawRects)(rects, rectCount, state()->matrix, d->surface)); + } +} + +void QDirectFBPaintEngine::drawLines(const QLine *lines, int lineCount) +{ + Q_D(QDirectFBPaintEngine); + + const QPen &pen = state()->pen; + if (!d->simplePen + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip + || !(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported)) { + RASTERFALLBACK(DRAW_LINES, lineCount, VOID_ARG(), VOID_ARG()); + d->lock(); + QRasterPaintEngine::drawLines(lines, lineCount); + return; + } + + if (pen.style() != Qt::NoPen) { + d->setDFBColor(pen.color()); + CLIPPED_PAINT(QT_PREPEND_NAMESPACE(drawLines)(lines, lineCount, state()->matrix, d->surface)); + } +} + +void QDirectFBPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ + Q_D(QDirectFBPaintEngine); + + const QPen &pen = state()->pen; + if (!d->simplePen + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip + || !(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported)) { + RASTERFALLBACK(DRAW_LINES, lineCount, VOID_ARG(), VOID_ARG()); + d->lock(); + QRasterPaintEngine::drawLines(lines, lineCount); + return; + } + + if (pen.style() != Qt::NoPen) { + d->setDFBColor(pen.color()); + CLIPPED_PAINT(QT_PREPEND_NAMESPACE(drawLines)(lines, lineCount, state()->matrix, d->surface)); + } +} + +void QDirectFBPaintEngine::drawImage(const QRectF &r, const QImage &image, + const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + Q_D(QDirectFBPaintEngine); + Q_UNUSED(flags); + + /* This is hard to read. The way it works is like this: + + - If you do not have support for preallocated surfaces and do not use an + image cache we always fall back to raster engine. + + - If it's rotated/sheared/mirrored (negative scale) or we can't + clip it we fall back to raster engine. + + - If we don't cache the image, but we do have support for + preallocated surfaces we fall back to the raster engine if the + image is in a format DirectFB can't handle. + + - If we do cache the image but don't have support for preallocated + images and the cost of caching the image (bytes used) is higher + than the max image cache size we fall back to raster engine. + */ + +#if !defined QT_NO_DIRECTFB_PREALLOCATED || defined QT_DIRECTFB_IMAGECACHE + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || (d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) + || (!d->supportsStretchBlit() && state()->matrix.mapRect(r).size() != sr.size()) +#ifndef QT_DIRECTFB_IMAGECACHE + || (QDirectFBScreen::getSurfacePixelFormat(image.format()) == DSPF_UNKNOWN) +#elif defined QT_NO_DIRECTFB_PREALLOCATED + || (QDirectFBPaintEnginePrivate::cacheCost(image) > imageCache.maxCost()) +#endif + ) +#endif + { + RASTERFALLBACK(DRAW_IMAGE, r, image.size(), sr); + d->lock(); + QRasterPaintEngine::drawImage(r, image, sr, flags); + return; + } +#if !defined QT_NO_DIRECTFB_PREALLOCATED || defined QT_DIRECTFB_IMAGECACHE + bool release; + IDirectFBSurface *imgSurface = d->getSurface(image, &release); + uint blitFlags = 0; + if (image.hasAlphaChannel()) + blitFlags |= QDirectFBPaintEnginePrivate::HasAlpha; + if (QDirectFBScreen::isPremultiplied(image.format())) + blitFlags |= QDirectFBPaintEnginePrivate::Premultiplied; + d->prepareForBlit(blitFlags); + CLIPPED_PAINT(d->blit(r, imgSurface, sr)); + if (release) { +#if (Q_DIRECTFB_VERSION >= 0x010000) + d->surface->ReleaseSource(d->surface); +#endif + imgSurface->Release(imgSurface); + } +#endif +} + +void QDirectFBPaintEngine::drawImage(const QPointF &p, const QImage &img) +{ + drawImage(QRectF(p, img.size()), img, img.rect()); +} + +void QDirectFBPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, + const QRectF &sr) +{ + Q_D(QDirectFBPaintEngine); + + if (pixmap.pixmapData()->classId() != QPixmapData::DirectFBClass) { + RASTERFALLBACK(DRAW_PIXMAP, r, pixmap.size(), sr); + d->lock(); + QRasterPaintEngine::drawPixmap(r, pixmap, sr); + } else { + QPixmapData *data = pixmap.pixmapData(); + Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); + QDirectFBPixmapData *dfbData = static_cast(data); + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || (d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) + || (!d->supportsStretchBlit() && state()->matrix.mapRect(r).size() != sr.size())) { + RASTERFALLBACK(DRAW_PIXMAP, r, pixmap.size(), sr); + const QImage *img = dfbData->buffer(); + d->lock(); + QRasterPaintEngine::drawImage(r, *img, sr); + } else { + QDirectFBPaintEnginePrivate::unlock(dfbData); + IDirectFBSurface *s = dfbData->directFBSurface(); + uint blitFlags = 0; + if (pixmap.hasAlphaChannel()) + blitFlags |= QDirectFBPaintEnginePrivate::HasAlpha; + if (QDirectFBScreen::isPremultiplied(dfbData->pixelFormat())) + blitFlags |= QDirectFBPaintEnginePrivate::Premultiplied; + + d->prepareForBlit(blitFlags); + CLIPPED_PAINT(d->blit(r, s, sr)); + } + } +} + +void QDirectFBPaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) +{ + drawPixmap(QRectF(p, pm.size()), pm, pm.rect()); +} + +void QDirectFBPaintEngine::drawTiledPixmap(const QRectF &r, + const QPixmap &pixmap, + const QPointF &offset) +{ + Q_D(QDirectFBPaintEngine); + if (pixmap.pixmapData()->classId() != QPixmapData::DirectFBClass) { + RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); + d->lock(); + QRasterPaintEngine::drawTiledPixmap(r, pixmap, offset); + } else if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || (d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) + || (!d->supportsStretchBlit() && state()->matrix.isScaling())) { + RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); + QPixmapData *pixmapData = pixmap.pixmapData(); + Q_ASSERT(pixmapData->classId() == QPixmapData::DirectFBClass); + QDirectFBPixmapData *dfbData = static_cast(pixmapData); + const QImage *img = dfbData->buffer(); + d->lock(); + QRasterPixmapData *data = new QRasterPixmapData(QPixmapData::PixmapType); + data->fromImage(*img, Qt::AutoColor); + const QPixmap pix(data); + QRasterPaintEngine::drawTiledPixmap(r, pix, offset); + } else { + QTransform transform(state()->matrix); + CLIPPED_PAINT(d->drawTiledPixmap(r, pixmap, offset, transform)); + } +} + + +void QDirectFBPaintEngine::stroke(const QVectorPath &path, const QPen &pen) +{ + RASTERFALLBACK(STROKE_PATH, path, VOID_ARG(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::stroke(path, pen); +} + +void QDirectFBPaintEngine::drawPath(const QPainterPath &path) +{ + RASTERFALLBACK(DRAW_PATH, path, VOID_ARG(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPath(path); +} + +void QDirectFBPaintEngine::drawPoints(const QPointF *points, int pointCount) +{ + RASTERFALLBACK(DRAW_POINTS, pointCount, VOID_ARG(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPoints(points, pointCount); +} + +void QDirectFBPaintEngine::drawPoints(const QPoint *points, int pointCount) +{ + RASTERFALLBACK(DRAW_POINTS, pointCount, VOID_ARG(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPoints(points, pointCount); +} + +void QDirectFBPaintEngine::drawEllipse(const QRectF &rect) +{ + RASTERFALLBACK(DRAW_ELLIPSE, rect, VOID_ARG(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawEllipse(rect); +} + +void QDirectFBPaintEngine::drawPolygon(const QPointF *points, int pointCount, + PolygonDrawMode mode) +{ + RASTERFALLBACK(DRAW_POLYGON, pointCount, mode, VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPolygon(points, pointCount, mode); +} + +void QDirectFBPaintEngine::drawPolygon(const QPoint *points, int pointCount, + PolygonDrawMode mode) +{ + RASTERFALLBACK(DRAW_POLYGON, pointCount, mode, VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPolygon(points, pointCount, mode); +} + +void QDirectFBPaintEngine::drawTextItem(const QPointF &p, + const QTextItem &textItem) +{ + RASTERFALLBACK(DRAW_TEXT, p, textItem.text(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawTextItem(p, textItem); +} + +void QDirectFBPaintEngine::fill(const QVectorPath &path, const QBrush &brush) +{ + if (brush.style() == Qt::NoBrush) + return; + RASTERFALLBACK(FILL_PATH, path, brush, VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::fill(path, brush); +} + +void QDirectFBPaintEngine::drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode) +{ + RASTERFALLBACK(DRAW_ROUNDED_RECT, rect, xrad, yrad); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawRoundedRect(rect, xrad, yrad, mode); +} + +void QDirectFBPaintEngine::drawStaticTextItem(QStaticTextItem *item) +{ + RASTERFALLBACK(DRAW_STATICTEXT, item, VOID_ARG(), VOID_ARG()); + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawStaticTextItem(item); +} + +void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) +{ + Q_D(QDirectFBPaintEngine); + if (brush.style() == Qt::NoBrush) + return; + if (d->clipType != QDirectFBPaintEnginePrivate::ComplexClip) { + switch (brush.style()) { + case Qt::SolidPattern: { + const QColor color = brush.color(); + if (!color.isValid()) + return; + + if (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported + || !(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported)) { + break; + } + d->setDFBColor(color); + const QRect r = state()->matrix.mapRect(rect).toRect(); + CLIPPED_PAINT(d->surface->FillRectangle(d->surface, r.x(), r.y(), r.width(), r.height())); + return; } + + case Qt::TexturePattern: { + const QPointF &brushOrigin = state()->brushOrigin; + const QTransform stateTransform = state()->matrix; + QTransform transform(stateTransform); + transform.translate(brushOrigin.x(), brushOrigin.y()); + transform = brush.transform() * transform; + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported) + || (QDirectFBPaintEnginePrivate::getTransformationType(transform) & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || (!d->supportsStretchBlit() && transform.isScaling())) { + break; + } + + const QPixmap texture = brush.texture(); + if (texture.pixmapData()->classId() != QPixmapData::DirectFBClass) + break; + + CLIPPED_PAINT(d->drawTiledPixmap(stateTransform.mapRect(rect), texture, rect.topLeft() - brushOrigin, transform)); + return; } + default: + break; + } + } + RASTERFALLBACK(FILL_RECT, rect, brush, VOID_ARG()); + d->lock(); + QRasterPaintEngine::fillRect(rect, brush); +} + +void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QColor &color) +{ + if (!color.isValid()) + return; + Q_D(QDirectFBPaintEngine); + if ((d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported) + || (d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) + || !(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_Supported)) { + RASTERFALLBACK(FILL_RECT, rect, color, VOID_ARG()); + d->lock(); + QRasterPaintEngine::fillRect(rect, color); + } else { + d->setDFBColor(color); + const QRect r = state()->matrix.mapRect(rect).toRect(); + CLIPPED_PAINT(d->surface->FillRectangle(d->surface, r.x(), r.y(), r.width(), r.height())); + } +} + +void QDirectFBPaintEngine::drawBufferSpan(const uint *buffer, int bufsize, + int x, int y, int length, + uint const_alpha) +{ + Q_D(QDirectFBPaintEngine); + IDirectFBSurface *src = d->surfaceCache->getSurface(buffer, bufsize); + // ### how does this play with setDFBColor + src->SetColor(src, 0, 0, 0, const_alpha); + const DFBRectangle rect = { 0, 0, length, 1 }; + d->surface->Blit(d->surface, src, &rect, x, y); +} + +#ifdef QT_DIRECTFB_IMAGECACHE +static void cachedImageCleanupHook(qint64 key) +{ + delete imageCache.take(key); +} +void QDirectFBPaintEngine::initImageCache(int size) +{ + Q_ASSERT(size >= 0); + imageCache.setMaxCost(size); + QImagePixmapCleanupHooks::instance()->addImageHook(cachedImageCleanupHook); +} + +#endif // QT_DIRECTFB_IMAGECACHE + +// ---- QDirectFBPaintEnginePrivate ---- + + +QDirectFBPaintEnginePrivate::QDirectFBPaintEnginePrivate(QDirectFBPaintEngine *p) + : surface(0), antialiased(false), simplePen(false), + transformationType(0), opacity(255), + clipType(ClipUnset), dfbDevice(0), + compositionModeStatus(0), isPremultiplied(false), inClip(false), q(p) +{ + fb = QDirectFBScreen::instance()->dfb(); + surfaceCache = new SurfaceCache; +} + +QDirectFBPaintEnginePrivate::~QDirectFBPaintEnginePrivate() +{ + delete surfaceCache; +} + +bool QDirectFBPaintEnginePrivate::isSimpleBrush(const QBrush &brush) const +{ + return (brush.style() == Qt::NoBrush) || (brush.style() == Qt::SolidPattern && !antialiased); +} + +void QDirectFBPaintEnginePrivate::lock() +{ + // We will potentially get a new pointer to the buffer after a + // lock so we need to call the base implementation of prepare so + // it updates its rasterBuffer to point to the new buffer address. + Q_ASSERT(dfbDevice); + if (dfbDevice->lockSurface(DSLF_READ|DSLF_WRITE)) { + prepare(dfbDevice); + } +} + +void QDirectFBPaintEnginePrivate::unlock() +{ + Q_ASSERT(dfbDevice); +#ifdef QT_DIRECTFB_SUBSURFACE + dfbDevice->syncPending = true; +#else + QDirectFBPaintEnginePrivate::unlock(dfbDevice); +#endif +} + +void QDirectFBPaintEnginePrivate::unlock(QDirectFBPaintDevice *device) +{ +#ifdef QT_NO_DIRECTFB_SUBSURFACE + Q_ASSERT(device); + device->unlockSurface(); +#else + Q_UNUSED(device); +#endif +} + +void QDirectFBPaintEnginePrivate::setTransform(const QTransform &transform) +{ + transformationType = getTransformationType(transform); + setPen(q->state()->pen); +} + +void QDirectFBPaintEnginePrivate::setPen(const QPen &pen) +{ + if (pen.style() == Qt::NoPen) { + simplePen = true; + } else if (pen.style() == Qt::SolidLine + && !antialiased + && pen.brush().style() == Qt::SolidPattern + && pen.widthF() <= 1.0 + && (transformationType < QTransform::TxScale || pen.isCosmetic())) { + simplePen = true; + } else { + simplePen = false; + } +} + +void QDirectFBPaintEnginePrivate::setCompositionMode(QPainter::CompositionMode mode) +{ + if (!surface) + return; + + static const bool forceRasterFallBack = qgetenv("QT_DIRECTFB_FORCE_RASTER").toInt() > 0; + if (forceRasterFallBack) { + compositionModeStatus = PorterDuff_None; + return; + } + + compositionModeStatus = PorterDuff_Supported|PorterDuff_PremultiplyColors|PorterDuff_AlwaysBlend; + switch (mode) { + case QPainter::CompositionMode_Clear: + surface->SetPorterDuff(surface, DSPD_CLEAR); + break; + case QPainter::CompositionMode_Source: + surface->SetPorterDuff(surface, DSPD_SRC); + compositionModeStatus &= ~PorterDuff_AlwaysBlend; + if (!isPremultiplied) + compositionModeStatus &= ~PorterDuff_PremultiplyColors; + break; + case QPainter::CompositionMode_SourceOver: + compositionModeStatus &= ~PorterDuff_AlwaysBlend; + surface->SetPorterDuff(surface, DSPD_SRC_OVER); + break; + case QPainter::CompositionMode_DestinationOver: + surface->SetPorterDuff(surface, DSPD_DST_OVER); + break; + case QPainter::CompositionMode_SourceIn: + surface->SetPorterDuff(surface, DSPD_SRC_IN); + if (!isPremultiplied) + compositionModeStatus &= ~PorterDuff_PremultiplyColors; + break; + case QPainter::CompositionMode_DestinationIn: + surface->SetPorterDuff(surface, DSPD_DST_IN); + break; + case QPainter::CompositionMode_SourceOut: + surface->SetPorterDuff(surface, DSPD_SRC_OUT); + break; + case QPainter::CompositionMode_DestinationOut: + surface->SetPorterDuff(surface, DSPD_DST_OUT); + break; + case QPainter::CompositionMode_Destination: + surface->SetSrcBlendFunction(surface, DSBF_ZERO); + surface->SetDstBlendFunction(surface, DSBF_ONE); + break; +#if (Q_DIRECTFB_VERSION >= 0x010000) + case QPainter::CompositionMode_SourceAtop: + surface->SetPorterDuff(surface, DSPD_SRC_ATOP); + break; + case QPainter::CompositionMode_DestinationAtop: + surface->SetPorterDuff(surface, DSPD_DST_ATOP); + break; + case QPainter::CompositionMode_Plus: + surface->SetPorterDuff(surface, DSPD_ADD); + break; + case QPainter::CompositionMode_Xor: + surface->SetPorterDuff(surface, DSPD_XOR); + break; +#endif + default: + compositionModeStatus = PorterDuff_None; + break; + } +} + +void QDirectFBPaintEnginePrivate::setRenderHints(QPainter::RenderHints hints) +{ + const bool old = antialiased; + antialiased = bool(hints & QPainter::Antialiasing); + if (old != antialiased) { + setPen(q->state()->pen); + } +} + +void QDirectFBPaintEnginePrivate::prepareForBlit(uint flags) +{ + DFBSurfaceBlittingFlags blittingFlags = DSBLIT_NOFX; + if (flags & Premultiplied) + blittingFlags |= DSBLIT_SRC_PREMULTIPLY; + if (flags & HasAlpha) + blittingFlags |= DSBLIT_BLEND_ALPHACHANNEL; + if (opacity != 255) { + blittingFlags |= DSBLIT_BLEND_COLORALPHA; + surface->SetColor(surface, 0xff, 0xff, 0xff, opacity); + } + + surface->SetBlittingFlags(surface, blittingFlags); +} + +static inline uint ALPHA_MUL(uint x, uint a) +{ + uint t = x * a; + t = ((t + (t >> 8) + 0x80) >> 8) & 0xff; + return t; +} + +void QDirectFBPaintEnginePrivate::setDFBColor(const QColor &color) +{ + Q_ASSERT(surface); + Q_ASSERT(compositionModeStatus & PorterDuff_Supported); + const quint8 alpha = (opacity == 255 ? + color.alpha() : ALPHA_MUL(color.alpha(), opacity)); + QColor col; + if (compositionModeStatus & PorterDuff_PremultiplyColors) { + col = QColor(ALPHA_MUL(color.red(), alpha), + ALPHA_MUL(color.green(), alpha), + ALPHA_MUL(color.blue(), alpha), + alpha); + } else { + col = QColor(color.red(), color.green(), color.blue(), alpha); + } + surface->SetColor(surface, col.red(), col.green(), col.blue(), col.alpha()); + surface->SetDrawingFlags(surface, alpha == 255 && !(compositionModeStatus & PorterDuff_AlwaysBlend) ? DSDRAW_NOFX : DSDRAW_BLEND); +} + +IDirectFBSurface *QDirectFBPaintEnginePrivate::getSurface(const QImage &img, bool *release) +{ +#ifdef QT_NO_DIRECTFB_IMAGECACHE + *release = true; + return QDirectFBScreen::instance()->createDFBSurface(img, img.format(), QDirectFBScreen::DontTrackSurface); +#else + const qint64 key = img.cacheKey(); + *release = false; + if (imageCache.contains(key)) { + return imageCache[key]->surface; + } + + const int cost = cacheCost(img); + const bool cache = cost <= imageCache.maxCost(); + QDirectFBScreen *screen = QDirectFBScreen::instance(); + const QImage::Format format = (img.format() == screen->alphaPixmapFormat() || QDirectFBPixmapData::hasAlphaChannel(img) + ? screen->alphaPixmapFormat() : screen->pixelFormat()); + + IDirectFBSurface *surface = screen->createDFBSurface(img, format, + cache + ? QDirectFBScreen::TrackSurface + : QDirectFBScreen::DontTrackSurface); + if (cache) { + CachedImage *cachedImage = new CachedImage; + const_cast(img).data_ptr()->is_cached = true; + cachedImage->surface = surface; + imageCache.insert(key, cachedImage, cost); + } else { + *release = true; + } + return surface; +#endif +} + + +void QDirectFBPaintEnginePrivate::blit(const QRectF &dest, IDirectFBSurface *s, const QRectF &src) +{ + const QRect sr = src.toRect(); + const QRect dr = q->state()->matrix.mapRect(dest).toRect(); + if (dr.isEmpty()) + return; + const DFBRectangle sRect = { sr.x(), sr.y(), sr.width(), sr.height() }; + DFBResult result; + + if (dr.size() == sr.size()) { + result = surface->Blit(surface, s, &sRect, dr.x(), dr.y()); + } else { + Q_ASSERT(supportsStretchBlit()); + const DFBRectangle dRect = { dr.x(), dr.y(), dr.width(), dr.height() }; + result = surface->StretchBlit(surface, s, &sRect, &dRect); + } + if (result != DFB_OK) + DirectFBError("QDirectFBPaintEngine::drawPixmap()", result); +} + +static inline qreal fixCoord(qreal rect_pos, qreal pixmapSize, qreal offset) +{ + qreal pos = rect_pos - offset; + while (pos > rect_pos) + pos -= pixmapSize; + while (pos + pixmapSize < rect_pos) + pos += pixmapSize; + return pos; +} + +void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap, + const QPointF &off, const QTransform &pixmapTransform) +{ + const QTransform &transform = q->state()->matrix; + Q_ASSERT(!(getTransformationType(transform) & Matrix_BlitsUnsupported) && + !(getTransformationType(pixmapTransform) & Matrix_BlitsUnsupported)); + const QRect destinationRect = transform.mapRect(dest).toRect().normalized(); + QRect newClip = destinationRect; + if (!currentClip.isEmpty()) + newClip &= currentClip; + + if (newClip.isNull()) + return; + + const DFBRegion clip = { + newClip.x(), + newClip.y(), + newClip.right(), + newClip.bottom() + }; + surface->SetClip(surface, &clip); + + QPointF offset = pixmapTransform.inverted().map(off); + Q_ASSERT(transform.type() <= QTransform::TxScale); + QPixmapData *data = pixmap.pixmapData(); + Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); + QDirectFBPixmapData *dfbData = static_cast(data); + IDirectFBSurface *sourceSurface = dfbData->directFBSurface(); + uint blitFlags = 0; + if (dfbData->hasAlphaChannel()) + blitFlags |= HasAlpha; + if (QDirectFBScreen::isPremultiplied(dfbData->pixelFormat())) + blitFlags |= Premultiplied; + prepareForBlit(blitFlags); + QDirectFBPaintEnginePrivate::unlock(dfbData); + const QSize pixmapSize = dfbData->size(); + if (transform.isScaling() || pixmapTransform.isScaling()) { + Q_ASSERT(supportsStretchBlit()); + Q_ASSERT(qMin(transform.m11(), transform.m22()) >= 0); + offset.rx() *= transform.m11(); + offset.ry() *= transform.m22(); + + const QSizeF mappedSize(pixmapSize.width() * pixmapTransform.m11(), pixmapSize.height() * pixmapTransform.m22()); + qreal y = fixCoord(destinationRect.y(), mappedSize.height(), offset.y()); + const qreal startX = fixCoord(destinationRect.x(), mappedSize.width(), offset.x()); + while (y <= destinationRect.bottom()) { + qreal x = startX; + while (x <= destinationRect.right()) { + const DFBRectangle destination = { qRound(x), qRound(y), mappedSize.width(), mappedSize.height() }; + surface->StretchBlit(surface, sourceSurface, 0, &destination); + x += mappedSize.width(); + } + y += mappedSize.height(); + } + } else { + qreal y = fixCoord(destinationRect.y(), pixmapSize.height(), offset.y()); + const qreal startX = fixCoord(destinationRect.x(), pixmapSize.width(), offset.x()); + int horizontal = qMax(1, destinationRect.width() / pixmapSize.width()) + 1; + if (startX != destinationRect.x()) + ++horizontal; + int vertical = qMax(1, destinationRect.height() / pixmapSize.height()) + 1; + if (y != destinationRect.y()) + ++vertical; + + const int maxCount = (vertical * horizontal); + QVarLengthArray sourceRects(maxCount); + QVarLengthArray points(maxCount); + + int i = 0; + while (y <= destinationRect.bottom()) { + Q_ASSERT(i < maxCount); + qreal x = startX; + while (x <= destinationRect.right()) { + points[i].x = qRound(x); + points[i].y = qRound(y); + sourceRects[i].x = 0; + sourceRects[i].y = 0; + sourceRects[i].w = int(pixmapSize.width()); + sourceRects[i].h = int(pixmapSize.height()); + x += pixmapSize.width(); + ++i; + } + y += pixmapSize.height(); + } + surface->BatchBlit(surface, sourceSurface, sourceRects.constData(), points.constData(), i); + } + + if (currentClip.isEmpty()) { + surface->SetClip(surface, 0); + } else { + const DFBRegion clip = { + currentClip.x(), + currentClip.y(), + currentClip.right(), + currentClip.bottom() + }; + surface->SetClip(surface, &clip); + } +} + +void QDirectFBPaintEnginePrivate::updateClip() +{ + Q_ASSERT(surface); + currentClip = QRect(); + const QClipData *clipData = clip(); + if (!clipData || !clipData->enabled) { + surface->SetClip(surface, NULL); + clipType = NoClip; + } else if (clipData->hasRectClip) { + const DFBRegion r = { + clipData->clipRect.x(), + clipData->clipRect.y(), + clipData->clipRect.right(), + clipData->clipRect.bottom() + }; + surface->SetClip(surface, &r); + currentClip = clipData->clipRect.normalized(); + // ### is this guaranteed to always be normalized? + clipType = RectClip; + } else if (clipData->hasRegionClip) { + clipType = RegionClip; + } else { + clipType = ComplexClip; + } +} + +bool QDirectFBPaintEnginePrivate::supportsStretchBlit() const +{ +#ifdef QT_DIRECTFB_STRETCHBLIT + return !(q->state()->renderHints & QPainter::SmoothPixmapTransform); +#else + return false; +#endif +} + + +void QDirectFBPaintEnginePrivate::systemStateChanged() +{ + QRasterPaintEnginePrivate::systemStateChanged(); + updateClip(); +} + +IDirectFBSurface *SurfaceCache::getSurface(const uint *buf, int size) +{ + if (buffer == buf && bufsize == size) + return surface; + + clear(); + + const DFBSurfaceDescription description = QDirectFBScreen::getSurfaceDescription(buf, size); + surface = QDirectFBScreen::instance()->createDFBSurface(description, QDirectFBScreen::TrackSurface, 0); + if (!surface) + qWarning("QDirectFBPaintEngine: SurfaceCache: Unable to create surface"); + + buffer = const_cast(buf); + bufsize = size; + + return surface; +} + +void SurfaceCache::clear() +{ + if (surface && QDirectFBScreen::instance()) + QDirectFBScreen::instance()->releaseDFBSurface(surface); + surface = 0; + buffer = 0; + bufsize = 0; +} + + +static inline QRect mapRect(const QTransform &transform, const QRect &rect) { return transform.mapRect(rect); } +static inline QRect mapRect(const QTransform &transform, const QRectF &rect) { return transform.mapRect(rect).toRect(); } +static inline QLine map(const QTransform &transform, const QLine &line) { return transform.map(line); } +static inline QLine map(const QTransform &transform, const QLineF &line) { return transform.map(line).toLine(); } +template +static inline void drawLines(const T *lines, int n, const QTransform &transform, IDirectFBSurface *surface) +{ + if (n == 1) { + const QLine l = map(transform, lines[0]); + surface->DrawLine(surface, l.x1(), l.y1(), l.x2(), l.y2()); + } else { + QVarLengthArray lineArray(n); + for (int i=0; iDrawLines(surface, lineArray.constData(), n); + } +} + +template +static inline void fillRects(const T *rects, int n, const QTransform &transform, IDirectFBSurface *surface) +{ + if (n == 1) { + const QRect r = mapRect(transform, rects[0]); + surface->FillRectangle(surface, r.x(), r.y(), r.width(), r.height()); + } else { + QVarLengthArray rectArray(n); + for (int i=0; iFillRectangles(surface, rectArray.constData(), n); + } +} + +template +static inline void drawRects(const T *rects, int n, const QTransform &transform, IDirectFBSurface *surface) +{ + for (int i=0; iDrawRectangle(surface, r.x(), r.y(), r.width(), r.height()); + } +} + +template inline const T *ptr(const T &t) { return &t; } +template <> inline const bool* ptr(const bool &) { return 0; } +template +static void rasterFallbackWarn(const char *msg, const char *func, const device *dev, + uint transformationType, bool simplePen, + uint clipType, uint compositionModeStatus, + const char *nameOne, const T1 &one, + const char *nameTwo, const T2 &two, + const char *nameThree, const T3 &three) +{ + QString out; + QDebug dbg(&out); + dbg << msg << (QByteArray(func) + "()") << "painting on"; + if (dev->devType() == QInternal::Widget) { + dbg << static_cast(dev); + } else { + dbg << dev << "of type" << dev->devType(); + } + + dbg << QString::fromLatin1("transformationType 0x%1").arg(transformationType, 3, 16, QLatin1Char('0')) + << "simplePen" << simplePen + << "clipType" << clipType + << "compositionModeStatus" << compositionModeStatus; + + const T1 *t1 = ptr(one); + const T2 *t2 = ptr(two); + const T3 *t3 = ptr(three); + + if (t1) { + dbg << nameOne << *t1; + if (t2) { + dbg << nameTwo << *t2; + if (t3) { + dbg << nameThree << *t3; + } + } + } + qWarning("%s", qPrintable(out)); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h new file mode 100644 index 0000000000..1908f3ad32 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QPAINTENGINE_DIRECTFB_P_H +#define QPAINTENGINE_DIRECTFB_P_H + +#include +#include + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDirectFBPaintEnginePrivate; + +class QDirectFBPaintEngine : public QRasterPaintEngine +{ + Q_DECLARE_PRIVATE(QDirectFBPaintEngine) +public: + QDirectFBPaintEngine(QPaintDevice *device); + virtual ~QDirectFBPaintEngine(); + + virtual bool begin(QPaintDevice *device); + virtual bool end(); + + virtual void drawRects(const QRect *rects, int rectCount); + virtual void drawRects(const QRectF *rects, int rectCount); + + virtual void fillRect(const QRectF &r, const QBrush &brush); + virtual void fillRect(const QRectF &r, const QColor &color); + + virtual void drawLines(const QLine *line, int lineCount); + virtual void drawLines(const QLineF *line, int lineCount); + + virtual void drawImage(const QPointF &p, const QImage &img); + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags falgs = Qt::AutoColor); + + virtual void drawPixmap(const QPointF &p, const QPixmap &pm); + virtual void drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr); + virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr); + + virtual void drawBufferSpan(const uint *buffer, int bufsize, + int x, int y, int length, uint const_alpha); + + virtual void stroke(const QVectorPath &path, const QPen &pen); + virtual void drawPath(const QPainterPath &path); + virtual void drawPoints(const QPointF *points, int pointCount); + virtual void drawPoints(const QPoint *points, int pointCount); + virtual void drawEllipse(const QRectF &rect); + virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode); + virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); + virtual void fill(const QVectorPath &path, const QBrush &brush); + virtual void drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode); + + virtual void clipEnabledChanged(); + virtual void penChanged(); + virtual void opacityChanged(); + virtual void compositionModeChanged(); + virtual void renderHintsChanged(); + virtual void transformChanged(); + + virtual void setState(QPainterState *state); + + virtual void clip(const QVectorPath &path, Qt::ClipOperation op); + virtual void clip(const QRegion ®ion, Qt::ClipOperation op); + virtual void clip(const QRect &rect, Qt::ClipOperation op); + + virtual void drawStaticTextItem(QStaticTextItem *item); + + static void initImageCache(int size); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_DIRECTFB + +#endif // QPAINTENGINE_DIRECTFB_P_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp new file mode 100644 index 0000000000..eaff74a41c --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp @@ -0,0 +1,588 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbpixmap.h" + +#ifndef QT_NO_QWS_DIRECTFB + +#include "qdirectfbscreen.h" +#include "qdirectfbpaintengine.h" + +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +static int global_ser_no = 0; + +QDirectFBPixmapData::QDirectFBPixmapData(QDirectFBScreen *screen, PixelType pixelType) + : QPixmapData(pixelType, DirectFBClass), QDirectFBPaintDevice(screen), + alpha(false) +{ + setSerialNumber(0); +} + +QDirectFBPixmapData::~QDirectFBPixmapData() +{ +} + +void QDirectFBPixmapData::resize(int width, int height) +{ + if (width <= 0 || height <= 0) { + invalidate(); + return; + } + + imageFormat = screen->pixelFormat(); + dfbSurface = screen->createDFBSurface(QSize(width, height), + imageFormat, + QDirectFBScreen::TrackSurface); + d = QDirectFBScreen::depth(imageFormat); + alpha = false; + if (!dfbSurface) { + invalidate(); + qWarning("QDirectFBPixmapData::resize(): Unable to allocate surface"); + return; + } + + w = width; + h = height; + is_null = (w <= 0 || h <= 0); + setSerialNumber(++global_ser_no); +} + +#ifdef QT_DIRECTFB_OPAQUE_DETECTION +// mostly duplicated from qimage.cpp (QImageData::checkForAlphaPixels) +static bool checkForAlphaPixels(const QImage &img) +{ + const uchar *bits = img.bits(); + const int bytes_per_line = img.bytesPerLine(); + const uchar *end_bits = bits + bytes_per_line; + const int width = img.width(); + const int height = img.height(); + switch (img.format()) { + case QImage::Format_Indexed8: + return img.hasAlphaChannel(); + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + for (int y=0; y(data.constData()), data.size(), format, flags); + } else { + DFBDataBufferDescription description; + description.flags = DBDESC_FILE; + const QByteArray fileNameData = filename.toLocal8Bit(); + description.file = fileNameData.constData(); + if (fromDataBufferDescription(description)) { + return true; + } + // fall back to Qt + } + } + return QPixmapData::fromFile(filename, format, flags); +} + +bool QDirectFBPixmapData::fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags) +{ + if (flags == Qt::AutoColor) { + DFBDataBufferDescription description; + description.flags = DBDESC_MEMORY; + description.memory.data = buffer; + description.memory.length = len; + if (fromDataBufferDescription(description)) + return true; + // fall back to Qt + } + return QPixmapData::fromData(buffer, len, format, flags); +} + +template struct QDirectFBInterfaceCleanupHandler +{ + static void cleanup(T *t) { if (t) t->Release(t); } +}; + +template +class QDirectFBPointer : public QScopedPointer > +{ +public: + QDirectFBPointer(T *t = 0) + : QScopedPointer >(t) + {} +}; + +bool QDirectFBPixmapData::fromDataBufferDescription(const DFBDataBufferDescription &dataBufferDescription) +{ + IDirectFB *dfb = screen->dfb(); + Q_ASSERT(dfb); + DFBResult result = DFB_OK; + IDirectFBDataBuffer *dataBufferPtr; + if ((result = dfb->CreateDataBuffer(dfb, &dataBufferDescription, &dataBufferPtr)) != DFB_OK) { + DirectFBError("QDirectFBPixmapData::fromDataBufferDescription()", result); + return false; + } + QDirectFBPointer dataBuffer(dataBufferPtr); + + IDirectFBImageProvider *providerPtr; + if ((result = dataBuffer->CreateImageProvider(dataBuffer.data(), &providerPtr)) != DFB_OK) + return false; + + QDirectFBPointer provider(providerPtr); + + DFBImageDescription imageDescription; + result = provider->GetImageDescription(provider.data(), &imageDescription); + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::fromSurfaceDescription(): Can't get image description", result); + return false; + } + + if (imageDescription.caps & DICAPS_COLORKEY) { + return false; + } + + DFBSurfaceDescription surfaceDescription; + if ((result = provider->GetSurfaceDescription(provider.data(), &surfaceDescription)) != DFB_OK) { + DirectFBError("QDirectFBPixmapData::fromDataBufferDescription(): Can't get surface description", result); + return false; + } + + alpha = imageDescription.caps & DICAPS_ALPHACHANNEL; + imageFormat = alpha ? screen->alphaPixmapFormat() : screen->pixelFormat(); + + dfbSurface = screen->createDFBSurface(QSize(surfaceDescription.width, surfaceDescription.height), + imageFormat, QDirectFBScreen::TrackSurface); + + result = provider->RenderTo(provider.data(), dfbSurface, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::fromSurfaceDescription(): Can't render to surface", result); + return false; + } + + w = surfaceDescription.width; + h = surfaceDescription.height; + is_null = (w <= 0 || h <= 0); + d = QDirectFBScreen::depth(imageFormat); + setSerialNumber(++global_ser_no); + +#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE + screen->setDirectFBImageProvider(providerPtr); + provider.take(); +#endif + + return true; +} + +#endif + +void QDirectFBPixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags) +{ + alpha = QDirectFBPixmapData::hasAlphaChannel(img, flags); + imageFormat = alpha ? screen->alphaPixmapFormat() : screen->pixelFormat(); + + QImage image; + if ((flags & ~Qt::NoOpaqueDetection) != Qt::AutoColor) { + image = img.convertToFormat(imageFormat, flags); + flags = Qt::AutoColor; + } else if (img.format() == QImage::Format_RGB32 || img.depth() == 1) { + image = img.convertToFormat(imageFormat, flags); + } else if (img.format() != imageFormat) { + image = img.convertToFormat(imageFormat, flags); + } else { + image = img; + } + + dfbSurface = screen->createDFBSurface(image, image.format(), QDirectFBScreen::NoPreallocated | QDirectFBScreen::TrackSurface); + if (!dfbSurface) { + qWarning("QDirectFBPixmapData::fromImage()"); + invalidate(); + return; + } + + w = image.width(); + h = image.height(); + is_null = (w <= 0 || h <= 0); + d = QDirectFBScreen::depth(imageFormat); + setSerialNumber(++global_ser_no); +#ifdef QT_NO_DIRECTFB_OPAQUE_DETECTION + Q_UNUSED(flags); +#endif +} + +void QDirectFBPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + if (data->classId() != DirectFBClass) { + QPixmapData::copy(data, rect); + return; + } + + const QDirectFBPixmapData *otherData = static_cast(data); +#ifdef QT_NO_DIRECTFB_SUBSURFACE + if (otherData->lockFlags()) { + const_cast(otherData)->unlockSurface(); + } +#endif + IDirectFBSurface *src = otherData->directFBSurface(); + alpha = data->hasAlphaChannel(); + imageFormat = (alpha + ? QDirectFBScreen::instance()->alphaPixmapFormat() + : QDirectFBScreen::instance()->pixelFormat()); + + + dfbSurface = screen->createDFBSurface(rect.size(), imageFormat, + QDirectFBScreen::TrackSurface); + if (!dfbSurface) { + qWarning("QDirectFBPixmapData::copy()"); + invalidate(); + return; + } + + if (alpha) { + dfbSurface->Clear(dfbSurface, 0, 0, 0, 0); + dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_BLEND_ALPHACHANNEL); + } else { + dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); + } + const DFBRectangle blitRect = { rect.x(), rect.y(), + rect.width(), rect.height() }; + w = rect.width(); + h = rect.height(); + d = otherData->d; + is_null = (w <= 0 || h <= 0); + unlockSurface(); + DFBResult result = dfbSurface->Blit(dfbSurface, src, &blitRect, 0, 0); +#if (Q_DIRECTFB_VERSION >= 0x010000) + dfbSurface->ReleaseSource(dfbSurface); +#endif + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::copy()", result); + invalidate(); + return; + } + + setSerialNumber(++global_ser_no); +} + +static inline bool isOpaqueFormat(QImage::Format format) +{ + switch (format) { + case QImage::Format_RGB32: + case QImage::Format_RGB16: + case QImage::Format_RGB666: + case QImage::Format_RGB555: + case QImage::Format_RGB888: + case QImage::Format_RGB444: + return true; + default: + break; + } + return false; +} + +void QDirectFBPixmapData::fill(const QColor &color) +{ + if (!serialNumber()) + return; + + Q_ASSERT(dfbSurface); + + alpha |= (color.alpha() < 255); + + if (alpha && isOpaqueFormat(imageFormat)) { + QSize size; + dfbSurface->GetSize(dfbSurface, &size.rwidth(), &size.rheight()); + screen->releaseDFBSurface(dfbSurface); + imageFormat = screen->alphaPixmapFormat(); + d = QDirectFBScreen::depth(imageFormat); + dfbSurface = screen->createDFBSurface(size, screen->alphaPixmapFormat(), QDirectFBScreen::TrackSurface); + setSerialNumber(++global_ser_no); + if (!dfbSurface) { + qWarning("QDirectFBPixmapData::fill()"); + invalidate(); + return; + } + } + + dfbSurface->Clear(dfbSurface, color.red(), color.green(), color.blue(), color.alpha()); +} + +QPixmap QDirectFBPixmapData::transformed(const QTransform &transform, + Qt::TransformationMode mode) const +{ + QDirectFBPixmapData *that = const_cast(this); +#ifdef QT_NO_DIRECTFB_SUBSURFACE + if (lockFlags()) + that->unlockSurface(); +#endif + + if (!dfbSurface || transform.type() != QTransform::TxScale + || mode != Qt::FastTransformation) + { + const QImage *image = that->buffer(); + Q_ASSERT(image); + const QImage transformed = image->transformed(transform, mode); + QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType); + data->fromImage(transformed, Qt::AutoColor); + return QPixmap(data); + } + + const QSize size = transform.mapRect(QRect(0, 0, w, h)).size(); + if (size.isEmpty()) + return QPixmap(); + + QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType); + data->setSerialNumber(++global_ser_no); + DFBSurfaceBlittingFlags flags = DSBLIT_NOFX; + data->alpha = alpha; + if (alpha) { + flags = DSBLIT_BLEND_ALPHACHANNEL; + } + data->dfbSurface = screen->createDFBSurface(size, + imageFormat, + QDirectFBScreen::TrackSurface); + if (flags & DSBLIT_BLEND_ALPHACHANNEL) { + data->dfbSurface->Clear(data->dfbSurface, 0, 0, 0, 0); + } + data->dfbSurface->SetBlittingFlags(data->dfbSurface, flags); + + const DFBRectangle destRect = { 0, 0, size.width(), size.height() }; + data->dfbSurface->StretchBlit(data->dfbSurface, dfbSurface, 0, &destRect); + data->w = size.width(); + data->h = size.height(); + data->is_null = (data->w <= 0 || data->h <= 0); + +#if (Q_DIRECTFB_VERSION >= 0x010000) + data->dfbSurface->ReleaseSource(data->dfbSurface); +#endif + return QPixmap(data); +} + +QImage QDirectFBPixmapData::toImage() const +{ + if (!dfbSurface) + return QImage(); + +#if 0 + // In later versions of DirectFB one can set a flag to tell + // DirectFB not to move the surface to videomemory. When that + // happens we can use this (hopefully faster) codepath +#ifndef QT_NO_DIRECTFB_PREALLOCATED + QImage ret(w, h, QDirectFBScreen::getImageFormat(dfbSurface)); + if (IDirectFBSurface *imgSurface = screen->createDFBSurface(ret, QDirectFBScreen::DontTrackSurface)) { + if (hasAlphaChannel()) { + imgSurface->SetBlittingFlags(imgSurface, DSBLIT_BLEND_ALPHACHANNEL); + imgSurface->Clear(imgSurface, 0, 0, 0, 0); + } else { + imgSurface->SetBlittingFlags(imgSurface, DSBLIT_NOFX); + } + imgSurface->Blit(imgSurface, dfbSurface, 0, 0, 0); +#if (Q_DIRECTFB_VERSION >= 0x010000) + imgSurface->ReleaseSource(imgSurface); +#endif + imgSurface->Release(imgSurface); + return ret; + } +#endif +#endif + + QDirectFBPixmapData *that = const_cast(this); + const QImage *img = that->buffer(); + return img->copy(); +} + +/* This is QPixmapData::paintEngine(), not QPaintDevice::paintEngine() */ + +QPaintEngine *QDirectFBPixmapData::paintEngine() const +{ + if (!engine) { + // QDirectFBPixmapData is also a QCustomRasterPaintDevice, so pass + // that to the paint engine: + QDirectFBPixmapData *that = const_cast(this); + that->engine = new QDirectFBPaintEngine(that); + } + return engine; +} + +QImage *QDirectFBPixmapData::buffer() +{ + if (!lockFlgs) { + lockSurface(DSLF_READ|DSLF_WRITE); + } + Q_ASSERT(lockFlgs); + Q_ASSERT(!lockedImage.isNull()); + return &lockedImage; +} + + +bool QDirectFBPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + if (!dfbSurface) { + return false; + } + unlockSurface(); + DFBResult result = dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::scroll", result); + return false; + } + result = dfbSurface->SetPorterDuff(dfbSurface, DSPD_NONE); + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::scroll", result); + return false; + } + + const DFBRectangle source = { rect.x(), rect.y(), rect.width(), rect.height() }; + result = dfbSurface->Blit(dfbSurface, dfbSurface, &source, source.x + dx, source.y + dy); + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::scroll", result); + return false; + } + + return true; +} + +void QDirectFBPixmapData::invalidate() +{ + if (dfbSurface) { + screen->releaseDFBSurface(dfbSurface); + dfbSurface = 0; + } + setSerialNumber(0); + alpha = false; + d = w = h = 0; + is_null = true; + imageFormat = QImage::Format_Invalid; +} + +Q_GUI_EXPORT IDirectFBSurface *qt_directfb_surface_for_pixmap(const QPixmap &pixmap) +{ + const QPixmapData *data = pixmap.pixmapData(); + if (!data || data->classId() != QPixmapData::DirectFBClass) + return 0; + const QDirectFBPixmapData *dfbData = static_cast(data); + return dfbData->directFBSurface(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h new file mode 100644 index 0000000000..f8e3fa1d84 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QDIRECTFBPIXMAP_H +#define QDIRECTFBPIXMAP_H + +#include + +#ifndef QT_NO_QWS_DIRECTFB + +#include +#include +#include "qdirectfbpaintdevice.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDirectFBPaintEngine; + +class QDirectFBPixmapData : public QPixmapData, public QDirectFBPaintDevice +{ +public: + QDirectFBPixmapData(QDirectFBScreen *screen, PixelType pixelType); + ~QDirectFBPixmapData(); + + // Re-implemented from QPixmapData: + virtual void resize(int width, int height); + virtual void fromImage(const QImage &image, Qt::ImageConversionFlags flags); +#ifdef QT_DIRECTFB_IMAGEPROVIDER + virtual bool fromFile(const QString &filename, const char *format, + Qt::ImageConversionFlags flags); + virtual bool fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags); +#endif + virtual void copy(const QPixmapData *data, const QRect &rect); + virtual void fill(const QColor &color); + virtual QPixmap transformed(const QTransform &matrix, + Qt::TransformationMode mode) const; + virtual QImage toImage() const; + virtual QPaintEngine *paintEngine() const; + virtual QImage *buffer(); + virtual bool scroll(int dx, int dy, const QRect &rect); + // Pure virtual in QPixmapData, so re-implement here and delegate to QDirectFBPaintDevice + virtual int metric(QPaintDevice::PaintDeviceMetric m) const { return QDirectFBPaintDevice::metric(m); } + + inline QImage::Format pixelFormat() const { return imageFormat; } + inline bool hasAlphaChannel() const { return alpha; } + static bool hasAlphaChannel(const QImage &img, Qt::ImageConversionFlags flags = Qt::AutoColor); +private: +#ifdef QT_DIRECTFB_IMAGEPROVIDER + bool fromDataBufferDescription(const DFBDataBufferDescription &dataBuffer); +#endif + void invalidate(); + bool alpha; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_DIRECTFB + +#endif // QDIRECTFBPIXMAP_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp new file mode 100644 index 0000000000..ff15078ee4 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp @@ -0,0 +1,1819 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbscreen.h" +#include "qdirectfbwindowsurface.h" +#include "qdirectfbpixmap.h" +#include "qdirectfbmouse.h" +#include "qdirectfbkeyboard.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_NAMESPACE + +class QDirectFBScreenPrivate : public QObject, public QWSGraphicsSystem +{ + Q_OBJECT +public: + QDirectFBScreenPrivate(QDirectFBScreen *qptr); + ~QDirectFBScreenPrivate(); + + void setFlipFlags(const QStringList &args); + QPixmapData *createPixmapData(QPixmapData::PixelType type) const; +public slots: +#ifdef QT_DIRECTFB_WM + void onWindowEvent(QWSWindow *window, QWSServer::WindowEvent event); +#endif +public: + IDirectFB *dfb; + DFBSurfaceFlipFlags flipFlags; + QDirectFBScreen::DirectFBFlags directFBFlags; + QImage::Format alphaPixmapFormat; + IDirectFBScreen *dfbScreen; +#ifdef QT_NO_DIRECTFB_WM + IDirectFBSurface *primarySurface; + QColor backgroundColor; +#endif +#ifndef QT_NO_DIRECTFB_LAYER + IDirectFBDisplayLayer *dfbLayer; +#endif + QSet allocatedSurfaces; + +#ifndef QT_NO_DIRECTFB_MOUSE + QDirectFBMouseHandler *mouse; +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + QDirectFBKeyboardHandler *keyboard; +#endif +#if defined QT_DIRECTFB_IMAGEPROVIDER && defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE + IDirectFBImageProvider *imageProvider; +#endif + IDirectFBSurface *cursorSurface; + qint64 cursorImageKey; + + QDirectFBScreen *q; + static QDirectFBScreen *instance; +}; + +QDirectFBScreen *QDirectFBScreenPrivate::instance = 0; + +QDirectFBScreenPrivate::QDirectFBScreenPrivate(QDirectFBScreen *qptr) + : QWSGraphicsSystem(qptr), dfb(0), flipFlags(DSFLIP_NONE), + directFBFlags(QDirectFBScreen::NoFlags), alphaPixmapFormat(QImage::Format_Invalid), + dfbScreen(0) +#ifdef QT_NO_DIRECTFB_WM + , primarySurface(0) +#endif +#ifndef QT_NO_DIRECTFB_LAYER + , dfbLayer(0) +#endif +#ifndef QT_NO_DIRECTFB_MOUSE + , mouse(0) +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + , keyboard(0) +#endif +#if defined QT_DIRECTFB_IMAGEPROVIDER && defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE + , imageProvider(0) +#endif + , cursorSurface(0) + , cursorImageKey(0) + , q(qptr) +{ +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addObject(this); +#endif +#ifdef QT_DIRECTFB_WM + connect(QWSServer::instance(), SIGNAL(windowEvent(QWSWindow*,QWSServer::WindowEvent)), + this, SLOT(onWindowEvent(QWSWindow*,QWSServer::WindowEvent))); +#endif +} + +QDirectFBScreenPrivate::~QDirectFBScreenPrivate() +{ +#ifndef QT_NO_DIRECTFB_MOUSE + delete mouse; +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + delete keyboard; +#endif +#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE + if (imageProvider) + imageProvider->Release(imageProvider); +#endif + + for (QSet::const_iterator it = allocatedSurfaces.begin(); it != allocatedSurfaces.end(); ++it) { + (*it)->Release(*it); + } + +#ifdef QT_NO_DIRECTFB_WM + if (primarySurface) + primarySurface->Release(primarySurface); +#endif + +#ifndef QT_NO_DIRECTFB_LAYER + if (dfbLayer) + dfbLayer->Release(dfbLayer); +#endif + + if (dfbScreen) + dfbScreen->Release(dfbScreen); + + if (dfb) + dfb->Release(dfb); +} + +IDirectFBSurface *QDirectFBScreen::createDFBSurface(const QImage &image, QImage::Format format, SurfaceCreationOptions options, DFBResult *resultPtr) +{ + if (image.isNull()) // assert? + return 0; + + if (QDirectFBScreen::getSurfacePixelFormat(format) == DSPF_UNKNOWN) { + format = QDirectFBPixmapData::hasAlphaChannel(image) ? d_ptr->alphaPixmapFormat : pixelFormat(); + } + if (image.format() != format) { + return createDFBSurface(image.convertToFormat(format), format, options | NoPreallocated, resultPtr); + } + + DFBSurfaceDescription description; + memset(&description, 0, sizeof(DFBSurfaceDescription)); + description.width = image.width(); + description.height = image.height(); + description.flags = DSDESC_WIDTH|DSDESC_HEIGHT|DSDESC_PIXELFORMAT; + initSurfaceDescriptionPixelFormat(&description, format); + bool doMemCopy = true; +#ifdef QT_DIRECTFB_PREALLOCATED + if (!(options & NoPreallocated)) { + doMemCopy = false; + description.flags |= DSDESC_PREALLOCATED; + description.preallocated[0].data = const_cast(image.bits()); + description.preallocated[0].pitch = image.bytesPerLine(); + description.preallocated[1].data = 0; + description.preallocated[1].pitch = 0; + } +#endif + DFBResult result; + IDirectFBSurface *surface = createDFBSurface(description, options, &result); + if (resultPtr) + *resultPtr = result; + if (!surface) { + DirectFBError("Couldn't create surface createDFBSurface(QImage, QImage::Format, SurfaceCreationOptions)", result); + return 0; + } + if (doMemCopy) { + int bplDFB; + uchar *mem = QDirectFBScreen::lockSurface(surface, DSLF_WRITE, &bplDFB); + if (mem) { + const int height = image.height(); + const int bplQt = image.bytesPerLine(); + if (bplQt == bplDFB && bplQt == (image.width() * image.depth() / 8)) { + memcpy(mem, image.bits(), image.byteCount()); + } else { + for (int i=0; iUnlock(surface); + } + } +#ifdef QT_DIRECTFB_PALETTE + if (image.colorCount() != 0 && surface) + QDirectFBScreen::setSurfaceColorTable(surface, image); +#endif + return surface; +} + +IDirectFBSurface *QDirectFBScreen::copyDFBSurface(IDirectFBSurface *src, + QImage::Format format, + SurfaceCreationOptions options, + DFBResult *result) +{ + Q_ASSERT(src); + QSize size; + src->GetSize(src, &size.rwidth(), &size.rheight()); + IDirectFBSurface *surface = createDFBSurface(size, format, options, result); + DFBSurfaceBlittingFlags flags = QDirectFBScreen::hasAlphaChannel(surface) + ? DSBLIT_BLEND_ALPHACHANNEL + : DSBLIT_NOFX; + if (flags & DSBLIT_BLEND_ALPHACHANNEL) + surface->Clear(surface, 0, 0, 0, 0); + + surface->SetBlittingFlags(surface, flags); + surface->Blit(surface, src, 0, 0, 0); +#if (Q_DIRECTFB_VERSION >= 0x010000) + surface->ReleaseSource(surface); +#endif + return surface; +} + +IDirectFBSurface *QDirectFBScreen::createDFBSurface(const QSize &size, + QImage::Format format, + SurfaceCreationOptions options, + DFBResult *result) +{ + DFBSurfaceDescription desc; + memset(&desc, 0, sizeof(DFBSurfaceDescription)); + desc.flags |= DSDESC_WIDTH|DSDESC_HEIGHT; + if (!QDirectFBScreen::initSurfaceDescriptionPixelFormat(&desc, format)) + return 0; + desc.width = size.width(); + desc.height = size.height(); + return createDFBSurface(desc, options, result); +} + +IDirectFBSurface *QDirectFBScreen::createDFBSurface(DFBSurfaceDescription desc, SurfaceCreationOptions options, DFBResult *resultPtr) +{ + DFBResult tmp; + DFBResult &result = (resultPtr ? *resultPtr : tmp); + result = DFB_OK; + IDirectFBSurface *newSurface = 0; + + if (!d_ptr->dfb) { + qWarning("QDirectFBScreen::createDFBSurface() - not connected"); + return 0; + } + + if (d_ptr->directFBFlags & VideoOnly + && !(desc.flags & DSDESC_PREALLOCATED) + && (!(desc.flags & DSDESC_CAPS) || !(desc.caps & DSCAPS_SYSTEMONLY))) { + // Add the video only capability. This means the surface will be created in video ram + if (!(desc.flags & DSDESC_CAPS)) { + desc.caps = DSCAPS_VIDEOONLY; + desc.flags |= DSDESC_CAPS; + } else { + desc.caps |= DSCAPS_VIDEOONLY; + } + result = d_ptr->dfb->CreateSurface(d_ptr->dfb, &desc, &newSurface); + if (result != DFB_OK +#ifdef QT_NO_DEBUG + && (desc.flags & DSDESC_CAPS) && (desc.caps & DSCAPS_PRIMARY) +#endif + ) { + qWarning("QDirectFBScreen::createDFBSurface() Failed to create surface in video memory!\n" + " Flags %0x Caps %0x width %d height %d pixelformat %0x %d preallocated %p %d\n%s", + desc.flags, desc.caps, desc.width, desc.height, + desc.pixelformat, DFB_PIXELFORMAT_INDEX(desc.pixelformat), + desc.preallocated[0].data, desc.preallocated[0].pitch, + DirectFBErrorString(result)); + } + desc.caps &= ~DSCAPS_VIDEOONLY; + } + + if (d_ptr->directFBFlags & SystemOnly) + desc.caps |= DSCAPS_SYSTEMONLY; + + if (!newSurface) + result = d_ptr->dfb->CreateSurface(d_ptr->dfb, &desc, &newSurface); + + if (result != DFB_OK) { + qWarning("QDirectFBScreen::createDFBSurface() Failed!\n" + " Flags %0x Caps %0x width %d height %d pixelformat %0x %d preallocated %p %d\n%s", + desc.flags, desc.caps, desc.width, desc.height, + desc.pixelformat, DFB_PIXELFORMAT_INDEX(desc.pixelformat), + desc.preallocated[0].data, desc.preallocated[0].pitch, + DirectFBErrorString(result)); + return 0; + } + + Q_ASSERT(newSurface); + + if (options & TrackSurface) { + d_ptr->allocatedSurfaces.insert(newSurface); + } + + return newSurface; +} + +#ifdef QT_DIRECTFB_SUBSURFACE +IDirectFBSurface *QDirectFBScreen::getSubSurface(IDirectFBSurface *surface, + const QRect &rect, + SurfaceCreationOptions options, + DFBResult *resultPtr) +{ + Q_ASSERT(!(options & NoPreallocated)); + Q_ASSERT(surface); + DFBResult res; + DFBResult &result = (resultPtr ? *resultPtr : res); + IDirectFBSurface *subSurface = 0; + if (rect.isNull()) { + result = surface->GetSubSurface(surface, 0, &subSurface); + } else { + const DFBRectangle subRect = { rect.x(), rect.y(), rect.width(), rect.height() }; + result = surface->GetSubSurface(surface, &subRect, &subSurface); + } + if (result != DFB_OK) { + DirectFBError("Can't get sub surface", result); + } else if (options & TrackSurface) { + d_ptr->allocatedSurfaces.insert(subSurface); + } + return subSurface; +} +#endif + + +void QDirectFBScreen::releaseDFBSurface(IDirectFBSurface *surface) +{ + Q_ASSERT(QDirectFBScreen::instance()); + Q_ASSERT(surface); + surface->Release(surface); + if (!d_ptr->allocatedSurfaces.remove(surface)) + qWarning("QDirectFBScreen::releaseDFBSurface() - %p not in list", surface); + + //qDebug("Released surface at %p. New count = %d", surface, d_ptr->allocatedSurfaces.count()); +} + +QDirectFBScreen::DirectFBFlags QDirectFBScreen::directFBFlags() const +{ + return d_ptr->directFBFlags; +} + +IDirectFB *QDirectFBScreen::dfb() +{ + return d_ptr->dfb; +} + +#ifdef QT_NO_DIRECTFB_WM +IDirectFBSurface *QDirectFBScreen::primarySurface() +{ + return d_ptr->primarySurface; +} +#endif + +#ifndef QT_NO_DIRECTFB_LAYER +IDirectFBDisplayLayer *QDirectFBScreen::dfbDisplayLayer() +{ + return d_ptr->dfbLayer; +} +#endif + +DFBSurfacePixelFormat QDirectFBScreen::getSurfacePixelFormat(QImage::Format format) +{ + switch (format) { +#ifndef QT_NO_DIRECTFB_PALETTE + case QImage::Format_Indexed8: + return DSPF_LUT8; +#endif + case QImage::Format_RGB888: + return DSPF_RGB24; + case QImage::Format_ARGB4444_Premultiplied: + return DSPF_ARGB4444; +#if (Q_DIRECTFB_VERSION >= 0x010100) + case QImage::Format_RGB444: + return DSPF_RGB444; + case QImage::Format_RGB555: + return DSPF_RGB555; +#endif + case QImage::Format_RGB16: + return DSPF_RGB16; +#if (Q_DIRECTFB_VERSION >= 0x010000) + case QImage::Format_ARGB6666_Premultiplied: + return DSPF_ARGB6666; + case QImage::Format_RGB666: + return DSPF_RGB18; +#endif + case QImage::Format_RGB32: + return DSPF_RGB32; + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB32: + return DSPF_ARGB; + default: + return DSPF_UNKNOWN; + }; +} + +QImage::Format QDirectFBScreen::getImageFormat(IDirectFBSurface *surface) +{ + DFBSurfacePixelFormat format; + surface->GetPixelFormat(surface, &format); + + switch (format) { + case DSPF_LUT8: + return QImage::Format_Indexed8; + case DSPF_RGB24: + return QImage::Format_RGB888; + case DSPF_ARGB4444: + return QImage::Format_ARGB4444_Premultiplied; +#if (Q_DIRECTFB_VERSION >= 0x010100) + case DSPF_RGB444: + return QImage::Format_RGB444; + case DSPF_RGB555: +#endif + case DSPF_ARGB1555: + return QImage::Format_RGB555; + case DSPF_RGB16: + return QImage::Format_RGB16; +#if (Q_DIRECTFB_VERSION >= 0x010000) + case DSPF_ARGB6666: + return QImage::Format_ARGB6666_Premultiplied; + case DSPF_RGB18: + return QImage::Format_RGB666; +#endif + case DSPF_RGB32: + return QImage::Format_RGB32; + case DSPF_ARGB: { + DFBSurfaceCapabilities caps; + const DFBResult result = surface->GetCapabilities(surface, &caps); + Q_ASSERT(result == DFB_OK); + Q_UNUSED(result); + return (caps & DSCAPS_PREMULTIPLIED + ? QImage::Format_ARGB32_Premultiplied + : QImage::Format_ARGB32); } + default: + break; + } + return QImage::Format_Invalid; +} + +DFBSurfaceDescription QDirectFBScreen::getSurfaceDescription(const uint *buffer, + int length) +{ + DFBSurfaceDescription description; + memset(&description, 0, sizeof(DFBSurfaceDescription)); + + description.flags = DSDESC_CAPS|DSDESC_WIDTH|DSDESC_HEIGHT|DSDESC_PIXELFORMAT|DSDESC_PREALLOCATED; + description.caps = DSCAPS_PREMULTIPLIED; + description.width = length; + description.height = 1; + description.pixelformat = DSPF_ARGB; + description.preallocated[0].data = (void*)buffer; + description.preallocated[0].pitch = length * sizeof(uint); + description.preallocated[1].data = 0; + description.preallocated[1].pitch = 0; + return description; +} + +#ifndef QT_NO_DIRECTFB_PALETTE +void QDirectFBScreen::setSurfaceColorTable(IDirectFBSurface *surface, + const QImage &image) +{ + if (!surface) + return; + + const int numColors = image.colorCount(); + if (numColors == 0) + return; + + QVarLengthArray colors(numColors); + for (int i = 0; i < numColors; ++i) { + QRgb c = image.color(i); + colors[i].a = qAlpha(c); + colors[i].r = qRed(c); + colors[i].g = qGreen(c); + colors[i].b = qBlue(c); + } + + IDirectFBPalette *palette; + DFBResult result; + result = surface->GetPalette(surface, &palette); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::setSurfaceColorTable GetPalette", + result); + return; + } + result = palette->SetEntries(palette, colors.data(), numColors, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::setSurfaceColorTable SetEntries", + result); + } + palette->Release(palette); +} + +#endif // QT_NO_DIRECTFB_PALETTE + +#if defined QT_DIRECTFB_CURSOR +class Q_GUI_EXPORT QDirectFBScreenCursor : public QScreenCursor +{ +public: + QDirectFBScreenCursor(); + virtual void set(const QImage &image, int hotx, int hoty); + virtual void move(int x, int y); + virtual void show(); + virtual void hide(); +private: +#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR + ~QDirectFBScreenCursor(); + bool createWindow(); + IDirectFBWindow *window; +#endif + IDirectFBDisplayLayer *layer; +}; + +QDirectFBScreenCursor::QDirectFBScreenCursor() +{ + IDirectFB *fb = QDirectFBScreen::instance()->dfb(); + if (!fb) + qFatal("QDirectFBScreenCursor: DirectFB not initialized"); + + layer = QDirectFBScreen::instance()->dfbDisplayLayer(); + Q_ASSERT(layer); + + enable = false; + hwaccel = true; + supportsAlpha = true; +#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR + window = 0; + DFBResult result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cooperative level", result); + } + result = layer->SetCursorOpacity(layer, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cursor opacity", result); + } + + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cooperative level", result); + } +#endif +} + +#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR +QDirectFBScreenCursor::~QDirectFBScreenCursor() +{ + if (window) { + window->Release(window); + window = 0; + } +} + +bool QDirectFBScreenCursor::createWindow() +{ + Q_ASSERT(!window); + Q_ASSERT(!cursor.isNull()); + DFBWindowDescription description; + memset(&description, 0, sizeof(DFBWindowDescription)); + description.flags = DWDESC_POSX|DWDESC_POSY|DWDESC_WIDTH|DWDESC_HEIGHT|DWDESC_CAPS|DWDESC_PIXELFORMAT|DWDESC_SURFACE_CAPS; + description.width = cursor.width(); + description.height = cursor.height(); + description.posx = pos.x() - hotspot.x(); + description.posy = pos.y() - hotspot.y(); +#if (Q_DIRECTFB_VERSION >= 0x010100) + description.flags |= DWDESC_OPTIONS; + description.options = DWOP_GHOST|DWOP_ALPHACHANNEL; +#endif + description.caps = DWCAPS_NODECORATION|DWCAPS_DOUBLEBUFFER; + const QImage::Format format = QDirectFBScreen::instance()->alphaPixmapFormat(); + description.pixelformat = QDirectFBScreen::getSurfacePixelFormat(format); + if (QDirectFBScreen::isPremultiplied(format)) + description.surface_caps = DSCAPS_PREMULTIPLIED; + + DFBResult result = layer->CreateWindow(layer, &description, &window); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::createWindow: Unable to create window", result); + return false; + } + result = window->SetOpacity(window, 255); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::createWindow: Unable to set opacity ", result); + return false; + } + + result = window->SetStackingClass(window, DWSC_UPPER); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::createWindow: Unable to set stacking class ", result); + return false; + } + + result = window->RaiseToTop(window); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::createWindow: Unable to raise window ", result); + return false; + } + + return true; +} +#endif + +void QDirectFBScreenCursor::move(int x, int y) +{ + pos = QPoint(x, y); +#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR + if (window) { + const QPoint p = pos - hotspot; + DFBResult result = window->MoveTo(window, p.x(), p.y()); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::move: Unable to move window", result); + } + } +#else + layer->WarpCursor(layer, x, y); +#endif +} + +void QDirectFBScreenCursor::hide() +{ + if (enable) { + enable = false; + DFBResult result; +#ifndef QT_DIRECTFB_WINDOW_AS_CURSOR + result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cooperative level", result); + } + result = layer->SetCursorOpacity(layer, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cursor opacity", result); + } + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cooperative level", result); + } +#else + if (window) { + result = window->SetOpacity(window, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set window opacity", result); + } + } +#endif + } +} + +void QDirectFBScreenCursor::show() +{ + if (!enable) { + enable = true; + DFBResult result; + result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cooperative level", result); + } + result = layer->SetCursorOpacity(layer, +#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR + 0 +#else + 255 +#endif + ); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cursor shape", result); + } + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cooperative level", result); + } +#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR + if (window) { + DFBResult result = window->SetOpacity(window, 255); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set window opacity", result); + } + } +#endif + } +} + +void QDirectFBScreenCursor::set(const QImage &image, int hotx, int hoty) +{ + QDirectFBScreen *screen = QDirectFBScreen::instance(); + if (!screen) + return; + + if (image.isNull()) { + cursor = QImage(); + hide(); + } else { + cursor = image.convertToFormat(screen->alphaPixmapFormat()); + size = cursor.size(); + hotspot = QPoint(hotx, hoty); + DFBResult result = DFB_OK; + IDirectFBSurface *surface = screen->createDFBSurface(cursor, screen->alphaPixmapFormat(), + QDirectFBScreen::DontTrackSurface, &result); + if (!surface) { + DirectFBError("QDirectFBScreenCursor::set: Unable to create surface", result); + return; + } +#ifndef QT_DIRECTFB_WINDOW_AS_CURSOR + result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cooperative level", result); + } + result = layer->SetCursorShape(layer, surface, hotx, hoty); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cursor shape", result); + } + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cooperative level", result); + } +#else + if (window || createWindow()) { + QSize windowSize; + result = window->GetSize(window, &windowSize.rwidth(), &windowSize.rheight()); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: " + "Unable to get window size", result); + } + result = window->Resize(window, size.width(), size.height()); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: Unable to resize window", result); + } + + IDirectFBSurface *windowSurface; + result = window->GetSurface(window, &windowSurface); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: Unable to get window surface", result); + } else { + result = windowSurface->Clear(windowSurface, 0, 0, 0, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: Unable to clear surface", result); + } + + result = windowSurface->Blit(windowSurface, surface, 0, 0, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: Unable to blit to surface", result); + } + } + result = windowSurface->Flip(windowSurface, 0, DSFLIP_NONE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: Unable to flip window", result); + } + + windowSurface->Release(windowSurface); + } +#endif + surface->Release(surface); + show(); + } + +} +#endif // QT_DIRECTFB_CURSOR + +QDirectFBScreen::QDirectFBScreen(int display_id) + : QScreen(display_id, DirectFBClass), d_ptr(new QDirectFBScreenPrivate(this)) +{ + QDirectFBScreenPrivate::instance = this; +} + +QDirectFBScreen::~QDirectFBScreen() +{ + if (QDirectFBScreenPrivate::instance == this) + QDirectFBScreenPrivate::instance = 0; + delete d_ptr; +} + +QDirectFBScreen *QDirectFBScreen::instance() +{ + return QDirectFBScreenPrivate::instance; +} + +int QDirectFBScreen::depth(DFBSurfacePixelFormat format) +{ + switch (format) { + case DSPF_A1: + return 1; + case DSPF_A8: + case DSPF_RGB332: + case DSPF_LUT8: + case DSPF_ALUT44: + return 8; + case DSPF_I420: + case DSPF_YV12: + case DSPF_NV12: + case DSPF_NV21: +#if (Q_DIRECTFB_VERSION >= 0x010100) + case DSPF_RGB444: +#endif + return 12; +#if (Q_DIRECTFB_VERSION >= 0x010100) + case DSPF_RGB555: + return 15; +#endif + case DSPF_ARGB1555: + case DSPF_RGB16: + case DSPF_YUY2: + case DSPF_UYVY: + case DSPF_NV16: + case DSPF_ARGB2554: + case DSPF_ARGB4444: + return 16; + case DSPF_RGB24: + return 24; + case DSPF_RGB32: + case DSPF_ARGB: + case DSPF_AiRGB: + return 32; + case DSPF_UNKNOWN: + default: + return 0; + }; + return 0; +} + +int QDirectFBScreen::depth(QImage::Format format) +{ + int depth = 0; + switch(format) { + case QImage::Format_Invalid: + case QImage::NImageFormats: + Q_ASSERT(false); + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + depth = 1; + break; + case QImage::Format_Indexed8: + depth = 8; + break; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + depth = 32; + break; + case QImage::Format_RGB555: + case QImage::Format_RGB16: + case QImage::Format_RGB444: + case QImage::Format_ARGB4444_Premultiplied: + depth = 16; + break; + case QImage::Format_RGB666: + case QImage::Format_ARGB6666_Premultiplied: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_RGB888: + depth = 24; + break; + } + return depth; +} + +void QDirectFBScreenPrivate::setFlipFlags(const QStringList &args) +{ + QRegExp flipRegexp(QLatin1String("^flip=([\\w,]*)$")); + int index = args.indexOf(flipRegexp); + if (index >= 0) { + const QStringList flips = flipRegexp.cap(1).split(QLatin1Char(','), + QString::SkipEmptyParts); + flipFlags = DSFLIP_NONE; + foreach(const QString &flip, flips) { + if (flip == QLatin1String("wait")) + flipFlags |= DSFLIP_WAIT; + else if (flip == QLatin1String("blit")) + flipFlags |= DSFLIP_BLIT; + else if (flip == QLatin1String("onsync")) + flipFlags |= DSFLIP_ONSYNC; + else if (flip == QLatin1String("pipeline")) + flipFlags |= DSFLIP_PIPELINE; + else + qWarning("QDirectFBScreen: Unknown flip argument: %s", + qPrintable(flip)); + } + } else { + flipFlags = DSFLIP_BLIT|DSFLIP_ONSYNC; + } +} + +#ifdef QT_DIRECTFB_WM +void QDirectFBScreenPrivate::onWindowEvent(QWSWindow *window, QWSServer::WindowEvent event) +{ + if (event == QWSServer::Raise) { + QWSWindowSurface *windowSurface = window->windowSurface(); + if (windowSurface && windowSurface->key() == QLatin1String("directfb")) { + static_cast(windowSurface)->raise(); + } + } +} +#endif + +QPixmapData *QDirectFBScreenPrivate::createPixmapData(QPixmapData::PixelType type) const +{ + if (type == QPixmapData::BitmapType) + return QWSGraphicsSystem::createPixmapData(type); + + return new QDirectFBPixmapData(q, type); +} + +#if (Q_DIRECTFB_VERSION >= 0x000923) +#ifdef QT_NO_DEBUG +struct FlagDescription; +static const FlagDescription *accelerationDescriptions = 0; +static const FlagDescription *blitDescriptions = 0; +static const FlagDescription *drawDescriptions = 0; +#else +struct FlagDescription { + const char *name; + uint flag; +}; + +static const FlagDescription accelerationDescriptions[] = { + { "DFXL_NONE", DFXL_NONE }, + { "DFXL_FILLRECTANGLE", DFXL_FILLRECTANGLE }, + { "DFXL_DRAWRECTANGLE", DFXL_DRAWRECTANGLE }, + { "DFXL_DRAWLINE", DFXL_DRAWLINE }, + { "DFXL_FILLTRIANGLE", DFXL_FILLTRIANGLE }, + { "DFXL_BLIT", DFXL_BLIT }, + { "DFXL_STRETCHBLIT", DFXL_STRETCHBLIT }, + { "DFXL_TEXTRIANGLES", DFXL_TEXTRIANGLES }, + { "DFXL_DRAWSTRING", DFXL_DRAWSTRING }, + { 0, 0 } +}; + +static const FlagDescription blitDescriptions[] = { + { "DSBLIT_NOFX", DSBLIT_NOFX }, + { "DSBLIT_BLEND_ALPHACHANNEL", DSBLIT_BLEND_ALPHACHANNEL }, + { "DSBLIT_BLEND_COLORALPHA", DSBLIT_BLEND_COLORALPHA }, + { "DSBLIT_COLORIZE", DSBLIT_COLORIZE }, + { "DSBLIT_SRC_COLORKEY", DSBLIT_SRC_COLORKEY }, + { "DSBLIT_DST_COLORKEY", DSBLIT_DST_COLORKEY }, + { "DSBLIT_SRC_PREMULTIPLY", DSBLIT_SRC_PREMULTIPLY }, + { "DSBLIT_DST_PREMULTIPLY", DSBLIT_DST_PREMULTIPLY }, + { "DSBLIT_DEMULTIPLY", DSBLIT_DEMULTIPLY }, + { "DSBLIT_DEINTERLACE", DSBLIT_DEINTERLACE }, +#if (Q_DIRECTFB_VERSION >= 0x000923) + { "DSBLIT_SRC_PREMULTCOLOR", DSBLIT_SRC_PREMULTCOLOR }, + { "DSBLIT_XOR", DSBLIT_XOR }, +#endif +#if (Q_DIRECTFB_VERSION >= 0x010000) + { "DSBLIT_INDEX_TRANSLATION", DSBLIT_INDEX_TRANSLATION }, +#endif + { 0, 0 } +}; + +static const FlagDescription drawDescriptions[] = { + { "DSDRAW_NOFX", DSDRAW_NOFX }, + { "DSDRAW_BLEND", DSDRAW_BLEND }, + { "DSDRAW_DST_COLORKEY", DSDRAW_DST_COLORKEY }, + { "DSDRAW_SRC_PREMULTIPLY", DSDRAW_SRC_PREMULTIPLY }, + { "DSDRAW_DST_PREMULTIPLY", DSDRAW_DST_PREMULTIPLY }, + { "DSDRAW_DEMULTIPLY", DSDRAW_DEMULTIPLY }, + { "DSDRAW_XOR", DSDRAW_XOR }, + { 0, 0 } +}; +#endif + +static const QByteArray flagDescriptions(uint mask, const FlagDescription *flags) +{ +#ifdef QT_NO_DEBUG + Q_UNUSED(mask); + Q_UNUSED(flags); + return QByteArray(""); +#else + if (!mask) + return flags[0].name; + + QStringList list; + for (int i=1; flags[i].name; ++i) { + if (mask & flags[i].flag) { + list.append(QString::fromLatin1(flags[i].name)); + } + } + Q_ASSERT(!list.isEmpty()); + return (QLatin1Char(' ') + list.join(QLatin1String("|"))).toLatin1(); +#endif +} +static void printDirectFBInfo(IDirectFB *fb, IDirectFBSurface *primarySurface) +{ + DFBResult result; + DFBGraphicsDeviceDescription dev; + + result = fb->GetDeviceDescription(fb, &dev); + if (result != DFB_OK) { + DirectFBError("Error reading graphics device description", result); + return; + } + + DFBSurfacePixelFormat pixelFormat; + primarySurface->GetPixelFormat(primarySurface, &pixelFormat); + + qDebug("Device: %s (%s), Driver: %s v%i.%i (%s) Pixelformat: %d (%d)\n" + "acceleration: 0x%x%s\nblit: 0x%x%s\ndraw: 0x%0x%s\nvideo: %iKB\n", + dev.name, dev.vendor, dev.driver.name, dev.driver.major, + dev.driver.minor, dev.driver.vendor, DFB_PIXELFORMAT_INDEX(pixelFormat), + QDirectFBScreen::getImageFormat(primarySurface), dev.acceleration_mask, + flagDescriptions(dev.acceleration_mask, accelerationDescriptions).constData(), + dev.blitting_flags, flagDescriptions(dev.blitting_flags, blitDescriptions).constData(), + dev.drawing_flags, flagDescriptions(dev.drawing_flags, drawDescriptions).constData(), + (dev.video_memory >> 10)); +} +#endif + +static inline bool setIntOption(const QStringList &arguments, const QString &variable, int *value) +{ + Q_ASSERT(value); + QRegExp rx(QString::fromLatin1("%1=?(\\d+)").arg(variable)); + rx.setCaseSensitivity(Qt::CaseInsensitive); + if (arguments.indexOf(rx) != -1) { + *value = rx.cap(1).toInt(); + return true; + } + return false; +} + +static inline QColor colorFromName(const QString &name) +{ + QRegExp rx(QLatin1String("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])")); + rx.setCaseSensitivity(Qt::CaseInsensitive); + if (rx.exactMatch(name)) { + Q_ASSERT(rx.captureCount() == 4); + int ints[4]; + int i; + for (i=0; i<4; ++i) { + bool ok; + ints[i] = rx.cap(i + 1).toUInt(&ok, 16); + if (!ok || ints[i] > 255) + break; + } + if (i == 4) + return QColor(ints[0], ints[1], ints[2], ints[3]); + } + return QColor(name); +} + +bool QDirectFBScreen::connect(const QString &displaySpec) +{ + DFBResult result = DFB_OK; + + { // pass command line arguments to DirectFB + const QStringList args = QCoreApplication::arguments(); + int argc = args.size(); + char **argv = new char*[argc]; + + for (int i = 0; i < argc; ++i) + argv[i] = qstrdup(args.at(i).toLocal8Bit().constData()); + + result = DirectFBInit(&argc, &argv); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen: error initializing DirectFB", + result); + } + delete[] argv; + } + + const QStringList displayArgs = displaySpec.split(QLatin1Char(':'), + QString::SkipEmptyParts); + + d_ptr->setFlipFlags(displayArgs); + + result = DirectFBCreate(&d_ptr->dfb); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen: error creating DirectFB interface", + result); + return false; + } + + if (displayArgs.contains(QLatin1String("videoonly"), Qt::CaseInsensitive)) + d_ptr->directFBFlags |= VideoOnly; + + if (displayArgs.contains(QLatin1String("systemonly"), Qt::CaseInsensitive)) { + if (d_ptr->directFBFlags & VideoOnly) { + qWarning("QDirectFBScreen: error. videoonly and systemonly are mutually exclusive"); + } else { + d_ptr->directFBFlags |= SystemOnly; + } + } + + if (displayArgs.contains(QLatin1String("boundingrectflip"), Qt::CaseInsensitive)) { + d_ptr->directFBFlags |= BoundingRectFlip; + } else if (displayArgs.contains(QLatin1String("nopartialflip"), Qt::CaseInsensitive)) { + d_ptr->directFBFlags |= NoPartialFlip; + } + +#ifdef QT_DIRECTFB_IMAGECACHE + int imageCacheSize = 4 * 1024 * 1024; // 4 MB + setIntOption(displayArgs, QLatin1String("imagecachesize"), &imageCacheSize); + QDirectFBPaintEngine::initImageCache(imageCacheSize); +#endif + +#ifndef QT_NO_DIRECTFB_WM + if (displayArgs.contains(QLatin1String("fullscreen"))) +#endif + d_ptr->dfb->SetCooperativeLevel(d_ptr->dfb, DFSCL_FULLSCREEN); + + const bool forcePremultiplied = displayArgs.contains(QLatin1String("forcepremultiplied"), Qt::CaseInsensitive); + + DFBSurfaceDescription description; + memset(&description, 0, sizeof(DFBSurfaceDescription)); + IDirectFBSurface *surface; + +#ifdef QT_NO_DIRECTFB_WM + description.flags = DSDESC_CAPS; + if (::setIntOption(displayArgs, QLatin1String("width"), &description.width)) + description.flags |= DSDESC_WIDTH; + if (::setIntOption(displayArgs, QLatin1String("height"), &description.height)) + description.flags |= DSDESC_HEIGHT; + + description.caps = DSCAPS_PRIMARY|DSCAPS_DOUBLE; + struct { + const char *name; + const DFBSurfaceCapabilities cap; + } const capabilities[] = { + { "static_alloc", DSCAPS_STATIC_ALLOC }, + { "triplebuffer", DSCAPS_TRIPLE }, + { "interlaced", DSCAPS_INTERLACED }, + { "separated", DSCAPS_SEPARATED }, +// { "depthbuffer", DSCAPS_DEPTH }, // only makes sense with TextureTriangles which are not supported + { 0, DSCAPS_NONE } + }; + for (int i=0; capabilities[i].name; ++i) { + if (displayArgs.contains(QString::fromLatin1(capabilities[i].name), Qt::CaseInsensitive)) + description.caps |= capabilities[i].cap; + } + + if (forcePremultiplied) { + description.caps |= DSCAPS_PREMULTIPLIED; + } + + // We don't track the primary surface as it's released in disconnect + d_ptr->primarySurface = createDFBSurface(description, DontTrackSurface, &result); + if (!d_ptr->primarySurface) { + DirectFBError("QDirectFBScreen: error creating primary surface", + result); + return false; + } + + surface = d_ptr->primarySurface; +#else + description.flags = DSDESC_WIDTH|DSDESC_HEIGHT; + description.width = description.height = 1; + surface = createDFBSurface(description, DontTrackSurface, &result); + if (!surface) { + DirectFBError("QDirectFBScreen: error creating surface", result); + return false; + } +#endif + // Work out what format we're going to use for surfaces with an alpha channel + QImage::Format pixelFormat = QDirectFBScreen::getImageFormat(surface); + d_ptr->alphaPixmapFormat = pixelFormat; + + switch (pixelFormat) { + case QImage::Format_RGB666: + d_ptr->alphaPixmapFormat = QImage::Format_ARGB6666_Premultiplied; + break; + case QImage::Format_RGB444: + d_ptr->alphaPixmapFormat = QImage::Format_ARGB4444_Premultiplied; + break; + case QImage::Format_RGB32: + pixelFormat = d_ptr->alphaPixmapFormat = QImage::Format_ARGB32_Premultiplied; + // ### Format_RGB32 doesn't work so well with Qt. Force ARGB32 for windows/pixmaps + break; + case QImage::Format_Indexed8: + qWarning("QDirectFBScreen::connect(). Qt/DirectFB does not work with the LUT8 pixelformat."); + return false; + case QImage::NImageFormats: + case QImage::Format_Invalid: + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + case QImage::Format_RGB888: + case QImage::Format_RGB16: + case QImage::Format_RGB555: + d_ptr->alphaPixmapFormat = QImage::Format_ARGB32_Premultiplied; + break; + case QImage::Format_ARGB32: + if (forcePremultiplied) + d_ptr->alphaPixmapFormat = pixelFormat = QImage::Format_ARGB32_Premultiplied; + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB4444_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB6666_Premultiplied: + // works already + break; + } + setPixelFormat(pixelFormat); + QScreen::d = QDirectFBScreen::depth(pixelFormat); + data = 0; + lstep = 0; + size = 0; + + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect: " + "Unable to get screen!", result); + return false; + } + const QString qws_size = QString::fromLatin1(qgetenv("QWS_SIZE")); + if (!qws_size.isEmpty()) { + QRegExp rx(QLatin1String("(\\d+)x(\\d+)")); + if (!rx.exactMatch(qws_size)) { + qWarning("QDirectFBScreen::connect: Can't parse QWS_SIZE=\"%s\"", qPrintable(qws_size)); + } else { + int *ints[2] = { &w, &h }; + for (int i=0; i<2; ++i) { + *ints[i] = rx.cap(i + 1).toInt(); + if (*ints[i] <= 0) { + qWarning("QDirectFBScreen::connect: %s is not a positive integer", + qPrintable(rx.cap(i + 1))); + w = h = 0; + break; + } + } + } + } + + setIntOption(displayArgs, QLatin1String("width"), &w); + setIntOption(displayArgs, QLatin1String("height"), &h); + +#ifndef QT_NO_DIRECTFB_LAYER + int layerId = DLID_PRIMARY; + setIntOption(displayArgs, QLatin1String("layerid"), &layerId); + + result = d_ptr->dfb->GetDisplayLayer(d_ptr->dfb, static_cast(layerId), + &d_ptr->dfbLayer); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect: " + "Unable to get display layer!", result); + return false; + } + result = d_ptr->dfbLayer->GetScreen(d_ptr->dfbLayer, &d_ptr->dfbScreen); +#else + result = d_ptr->dfb->GetScreen(d_ptr->dfb, 0, &d_ptr->dfbScreen); +#endif + + if (w <= 0 || h <= 0) { +#ifdef QT_NO_DIRECTFB_WM + result = d_ptr->primarySurface->GetSize(d_ptr->primarySurface, &w, &h); +#elif (Q_DIRECTFB_VERSION >= 0x010000) + IDirectFBSurface *layerSurface; + if (d_ptr->dfbLayer->GetSurface(d_ptr->dfbLayer, &layerSurface) == DFB_OK) { + result = layerSurface->GetSize(layerSurface, &w, &h); + layerSurface->Release(layerSurface); + } + if (w <= 0 || h <= 0) { + result = d_ptr->dfbScreen->GetSize(d_ptr->dfbScreen, &w, &h); + } +#else + qWarning("QDirectFBScreen::connect: DirectFB versions prior to 1.0 do not offer a way\n" + "query the size of the primary surface in windowed mode. You have to specify\n" + "the size of the display using QWS_SIZE=[0-9]x[0-9] or\n" + "QWS_DISPLAY=directfb:width=[0-9]:height=[0-9]"); + return false; +#endif + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect: " + "Unable to get screen size!", result); + return false; + } + } + + + dw = w; + dh = h; + + Q_ASSERT(dw != 0 && dh != 0); + + physWidth = physHeight = -1; + setIntOption(displayArgs, QLatin1String("mmWidth"), &physWidth); + setIntOption(displayArgs, QLatin1String("mmHeight"), &physHeight); + const int dpi = 72; + if (physWidth < 0) + physWidth = qRound(dw * 25.4 / dpi); + if (physHeight < 0) + physHeight = qRound(dh * 25.4 / dpi); + + setGraphicsSystem(d_ptr); + +#if (Q_DIRECTFB_VERSION >= 0x000923) + if (displayArgs.contains(QLatin1String("debug"), Qt::CaseInsensitive)) + printDirectFBInfo(d_ptr->dfb, surface); +#endif +#ifdef QT_DIRECTFB_WM + surface->Release(surface); + QColor backgroundColor; +#else + QColor &backgroundColor = d_ptr->backgroundColor; +#endif + + QRegExp backgroundColorRegExp(QLatin1String("bgcolor=(.+)")); + backgroundColorRegExp.setCaseSensitivity(Qt::CaseInsensitive); + if (displayArgs.indexOf(backgroundColorRegExp) != -1) { + backgroundColor = colorFromName(backgroundColorRegExp.cap(1)); + } +#ifdef QT_NO_DIRECTFB_WM + if (!backgroundColor.isValid()) + backgroundColor = Qt::green; + d_ptr->primarySurface->Clear(d_ptr->primarySurface, backgroundColor.red(), + backgroundColor.green(), backgroundColor.blue(), + backgroundColor.alpha()); + d_ptr->primarySurface->Flip(d_ptr->primarySurface, 0, d_ptr->flipFlags); +#else + if (backgroundColor.isValid()) { + DFBResult result = d_ptr->dfbLayer->SetCooperativeLevel(d_ptr->dfbLayer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect " + "Unable to set cooperative level", result); + } + result = d_ptr->dfbLayer->SetBackgroundColor(d_ptr->dfbLayer, backgroundColor.red(), backgroundColor.green(), + backgroundColor.blue(), backgroundColor.alpha()); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::connect: " + "Unable to set background color", result); + } + + result = d_ptr->dfbLayer->SetBackgroundMode(d_ptr->dfbLayer, DLBM_COLOR); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::connect: " + "Unable to set background mode", result); + } + + result = d_ptr->dfbLayer->SetCooperativeLevel(d_ptr->dfbLayer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect " + "Unable to set cooperative level", result); + } + + } +#endif + + return true; +} + +void QDirectFBScreen::disconnect() +{ +#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE + if (d_ptr->imageProvider) + d_ptr->imageProvider->Release(d_ptr->imageProvider); +#endif +#ifdef QT_NO_DIRECTFB_WM + d_ptr->primarySurface->Release(d_ptr->primarySurface); + d_ptr->primarySurface = 0; +#endif + + foreach (IDirectFBSurface *surf, d_ptr->allocatedSurfaces) + surf->Release(surf); + d_ptr->allocatedSurfaces.clear(); + +#ifndef QT_NO_DIRECTFB_LAYER + d_ptr->dfbLayer->Release(d_ptr->dfbLayer); + d_ptr->dfbLayer = 0; +#endif + + d_ptr->dfbScreen->Release(d_ptr->dfbScreen); + d_ptr->dfbScreen = 0; + + d_ptr->dfb->Release(d_ptr->dfb); + d_ptr->dfb = 0; +} + +bool QDirectFBScreen::initDevice() +{ +#ifndef QT_NO_DIRECTFB_MOUSE + if (qgetenv("QWS_MOUSE_PROTO").isEmpty()) { + QWSServer::instance()->setDefaultMouse("None"); + d_ptr->mouse = new QDirectFBMouseHandler; + } +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + if (qgetenv("QWS_KEYBOARD").isEmpty()) { + QWSServer::instance()->setDefaultKeyboard("None"); + d_ptr->keyboard = new QDirectFBKeyboardHandler(QString()); + } +#endif + +#ifdef QT_DIRECTFB_CURSOR + qt_screencursor = new QDirectFBScreenCursor; +#elif !defined QT_NO_QWS_CURSOR + QScreenCursor::initSoftwareCursor(); +#endif + return true; +} + +void QDirectFBScreen::shutdownDevice() +{ +#ifndef QT_NO_DIRECTFB_MOUSE + delete d_ptr->mouse; + d_ptr->mouse = 0; +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + delete d_ptr->keyboard; + d_ptr->keyboard = 0; +#endif + +#ifndef QT_NO_QWS_CURSOR + delete qt_screencursor; + qt_screencursor = 0; +#endif +} + +void QDirectFBScreen::setMode(int width, int height, int depth) +{ + d_ptr->dfb->SetVideoMode(d_ptr->dfb, width, height, depth); +} + +void QDirectFBScreen::blank(bool on) +{ + d_ptr->dfbScreen->SetPowerMode(d_ptr->dfbScreen, + (on ? DSPM_ON : DSPM_SUSPEND)); +} + +QWSWindowSurface *QDirectFBScreen::createSurface(QWidget *widget) const +{ +#ifdef QT_NO_DIRECTFB_WM + if (QApplication::type() == QApplication::GuiServer) { + return new QDirectFBWindowSurface(d_ptr->flipFlags, const_cast(this), widget); + } else { + return QScreen::createSurface(widget); + } +#else + return new QDirectFBWindowSurface(d_ptr->flipFlags, const_cast(this), widget); +#endif +} + +QWSWindowSurface *QDirectFBScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("directfb")) { + return new QDirectFBWindowSurface(d_ptr->flipFlags, const_cast(this)); + } + return QScreen::createSurface(key); +} + +#if defined QT_NO_DIRECTFB_WM +struct PaintCommand { + PaintCommand() : dfbSurface(0), windowOpacity(255), blittingFlags(DSBLIT_NOFX) {} + IDirectFBSurface *dfbSurface; + QImage image; + QPoint windowPosition; + QRegion source; + quint8 windowOpacity; + DFBSurfaceBlittingFlags blittingFlags; +}; + +static inline void initParameters(DFBRectangle &source, const QRect &sourceGlobal, const QPoint &pos) +{ + source.x = sourceGlobal.x() - pos.x(); + source.y = sourceGlobal.y() - pos.y(); + source.w = sourceGlobal.width(); + source.h = sourceGlobal.height(); +} +#endif + +void QDirectFBScreen::exposeRegion(QRegion r, int) +{ + Q_UNUSED(r); +#if defined QT_NO_DIRECTFB_WM + + r &= region(); + if (r.isEmpty()) { + return; + } + r = r.boundingRect(); + + IDirectFBSurface *primary = d_ptr->primarySurface; + const QList windows = QWSServer::instance()->clientWindows(); + QVarLengthArray commands(windows.size()); + QRegion region = r; + int idx = 0; + for (int i=0; iwindowSurface(); + if (!surface) + continue; + + const QRect windowGeometry = surface->geometry(); + const QRegion intersection = region & windowGeometry; + if (intersection.isEmpty()) { + continue; + } + + PaintCommand &cmd = commands[idx]; + + if (surface->key() == QLatin1String("directfb")) { + const QDirectFBWindowSurface *ws = static_cast(surface); + cmd.dfbSurface = ws->directFBSurface(); + + if (!cmd.dfbSurface) { + continue; + } + } else { + cmd.image = surface->image(); + if (cmd.image.isNull()) { + continue; + } + } + ++idx; + + cmd.windowPosition = windowGeometry.topLeft(); + cmd.source = intersection; + if (windows.at(i)->isOpaque()) { + region -= intersection; + if (region.isEmpty()) + break; + } else { + cmd.windowOpacity = windows.at(i)->opacity(); + cmd.blittingFlags = cmd.windowOpacity == 255 + ? DSBLIT_BLEND_ALPHACHANNEL + : (DSBLIT_BLEND_ALPHACHANNEL|DSBLIT_BLEND_COLORALPHA); + } + } + + solidFill(d_ptr->backgroundColor, region); + + while (idx > 0) { + const PaintCommand &cmd = commands[--idx]; + Q_ASSERT(cmd.dfbSurface || !cmd.image.isNull()); + IDirectFBSurface *surface; + if (cmd.dfbSurface) { + surface = cmd.dfbSurface; + } else { + Q_ASSERT(!cmd.image.isNull()); + DFBResult result; + surface = createDFBSurface(cmd.image, cmd.image.format(), DontTrackSurface, &result); + Q_ASSERT((result != DFB_OK) == !surface); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::exposeRegion: Can't create surface from image", result); + continue; + } + } + + primary->SetBlittingFlags(primary, cmd.blittingFlags); + if (cmd.blittingFlags & DSBLIT_BLEND_COLORALPHA) { + primary->SetColor(primary, 0xff, 0xff, 0xff, cmd.windowOpacity); + } + const QRegion ®ion = cmd.source; + const int rectCount = region.rectCount(); + DFBRectangle source; + if (rectCount == 1) { + ::initParameters(source, region.boundingRect(), cmd.windowPosition); + primary->Blit(primary, surface, &source, cmd.windowPosition.x() + source.x, cmd.windowPosition.y() + source.y); + } else { + const QVector rects = region.rects(); + for (int i=0; iBlit(primary, surface, &source, cmd.windowPosition.x() + source.x, cmd.windowPosition.y() + source.y); + } + } + if (surface != cmd.dfbSurface) { + surface->Release(surface); + } + } + + primary->SetColor(primary, 0xff, 0xff, 0xff, 0xff); + +#if defined QT_NO_DIRECTFB_CURSOR and !defined QT_NO_QWS_CURSOR + if (QScreenCursor *cursor = QScreenCursor::instance()) { + const QRect cursorRectangle = cursor->boundingRect(); + if (cursor->isVisible() && !cursor->isAccelerated() && r.intersects(cursorRectangle)) { + const QImage image = cursor->image(); + if (image.cacheKey() != d_ptr->cursorImageKey) { + if (d_ptr->cursorSurface) { + releaseDFBSurface(d_ptr->cursorSurface); + } + d_ptr->cursorSurface = createDFBSurface(image, image.format(), QDirectFBScreen::TrackSurface); + d_ptr->cursorImageKey = image.cacheKey(); + } + + Q_ASSERT(d_ptr->cursorSurface); + primary->SetBlittingFlags(primary, DSBLIT_BLEND_ALPHACHANNEL); + primary->Blit(primary, d_ptr->cursorSurface, 0, cursorRectangle.x(), cursorRectangle.y()); + } + } +#endif + flipSurface(primary, d_ptr->flipFlags, r, QPoint()); + primary->SetBlittingFlags(primary, DSBLIT_NOFX); +#endif +} + +void QDirectFBScreen::solidFill(const QColor &color, const QRegion ®ion) +{ +#ifdef QT_DIRECTFB_WM + Q_UNUSED(color); + Q_UNUSED(region); +#else + QDirectFBScreen::solidFill(d_ptr->primarySurface, color, region); +#endif +} + +static inline void clearRect(IDirectFBSurface *surface, const QColor &color, const QRect &rect) +{ + Q_ASSERT(surface); + const DFBRegion region = { rect.left(), rect.top(), rect.right(), rect.bottom() }; + // could just reinterpret_cast this to a DFBRegion + surface->SetClip(surface, ®ion); + surface->Clear(surface, color.red(), color.green(), color.blue(), color.alpha()); +} + +void QDirectFBScreen::solidFill(IDirectFBSurface *surface, const QColor &color, const QRegion ®ion) +{ + if (region.isEmpty()) + return; + + const int n = region.rectCount(); + if (n == 1) { + clearRect(surface, color, region.boundingRect()); + } else { + const QVector rects = region.rects(); + for (int i=0; iSetClip(surface, 0); +} + +QImage::Format QDirectFBScreen::alphaPixmapFormat() const +{ + return d_ptr->alphaPixmapFormat; +} + +bool QDirectFBScreen::initSurfaceDescriptionPixelFormat(DFBSurfaceDescription *description, + QImage::Format format) +{ + const DFBSurfacePixelFormat pixelformat = QDirectFBScreen::getSurfacePixelFormat(format); + if (pixelformat == DSPF_UNKNOWN) + return false; + description->flags |= DSDESC_PIXELFORMAT; + description->pixelformat = pixelformat; + if (QDirectFBScreen::isPremultiplied(format)) { + if (!(description->flags & DSDESC_CAPS)) { + description->caps = DSCAPS_PREMULTIPLIED; + description->flags |= DSDESC_CAPS; + } else { + description->caps |= DSCAPS_PREMULTIPLIED; + } + } + return true; +} + +uchar *QDirectFBScreen::lockSurface(IDirectFBSurface *surface, DFBSurfaceLockFlags flags, int *bpl) +{ + void *mem = 0; + const DFBResult result = surface->Lock(surface, flags, &mem, bpl); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::lockSurface()", result); + } + + return reinterpret_cast(mem); +} + +static inline bool isFullUpdate(IDirectFBSurface *surface, const QRegion ®ion, const QPoint &offset) +{ + if (offset == QPoint(0, 0) && region.rectCount() == 1) { + QSize size; + surface->GetSize(surface, &size.rwidth(), &size.rheight()); + if (region.boundingRect().size() == size) + return true; + } + return false; +} + +void QDirectFBScreen::flipSurface(IDirectFBSurface *surface, DFBSurfaceFlipFlags flipFlags, + const QRegion ®ion, const QPoint &offset) +{ + if (d_ptr->directFBFlags & NoPartialFlip + || (!(flipFlags & DSFLIP_BLIT) && QT_PREPEND_NAMESPACE(isFullUpdate(surface, region, offset)))) { + surface->Flip(surface, 0, flipFlags); + } else { + if (!(d_ptr->directFBFlags & BoundingRectFlip) && region.rectCount() > 1) { + const QVector rects = region.rects(); + const DFBSurfaceFlipFlags nonWaitFlags = flipFlags & ~DSFLIP_WAIT; + for (int i=0; iFlip(surface, &dfbReg, i + 1 < rects.size() ? nonWaitFlags : flipFlags); + } + } else { + const QRect r = region.boundingRect(); + const DFBRegion dfbReg = { r.x() + offset.x(), r.y() + offset.y(), + r.right() + offset.x(), + r.bottom() + offset.y() }; + surface->Flip(surface, &dfbReg, flipFlags); + } + } +} + +#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE +void QDirectFBScreen::setDirectFBImageProvider(IDirectFBImageProvider *provider) +{ + Q_ASSERT(provider); + if (d_ptr->imageProvider) + d_ptr->imageProvider->Release(d_ptr->imageProvider); + d_ptr->imageProvider = provider; +} +#endif + +void QDirectFBScreen::waitIdle() +{ + d_ptr->dfb->WaitIdle(d_ptr->dfb); +} + +#ifdef QT_DIRECTFB_WM +IDirectFBWindow *QDirectFBScreen::windowForWidget(const QWidget *widget) const +{ + if (widget) { + const QWSWindowSurface *surface = static_cast(widget->windowSurface()); + if (surface && surface->key() == QLatin1String("directfb")) { + return static_cast(surface)->directFBWindow(); + } + } + return 0; +} +#endif + +IDirectFBSurface * QDirectFBScreen::surfaceForWidget(const QWidget *widget, QRect *rect) const +{ + Q_ASSERT(widget); + if (!widget->isVisible() || widget->size().isNull()) + return 0; + + const QWSWindowSurface *surface = static_cast(widget->windowSurface()); + if (surface && surface->key() == QLatin1String("directfb")) { + return static_cast(surface)->surfaceForWidget(widget, rect); + } + return 0; +} + +#ifdef QT_DIRECTFB_SUBSURFACE +IDirectFBSurface *QDirectFBScreen::subSurfaceForWidget(const QWidget *widget, const QRect &area) const +{ + Q_ASSERT(widget); + QRect rect; + IDirectFBSurface *surface = surfaceForWidget(widget, &rect); + IDirectFBSurface *subSurface = 0; + if (surface) { + if (!area.isNull()) + rect &= area.translated(widget->mapTo(widget->window(), QPoint(0, 0))); + if (!rect.isNull()) { + const DFBRectangle subRect = { rect.x(), rect.y(), rect.width(), rect.height() }; + const DFBResult result = surface->GetSubSurface(surface, &subRect, &subSurface); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::subSurface(): Can't get sub surface", result); + } + } + } + return subSurface; +} +#endif + +Q_GUI_EXPORT IDirectFBSurface *qt_directfb_surface_for_widget(const QWidget *widget, QRect *rect) +{ + return QDirectFBScreen::instance() ? QDirectFBScreen::instance()->surfaceForWidget(widget, rect) : 0; +} +#ifdef QT_DIRECTFB_SUBSURFACE +Q_GUI_EXPORT IDirectFBSurface *qt_directfb_subsurface_for_widget(const QWidget *widget, const QRect &area) +{ + return QDirectFBScreen::instance() ? QDirectFBScreen::instance()->subSurfaceForWidget(widget, area) : 0; +} +#endif +#ifdef QT_DIRECTFB_WM +Q_GUI_EXPORT IDirectFBWindow *qt_directfb_window_for_widget(const QWidget *widget) +{ + return QDirectFBScreen::instance() ? QDirectFBScreen::instance()->windowForWidget(widget) : 0; +} + +#endif + +QT_END_NAMESPACE + +#include "qdirectfbscreen.moc" +#endif // QT_NO_QWS_DIRECTFB + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h new file mode 100644 index 0000000000..0e9098df3b --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QDIRECTFBSCREEN_H +#define QDIRECTFBSCREEN_H + +#include +#ifndef QT_NO_QWS_DIRECTFB +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined QT_DIRECTFB_SUBSURFACE && !defined QT_NO_DIRECTFB_SUBSURFACE +#define QT_NO_DIRECTFB_SUBSURFACE +#endif +#if !defined QT_NO_DIRECTFB_LAYER && !defined QT_DIRECTFB_LAYER +#define QT_DIRECTFB_LAYER +#endif +#if !defined QT_NO_DIRECTFB_WM && !defined QT_DIRECTFB_WM +#define QT_DIRECTFB_WM +#endif +#if !defined QT_DIRECTFB_IMAGECACHE && !defined QT_NO_DIRECTFB_IMAGECACHE +#define QT_NO_DIRECTFB_IMAGECACHE +#endif +#if !defined QT_NO_DIRECTFB_IMAGEPROVIDER && !defined QT_DIRECTFB_IMAGEPROVIDER +#define QT_DIRECTFB_IMAGEPROVIDER +#endif +#if !defined QT_NO_DIRECTFB_STRETCHBLIT && !defined QT_DIRECTFB_STRETCHBLIT +#define QT_DIRECTFB_STRETCHBLIT +#endif +#if !defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE && !defined QT_NO_DIRECTFB_IMAGEPROVIDER_KEEPALIVE +#define QT_NO_DIRECTFB_IMAGEPROVIDER_KEEPALIVE +#endif +#if !defined QT_DIRECTFB_WINDOW_AS_CURSOR && !defined QT_NO_DIRECTFB_WINDOW_AS_CURSOR +#define QT_NO_DIRECTFB_WINDOW_AS_CURSOR +#endif +#if !defined QT_DIRECTFB_PALETTE && !defined QT_NO_DIRECTFB_PALETTE +#define QT_NO_DIRECTFB_PALETTE +#endif +#if !defined QT_NO_DIRECTFB_PREALLOCATED && !defined QT_DIRECTFB_PREALLOCATED +#define QT_DIRECTFB_PREALLOCATED +#endif +#if !defined QT_NO_DIRECTFB_MOUSE && !defined QT_DIRECTFB_MOUSE +#define QT_DIRECTFB_MOUSE +#endif +#if !defined QT_NO_DIRECTFB_KEYBOARD && !defined QT_DIRECTFB_KEYBOARD +#define QT_DIRECTFB_KEYBOARD +#endif +#if !defined QT_NO_DIRECTFB_OPAQUE_DETECTION && !defined QT_DIRECTFB_OPAQUE_DETECTION +#define QT_DIRECTFB_OPAQUE_DETECTION +#endif +#ifndef QT_NO_QWS_CURSOR +#if defined QT_DIRECTFB_WM && defined QT_DIRECTFB_WINDOW_AS_CURSOR +#define QT_DIRECTFB_CURSOR +#elif defined QT_DIRECTFB_LAYER +#define QT_DIRECTFB_CURSOR +#endif +#endif +#ifndef QT_DIRECTFB_CURSOR +#define QT_NO_DIRECTFB_CURSOR +#endif +#if defined QT_NO_DIRECTFB_LAYER && defined QT_DIRECTFB_WM +#error QT_NO_DIRECTFB_LAYER requires QT_NO_DIRECTFB_WM +#endif +#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE && defined QT_NO_DIRECTFB_IMAGEPROVIDER +#error QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE requires QT_DIRECTFB_IMAGEPROVIDER to be defined +#endif +#if defined QT_DIRECTFB_WINDOW_AS_CURSOR && defined QT_NO_DIRECTFB_WM +#error QT_DIRECTFB_WINDOW_AS_CURSOR requires QT_DIRECTFB_WM to be defined +#endif + +#define Q_DIRECTFB_VERSION ((DIRECTFB_MAJOR_VERSION << 16) | (DIRECTFB_MINOR_VERSION << 8) | DIRECTFB_MICRO_VERSION) + +#define DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(F) \ + static inline F operator~(F f) { return F(~int(f)); } \ + static inline F operator&(F left, F right) { return F(int(left) & int(right)); } \ + static inline F operator|(F left, F right) { return F(int(left) | int(right)); } \ + static inline F &operator|=(F &left, F right) { left = (left | right); return left; } \ + static inline F &operator&=(F &left, F right) { left = (left & right); return left; } + +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBInputDeviceCapabilities); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBWindowDescriptionFlags); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBWindowCapabilities); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBWindowOptions); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBSurfaceDescriptionFlags); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBSurfaceCapabilities); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBSurfaceLockFlags); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBSurfaceBlittingFlags); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBSurfaceDrawingFlags); +DIRECTFB_DECLARE_OPERATORS_FOR_FLAGS(DFBSurfaceFlipFlags); + +class QDirectFBScreenPrivate; +class Q_GUI_EXPORT QDirectFBScreen : public QScreen +{ +public: + QDirectFBScreen(int display_id); + ~QDirectFBScreen(); + + enum DirectFBFlag { + NoFlags = 0x00, + VideoOnly = 0x01, + SystemOnly = 0x02, + BoundingRectFlip = 0x04, + NoPartialFlip = 0x08 + }; + + Q_DECLARE_FLAGS(DirectFBFlags, DirectFBFlag); + + DirectFBFlags directFBFlags() const; + + bool connect(const QString &displaySpec); + void disconnect(); + bool initDevice(); + void shutdownDevice(); + + void exposeRegion(QRegion r, int changing); + void solidFill(const QColor &color, const QRegion ®ion); + static void solidFill(IDirectFBSurface *surface, const QColor &color, const QRegion ®ion); + + void setMode(int width, int height, int depth); + void blank(bool on); + + QWSWindowSurface *createSurface(QWidget *widget) const; + QWSWindowSurface *createSurface(const QString &key) const; + + static QDirectFBScreen *instance(); + void waitIdle(); + IDirectFBSurface *surfaceForWidget(const QWidget *widget, QRect *rect) const; +#ifdef QT_DIRECTFB_SUBSURFACE + IDirectFBSurface *subSurfaceForWidget(const QWidget *widget, const QRect &area = QRect()) const; +#endif + IDirectFB *dfb(); +#ifdef QT_DIRECTFB_WM + IDirectFBWindow *windowForWidget(const QWidget *widget) const; +#else + IDirectFBSurface *primarySurface(); +#endif +#ifndef QT_NO_DIRECTFB_LAYER + IDirectFBDisplayLayer *dfbDisplayLayer(); +#endif + + // Track surface creation/release so we can release all on exit + enum SurfaceCreationOption { + DontTrackSurface = 0x1, + TrackSurface = 0x2, + NoPreallocated = 0x4 + }; + Q_DECLARE_FLAGS(SurfaceCreationOptions, SurfaceCreationOption); + IDirectFBSurface *createDFBSurface(const QImage &image, + QImage::Format format, + SurfaceCreationOptions options, + DFBResult *result = 0); + IDirectFBSurface *createDFBSurface(const QSize &size, + QImage::Format format, + SurfaceCreationOptions options, + DFBResult *result = 0); + IDirectFBSurface *copyDFBSurface(IDirectFBSurface *src, + QImage::Format format, + SurfaceCreationOptions options, + DFBResult *result = 0); + IDirectFBSurface *createDFBSurface(DFBSurfaceDescription desc, + SurfaceCreationOptions options, + DFBResult *result); +#ifdef QT_DIRECTFB_SUBSURFACE + IDirectFBSurface *getSubSurface(IDirectFBSurface *surface, + const QRect &rect, + SurfaceCreationOptions options, + DFBResult *result); +#endif + + void flipSurface(IDirectFBSurface *surface, DFBSurfaceFlipFlags flipFlags, + const QRegion ®ion, const QPoint &offset); + void releaseDFBSurface(IDirectFBSurface *surface); + + using QScreen::depth; + static int depth(DFBSurfacePixelFormat format); + static int depth(QImage::Format format); + + static DFBSurfacePixelFormat getSurfacePixelFormat(QImage::Format format); + static DFBSurfaceDescription getSurfaceDescription(const uint *buffer, + int length); + static QImage::Format getImageFormat(IDirectFBSurface *surface); + static bool initSurfaceDescriptionPixelFormat(DFBSurfaceDescription *description, QImage::Format format); + static inline bool isPremultiplied(QImage::Format format); + static inline bool hasAlphaChannel(DFBSurfacePixelFormat format); + static inline bool hasAlphaChannel(IDirectFBSurface *surface); + QImage::Format alphaPixmapFormat() const; + +#ifndef QT_NO_DIRECTFB_PALETTE + static void setSurfaceColorTable(IDirectFBSurface *surface, + const QImage &image); +#endif + + static uchar *lockSurface(IDirectFBSurface *surface, DFBSurfaceLockFlags flags, int *bpl = 0); +#if defined QT_DIRECTFB_IMAGEPROVIDER && defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE + void setDirectFBImageProvider(IDirectFBImageProvider *provider); +#endif +private: + QDirectFBScreenPrivate *d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDirectFBScreen::SurfaceCreationOptions); +Q_DECLARE_OPERATORS_FOR_FLAGS(QDirectFBScreen::DirectFBFlags); + +inline bool QDirectFBScreen::isPremultiplied(QImage::Format format) +{ + switch (format) { + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB6666_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_ARGB4444_Premultiplied: + return true; + default: + break; + } + return false; +} + +inline bool QDirectFBScreen::hasAlphaChannel(DFBSurfacePixelFormat format) +{ + switch (format) { + case DSPF_ARGB1555: + case DSPF_ARGB: + case DSPF_LUT8: + case DSPF_AiRGB: + case DSPF_A1: + case DSPF_ARGB2554: + case DSPF_ARGB4444: +#if (Q_DIRECTFB_VERSION >= 0x000923) + case DSPF_AYUV: +#endif +#if (Q_DIRECTFB_VERSION >= 0x010000) + case DSPF_A4: + case DSPF_ARGB1666: + case DSPF_ARGB6666: + case DSPF_LUT2: +#endif + return true; + default: + return false; + } +} + +inline bool QDirectFBScreen::hasAlphaChannel(IDirectFBSurface *surface) +{ + Q_ASSERT(surface); + DFBSurfacePixelFormat format; + surface->GetPixelFormat(surface, &format); + return QDirectFBScreen::hasAlphaChannel(format); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_DIRECTFB +#endif // QDIRECTFBSCREEN_H + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp new file mode 100644 index 0000000000..742c857e9e --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbscreen.h" + +#include +#include +#ifndef QT_NO_QWS_DIRECTFB + +class DirectFBScreenDriverPlugin : public QScreenDriverPlugin +{ +public: + DirectFBScreenDriverPlugin(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +DirectFBScreenDriverPlugin::DirectFBScreenDriverPlugin() + : QScreenDriverPlugin() +{ +} + +QStringList DirectFBScreenDriverPlugin::keys() const +{ + return (QStringList() << "directfb"); +} + +QScreen* DirectFBScreenDriverPlugin::create(const QString& driver, + int displayId) +{ + if (driver.toLower() != "directfb") + return 0; + + return new QDirectFBScreen(displayId); +} + +Q_EXPORT_PLUGIN2(qdirectfbscreen, DirectFBScreenDriverPlugin) + +#endif diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp new file mode 100644 index 0000000000..9a94c30d8c --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp @@ -0,0 +1,506 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qdirectfbwindowsurface.h" +#include "qdirectfbscreen.h" +#include "qdirectfbpaintengine.h" + +#include +#include +#include +#include +#include + +#ifndef QT_NO_QWS_DIRECTFB + +QT_BEGIN_NAMESPACE + +QDirectFBWindowSurface::QDirectFBWindowSurface(DFBSurfaceFlipFlags flip, QDirectFBScreen *scr) + : QDirectFBPaintDevice(scr) +#ifndef QT_NO_DIRECTFB_WM + , dfbWindow(0) +#endif + , flipFlags(flip) + , boundingRectFlip(scr->directFBFlags() & QDirectFBScreen::BoundingRectFlip) + , flushPending(false) +{ +#ifdef QT_NO_DIRECTFB_WM + mode = Offscreen; +#endif + setSurfaceFlags(Opaque | Buffered); +#ifdef QT_DIRECTFB_TIMING + frames = 0; + timer.start(); +#endif +} + +QDirectFBWindowSurface::QDirectFBWindowSurface(DFBSurfaceFlipFlags flip, QDirectFBScreen *scr, QWidget *widget) + : QWSWindowSurface(widget), QDirectFBPaintDevice(scr) +#ifndef QT_NO_DIRECTFB_WM + , dfbWindow(0) +#endif + , flipFlags(flip) + , boundingRectFlip(scr->directFBFlags() & QDirectFBScreen::BoundingRectFlip) + , flushPending(false) +{ + SurfaceFlags flags = 0; + if (!widget || widget->window()->windowOpacity() == 0xff) + flags |= Opaque; +#ifdef QT_NO_DIRECTFB_WM + if (widget && widget->testAttribute(Qt::WA_PaintOnScreen)) { + flags = RegionReserved; + mode = Primary; + } else { + mode = Offscreen; + flags = Buffered; + } +#endif + setSurfaceFlags(flags); +#ifdef QT_DIRECTFB_TIMING + frames = 0; + timer.start(); +#endif +} + +QDirectFBWindowSurface::~QDirectFBWindowSurface() +{ + releaseSurface(); + // these are not tracked by QDirectFBScreen so we don't want QDirectFBPaintDevice to release it +} + +bool QDirectFBWindowSurface::isValid() const +{ + return true; +} + +#ifdef QT_DIRECTFB_WM +void QDirectFBWindowSurface::raise() +{ + if (IDirectFBWindow *window = directFBWindow()) { + window->RaiseToTop(window); + } +} + +IDirectFBWindow *QDirectFBWindowSurface::directFBWindow() const +{ + return dfbWindow; +} + +void QDirectFBWindowSurface::createWindow(const QRect &rect) +{ + IDirectFBDisplayLayer *layer = screen->dfbDisplayLayer(); + if (!layer) + qFatal("QDirectFBWindowSurface: Unable to get primary display layer!"); + + updateIsOpaque(); + + DFBWindowDescription description; + memset(&description, 0, sizeof(DFBWindowDescription)); + + description.flags = DWDESC_CAPS|DWDESC_HEIGHT|DWDESC_WIDTH|DWDESC_POSX|DWDESC_POSY|DWDESC_SURFACE_CAPS|DWDESC_PIXELFORMAT; + description.caps = DWCAPS_NODECORATION; + description.surface_caps = DSCAPS_NONE; + imageFormat = screen->pixelFormat(); + + if (!(surfaceFlags() & Opaque)) { + imageFormat = screen->alphaPixmapFormat(); + description.caps |= DWCAPS_ALPHACHANNEL; +#if (Q_DIRECTFB_VERSION >= 0x010200) + description.flags |= DWDESC_OPTIONS; + description.options |= DWOP_ALPHACHANNEL; +#endif + } + description.pixelformat = QDirectFBScreen::getSurfacePixelFormat(imageFormat); + description.posx = rect.x(); + description.posy = rect.y(); + description.width = rect.width(); + description.height = rect.height(); + + if (QDirectFBScreen::isPremultiplied(imageFormat)) + description.surface_caps = DSCAPS_PREMULTIPLIED; + + if (screen->directFBFlags() & QDirectFBScreen::VideoOnly) + description.surface_caps |= DSCAPS_VIDEOONLY; + + DFBResult result = layer->CreateWindow(layer, &description, &dfbWindow); + + if (result != DFB_OK) + DirectFBErrorFatal("QDirectFBWindowSurface::createWindow", result); + + if (window()) { + if (window()->windowFlags() & Qt::WindowStaysOnTopHint) { + dfbWindow->SetStackingClass(dfbWindow, DWSC_UPPER); + } + DFBWindowID winid; + result = dfbWindow->GetID(dfbWindow, &winid); + if (result != DFB_OK) { + DirectFBError("QDirectFBWindowSurface::createWindow. Can't get ID", result); + } else { + window()->setProperty("_q_DirectFBWindowID", winid); + } + } + + Q_ASSERT(!dfbSurface); + dfbWindow->GetSurface(dfbWindow, &dfbSurface); +} + +static DFBResult setWindowGeometry(IDirectFBWindow *dfbWindow, const QRect &old, const QRect &rect) +{ + DFBResult result = DFB_OK; + const bool isMove = old.isEmpty() || rect.topLeft() != old.topLeft(); + const bool isResize = rect.size() != old.size(); + +#if (Q_DIRECTFB_VERSION >= 0x010000) + if (isResize && isMove) { + result = dfbWindow->SetBounds(dfbWindow, rect.x(), rect.y(), + rect.width(), rect.height()); + } else if (isResize) { + result = dfbWindow->Resize(dfbWindow, + rect.width(), rect.height()); + } else if (isMove) { + result = dfbWindow->MoveTo(dfbWindow, rect.x(), rect.y()); + } +#else + if (isResize) { + result = dfbWindow->Resize(dfbWindow, + rect.width(), rect.height()); + } + if (isMove) { + result = dfbWindow->MoveTo(dfbWindow, rect.x(), rect.y()); + } +#endif + return result; +} +#endif // QT_NO_DIRECTFB_WM + +void QDirectFBWindowSurface::setGeometry(const QRect &rect) +{ + const QRect oldRect = geometry(); + if (oldRect == rect) + return; + + IDirectFBSurface *oldSurface = dfbSurface; + const bool sizeChanged = oldRect.size() != rect.size(); + if (sizeChanged) { + delete engine; + engine = 0; + releaseSurface(); + Q_ASSERT(!dfbSurface); + } + + if (rect.isNull()) { +#ifndef QT_NO_DIRECTFB_WM + if (dfbWindow) { + if (window()) + window()->setProperty("_q_DirectFBWindowID", QVariant()); + + dfbWindow->Release(dfbWindow); + dfbWindow = 0; + } +#endif + Q_ASSERT(!dfbSurface); +#ifdef QT_DIRECTFB_SUBSURFACE + Q_ASSERT(!subSurface); +#endif + } else { +#ifdef QT_DIRECTFB_WM + if (!dfbWindow) { + createWindow(rect); + } else { + setWindowGeometry(dfbWindow, oldRect, rect); + Q_ASSERT(!sizeChanged || !dfbSurface); + if (sizeChanged) + dfbWindow->GetSurface(dfbWindow, &dfbSurface); + } +#else + IDirectFBSurface *primarySurface = screen->primarySurface(); + DFBResult result = DFB_OK; + if (mode == Primary) { + Q_ASSERT(primarySurface); + if (rect == screen->region().boundingRect()) { + dfbSurface = primarySurface; + } else { + const DFBRectangle r = { rect.x(), rect.y(), + rect.width(), rect.height() }; + result = primarySurface->GetSubSurface(primarySurface, &r, &dfbSurface); + } + } else { // mode == Offscreen + if (!dfbSurface) { + dfbSurface = screen->createDFBSurface(rect.size(), surfaceFlags() & Opaque ? screen->pixelFormat() : screen->alphaPixmapFormat(), + QDirectFBScreen::DontTrackSurface); + } + } + if (result != DFB_OK) + DirectFBErrorFatal("QDirectFBWindowSurface::setGeometry()", result); +#endif + } + if (oldSurface != dfbSurface) { + imageFormat = dfbSurface ? QDirectFBScreen::getImageFormat(dfbSurface) : QImage::Format_Invalid; + } + + if (oldRect.size() != rect.size()) { + QWSWindowSurface::setGeometry(rect); + } else { + QWindowSurface::setGeometry(rect); + } +} + +QByteArray QDirectFBWindowSurface::permanentState() const +{ + QByteArray state(sizeof(SurfaceFlags) + sizeof(DFBWindowID), 0); + char *ptr = state.data(); + SurfaceFlags flags = surfaceFlags(); + memcpy(ptr, &flags, sizeof(SurfaceFlags)); + ptr += sizeof(SurfaceFlags); + DFBWindowID did = (DFBWindowID)(-1); + if (dfbWindow) + dfbWindow->GetID(dfbWindow, &did); + memcpy(ptr, &did, sizeof(DFBWindowID)); + return state; +} + +void QDirectFBWindowSurface::setPermanentState(const QByteArray &state) +{ + const char *ptr = state.constData(); + IDirectFBDisplayLayer *layer = screen->dfbDisplayLayer(); + SurfaceFlags flags; + memcpy(&flags, ptr, sizeof(SurfaceFlags)); + + setSurfaceFlags(flags); + ptr += sizeof(SurfaceFlags); + DFBWindowID id; + memcpy(&id, ptr, sizeof(DFBWindowID)); + if (dfbSurface) + dfbSurface->Release(dfbSurface); + if (id != (DFBWindowID)-1) { + IDirectFBWindow *dw; + layer->GetWindow(layer, id, &dw); + if (dw->GetSurface(dw, &dfbSurface) != DFB_OK) + dfbSurface = 0; + dw->Release(dw); + } + else { + dfbSurface = 0; + } +} + +bool QDirectFBWindowSurface::scroll(const QRegion ®ion, int dx, int dy) +{ + if (!dfbSurface || !(flipFlags & DSFLIP_BLIT) || region.rectCount() != 1) + return false; + if (flushPending) { + dfbSurface->Flip(dfbSurface, 0, DSFLIP_BLIT); + } else { + flushPending = true; + } + dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); + const QRect r = region.boundingRect(); + const DFBRectangle rect = { r.x(), r.y(), r.width(), r.height() }; + dfbSurface->Blit(dfbSurface, dfbSurface, &rect, r.x() + dx, r.y() + dy); + return true; +} + +bool QDirectFBWindowSurface::move(const QPoint &moveBy) +{ + setGeometry(geometry().translated(moveBy)); + return true; +} + +void QDirectFBWindowSurface::setOpaque(bool opaque) +{ + SurfaceFlags flags = surfaceFlags(); + if (opaque != (flags & Opaque)) { + if (opaque) { + flags |= Opaque; + } else { + flags &= ~Opaque; + } + setSurfaceFlags(flags); + } +} + + +void QDirectFBWindowSurface::flush(QWidget *widget, const QRegion ®ion, + const QPoint &offset) +{ + QWidget *win = window(); + if (!win) + return; + +#if !defined(QT_NO_QWS_PROXYSCREEN) && !defined(QT_NO_GRAPHICSVIEW) + QWExtra *extra = qt_widget_private(widget)->extraData(); + if (extra && extra->proxyWidget) + return; +#else + Q_UNUSED(widget); +#endif + + const quint8 windowOpacity = quint8(win->windowOpacity() * 0xff); + const QRect windowGeometry = geometry(); +#ifdef QT_DIRECTFB_WM + quint8 currentOpacity; + Q_ASSERT(dfbWindow); + dfbWindow->GetOpacity(dfbWindow, ¤tOpacity); + if (currentOpacity != windowOpacity) { + dfbWindow->SetOpacity(dfbWindow, windowOpacity); + } + + screen->flipSurface(dfbSurface, flipFlags, region, offset); +#else + setOpaque(windowOpacity == 0xff); + if (mode == Offscreen) { + screen->exposeRegion(region.translated(offset + geometry().topLeft()), 0); + } else { + screen->flipSurface(dfbSurface, flipFlags, region, offset); + } +#endif + +#ifdef QT_DIRECTFB_TIMING + enum { Secs = 3 }; + ++frames; + if (timer.elapsed() >= Secs * 1000) { + qDebug("%d fps", int(double(frames) / double(Secs))); + frames = 0; + timer.restart(); + } +#endif + flushPending = false; +} + +void QDirectFBWindowSurface::beginPaint(const QRegion ®ion) +{ + if (!engine) { + engine = new QDirectFBPaintEngine(this); + } + + if (dfbSurface) { + const QWidget *win = window(); + if (win && win->testAttribute(Qt::WA_NoSystemBackground)) { + QDirectFBScreen::solidFill(dfbSurface, Qt::transparent, region); + } + } + flushPending = true; +} + +void QDirectFBWindowSurface::endPaint(const QRegion &) +{ +#ifdef QT_NO_DIRECTFB_SUBSURFACE + unlockSurface(); +#endif +} + +IDirectFBSurface *QDirectFBWindowSurface::directFBSurface() const +{ + return dfbSurface; +} + + +IDirectFBSurface *QDirectFBWindowSurface::surfaceForWidget(const QWidget *widget, QRect *rect) const +{ + Q_ASSERT(widget); + if (!dfbSurface) + return 0; + QWidget *win = window(); + Q_ASSERT(win); + if (rect) { + if (win == widget) { + *rect = widget->rect(); + } else { + *rect = QRect(widget->mapTo(win, QPoint(0, 0)), widget->size()); + } + } + + Q_ASSERT(win == widget || win->isAncestorOf(widget)); + return dfbSurface; +} + +void QDirectFBWindowSurface::releaseSurface() +{ + if (dfbSurface) { +#ifdef QT_DIRECTFB_SUBSURFACE + releaseSubSurface(); +#else + unlockSurface(); +#endif +#ifdef QT_NO_DIRECTFB_WM + Q_ASSERT(screen->primarySurface()); + if (dfbSurface != screen->primarySurface()) +#endif + + dfbSurface->Release(dfbSurface); + dfbSurface = 0; + } +} + +void QDirectFBWindowSurface::updateIsOpaque() +{ + const QWidget *win = window(); + Q_ASSERT(win); + if (win->testAttribute(Qt::WA_OpaquePaintEvent) || win->testAttribute(Qt::WA_PaintOnScreen)) { + setOpaque(true); + return; + } + + if (qFuzzyCompare(static_cast(win->windowOpacity()), 1.0f)) { + const QPalette &pal = win->palette(); + + if (win->autoFillBackground()) { + const QBrush &autoFillBrush = pal.brush(win->backgroundRole()); + if (autoFillBrush.style() != Qt::NoBrush && autoFillBrush.isOpaque()) { + setOpaque(true); + return; + } + } + + if (win->isWindow() && !win->testAttribute(Qt::WA_NoSystemBackground)) { + const QBrush &windowBrush = win->palette().brush(QPalette::Window); + if (windowBrush.style() != Qt::NoBrush && windowBrush.isOpaque()) { + setOpaque(true); + return; + } + } + } + setOpaque(false); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h new file mode 100644 index 0000000000..75d462b523 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QDIRECFBWINDOWSURFACE_H +#define QDIRECFBWINDOWSURFACE_H + +#include "qdirectfbpaintengine.h" +#include "qdirectfbpaintdevice.h" +#include "qdirectfbscreen.h" + +#ifndef QT_NO_QWS_DIRECTFB + +#include +#include + +#ifdef QT_DIRECTFB_TIMING +#include +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDirectFBWindowSurface : public QWSWindowSurface, public QDirectFBPaintDevice +{ +public: + QDirectFBWindowSurface(DFBSurfaceFlipFlags flipFlags, QDirectFBScreen *scr); + QDirectFBWindowSurface(DFBSurfaceFlipFlags flipFlags, QDirectFBScreen *scr, QWidget *widget); + ~QDirectFBWindowSurface(); + +#ifdef QT_DIRECTFB_WM + void raise(); +#endif + bool isValid() const; + + void setGeometry(const QRect &rect); + + QString key() const { return QLatin1String("directfb"); } + QByteArray permanentState() const; + void setPermanentState(const QByteArray &state); + + bool scroll(const QRegion &area, int dx, int dy); + + bool move(const QPoint &offset); + + QImage image() const { return QImage(); } + QPaintDevice *paintDevice() { return this; } + + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + + void beginPaint(const QRegion &); + void endPaint(const QRegion &); + + IDirectFBSurface *surfaceForWidget(const QWidget *widget, QRect *rect) const; + IDirectFBSurface *directFBSurface() const; +#ifdef QT_DIRECTFB_WM + IDirectFBWindow *directFBWindow() const; +#endif +private: + void updateIsOpaque(); + void setOpaque(bool opaque); + void releaseSurface(); + +#ifdef QT_DIRECTFB_WM + void createWindow(const QRect &rect); + IDirectFBWindow *dfbWindow; +#else + enum Mode { + Primary, + Offscreen + } mode; +#endif + + DFBSurfaceFlipFlags flipFlags; + bool boundingRectFlip; + bool flushPending; +#ifdef QT_DIRECTFB_TIMING + int frames; + QTime timer; +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_DIRECTFB + +#endif // QDIRECFBWINDOWSURFACE_H diff --git a/src/plugins/gfxdrivers/eglnullws/README b/src/plugins/gfxdrivers/eglnullws/README new file mode 100644 index 0000000000..80b88c7e01 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/README @@ -0,0 +1,48 @@ +EGL NullWS QScreen Driver +========================= + +If your application draws everything within a single full-screen QGLWidget then +you may wish to use this QScreen plugin driver. This driver simply returns 0 +(as a EGLNativeWindowType value) when asked by the QtOpenGl module to create a +native window. Some OpenGL ES implementations (including PowerVR) interpret this +to mean that a full-screen OpenGL context is desired without any windowing +support (NullWS). + +To tell a Qt/Embedded application to use this driver use the -display command +line option or the QWS_DISPLAY environment variable. The following driver +options are supported: + +size=WIDTHxHEIGHT Screen size reported by the driver +format=FORMAT Screen format + +Run with '-display eglnullws:help' to get a full list of options (including a +list of supported format strings). + +If you choose a screen format that is not supported by the hardware then the +QtOpenGl module will write out a list of supported EGL configurations. Use +one of the supported screen formats from this list. + +Using this driver with PowerVR hardware +--------------------------------------- + +Using this plugin with PowerVR hardware should give a significant speedup +compared to running with the Qt powervr driver (with a full-screen QGLWidget). +This is because sacrificing the window system allows less work to be done in +order to get graphics on the screen. Using this driver also avoids the memory +fragmentation issues present in the powervr driver and avoids any direct +dependencies on the deprecated PVR2D API from Imagination Technologies. + +To use this driver ensure you have /etc/powervr.ini with contents similar to +this: + +[default] +WindowSystem=libpvrPVR2D_FLIPWSEGL.so + +This driver will also function with libpvrPVR2D_FRONTWSEGL.so, but that draws +straight into the framebuffer and will therefore cause flickering (it can be +useful for performance testing though). The flip plugin uses triple buffering, +so you will need to set the virtual vertical resolution of your framebuffer to +be three times the physical vertical resolution of your screen. This can be +done with 'fbset -vyres'. Failure to do this can cause system crashes. You +should also ensure that the plugin you choose in powervr.ini is in your library +path (it may just silently default to the flip plugin if not). diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullws.pro b/src/plugins/gfxdrivers/eglnullws/eglnullws.pro new file mode 100644 index 0000000000..242ab07ecd --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullws.pro @@ -0,0 +1,18 @@ +TARGET = qeglnullws +include(../../qpluginbase.pri) + +CONFIG += warn_on +QT += opengl + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +HEADERS = eglnullwsscreen.h \ + eglnullwsscreenplugin.h \ + eglnullwswindowsurface.h + +SOURCES = eglnullwsscreen.cpp \ + eglnullwsscreenplugin.cpp \ + eglnullwswindowsurface.cpp diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp new file mode 100644 index 0000000000..d090e857ad --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "eglnullwsscreen.h" +#include "eglnullwswindowsurface.h" +#include "eglnullwsscreenplugin.h" + +#include +#include + +namespace +{ + class EGLNullWSScreenSurfaceFunctions : public QGLScreenSurfaceFunctions + { + public: + virtual bool createNativeWindow(QWidget *, EGLNativeWindowType *native) + { *native = 0; return true; } + }; +} + +EGLNullWSScreen::EGLNullWSScreen(int displayId) : QGLScreen(displayId) {} + +EGLNullWSScreen::~EGLNullWSScreen() {} + +bool EGLNullWSScreen::initDevice() +{ + setSurfaceFunctions(new EGLNullWSScreenSurfaceFunctions); + return true; +} + +static const QHash formatDictionary() +{ + QHash dictionary; + dictionary["rgb32"] = QImage::Format_RGB32; + dictionary["argb32"] = QImage::Format_ARGB32; + dictionary["rgb16"] = QImage::Format_RGB16; + dictionary["rgb666"] = QImage::Format_RGB666; + dictionary["rgb555"] = QImage::Format_RGB555; + dictionary["rgb888"] = QImage::Format_RGB888; + dictionary["rgb444"] = QImage::Format_RGB444; + return dictionary; +} + +static int depthForFormat(QImage::Format format) +{ + switch (format) { + case QImage::Format_RGB32: return 32; + case QImage::Format_ARGB32: return 32; + case QImage::Format_RGB16: return 16; + case QImage::Format_RGB666: return 24; + case QImage::Format_RGB555: return 16; + case QImage::Format_RGB888: return 24; + case QImage::Format_RGB444: return 16; + default: + Q_ASSERT_X(false, "EGLNullWSScreen", "Unknown format"); + return -1; + } +} + +static void printHelp(const QHash &formatDictionary) +{ + QByteArray formatsBuf; + QTextStream(&formatsBuf) << QStringList(formatDictionary.keys()).join(", "); + qWarning( + "%s: Valid options are:\n" + "size=WIDTHxHEIGHT Screen size reported by this driver\n" + "format=FORMAT Screen format, where FORMAT is one of the following:\n" + " %s\n", + PluginName, + formatsBuf.constData()); +} + +bool EGLNullWSScreen::connect(const QString &displaySpec) +{ + const QStringList args = displaySpec.section(':', 1).split(':', QString::SkipEmptyParts); + const QHash formatDict = formatDictionary(); + Q_FOREACH(const QString arg, args) { + const QString optionName = arg.section('=', 0, 0); + const QString optionArg = arg.section('=', 1); + if (optionName == QLatin1String("size")) { + w = optionArg.section('x', 0, 0).toInt(); + h = optionArg.section('x', 1, 1).toInt(); + } else if (optionName == QLatin1String("format")) { + if (formatDict.contains(optionArg)) + setPixelFormat(formatDict.value(optionArg)); + else + printHelp(formatDict); + } else { + printHelp(formatDict); + } + } + + if (w == 0 || h == 0) { + w = 640; + h = 480; + qWarning("%s: Using default screen size %dx%d", PluginName, w, h); + } + dw = w; + dh = h; + + if (pixelFormat() == QImage::Format_Invalid) { + qWarning("%s: Using default screen format argb32", PluginName); + setPixelFormat(QImage::Format_ARGB32); + } + d = depthForFormat(pixelFormat()); + + static const int Dpi = 120; + static const qreal ScalingFactor = static_cast(25.4) / Dpi; + physWidth = qRound(dw * ScalingFactor); + physHeight = qRound(dh * ScalingFactor); + + return true; +} + +void EGLNullWSScreen::disconnect() {} + +void EGLNullWSScreen::shutdownDevice() {} + +void EGLNullWSScreen::setMode(int /*width*/, int /*height*/, int /*depth*/) {} + +void EGLNullWSScreen::blank(bool /*on*/) {} + +void EGLNullWSScreen::exposeRegion(QRegion /*r*/, int /*changing*/) {} + +QWSWindowSurface* EGLNullWSScreen::createSurface(QWidget *widget) const +{ + if (qobject_cast(widget)) { + return new EGLNullWSWindowSurface(widget); + } else { + qWarning("%s: Creating non-GL surface", PluginName); + return QScreen::createSurface(widget); + } +} + +QWSWindowSurface* EGLNullWSScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("eglnullws")) { + return new EGLNullWSWindowSurface; + } else { + qWarning("%s: Creating non-GL surface", PluginName); + return QScreen::createSurface(key); + } +} diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h new file mode 100644 index 0000000000..08ba2fa0cc --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef EGLNULLWSSCREEN +#define EGLNULLWSSCREEN + +#include + +class EGLNullWSScreen : public QGLScreen +{ +public: + EGLNullWSScreen(int displayId); + ~EGLNullWSScreen(); + + bool initDevice(); + bool connect(const QString &displaySpec); + void disconnect(); + void shutdownDevice(); + + void setMode(int width, int height, int depth); + void blank(bool on); + + void exposeRegion(QRegion r, int changing); + + QWSWindowSurface* createSurface(QWidget *widget) const; + QWSWindowSurface* createSurface(const QString &key) const; + + bool hasOpenGL() { return true; } +}; + +#endif // EGLNULLWSSCREEN diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp new file mode 100644 index 0000000000..f7080333f8 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "eglnullwsscreenplugin.h" +#include "eglnullwsscreen.h" + +#include +#include + +class EGLNullWSScreenPlugin : public QScreenDriverPlugin +{ +public: + virtual QStringList keys() const; + virtual QScreen *create(const QString& driver, int displayId); +}; + +QStringList EGLNullWSScreenPlugin::keys() const +{ + return QStringList() << QLatin1String(PluginName); +} + +QScreen *EGLNullWSScreenPlugin::create(const QString& driver, int displayId) +{ + return (driver.toLower() == QLatin1String(PluginName) ? + new EGLNullWSScreen(displayId) : 0); +} + +Q_EXPORT_PLUGIN2(qeglnullws, EGLNullWSScreenPlugin) diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h new file mode 100644 index 0000000000..64a362374d --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef EGLNULLWSSCREENPLUGIN_H +#define EGLNULLWSSCREENPLUGIN_H + +const char *const PluginName = "eglnullws"; + +#endif // EGLNULLWSSCREENPLUGIN_H diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp new file mode 100644 index 0000000000..8af4d406a5 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "eglnullwswindowsurface.h" +#include "eglnullwsscreenplugin.h" + +#include + +static const QWSWindowSurface::SurfaceFlags Flags + = QWSWindowSurface::RegionReserved | QWSWindowSurface::RegionReserved; + +EGLNullWSWindowSurface::EGLNullWSWindowSurface(QWidget *w) + : + QWSGLWindowSurface(w), + widget(w) +{ + setSurfaceFlags(Flags); +} + +EGLNullWSWindowSurface::EGLNullWSWindowSurface() + : widget(0) +{ + setSurfaceFlags(Flags); +} + +EGLNullWSWindowSurface::~EGLNullWSWindowSurface() {} + +QString EGLNullWSWindowSurface::key() const +{ + return QLatin1String(PluginName); +} + +QPaintDevice *EGLNullWSWindowSurface::paintDevice() +{ + return widget; +} + +bool EGLNullWSWindowSurface::isValid() const +{ + return qobject_cast(window()); +} + +QImage EGLNullWSWindowSurface::image() const +{ + return QImage(); +} diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h new file mode 100644 index 0000000000..793d3256d6 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef EGLNULLWSWINDOWSURFACE_H +#define EGLNULLWSWINDOWSURFACE_H + +#include + +class EGLNullWSWindowSurface : public QWSGLWindowSurface +{ +public: + EGLNullWSWindowSurface(QWidget *widget); + EGLNullWSWindowSurface(); + virtual ~EGLNullWSWindowSurface(); + + virtual QString key() const; + virtual QPaintDevice *paintDevice(); + virtual bool isValid() const; + virtual QImage image() const; + +private: + QWidget *widget; +}; + +#endif // EGLNULLWSWINDOWSURFACE_H diff --git a/src/plugins/gfxdrivers/gfxdrivers.pro b/src/plugins/gfxdrivers/gfxdrivers.pro new file mode 100644 index 0000000000..1f38942a50 --- /dev/null +++ b/src/plugins/gfxdrivers/gfxdrivers.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +contains(gfx-plugins, ahi) :SUBDIRS += ahi +contains(gfx-plugins, directfb) :SUBDIRS += directfb +contains(gfx-plugins, linuxfb) :SUBDIRS += linuxfb +contains(gfx-plugins, qvfb) :SUBDIRS += qvfb +contains(gfx-plugins, vnc) :SUBDIRS += vnc +contains(gfx-plugins, transformed) :SUBDIRS += transformed +contains(gfx-plugins, svgalib) :SUBDIRS += svgalib +contains(gfx-plugins, powervr) :SUBDIRS += powervr +contains(gfx-plugins, eglnullws) :SUBDIRS += eglnullws diff --git a/src/plugins/gfxdrivers/linuxfb/linuxfb.pro b/src/plugins/gfxdrivers/linuxfb/linuxfb.pro new file mode 100644 index 0000000000..2a376e4158 --- /dev/null +++ b/src/plugins/gfxdrivers/linuxfb/linuxfb.pro @@ -0,0 +1,14 @@ +TARGET = qscreenlinuxfb +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +DEFINES += QT_QWS_LINUXFB + +HEADERS = $$QT_SOURCE_TREE/src/gui/embedded/qscreenlinuxfb_qws.h + +SOURCES = main.cpp \ + $$QT_SOURCE_TREE/src/gui/embedded/qscreenlinuxfb_qws.cpp diff --git a/src/plugins/gfxdrivers/linuxfb/main.cpp b/src/plugins/gfxdrivers/linuxfb/main.cpp new file mode 100644 index 0000000000..6ea7b9ca24 --- /dev/null +++ b/src/plugins/gfxdrivers/linuxfb/main.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 +#include +#include + +QT_BEGIN_NAMESPACE + +class QScreenLinuxFbPlugin : public QScreenDriverPlugin +{ +public: + QScreenLinuxFbPlugin(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +QScreenLinuxFbPlugin::QScreenLinuxFbPlugin() + : QScreenDriverPlugin() +{ +} + +QStringList QScreenLinuxFbPlugin::keys() const +{ + QStringList list; + list << QLatin1String("LinuxFb"); + return list; +} + +QScreen* QScreenLinuxFbPlugin::create(const QString& driver, int displayId) +{ + if (driver.toLower() == QLatin1String("linuxfb")) + return new QLinuxFbScreen(displayId); + + return 0; +} + +Q_EXPORT_PLUGIN2(qscreenlinuxfb, QScreenLinuxFbPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/QWSWSEGL.pro b/src/plugins/gfxdrivers/powervr/QWSWSEGL/QWSWSEGL.pro new file mode 100644 index 0000000000..595cf45301 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/QWSWSEGL.pro @@ -0,0 +1,26 @@ +TEMPLATE = lib +TARGET = pvrQWSWSEGL +CONFIG += dll warn_on +CONFIG -= qt + +HEADERS+=\ + pvrqwsdrawable.h \ + pvrqwsdrawable_p.h + +SOURCES+=\ + pvrqwsdrawable.c \ + pvrqwswsegl.c + +INCLUDEPATH += $$QMAKE_INCDIR_EGL + +for(p, QMAKE_LIBDIR_EGL) { + exists($$p):LIBS += -L$$p +} + +LIBS += -lpvr2d + +DESTDIR = $$QMAKE_LIBDIR_QT +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target + +include(../powervr.pri) \ No newline at end of file diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c new file mode 100644 index 0000000000..8dc0120fe1 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c @@ -0,0 +1,830 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "pvrqwsdrawable_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PvrQwsDisplay pvrQwsDisplay; + +static void pvrQwsDestroyDrawableForced(PvrQwsDrawable *drawable); + +/* Initialize the /dev/fbN device for a specific screen */ +static int pvrQwsInitFbScreen(int screen) +{ + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + unsigned long start; + unsigned long length; + int width, height, stride; + PVR2DFORMAT format; + void *mapped; + int fd, bytesPerPixel; + char name[64]; + PVR2DMEMINFO *memInfo; + unsigned long pageAddresses[2]; + + /* Bail out if already initialized, or the number is incorrect */ + if (screen < 0 || screen >= PVRQWS_MAX_SCREENS) + return 0; + if (pvrQwsDisplay.screens[screen].initialized) + return 1; + + /* Open the framebuffer and fetch its properties */ + sprintf(name, "/dev/fb%d", screen); + fd = open(name, O_RDWR, 0); + if (fd < 0) { + perror(name); + return 0; + } + if (ioctl(fd, FBIOGET_VSCREENINFO, &var) < 0) { + perror("FBIOGET_VSCREENINFO"); + close(fd); + return 0; + } + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) < 0) { + perror("FBIOGET_FSCREENINFO"); + close(fd); + return 0; + } + width = var.xres; + height = var.yres; + bytesPerPixel = var.bits_per_pixel / 8; + stride = fix.line_length; + format = PVR2D_1BPP; + if (var.bits_per_pixel == 16) { + if (var.red.length == 5 && var.green.length == 6 && + var.blue.length == 5 && var.red.offset == 11 && + var.green.offset == 5 && var.blue.offset == 0) { + format = PVR2D_RGB565; + } + if (var.red.length == 4 && var.green.length == 4 && + var.blue.length == 4 && var.transp.length == 4 && + var.red.offset == 8 && var.green.offset == 4 && + var.blue.offset == 0 && var.transp.offset == 12) { + format = PVR2D_ARGB4444; + } + } else if (var.bits_per_pixel == 32) { + if (var.red.length == 8 && var.green.length == 8 && + var.blue.length == 8 && var.transp.length == 8 && + var.red.offset == 16 && var.green.offset == 8 && + var.blue.offset == 0 && var.transp.offset == 24) { + format = PVR2D_ARGB8888; + } + } + if (format == PVR2D_1BPP) { + fprintf(stderr, "%s: could not find a suitable PVR2D pixel format\n", name); + close(fd); + return 0; + } + start = fix.smem_start; + length = var.xres_virtual * var.yres_virtual * bytesPerPixel; + + if (screen == 0) { + /* We use PVR2DGetFrameBuffer to map the first screen. + On some chipsets it is more reliable than using PVR2DMemWrap */ + mapped = 0; + memInfo = 0; + } else { + /* Other screens: map the framebuffer region into memory */ + mapped = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!mapped || mapped == (void *)(-1)) { + perror("mmap"); + close(fd); + return 0; + } + + /* Allocate a PVR2D memory region for the framebuffer */ + memInfo = 0; + if (pvrQwsDisplay.context) { + pageAddresses[0] = start & 0xFFFFF000; + pageAddresses[1] = 0; + if (PVR2DMemWrap + (pvrQwsDisplay.context, mapped, PVR2D_WRAPFLAG_CONTIGUOUS, + length, pageAddresses, &memInfo) != PVR2D_OK) { + munmap(mapped, length); + close(fd); + return 0; + } + } + } + + /* We don't need the file descriptor any more */ + close(fd); + + /* The framebuffer is ready, so initialize the PvrQwsScreenInfo */ + pvrQwsDisplay.screens[screen].screenRect.x = 0; + pvrQwsDisplay.screens[screen].screenRect.y = 0; + pvrQwsDisplay.screens[screen].screenRect.width = width; + pvrQwsDisplay.screens[screen].screenRect.height = height; + pvrQwsDisplay.screens[screen].screenStride = stride; + pvrQwsDisplay.screens[screen].pixelFormat = format; + pvrQwsDisplay.screens[screen].bytesPerPixel = bytesPerPixel; + pvrQwsDisplay.screens[screen].screenDrawable = 0; + if (mapped) { + /* Don't set these fields if mapped is 0, because PVR2DGetFrameBuffer + may have already been called and set them */ + pvrQwsDisplay.screens[screen].frameBuffer = memInfo; + pvrQwsDisplay.screens[screen].mapped = mapped; + } + pvrQwsDisplay.screens[screen].mappedLength = length; + pvrQwsDisplay.screens[screen].screenStart = start; + pvrQwsDisplay.screens[screen].needsUnmap = (mapped != 0); + pvrQwsDisplay.screens[screen].initialized = 1; + return 1; +} + +/* Called when a new drawable is added to ensure that we have a + PVR2D context and framebuffer PVR2DMEMINFO blocks */ +static int pvrQwsAddDrawable(void) +{ + int numDevs, screen; + PVR2DDEVICEINFO *devs; + unsigned long devId; + unsigned long pageAddresses[2]; + PVR2DMEMINFO *memInfo; + PVR2DDISPLAYINFO displayInfo; + + /* Bail out early if this is not the first drawable */ + if (pvrQwsDisplay.numDrawables > 0) { + ++(pvrQwsDisplay.numDrawables); + return 1; + } + + /* Find the first PVR2D device in the system and open it */ + numDevs = PVR2DEnumerateDevices(0); + if (numDevs <= 0) + return 0; + devs = (PVR2DDEVICEINFO *)malloc(sizeof(PVR2DDEVICEINFO) * numDevs); + if (!devs) + return 0; + if (PVR2DEnumerateDevices(devs) != PVR2D_OK) { + free(devs); + return 0; + } + devId = devs[0].ulDevID; + free(devs); + if (PVR2DCreateDeviceContext(devId, &pvrQwsDisplay.context, 0) != PVR2D_OK) + return 0; + pvrQwsDisplay.numFlipBuffers = 0; + pvrQwsDisplay.flipChain = 0; + if (PVR2DGetDeviceInfo(pvrQwsDisplay.context, &displayInfo) == PVR2D_OK) { + if (displayInfo.ulMaxFlipChains > 0 && displayInfo.ulMaxBuffersInChain > 0) + pvrQwsDisplay.numFlipBuffers = displayInfo.ulMaxBuffersInChain; + if (pvrQwsDisplay.numFlipBuffers > PVRQWS_MAX_FLIP_BUFFERS) + pvrQwsDisplay.numFlipBuffers = PVRQWS_MAX_FLIP_BUFFERS; + } + + /* Create the PVR2DMEMINFO blocks for the active framebuffers */ + for (screen = 0; screen < PVRQWS_MAX_SCREENS; ++screen) { + if (screen != 0 && pvrQwsDisplay.screens[screen].mapped) { + pageAddresses[0] + = pvrQwsDisplay.screens[screen].screenStart & 0xFFFFF000; + pageAddresses[1] = 0; + if (PVR2DMemWrap + (pvrQwsDisplay.context, + pvrQwsDisplay.screens[screen].mapped, + PVR2D_WRAPFLAG_CONTIGUOUS, + pvrQwsDisplay.screens[screen].mappedLength, + pageAddresses, &memInfo) != PVR2D_OK) { + PVR2DDestroyDeviceContext(pvrQwsDisplay.context); + pvrQwsDisplay.context = 0; + return 0; + } + pvrQwsDisplay.screens[screen].frameBuffer = memInfo; + } else if (screen == 0) { + if (PVR2DGetFrameBuffer + (pvrQwsDisplay.context, + PVR2D_FB_PRIMARY_SURFACE, &memInfo) != PVR2D_OK) { + fprintf(stderr, "QWSWSEGL: could not get the primary framebuffer surface\n"); + PVR2DDestroyDeviceContext(pvrQwsDisplay.context); + pvrQwsDisplay.context = 0; + return 0; + } + pvrQwsDisplay.screens[screen].frameBuffer = memInfo; + pvrQwsDisplay.screens[screen].mapped = memInfo->pBase; + } + } + + /* Create a flip chain for the screen if supported by the hardware */ + pvrQwsDisplay.usePresentBlit = 0; + if (pvrQwsDisplay.numFlipBuffers > 0) { + long stride = 0; + unsigned long flipId = 0; + unsigned long numBuffers; + if (PVR2DCreateFlipChain(pvrQwsDisplay.context, 0, + //PVR2D_CREATE_FLIPCHAIN_SHARED | + //PVR2D_CREATE_FLIPCHAIN_QUERY, + pvrQwsDisplay.numFlipBuffers, + pvrQwsDisplay.screens[0].screenRect.width, + pvrQwsDisplay.screens[0].screenRect.height, + pvrQwsDisplay.screens[0].pixelFormat, + &stride, &flipId, &(pvrQwsDisplay.flipChain)) + == PVR2D_OK) { + pvrQwsDisplay.screens[0].screenStride = stride; + PVR2DGetFlipChainBuffers(pvrQwsDisplay.context, + pvrQwsDisplay.flipChain, + &numBuffers, + pvrQwsDisplay.flipBuffers); + } else { + pvrQwsDisplay.flipChain = 0; + pvrQwsDisplay.numFlipBuffers = 0; + } + + /* PVR2DPresentBlt is a little more reliable than PVR2DBlt + when flip chains are present, even if we cannot create a + flip chain at the moment */ + pvrQwsDisplay.usePresentBlit = 1; + } + + /* The context is ready to go */ + ++(pvrQwsDisplay.numDrawables); + return 1; +} + +/* Called when the last drawable is destroyed. The PVR2D context + will be destroyed but the raw framebuffer memory will stay mapped */ +static void pvrQwsDestroyContext(void) +{ + int screen; + for (screen = 0; screen < PVRQWS_MAX_SCREENS; ++screen) { + if (pvrQwsDisplay.screens[screen].frameBuffer) { + PVR2DMemFree + (pvrQwsDisplay.context, + pvrQwsDisplay.screens[screen].frameBuffer); + pvrQwsDisplay.screens[screen].frameBuffer = 0; + } + } + + if (pvrQwsDisplay.numFlipBuffers > 0) + PVR2DDestroyFlipChain(pvrQwsDisplay.context, pvrQwsDisplay.flipChain); + PVR2DDestroyDeviceContext(pvrQwsDisplay.context); + pvrQwsDisplay.context = 0; + pvrQwsDisplay.flipChain = 0; + pvrQwsDisplay.numFlipBuffers = 0; + pvrQwsDisplay.usePresentBlit = 0; +} + +int pvrQwsDisplayOpen(void) +{ + int screen; + + /* If the display is already open, increase reference count and return */ + if (pvrQwsDisplay.refCount > 0) { + ++(pvrQwsDisplay.refCount); + return 1; + } + + /* Open the framebuffer and map it directly */ + if (!pvrQwsInitFbScreen(0)) { + --(pvrQwsDisplay.refCount); + return 0; + } + + /* Clear the other screens. We will create them if they are referenced */ + for (screen = 1; screen < PVRQWS_MAX_SCREENS; ++screen) + memset(&(pvrQwsDisplay.screens[screen]), 0, sizeof(PvrQwsScreenInfo)); + + /* The display is open and ready */ + ++(pvrQwsDisplay.refCount); + return 1; +} + +void pvrQwsDisplayClose(void) +{ + int screen; + + if (pvrQwsDisplay.refCount == 0) + return; + if (--(pvrQwsDisplay.refCount) > 0) + return; + + /* Prevent pvrQwsDestroyContext from being called for the time being */ + ++pvrQwsDisplay.numDrawables; + + /* Free the screens */ + for (screen = 0; screen < PVRQWS_MAX_SCREENS; ++screen) { + PvrQwsScreenInfo *info = &(pvrQwsDisplay.screens[screen]); + if (info->screenDrawable) + pvrQwsDestroyDrawableForced(info->screenDrawable); + if (info->frameBuffer) + PVR2DMemFree(pvrQwsDisplay.context, info->frameBuffer); + if (info->mapped && info->needsUnmap) + munmap(info->mapped, info->mappedLength); + } + + /* Now it is safe to destroy the PVR2D context */ + --pvrQwsDisplay.numDrawables; + if (pvrQwsDisplay.context) + PVR2DDestroyDeviceContext(pvrQwsDisplay.context); + + memset(&pvrQwsDisplay, 0, sizeof(pvrQwsDisplay)); +} + +int pvrQwsDisplayIsOpen(void) +{ + return (pvrQwsDisplay.refCount > 0); +} + +/* Ensure that a specific screen has been initialized */ +static int pvrQwsEnsureScreen(int screen) +{ + if (screen < 0 || screen >= PVRQWS_MAX_SCREENS) + return 0; + if (!screen) + return 1; + return pvrQwsInitFbScreen(screen); +} + +PvrQwsDrawable *pvrQwsScreenWindow(int screen) +{ + PvrQwsDrawable *drawable; + + if (!pvrQwsEnsureScreen(screen)) + return 0; + + drawable = pvrQwsDisplay.screens[screen].screenDrawable; + if (drawable) + return drawable; + + drawable = (PvrQwsDrawable *)calloc(1, sizeof(PvrQwsDrawable)); + if (!drawable) + return 0; + + drawable->type = PvrQwsScreen; + drawable->screen = screen; + drawable->pixelFormat = pvrQwsDisplay.screens[screen].pixelFormat; + drawable->rect = pvrQwsDisplay.screens[screen].screenRect; + drawable->visibleRects[0] = drawable->rect; + drawable->numVisibleRects = 1; + drawable->isFullScreen = 1; + + if (!pvrQwsAddDrawable()) { + free(drawable); + return 0; + } + + pvrQwsDisplay.screens[screen].screenDrawable = drawable; + + return drawable; +} + +PvrQwsDrawable *pvrQwsCreateWindow(int screen, long winId, const PvrQwsRect *rect) +{ + PvrQwsDrawable *drawable; + + if (!pvrQwsEnsureScreen(screen)) + return 0; + + drawable = (PvrQwsDrawable *)calloc(1, sizeof(PvrQwsDrawable)); + if (!drawable) + return 0; + + drawable->type = PvrQwsWindow; + drawable->winId = winId; + drawable->refCount = 1; + drawable->screen = screen; + drawable->pixelFormat = pvrQwsDisplay.screens[screen].pixelFormat; + drawable->rect = *rect; + + if (!pvrQwsAddDrawable()) { + free(drawable); + return 0; + } + + drawable->nextWinId = pvrQwsDisplay.firstWinId; + pvrQwsDisplay.firstWinId = drawable; + + return drawable; +} + +PvrQwsDrawable *pvrQwsFetchWindow(long winId) +{ + PvrQwsDrawable *drawable = pvrQwsDisplay.firstWinId; + while (drawable != 0 && drawable->winId != winId) + drawable = drawable->nextWinId; + + if (drawable) + ++(drawable->refCount); + return drawable; +} + +int pvrQwsReleaseWindow(PvrQwsDrawable *drawable) +{ + if (drawable->type == PvrQwsWindow) + return (--(drawable->refCount) <= 0); + else + return 0; +} + +PvrQwsDrawable *pvrQwsCreatePixmap(int width, int height, int screen) +{ + PvrQwsDrawable *drawable; + + if (!pvrQwsEnsureScreen(screen)) + return 0; + + drawable = (PvrQwsDrawable *)calloc(1, sizeof(PvrQwsDrawable)); + if (!drawable) + return 0; + + drawable->type = PvrQwsPixmap; + drawable->screen = screen; + drawable->pixelFormat = pvrQwsDisplay.screens[screen].pixelFormat; + drawable->rect.x = 0; + drawable->rect.y = 0; + drawable->rect.width = width; + drawable->rect.height = height; + + if (!pvrQwsAddDrawable()) { + free(drawable); + return 0; + } + + return drawable; +} + +static void pvrQwsDestroyDrawableForced(PvrQwsDrawable *drawable) +{ + /* Remove the drawable from the display's winId list */ + PvrQwsDrawable *current = pvrQwsDisplay.firstWinId; + PvrQwsDrawable *prev = 0; + while (current != 0 && current != drawable) { + prev = current; + current = current->nextWinId; + } + if (current != 0) { + if (prev) + prev->nextWinId = current->nextWinId; + else + pvrQwsDisplay.firstWinId = current->nextWinId; + } + + pvrQwsFreeBuffers(drawable); + free(drawable); + + --pvrQwsDisplay.numDrawables; + if (pvrQwsDisplay.numDrawables == 0) + pvrQwsDestroyContext(); +} + +void pvrQwsDestroyDrawable(PvrQwsDrawable *drawable) +{ + if (drawable && drawable->type != PvrQwsScreen) + pvrQwsDestroyDrawableForced(drawable); +} + +PvrQwsDrawableType pvrQwsGetDrawableType(PvrQwsDrawable *drawable) +{ + return drawable->type; +} + +void pvrQwsSetVisibleRegion + (PvrQwsDrawable *drawable, const PvrQwsRect *rects, int numRects) +{ + int index, indexOut; + PvrQwsRect *rect; + PvrQwsRect *screenRect; + + /* Visible regions don't make sense for pixmaps */ + if (drawable->type == PvrQwsPixmap) + return; + + /* Restrict the number of rectangles to prevent buffer overflow */ + if (numRects > PVRQWS_MAX_VISIBLE_RECTS) + numRects = PVRQWS_MAX_VISIBLE_RECTS; + if (numRects > 0) + memcpy(drawable->visibleRects, rects, numRects * sizeof(PvrQwsRect)); + + /* Convert the rectangles into screen-relative co-ordinates and + then clamp them to the screen boundaries. If any of the + clamped rectangles are empty, remove them from the list */ + screenRect = &(pvrQwsDisplay.screens[drawable->screen].screenRect); + indexOut = 0; + for (index = 0, rect = drawable->visibleRects; index < numRects; ++index, ++rect) { + if (rect->x < 0) { + rect->width += rect->x; + rect->x = 0; + if (rect->width < 0) + rect->width = 0; + } else if (rect->x >= screenRect->width) { + rect->x = screenRect->width; + rect->width = 0; + } + if ((rect->x + rect->width) > screenRect->width) { + rect->width = screenRect->width - rect->x; + } + if (rect->y < 0) { + rect->height += rect->y; + rect->y = 0; + if (rect->height < 0) + rect->height = 0; + } else if (rect->y >= screenRect->height) { + rect->y = screenRect->height; + rect->height = 0; + } + if ((rect->y + rect->height) > screenRect->height) { + rect->height = screenRect->height - rect->y; + } + if (rect->width > 0 && rect->height > 0) { + if (index != indexOut) + drawable->visibleRects[indexOut] = *rect; + ++indexOut; + } + } + drawable->numVisibleRects = indexOut; +} + +void pvrQwsClearVisibleRegion(PvrQwsDrawable *drawable) +{ + if (drawable->type != PvrQwsPixmap) + drawable->numVisibleRects = 0; +} + +void pvrQwsSetGeometry(PvrQwsDrawable *drawable, const PvrQwsRect *rect) +{ + /* We can only change the geometry of window drawables */ + if (drawable->type != PvrQwsWindow) + return; + + /* If the position has changed, then clear the visible region */ + if (drawable->rect.x != rect->x || drawable->rect.y != rect->y) { + drawable->rect.x = rect->x; + drawable->rect.y = rect->y; + drawable->numVisibleRects = 0; + } + + /* If the size has changed, then clear the visible region and + invalidate the drawable's buffers. Invalidating the buffers + will force EGL to recreate the drawable, which will then + allocate new buffers for the new size */ + if (drawable->rect.width != rect->width || + drawable->rect.height != rect->height) { + drawable->rect.width = rect->width; + drawable->rect.height = rect->height; + drawable->numVisibleRects = 0; + pvrQwsInvalidateBuffers(drawable); + } +} + +void pvrQwsGetGeometry(PvrQwsDrawable *drawable, PvrQwsRect *rect) +{ + *rect = drawable->rect; +} + +void pvrQwsSetRotation(PvrQwsDrawable *drawable, int angle) +{ + if (drawable->rotationAngle != angle) { + drawable->rotationAngle = angle; + + /* Force the buffers to be recreated if the rotation angle changes */ + pvrQwsInvalidateBuffers(drawable); + } +} + +int pvrQwsGetStride(PvrQwsDrawable *drawable) +{ + if (drawable->backBuffersValid) + return drawable->strideBytes; + else + return 0; +} + +PvrQwsPixelFormat pvrQwsGetPixelFormat(PvrQwsDrawable *drawable) +{ + return (PvrQwsPixelFormat)(drawable->pixelFormat); +} + +void *pvrQwsGetRenderBuffer(PvrQwsDrawable *drawable) +{ + if (drawable->backBuffersValid) + return drawable->backBuffers[drawable->currentBackBuffer]->pBase; + else + return 0; +} + +int pvrQwsAllocBuffers(PvrQwsDrawable *drawable) +{ + int index; + int numBuffers = PVRQWS_MAX_BACK_BUFFERS; + if (drawable->type == PvrQwsPixmap) + numBuffers = 1; + if (drawable->backBuffers[0]) { + if (drawable->backBuffersValid) + return 1; + if (!drawable->usingFlipBuffers) { + for (index = 0; index < numBuffers; ++index) + PVR2DMemFree(pvrQwsDisplay.context, drawable->backBuffers[index]); + } + } + drawable->stridePixels = (drawable->rect.width + 31) & ~31; + drawable->strideBytes = + drawable->stridePixels * + pvrQwsDisplay.screens[drawable->screen].bytesPerPixel; + drawable->usingFlipBuffers = + (pvrQwsDisplay.numFlipBuffers > 0 && drawable->isFullScreen); + if (drawable->usingFlipBuffers) { + if (numBuffers > (int)(pvrQwsDisplay.numFlipBuffers)) + numBuffers = pvrQwsDisplay.numFlipBuffers; + for (index = 0; index < numBuffers; ++index) + drawable->backBuffers[index] = pvrQwsDisplay.flipBuffers[index]; + } else { + for (index = 0; index < numBuffers; ++index) { + if (PVR2DMemAlloc(pvrQwsDisplay.context, + drawable->strideBytes * drawable->rect.height, + 128, 0, + &(drawable->backBuffers[index])) != PVR2D_OK) { + while (--index >= 0) + PVR2DMemFree(pvrQwsDisplay.context, drawable->backBuffers[index]); + memset(drawable->backBuffers, 0, sizeof(drawable->backBuffers)); + drawable->backBuffersValid = 0; + return 0; + } + } + } + for (index = numBuffers; index < PVRQWS_MAX_BACK_BUFFERS; ++index) { + drawable->backBuffers[index] = drawable->backBuffers[0]; + } + drawable->backBuffersValid = 1; + drawable->currentBackBuffer = 0; + return 1; +} + +void pvrQwsFreeBuffers(PvrQwsDrawable *drawable) +{ + int index; + int numBuffers = PVRQWS_MAX_BACK_BUFFERS; + if (drawable->type == PvrQwsPixmap) + numBuffers = 1; + if (!drawable->usingFlipBuffers) { + for (index = 0; index < numBuffers; ++index) { + if (drawable->backBuffers[index]) + PVR2DMemFree(pvrQwsDisplay.context, drawable->backBuffers[index]); + } + } + memset(drawable->backBuffers, 0, sizeof(drawable->backBuffers)); + drawable->backBuffersValid = 0; + drawable->usingFlipBuffers = 0; +} + +void pvrQwsInvalidateBuffers(PvrQwsDrawable *drawable) +{ + drawable->backBuffersValid = 0; +} + +int pvrQwsGetBuffers + (PvrQwsDrawable *drawable, PVR2DMEMINFO **source, PVR2DMEMINFO **render) +{ + if (!drawable->backBuffersValid) + return 0; + *render = drawable->backBuffers[drawable->currentBackBuffer]; + *source = drawable->backBuffers + [(drawable->currentBackBuffer + PVRQWS_MAX_BACK_BUFFERS - 1) % + PVRQWS_MAX_BACK_BUFFERS]; + return 1; +} + +int pvrQwsSwapBuffers(PvrQwsDrawable *drawable, int repaintOnly) +{ + PVR2DMEMINFO *buffer; + PvrQwsRect *rect; + int index; + + /* Bail out if the back buffers have been invalidated */ + if (!drawable->backBuffersValid) + return 0; + + /* If there is a swap function, then use that instead */ + if (drawable->swapFunction) { + (*(drawable->swapFunction))(drawable, drawable->userData, repaintOnly); + if (!repaintOnly) { + drawable->currentBackBuffer + = (drawable->currentBackBuffer + 1) % PVRQWS_MAX_BACK_BUFFERS; + } + return 1; + } + + /* Iterate through the visible rectangles and blit them to the screen */ + if (!repaintOnly) { + index = drawable->currentBackBuffer; + } else { + index = (drawable->currentBackBuffer + PVRQWS_MAX_BACK_BUFFERS - 1) + % PVRQWS_MAX_BACK_BUFFERS; + } + buffer = drawable->backBuffers[index]; + rect = drawable->visibleRects; + if (drawable->usingFlipBuffers) { + PVR2DPresentFlip(pvrQwsDisplay.context, pvrQwsDisplay.flipChain, buffer, 0); + } else if (pvrQwsDisplay.usePresentBlit && drawable->numVisibleRects > 0) { + PVR2DRECT pvrRects[PVRQWS_MAX_VISIBLE_RECTS]; + for (index = 0; index < drawable->numVisibleRects; ++index, ++rect) { + pvrRects[index].left = rect->x; + pvrRects[index].top = rect->y; + pvrRects[index].right = rect->x + rect->width; + pvrRects[index].bottom = rect->y + rect->height; + } + for (index = 0; index < drawable->numVisibleRects; index += 4) { + int numClip = drawable->numVisibleRects - index; + if (numClip > 4) /* No more than 4 clip rects at a time */ + numClip = 4; + PVR2DSetPresentBltProperties + (pvrQwsDisplay.context, + PVR2D_PRESENT_PROPERTY_SRCSTRIDE | + PVR2D_PRESENT_PROPERTY_DSTSIZE | + PVR2D_PRESENT_PROPERTY_DSTPOS | + PVR2D_PRESENT_PROPERTY_CLIPRECTS, + drawable->strideBytes, + drawable->rect.width, drawable->rect.height, + drawable->rect.x, drawable->rect.y, + numClip, pvrRects + index, 0); + PVR2DPresentBlt(pvrQwsDisplay.context, buffer, 0); + } + PVR2DQueryBlitsComplete(pvrQwsDisplay.context, buffer, 1); + } else { + /* TODO: use PVR2DBltClipped for faster transfers of clipped windows */ + PVR2DBLTINFO blit; + for (index = 0; index < drawable->numVisibleRects; ++index, ++rect) { + memset(&blit, 0, sizeof(blit)); + + blit.CopyCode = PVR2DROPcopy; + blit.BlitFlags = PVR2D_BLIT_DISABLE_ALL; + + blit.pSrcMemInfo = buffer; + blit.SrcStride = drawable->strideBytes; + blit.SrcX = rect->x - drawable->rect.x; + blit.SrcY = rect->y - drawable->rect.y; + blit.SizeX = rect->width; + blit.SizeY = rect->height; + blit.SrcFormat = drawable->pixelFormat; + + blit.pDstMemInfo = pvrQwsDisplay.screens[drawable->screen].frameBuffer; + blit.DstStride = pvrQwsDisplay.screens[drawable->screen].screenStride; + blit.DstX = rect->x; + blit.DstY = rect->y; + blit.DSizeX = rect->width; + blit.DSizeY = rect->height; + blit.DstFormat = pvrQwsDisplay.screens[drawable->screen].pixelFormat; + + PVR2DBlt(pvrQwsDisplay.context, &blit); + } + } + + /* Swap the buffers */ + if (!repaintOnly) { + drawable->currentBackBuffer + = (drawable->currentBackBuffer + 1) % PVRQWS_MAX_BACK_BUFFERS; + } + return 1; +} + +void pvrQwsSetSwapFunction + (PvrQwsDrawable *drawable, PvrQwsSwapFunction func, void *userData) +{ + drawable->swapFunction = func; + drawable->userData = userData; +} diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h new file mode 100644 index 0000000000..8c8cc27018 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef PVRQWSDRAWABLE_H +#define PVRQWSDRAWABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int x, y, width, height; +} PvrQwsRect; + +typedef enum +{ + PvrQwsScreen, + PvrQwsWindow, + PvrQwsPixmap + +} PvrQwsDrawableType; + +typedef enum +{ + PvrQws_1BPP = 0, + PvrQws_RGB565, + PvrQws_ARGB4444, + PvrQws_RGB888, + PvrQws_ARGB8888, + PvrQws_VGAEMU + +} PvrQwsPixelFormat; + +typedef struct _PvrQwsDrawable PvrQwsDrawable; + +typedef void (*PvrQwsSwapFunction) + (PvrQwsDrawable *drawable, void *userData, int repaintOnly); + +/* Open the display and prepare for window operations. The display + can be opened multiple times and each time is reference counted. + The display will be finally closed when the same number of + calls to pvrQwsDisplayClose() have been encountered */ +int pvrQwsDisplayOpen(void); + +/* Close the display */ +void pvrQwsDisplayClose(void); + +/* Determine if the display is already open */ +int pvrQwsDisplayIsOpen(void); + +/* Create a window that represents a particular framebuffer screen. + Initially the visible region will be the whole screen. If the screen + window has already been created, then will return the same value */ +PvrQwsDrawable *pvrQwsScreenWindow(int screen); + +/* Create a top-level window on a particular framebuffer screen. + Initially the window will not have a visible region */ +PvrQwsDrawable *pvrQwsCreateWindow(int screen, long winId, const PvrQwsRect *rect); + +/* Fetch an existing window for a window id and increase its refcount */ +PvrQwsDrawable *pvrQwsFetchWindow(long winId); + +/* Release the refcount on a window. Returns 1 if refcount is zero */ +int pvrQwsReleaseWindow(PvrQwsDrawable *drawable); + +/* Create an off-screen pixmap */ +PvrQwsDrawable *pvrQwsCreatePixmap(int width, int height, int screen); + +/* Destroy a previously-created drawable. Will not destroy screens. */ +void pvrQwsDestroyDrawable(PvrQwsDrawable *drawable); + +/* Get a drawable's type */ +PvrQwsDrawableType pvrQwsGetDrawableType(PvrQwsDrawable *drawable); + +/* Sets the visible region for a window or screen drawable. Pixels within + the specified rectangles will be copied to the framebuffer when the window + or screen is swapped. The rectangles should be in global co-ordinates */ +void pvrQwsSetVisibleRegion + (PvrQwsDrawable *drawable, const PvrQwsRect *rects, int numRects); + +/* Clear the visible region for a window or screen drawable, + effectively removing it from the screen */ +void pvrQwsClearVisibleRegion(PvrQwsDrawable *drawable); + +/* Set the geometry for a drawable. This can only be used on windows */ +void pvrQwsSetGeometry(PvrQwsDrawable *drawable, const PvrQwsRect *rect); + +/* Get the current geometry for a drawable */ +void pvrQwsGetGeometry(PvrQwsDrawable *drawable, PvrQwsRect *rect); + +/* Set the rotation angle in degrees */ +void pvrQwsSetRotation(PvrQwsDrawable *drawable, int angle); + +/* Get the line stride for a drawable. Returns zero if the buffers + are not allocated or have been invalidated */ +int pvrQwsGetStride(PvrQwsDrawable *drawable); + +/* Get the pixel format for a drawable */ +PvrQwsPixelFormat pvrQwsGetPixelFormat(PvrQwsDrawable *drawable); + +/* Get a pointer to the beginning of a drawable's current render buffer. + Returns null if the buffers are not allocated or have been invalidated */ +void *pvrQwsGetRenderBuffer(PvrQwsDrawable *drawable); + +/* Allocate the buffers associated with a drawable. We allocate one buffer + for pixmaps, and several for windows and screens */ +int pvrQwsAllocBuffers(PvrQwsDrawable *drawable); + +/* Free the buffers associated with a drawable */ +void pvrQwsFreeBuffers(PvrQwsDrawable *drawable); + +/* Invalidate the buffers associated with a drawable. The buffers will + still be allocated but the next attempt to swap the buffers will fail */ +void pvrQwsInvalidateBuffers(PvrQwsDrawable *drawable); + +/* Swap the back buffers for a window or screen and copy to the framebuffer */ +int pvrQwsSwapBuffers(PvrQwsDrawable *drawable, int repaintOnly); + +/* Set the swap function for a drawable. When pvrQwsSwapBuffers() + is called on the drawable, the supplied function will be called + instead of copying the drawable contents to the screen. This allows + higher-level compositors to know when a drawable has changed. + The swap function can be set to null to return to normal processing */ +void pvrQwsSetSwapFunction + (PvrQwsDrawable *drawable, PvrQwsSwapFunction func, void *userData); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable_p.h b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable_p.h new file mode 100644 index 0000000000..3ad0693e02 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef PVRQWSDRAWABLE_P_H +#define PVRQWSDRAWABLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// reasons. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include +#include "pvrqwsdrawable.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PVRQWS_MAX_VISIBLE_RECTS 32 +#define PVRQWS_MAX_SCREENS 1 +#define PVRQWS_MAX_BACK_BUFFERS 2 +#define PVRQWS_MAX_FLIP_BUFFERS 2 + +typedef struct { + + PvrQwsRect screenRect; + int screenStride; + PVR2DFORMAT pixelFormat; + int bytesPerPixel; + PVR2DMEMINFO *frameBuffer; + PvrQwsDrawable *screenDrawable; + void *mapped; + int mappedLength; + unsigned long screenStart; + int needsUnmap; + int initialized; + +} PvrQwsScreenInfo; + +typedef struct { + + int refCount; + PvrQwsScreenInfo screens[PVRQWS_MAX_SCREENS]; + PVR2DCONTEXTHANDLE context; + int numDrawables; + unsigned long numFlipBuffers; + PVR2DFLIPCHAINHANDLE flipChain; + PVR2DMEMINFO *flipBuffers[PVRQWS_MAX_FLIP_BUFFERS]; + int usePresentBlit; + PvrQwsDrawable *firstWinId; + +} PvrQwsDisplay; + +extern PvrQwsDisplay pvrQwsDisplay; + +struct _PvrQwsDrawable +{ + PvrQwsDrawableType type; + long winId; + int refCount; + PvrQwsRect rect; + int screen; + PVR2DFORMAT pixelFormat; + PvrQwsRect visibleRects[PVRQWS_MAX_VISIBLE_RECTS]; + int numVisibleRects; + PVR2DMEMINFO *backBuffers[PVRQWS_MAX_BACK_BUFFERS]; + int currentBackBuffer; + int backBuffersValid; + int usingFlipBuffers; + int isFullScreen; + int strideBytes; + int stridePixels; + int rotationAngle; + PvrQwsSwapFunction swapFunction; + void *userData; + PvrQwsDrawable *nextWinId; + +}; + +/* Get the current source and render buffers for a drawable */ +int pvrQwsGetBuffers + (PvrQwsDrawable *drawable, PVR2DMEMINFO **source, PVR2DMEMINFO **render); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwswsegl.c b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwswsegl.c new file mode 100644 index 0000000000..f861838e90 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwswsegl.c @@ -0,0 +1,402 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 +#include +#include +#include +#include +#include "pvrqwsdrawable_p.h" + +#define WSEGL_UNUSED(x) (void)x; + +// If the PVR2D version is not specified, then assume MBX-style headers. +// If the version is defined, then we assume that we have SGX-style headers. +#if !defined(PVR2D_REV_MAJOR) +#define WSEGL_CAP_WINDOWS_USE_HW_SYNC WSEGL_CAP_WINDOWS_USE_MBX_SYNC +#define WSEGL_CAP_PIXMAPS_USE_HW_SYNC WSEGL_CAP_PIXMAPS_USE_MBX_SYNC +#endif + +/* Capability information for the display */ +static WSEGLCaps const wseglDisplayCaps[] = { + {WSEGL_CAP_WINDOWS_USE_HW_SYNC, 1}, + {WSEGL_CAP_PIXMAPS_USE_HW_SYNC, 1}, + {WSEGL_NO_CAPS, 0} +}; + +/* Configuration information for the display */ +static WSEGLConfig wseglDisplayConfigs[] = { + {WSEGL_DRAWABLE_WINDOW, WSEGL_PIXELFORMAT_565, WSEGL_FALSE, + 0, 0, 0, WSEGL_OPAQUE, 0}, + {WSEGL_DRAWABLE_PIXMAP, WSEGL_PIXELFORMAT_565, WSEGL_FALSE, + 0, 0, 0, WSEGL_OPAQUE, 0}, + {WSEGL_NO_DRAWABLE, 0, 0, 0, 0, 0, 0, 0} +}; + +/* Determine if nativeDisplay is a valid display handle */ +static WSEGLError wseglIsDisplayValid(NativeDisplayType nativeDisplay) +{ + /* We only have the default display in this system */ + if (nativeDisplay == WSEGL_DEFAULT_DISPLAY) + return WSEGL_SUCCESS; + else + return WSEGL_BAD_NATIVE_DISPLAY; +} + +/* Initialize a native display for use with WSEGL */ +static WSEGLError wseglInitializeDisplay + (NativeDisplayType nativeDisplay, WSEGLDisplayHandle *display, + const WSEGLCaps **caps, WSEGLConfig **configs) +{ + WSEGLPixelFormat pixelFormat; + + /* Bail out if the native display is incorrect */ + if (nativeDisplay != WSEGL_DEFAULT_DISPLAY) + return WSEGL_CANNOT_INITIALISE; + + /* Open the PVR/QWS display, which will initialize the framebuffer */ + if (!pvrQwsDisplayOpen()) + return WSEGL_CANNOT_INITIALISE; + + /* Convert the PVR2D pixel format into a WSEGL pixel format */ + switch (pvrQwsDisplay.screens[0].pixelFormat) { + case PVR2D_RGB565: + pixelFormat = WSEGL_PIXELFORMAT_565; + break; + + case PVR2D_ARGB4444: + pixelFormat = WSEGL_PIXELFORMAT_4444; + break; + + case PVR2D_ARGB8888: + pixelFormat = WSEGL_PIXELFORMAT_8888; + break; + + default: + pvrQwsDisplayClose(); + return WSEGL_CANNOT_INITIALISE; + } + wseglDisplayConfigs[0].ePixelFormat = pixelFormat; + wseglDisplayConfigs[1].ePixelFormat = pixelFormat; + + /* The display has been initialized */ + *display = (WSEGLDisplayHandle)&pvrQwsDisplay; + *caps = wseglDisplayCaps; + *configs = wseglDisplayConfigs; + return WSEGL_SUCCESS; +} + +/* Close the WSEGL display */ +static WSEGLError wseglCloseDisplay(WSEGLDisplayHandle display) +{ + if (display == (WSEGLDisplayHandle)&pvrQwsDisplay) + pvrQwsDisplayClose(); + return WSEGL_SUCCESS; +} + +static WSEGLRotationAngle wseglRotationValue(int degrees) +{ + switch (degrees) { + case 90: return WSEGL_ROTATE_90; + case 180: return WSEGL_ROTATE_180; + case 270: return WSEGL_ROTATE_270; + default: return WSEGL_ROTATE_0; + } +} + +/* Create the WSEGL drawable version of a native window */ +static WSEGLError wseglCreateWindowDrawable + (WSEGLDisplayHandle display, WSEGLConfig *config, + WSEGLDrawableHandle *drawable, NativeWindowType nativeWindow, + WSEGLRotationAngle *rotationAngle) +{ + PvrQwsDrawable *draw; + + WSEGL_UNUSED(display); + WSEGL_UNUSED(config); + + /* Check for special handles that indicate framebuffer screens */ + if (nativeWindow >= (NativeWindowType)0 && + nativeWindow < (NativeWindowType)PVRQWS_MAX_SCREENS) { + PvrQwsDrawable *screen = pvrQwsScreenWindow((int)nativeWindow); + if (!screen) + return WSEGL_OUT_OF_MEMORY; + *drawable = (WSEGLDrawableHandle)screen; + if (!pvrQwsAllocBuffers(screen)) + return WSEGL_OUT_OF_MEMORY; + *rotationAngle = wseglRotationValue(screen->rotationAngle); + return WSEGL_SUCCESS; + } + + /* The native window is the winId - fetch the underlying drawable */ + draw = pvrQwsFetchWindow((long)nativeWindow); + if (!draw) + return WSEGL_BAD_DRAWABLE; + + /* The drawable is ready to go */ + *drawable = (WSEGLDrawableHandle)draw; + *rotationAngle = wseglRotationValue(draw->rotationAngle); + if (!pvrQwsAllocBuffers(draw)) + return WSEGL_OUT_OF_MEMORY; + return WSEGL_SUCCESS; +} + +/* Create the WSEGL drawable version of a native pixmap */ +static WSEGLError wseglCreatePixmapDrawable + (WSEGLDisplayHandle display, WSEGLConfig *config, + WSEGLDrawableHandle *drawable, NativePixmapType nativePixmap, + WSEGLRotationAngle *rotationAngle) +{ + WSEGL_UNUSED(display); + WSEGL_UNUSED(config); + if (!nativePixmap) + return WSEGL_BAD_NATIVE_PIXMAP; + if (!pvrQwsAllocBuffers((PvrQwsDrawable *)nativePixmap)) + return WSEGL_OUT_OF_MEMORY; + *drawable = (WSEGLDrawableHandle)nativePixmap; + *rotationAngle = WSEGL_ROTATE_0; + return WSEGL_SUCCESS; +} + +/* Delete a specific drawable */ +static WSEGLError wseglDeleteDrawable(WSEGLDrawableHandle _drawable) +{ + PvrQwsDrawable *drawable = (PvrQwsDrawable *)_drawable; + if (!drawable || drawable->type == PvrQwsScreen) + return WSEGL_SUCCESS; + if (pvrQwsDisplay.numFlipBuffers == 0) + pvrQwsFreeBuffers(drawable); + if (pvrQwsReleaseWindow(drawable)) + pvrQwsDestroyDrawable(drawable); + return WSEGL_SUCCESS; +} + +/* Swap the contents of a drawable to the screen */ +static WSEGLError wseglSwapDrawable + (WSEGLDrawableHandle _drawable, unsigned long data) +{ + WSEGL_UNUSED(data); + PvrQwsDrawable *drawable = (PvrQwsDrawable *)_drawable; + if (drawable->type != PvrQwsPixmap && !pvrQwsSwapBuffers(drawable, 0)) + return WSEGL_BAD_DRAWABLE; + else + return WSEGL_SUCCESS; +} + +/* Set the swap interval of a window drawable */ +static WSEGLError wseglSwapControlInterval + (WSEGLDrawableHandle drawable, unsigned long interval) +{ + WSEGL_UNUSED(drawable); + if (pvrQwsDisplay.flipChain) { + PVR2DSetPresentFlipProperties + (pvrQwsDisplay.context, pvrQwsDisplay.flipChain, + PVR2D_PRESENT_PROPERTY_INTERVAL, 0, 0, 0, NULL, interval); + } + return WSEGL_SUCCESS; +} + +/* Flush native rendering requests on a drawable */ +static WSEGLError wseglWaitNative + (WSEGLDrawableHandle drawable, unsigned long engine) +{ + WSEGL_UNUSED(drawable); + if (engine == WSEGL_DEFAULT_NATIVE_ENGINE) + return WSEGL_SUCCESS; + else + return WSEGL_BAD_NATIVE_ENGINE; +} + +/* Copy color data from a drawable to a native pixmap */ +static WSEGLError wseglCopyFromDrawable + (WSEGLDrawableHandle _drawable, NativePixmapType nativePixmap) +{ + PvrQwsDrawable *drawable = (PvrQwsDrawable *)_drawable; + PvrQwsDrawable *pixmap = (PvrQwsDrawable *)nativePixmap; + PVR2DBLTINFO blit; + + if (!drawable || !drawable->backBuffersValid) + return WSEGL_BAD_NATIVE_WINDOW; + if (!pixmap || !pixmap->backBuffersValid) + return WSEGL_BAD_NATIVE_PIXMAP; + + memset(&blit, 0, sizeof(blit)); + + blit.CopyCode = PVR2DROPcopy; + blit.BlitFlags = PVR2D_BLIT_DISABLE_ALL; + + blit.pSrcMemInfo = drawable->backBuffers[drawable->currentBackBuffer]; + blit.SrcStride = drawable->strideBytes; + blit.SrcX = 0; + blit.SrcY = 0; + blit.SizeX = drawable->rect.width; + blit.SizeY = drawable->rect.height; + blit.SrcFormat = drawable->pixelFormat; + + blit.pDstMemInfo = pixmap->backBuffers[pixmap->currentBackBuffer]; + blit.DstStride = pixmap->strideBytes; + blit.DstX = 0; + blit.DstY = 0; + blit.DSizeX = pixmap->rect.width; + blit.DSizeY = pixmap->rect.height; + blit.DstFormat = pixmap->pixelFormat; + + PVR2DBlt(pvrQwsDisplay.context, &blit); + PVR2DQueryBlitsComplete + (pvrQwsDisplay.context, pixmap->backBuffers[pixmap->currentBackBuffer], 1); + + return WSEGL_SUCCESS; +} + +/* Copy color data from a PBuffer to a native pixmap */ +static WSEGLError wseglCopyFromPBuffer + (void *address, unsigned long width, unsigned long height, + unsigned long stride, WSEGLPixelFormat format, + NativePixmapType nativePixmap) +{ + PvrQwsDrawable *pixmap = (PvrQwsDrawable *)nativePixmap; + PVR2DFORMAT pixelFormat; + + if (!pixmap) + return WSEGL_BAD_NATIVE_PIXMAP; + + /* We can only copy under certain conditions */ + switch (format) { + case WSEGL_PIXELFORMAT_565: + pixelFormat = PVR2D_RGB565; break; + case WSEGL_PIXELFORMAT_4444: + pixelFormat = PVR2D_ARGB4444; break; + case WSEGL_PIXELFORMAT_8888: + pixelFormat = PVR2D_ARGB8888; break; + default: + return WSEGL_BAD_CONFIG; + } + if (width > (unsigned long)(pixmap->rect.width) || + height > (unsigned long)(pixmap->rect.height) || + pixelFormat != pixmap->pixelFormat) { + return WSEGL_BAD_CONFIG; + } + + /* We'd like to use PVR2DBlt to do this, but there is no easy way + to map the virtual "address" into physical space to be able + to use the hardware assist. Use memcpy to do the work instead. + Note: PBuffer's are upside down, so we copy from the bottom up */ + char *srcaddr = (char *)address; + char *dstaddr = (char *)(pixmap->backBuffers[pixmap->currentBackBuffer]->pBase); + int dststride = pixmap->strideBytes; + int srcwidth = ((int)width) * pvrQwsDisplay.screens[0].bytesPerPixel; + srcaddr += height * stride; + while (height > 0) { + srcaddr -= (int)stride; + memcpy(dstaddr, srcaddr, srcwidth); + dstaddr += dststride; + --height; + } + return WSEGL_SUCCESS; +} + +/* Return the parameters of a drawable that are needed by the EGL layer */ +static WSEGLError wseglGetDrawableParameters + (WSEGLDrawableHandle _drawable, WSEGLDrawableParams *sourceParams, + WSEGLDrawableParams *renderParams) +{ + PvrQwsDrawable *drawable = (PvrQwsDrawable *)_drawable; + PVR2DMEMINFO *source, *render; + WSEGLPixelFormat pixelFormat; + + if (!pvrQwsGetBuffers(drawable, &source, &render)) + return WSEGL_BAD_DRAWABLE; + + switch (drawable->pixelFormat) { + case PVR2D_RGB565: + default: + pixelFormat = WSEGL_PIXELFORMAT_565; + break; + + case PVR2D_ARGB4444: + pixelFormat = WSEGL_PIXELFORMAT_4444; + break; + + case PVR2D_ARGB8888: + pixelFormat = WSEGL_PIXELFORMAT_8888; + break; + } + + sourceParams->ui32Width = drawable->rect.width; + sourceParams->ui32Height = drawable->rect.height; + sourceParams->ui32Stride = drawable->stridePixels; + sourceParams->ePixelFormat = pixelFormat; + sourceParams->pvLinearAddress = source->pBase; + sourceParams->ui32HWAddress = source->ui32DevAddr; + sourceParams->hPrivateData = source->hPrivateData; + + renderParams->ui32Width = drawable->rect.width; + renderParams->ui32Height = drawable->rect.height; + renderParams->ui32Stride = drawable->stridePixels; + renderParams->ePixelFormat = pixelFormat; + renderParams->pvLinearAddress = render->pBase; + renderParams->ui32HWAddress = render->ui32DevAddr; + renderParams->hPrivateData = render->hPrivateData; + + return WSEGL_SUCCESS; +} + +static WSEGL_FunctionTable const wseglFunctions = { + WSEGL_VERSION, + wseglIsDisplayValid, + wseglInitializeDisplay, + wseglCloseDisplay, + wseglCreateWindowDrawable, + wseglCreatePixmapDrawable, + wseglDeleteDrawable, + wseglSwapDrawable, + wseglSwapControlInterval, + wseglWaitNative, + wseglCopyFromDrawable, + wseglCopyFromPBuffer, + wseglGetDrawableParameters +}; + +/* Return the table of WSEGL functions to the EGL implementation */ +const WSEGL_FunctionTable *WSEGL_GetFunctionTablePointer(void) +{ + return &wseglFunctions; +} diff --git a/src/plugins/gfxdrivers/powervr/README b/src/plugins/gfxdrivers/powervr/README new file mode 100644 index 0000000000..513e7f5e9e --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/README @@ -0,0 +1,66 @@ +PowerVR QScreen Driver +====================== + +This QScreen plugin driver allows the QtOpenGl module to integrate with PowerVR +hardware from Imagination Technologies. Using this plugin, applications may use +QGLWidget & QGLPixelBuffer with OpenGL ES. The integration with PowerVR drivers +is built as two libraries: The actual QScreen plugin used by Qt (in the +pvreglscreen directory) and a WSEGL plugin for the PowerVR drivers (in the +QWSWSEGL directory). + +Qt/Embedded needs to be configured with the QT_QWS_CLIENTBLIT and +QT_NO_QWS_CURSOR defines. + +The PowerVR drivers provide the WSEGL plugin API to allow window systems such as +QWS to integrate correctly. In order to use the integration, the WSEGL plugin +(libpvrQWSWSEGL.so, usually installed into the Qt library directory) must be in +the LD library path. The PowerVR driver also needs to be told which WSEGL library +to use. This is done by creating/modifying /etc/powervr.ini: + +[default] +WindowSystem=libpvrQWSWSEGL.so + +Note: It is important that the /etc/powervr.ini file not contain ^M (Ctrl-M) DOS +end of line markers at the end of its lines. If ^M markers are present, then the +libpvrQWSWSEGL.so driver will not be loaded and the default null Linux driver +will be loaded silently instead. Make sure that the end of line markers are +strictly Unix-style markers. + + +*************************************************************************** +* IMPORTANT: To build the QScreen plugin and the WSEGL library it depends * +* on, the pvr2d.h, wsegl.h headers for your platform are required. You * +* can find a copy of these headers in src/3rdparty/powervr for SGX based * +* platforms like the TI OMAP3xxx. They probably will not work on MBX * +* because of differences in the layout of certain PVR2D structures. * +* You can tell Qt where to find the actual headers for your system by * +* setting QMAKE_INCDIR_POWERVR in the mkspec. * +*************************************************************************** + +When you start a Qt/Embedded application, you should modify the QWS_DISPLAY +environment variable to use the "powervr" driver instead of "LinuxFb". For +example, if your original QWS_DISPLAY variable was: + + LinuxFb:mmWidth40:mmHeight54:0 + +then it should be changed to: + + powervr:mmWidth40:mmHeight54:0 + +To test the OpenGL ES integration, you can use the hellogl_es example and run it +on the device with: + + hellogl_es -qws + +The driver also supports screen rotation if Qt is configured with the +-qt-gfx-transformed option and the QWS_DISPLAY variable is wrapped in a +"Transformed" declaration: + + Transformed:powervr:mmWidth40:mmHeight54:Rot90:0 + +Know Issues: + * A QGLWidget may not have window decorations if it is a top-level window. + * On some platforms, starting a QWS application after the system has been up + for a long time may cause the driver to fail. This is due to fragmentation + of main memory prevening older PowerVR drivers from allocating a contiguous + region of phyical RAM for the GL surface. diff --git a/src/plugins/gfxdrivers/powervr/powervr.pri b/src/plugins/gfxdrivers/powervr/powervr.pri new file mode 100644 index 0000000000..9df8c0ed5b --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/powervr.pri @@ -0,0 +1,2 @@ + +INCLUDEPATH += $$QMAKE_INCDIR_POWERVR diff --git a/src/plugins/gfxdrivers/powervr/powervr.pro b/src/plugins/gfxdrivers/powervr/powervr.pro new file mode 100644 index 0000000000..f31ad042d8 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/powervr.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = QWSWSEGL pvreglscreen +CONFIG += ordered diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp new file mode 100644 index 0000000000..c981e0d300 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "pvreglscreen.h" +#include "pvreglwindowsurface.h" +#include "pvrqwsdrawable_p.h" +#include +#include +#ifndef QT_NO_QWS_TRANSFORMED +#include +#endif +#include +#include +#include +#include +#include + +//![0] +PvrEglScreen::PvrEglScreen(int displayId) + : QGLScreen(displayId) +{ + setOptions(NativeWindows); + setSupportsBlitInClients(true); + setSurfaceFunctions(new PvrEglScreenSurfaceFunctions(this, displayId)); +//![0] + fd = -1; + ttyfd = -1; + doGraphicsMode = true; + oldKdMode = KD_TEXT; + parent = 0; + + // Make sure that the EGL layer is initialized and the drivers loaded. + EGLDisplay dpy = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY); + if (!eglInitialize(dpy, 0, 0)) + qWarning("Could not initialize EGL display - are the drivers loaded?"); + + // Make sure that screen 0 is initialized. + pvrQwsScreenWindow(0); +} + +PvrEglScreen::~PvrEglScreen() +{ + if (fd >= 0) + ::close(fd); +} + +bool PvrEglScreen::initDevice() +{ + openTty(); + return true; +} + +bool PvrEglScreen::connect(const QString &displaySpec) +{ + if (!pvrQwsDisplayOpen()) + return false; + + // Initialize the QScreen properties. + data = (uchar *)(pvrQwsDisplay.screens[0].mapped); + w = pvrQwsDisplay.screens[0].screenRect.width; + h = pvrQwsDisplay.screens[0].screenRect.height; + lstep = pvrQwsDisplay.screens[0].screenStride; + dw = w; + dh = h; + size = h * lstep; + mapsize = size; + switch (pvrQwsDisplay.screens[0].pixelFormat) { + case PVR2D_RGB565: + d = 16; + setPixelFormat(QImage::Format_RGB16); + break; + case PVR2D_ARGB4444: + d = 16; + setPixelFormat(QImage::Format_ARGB4444_Premultiplied); + break; + case PVR2D_ARGB8888: + d = 32; + setPixelFormat(QImage::Format_ARGB32_Premultiplied); + break; + default: + pvrQwsDisplayClose(); + qWarning("PvrEglScreen::connect: unsupported pixel format %d", (int)(pvrQwsDisplay.screens[0].pixelFormat)); + return false; + } + + // 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) { + const int dpi = 72; + physWidth = qRound(dw * 25.4 / dpi); + physHeight = qRound(dh * 25.4 / dpi); + } + + // Find the name of the tty device to use. + QRegExp ttyRegExp(QLatin1String("tty=(.*)")); + if (displayArgs.indexOf(ttyRegExp) != -1) + ttyDevice = ttyRegExp.cap(1); + if (displayArgs.contains(QLatin1String("nographicsmodeswitch"))) + doGraphicsMode = false; + + // The screen is ready. + return true; +} + +void PvrEglScreen::disconnect() +{ + pvrQwsDisplayClose(); +} + +void PvrEglScreen::shutdownDevice() +{ + closeTty(); +} + +void PvrEglScreen::blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion) +{ + QGLScreen::blit(img, topLeft, region); + sync(); +} + +void PvrEglScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + QGLScreen::solidFill(color, region); + sync(); +} + +bool PvrEglScreen::chooseContext + (QGLContext *context, const QGLContext *shareContext) +{ + // We use PvrEglScreenSurfaceFunctions instead. + Q_UNUSED(context); + Q_UNUSED(shareContext); + return false; +} + +bool PvrEglScreen::hasOpenGL() +{ + return true; +} + +//![1] +QWSWindowSurface* PvrEglScreen::createSurface(QWidget *widget) const +{ + if (qobject_cast(widget)) + return new PvrEglWindowSurface(widget, (PvrEglScreen *)this, displayId); + + return QScreen::createSurface(widget); +} + +QWSWindowSurface* PvrEglScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("PvrEgl")) + return new PvrEglWindowSurface(); + + return QScreen::createSurface(key); +} +//![1] + +#ifndef QT_NO_QWS_TRANSFORMED + +static const QScreen *parentScreen + (const QScreen *current, const QScreen *lookingFor) +{ + if (!current) + return 0; + switch (current->classId()) { + case QScreen::ProxyClass: + case QScreen::TransformedClass: { + const QScreen *child = + static_cast(current)->screen(); + if (child == lookingFor) + return current; + else + return parentScreen(child, lookingFor); + } + // Not reached. + + case QScreen::MultiClass: { + QList screens = current->subScreens(); + foreach (QScreen *screen, screens) { + if (screen == lookingFor) + return current; + const QScreen *parent = parentScreen(screen, lookingFor); + if (parent) + return parent; + } + } + break; + + default: break; + } + return 0; +} + +int PvrEglScreen::transformation() const +{ + // We need to search for our parent screen, which is assumed to be + // "Transformed". If it isn't, then there is no transformation. + // There is no direct method to get the parent screen so we need + // to search every screen until we find ourselves. + if (!parent && qt_screen != this) + parent = parentScreen(qt_screen, this); + if (!parent) + return 0; + if (parent->classId() != QScreen::TransformedClass) + return 0; + return 90 * static_cast(parent) + ->transformOrientation(); +} + +#else + +int PvrEglScreen::transformation() const +{ + return 0; +} + +#endif + +void PvrEglScreen::sync() +{ + // Put code here to synchronize 2D and 3D operations if necessary. +} + +void PvrEglScreen::openTty() +{ + const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0}; + + if (ttyDevice.isEmpty()) { + for (const char * const *dev = devs; *dev; ++dev) { + ttyfd = ::open(*dev, O_RDWR); + if (ttyfd != -1) + break; + } + } else { + ttyfd = ::open(ttyDevice.toAscii().constData(), O_RDWR); + } + + if (ttyfd == -1) + return; + + ::fcntl(ttyfd, F_SETFD, FD_CLOEXEC); + + 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"; + ::write(ttyfd, termctl, sizeof(termctl)); +} + +void PvrEglScreen::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"; + ::write(ttyfd, termctl, sizeof(termctl)); + + ::close(ttyfd); + ttyfd = -1; +} + +//![2] +bool PvrEglScreenSurfaceFunctions::createNativeWindow(QWidget *widget, EGLNativeWindowType *native) +{ +//![2] + QWSWindowSurface *surface = + static_cast(widget->windowSurface()); + if (!surface) { + // The widget does not have a surface yet, so give it one. + surface = new PvrEglWindowSurface(widget, screen, displayId); + widget->setWindowSurface(surface); + } else if (surface->key() != QLatin1String("PvrEgl")) { + // The application has attached a QGLContext to an ordinary QWidget. + // Replace the widget's window surface with a new one that can do GL. + QRect geometry = widget->frameGeometry(); + geometry.moveTo(widget->mapToGlobal(QPoint(0, 0))); + surface = new PvrEglWindowSurface(widget, screen, displayId); + surface->setGeometry(geometry); + widget->setWindowSurface(surface); + widget->setAttribute(Qt::WA_NoSystemBackground, true); + } + PvrEglWindowSurface *nsurface = static_cast(surface); + *native = (EGLNativeWindowType)(nsurface->nativeDrawable()); + return true; +} diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h new file mode 100644 index 0000000000..efb2406771 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef PVREGLSCREEN_H +#define PVREGLSCREEN_H + +#include +#include +#include "pvrqwsdrawable.h" + +class PvrEglScreen; + +class PvrEglScreenSurfaceFunctions : public QGLScreenSurfaceFunctions +{ +public: + PvrEglScreenSurfaceFunctions(PvrEglScreen *s, int screenNum) + : screen(s), displayId(screenNum) {} + + bool createNativeWindow(QWidget *widget, EGLNativeWindowType *native); + +private: + PvrEglScreen *screen; + int displayId; +}; + +class PvrEglScreen : public QGLScreen +{ +public: + PvrEglScreen(int displayId); + ~PvrEglScreen(); + + bool initDevice(); + bool connect(const QString &displaySpec); + void disconnect(); + void shutdownDevice(); + void setMode(int, int, int) {} + + void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + void solidFill(const QColor &color, const QRegion ®ion); + + bool chooseContext(QGLContext *context, const QGLContext *shareContext); + bool hasOpenGL(); + + QWSWindowSurface* createSurface(QWidget *widget) const; + QWSWindowSurface* createSurface(const QString &key) const; + + int transformation() const; + +private: + void sync(); + void openTty(); + void closeTty(); + + int fd; + int ttyfd, oldKdMode; + QString ttyDevice; + bool doGraphicsMode; + mutable const QScreen *parent; +}; + +#endif diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro new file mode 100644 index 0000000000..675be85460 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro @@ -0,0 +1,27 @@ +TEMPLATE = lib +TARGET = qgfxpvregl +CONFIG += qt plugin warn_on +QT += opengl + +LIBS += -lpvrQWSWSEGL + +DEFINES += QT_QWS_CLIENTBLIT + +INCLUDEPATH += ../QWSWSEGL + + +HEADERS = \ + pvreglscreen.h \ + pvreglwindowsurface.h + +SOURCES = \ + pvreglscreenplugin.cpp \ + pvreglscreen.cpp \ + pvreglwindowsurface.cpp + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +include(../powervr.pri) \ No newline at end of file diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp new file mode 100644 index 0000000000..872285e818 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "pvreglscreen.h" + +#include +#include + +class PvrEglScreenPlugin : public QScreenDriverPlugin +{ +public: + PvrEglScreenPlugin(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +PvrEglScreenPlugin::PvrEglScreenPlugin() + : QScreenDriverPlugin() +{ +} + +QStringList PvrEglScreenPlugin::keys() const +{ + return (QStringList() << "powervr"); +} + +QScreen* PvrEglScreenPlugin::create(const QString& driver, int displayId) +{ + if (driver.toLower() != "powervr") + return 0; + + return new PvrEglScreen(displayId); +} + +Q_EXPORT_PLUGIN2(qgfxpvregl, PvrEglScreenPlugin) diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.cpp b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.cpp new file mode 100644 index 0000000000..43648159c8 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "pvreglwindowsurface.h" +#include "pvreglscreen.h" +#include +#include +#include + +PvrEglWindowSurface::PvrEglWindowSurface + (QWidget *widget, PvrEglScreen *screen, int screenNum) + : QWSGLWindowSurface(widget) +{ + setSurfaceFlags(QWSWindowSurface::Opaque); + + this->widget = widget; + this->screen = screen; + this->pdevice = 0; + + QPoint pos = offset(widget); + QSize size = widget->size(); + + PvrQwsRect pvrRect; + pvrRect.x = pos.x(); + pvrRect.y = pos.y(); + pvrRect.width = size.width(); + pvrRect.height = size.height(); + transformRects(&pvrRect, 1); + + // Try to recover a previous PvrQwsDrawable object for the widget + // if there is one. This can happen when a PvrEglWindowSurface + // is created for a widget, bound to a EGLSurface, and then destroyed. + // When a new PvrEglWindowSurface is created for the widget, it will + // pick up the previous PvrQwsDrawable if the EGLSurface has not been + // destroyed in the meantime. + drawable = pvrQwsFetchWindow((long)widget); + if (drawable) + pvrQwsSetGeometry(drawable, &pvrRect); + else + drawable = pvrQwsCreateWindow(screenNum, (long)widget, &pvrRect); + pvrQwsSetRotation(drawable, screen->transformation()); +} + +PvrEglWindowSurface::PvrEglWindowSurface() + : QWSGLWindowSurface() +{ + setSurfaceFlags(QWSWindowSurface::Opaque); + drawable = 0; + widget = 0; + screen = 0; + pdevice = 0; +} + +PvrEglWindowSurface::~PvrEglWindowSurface() +{ + // Release the PvrQwsDrawable. If it is bound to an EGLSurface, + // then it will stay around until a new PvrEglWindowSurface is + // created for the widget. If it is not bound to an EGLSurface, + // it will be destroyed immediately. + if (drawable && pvrQwsReleaseWindow(drawable)) + pvrQwsDestroyDrawable(drawable); + + delete pdevice; +} + +bool PvrEglWindowSurface::isValid() const +{ + return (widget != 0); +} + +void PvrEglWindowSurface::setGeometry(const QRect &rect) +{ + if (drawable) { + // XXX: adjust for the screen offset. + PvrQwsRect pvrRect; + pvrRect.x = rect.x(); + pvrRect.y = rect.y(); + pvrRect.width = rect.width(); + pvrRect.height = rect.height(); + transformRects(&pvrRect, 1); + pvrQwsSetGeometry(drawable, &pvrRect); + pvrQwsSetRotation(drawable, screen->transformation()); + } + QWSGLWindowSurface::setGeometry(rect); +} + +bool PvrEglWindowSurface::move(const QPoint &offset) +{ + QRect rect = geometry().translated(offset); + if (drawable) { + PvrQwsRect pvrRect; + pvrRect.x = rect.x(); + pvrRect.y = rect.y(); + pvrRect.width = rect.width(); + pvrRect.height = rect.height(); + transformRects(&pvrRect, 1); + pvrQwsSetGeometry(drawable, &pvrRect); + pvrQwsSetRotation(drawable, screen->transformation()); + } + return QWSGLWindowSurface::move(offset); +} + +QByteArray PvrEglWindowSurface::permanentState() const +{ + // Nothing interesting to pass to the server just yet. + return QByteArray(); +} + +void PvrEglWindowSurface::setPermanentState(const QByteArray &state) +{ + Q_UNUSED(state); +} + +void PvrEglWindowSurface::flush + (QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + // The GL paint engine is responsible for the swapBuffers() call. + // If we were to call the base class's implementation of flush() + // then it would fetch the image() and manually blit it to the + // screeen instead of using the fast PVR2D blit. + Q_UNUSED(widget); + Q_UNUSED(region); + Q_UNUSED(offset); +} + +QImage PvrEglWindowSurface::image() const +{ + if (drawable) { + PvrQwsRect pvrRect; + pvrQwsGetGeometry(drawable, &pvrRect); + void *data = pvrQwsGetRenderBuffer(drawable); + if (data) { + return QImage((uchar *)data, pvrRect.width, pvrRect.height, + pvrQwsGetStride(drawable), screen->pixelFormat()); + } + } + return QImage(16, 16, screen->pixelFormat()); +} + +QPaintDevice *PvrEglWindowSurface::paintDevice() +{ + return widget; +} + +void PvrEglWindowSurface::setDirectRegion(const QRegion &r, int id) +{ + QWSGLWindowSurface::setDirectRegion(r, id); + + if (!drawable) + return; + + // Clip the region to the window boundaries in case the child + // is partially outside the geometry of the parent. + QWidget *window = widget->window(); + QRegion region = r; + if (widget != window) { + QRect rect = window->geometry(); + rect.moveTo(window->mapToGlobal(QPoint(0, 0))); + region = region.intersect(rect); + } + + if (region.isEmpty()) { + pvrQwsClearVisibleRegion(drawable); + } else if (region.rectCount() == 1) { + QRect rect = region.boundingRect(); + PvrQwsRect pvrRect; + pvrRect.x = rect.x(); + pvrRect.y = rect.y(); + pvrRect.width = rect.width(); + pvrRect.height = rect.height(); + transformRects(&pvrRect, 1); + pvrQwsSetVisibleRegion(drawable, &pvrRect, 1); + pvrQwsSetRotation(drawable, screen->transformation()); + if (!pvrQwsSwapBuffers(drawable, 1)) + screen->solidFill(QColor(0, 0, 0), region); + } else { + QVector rects = region.rects(); + PvrQwsRect *pvrRects = new PvrQwsRect [rects.size()]; + for (int index = 0; index < rects.size(); ++index) { + QRect rect = rects[index]; + pvrRects[index].x = rect.x(); + pvrRects[index].y = rect.y(); + pvrRects[index].width = rect.width(); + pvrRects[index].height = rect.height(); + } + transformRects(pvrRects, rects.size()); + pvrQwsSetVisibleRegion(drawable, pvrRects, rects.size()); + pvrQwsSetRotation(drawable, screen->transformation()); + if (!pvrQwsSwapBuffers(drawable, 1)) + screen->solidFill(QColor(0, 0, 0), region); + delete [] pvrRects; + } +} + +void PvrEglWindowSurface::transformRects(PvrQwsRect *rects, int count) const +{ + switch (screen->transformation()) { + case 0: break; + + case 90: + { + for (int index = 0; index < count; ++index) { + int x = rects[index].y; + int y = screen->height() - (rects[index].x + rects[index].width); + rects[index].x = x; + rects[index].y = y; + qSwap(rects[index].width, rects[index].height); + } + } + break; + + case 180: + { + for (int index = 0; index < count; ++index) { + int x = screen->width() - (rects[index].x + rects[index].width); + int y = screen->height() - (rects[index].y + rects[index].height); + rects[index].x = x; + rects[index].y = y; + } + } + break; + + case 270: + { + for (int index = 0; index < count; ++index) { + int x = screen->width() - (rects[index].y + rects[index].height); + int y = rects[index].x; + rects[index].x = x; + rects[index].y = y; + qSwap(rects[index].width, rects[index].height); + } + } + break; + } +} diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h new file mode 100644 index 0000000000..9f5600c136 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef PVREGLWINDOWSURFACE_H +#define PVREGLWINDOWSURFACE_H + +#include +#include "pvrqwsdrawable.h" + +class PvrEglScreen; + +class PvrEglWindowSurface : public QWSGLWindowSurface +{ +public: + PvrEglWindowSurface(QWidget *widget, PvrEglScreen *screen, int screenNum); + PvrEglWindowSurface(); + ~PvrEglWindowSurface(); + + QString key() const { return QLatin1String("PvrEgl"); } + + bool isValid() const; + + void setGeometry(const QRect &rect); + bool move(const QPoint &offset); + + QByteArray permanentState() const; + void setPermanentState(const QByteArray &state); + + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + + QImage image() const; + QPaintDevice *paintDevice(); + + void setDirectRegion(const QRegion ®ion, int id); + + long nativeDrawable() const { return (long)widget; } + +private: + QWidget *widget; + PvrQwsDrawable *drawable; + PvrEglScreen *screen; + QPaintDevice *pdevice; + + void transformRects(PvrQwsRect *rects, int count) const; +}; + +#endif diff --git a/src/plugins/gfxdrivers/qvfb/main.cpp b/src/plugins/gfxdrivers/qvfb/main.cpp new file mode 100644 index 0000000000..fb275818b9 --- /dev/null +++ b/src/plugins/gfxdrivers/qvfb/main.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 +#include +#include + +#ifndef QT_NO_LIBRARY +QT_BEGIN_NAMESPACE + +class ScreenVfbDriver : public QScreenDriverPlugin +{ +public: + ScreenVfbDriver(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +ScreenVfbDriver::ScreenVfbDriver() +: QScreenDriverPlugin() +{ +} + +QStringList ScreenVfbDriver::keys() const +{ + QStringList list; + list << "QVFb"; + return list; +} + +QScreen* ScreenVfbDriver::create(const QString& driver, int displayId) +{ + if (driver.toLower() == "qvfb") + return new QVFbScreen(displayId); + + return 0; +} + +Q_EXPORT_STATIC_PLUGIN(ScreenVfbDriver) +Q_EXPORT_PLUGIN2(qscreenvfb, ScreenVfbDriver) + +QT_END_NAMESPACE +#endif //QT_NO_LIBRARY diff --git a/src/plugins/gfxdrivers/qvfb/qvfb.pro b/src/plugins/gfxdrivers/qvfb/qvfb.pro new file mode 100644 index 0000000000..a0996e77dc --- /dev/null +++ b/src/plugins/gfxdrivers/qvfb/qvfb.pro @@ -0,0 +1,19 @@ +TARGET = qscreenvfb +include(../../qpluginbase.pri) + +DEFINES += QT_QWS_QVFB QT_QWS_MOUSE_QVFB QT_QWS_KBD_QVFB + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +HEADERS = \ + $$QT_SOURCE_TREE/src/gui/embedded/qscreenvfb_qws.h \ + $$QT_SOURCE_TREE/src/gui/embedded/qkbdvfb_qws.h \ + $$QT_SOURCE_TREE/src/gui/embedded/qmousevfb_qws.h + +SOURCES = main.cpp \ + $$QT_SOURCE_TREE/src/gui/embedded/qscreenvfb_qws.cpp \ + $$QT_SOURCE_TREE/src/gui/embedded/qkbdvfb_qws.cpp \ + $$QT_SOURCE_TREE/src/gui/embedded/qmousevfb_qws.cpp + +target.path += $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target diff --git a/src/plugins/gfxdrivers/transformed/main.cpp b/src/plugins/gfxdrivers/transformed/main.cpp new file mode 100644 index 0000000000..da619bb3e5 --- /dev/null +++ b/src/plugins/gfxdrivers/transformed/main.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 +#include +#include +#ifndef QT_NO_LIBRARY +QT_BEGIN_NAMESPACE + +class GfxTransformedDriver : public QScreenDriverPlugin +{ +public: + GfxTransformedDriver(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +GfxTransformedDriver::GfxTransformedDriver() +: QScreenDriverPlugin() +{ +} + +QStringList GfxTransformedDriver::keys() const +{ + QStringList list; + list << "Transformed"; + return list; +} + +QScreen* GfxTransformedDriver::create(const QString& driver, int displayId) +{ +#ifndef QT_NO_QWS_TRANSFORMED + if (driver.toLower() == "transformed") + return new QTransformedScreen(displayId); +#else //QT_NO_QWS_TRANSFORMED + printf("QT buildt with QT_NO_QWS_TRANSFORMED. No screen driver returned\n"); +#endif //QT_NO_QWS_TRANSFORMED + return 0; +} + +Q_EXPORT_STATIC_PLUGIN(GfxTransformedDriver) +Q_EXPORT_PLUGIN2(qgfxtransformed, GfxTransformedDriver) + +QT_END_NAMESPACE +#endif //QT_NO_LIBRARY diff --git a/src/plugins/gfxdrivers/transformed/transformed.pro b/src/plugins/gfxdrivers/transformed/transformed.pro new file mode 100644 index 0000000000..173f7e99d4 --- /dev/null +++ b/src/plugins/gfxdrivers/transformed/transformed.pro @@ -0,0 +1,13 @@ +TARGET = qgfxtransformed +include(../../qpluginbase.pri) + +DEFINES += QT_QWS_TRANSFORMED + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +HEADERS = $$QT_SOURCE_TREE/src/gui/embedded/qscreentransformed_qws.h +SOURCES = main.cpp \ + $$QT_SOURCE_TREE/src/gui/embedded/qscreentransformed_qws.cpp + +target.path=$$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target diff --git a/src/plugins/gfxdrivers/vnc/main.cpp b/src/plugins/gfxdrivers/vnc/main.cpp new file mode 100644 index 0000000000..65c6cf25c6 --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/main.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 +#include +#include + +#ifndef QT_NO_LIBRARY +QT_BEGIN_NAMESPACE + +class GfxVncDriver : public QScreenDriverPlugin +{ +public: + GfxVncDriver(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +GfxVncDriver::GfxVncDriver() +: QScreenDriverPlugin() +{ +} + +QStringList GfxVncDriver::keys() const +{ + QStringList list; + list << "VNC"; + return list; +} + +QScreen* GfxVncDriver::create(const QString& driver, int displayId) +{ +#ifndef QT_NO_QWS_VNC + if (driver.toLower() == "vnc") + return new QVNCScreen(displayId); +#else //QT_NO_QWS_VNC + printf("QT buildt with QT_NO_QWS_VNC. No screen driver returned\n"); +#endif //QT_NO_QWS_VNC + return 0; +} + +Q_EXPORT_STATIC_PLUGIN(GfxVncDriver) +Q_EXPORT_PLUGIN2(qgfxvnc, GfxVncDriver) + +QT_END_NAMESPACE + +#endif //QT_NO_LIBRARY diff --git a/src/plugins/gfxdrivers/vnc/qscreenvnc_p.h b/src/plugins/gfxdrivers/vnc/qscreenvnc_p.h new file mode 100644 index 0000000000..9a02072815 --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/qscreenvnc_p.h @@ -0,0 +1,524 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QSCREENVNC_P_H +#define QSCREENVNC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include "qscreenvnc_qws.h" + +#ifndef QT_NO_QWS_VNC + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QVNCServer; + +#ifndef QT_NO_QWS_CURSOR +class QVNCCursor : public QProxyScreenCursor +{ +public: + QVNCCursor(QVNCScreen *s); + ~QVNCCursor(); + + void hide(); + void show(); + void set(const QImage &image, int hotx, int hoty); + void move(int x, int y); + +private: + void setDirty(const QRect &r) const; + QVNCScreen *screen; +}; + +class QVNCClientCursor : public QProxyScreenCursor +{ +public: + QVNCClientCursor(QVNCServer *s); + ~QVNCClientCursor(); + + void set(const QImage &image, int hotx, int hoty); + void write() const; + +private: + QVNCServer *server; +}; +#endif // QT_NO_QWS_CURSOR + +#define MAP_TILE_SIZE 16 +#define MAP_WIDTH 1280 / MAP_TILE_SIZE +#define MAP_HEIGHT 1024 / MAP_TILE_SIZE + +class QVNCDirtyMap +{ +public: + QVNCDirtyMap(QScreen *screen); + virtual ~QVNCDirtyMap(); + + void reset(); + bool dirty(int x, int y) const; + virtual void setDirty(int x, int y, bool force = false) = 0; + void setClean(int x, int y); + + int bytesPerPixel; + + int numDirty; + int mapWidth; + int mapHeight; + +protected: + uchar *map; + QScreen *screen; + uchar *buffer; + int bufferWidth; + int bufferHeight; + int bufferStride; + int numTiles; +}; + +template +class QVNCDirtyMapOptimized : public QVNCDirtyMap +{ +public: + QVNCDirtyMapOptimized(QScreen *screen) : QVNCDirtyMap(screen) {} + ~QVNCDirtyMapOptimized() {} + + void setDirty(int x, int y, bool force = false); +}; + +class QRfbRect +{ +public: + QRfbRect() {} + QRfbRect(quint16 _x, quint16 _y, quint16 _w, quint16 _h) { + x = _x; y = _y; w = _w; h = _h; + } + + void read(QTcpSocket *s); + void write(QTcpSocket *s) const; + + quint16 x; + quint16 y; + quint16 w; + quint16 h; +}; + +class QRfbPixelFormat +{ +public: + static int size() { return 16; } + + void read(QTcpSocket *s); + void write(QTcpSocket *s); + + int bitsPerPixel; + int depth; + bool bigEndian; + bool trueColor; + int redBits; + int greenBits; + int blueBits; + int redShift; + int greenShift; + int blueShift; +}; + +class QRfbServerInit +{ +public: + QRfbServerInit() { name = 0; } + ~QRfbServerInit() { delete[] name; } + + int size() const { return QRfbPixelFormat::size() + 8 + strlen(name); } + void setName(const char *n); + + void read(QTcpSocket *s); + void write(QTcpSocket *s); + + quint16 width; + quint16 height; + QRfbPixelFormat format; + char *name; +}; + +class QRfbSetEncodings +{ +public: + bool read(QTcpSocket *s); + + quint16 count; +}; + +class QRfbFrameBufferUpdateRequest +{ +public: + bool read(QTcpSocket *s); + + char incremental; + QRfbRect rect; +}; + +class QRfbKeyEvent +{ +public: + bool read(QTcpSocket *s); + + char down; + int keycode; + int unicode; +}; + +class QRfbPointerEvent +{ +public: + bool read(QTcpSocket *s); + + uint buttons; + quint16 x; + quint16 y; +}; + +class QRfbClientCutText +{ +public: + bool read(QTcpSocket *s); + + quint32 length; +}; + +class QVNCScreenPrivate : public QObject +{ +public: + QVNCScreenPrivate(QVNCScreen *parent); + ~QVNCScreenPrivate(); + + void setDirty(const QRect &rect, bool force = false); + void configure(); + + qreal dpiX; + qreal dpiY; + bool doOnScreenSurface; + QVNCDirtyMap *dirty; + int refreshRate; + QVNCServer *vncServer; + +#if !defined(QT_NO_QWS_MULTIPROCESS) && !defined(QT_NO_SHAREDMEMORY) + QSharedMemory shm; +#endif + + QVNCScreen *q_ptr; + + bool noDisablePainting; +}; + +class QRfbEncoder +{ +public: + QRfbEncoder(QVNCServer *s) : server(s) {} + virtual ~QRfbEncoder() {} + + virtual void write() = 0; + +protected: + QVNCServer *server; +}; + +class QRfbRawEncoder : public QRfbEncoder +{ +public: + QRfbRawEncoder(QVNCServer *s) : QRfbEncoder(s) {} + + void write(); + +private: + QByteArray buffer; +}; + +template class QRfbHextileEncoder; + +template +class QRfbSingleColorHextile +{ +public: + QRfbSingleColorHextile(QRfbHextileEncoder *e) : encoder(e) {} + bool read(const uchar *data, int width, int height, int stride); + void write(QTcpSocket *socket) const; + +private: + QRfbHextileEncoder *encoder; +}; + +template +class QRfbDualColorHextile +{ +public: + QRfbDualColorHextile(QRfbHextileEncoder *e) : encoder(e) {} + bool read(const uchar *data, int width, int height, int stride); + void write(QTcpSocket *socket) const; + +private: + struct Rect { + quint8 xy; + quint8 wh; + } Q_PACKED rects[8 * 16]; + + quint8 numRects; + QRfbHextileEncoder *encoder; + +private: + inline int lastx() const { return rectx(numRects); } + inline int lasty() const { return recty(numRects); } + inline int rectx(int r) const { return rects[r].xy >> 4; } + inline int recty(int r) const { return rects[r].xy & 0x0f; } + inline int width(int r) const { return (rects[r].wh >> 4) + 1; } + inline int height(int r) const { return (rects[r].wh & 0x0f) + 1; } + + inline void setX(int r, int x) { + rects[r].xy = (x << 4) | (rects[r].xy & 0x0f); + } + inline void setY(int r, int y) { + rects[r].xy = (rects[r].xy & 0xf0) | y; + } + inline void setWidth(int r, int width) { + rects[r].wh = ((width - 1) << 4) | (rects[r].wh & 0x0f); + } + inline void setHeight(int r, int height) { + rects[r].wh = (rects[r].wh & 0xf0) | (height - 1); + } + + inline void setWidth(int width) { setWidth(numRects, width); } + inline void setHeight(int height) { setHeight(numRects, height); } + inline void setX(int x) { setX(numRects, x); } + inline void setY(int y) { setY(numRects, y); } + void next(); +}; + +template +class QRfbMultiColorHextile +{ +public: + QRfbMultiColorHextile(QRfbHextileEncoder *e) : encoder(e) {} + bool read(const uchar *data, int width, int height, int stride); + void write(QTcpSocket *socket) const; + +private: + inline quint8* rect(int r) { + return rects.data() + r * (bpp + 2); + } + inline const quint8* rect(int r) const { + return rects.constData() + r * (bpp + 2); + } + inline void setX(int r, int x) { + quint8 *ptr = rect(r) + bpp; + *ptr = (x << 4) | (*ptr & 0x0f); + } + inline void setY(int r, int y) { + quint8 *ptr = rect(r) + bpp; + *ptr = (*ptr & 0xf0) | y; + } + void setColor(SRC color); + inline int rectx(int r) const { + const quint8 *ptr = rect(r) + bpp; + return *ptr >> 4; + } + inline int recty(int r) const { + const quint8 *ptr = rect(r) + bpp; + return *ptr & 0x0f; + } + inline void setWidth(int r, int width) { + quint8 *ptr = rect(r) + bpp + 1; + *ptr = ((width - 1) << 4) | (*ptr & 0x0f); + } + inline void setHeight(int r, int height) { + quint8 *ptr = rect(r) + bpp + 1; + *ptr = (*ptr & 0xf0) | (height - 1); + } + + bool beginRect(); + void endRect(); + + static const int maxRectsSize = 16 * 16; + QVarLengthArray rects; + + quint8 bpp; + quint8 numRects; + QRfbHextileEncoder *encoder; +}; + +template +class QRfbHextileEncoder : public QRfbEncoder +{ +public: + QRfbHextileEncoder(QVNCServer *s); + void write(); + +private: + enum SubEncoding { + Raw = 1, + BackgroundSpecified = 2, + ForegroundSpecified = 4, + AnySubrects = 8, + SubrectsColoured = 16 + }; + + QByteArray buffer; + QRfbSingleColorHextile singleColorHextile; + QRfbDualColorHextile dualColorHextile; + QRfbMultiColorHextile multiColorHextile; + + SRC bg; + SRC fg; + bool newBg; + bool newFg; + + friend class QRfbSingleColorHextile; + friend class QRfbDualColorHextile; + friend class QRfbMultiColorHextile; +}; + +class QVNCServer : public QObject +{ + Q_OBJECT +public: + QVNCServer(QVNCScreen *screen); + QVNCServer(QVNCScreen *screen, int id); + ~QVNCServer(); + + void setDirty(); + void setDirtyCursor() { dirtyCursor = true; setDirty(); } + inline bool isConnected() const { return state == Connected; } + inline void setRefreshRate(int rate) { refreshRate = rate; } + + enum ClientMsg { SetPixelFormat = 0, + FixColourMapEntries = 1, + SetEncodings = 2, + FramebufferUpdateRequest = 3, + KeyEvent = 4, + PointerEvent = 5, + ClientCutText = 6 }; + + enum ServerMsg { FramebufferUpdate = 0, + SetColourMapEntries = 1 }; + + void convertPixels(char *dst, const char *src, int count) const; + + inline int clientBytesPerPixel() const { + return pixelFormat.bitsPerPixel / 8; + } + + inline QVNCScreen* screen() const { return qvnc_screen; } + inline QVNCDirtyMap* dirtyMap() const { return qvnc_screen->d_ptr->dirty; } + inline QTcpSocket* clientSocket() const { return client; } + QImage screenImage() const; + inline bool doPixelConversion() const { return needConversion; } +#ifndef QT_NO_QWS_CURSOR + inline bool hasClientCursor() const { return qvnc_cursor != 0; } +#endif + +private: + void setPixelFormat(); + void setEncodings(); + void frameBufferUpdateRequest(); + void pointerEvent(); + void keyEvent(); + void clientCutText(); + bool pixelConversionNeeded() const; + +private slots: + void newConnection(); + void readClient(); + void checkUpdate(); + void discardClient(); + +private: + void init(uint port); + enum ClientState { Unconnected, Protocol, Init, Connected }; + QTimer *timer; + QTcpServer *serverSocket; + QTcpSocket *client; + ClientState state; + quint8 msgType; + bool handleMsg; + QRfbPixelFormat pixelFormat; + Qt::KeyboardModifiers keymod; + int encodingsPending; + int cutTextPending; + uint supportCopyRect : 1; + uint supportRRE : 1; + uint supportCoRRE : 1; + uint supportHextile : 1; + uint supportZRLE : 1; + uint supportCursor : 1; + uint supportDesktopSize : 1; + bool wantUpdate; + bool sameEndian; + bool needConversion; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + bool swapBytes; +#endif + bool dirtyCursor; + int refreshRate; + QVNCScreen *qvnc_screen; +#ifndef QT_NO_QWS_CURSOR + QVNCClientCursor *qvnc_cursor; +#endif + + QRfbEncoder *encoder; +}; + + +QT_END_NAMESPACE +#endif // QT_NO_QWS_VNC +#endif // QSCREENVNC_P_H diff --git a/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.cpp b/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.cpp new file mode 100644 index 0000000000..63e06659aa --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.cpp @@ -0,0 +1,2338 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 "qscreenvnc_qws.h" + +#ifndef QT_NO_QWS_VNC + +#include "qscreenvnc_p.h" +#include "qwindowsystem_qws.h" +#include "qwsdisplay_qws.h" +#include "qscreendriverfactory_qws.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +//#define QT_QWS_VNC_DEBUG + +extern QString qws_qtePipeFilename(); + +#ifndef QT_NO_QWS_CURSOR + +QVNCCursor::QVNCCursor(QVNCScreen *s) + : screen(s) +{ + if (qt_screencursor) + setScreenCursor(qt_screencursor); + else + hwaccel = true; +} + +QVNCCursor::~QVNCCursor() +{ + if (screenCursor()) + qt_screencursor = screenCursor(); +} + +void QVNCCursor::setDirty(const QRect &r) const +{ + screen->d_ptr->setDirty(r, true); +} + +void QVNCCursor::hide() +{ + QProxyScreenCursor::hide(); + if (enable) + setDirty(boundingRect()); +} + +void QVNCCursor::show() +{ + QProxyScreenCursor::show(); + if (enable) + setDirty(boundingRect()); +} + +void QVNCCursor::set(const QImage &image, int hotx, int hoty) +{ + QRegion dirty = boundingRect(); + QProxyScreenCursor::set(image, hotx, hoty); + dirty |= boundingRect(); + if (enable && hwaccel && !screen->d_ptr->vncServer->hasClientCursor()) { + const QVector rects = dirty.rects(); + for (int i = 0; i < rects.size(); ++i) + setDirty(rects.at(i)); + } +} + +void QVNCCursor::move(int x, int y) +{ + if (enable && hwaccel && !screen->d_ptr->vncServer->hasClientCursor()) { + QRegion dirty = boundingRect(); + QProxyScreenCursor::move(x, y); + dirty |= boundingRect(); + if (enable) { + const QVector rects = dirty.rects(); + for (int i = 0; i < rects.size(); ++i) + setDirty(rects.at(i)); + } + } else { + QProxyScreenCursor::move(x, y); + } +} + +QVNCClientCursor::QVNCClientCursor(QVNCServer *s) + : server(s) +{ + setScreenCursor(qt_screencursor); + Q_ASSERT(hwaccel); + qt_screencursor = this; // hw: XXX + + set(image(), hotspot.x(), hotspot.y()); +} + +QVNCClientCursor::~QVNCClientCursor() +{ + qt_screencursor = screenCursor(); +} + +void QVNCClientCursor::set(const QImage &image, int hotx, int hoty) +{ + QScreenCursor::set(image, hotx, hoty); + server->setDirtyCursor(); +} + +void QVNCClientCursor::write() const +{ + QTcpSocket *socket = server->clientSocket(); + + // FramebufferUpdate header + { + const quint16 tmp[6] = { htons(0), + htons(1), + htons(hotspot.x()), htons(hotspot.y()), + htons(cursor.width()), + htons(cursor.height()) }; + socket->write((char*)tmp, sizeof(tmp)); + + const quint32 encoding = htonl(-239); + socket->write((char*)(&encoding), sizeof(encoding)); + } + + if (cursor.isNull()) + return; + + // write pixels + Q_ASSERT(cursor.hasAlphaChannel()); + const QImage img = cursor.convertToFormat(server->screen()->pixelFormat()); + const int n = server->clientBytesPerPixel() * img.width(); + char *buffer = new char[n]; + for (int i = 0; i < img.height(); ++i) { + server->convertPixels(buffer, (const char*)img.scanLine(i), img.width()); + socket->write(buffer, n); + } + delete[] buffer; + + // write mask + const QImage bitmap = cursor.createAlphaMask().convertToFormat(QImage::Format_Mono); + Q_ASSERT(bitmap.depth() == 1); + Q_ASSERT(bitmap.size() == img.size()); + const int width = (bitmap.width() + 7) / 8; + for (int i = 0; i < bitmap.height(); ++i) + socket->write((const char*)bitmap.scanLine(i), width); +} + +#endif // QT_NO_QWS_CURSOR + +QVNCScreenPrivate::QVNCScreenPrivate(QVNCScreen *parent) + : dpiX(72), dpiY(72), doOnScreenSurface(false), refreshRate(25), + vncServer(0), q_ptr(parent), noDisablePainting(false) +{ +#ifdef QT_BUILD_INTERNAL + noDisablePainting = (qgetenv("QT_VNC_NO_DISABLEPAINTING").toInt() > 0); +#endif +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addObject(this); +#endif +} + +QVNCScreenPrivate::~QVNCScreenPrivate() +{ +#if defined(QT_NO_QWS_MULTIPROCESS) || defined(QT_NO_SHAREDMEMORY) + if (q_ptr->screen()) + return; + + delete[] q_ptr->data; + q_ptr->data = 0; +#else + shm.detach(); +#endif +} + +void QVNCScreenPrivate::configure() +{ + if (q_ptr->screen()) + return; + + q_ptr->lstep = q_ptr->dw * ((q_ptr->d + 7) / 8); + q_ptr->size = q_ptr->h * q_ptr->lstep; + q_ptr->mapsize = q_ptr->size; + q_ptr->physWidth = qRound(q_ptr->dw * qreal(25.4) / dpiX); + q_ptr->physHeight = qRound(q_ptr->dh * qreal(25.4) / dpiY); + + switch (q_ptr->d) { + case 1: + q_ptr->setPixelFormat(QImage::Format_Mono); //### LSB??? + break; + case 8: + q_ptr->setPixelFormat(QImage::Format_Indexed8); + break; + case 12: + q_ptr->setPixelFormat(QImage::Format_RGB444); + break; + case 15: + q_ptr->setPixelFormat(QImage::Format_RGB555); + break; + case 16: + q_ptr->setPixelFormat(QImage::Format_RGB16); + break; + case 18: + q_ptr->setPixelFormat(QImage::Format_RGB666); + break; + case 24: + q_ptr->setPixelFormat(QImage::Format_RGB888); + break; + case 32: + q_ptr->setPixelFormat(QImage::Format_ARGB32_Premultiplied); + break; + } + +#if !defined(QT_NO_QWS_MULTIPROCESS) && !defined(QT_NO_SHAREDMEMORY) + if (q_ptr->size != shm.size()) { + shm.detach(); + const QString key = qws_qtePipeFilename() + + QString().sprintf("_vnc_%d_%d", + q_ptr->displayId, q_ptr->size); + shm.setKey(key); + if (QApplication::type() == QApplication::GuiServer) { + if (!shm.create(q_ptr->size)) { + qWarning() << "QVNCScreen could not create shared memory:" + << shm.errorString(); + if (!shm.attach()) { + qWarning() << "QVNCScreen could not attach to shared memory:" + << shm.errorString(); + } + } + } else if (!shm.attach()) { + qWarning() << "QVNCScreen could not attach to shared memory:" + << shm.errorString(); + } + q_ptr->data = reinterpret_cast(shm.data()); + } +#else + if (q_ptr->data) + delete[] q_ptr->data; + q_ptr->data = new uchar[q_ptr->size]; +#endif +} + +//=========================================================================== + +static const struct { + int keysym; + int keycode; +} keyMap[] = { + { 0xff08, Qt::Key_Backspace }, + { 0xff09, Qt::Key_Tab }, + { 0xff0d, Qt::Key_Return }, + { 0xff1b, Qt::Key_Escape }, + { 0xff63, Qt::Key_Insert }, + { 0xffff, Qt::Key_Delete }, + { 0xff50, Qt::Key_Home }, + { 0xff57, Qt::Key_End }, + { 0xff55, Qt::Key_PageUp }, + { 0xff56, Qt::Key_PageDown }, + { 0xff51, Qt::Key_Left }, + { 0xff52, Qt::Key_Up }, + { 0xff53, Qt::Key_Right }, + { 0xff54, Qt::Key_Down }, + { 0xffbe, Qt::Key_F1 }, + { 0xffbf, Qt::Key_F2 }, + { 0xffc0, Qt::Key_F3 }, + { 0xffc1, Qt::Key_F4 }, + { 0xffc2, Qt::Key_F5 }, + { 0xffc3, Qt::Key_F6 }, + { 0xffc4, Qt::Key_F7 }, + { 0xffc5, Qt::Key_F8 }, + { 0xffc6, Qt::Key_F9 }, + { 0xffc7, Qt::Key_F10 }, + { 0xffc8, Qt::Key_F11 }, + { 0xffc9, Qt::Key_F12 }, + { 0xffe1, Qt::Key_Shift }, + { 0xffe2, Qt::Key_Shift }, + { 0xffe3, Qt::Key_Control }, + { 0xffe4, Qt::Key_Control }, + { 0xffe7, Qt::Key_Meta }, + { 0xffe8, Qt::Key_Meta }, + { 0xffe9, Qt::Key_Alt }, + { 0xffea, Qt::Key_Alt }, + + { 0xffb0, Qt::Key_0 }, + { 0xffb1, Qt::Key_1 }, + { 0xffb2, Qt::Key_2 }, + { 0xffb3, Qt::Key_3 }, + { 0xffb4, Qt::Key_4 }, + { 0xffb5, Qt::Key_5 }, + { 0xffb6, Qt::Key_6 }, + { 0xffb7, Qt::Key_7 }, + { 0xffb8, Qt::Key_8 }, + { 0xffb9, Qt::Key_9 }, + + { 0xff8d, Qt::Key_Return }, + { 0xffaa, Qt::Key_Asterisk }, + { 0xffab, Qt::Key_Plus }, + { 0xffad, Qt::Key_Minus }, + { 0xffae, Qt::Key_Period }, + { 0xffaf, Qt::Key_Slash }, + + { 0xff95, Qt::Key_Home }, + { 0xff96, Qt::Key_Left }, + { 0xff97, Qt::Key_Up }, + { 0xff98, Qt::Key_Right }, + { 0xff99, Qt::Key_Down }, + { 0xff9a, Qt::Key_PageUp }, + { 0xff9b, Qt::Key_PageDown }, + { 0xff9c, Qt::Key_End }, + { 0xff9e, Qt::Key_Insert }, + { 0xff9f, Qt::Key_Delete }, + + { 0, 0 } +}; + +void QRfbRect::read(QTcpSocket *s) +{ + quint16 buf[4]; + s->read((char*)buf, 8); + x = ntohs(buf[0]); + y = ntohs(buf[1]); + w = ntohs(buf[2]); + h = ntohs(buf[3]); +} + +void QRfbRect::write(QTcpSocket *s) const +{ + quint16 buf[4]; + buf[0] = htons(x); + buf[1] = htons(y); + buf[2] = htons(w); + buf[3] = htons(h); + s->write((char*)buf, 8); +} + +void QRfbPixelFormat::read(QTcpSocket *s) +{ + char buf[16]; + s->read(buf, 16); + bitsPerPixel = buf[0]; + depth = buf[1]; + bigEndian = buf[2]; + trueColor = buf[3]; + + quint16 a = ntohs(*(quint16 *)(buf + 4)); + redBits = 0; + while (a) { a >>= 1; redBits++; } + + a = ntohs(*(quint16 *)(buf + 6)); + greenBits = 0; + while (a) { a >>= 1; greenBits++; } + + a = ntohs(*(quint16 *)(buf + 8)); + blueBits = 0; + while (a) { a >>= 1; blueBits++; } + + redShift = buf[10]; + greenShift = buf[11]; + blueShift = buf[12]; +} + +void QRfbPixelFormat::write(QTcpSocket *s) +{ + char buf[16]; + buf[0] = bitsPerPixel; + buf[1] = depth; + buf[2] = bigEndian; + buf[3] = trueColor; + + quint16 a = 0; + for (int i = 0; i < redBits; i++) a = (a << 1) | 1; + *(quint16 *)(buf + 4) = htons(a); + + a = 0; + for (int i = 0; i < greenBits; i++) a = (a << 1) | 1; + *(quint16 *)(buf + 6) = htons(a); + + a = 0; + for (int i = 0; i < blueBits; i++) a = (a << 1) | 1; + *(quint16 *)(buf + 8) = htons(a); + + buf[10] = redShift; + buf[11] = greenShift; + buf[12] = blueShift; + s->write(buf, 16); +} + + +void QRfbServerInit::setName(const char *n) +{ + delete[] name; + name = new char [strlen(n) + 1]; + strcpy(name, n); +} + +void QRfbServerInit::read(QTcpSocket *s) +{ + s->read((char *)&width, 2); + width = ntohs(width); + s->read((char *)&height, 2); + height = ntohs(height); + format.read(s); + + quint32 len; + s->read((char *)&len, 4); + len = ntohl(len); + + name = new char [len + 1]; + s->read(name, len); + name[len] = '\0'; +} + +void QRfbServerInit::write(QTcpSocket *s) +{ + quint16 t = htons(width); + s->write((char *)&t, 2); + t = htons(height); + s->write((char *)&t, 2); + format.write(s); + quint32 len = strlen(name); + len = htonl(len); + s->write((char *)&len, 4); + s->write(name, strlen(name)); +} + +bool QRfbSetEncodings::read(QTcpSocket *s) +{ + if (s->bytesAvailable() < 3) + return false; + + char tmp; + s->read(&tmp, 1); // padding + s->read((char *)&count, 2); + count = ntohs(count); + + return true; +} + +bool QRfbFrameBufferUpdateRequest::read(QTcpSocket *s) +{ + if (s->bytesAvailable() < 9) + return false; + + s->read(&incremental, 1); + rect.read(s); + + return true; +} + +bool QRfbKeyEvent::read(QTcpSocket *s) +{ + if (s->bytesAvailable() < 7) + return false; + + s->read(&down, 1); + quint16 tmp; + s->read((char *)&tmp, 2); // padding + + quint32 key; + s->read((char *)&key, 4); + key = ntohl(key); + + unicode = 0; + keycode = 0; + int i = 0; + while (keyMap[i].keysym && !keycode) { + if (keyMap[i].keysym == (int)key) + keycode = keyMap[i].keycode; + i++; + } + + if (keycode >= ' ' && keycode <= '~') + unicode = keycode; + + if (!keycode) { + if (key <= 0xff) { + unicode = key; + if (key >= 'a' && key <= 'z') + keycode = Qt::Key_A + key - 'a'; + else if (key >= ' ' && key <= '~') + keycode = Qt::Key_Space + key - ' '; + } + } + + return true; +} + +bool QRfbPointerEvent::read(QTcpSocket *s) +{ + if (s->bytesAvailable() < 5) + return false; + + char buttonMask; + s->read(&buttonMask, 1); + buttons = 0; + if (buttonMask & 1) + buttons |= Qt::LeftButton; + if (buttonMask & 2) + buttons |= Qt::MidButton; + if (buttonMask & 4) + buttons |= Qt::RightButton; + + quint16 tmp; + s->read((char *)&tmp, 2); + x = ntohs(tmp); + s->read((char *)&tmp, 2); + y = ntohs(tmp); + + return true; +} + +bool QRfbClientCutText::read(QTcpSocket *s) +{ + if (s->bytesAvailable() < 7) + return false; + + char tmp[3]; + s->read(tmp, 3); // padding + s->read((char *)&length, 4); + length = ntohl(length); + + return true; +} + +//=========================================================================== + +QVNCServer::QVNCServer(QVNCScreen *screen) + : qvnc_screen(screen) +{ + init(5900); +} + +QVNCServer::QVNCServer(QVNCScreen *screen, int id) + : qvnc_screen(screen) +{ + init(5900 + id); +} + +void QVNCServer::init(uint port) +{ + handleMsg = false; + client = 0; + encodingsPending = 0; + cutTextPending = 0; + keymod = 0; + state = Unconnected; + dirtyCursor = false; + + refreshRate = 25; + timer = new QTimer(this); + timer->setSingleShot(true); + connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdate())); + + serverSocket = new QTcpServer(this); + if (!serverSocket->listen(QHostAddress::Any, port)) + qDebug() << "QVNCServer could not connect:" << serverSocket->errorString(); + else + qDebug("QVNCServer created on port %d", port); + + connect(serverSocket, SIGNAL(newConnection()), this, SLOT(newConnection())); + +#ifndef QT_NO_QWS_CURSOR + qvnc_cursor = 0; +#endif + encoder = 0; +} + +QVNCServer::~QVNCServer() +{ + delete encoder; + encoder = 0; + delete client; + client = 0; +#ifndef QT_NO_QWS_CURSOR + delete qvnc_cursor; + qvnc_cursor = 0; +#endif +} + +void QVNCServer::setDirty() +{ + if (state == Connected && !timer->isActive() && + ((dirtyMap()->numDirty > 0) || dirtyCursor)) { + timer->start(); + } +} + +void QVNCServer::newConnection() +{ + if (client) + delete client; + + client = serverSocket->nextPendingConnection(); + connect(client,SIGNAL(readyRead()),this,SLOT(readClient())); + connect(client,SIGNAL(disconnected()),this,SLOT(discardClient())); + handleMsg = false; + encodingsPending = 0; + cutTextPending = 0; + supportHextile = false; + wantUpdate = false; + + timer->start(1000 / refreshRate); + dirtyMap()->reset(); + + // send protocol version + const char *proto = "RFB 003.003\n"; + client->write(proto, 12); + state = Protocol; + + if (!qvnc_screen->screen() && !qvnc_screen->d_ptr->noDisablePainting) + QWSServer::instance()->enablePainting(true); +} + +void QVNCServer::readClient() +{ + switch (state) { + case Protocol: + if (client->bytesAvailable() >= 12) { + char proto[13]; + client->read(proto, 12); + proto[12] = '\0'; + qDebug("Client protocol version %s", proto); + // No authentication + quint32 auth = htonl(1); + client->write((char *) &auth, sizeof(auth)); + state = Init; + } + break; + + case Init: + if (client->bytesAvailable() >= 1) { + quint8 shared; + client->read((char *) &shared, 1); + + // Server Init msg + QRfbServerInit sim; + QRfbPixelFormat &format = sim.format; + switch (qvnc_screen->depth()) { + case 32: + format.bitsPerPixel = 32; + format.depth = 32; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 8; + format.greenBits = 8; + format.blueBits = 8; + format.redShift = 16; + format.greenShift = 8; + format.blueShift = 0; + break; + + case 24: + format.bitsPerPixel = 24; + format.depth = 24; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 8; + format.greenBits = 8; + format.blueBits = 8; + format.redShift = 16; + format.greenShift = 8; + format.blueShift = 0; + break; + + case 18: + format.bitsPerPixel = 24; + format.depth = 18; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 6; + format.greenBits = 6; + format.blueBits = 6; + format.redShift = 12; + format.greenShift = 6; + format.blueShift = 0; + break; + + case 16: + format.bitsPerPixel = 16; + format.depth = 16; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 5; + format.greenBits = 6; + format.blueBits = 5; + format.redShift = 11; + format.greenShift = 5; + format.blueShift = 0; + break; + + case 15: + format.bitsPerPixel = 16; + format.depth = 15; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 5; + format.greenBits = 5; + format.blueBits = 5; + format.redShift = 10; + format.greenShift = 5; + format.blueShift = 0; + break; + + case 12: + format.bitsPerPixel = 16; + format.depth = 12; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 4; + format.greenBits = 4; + format.blueBits = 4; + format.redShift = 8; + format.greenShift = 4; + format.blueShift = 0; + break; + + case 8: + case 4: + format.bitsPerPixel = 8; + format.depth = 8; + format.bigEndian = 0; + format.trueColor = false; + format.redBits = 0; + format.greenBits = 0; + format.blueBits = 0; + format.redShift = 0; + format.greenShift = 0; + format.blueShift = 0; + break; + + default: + qDebug("QVNC cannot drive depth %d", qvnc_screen->depth()); + discardClient(); + return; + } + sim.width = qvnc_screen->deviceWidth(); + sim.height = qvnc_screen->deviceHeight(); + sim.setName("Qt for Embedded Linux VNC Server"); + sim.write(client); + state = Connected; + } + break; + + case Connected: + do { + if (!handleMsg) { + client->read((char *)&msgType, 1); + handleMsg = true; + } + if (handleMsg) { + switch (msgType ) { + case SetPixelFormat: + setPixelFormat(); + break; + case FixColourMapEntries: + qDebug("Not supported: FixColourMapEntries"); + handleMsg = false; + break; + case SetEncodings: + setEncodings(); + break; + case FramebufferUpdateRequest: + frameBufferUpdateRequest(); + break; + case KeyEvent: + keyEvent(); + break; + case PointerEvent: + pointerEvent(); + break; + case ClientCutText: + clientCutText(); + break; + default: + qDebug("Unknown message type: %d", (int)msgType); + handleMsg = false; + } + } + } while (!handleMsg && client->bytesAvailable()); + break; + default: + break; + } +} + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +bool QVNCScreen::swapBytes() const +{ + if (depth() != 16) + return false; + + if (screen()) + return screen()->frameBufferLittleEndian(); + return frameBufferLittleEndian(); +} +#endif + +void QVNCServer::setPixelFormat() +{ + if (client->bytesAvailable() >= 19) { + char buf[3]; + client->read(buf, 3); // just padding + pixelFormat.read(client); +#ifdef QT_QWS_VNC_DEBUG + qDebug("Want format: %d %d %d %d %d %d %d %d %d %d", + int(pixelFormat.bitsPerPixel), + int(pixelFormat.depth), + int(pixelFormat.bigEndian), + int(pixelFormat.trueColor), + int(pixelFormat.redBits), + int(pixelFormat.greenBits), + int(pixelFormat.blueBits), + int(pixelFormat.redShift), + int(pixelFormat.greenShift), + int(pixelFormat.blueShift)); +#endif + if (!pixelFormat.trueColor) { + qDebug("Can only handle true color clients"); + discardClient(); + } + handleMsg = false; + sameEndian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) == !!pixelFormat.bigEndian; + needConversion = pixelConversionNeeded(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + swapBytes = qvnc_screen->swapBytes(); +#endif + } +} + +void QVNCServer::setEncodings() +{ + QRfbSetEncodings enc; + + if (!encodingsPending && enc.read(client)) { + encodingsPending = enc.count; + if (!encodingsPending) + handleMsg = false; + } + + if (encoder) { + delete encoder; + encoder = 0; + } + + enum Encodings { + Raw = 0, + CopyRect = 1, + RRE = 2, + CoRRE = 4, + Hextile = 5, + ZRLE = 16, + Cursor = -239, + DesktopSize = -223 + }; + + if (encodingsPending && (unsigned)client->bytesAvailable() >= + encodingsPending * sizeof(quint32)) { + for (int i = 0; i < encodingsPending; ++i) { + qint32 enc; + client->read((char *)&enc, sizeof(qint32)); + enc = ntohl(enc); +#ifdef QT_QWS_VNC_DEBUG + qDebug("QVNCServer::setEncodings: %d", enc); +#endif + switch (enc) { + case Raw: + if (!encoder) { + encoder = new QRfbRawEncoder(this); +#ifdef QT_QWS_VNC_DEBUG + qDebug("QVNCServer::setEncodings: using raw"); +#endif + } + break; + case CopyRect: + supportCopyRect = true; + break; + case RRE: + supportRRE = true; + break; + case CoRRE: + supportCoRRE = true; + break; + case Hextile: + supportHextile = true; + if (encoder) + break; + switch (qvnc_screen->depth()) { +#ifdef QT_QWS_DEPTH_8 + case 8: + encoder = new QRfbHextileEncoder(this); + break; +#endif +#ifdef QT_QWS_DEPTH_12 + case 12: + encoder = new QRfbHextileEncoder(this); + break; +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: + encoder = new QRfbHextileEncoder(this); + break; +#endif +#ifdef QT_QWS_DEPTH_16 + case 16: + encoder = new QRfbHextileEncoder(this); + break; +#endif +#ifdef QT_QWS_DEPTH_18 + case 18: + encoder = new QRfbHextileEncoder(this); + break; +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: + encoder = new QRfbHextileEncoder(this); + break; +#endif +#ifdef QT_QWS_DEPTH_32 + case 32: + encoder = new QRfbHextileEncoder(this); + break; +#endif + default: + break; + } +#ifdef QT_QWS_VNC_DEBUG + qDebug("QVNCServer::setEncodings: using hextile"); +#endif + break; + case ZRLE: + supportZRLE = true; + break; + case Cursor: + supportCursor = true; +#ifndef QT_NO_QWS_CURSOR + if (!qvnc_screen->screen() || qt_screencursor->isAccelerated()) { + delete qvnc_cursor; + qvnc_cursor = new QVNCClientCursor(this); + } +#endif + break; + case DesktopSize: + supportDesktopSize = true; + break; + default: + break; + } + } + handleMsg = false; + encodingsPending = 0; + } + + if (!encoder) { + encoder = new QRfbRawEncoder(this); +#ifdef QT_QWS_VNC_DEBUG + qDebug("QVNCServer::setEncodings: fallback using raw"); +#endif + } +} + +void QVNCServer::frameBufferUpdateRequest() +{ + QRfbFrameBufferUpdateRequest ev; + + if (ev.read(client)) { + if (!ev.incremental) { + QRect r(ev.rect.x, ev.rect.y, ev.rect.w, ev.rect.h); + r.translate(qvnc_screen->offset()); + qvnc_screen->d_ptr->setDirty(r, true); + } + wantUpdate = true; + checkUpdate(); + handleMsg = false; + } +} + +void QVNCServer::pointerEvent() +{ + QRfbPointerEvent ev; + if (ev.read(client)) { + const QPoint offset = qvnc_screen->offset(); + QWSServer::sendMouseEvent(offset + QPoint(ev.x, ev.y), ev.buttons); + handleMsg = false; + } +} + +void QVNCServer::keyEvent() +{ + QRfbKeyEvent ev; + + if (ev.read(client)) { + if (ev.keycode == Qt::Key_Shift) + keymod = ev.down ? keymod | Qt::ShiftModifier : + keymod & ~Qt::ShiftModifier; + else if (ev.keycode == Qt::Key_Control) + keymod = ev.down ? keymod | Qt::ControlModifier : + keymod & ~Qt::ControlModifier; + else if (ev.keycode == Qt::Key_Alt) + keymod = ev.down ? keymod | Qt::AltModifier : + keymod & ~Qt::AltModifier; + if (ev.unicode || ev.keycode) + QWSServer::sendKeyEvent(ev.unicode, ev.keycode, keymod, ev.down, false); + handleMsg = false; + } +} + +void QVNCServer::clientCutText() +{ + QRfbClientCutText ev; + + if (cutTextPending == 0 && ev.read(client)) { + cutTextPending = ev.length; + if (!cutTextPending) + handleMsg = false; + } + + if (cutTextPending && client->bytesAvailable() >= cutTextPending) { + char *text = new char [cutTextPending+1]; + client->read(text, cutTextPending); + delete [] text; + cutTextPending = 0; + handleMsg = false; + } +} + +// stride in bytes +template +bool QRfbSingleColorHextile::read(const uchar *data, + int width, int height, int stride) +{ + const int depth = encoder->server->screen()->depth(); + if (width % (depth / 8)) // hw: should rather fallback to simple loop + return false; + + static int alwaysFalse = qgetenv("QT_VNC_NOCHECKFILL").toInt(); + if (alwaysFalse) + return false; + + switch (depth) { + case 4: { + const quint8 *data8 = reinterpret_cast(data); + if ((data8[0] & 0xf) != (data8[0] >> 4)) + return false; + width /= 2; + } // fallthrough + case 8: { + const quint8 *data8 = reinterpret_cast(data); + if (data8[0] != data8[1]) + return false; + width /= 2; + } // fallthrough + case 12: + case 15: + case 16: { + const quint16 *data16 = reinterpret_cast(data); + if (data16[0] != data16[1]) + return false; + width /= 2; + } // fallthrough + case 18: + case 24: + case 32: { + const quint32 *data32 = reinterpret_cast(data); + const quint32 first = data32[0]; + const int linestep = (stride / sizeof(quint32)) - width; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + if (*(data32++) != first) + return false; + } + data32 += linestep; + } + break; + } + default: + return false; + } + + SRC color = reinterpret_cast(data)[0]; + encoder->newBg |= (color != encoder->bg); + encoder->bg = color; + return true; +} + +template +void QRfbSingleColorHextile::write(QTcpSocket *socket) const +{ + if (true || encoder->newBg) { + const int bpp = encoder->server->clientBytesPerPixel(); + const int padding = 3; + QVarLengthArray buffer(padding + 1 + bpp); + buffer[padding] = 2; // BackgroundSpecified + encoder->server->convertPixels(buffer.data() + padding + 1, + reinterpret_cast(&encoder->bg), + 1); + socket->write(buffer.data() + padding, bpp + 1); +// encoder->newBg = false; + } else { + char subenc = 0; + socket->write(&subenc, 1); + } +} + +template +bool QRfbDualColorHextile::read(const uchar *data, + int width, int height, int stride) +{ + const SRC *ptr = reinterpret_cast(data); + const int linestep = (stride / sizeof(SRC)) - width; + + SRC c1; + SRC c2 = 0; + int n1 = 0; + int n2 = 0; + int x = 0; + int y = 0; + + c1 = *ptr; + + // find second color + while (y < height) { + while (x < width) { + if (*ptr == c1) { + ++n1; + } else { + c2 = *ptr; + goto found_second_color; + } + ++ptr; + ++x; + } + x = 0; + ptr += linestep; + ++y; + } + +found_second_color: + // finish counting + while (y < height) { + while (x < width) { + if (*ptr == c1) { + ++n1; + } else if (*ptr == c2) { + ++n2; + } else { + return false; + } + ++ptr; + ++x; + } + x = 0; + ptr += linestep; + ++y; + } + + if (n2 > n1) { + const quint32 tmpC = c1; + c1 = c2; + c2 = tmpC; + } + + encoder->newBg |= (c1 != encoder->bg); + encoder->newFg |= (c2 != encoder->fg); + + encoder->bg = c1; + encoder->fg = c2; + + // create map + bool inRect = false; + numRects = 0; + ptr = reinterpret_cast(data); + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + if (inRect && *ptr == encoder->bg) { + // rect finished + setWidth(x - lastx()); + next(); + inRect = false; + } else if (!inRect && *ptr == encoder->fg) { + // rect start + setX(x); + setY(y); + setHeight(1); + inRect = true; + } + ++ptr; + } + if (inRect) { + // finish rect + setWidth(width - lastx()); + next(); + inRect = false; + } + ptr += linestep; + } + + return true; +} + +template +void QRfbDualColorHextile::write(QTcpSocket *socket) const +{ + const int bpp = encoder->server->clientBytesPerPixel(); + const int padding = 3; + QVarLengthArray buffer(padding + 2 * bpp + sizeof(char) + sizeof(numRects)); + char &subenc = buffer[padding]; + int n = padding + sizeof(subenc); + + subenc = 0x8; // AnySubrects + + if (encoder->newBg) { + subenc |= 0x2; // Background + encoder->server->convertPixels(buffer.data() + n, (char*)&encoder->bg, 1); + n += bpp; +// encoder->newBg = false; + } + + if (encoder->newFg) { + subenc |= 0x4; // Foreground + encoder->server->convertPixels(buffer.data() + n, (char*)&encoder->fg, 1); + n += bpp; +// encoder->newFg = false; + } + buffer[n] = numRects; + n += sizeof(numRects); + + socket->write(buffer.data() + padding, n - padding); + socket->write((char*)rects, numRects * sizeof(Rect)); +} + +template +void QRfbDualColorHextile::next() +{ + for (int r = numRects - 1; r >= 0; --r) { + if (recty(r) == lasty()) + continue; + if (recty(r) < lasty() - 1) // only search previous scanline + break; + if (rectx(r) == lastx() && width(r) == width(numRects)) { + ++rects[r].wh; + return; + } + } + ++numRects; +} + +template +inline void QRfbMultiColorHextile::setColor(SRC color) +{ + encoder->server->convertPixels(reinterpret_cast(rect(numRects)), + (const char*)&color, 1); +} + +template +inline bool QRfbMultiColorHextile::beginRect() +{ + if ((rects.size() + bpp + 2) > maxRectsSize) + return false; + rects.resize(rects.size() + bpp + 2); + return true; +} + +template +inline void QRfbMultiColorHextile::endRect() +{ + setHeight(numRects, 1); + ++numRects; +} + +template +bool QRfbMultiColorHextile::read(const uchar *data, + int width, int height, int stride) +{ + const SRC *ptr = reinterpret_cast(data); + const int linestep = (stride / sizeof(SRC)) - width; + + bpp = encoder->server->clientBytesPerPixel(); + + if (encoder->newBg) + encoder->bg = ptr[0]; + + const SRC bg = encoder->bg; + SRC color = bg; + bool inRect = false; + + numRects = 0; + rects.clear(); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + if (inRect && *ptr != color) { // end rect + setWidth(numRects, x - rectx(numRects)); + endRect(); + inRect = false; + } + + if (!inRect && *ptr != bg) { // begin rect + if (!beginRect()) + return false; + inRect = true; + color = *ptr; + setColor(color); + setX(numRects, x); + setY(numRects, y); + } + ++ptr; + } + if (inRect) { // end rect + setWidth(numRects, width - rectx(numRects)); + endRect(); + inRect = false; + } + ptr += linestep; + } + + return true; +} + +template +void QRfbMultiColorHextile::write(QTcpSocket *socket) const +{ + const int padding = 3; + QVarLengthArray buffer(bpp + padding + sizeof(quint8) + sizeof(numRects)); + + quint8 &subenc = buffer[padding]; + int n = padding + sizeof(quint8); + + subenc = 8 | 16; // AnySubrects | SubrectsColoured + + if (encoder->newBg) { + subenc |= 0x2; // Background + encoder->server->convertPixels(reinterpret_cast(buffer.data() + n), + reinterpret_cast(&encoder->bg), + 1); + n += bpp; +// encoder->newBg = false; + } + + buffer[n] = numRects; + n += sizeof(numRects); + + socket->write(reinterpret_cast(buffer.data() + padding), + n - padding); + socket->write(reinterpret_cast(rects.constData()), + rects.size()); +} + +bool QVNCServer::pixelConversionNeeded() const +{ + if (!sameEndian) + return true; + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (qvnc_screen->swapBytes()) + return true; +#endif + + const int screendepth = qvnc_screen->depth(); + if (screendepth != pixelFormat.bitsPerPixel) + return true; + + switch (screendepth) { + case 32: + case 24: + return false; + case 18: + return (pixelFormat.redBits == 6 + && pixelFormat.greenBits == 6 + && pixelFormat.blueBits == 6); + case 16: + return (pixelFormat.redBits == 5 + && pixelFormat.greenBits == 6 + && pixelFormat.blueBits == 5); + case 15: + return (pixelFormat.redBits == 5 + && pixelFormat.greenBits == 5 + && pixelFormat.blueBits == 5); + case 12: + return (pixelFormat.redBits == 4 + && pixelFormat.greenBits == 4 + && pixelFormat.blueBits == 4); + } + return true; +} + +// count: number of pixels +void QVNCServer::convertPixels(char *dst, const char *src, int count) const +{ + const int screendepth = qvnc_screen->depth(); + const bool isBgr = qvnc_screen->pixelType() == QScreen::BGRPixel; + + // cutoffs +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (!swapBytes) +#endif + if (sameEndian) { + if (screendepth == pixelFormat.bitsPerPixel) { // memcpy cutoffs + + switch (screendepth) { + case 32: + memcpy(dst, src, count * sizeof(quint32)); + return; + case 16: + if (pixelFormat.redBits == 5 + && pixelFormat.greenBits == 6 + && pixelFormat.blueBits == 5) + { + memcpy(dst, src, count * sizeof(quint16)); + return; + } + } + } else if (screendepth == 16 && pixelFormat.bitsPerPixel == 32) { +#if defined(__i386__) // Currently fails on ARM if dst is not 4 byte aligned + const quint32 *src32 = reinterpret_cast(src); + quint32 *dst32 = reinterpret_cast(dst); + int count32 = count * sizeof(quint16) / sizeof(quint32); + while (count32--) { + const quint32 s = *src32++; + quint32 result1; + quint32 result2; + + // red + result1 = ((s & 0xf8000000) | ((s & 0xe0000000) >> 5)) >> 8; + result2 = ((s & 0x0000f800) | ((s & 0x0000e000) >> 5)) << 8; + + // green + result1 |= ((s & 0x07e00000) | ((s & 0x06000000) >> 6)) >> 11; + result2 |= ((s & 0x000007e0) | ((s & 0x00000600) >> 6)) << 5; + + // blue + result1 |= ((s & 0x001f0000) | ((s & 0x001c0000) >> 5)) >> 13; + result2 |= ((s & 0x0000001f) | ((s & 0x0000001c) >> 5)) << 3; + + *dst32++ = result2; + *dst32++ = result1; + } + if (count & 0x1) { + const quint16 *src16 = reinterpret_cast(src); + *dst32 = qt_conv16ToRgb(src16[count - 1]); + } + return; +#endif + } + } + + const int bytesPerPixel = (pixelFormat.bitsPerPixel + 7) / 8; + +// nibble = 0; + + for (int i = 0; i < count; ++i) { + int r, g, b; + + switch (screendepth) { +#if 0 + case 4: { + if (!nibble) { + r = ((*src) & 0x0f) << 4; + } else { + r = (*src) & 0xf0; + src++; + } + nibble = !nibble; + g = b = r; + break; + } +#endif + case 8: { + QRgb rgb = qvnc_screen->clut()[int(*src)]; + r = qRed(rgb); + g = qGreen(rgb); + b = qBlue(rgb); + src++; + break; + } +#ifdef QT_QWS_DEPTH_12 + case 12: { + quint32 p = quint32(*reinterpret_cast(src)); + r = qRed(p); + g = qGreen(p); + b = qBlue(p); + src += sizeof(qrgb444); + break; + } +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: { + quint32 p = quint32(*reinterpret_cast(src)); + r = qRed(p); + g = qGreen(p); + b = qBlue(p); + src += sizeof(qrgb555); + break; + } +#endif + case 16: { + quint16 p = *reinterpret_cast(src); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (swapBytes) + p = ((p & 0xff) << 8) | ((p & 0xff00) >> 8); +#endif + r = (p >> 11) & 0x1f; + g = (p >> 5) & 0x3f; + b = p & 0x1f; + r <<= 3; + g <<= 2; + b <<= 3; + src += sizeof(quint16); + break; + } +#ifdef QT_QWS_DEPTH_18 + case 18: { + quint32 p = quint32(*reinterpret_cast(src)); + r = qRed(p); + g = qGreen(p); + b = qBlue(p); + src += sizeof(qrgb666); + break; + } +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: { + quint32 p = quint32(*reinterpret_cast(src)); + r = qRed(p); + g = qGreen(p); + b = qBlue(p); + src += sizeof(qrgb888); + break; + } +#endif + case 32: { + quint32 p = *reinterpret_cast(src); + r = (p >> 16) & 0xff; + g = (p >> 8) & 0xff; + b = p & 0xff; + src += sizeof(quint32); + break; + } + default: { + r = g = b = 0; + qDebug("QVNCServer: don't support %dbpp display", screendepth); + return; + } + } + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (swapBytes ^ isBgr) +#else + if (isBgr) +#endif + qSwap(r, b); + + r >>= (8 - pixelFormat.redBits); + g >>= (8 - pixelFormat.greenBits); + b >>= (8 - pixelFormat.blueBits); + + int pixel = (r << pixelFormat.redShift) | + (g << pixelFormat.greenShift) | + (b << pixelFormat.blueShift); + + if (sameEndian || pixelFormat.bitsPerPixel == 8) { + memcpy(dst, &pixel, bytesPerPixel); // XXX: do a simple for-loop instead? + dst += bytesPerPixel; + continue; + } + + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + switch (pixelFormat.bitsPerPixel) { + case 16: + pixel = (((pixel & 0x0000ff00) << 8) | + ((pixel & 0x000000ff) << 24)); + break; + case 32: + pixel = (((pixel & 0xff000000) >> 24) | + ((pixel & 0x00ff0000) >> 8) | + ((pixel & 0x0000ff00) << 8) | + ((pixel & 0x000000ff) << 24)); + break; + default: + qDebug("Cannot handle %d bpp client", pixelFormat.bitsPerPixel); + } + } else { // QSysInfo::ByteOrder == QSysInfo::LittleEndian + switch (pixelFormat.bitsPerPixel) { + case 16: + pixel = (((pixel & 0xff000000) >> 8) | + ((pixel & 0x00ff0000) << 8)); + break; + case 32: + pixel = (((pixel & 0xff000000) >> 24) | + ((pixel & 0x00ff0000) >> 8) | + ((pixel & 0x0000ff00) << 8) | + ((pixel & 0x000000ff) << 24)); + break; + default: + qDebug("Cannot handle %d bpp client", + pixelFormat.bitsPerPixel); + break; + } + } + memcpy(dst, &pixel, bytesPerPixel); // XXX: simple for-loop instead? + dst += bytesPerPixel; + } +} + +#ifndef QT_NO_QWS_CURSOR +static void blendCursor(QImage &image, const QRect &imageRect) +{ + const QRect cursorRect = qt_screencursor->boundingRect(); + const QRect intersection = (cursorRect & imageRect); + const QRect destRect = intersection.translated(-imageRect.topLeft()); + const QRect srcRect = intersection.translated(-cursorRect.topLeft()); + + QPainter painter(&image); + painter.drawImage(destRect, qt_screencursor->image(), srcRect); + painter.end(); +} +#endif // QT_NO_QWS_CURSOR + +QVNCDirtyMap::QVNCDirtyMap(QScreen *s) + : bytesPerPixel(0), numDirty(0), screen(s) +{ + bytesPerPixel = (screen->depth() + 7) / 8; + bufferWidth = screen->deviceWidth(); + bufferHeight = screen->deviceHeight(); + bufferStride = bufferWidth * bytesPerPixel; + buffer = new uchar[bufferHeight * bufferStride]; + + mapWidth = (bufferWidth + MAP_TILE_SIZE - 1) / MAP_TILE_SIZE; + mapHeight = (bufferHeight + MAP_TILE_SIZE - 1) / MAP_TILE_SIZE; + numTiles = mapWidth * mapHeight; + map = new uchar[numTiles]; +} + +QVNCDirtyMap::~QVNCDirtyMap() +{ + delete[] map; + delete[] buffer; +} + +void QVNCDirtyMap::reset() +{ + memset(map, 1, numTiles); + memset(buffer, 0, bufferHeight * bufferStride); + numDirty = numTiles; +} + +inline bool QVNCDirtyMap::dirty(int x, int y) const +{ + return map[y * mapWidth + x]; +} + +inline void QVNCDirtyMap::setClean(int x, int y) +{ + map[y * mapWidth + x] = 0; + --numDirty; +} + +template +void QVNCDirtyMapOptimized::setDirty(int tileX, int tileY, bool force) +{ + static bool alwaysForce = qgetenv("QT_VNC_NO_COMPAREBUFFER").toInt(); + if (alwaysForce) + force = true; + + bool changed = false; + + if (!force) { + const int lstep = screen->linestep(); + const int startX = tileX * MAP_TILE_SIZE; + const int startY = tileY * MAP_TILE_SIZE; + const uchar *scrn = screen->base() + + startY * lstep + startX * bytesPerPixel; + uchar *old = buffer + startY * bufferStride + startX * sizeof(T); + + const int tileHeight = (startY + MAP_TILE_SIZE > bufferHeight ? + bufferHeight - startY : MAP_TILE_SIZE); + const int tileWidth = (startX + MAP_TILE_SIZE > bufferWidth ? + bufferWidth - startX : MAP_TILE_SIZE); + const bool doInlines = (tileWidth == MAP_TILE_SIZE); + + int y = tileHeight; + + if (doInlines) { // hw: memcmp/memcpy is inlined when using constants + while (y) { + if (memcmp(old, scrn, sizeof(T) * MAP_TILE_SIZE)) { + changed = true; + break; + } + scrn += lstep; + old += bufferStride; + --y; + } + + while (y) { + memcpy(old, scrn, sizeof(T) * MAP_TILE_SIZE); + scrn += lstep; + old += bufferStride; + --y; + } + } else { + while (y) { + if (memcmp(old, scrn, sizeof(T) * tileWidth)) { + changed = true; + break; + } + scrn += lstep; + old += bufferStride; + --y; + } + + while (y) { + memcpy(old, scrn, sizeof(T) * tileWidth); + scrn += lstep; + old += bufferStride; + --y; + } + } + } + + const int mapIndex = tileY * mapWidth + tileX; + if ((force || changed) && !map[mapIndex]) { + map[mapIndex] = 1; + ++numDirty; + } +} + +template +QRfbHextileEncoder::QRfbHextileEncoder(QVNCServer *s) + : QRfbEncoder(s), + singleColorHextile(this), dualColorHextile(this), multiColorHextile(this) +{ +} + +/* + \internal + Send dirty rects using hextile encoding. +*/ +template +void QRfbHextileEncoder::write() +{ + QWSDisplay::grab(true); + + QVNCDirtyMap *map = server->dirtyMap(); + QTcpSocket *socket = server->clientSocket(); + + const quint32 encoding = htonl(5); // hextile encoding + const int bytesPerPixel = server->clientBytesPerPixel(); + + { + const char tmp[2] = { 0, 0 }; // msg type, padding + socket->write(tmp, sizeof(tmp)); + } + { + const quint16 count = htons(map->numDirty); + socket->write((char *)&count, sizeof(count)); + } + + if (map->numDirty <= 0) { + QWSDisplay::ungrab(); + return; + } + + newBg = true; + newFg = true; + + const QImage screenImage = server->screenImage(); + QRfbRect rect(0, 0, MAP_TILE_SIZE, MAP_TILE_SIZE); + + for (int y = 0; y < map->mapHeight; ++y) { + if (rect.y + MAP_TILE_SIZE > server->screen()->height()) + rect.h = server->screen()->height() - rect.y; + rect.w = MAP_TILE_SIZE; + for (int x = 0; x < map->mapWidth; ++x) { + if (!map->dirty(x, y)) + continue; + map->setClean(x, y); + + rect.x = x * MAP_TILE_SIZE; + if (rect.x + MAP_TILE_SIZE > server->screen()->deviceWidth()) + rect.w = server->screen()->deviceWidth() - rect.x; + rect.write(socket); + + socket->write((char *)&encoding, sizeof(encoding)); + + const uchar *screendata = screenImage.scanLine(rect.y) + + rect.x * screenImage.depth() / 8; + int linestep = screenImage.bytesPerLine(); + +#ifndef QT_NO_QWS_CURSOR + // hardware cursors must be blended with the screen memory + const bool doBlendCursor = qt_screencursor + && !server->hasClientCursor() + && qt_screencursor->isAccelerated(); + QImage tileImage; + if (doBlendCursor) { + const QRect tileRect(rect.x, rect.y, rect.w, rect.h); + const QRect cursorRect = qt_screencursor->boundingRect() + .translated(-server->screen()->offset()); + if (tileRect.intersects(cursorRect)) { + tileImage = screenImage.copy(tileRect); + blendCursor(tileImage, + tileRect.translated(server->screen()->offset())); + screendata = tileImage.bits(); + linestep = tileImage.bytesPerLine(); + } + } +#endif // QT_NO_QWS_CURSOR + + if (singleColorHextile.read(screendata, rect.w, rect.h, linestep)) { + singleColorHextile.write(socket); + } else if (dualColorHextile.read(screendata, rect.w, rect.h, linestep)) { + dualColorHextile.write(socket); + } else if (multiColorHextile.read(screendata, rect.w, rect.h, linestep)) { + multiColorHextile.write(socket); + } else if (server->doPixelConversion()) { + const int bufferSize = rect.w * rect.h * bytesPerPixel + 1; + const int padding = sizeof(quint32) - sizeof(char); + buffer.resize(bufferSize + padding); + + buffer[padding] = 1; // Raw subencoding + + // convert pixels + char *b = buffer.data() + padding + 1; + const int bstep = rect.w * bytesPerPixel; + for (int i = 0; i < rect.h; ++i) { + server->convertPixels(b, (const char*)screendata, rect.w); + screendata += linestep; + b += bstep; + } + socket->write(buffer.constData() + padding, bufferSize); + } else { + quint8 subenc = 1; // Raw subencoding + socket->write((char *)&subenc, 1); + + // send pixels + for (int i = 0; i < rect.h; ++i) { + socket->write((const char*)screendata, + rect.w * bytesPerPixel); + screendata += linestep; + } + } + } + if (socket->state() == QAbstractSocket::UnconnectedState) + break; + rect.y += MAP_TILE_SIZE; + } + socket->flush(); + Q_ASSERT(map->numDirty == 0); + + QWSDisplay::ungrab(); +} + +void QRfbRawEncoder::write() +{ + QWSDisplay::grab(false); + + QVNCDirtyMap *map = server->dirtyMap(); + QTcpSocket *socket = server->clientSocket(); + + const int bytesPerPixel = server->clientBytesPerPixel(); + + // create a region from the dirty rects and send the region's merged rects. + QRegion rgn; + if (map) { + for (int y = 0; y < map->mapHeight; ++y) { + for (int x = 0; x < map->mapWidth; ++x) { + if (!map->dirty(x, y)) + continue; + rgn += QRect(x * MAP_TILE_SIZE, y * MAP_TILE_SIZE, + MAP_TILE_SIZE, MAP_TILE_SIZE); + map->setClean(x, y); + } + } + + rgn &= QRect(0, 0, server->screen()->deviceWidth(), + server->screen()->deviceHeight()); + } + const QVector rects = rgn.rects(); + + { + const char tmp[2] = { 0, 0 }; // msg type, padding + socket->write(tmp, sizeof(tmp)); + } + + { + const quint16 count = htons(rects.size()); + socket->write((char *)&count, sizeof(count)); + } + + if (rects.size() <= 0) { + QWSDisplay::ungrab(); + return; + } + + const QImage screenImage = server->screenImage(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect tileRect = rects.at(i); + const QRfbRect rect(tileRect.x(), tileRect.y(), + tileRect.width(), tileRect.height()); + rect.write(socket); + + const quint32 encoding = htonl(0); // raw encoding + socket->write((char *)&encoding, sizeof(encoding)); + + int linestep = screenImage.bytesPerLine(); + const uchar *screendata = screenImage.scanLine(rect.y) + + rect.x * screenImage.depth() / 8; + +#ifndef QT_NO_QWS_CURSOR + // hardware cursors must be blended with the screen memory + const bool doBlendCursor = qt_screencursor + && !server->hasClientCursor() + && qt_screencursor->isAccelerated(); + QImage tileImage; + if (doBlendCursor) { + const QRect cursorRect = qt_screencursor->boundingRect() + .translated(-server->screen()->offset()); + if (tileRect.intersects(cursorRect)) { + tileImage = screenImage.copy(tileRect); + blendCursor(tileImage, + tileRect.translated(server->screen()->offset())); + screendata = tileImage.bits(); + linestep = tileImage.bytesPerLine(); + } + } +#endif // QT_NO_QWS_CURSOR + + if (server->doPixelConversion()) { + const int bufferSize = rect.w * rect.h * bytesPerPixel; + if (bufferSize > buffer.size()) + buffer.resize(bufferSize); + + // convert pixels + char *b = buffer.data(); + const int bstep = rect.w * bytesPerPixel; + for (int i = 0; i < rect.h; ++i) { + server->convertPixels(b, (const char*)screendata, rect.w); + screendata += linestep; + b += bstep; + } + socket->write(buffer.constData(), bufferSize); + } else { + for (int i = 0; i < rect.h; ++i) { + socket->write((const char*)screendata, rect.w * bytesPerPixel); + screendata += linestep; + } + } + if (socket->state() == QAbstractSocket::UnconnectedState) + break; + } + socket->flush(); + + QWSDisplay::ungrab(); +} + +inline QImage QVNCServer::screenImage() const +{ + return QImage(qvnc_screen->base(), qvnc_screen->deviceWidth(), + qvnc_screen->deviceHeight(), qvnc_screen->linestep(), + qvnc_screen->pixelFormat()); +} + +void QVNCServer::checkUpdate() +{ + if (!wantUpdate) + return; + + if (dirtyCursor) { +#ifndef QT_NO_QWS_CURSOR + Q_ASSERT(qvnc_cursor); + qvnc_cursor->write(); +#endif + dirtyCursor = false; + wantUpdate = false; + return; + } + + if (dirtyMap()->numDirty > 0) { + if (encoder) + encoder->write(); + wantUpdate = false; + } +} + +void QVNCServer::discardClient() +{ + timer->stop(); + state = Unconnected; + delete encoder; + encoder = 0; +#ifndef QT_NO_QWS_CURSOR + delete qvnc_cursor; + qvnc_cursor = 0; +#endif + if (!qvnc_screen->screen() && !qvnc_screen->d_ptr->noDisablePainting && QWSServer::instance()) + QWSServer::instance()->enablePainting(false); +} + + +//=========================================================================== + +/*! + \class QVNCScreen + \internal + \ingroup qws + + \brief The QVNCScreen class implements a screen driver for VNC + servers. + + Note that this class is only available in \l{Qt for Embedded Linux}. + Custom screen drivers can be added by subclassing the QScreen + class, using the QScreenDriverFactory class to dynamically load + the driver into the application. + + The VNC protocol allows you to view and interact with the + computer's display from anywhere on the network. See the + \l{The VNC Protocol and Qt for Embedded Linux}{VNC protocol} + documentation for more details. + + The default implementation of QVNCScreen inherits QLinuxFbScreen, + but any QScreen subclass, or QScreen itself, can serve as its base + class. This is easily achieved by manipulating the \c + VNCSCREEN_BASE definition in the header file. + + \sa QScreen, {Running Applications} +*/ + +/*! + \fn QVNCScreen::QVNCScreen(int displayId) + + Constructs a QVNCScreen object. The \a displayId argument + identifies the Qt for Embedded Linux server to connect to. +*/ +QVNCScreen::QVNCScreen(int display_id) + : QProxyScreen(display_id, VNCClass) +{ + d_ptr = new QVNCScreenPrivate(this); +} + +/*! + Destroys this QVNCScreen object. +*/ +QVNCScreen::~QVNCScreen() +{ + delete d_ptr; +} + +/*! + \reimp +*/ +void QVNCScreen::setDirty(const QRect &rect) +{ + d_ptr->setDirty(rect); +} + +void QVNCScreenPrivate::setDirty(const QRect& rect, bool force) +{ + if (rect.isEmpty()) + return; + + if (q_ptr->screen()) + q_ptr->screen()->setDirty(rect); + + if (!vncServer || !vncServer->isConnected()) + return; + + const QRect r = rect.translated(-q_ptr->offset()); + const int x1 = r.x() / MAP_TILE_SIZE; + int y = r.y() / MAP_TILE_SIZE; + for (; (y <= r.bottom() / MAP_TILE_SIZE) && y < dirty->mapHeight; y++) + for (int x = x1; (x <= r.right() / MAP_TILE_SIZE) && x < dirty->mapWidth; x++) + dirty->setDirty(x, y, force); + + vncServer->setDirty(); +} + +static int getDisplayId(const QString &spec) +{ + QRegExp regexp(QLatin1String(":(\\d+)\\b")); + if (regexp.lastIndexIn(spec) != -1) { + const QString capture = regexp.cap(1); + return capture.toInt(); + } + return 0; +} + +/*! + \reimp +*/ +bool QVNCScreen::connect(const QString &displaySpec) +{ + QString dspec = displaySpec; + if (dspec.startsWith(QLatin1String("vnc:"), Qt::CaseInsensitive)) + dspec = dspec.mid(QString::fromLatin1("vnc:").size()); + else if (dspec.compare(QLatin1String("vnc"), Qt::CaseInsensitive) == 0) + dspec = QString(); + + const QString displayIdSpec = QString::fromLatin1(" :%1").arg(displayId); + if (dspec.endsWith(displayIdSpec)) + dspec = dspec.left(dspec.size() - displayIdSpec.size()); + + QStringList args = dspec.split(QLatin1Char(':'), + QString::SkipEmptyParts); + QRegExp refreshRegexp(QLatin1String("^refreshrate=(\\d+)$")); + int index = args.indexOf(refreshRegexp); + if (index >= 0) { + d_ptr->refreshRate = refreshRegexp.cap(1).toInt(); + args.removeAt(index); + dspec = args.join(QLatin1String(":")); + } + + QString driver = dspec; + int colon = driver.indexOf(QLatin1Char(':')); + if (colon >= 0) + driver.truncate(colon); + + if (QScreenDriverFactory::keys().contains(driver, Qt::CaseInsensitive)) { + const int id = getDisplayId(dspec); + QScreen *s = qt_get_screen(id, dspec.toLatin1().constData()); + if (s->pixelFormat() == QImage::Format_Indexed8 + || s->pixelFormat() == QImage::Format_Invalid && s->depth() == 8) + qFatal("QVNCScreen: unsupported screen format"); + setScreen(s); + } else { // create virtual screen +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + QScreen::setFrameBufferLittleEndian(false); +#endif + + d = qgetenv("QWS_DEPTH").toInt(); + if (!d) + d = 16; + + QByteArray str = qgetenv("QWS_SIZE"); + if(!str.isEmpty()) { + sscanf(str.constData(), "%dx%d", &w, &h); + dw = w; + dh = h; + } else { + dw = w = 640; + dh = h = 480; + } + + const QStringList args = displaySpec.split(QLatin1Char(':'), + QString::SkipEmptyParts); + + if (args.contains(QLatin1String("paintonscreen"), Qt::CaseInsensitive)) + d_ptr->doOnScreenSurface = true; + + QRegExp depthRegexp(QLatin1String("^depth=(\\d+)$")); + if (args.indexOf(depthRegexp) != -1) + d = depthRegexp.cap(1).toInt(); + + QRegExp sizeRegexp(QLatin1String("^size=(\\d+)x(\\d+)$")); + if (args.indexOf(sizeRegexp) != -1) { + dw = w = sizeRegexp.cap(1).toInt(); + dh = h = sizeRegexp.cap(2).toInt(); + } + + // Handle display physical size spec. + QRegExp mmWidthRegexp(QLatin1String("^mmWidth=?(\\d+)$")); + if (args.indexOf(mmWidthRegexp) != -1) { + const int mmWidth = mmWidthRegexp.cap(1).toInt(); + if (mmWidth > 0) + d_ptr->dpiX = dw * 25.4 / mmWidth; + } + QRegExp mmHeightRegexp(QLatin1String("^mmHeight=?(\\d+)$")); + if (args.indexOf(mmHeightRegexp) != -1) { + const int mmHeight = mmHeightRegexp.cap(1).toInt(); + if (mmHeight > 0) + d_ptr->dpiY = dh * 25.4 / mmHeight; + } + QRegExp dpiRegexp(QLatin1String("^dpi=(\\d+)(?:,(\\d+))?$")); + if (args.indexOf(dpiRegexp) != -1) { + const qreal dpiX = dpiRegexp.cap(1).toFloat(); + const qreal dpiY = dpiRegexp.cap(2).toFloat(); + if (dpiX > 0) + d_ptr->dpiX = dpiX; + d_ptr->dpiY = (dpiY > 0 ? dpiY : dpiX); + } + + if (args.contains(QLatin1String("noDisablePainting"))) + d_ptr->noDisablePainting = true; + + QWSServer::setDefaultMouse("None"); + QWSServer::setDefaultKeyboard("None"); + + d_ptr->configure(); + } + + // XXX + qt_screen = this; + + return true; +} + +/*! + \reimp +*/ +void QVNCScreen::disconnect() +{ + QProxyScreen::disconnect(); +#if !defined(QT_NO_QWS_MULTIPROCESS) && !defined(QT_NO_SHAREDMEMORY) + d_ptr->shm.detach(); +#endif +} + +/*! + \reimp +*/ +bool QVNCScreen::initDevice() +{ + if (!QProxyScreen::screen() && d == 4) { + screencols = 16; + int val = 0; + for (int idx = 0; idx < 16; idx++, val += 17) { + screenclut[idx] = qRgb(val, val, val); + } + } + d_ptr->vncServer = new QVNCServer(this, displayId); + d_ptr->vncServer->setRefreshRate(d_ptr->refreshRate); + + switch (depth()) { +#ifdef QT_QWS_DEPTH_32 + case 32: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif +#ifdef QT_QWS_DEPTH_18 + case 18: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif +#ifdef QT_QWS_DEPTH_16 + case 16: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif +#ifdef QT_QWS_DEPTH_12 + case 12: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif +#ifdef QT_QWS_DEPTH_8 + case 8: + d_ptr->dirty = new QVNCDirtyMapOptimized(this); + break; +#endif + default: + qWarning("QVNCScreen::initDevice: No support for screen depth %d", + depth()); + d_ptr->dirty = 0; + return false; + } + + + const bool ok = QProxyScreen::initDevice(); +#ifndef QT_NO_QWS_CURSOR + qt_screencursor = new QVNCCursor(this); +#endif + if (QProxyScreen::screen()) + return ok; + + // Disable painting if there is only 1 display and nothing is attached to the VNC server + if (!d_ptr->noDisablePainting) + QWSServer::instance()->enablePainting(false); + + return true; +} + +/*! + \reimp +*/ +void QVNCScreen::shutdownDevice() +{ + QProxyScreen::shutdownDevice(); + delete d_ptr->vncServer; + delete d_ptr->dirty; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_VNC diff --git a/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.h b/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.h new file mode 100644 index 0000000000..646109f5ef --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#ifndef QSCREENVNC_QWS_H +#define QSCREENVNC_QWS_H + +#include + +#ifndef QT_NO_QWS_VNC + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QVNCScreenPrivate; + +class QVNCScreen : public QProxyScreen +{ +public: + explicit QVNCScreen(int display_id); + virtual ~QVNCScreen(); + + bool initDevice(); + bool connect(const QString &displaySpec); + void disconnect(); + void shutdownDevice(); + + void setDirty(const QRect&); + +private: + friend class QVNCCursor; + friend class QVNCClientCursor; + friend class QVNCServer; + friend class QVNCScreenPrivate; + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + bool swapBytes() const; +#endif + + QVNCScreenPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_VNC +#endif // QSCREENVNC_QWS_H diff --git a/src/plugins/gfxdrivers/vnc/vnc.pro b/src/plugins/gfxdrivers/vnc/vnc.pro new file mode 100644 index 0000000000..31da2f404e --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/vnc.pro @@ -0,0 +1,16 @@ +TARGET = qgfxvnc +include(../../qpluginbase.pri) + +DEFINES += QT_QWS_VNC + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +HEADERS = \ + qscreenvnc_qws.h \ + qscreenvnc_p.h + +SOURCES = main.cpp \ + qscreenvnc_qws.cpp + +target.path += $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target -- cgit v1.2.3