diff options
Diffstat (limited to 'src/plugins/gfxdrivers')
51 files changed, 12209 insertions, 0 deletions
diff --git a/src/plugins/gfxdrivers/ahi/ahi.pro b/src/plugins/gfxdrivers/ahi/ahi.pro new file mode 100644 index 0000000..6fc8a5c --- /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 0000000..0f4a870 --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/qscreenahi_qws.cpp @@ -0,0 +1,598 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscreenahi_qws.h" + +#ifndef QT_NO_QWS_AHI + +#include <QtGui/qcolor.h> +#include <QtGui/qapplication.h> +#include <QtCore/qvector.h> +#include <QtCore/qvarlengtharray.h> +#include <private/qwssignalhandler_p.h> + +#include <ahi.h> + +//#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<QRect> rects = (reg & region()).rects(); + const int numRects = rects.size(); + QVarLengthArray<AhiPoint_t, 8> src(numRects); + QVarLengthArray<AhiRect_t, 8> 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<QRect> rects = (reg & region()).rects(); + QVarLengthArray<AhiRect_t> 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 0000000..37f2872 --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/qscreenahi_qws.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAHISCREEN_H +#define QAHISCREEN_H + +#include <QtGui/qscreenlinuxfb_qws.h> + +#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 0000000..c1a22fa --- /dev/null +++ b/src/plugins/gfxdrivers/ahi/qscreenahiplugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscreenahi_qws.h" + +#include <QScreenDriverPlugin> +#include <QStringList> + +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 0000000..96eb536 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/directfb.pro @@ -0,0 +1,40 @@ +TARGET = qdirectfbscreen +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +# These defines might be necessary if your DirectFB driver doesn't +# support all of the DirectFB API. +# +#DEFINES += QT_NO_DIRECTFB_WM +#DEFINES += QT_NO_DIRECTFB_LAYER +#DEFINES += QT_NO_DIRECTFB_PALETTE +#DEFINES += QT_NO_DIRECTFB_PREALLOCATED +#DEFINES += QT_NO_DIRECTFB_MOUSE +#DEFINES += QT_NO_DIRECTFB_KEYBOARD + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +HEADERS = \ + qdirectfbscreen.h \ + qdirectfbsurface.h \ + qdirectfbpaintengine.h \ + qdirectfbpaintdevice.h \ + qdirectfbpixmap.h \ + qdirectfbkeyboard.h \ + qdirectfbmouse.h + +SOURCES = \ + qdirectfbscreen.cpp \ + qdirectfbscreenplugin.cpp \ + qdirectfbsurface.cpp \ + qdirectfbpaintengine.cpp \ + qdirectfbpaintdevice.cpp \ + qdirectfbpixmap.cpp \ + qdirectfbkeyboard.cpp \ + qdirectfbmouse.cpp + +QMAKE_CXXFLAGS += $$QT_CFLAGS_DIRECTFB +LIBS += $$QT_LIBS_DIRECTFB + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp new file mode 100644 index 0000000..cd19f69 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbkeyboard.h" + +#ifndef QT_NO_DIRECTFB + +#include "qdirectfbscreen.h" +#include <qobject.h> +#include <qsocketnotifier.h> +#include <qhash.h> + +#include <directfb.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +class KeyMap : public QHash<DFBInputDeviceKeySymbol, Qt::Key> +{ +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; + +private slots: + void readKeyboardData(); +}; + +QDirectFBKeyboardHandlerPrivate::QDirectFBKeyboardHandlerPrivate(QDirectFBKeyboardHandler *h) + : handler(h), eventBuffer(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)); + bytesRead = 0; + + + 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<char*>(&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; + + 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; + } + // 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 int warningCount = 0; + if (!warningCount) { + qWarning("QDirectFBKeyboardHandler - Getting unexpected non-keyboard related events"); + warningCount = 100; + } + else + warningCount--; + break; + } + + bool press = input.type & DIET_KEYPRESS; + DFBInputDeviceKeySymbol symbol = input.key_symbol; + int unicode = -1; + int keycode = 0; + + keycode = keymap()->value(symbol); + if (keycode == 0 && DFB_KEY_TYPE(symbol) == DIKT_UNICODE) + unicode = symbol; + + if (unicode != -1 || keycode != 0) { + handler->processKeyEvent(unicode, keycode, + modifiers, press, false); + } + } +} + +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); +} + +#include "qdirectfbkeyboard.moc" + +#endif // QT_NO_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h new file mode 100644 index 0000000..234a266 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTFBKEYBOARD_H +#define QDIRECTFBKEYBOARD_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +#ifndef QT_NO_DIRECTFB + +class QDirectFBKeyboardHandlerPrivate; + +class QDirectFBKeyboardHandler : public QWSKeyboardHandler +{ +public: + QDirectFBKeyboardHandler(const QString &device); + ~QDirectFBKeyboardHandler(); + +private: + QDirectFBKeyboardHandlerPrivate *d; +}; + +#endif // QT_NO_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 0000000..f4d9b46 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbmouse.h" + +#include "qdirectfbscreen.h" +#include <qsocketnotifier.h> + +#include <directfb.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +class QDirectFBMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QDirectFBMouseHandlerPrivate(QDirectFBMouseHandler *h); + ~QDirectFBMouseHandlerPrivate(); + + void suspend(); + void resume(); + +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 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 = DFBInputDeviceCapabilities(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())); + resume(); +} + +QDirectFBMouseHandlerPrivate::~QDirectFBMouseHandlerPrivate() +{ + if (eventBuffer) + eventBuffer->Release(eventBuffer); +} + +void QDirectFBMouseHandlerPrivate::suspend() +{ + mouseNotifier->setEnabled(false); +} + +void QDirectFBMouseHandlerPrivate::resume() +{ + eventBuffer->Reset(eventBuffer); + mouseNotifier->setEnabled(true); +} + +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<char*>(&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) { +#ifdef QT_NO_DIRECTFB_LAYER + 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->suspend(); +} + +void QDirectFBMouseHandler::resume() +{ + d->resume(); +} + +#include "qdirectfbmouse.moc" + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbmouse.h b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.h new file mode 100644 index 0000000..e81a4ba --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbmouse.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTFBMOUSE_H +#define QDIRECTFBMOUSE_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +#ifndef QT_NO_DIRECTFB + +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; +}; + +#endif // QT_NO_DIRECTFB + +QT_END_HEADER + +#endif // QDIRECTFBMOUSE_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp new file mode 100644 index 0000000..d1802e4 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_DIRECTFB + +#include "qdirectfbscreen.h" +#include "qdirectfbpaintdevice.h" + + +IDirectFBSurface *QDirectFBPaintDevice::directFbSurface() const +{ + return dfbSurface; +} + + +// Locks the dfb surface and creates a QImage (lockedImage) from the pointer +void QDirectFBPaintDevice::lockDirectFB() { + + if (lockedImage) + return; // Already locked + + void *mem; + int w, h; + int bpl; + DFBSurfacePixelFormat format; + + DFBResult result = dfbSurface->Lock(dfbSurface, DSLF_WRITE, &mem, &bpl); + if (result != DFB_OK || !mem) { + DirectFBError("QDirectFBPixmapData::buffer()", result); + return; + } + + dfbSurface->GetSize(dfbSurface, &w, &h); + dfbSurface->GetPixelFormat(dfbSurface, &format); + + lockedImage = new QImage(static_cast<uchar*>(mem), w, h, bpl, + QDirectFBScreen::getImageFormat(format)); +} + + +void QDirectFBPaintDevice::unlockDirectFB() +{ + if (!lockedImage) + return; + + dfbSurface->Unlock(dfbSurface); + delete lockedImage; + lockedImage = 0; +} + + +void* QDirectFBPaintDevice::memory() const +{ + QDirectFBPaintDevice* that = const_cast<QDirectFBPaintDevice*>(this); + that->lockDirectFB(); + Q_ASSERT(that->lockedImage); + return that->lockedImage->bits(); +} + + +QImage::Format QDirectFBPaintDevice::format() const +{ + DFBSurfacePixelFormat dfbFormat; + dfbSurface->GetPixelFormat(dfbSurface, &dfbFormat); + return QDirectFBScreen::getImageFormat(dfbFormat); +} + + +int QDirectFBPaintDevice::bytesPerLine() const +{ + // Can only get the stride when we lock the surface + QDirectFBPaintDevice* that = const_cast<QDirectFBPaintDevice*>(this); + that->lockDirectFB(); + Q_ASSERT(that->lockedImage); + return that->lockedImage->bytesPerLine(); +} + + +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; + + int w, h; + dfbSurface->GetSize(dfbSurface, &w, &h); + + int dpmX, dpmY; // Dots-per-meter ;-) + + // Do some common calculations: + switch (metric) { + case QPaintDevice::PdmWidthMM: + case QPaintDevice::PdmPhysicalDpiX: + case QPaintDevice::PdmDpiX: + dpmX = (screen->deviceWidth() * 1000) / screen->physicalWidth(); + break; + case QPaintDevice::PdmHeightMM: + case QPaintDevice::PdmPhysicalDpiY: + case QPaintDevice::PdmDpiY: + dpmY = (screen->deviceHeight() * 1000) / screen->physicalHeight(); + break; + default: + break; + } + + // Now use those calculations + switch (metric) { + case QPaintDevice::PdmWidth: + return w; + case QPaintDevice::PdmHeight: + return h; + case QPaintDevice::PdmWidthMM: + return (w * 1000) / dpmX; + case QPaintDevice::PdmHeightMM: + return (h * 1000) / dpmY; + case QPaintDevice::PdmPhysicalDpiX: + case QPaintDevice::PdmDpiX: + return (dpmX * 254) / 10000; // 0.0254 meters-per-inch + case QPaintDevice::PdmPhysicalDpiY: + case QPaintDevice::PdmDpiY: + return (dpmY * 254) / 10000; // 0.0254 meters-per-inch + case QPaintDevice::PdmDepth: + DFBSurfacePixelFormat format; + dfbSurface->GetPixelFormat(dfbSurface, &format); + return QDirectFBScreen::depth(format); + case QPaintDevice::PdmNumColors: { + if (lockedImage) + return lockedImage->numColors(); + + 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; + } +} + +#endif diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h new file mode 100644 index 0000000..c28d37c --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTFBPAINTDEVICE_H +#define QDIRECTFBPAINTDEVICE_H + +#include <private/qpaintengine_raster_p.h> +#include <directfb.h> +#include "qdirectfbscreen.h" + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +// Inherited by both window surface and pixmap +class QDirectFBPaintDevice : public QCustomRasterPaintDevice +{ +public: + QDirectFBPaintDevice(QDirectFBScreen *scr = QDirectFBScreen::instance()) + : QCustomRasterPaintDevice(0), + dfbSurface(0), + lockedImage(0), + screen(scr) {} + + IDirectFBSurface *directFbSurface() const; + + void lockDirectFB(); + void unlockDirectFB(); + + // Reimplemented from QCustomRasterPaintDevice: + void* memory() const; + QImage::Format format() const; + int bytesPerLine() const; + QSize size() const; + int metric(QPaintDevice::PaintDeviceMetric metric) const; + +protected: + IDirectFBSurface *dfbSurface; + QImage *lockedImage; + QDirectFBScreen *screen; +}; + +QT_END_HEADER + +#endif //QDIRECTFBPAINTDEVICE_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp new file mode 100644 index 0000000..4fc4035 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp @@ -0,0 +1,1261 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbpaintengine.h" + +#ifndef QT_NO_DIRECTFB + +#include "qdirectfbsurface.h" +#include "qdirectfbscreen.h" +#include "qdirectfbpixmap.h" +#include <directfb.h> +#include <qtransform.h> +#include <qvarlengtharray.h> +#include <qcache.h> +#include <qmath.h> +#include <private/qpixmapdata_p.h> +#include <private/qpixmap_raster_p.h> + +static inline uint ALPHA_MUL(uint x, uint a) +{ + uint t = x * a; + t = ((t + (t >> 8) + 0x80) >> 8) & 0xff; + return t; +} + +static inline QRect mapRect(const QTransform &transform, const QRect &rect) +{ + return (transform.isIdentity() ? rect : transform.mapRect(rect)); +} + +static inline QRect mapRect(const QTransform &transform, const QRectF &rect) +{ + return (transform.isIdentity() ? rect : transform.mapRect(rect)). + toRect(); +} + +class SurfaceCache +{ +public: + SurfaceCache(); + ~SurfaceCache(); + + inline IDirectFBSurface *getSurface(const uint *buffer, int size); + inline void clear(); + +private: + IDirectFBSurface *surface; + uint *buffer; + int bufsize; +}; + +SurfaceCache::SurfaceCache() + : surface(0), buffer(0), bufsize(0) +{ +} + +class CachedImage +{ +public: + CachedImage(const QImage &image); + ~CachedImage(); + + IDirectFBSurface *surface() { return s; } + +private: + IDirectFBSurface *s; +}; + +CachedImage::CachedImage(const QImage &image) + : s(0) +{ + IDirectFBSurface *tmpSurface = 0; + DFBSurfaceDescription description; + description = QDirectFBScreen::getSurfaceDescription(image); + QDirectFBScreen* screen = QDirectFBScreen::instance(); + + tmpSurface = screen->createDFBSurface(&description); + if (!tmpSurface) { + qWarning("CachedImage CreateSurface failed!"); + return; + } + +#ifndef QT_NO_DIRECTFB_PALETTE + QDirectFBScreen::setSurfaceColorTable(tmpSurface, image); +#endif + + description.flags = DFBSurfaceDescriptionFlags(description.flags & ~DSDESC_PREALLOCATED); + + s = screen->createDFBSurface(&description); + if (!s) + qWarning("QDirectFBPaintEngine failed caching image"); + +#ifndef QT_NO_DIRECTFB_PALETTE + QDirectFBScreen::setSurfaceColorTable(s, image); +#endif + + if (s) { + s->SetBlittingFlags(s, DSBLIT_NOFX); + s->Blit(s, tmpSurface, 0, 0, 0); + s->ReleaseSource(s); + } + if (tmpSurface) + screen->releaseDFBSurface(tmpSurface); +} + +CachedImage::~CachedImage() +{ + if (s && QDirectFBScreen::instance()) + QDirectFBScreen::instance()->releaseDFBSurface(s); +} + +static QCache<qint64, CachedImage> imageCache(4*1024*1024); // 4 MB + +IDirectFBSurface* SurfaceCache::getSurface(const uint *buf, int size) +{ + if (buffer == buf && bufsize == size) + return surface; + + clear(); + + DFBSurfaceDescription description; + description = QDirectFBScreen::getSurfaceDescription(buf, size); + + surface = QDirectFBScreen::instance()->createDFBSurface(&description); + if (!surface) + qWarning("QDirectFBPaintEngine: SurfaceCache: Unable to create surface"); + + buffer = const_cast<uint*>(buf); + bufsize = size; + + return surface; +} + +void SurfaceCache::clear() +{ + if (surface) + QDirectFBScreen::instance()->releaseDFBSurface(surface); + surface = 0; + buffer = 0; + bufsize = 0; +} + +SurfaceCache::~SurfaceCache() +{ + clear(); +} + +class QDirectFBPaintEnginePrivate : public QRasterPaintEnginePrivate +{ +public: + QDirectFBPaintEnginePrivate(QDirectFBPaintEngine *p); + ~QDirectFBPaintEnginePrivate(); + + IDirectFBSurface *surface; + + QPen pen; + QBrush brush; + + bool antialiased; + + bool simplePen; + bool simpleBrush; + + bool matrixRotShear; + bool matrixScale; + + void setTransform(const QTransform &m); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setCompositionMode(QPainter::CompositionMode mode); + void setOpacity(const qreal value); + void setRenderHints(QPainter::RenderHints hints); + + inline void setDFBColor(const QColor &color) const; + + inline bool lock(); + inline void unlock(); + + inline bool dfbCanHandleClip(const QRect &rect) const; + inline bool dfbCanHandleClip(const QRectF &rect) const; + inline bool dfbCanHandleClip() const; + + void drawLines(const QLine *lines, int count) const; + void drawLines(const QLineF *lines, int count) const; + + void fillRegion(const QRegion &r) const; + void fillRects(const QRect *rects, int count) const; + void drawRects(const QRect *rects, int count) const; + void fillRects(const QRectF *rects, int count) const; + void drawRects(const QRectF *rects, int count) const; + + void drawPixmap(const QRectF &dest, + const QPixmap &pixmap, const QRectF &src); + void drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap); + void drawImage(const QRectF &dest, const QImage &image, const QRectF &src); + + void updateClip(); + void updateFlags(); + inline void setClipDirty(); + void systemStateChanged(); //Needed to be notified when system clip changes + + void begin(QPaintDevice *device); + void end(); + + SurfaceCache *surfaceCache; + +private: +// QRegion rectsToClippedRegion(const QRect *rects, int n) const; +// QRegion rectsToClippedRegion(const QRectF *rects, int n) const; + + IDirectFB *fb; + DFBSurfaceDescription fbDescription; + int fbWidth; + int fbHeight; + + quint8 opacity; + QTransform transform; + + quint32 drawFlags; + quint32 blitFlags; + quint32 duffFlags; + bool dirtyFlags; + bool dirtyClip; + bool dfbHandledClip; + + QDirectFBPaintEngine *q; +}; + +QDirectFBPaintEnginePrivate::QDirectFBPaintEnginePrivate(QDirectFBPaintEngine *p) + : surface(0), antialiased(false), simplePen(false), + simpleBrush(false), matrixRotShear(false), matrixScale(false), fbWidth(-1), fbHeight(-1), + opacity(255), drawFlags(0), blitFlags(0), duffFlags(0), dirtyFlags(false), dirtyClip(true), + dfbHandledClip(false), q(p) +{ + fb = QDirectFBScreen::instance()->dfb(); + surfaceCache = new SurfaceCache; + static int cacheLimit = qgetenv("QT_DIRECTFB_IMAGECACHE").toInt(); + if (cacheLimit > 0) + imageCache.setMaxCost(cacheLimit * 1024); +} + +QDirectFBPaintEnginePrivate::~QDirectFBPaintEnginePrivate() +{ + unlock(); + delete surfaceCache; +} + +bool QDirectFBPaintEnginePrivate::dfbCanHandleClip(const QRect &rect) const +{ + // TODO: Check to see if DirectFB can handle the clip for the given rect + return dfbHandledClip; +} + +bool QDirectFBPaintEnginePrivate::dfbCanHandleClip(const QRectF &rect) const +{ + // TODO: Check to see if DirectFB can handle the clip for the given rect + return dfbHandledClip; +} + +bool QDirectFBPaintEnginePrivate::dfbCanHandleClip() const +{ + return dfbHandledClip; +} + +void QDirectFBPaintEnginePrivate::setClipDirty() +{ + dirtyClip = true; +} + + +bool 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. + if (device->devType() == QInternal::CustomRaster) { + prepare(static_cast<QCustomRasterPaintDevice*>(device)); + return true; + } + return false; +} + +void QDirectFBPaintEnginePrivate::unlock() +{ + QPaintDevice *device = q->paintDevice(); + if (!device) //XXX This should probably be an assert + return; + + Q_ASSERT(device->devType() == QInternal::CustomRaster); + QDirectFBPaintDevice* dfbDevice = static_cast<QDirectFBPaintDevice*>(device); + dfbDevice->unlockDirectFB(); +} + +void QDirectFBPaintEnginePrivate::setTransform(const QTransform &m) +{ + transform = m; + matrixRotShear = (transform.m12() != 0 || transform.m21() != 0); + matrixScale = (transform.m11() != 1 || transform.m22() != 1); +} + +void QDirectFBPaintEnginePrivate::begin(QPaintDevice *device) +{ + QDirectFBPaintDevice* dfbDevice = 0; + + if (device->devType() == QInternal::CustomRaster) + dfbDevice = static_cast<QDirectFBPaintDevice*>(device); + else if (device->devType() == QInternal::Pixmap) { + QPixmapData *data = static_cast<QPixmap*>(device)->pixmapData(); + if (data->classId() == QPixmapData::DirectFBClass) { + QDirectFBPixmapData* dfbPixmapData = static_cast<QDirectFBPixmapData*>(data); + dfbDevice = static_cast<QDirectFBPaintDevice*>(dfbPixmapData); + } + } + + if (dfbDevice) + surface = dfbDevice->directFbSurface(); + + if (!surface) { + qFatal("QDirectFBPaintEngine used on an invalid device: 0x%x", + device->devType()); + } + + surface->GetSize(surface, &fbWidth, &fbHeight); + + setTransform(QTransform()); + antialiased = false; + drawFlags = DSDRAW_BLEND; + blitFlags = DSBLIT_BLEND_ALPHACHANNEL; + duffFlags = DSPD_SRC_OVER; + opacity = 255; + dirtyFlags = true; + dirtyClip = true; + setPen(q->state()->pen); + setDFBColor(pen.color()); +} + +void QDirectFBPaintEnginePrivate::end() +{ + surface->ReleaseSource(surface); + surface->SetClip(surface, NULL); + surface = 0; +} + +void QDirectFBPaintEnginePrivate::setPen(const QPen &p) +{ + pen = p; + simplePen = (pen.style() == Qt::NoPen) || + (pen.style() == Qt::SolidLine && !antialiased + && (pen.widthF() <= 1 && !matrixScale)); +} + +void QDirectFBPaintEnginePrivate::setBrush(const QBrush &b) +{ + // TODO: accelerate texture pattern + brush = b; + simpleBrush = (brush.style() == Qt::NoBrush) || + (brush.style() == Qt::SolidPattern && !antialiased); +} + +void QDirectFBPaintEnginePrivate::setCompositionMode(QPainter::CompositionMode mode) +{ + drawFlags &= ~(DSDRAW_XOR); + blitFlags &= ~(DSBLIT_XOR); + + // TODO: check these mappings!!!! + quint32 duff = DSPD_NONE; + quint32 blit = blitFlags; + + switch (mode) { + case QPainter::CompositionMode_SourceOver: + duff = DSPD_SRC_OVER; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_DestinationOver: + duff = DSPD_DST_OVER; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_Clear: + duff = DSPD_CLEAR; + blit &= ~DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_Source: + duff = DSPD_SRC; + blit &= ~DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_Destination: + blit &= ~DSBLIT_BLEND_ALPHACHANNEL; + return; + case QPainter::CompositionMode_SourceIn: + duff = DSPD_SRC_IN; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_DestinationIn: + duff = DSPD_DST_IN; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_SourceOut: + duff = DSPD_SRC_OUT; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_DestinationOut: + duff = DSPD_DST_OUT; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_SourceAtop: + duff = DSPD_SRC_OVER; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + break; + case QPainter::CompositionMode_DestinationAtop: + duff = DSPD_DST_OVER; + break; + case QPainter::CompositionMode_Xor: + duff = DSPD_NONE; + blit |= DSBLIT_BLEND_ALPHACHANNEL; + drawFlags |= DSDRAW_XOR; + blit |= DSBLIT_XOR; + dirtyFlags = true; + break; + default: + qWarning("QDirectFBPaintEnginePrivate::setCompositionMode(): " + "mode %d not implemented", mode); + break; + } + + if (duff != duffFlags || blit != blitFlags) { + duffFlags = duff; + blitFlags = blit; + dirtyFlags = true; + } +} + +void QDirectFBPaintEnginePrivate::setOpacity(const qreal value) +{ + const bool wasOpaque = (opacity == 255); + opacity = quint8(value * 255); + const bool opaque = (opacity == 255); + + if (opaque == wasOpaque) + return; + + if (opaque) + blitFlags &= ~(DSBLIT_BLEND_COLORALPHA | DSBLIT_SRC_PREMULTCOLOR); + else + blitFlags |= (DSBLIT_BLEND_COLORALPHA | DSBLIT_SRC_PREMULTCOLOR); + + dirtyFlags = true; +} + +void QDirectFBPaintEnginePrivate::setRenderHints(QPainter::RenderHints hints) +{ + const bool old = antialiased; + antialiased = bool(hints & QPainter::Antialiasing); + if (old != antialiased) { + setPen(q->state()->pen); + } +} + +void QDirectFBPaintEnginePrivate::updateFlags() +{ + if (!dirtyFlags) + return; + surface->SetDrawingFlags(surface, DFBSurfaceDrawingFlags(drawFlags)); + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(blitFlags)); + surface->SetPorterDuff(surface, DFBSurfacePorterDuffRule(duffFlags)); + dirtyFlags = false; +} + +void QDirectFBPaintEnginePrivate::setDFBColor(const QColor &color) const +{ + const quint8 alpha = (opacity == 255 ? + color.alpha() : ALPHA_MUL(color.alpha(), opacity)); + surface->SetColor(surface, + color.red(), color.green(), color.blue(), alpha); +} + +void QDirectFBPaintEnginePrivate::drawLines(const QLine *lines, int n) const +{ + QVarLengthArray<DFBRegion> regions(n); + + for (int i = 0; i < n; ++i) { + const QLine l = transform.map(lines[i]); + + // TODO: clip! + + regions[i].x1 = l.x1(); + regions[i].y1 = l.y1(); + regions[i].x2 = l.x2(); + regions[i].y2 = l.y2(); + } + surface->DrawLines(surface, regions.data(), n); +} + +void QDirectFBPaintEnginePrivate::drawLines(const QLineF *lines, int n) const +{ + QVarLengthArray<DFBRegion> regions(n); + + for (int i = 0; i < n; ++i) { + const QLine l = transform.map(lines[i]).toLine(); + + // TODO: clip! + + regions[i].x1 = l.x1(); + regions[i].y1 = l.y1(); + regions[i].x2 = l.x2(); + regions[i].y2 = l.y2(); + } + surface->DrawLines(surface, regions.data(), n); +} + +/* ### Commented out until it can be implemented properly using raster's QClipData +QRegion QDirectFBPaintEnginePrivate::rectsToClippedRegion(const QRect *rects, + int n) const +{ + QRegion region; + + for (int i = 0; i < n; ++i) { + const QRect r = ::mapRect(transform, rects[i]); + region += clip & r; + } + + return region; +} + +QRegion QDirectFBPaintEnginePrivate::rectsToClippedRegion(const QRectF *rects, + int n) const +{ + QRegion region; + + for (int i = 0; i < n; ++i) { + const QRect r = ::mapRect(transform, rects[i]); + region += clip & r; + } + + return region; +} +*/ + +void QDirectFBPaintEnginePrivate::fillRegion(const QRegion ®ion) const +{ + const QVector<QRect> rects = region.rects(); + const int n = rects.size(); + QVarLengthArray<DFBRectangle> dfbRects(n); + + for (int i = 0; i < n; ++i) { + const QRect r = rects.at(i); + dfbRects[i].x = r.x(); + dfbRects[i].y = r.y(); + dfbRects[i].w = r.width(); + dfbRects[i].h = r.height(); + + } + surface->FillRectangles(surface, dfbRects.data(), n); +} + +void QDirectFBPaintEnginePrivate::fillRects(const QRect *rects, int n) const +{ + QVarLengthArray<DFBRectangle> dfbRects(n); + for (int i = 0; i < n; ++i) { + const QRect r = ::mapRect(transform, rects[i]); + dfbRects[i].x = r.x(); + dfbRects[i].y = r.y(); + dfbRects[i].w = r.width(); + dfbRects[i].h = r.height(); + } + surface->FillRectangles(surface, dfbRects.data(), n); +} + +void QDirectFBPaintEnginePrivate::fillRects(const QRectF *rects, int n) const +{ + QVarLengthArray<DFBRectangle> dfbRects(n); + for (int i = 0; i < n; ++i) { + const QRect r = ::mapRect(transform, rects[i]); + dfbRects[i].x = r.x(); + dfbRects[i].y = r.y(); + dfbRects[i].w = r.width(); + dfbRects[i].h = r.height(); + } + surface->FillRectangles(surface, dfbRects.data(), n); +} + +void QDirectFBPaintEnginePrivate::drawRects(const QRect *rects, int n) const +{ + for (int i = 0; i < n; ++i) { + const QRect r = ::mapRect(transform, rects[i]); + surface->DrawRectangle(surface, r.x(), r.y(), + r.width() + 1, r.height() + 1); + } +} + +void QDirectFBPaintEnginePrivate::drawRects(const QRectF *rects, int n) const +{ + for (int i = 0; i < n; ++i) { + const QRect r = ::mapRect(transform, rects[i]); + surface->DrawRectangle(surface, r.x(), r.y(), + r.width() + 1, r.height() + 1); + } +} + +void QDirectFBPaintEnginePrivate::drawPixmap(const QRectF &dest, + const QPixmap &pixmap, + const QRectF &src) +{ + surface->SetColor(surface, 0xff, 0xff, 0xff, opacity); + + const bool changeFlags = !pixmap.hasAlphaChannel() + && (blitFlags & DSBLIT_BLEND_ALPHACHANNEL); + if (changeFlags) { + quint32 flags = blitFlags & ~DSBLIT_BLEND_ALPHACHANNEL; + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(flags)); + } + + QPixmapData *data = pixmap.pixmapData(); + Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); + QDirectFBPixmapData *dfbData = static_cast<QDirectFBPixmapData*>(data); + IDirectFBSurface *s = dfbData->directFbSurface(); + const QRect sr = src.toRect(); + const QRect dr = ::mapRect(transform, dest); + 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 { + 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); + if (changeFlags) + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(blitFlags)); +} + +void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, + const QPixmap &pixmap) +{ + surface->SetColor(surface, 0xff, 0xff, 0xff, opacity); + + const bool changeFlags = !pixmap.hasAlphaChannel() + && (blitFlags & DSBLIT_BLEND_ALPHACHANNEL); + if (changeFlags) { + quint32 flags = blitFlags & ~DSBLIT_BLEND_ALPHACHANNEL; + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(flags)); + } + + QPixmapData *data = pixmap.pixmapData(); + Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); + QDirectFBPixmapData *dfbData = static_cast<QDirectFBPixmapData*>(data); + IDirectFBSurface *s = dfbData->directFbSurface(); + const QRect dr = ::mapRect(transform, dest); + DFBResult result = DFB_OK; + + if (!matrixScale && dr == QRect(0, 0, fbWidth, fbHeight)) { + result = surface->TileBlit(surface, s, 0, 0, 0); + } else if (!matrixScale) { + const int dx = pixmap.width(); + const int dy = pixmap.height(); + const DFBRectangle rect = { 0, 0, dx, dy }; + QVarLengthArray<DFBRectangle> rects; + QVarLengthArray<DFBPoint> points; + + for (int y = dr.y(); y <= dr.bottom(); y += dy) { + for (int x = dr.x(); x <= dr.right(); x += dx) { + rects.append(rect); + const DFBPoint point = { x, y }; + points.append(point); + } + } + result = surface->BatchBlit(surface, s, rects.constData(), + points.constData(), points.size()); + } else { + const QRect sr = ::mapRect(transform, QRect(0, 0, pixmap.width(), pixmap.height())); + const int dx = sr.width(); + const int dy = sr.height(); + const DFBRectangle sRect = { 0, 0, dx, dy }; + + for (int y = dr.y(); y <= dr.bottom(); y += dy) { + for (int x = dr.x(); x <= dr.right(); x += dx) { + const DFBRectangle dRect = { x, y, dx, dy }; + result = surface->StretchBlit(surface, s, &sRect, &dRect); + if (result != DFB_OK) { + y = dr.bottom() + 1; + break; + } + } + } + } + + if (result != DFB_OK) + DirectFBError("QDirectFBPaintEngine::drawTiledPixmap()", result); + + if (changeFlags) + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(blitFlags)); +} + +void QDirectFBPaintEnginePrivate::drawImage(const QRectF &dest, + const QImage &srcImage, + const QRectF &src) +{ + QImage image = srcImage; + if (QDirectFBScreen::getSurfacePixelFormat(image) == DSPF_UNKNOWN) { + QImage::Format format; + if (image.hasAlphaChannel()) + format = QImage::Format_ARGB32_Premultiplied; + else + format = QImage::Format_RGB32; + image = image.convertToFormat(format); + } + + CachedImage *img = imageCache[image.cacheKey()]; + IDirectFBSurface *imgSurface = 0; + bool doRelease = false; + + if (img) { + imgSurface = img->surface(); + } else { + const int cost = image.width() * image.height() * image.depth() / 8; + if (cost <= imageCache.maxCost()) { + img = new CachedImage(image); + imgSurface = img->surface(); + if (imgSurface) { + imageCache.insert(image.cacheKey(), img, cost); + } else { + delete img; + img = 0; + } + } + + if (!imgSurface) { + DFBSurfaceDescription description; + + description = QDirectFBScreen::getSurfaceDescription(image); + imgSurface = QDirectFBScreen::instance()->createDFBSurface(&description); + if (!imgSurface) { + qWarning("QDirectFBPaintEnginePrivate::drawImage"); + return; + } + +#ifndef QT_NO_DIRECTFB_PALETTE + QDirectFBScreen::setSurfaceColorTable(surface, image); +#endif + doRelease = (imgSurface != 0); + } + } + + const QRect sr = src.toRect(); + const QRect dr = ::mapRect(transform, dest); + const DFBRectangle sRect = { sr.x(), sr.y(), sr.width(), sr.height() }; + + surface->SetColor(surface, 0xff, 0xff, 0xff, opacity); + + const bool changeFlags = !image.hasAlphaChannel() + && (blitFlags & DSBLIT_BLEND_ALPHACHANNEL); + if (changeFlags) { + quint32 flags = blitFlags & ~DSBLIT_BLEND_ALPHACHANNEL; + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(flags)); + } + if (dr.size() == sr.size()) { + surface->Blit(surface, imgSurface, &sRect, dr.x(), dr.y()); + } else { + const DFBRectangle dRect = { dr.x(), dr.y(), + dr.width(), dr.height() }; + surface->StretchBlit(surface, imgSurface, &sRect, &dRect); + } + if (changeFlags) + surface->SetBlittingFlags(surface, DFBSurfaceBlittingFlags(blitFlags)); + if (doRelease) + QDirectFBScreen::instance()->releaseDFBSurface(imgSurface); +} + +void QDirectFBPaintEnginePrivate::updateClip() +{ + if (!dirtyClip) + return; + + if (!clip() || !clip()->enabled) { + surface->SetClip(surface, NULL); + dfbHandledClip = true; + } + else if (clip()->hasRectClip) { + const DFBRegion r = { + clip()->clipRect.x(), + clip()->clipRect.y(), + clip()->clipRect.x() + clip()->clipRect.width(), + clip()->clipRect.y() + clip()->clipRect.height() + }; + surface->SetClip(surface, &r); + + dfbHandledClip = true; + } + else + dfbHandledClip = false; + + dirtyClip = false; +} + +void QDirectFBPaintEnginePrivate::systemStateChanged() +{ + setClipDirty(); + QRasterPaintEnginePrivate::systemStateChanged(); +} + +QDirectFBPaintEngine::QDirectFBPaintEngine(QPaintDevice *device) + : QRasterPaintEngine(*(new QDirectFBPaintEnginePrivate(this)), device) +{ +} + +QDirectFBPaintEngine::~QDirectFBPaintEngine() +{ +} + +bool QDirectFBPaintEngine::begin(QPaintDevice *device) +{ + Q_D(QDirectFBPaintEngine); + d->begin(device); + const bool status = QRasterPaintEngine::begin(device); + + // XXX: QRasterPaintEngine::begin() resets the capabilities + gccaps |= PorterDuff; + + return status; +} + +bool QDirectFBPaintEngine::end() +{ + Q_D(QDirectFBPaintEngine); + d->end(); + return QRasterPaintEngine::end(); +} + + + +void QDirectFBPaintEngine::clipEnabledChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setClipDirty(); + QRasterPaintEngine::clipEnabledChanged(); +} + +void QDirectFBPaintEngine::penChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setPen(state()->pen); + + QRasterPaintEngine::penChanged(); +} + +void QDirectFBPaintEngine::brushChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setBrush(state()->brush); + + QRasterPaintEngine::brushChanged(); +} + +void QDirectFBPaintEngine::opacityChanged() +{ + Q_D(QDirectFBPaintEngine); + d->setOpacity(state()->opacity); + + 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); + const bool old = d->matrixScale; + d->setTransform(state()->transform()); + if (d->matrixScale != old) { + d->setPen(state()->pen); + } + QRasterPaintEngine::transformChanged(); +} + +void QDirectFBPaintEngine::setState(QPainterState *s) +{ + Q_D(QDirectFBPaintEngine); + QRasterPaintEngine::setState(s); + if (d->surface) + d->updateClip(); + d->setPen(state()->pen); + d->setBrush(state()->brush); + d->setOpacity(state()->opacity); + d->setCompositionMode(state()->compositionMode()); + d->setTransform(state()->transform()); +} + +void QDirectFBPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ + Q_D(QDirectFBPaintEngine); + d->setClipDirty(); + QRasterPaintEngine::clip(path, op); +} + +void QDirectFBPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) +{ + Q_D(QDirectFBPaintEngine); + d->setClipDirty(); + QRasterPaintEngine::clip(rect, op); +} + + +void QDirectFBPaintEngine::drawRects(const QRect *rects, int rectCount) +{ + Q_D(QDirectFBPaintEngine); + d->updateClip(); + if (!d->dfbCanHandleClip() || d->matrixRotShear || !d->simpleBrush || !d->simplePen) { + d->lock(); + QRasterPaintEngine::drawRects(rects, rectCount); + return; + } + + d->unlock(); + + if (d->brush != Qt::NoBrush) { + d->updateFlags(); + d->setDFBColor(d->brush.color()); + d->fillRects(rects, rectCount); + } + if (d->pen != Qt::NoPen) { + d->updateFlags(); + d->setDFBColor(d->pen.color()); + d->drawRects(rects, rectCount); + } +} + +void QDirectFBPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + Q_D(QDirectFBPaintEngine); + d->updateClip(); + if (!d->dfbCanHandleClip() || d->matrixRotShear || !d->simpleBrush || !d->simplePen) { + d->lock(); + QRasterPaintEngine::drawRects(rects, rectCount); + return; + } + + d->unlock(); + + if (d->brush != Qt::NoBrush) { + d->updateFlags(); + d->setDFBColor(d->brush.color()); + d->fillRects(rects, rectCount); + } + if (d->pen != Qt::NoPen) { + d->updateFlags(); + d->setDFBColor(d->pen.color()); + d->drawRects(rects, rectCount); + } +} + +void QDirectFBPaintEngine::drawLines(const QLine *lines, int lineCount) +{ + Q_D(QDirectFBPaintEngine); + d->updateClip(); + if (!d->simplePen || !d->dfbCanHandleClip()) { + d->lock(); + QRasterPaintEngine::drawLines(lines, lineCount); + return; + } + + if (d->pen != Qt::NoPen) { + d->unlock(); + d->updateFlags(); + d->setDFBColor(d->pen.color()); + d->drawLines(lines, lineCount); + } +} + +void QDirectFBPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ + Q_D(QDirectFBPaintEngine); + d->updateClip(); + if (!d->simplePen || !d->dfbCanHandleClip()) { + d->lock(); + QRasterPaintEngine::drawLines(lines, lineCount); + return; + } + + if (d->pen != Qt::NoPen) { + d->unlock(); + d->updateFlags(); + d->setDFBColor(d->pen.color()); + d->drawLines(lines, lineCount); + } +} + +void QDirectFBPaintEngine::drawImage(const QRectF &r, const QImage &image, + const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + Q_D(QDirectFBPaintEngine); + Q_UNUSED(flags); // XXX + +#ifndef QT_NO_DIRECTFB_PREALLOCATED + d->updateClip(); + if (!d->dfbCanHandleClip(r) || d->matrixRotShear) +#endif + { + d->lock(); + QRasterPaintEngine::drawImage(r, image, sr, flags); + return; + } + +#ifndef QT_NO_DIRECTFB_PREALLOCATED + d->unlock(); + d->updateFlags(); + d->drawImage(r, image, sr); +#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); + d->updateClip(); + + if (pixmap.pixmapData()->classId() != QPixmapData::DirectFBClass) { + d->lock(); + QRasterPaintEngine::drawPixmap(r, pixmap, sr); + } + else if (!d->dfbCanHandleClip(r) || d->matrixRotShear) { + const QImage *img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(); + d->lock(); + QRasterPaintEngine::drawImage(r, *img, sr); + } + else { + d->unlock(); + d->updateFlags(); + d->drawPixmap(r, pixmap, 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 &sp) +{ + Q_D(QDirectFBPaintEngine); + d->updateClip(); + if (pixmap.pixmapData()->classId() != QPixmapData::DirectFBClass) { + d->lock(); + QRasterPaintEngine::drawTiledPixmap(r, pixmap, sp); + } + else if (!d->dfbCanHandleClip(r) || d->matrixRotShear || !sp.isNull()) { + QImage* img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(); + QRasterPixmapData *data = new QRasterPixmapData(QPixmapData::PixmapType); + data->fromImage(*img, Qt::AutoColor); + const QPixmap pix(data); + d->lock(); + QRasterPaintEngine::drawTiledPixmap(r, pix, sp); + } + else { + d->unlock(); + d->updateFlags(); + d->drawTiledPixmap(r, pixmap); + } +} + + +void QDirectFBPaintEngine::stroke(const QVectorPath &path, const QPen &pen) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::stroke(path, pen); +} + +void QDirectFBPaintEngine::drawPath(const QPainterPath &path) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPath(path); +} + +void QDirectFBPaintEngine::drawPoints(const QPointF *points, int pointCount) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPoints(points, pointCount); +} + +void QDirectFBPaintEngine::drawPoints(const QPoint *points, int pointCount) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPoints(points, pointCount); +} + +void QDirectFBPaintEngine::drawEllipse(const QRectF &rect) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawEllipse(rect); +} + +void QDirectFBPaintEngine::drawPolygon(const QPointF *points, int pointCount, + PolygonDrawMode mode) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPolygon(points, pointCount, mode); +} + +void QDirectFBPaintEngine::drawPolygon(const QPoint *points, int pointCount, + PolygonDrawMode mode) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawPolygon(points, pointCount, mode); +} + +void QDirectFBPaintEngine::drawTextItem(const QPointF &p, + const QTextItem &textItem) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::drawTextItem(p, textItem); +} + +void QDirectFBPaintEngine::fill(const QVectorPath &path, const QBrush &brush) +{ + Q_D(QDirectFBPaintEngine); + d->lock(); + QRasterPaintEngine::fill(path, brush); +} + +void QDirectFBPaintEngine::fillRect(const QRectF &r, const QBrush &brush) +{ + Q_D(QDirectFBPaintEngine); + if (brush.style() != Qt::SolidPattern) { + d->lock(); + QRasterPaintEngine::fillRect(r, brush); + } + else + fillRect(r, brush.color()); +} + +void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QColor &color) +{ + Q_D(QDirectFBPaintEngine); + d->updateClip(); + if (!d->dfbCanHandleClip() || d->matrixRotShear) { + d->lock(); + QRasterPaintEngine::fillRect(rect, color); + } else { + d->unlock(); + d->updateFlags(); + d->setDFBColor(color); + d->fillRects(&rect, 1); + } +} + + + + + + + +void QDirectFBPaintEngine::drawColorSpans(const QSpan *spans, int count, + uint color) +{ + Q_D(QDirectFBPaintEngine); + color = INV_PREMUL(color); + + QVarLengthArray<DFBRegion> lines(count); + int j = 0; + for (int i = 0; i < count; ++i) { + if (spans[i].coverage == 255) { + lines[j].x1 = spans[i].x; + lines[j].y1 = spans[i].y; + lines[j].x2 = spans[i].x + spans[i].len - 1; + lines[j].y2 = spans[i].y; + ++j; + } else { + DFBSpan span = { spans[i].x, spans[i].len }; + uint c = BYTE_MUL(color, spans[i].coverage); + d->surface->SetColor(d->surface, + qRed(c), qGreen(c), qBlue(c), qAlpha(c)); + d->surface->FillSpans(d->surface, spans[i].y, &span, 1); + } + } + if (j > 0) { + d->surface->SetColor(d->surface, + qRed(color), qGreen(color), qBlue(color), + qAlpha(color)); + d->surface->DrawLines(d->surface, lines.data(), j); + } +} + +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); + src->SetColor(src, 0, 0, 0, const_alpha); + const DFBRectangle rect = { 0, 0, length, 1 }; + d->surface->Blit(d->surface, src, &rect, x, y); +} + +#endif // QT_NO_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h new file mode 100644 index 0000000..3c2cefa --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAINTENGINE_DIRECTFB_P_H +#define QPAINTENGINE_DIRECTFB_P_H + +#include <QtGui/qpaintengine.h> +#include <private/qpaintengine_raster_p.h> + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +class QDirectFBPaintEnginePrivate; + +class QDirectFBPaintEngine : public QRasterPaintEngine +{ + Q_DECLARE_PRIVATE(QDirectFBPaintEngine) +public: + QDirectFBPaintEngine(QPaintDevice *device); + ~QDirectFBPaintEngine(); + + bool begin(QPaintDevice *device); + bool end(); + + void drawRects(const QRect *rects, int rectCount); + void drawRects(const QRectF *rects, int rectCount); + + void fillRect(const QRectF &r, const QBrush &brush); + void fillRect(const QRectF &r, const QColor &color); + + void drawLines(const QLine *line, int lineCount); + void drawLines(const QLineF *line, int lineCount); + + void drawImage(const QPointF &p, const QImage &img); + void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags falgs = Qt::AutoColor); + + void drawPixmap(const QPointF &p, const QPixmap &pm); + void drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr); + void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr); + + void drawColorSpans(const QSpan *spans, int count, uint color); + void drawBufferSpan(const uint *buffer, int bufsize, + int x, int y, int length, uint const_alpha); + + + // The following methods simply lock the surface & call the base implementation + void stroke(const QVectorPath &path, const QPen &pen); + void drawPath(const QPainterPath &path); + void drawPoints(const QPointF *points, int pointCount); + void drawPoints(const QPoint *points, int pointCount); + void drawEllipse(const QRectF &rect); + void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode); + void drawTextItem(const QPointF &p, const QTextItem &textItem); + void fill(const QVectorPath &path, const QBrush &brush); + + virtual void clipEnabledChanged(); + virtual void penChanged(); + virtual void brushChanged(); + 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 QRect &rect, Qt::ClipOperation op); + +}; + +QT_END_HEADER + +#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 0000000..6352652 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbpixmap.h" + +#include "qdirectfbscreen.h" +#include "qdirectfbpaintengine.h" + +#include <QtGui/qbitmap.h> +#include <directfb.h> + +static int global_ser_no = 0; + +QDirectFBPixmapData::QDirectFBPixmapData(PixelType pixelType) + : QPixmapData(pixelType, DirectFBClass), + engine(0) +{ + setSerialNumber(0); +} + +QDirectFBPixmapData::~QDirectFBPixmapData() +{ + unlockDirectFB(); + if (dfbSurface && QDirectFBScreen::instance()) + screen->releaseDFBSurface(dfbSurface); + delete engine; +} + +void QDirectFBPixmapData::resize(int width, int height) +{ + if (width <= 0 || height <= 0) { + setSerialNumber(0); + return; + } + + DFBSurfaceDescription description; + description.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | + DSDESC_HEIGHT); + description.width = width; + description.height = height; + + dfbSurface = screen->createDFBSurface(&description); + if (!dfbSurface) + qCritical("QDirectFBPixmapData::resize(): Unable to allocate surface"); + + setSerialNumber(++global_ser_no); +} + +void QDirectFBPixmapData::fromImage(const QImage &img, + Qt::ImageConversionFlags) +{ + QImage image; + if (QDirectFBScreen::getSurfacePixelFormat(img) == DSPF_UNKNOWN) + image = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); + else + image = img; + + DFBSurfaceDescription description; + description = QDirectFBScreen::getSurfaceDescription(image); + +#ifndef QT_NO_DIRECTFB_PREALLOCATED + IDirectFBSurface *imgSurface; + imgSurface = screen->createDFBSurface(&description); + if (!imgSurface) { + qWarning("QDirectFBPixmapData::fromImage()"); + setSerialNumber(0); + return; + } +#ifndef QT_NO_DIRECTFB_PALETTE + QDirectFBScreen::setSurfaceColorTable(imgSurface, image); +#endif +#endif // QT_NO_DIRECTFB_PREALLOCATED + + description.flags = DFBSurfaceDescriptionFlags(description.flags + & ~DSDESC_PREALLOCATED); + dfbSurface = screen->createDFBSurface(&description); + if (!dfbSurface) { + qWarning("QDirectFBPixmapData::fromImage()"); + setSerialNumber(0); + return; + } + +#ifndef QT_NO_DIRECTFB_PALETTE + QDirectFBScreen::setSurfaceColorTable(dfbSurface, image); +#endif + +#ifdef QT_NO_DIRECTFB_PREALLOCATED + char *mem; + surface->Lock(surface, DSLF_WRITE, (void**)&mem, &bpl); + const int w = image.width() * image.depth() / 8; + for (int i = 0; i < image.height(); ++i) { + memcpy(mem, image.scanLine(i), w); + mem += bpl; + } + surface->Unlock(surface); +#else + DFBResult result; + dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); + result = dfbSurface->Blit(dfbSurface, imgSurface, 0, 0, 0); + if (result != DFB_OK) + DirectFBError("QDirectFBPixmapData::fromImage()", result); + dfbSurface->Flip(dfbSurface, 0, DSFLIP_NONE); + dfbSurface->ReleaseSource(dfbSurface); + screen->releaseDFBSurface(imgSurface); +#endif // QT_NO_DIRECTFB_PREALLOCATED + + setSerialNumber(++global_ser_no); +} + +void QDirectFBPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + if (data->classId() != DirectFBClass) { + QPixmapData::copy(data, rect); + return; + } + + IDirectFBSurface *src = static_cast<const QDirectFBPixmapData*>(data)->directFbSurface(); + + DFBSurfaceDescription description; + description.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | + DSDESC_HEIGHT | + DSDESC_PIXELFORMAT); + description.width = rect.width(); + description.height = rect.height(); + src->GetPixelFormat(src, &description.pixelformat); + + dfbSurface = screen->createDFBSurface(&description); + if (!dfbSurface) { + qWarning("QDirectFBPixmapData::copy()"); + setSerialNumber(0); + return; + } + + DFBResult result; +#ifndef QT_NO_DIRECTFB_PALETTE + IDirectFBPalette *palette; + result = src->GetPalette(src, &palette); + if (result == DFB_OK) { + dfbSurface->SetPalette(dfbSurface, palette); + palette->Release(palette); + } +#endif + + dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); + const DFBRectangle blitRect = { rect.x(), rect.y(), + rect.width(), rect.height() }; + result = dfbSurface->Blit(dfbSurface, src, &blitRect, 0, 0); + if (result != DFB_OK) + DirectFBError("QDirectFBPixmapData::copy()", result); + + setSerialNumber(++global_ser_no); +} + + +void QDirectFBPixmapData::fill(const QColor &color) +{ + if (!serialNumber()) + return; + + Q_ASSERT(dfbSurface); + + if (color.alpha() < 255 && !hasAlphaChannel()) { + // convert to surface supporting alpha channel + DFBSurfacePixelFormat format; + dfbSurface->GetPixelFormat(dfbSurface, &format); + switch (format) { + case DSPF_YUY2: + case DSPF_UYVY: + format = DSPF_AYUV; + break; +#if (Q_DIRECTFB_VERSION >= 0x010100) + case DSPF_RGB444: + format = DSPF_ARGB4444; + break; + case DSPF_RGB555: +#endif + case DSPF_RGB18: + format = DSPF_ARGB6666; + break; + default: + format = DSPF_ARGB; + break; + } + + DFBSurfaceDescription description; + description.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | + DSDESC_HEIGHT | + DSDESC_PIXELFORMAT); + dfbSurface->GetSize(dfbSurface, &description.width, &description.height); + description.pixelformat = format; + screen->releaseDFBSurface(dfbSurface); // release old surface + + dfbSurface = screen->createDFBSurface(&description); + if (!dfbSurface) { + qWarning("QDirectFBPixmapData::fill()"); + setSerialNumber(0); + return; + } + } + + dfbSurface->Clear(dfbSurface, color.red(), color.green(), color.blue(), + color.alpha()); +} + +bool QDirectFBPixmapData::hasAlphaChannel() const +{ + if (!serialNumber()) + return false; + + DFBSurfacePixelFormat format; + dfbSurface->GetPixelFormat(dfbSurface, &format); + switch (format) { + case DSPF_ARGB1555: + case DSPF_ARGB: + case DSPF_LUT8: + case DSPF_AiRGB: + case DSPF_A1: + case DSPF_ARGB2554: + case DSPF_ARGB4444: + case DSPF_AYUV: + case DSPF_A4: + case DSPF_ARGB1666: + case DSPF_ARGB6666: + case DSPF_LUT2: + return true; + default: + return false; + } +} + +QPixmap QDirectFBPixmapData::transformed(const QTransform &transform, + Qt::TransformationMode mode) const +{ + if (!dfbSurface || transform.type() != QTransform::TxScale + || mode != Qt::FastTransformation) + { + QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this); + const QImage *image = that->buffer(); + if (image) { // avoid deep copy + const QImage transformed = image->transformed(transform, mode); + that->unlockDirectFB(); + QDirectFBPixmapData *data = new QDirectFBPixmapData(pixelType()); + data->fromImage(transformed, Qt::AutoColor); + return QPixmap(data); + } + return QPixmapData::transformed(transform, mode); + } + + int w, h; + dfbSurface->GetSize(dfbSurface, &w, &h); + + const QSize size = transform.mapRect(QRect(0, 0, w, h)).size(); + if (size.isEmpty()) + return QPixmap(); + + QDirectFBPixmapData *data = new QDirectFBPixmapData(pixelType()); + data->resize(size.width(), size.height()); + + IDirectFBSurface *dest = data->dfbSurface; + dest->SetBlittingFlags(dest, DSBLIT_NOFX); + + const DFBRectangle srcRect = { 0, 0, w, h }; + const DFBRectangle destRect = { 0, 0, size.width(), size.height() }; + dest->StretchBlit(dest, dfbSurface, &srcRect, &destRect); + + return QPixmap(data); +} + +QImage QDirectFBPixmapData::toImage() const +{ + if (!dfbSurface) + return QImage(); + +#ifdef QT_NO_DIRECTFB_PREALLOCATED + QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this); + const QImage *img = that->buffer(); + const QImage copied = img->copy(); + that->unlockDirectFB(); + return copied; +#else + + int w, h; + dfbSurface->GetSize(dfbSurface, &w, &h); + + // Always convert to ARGB32: + QImage image(w, h, QImage::Format_ARGB32); + + DFBSurfaceDescription description; + description = QDirectFBScreen::getSurfaceDescription(image); + + IDirectFBSurface *imgSurface = screen->createDFBSurface(&description); + if (!imgSurface) { + qWarning("QDirectFBPixmapData::toImage()"); + return QImage(); + } + + imgSurface->SetBlittingFlags(imgSurface, DSBLIT_NOFX); + DFBResult result = imgSurface->Blit(imgSurface, dfbSurface, 0, 0, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBPixmapData::toImage() blit failed", result); + return QImage(); + } + screen->releaseDFBSurface(imgSurface); + + return image; +#endif // QT_NO_DIRECTFB_PREALLOCATED +} + +QPaintEngine* QDirectFBPixmapData::paintEngine() const +{ + if (!engine) { + // QDirectFBPixmapData is also a QCustomRasterPaintDevice, so pass + // that to the paint engine: + QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this); + that->engine = new QDirectFBPaintEngine(that); + } + return engine; +} + + +QImage* QDirectFBPixmapData::buffer() +{ + lockDirectFB(); + return lockedImage; +} diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h new file mode 100644 index 0000000..32676f8 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTFBPIXMAP_H +#define QDIRECTFBPIXMAP_H + +#include <QtGui/private/qpixmapdata_p.h> +#include <QtGui/private/qpaintengine_raster_p.h> +#include "qdirectfbpaintdevice.h" +#include <directfb.h> + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +class QDirectFBPaintEngine; + +class QDirectFBPixmapData : public QPixmapData, public QDirectFBPaintDevice +{ +public: + QDirectFBPixmapData(PixelType pixelType); + ~QDirectFBPixmapData(); + + // Re-implemented from QPixmapData: + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void copy(const QPixmapData *data, const QRect &rect); + void fill(const QColor &color); + bool hasAlphaChannel() const; + QPixmap transformed(const QTransform &matrix, + Qt::TransformationMode mode) const; + QImage toImage() const; + QPaintEngine* paintEngine() const; + QImage *buffer(); + + // Pure virtual in QPixmapData, so re-implement here and delegate to QDirectFBPaintDevice + int metric(QPaintDevice::PaintDeviceMetric m) const {return QDirectFBPaintDevice::metric(m);} + +private: + QDirectFBPaintEngine *engine; +}; + +QT_END_HEADER + +#endif // QDIRECTFBPIXMAP_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp new file mode 100644 index 0000000..3249e65 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp @@ -0,0 +1,1064 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbscreen.h" +#include "qdirectfbsurface.h" +#include "qdirectfbpixmap.h" +#include "qdirectfbmouse.h" +#include "qdirectfbkeyboard.h" +#include <QtGui/qwsdisplay_qws.h> +#include <QtGui/qcolor.h> +#include <QtGui/qapplication.h> +#include <QtGui/qwindowsystem_qws.h> +#include <QtGui/private/qgraphicssystem_qws_p.h> +#include <QtGui/private/qwssignalhandler_p.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qvector.h> +#include <QtCore/qrect.h> + +class QDirectFBScreenPrivate : public QObject, public QWSGraphicsSystem +{ +public: + QDirectFBScreenPrivate(QDirectFBScreen*); + ~QDirectFBScreenPrivate(); + + void setFlipFlags(const QStringList &args); + QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + + IDirectFB *dfb; + IDirectFBSurface *dfbSurface; + DFBSurfaceFlipFlags flipFlags; +#ifndef QT_NO_DIRECTFB_LAYER + IDirectFBDisplayLayer *dfbLayer; +#endif + IDirectFBScreen *dfbScreen; + QRegion prevExpose; + + QSet<IDirectFBSurface*> allocatedSurfaces; + +#ifndef QT_NO_DIRECTFB_MOUSE + QDirectFBMouseHandler *mouse; +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + QDirectFBKeyboardHandler *keyboard; +#endif + bool videoonly; +}; + +QDirectFBScreenPrivate::QDirectFBScreenPrivate(QDirectFBScreen* screen) + : QWSGraphicsSystem(screen), dfb(0), dfbSurface(0), flipFlags(DSFLIP_BLIT) +#ifndef QT_NO_DIRECTFB_LAYER + , dfbLayer(0) +#endif +#ifndef QT_NO_DIRECTFB_MOUSE + , mouse(0) +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + , keyboard(0) +#endif + , videoonly(false) +{ +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addObject(this); +#endif +} + +QDirectFBScreenPrivate::~QDirectFBScreenPrivate() +{ +#ifndef QT_NO_DIRECTFB_MOUSE + delete mouse; +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + delete keyboard; +#endif + + foreach (IDirectFBSurface* surf, allocatedSurfaces) + surf->Release(surf); + allocatedSurfaces.clear(); + + if (dfbSurface) + dfbSurface->Release(dfbSurface); + +#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 DFBSurfaceDescription* desc, bool track) +{ + DFBResult result; + IDirectFBSurface* newSurface = 0; + + if (!d_ptr->dfb) { + qWarning("QDirectFBScreen::createDFBSurface() - not connected"); + return 0; + } + + if (d_ptr->videoonly && !(desc->flags & DSDESC_PREALLOCATED)) { + // Add the video only capability. This means the surface will be created in video ram + DFBSurfaceDescription voDesc = *desc; + voDesc.caps = DFBSurfaceCapabilities(voDesc.caps | DSCAPS_VIDEOONLY); + voDesc.flags = DFBSurfaceDescriptionFlags(voDesc.flags | DSDESC_CAPS); + result = d_ptr->dfb->CreateSurface(d_ptr->dfb, &voDesc, &newSurface); + } + + if (!newSurface) + result = d_ptr->dfb->CreateSurface(d_ptr->dfb, desc, &newSurface); + + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::createDFBSurface", result); + return 0; + } + + Q_ASSERT(newSurface); + + if (track) { + d_ptr->allocatedSurfaces.insert(newSurface); + + //qDebug("Created a new DirectFB surface at %p. New count = %d", + // newSurface, d_ptr->allocatedSurfaces.count()); + } + + return newSurface; +} + +void QDirectFBScreen::releaseDFBSurface(IDirectFBSurface* surface) +{ + 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()); +} + +bool QDirectFBScreen::preferVideoOnly() const +{ + return d_ptr->videoonly; +} + +IDirectFB* QDirectFBScreen::dfb() +{ + return d_ptr->dfb; +} + +IDirectFBSurface* QDirectFBScreen::dfbSurface() +{ + return d_ptr->dfbSurface; +} + +#ifndef QT_NO_DIRECTFB_LAYER +IDirectFBDisplayLayer* QDirectFBScreen::dfbDisplayLayer() +{ + return d_ptr->dfbLayer; +} +#endif + +DFBSurfacePixelFormat QDirectFBScreen::getSurfacePixelFormat(const QImage &image) +{ + switch (image.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; + case QImage::Format_ARGB6666_Premultiplied: + return DSPF_ARGB6666; + case QImage::Format_RGB666: + return DSPF_RGB18; + 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(DFBSurfacePixelFormat 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; + case DSPF_ARGB6666: + return QImage::Format_ARGB6666_Premultiplied; + case DSPF_RGB18: + return QImage::Format_RGB666; + case DSPF_RGB32: + return QImage::Format_RGB32; + case DSPF_ARGB: + return QImage::Format_ARGB32_Premultiplied; + default: + break; + } + return QImage::Format_Invalid; +} + +DFBSurfaceDescription QDirectFBScreen::getSurfaceDescription(const QImage &image) +{ + DFBSurfaceDescription description; + DFBSurfacePixelFormat format = getSurfacePixelFormat(image); + + if (format == DSPF_UNKNOWN || image.isNull()) { + description.flags = DFBSurfaceDescriptionFlags(0); + return description; + } + + description.flags = DFBSurfaceDescriptionFlags(DSDESC_CAPS + | DSDESC_WIDTH + | DSDESC_HEIGHT + | DSDESC_PIXELFORMAT + | DSDESC_PREALLOCATED); + + description.caps = DSCAPS_NONE; + description.width = image.width(); + description.height = image.height(); + description.pixelformat = format; + description.preallocated[0].data = (void*)(image.bits()); + description.preallocated[0].pitch = image.bytesPerLine(); + description.preallocated[1].data = 0; + description.preallocated[1].pitch = 0; + + switch (image.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: + description.caps = DSCAPS_PREMULTIPLIED; + default: + break; + } + + return description; +} + +DFBSurfaceDescription QDirectFBScreen::getSurfaceDescription(const uint *buffer, + int length) +{ + DFBSurfaceDescription description; + + description.flags = DFBSurfaceDescriptionFlags(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.numColors(); + if (numColors == 0) + return; + + QVarLengthArray<DFBColor> 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); +} + +void QDirectFBScreen::setImageColorTable(QImage *image, IDirectFBSurface *surface) +{ + if (!image || !surface || image->depth() > 8) + return; + + IDirectFBPalette *palette = 0; + unsigned int numColors = 0; + DFBResult result; + do { + result = surface->GetPalette(surface, &palette); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::setImageColorTable GetPalette", result); + break; + } + + result = palette->GetSize(palette, &numColors); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::setImageColorTable GetPalette", result); + break; + } + + if (numColors == 0) + break; + + QVarLengthArray<DFBColor> dfbColors(numColors); + result = palette->GetEntries(palette, dfbColors.data(), numColors, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::setImageColorTable GetPalette", result); + break; + } + + QVector<QRgb> qtColors(numColors); + for (unsigned int i=0; i<numColors; ++i) { + const DFBColor &col = dfbColors[i]; + qtColors[i] = qRgba(col.r, col.g, col.b, col.a); + } + image->setColorTable(qtColors); + + } while (0); + + if (palette) + palette->Release(palette); +} + +#endif // QT_NO_DIRECTFB_PALETTE + +#if !defined(QT_NO_DIRECTFB_LAYER) && !defined(QT_NO_QWS_CURSOR) +class Q_GUI_EXPORT QDirectFBScreenCursor : public QScreenCursor +{ +public: + QDirectFBScreenCursor(); + ~QDirectFBScreenCursor(); + + void set(const QImage &image, int hotx, int hoty); + void move(int x, int y); + void show(); + void hide(); + +private: + IDirectFBDisplayLayer *layer; + bool implicitHide; +}; + +QDirectFBScreenCursor::QDirectFBScreenCursor() +{ + IDirectFB *fb = QDirectFBScreen::instance()->dfb(); + if (!fb) + qFatal("QDirectFBScreenCursor: DirectFB not initialized"); + + layer = QDirectFBScreen::instance()->dfbDisplayLayer(); + + if (layer) + layer->SetCooperativeLevel(layer, DLSCL_SHARED); // XXX: hw: remove? + else + qFatal("QDirectFBScreenCursor: Unable to get primary display layer!"); + + enable = true; + hwaccel = true; + implicitHide = false; + supportsAlpha = true; + + set(QImage(), 0, 0); +} + +QDirectFBScreenCursor::~QDirectFBScreenCursor() +{ +} + +void QDirectFBScreenCursor::show() +{ + DFBResult result; + result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to set cooperative level", result); + } + result = layer->EnableCursor(layer, 1); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to enable cursor", result); + } + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::show: " + "Unable to reset cooperative level", result); + } + implicitHide = false; +} + +void QDirectFBScreenCursor::hide() +{ + DFBResult result; + result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to set cooperative level", result); + } + result = layer->EnableCursor(layer, 0); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to disable cursor", result); + } + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::hide: " + "Unable to reset cooperative level", result); + } + implicitHide = true; +} + +void QDirectFBScreenCursor::move(int x, int y) +{ + layer->WarpCursor(layer, x, y); +} + +void QDirectFBScreenCursor::set(const QImage &image, int hotx, int hoty) +{ + if (image.isNull() && isVisible()) { + hide(); + implicitHide = true; + } else if (!image.isNull() && implicitHide) { + show(); + } + +#ifdef QT_NO_DIRECTFB_PALETTE + if (image.numColors() > 0) + cursor = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + else +#endif + if (image.format() == QImage::Format_Indexed8) { + cursor = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } else { + cursor = image; + } + size = cursor.size(); + hotspot = QPoint(hotx, hoty); + + DFBSurfaceDescription description; + description = QDirectFBScreen::getSurfaceDescription(cursor); + + IDirectFBSurface *surface; + surface = QDirectFBScreen::instance()->createDFBSurface(&description); + if (!surface) { + qWarning("QDirectFBScreenCursor::set: Unable to create surface"); + return; + } +#ifndef QT_NO_DIRECTFB_PALETTE + QDirectFBScreen::setSurfaceColorTable(surface, cursor); +#endif + + DFBResult result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: " + "Unable to set cooperative level", result); + } + result = layer->SetCursorShape(layer, surface, hotx, hoty); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: Unable to set cursor shape", + result); + } + + result = layer->SetCooperativeLevel(layer, DLSCL_SHARED); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreenCursor::set: " + "Unable to reset cooperative level", result); + } + + if (surface) + QDirectFBScreen::instance()->releaseDFBSurface(surface); +} +#endif // QT_NO_DIRECTFB_LAYER + +QDirectFBScreen::QDirectFBScreen(int display_id) + : QScreen(display_id, DirectFBClass), d_ptr(new QDirectFBScreenPrivate(this)) +{ +} + +QDirectFBScreen::~QDirectFBScreen() +{ + delete d_ptr; +} + +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; +} + +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 (QString flip, flips) { + if (flip == QLatin1String("wait")) + flipFlags = DFBSurfaceFlipFlags(flipFlags | DSFLIP_WAIT); + else if (flip == QLatin1String("blit")) + flipFlags = DFBSurfaceFlipFlags(flipFlags | DSFLIP_BLIT); + else if (flip == QLatin1String("onsync")) + flipFlags = DFBSurfaceFlipFlags(flipFlags | DSFLIP_ONSYNC); + else if (flip == QLatin1String("pipeline")) + flipFlags = DFBSurfaceFlipFlags(flipFlags | DSFLIP_PIPELINE); + else + qWarning("QDirectFBScreen: Unknown flip argument: %s", + qPrintable(flip)); + } + } +} + +QPixmapData* QDirectFBScreenPrivate::createPixmapData(QPixmapData::PixelType type) const +{ + if (type == QPixmapData::BitmapType) + return QWSGraphicsSystem::createPixmapData(type); + + return new QDirectFBPixmapData(type); +} + +static void printDirectFBInfo(IDirectFB *fb) +{ + DFBResult result; + DFBGraphicsDeviceDescription dev; + + result = fb->GetDeviceDescription(fb, &dev); + if (result != DFB_OK) { + DirectFBError("Error reading graphics device description", result); + return; + } + + qDebug("Device: %s (%s), Driver: %s v%i.%i (%s)\n" + " acceleration: 0x%x, blit: 0x%x, draw: 0x%0x video: %i\n", + dev.name, dev.vendor, dev.driver.name, dev.driver.major, + dev.driver.minor, dev.driver.vendor, dev.acceleration_mask, + dev.blitting_flags, dev.drawing_flags, dev.video_memory); +} + +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("debug"), Qt::CaseInsensitive)) + printDirectFBInfo(d_ptr->dfb); + + if (displayArgs.contains(QLatin1String("videoonly"))) + d_ptr->videoonly = true; + +#ifndef QT_NO_DIRECTFB_WM + if (displayArgs.contains(QLatin1String("fullscreen"))) +#endif + d_ptr->dfb->SetCooperativeLevel(d_ptr->dfb, DFSCL_FULLSCREEN); + + DFBSurfaceDescription description; + description.flags = DFBSurfaceDescriptionFlags(DSDESC_CAPS); + description.caps = DFBSurfaceCapabilities(DSCAPS_PRIMARY + | DSCAPS_DOUBLE + | DSCAPS_STATIC_ALLOC); + if (!(d_ptr->flipFlags & DSFLIP_BLIT)) { + description.caps = DFBSurfaceCapabilities(description.caps + | DSCAPS_DOUBLE + | DSCAPS_TRIPLE); + } + + + // We don't track the primary surface as it's released in disconnect + d_ptr->dfbSurface = createDFBSurface(&description, false); + if (!d_ptr->dfbSurface) { + DirectFBError("QDirectFBScreen: error creating primary surface", + result); + return false; + } + d_ptr->dfbSurface->GetSize(d_ptr->dfbSurface, &w, &h); + + data = 0; + lstep = 0; + size = 0; + dw = w; + dh = h; + + DFBSurfacePixelFormat format; + result = d_ptr->dfbSurface->GetPixelFormat(d_ptr->dfbSurface, &format); + if (result == DFB_OK) + QScreen::d = depth(format); + else + DirectFBError("QDirectFBScreen: error getting surface format", result); + + setPixelFormat(getImageFormat(format)); + + physWidth = physHeight = -1; + QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)")); + int dimIdxW = displayArgs.indexOf(mmWidthRx); + if (dimIdxW >= 0) { + mmWidthRx.exactMatch(displayArgs.at(dimIdxW)); + physWidth = mmWidthRx.cap(1).toInt(); + } + QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)")); + int dimIdxH = displayArgs.indexOf(mmHeightRx); + if (dimIdxH >= 0) { + mmHeightRx.exactMatch(displayArgs.at(dimIdxH)); + physHeight = mmHeightRx.cap(1).toInt(); + } + const int dpi = 72; + if (physWidth < 0) + physWidth = qRound(dw * 25.4 / dpi); + if (physHeight < 0) + physHeight = qRound(dh * 25.4 / dpi); + +#ifndef QT_NO_DIRECTFB_LAYER + result = d_ptr->dfb->GetDisplayLayer(d_ptr->dfb, DLID_PRIMARY, + &d_ptr->dfbLayer); + if (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect: " + "Unable to get primary 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 (result != DFB_OK) { + DirectFBError("QDirectFBScreen::connect: " + "Unable to get screen!", result); + return false; + } + + setGraphicsSystem(d_ptr); + + return true; +} + +void QDirectFBScreen::disconnect() +{ + d_ptr->dfbSurface->Release(d_ptr->dfbSurface); + d_ptr->dfbSurface = 0; + + 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() +{ + QWSServer *server = QWSServer::instance(); +#ifndef QT_NO_DIRECTFB_MOUSE + if (qgetenv("QWS_MOUSE_PROTO").isEmpty()) { + server->setDefaultMouse("None"); + d_ptr->mouse = new QDirectFBMouseHandler; + } +#endif +#ifndef QT_NO_DIRECTFB_KEYBOARD + if (qgetenv("QWS_KEYBOARD").isEmpty()) { + server->setDefaultKeyboard("None"); + d_ptr->keyboard = new QDirectFBKeyboardHandler(QString()); + } +#endif + +#ifndef QT_NO_QWS_CURSOR +#ifdef QT_NO_DIRECTFB_LAYER + QScreenCursor::initSoftwareCursor(); +#else + qt_screencursor = new QDirectFBScreenCursor; +#endif +#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 QDirectFBSurface(const_cast<QDirectFBScreen*>(this), widget); + else + return QScreen::createSurface(widget); +#else + return new QDirectFBSurface(const_cast<QDirectFBScreen*>(this), widget); +#endif +} + +QWSWindowSurface* QDirectFBScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("directfb")) + return new QDirectFBSurface(const_cast<QDirectFBScreen*>(this)); + return QScreen::createSurface(key); +} + +void QDirectFBScreen::compose(const QRegion ®ion) +{ + const QList<QWSWindow*> windows = QWSServer::instance()->clientWindows(); + + QRegion blitRegion = region; + QRegion blendRegion; + + d_ptr->dfbSurface->SetBlittingFlags(d_ptr->dfbSurface, DSBLIT_NOFX); + + // blit opaque region + for (int i = 0; i < windows.size(); ++i) { + QWSWindow *win = windows.at(i); + QWSWindowSurface *surface = win->windowSurface(); + if (!surface) + continue; + + const QRegion r = win->allocatedRegion() & blitRegion; + if (r.isEmpty()) + continue; + + blitRegion -= r; + + if (surface->isRegionReserved()) { + // nothing + } else if (win->isOpaque()) { + const QPoint offset = win->requestedRegion().boundingRect().topLeft(); + + if (surface->key() == QLatin1String("directfb")) { + QDirectFBSurface *s = static_cast<QDirectFBSurface*>(surface); + blit(s->directFbSurface(), offset, r); + } else { + blit(surface->image(), offset, r); + } + } else { + blendRegion += r; + } + if (blitRegion.isEmpty()) + break; + } + + { // fill background + const QRegion fill = blitRegion + blendRegion; + if (!fill.isEmpty()) { + const QColor color = QWSServer::instance()->backgroundBrush().color(); + solidFill(color, fill); + blitRegion = QRegion(); + } + } + + if (blendRegion.isEmpty()) + return; + + // blend non-opaque region + for (int i = windows.size() - 1; i >= 0; --i) { + QWSWindow *win = windows.at(i); + QWSWindowSurface *surface = win->windowSurface(); + if (!surface) + continue; + + const QRegion r = win->allocatedRegion() & blendRegion; + if (r.isEmpty()) + continue; + + DFBSurfaceBlittingFlags flags = DSBLIT_NOFX; + if (!win->isOpaque()) { + flags = DFBSurfaceBlittingFlags(flags | DSBLIT_BLEND_ALPHACHANNEL); + const uint opacity = win->opacity(); + if (opacity < 255) { + flags = DFBSurfaceBlittingFlags(flags | DSBLIT_BLEND_COLORALPHA); + d_ptr->dfbSurface->SetColor(d_ptr->dfbSurface, 0xff, 0xff, 0xff, opacity); + } + } + d_ptr->dfbSurface->SetBlittingFlags(d_ptr->dfbSurface, flags); + + const QPoint offset = win->requestedRegion().boundingRect().topLeft(); + + if (surface->key() == QLatin1String("directfb")) { + QDirectFBSurface *s = static_cast<QDirectFBSurface*>(surface); + blit(s->directFbSurface(), offset, r); + } else { + blit(surface->image(), offset, r); + } + } +} + +// Normally, when using DirectFB to compose the windows (I.e. when +// QT_NO_DIRECTFB_WM isn't set), exposeRegion will simply return. If +// QT_NO_DIRECTFB_WM is set, exposeRegion will compose only non-directFB +// window surfaces. Normal, directFB surfaces are handled by DirectFB. +void QDirectFBScreen::exposeRegion(QRegion r, int changing) +{ + const QList<QWSWindow*> windows = QWSServer::instance()->clientWindows(); + if (changing < 0 || changing >= windows.size()) + return; + +#ifndef QT_NO_DIRECTFB_WM + QWSWindow *win = windows.at(changing); + QWSWindowSurface *s = win->windowSurface(); + if (s && s->key() == QLatin1String("directfb")) + return; +#endif + + r &= region(); + if (r.isEmpty()) + return; + + if (d_ptr->flipFlags & DSFLIP_BLIT) { + const QRect brect = r.boundingRect(); + DFBRegion dfbRegion = { brect.left(), brect.top(), + brect.right(), brect.bottom() }; + compose(r); + d_ptr->dfbSurface->Flip(d_ptr->dfbSurface, &dfbRegion, + d_ptr->flipFlags); + } else { + compose(r + d_ptr->prevExpose); + d_ptr->dfbSurface->Flip(d_ptr->dfbSurface, 0, d_ptr->flipFlags); + } + d_ptr->prevExpose = r; +} + + +void QDirectFBScreen::blit(const QImage &img, const QPoint &topLeft, + const QRegion ®) +{ + IDirectFBSurface *src = 0; + DFBSurfaceDescription description = getSurfaceDescription(img); + + src = createDFBSurface(&description); + if (!src) { + qWarning("QDirectFBScreen::blit(): Error creating surface"); + return; + } +#ifndef QT_NO_DIRECTFB_PALETTE + setSurfaceColorTable(d_ptr->dfbSurface, img); +#endif + + blit(src, topLeft, reg); + + releaseDFBSurface(src); +} + +void QDirectFBScreen::blit(IDirectFBSurface *src, const QPoint &topLeft, + const QRegion ®ion) +{ + const QVector<QRect> rs = region.translated(-offset()).rects(); + const int size = rs.size(); + const QPoint tl = topLeft - offset(); + + QVarLengthArray<DFBRectangle> rects(size); + QVarLengthArray<DFBPoint> points(size); + + int n = 0; + for (int i = 0; i < size; ++i) { + const QRect r = rs.at(i); + if (!r.isValid()) + continue; + rects[n].x = r.x() - tl.x(); + rects[n].y = r.y() - tl.y(); + rects[n].w = r.width(); + rects[n].h = r.height(); + points[n].x = r.x(); + points[n].y = r.y(); + ++n; + } + + d_ptr->dfbSurface->BatchBlit(d_ptr->dfbSurface, src, rects.data(), + points.data(), n); +} + +void QDirectFBScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + if (region.isEmpty()) + return; + + const QVector<QRect> rects = region.rects(); + QVarLengthArray<DFBRectangle> dfbRects(rects.size()); + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + dfbRects[i].x = r.x(); + dfbRects[i].y = r.y(); + dfbRects[i].w = r.width(); + dfbRects[i].h = r.height(); + } + + d_ptr->dfbSurface->SetColor(d_ptr->dfbSurface, + color.red(), color.green(), color.blue(), + color.alpha()); + d_ptr->dfbSurface->FillRectangles(d_ptr->dfbSurface, dfbRects.data(), + dfbRects.size()); +} + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h new file mode 100644 index 0000000..e9a2f63 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTFBSCREEN_H +#define QDIRECTFBSCREEN_H + +#include <QtGui/qscreen_qws.h> +#include <directfb.h> + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +#define Q_DIRECTFB_VERSION ((DIRECTFB_MAJOR_VERSION << 16) | (DIRECTFB_MINOR_VERION << 8) | DIRECTFB_MICRO_VERSION) + +class QDirectFBScreenPrivate; + +class Q_GUI_EXPORT QDirectFBScreen : public QScreen +{ +public: + QDirectFBScreen(int display_id); + ~QDirectFBScreen(); + + bool connect(const QString &displaySpec); + void disconnect(); + bool initDevice(); + void shutdownDevice(); + + void exposeRegion(QRegion r, int changing); + void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + void scroll(const QRegion ®ion, const QPoint &offset); + void solidFill(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 inline QDirectFBScreen* instance() { + QScreen *inst = QScreen::instance(); + Q_ASSERT(!inst || inst->classId() == QScreen::DirectFBClass); + return static_cast<QDirectFBScreen*>(inst); + } + + IDirectFB* dfb(); + IDirectFBSurface* dfbSurface(); +#ifndef QT_NO_DIRECTFB_LAYER + IDirectFBDisplayLayer* dfbDisplayLayer(); +#endif + + // Track surface creation/release so we can release all on exit + IDirectFBSurface* createDFBSurface(const DFBSurfaceDescription* desc, bool track = true); + void releaseDFBSurface(IDirectFBSurface* surface); + bool preferVideoOnly() const; + + static int depth(DFBSurfacePixelFormat format); + + static DFBSurfacePixelFormat getSurfacePixelFormat(const QImage &image); + static DFBSurfaceDescription getSurfaceDescription(const QImage &image); + static DFBSurfaceDescription getSurfaceDescription(const uint *buffer, + int length); + static QImage::Format getImageFormat(DFBSurfacePixelFormat format); + static inline bool isPremultiplied(QImage::Format format); + +#ifndef QT_NO_DIRECTFB_PALETTE + static void setSurfaceColorTable(IDirectFBSurface *surface, + const QImage &image); + static void setImageColorTable(QImage *image, IDirectFBSurface *surface); +#endif + +private: + void compose(const QRegion &r); + void blit(IDirectFBSurface *src, const QPoint &topLeft, + const QRegion ®ion); + + QDirectFBScreenPrivate *d_ptr; +}; + +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; +} + + +QT_END_HEADER + +#endif // QDIRECTFBSCREEN_H diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp new file mode 100644 index 0000000..ca863d2 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreenplugin.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbscreen.h" + +#include <QtGui/qscreendriverplugin_qws.h> +#include <QtCore/qstringlist.h> + +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) diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbsurface.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbsurface.cpp new file mode 100644 index 0000000..ab1d0f1 --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbsurface.cpp @@ -0,0 +1,393 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectfbsurface.h" +#include "qdirectfbscreen.h" +#include "qdirectfbpaintengine.h" + +#include <qwidget.h> +#include <qpaintdevice.h> +#include <qvarlengtharray.h> + + +//#define QT_DIRECTFB_DEBUG_SURFACES 1 + +QDirectFBSurface::QDirectFBSurface(QDirectFBScreen* scr) + : QDirectFBPaintDevice(scr) +#ifndef QT_NO_DIRECTFB_WM + , dfbWindow(0) +#endif + , engine(0) +{ + setSurfaceFlags(Opaque | Buffered); +} + +QDirectFBSurface::QDirectFBSurface(QDirectFBScreen* scr, QWidget *widget) + : QWSWindowSurface(widget), QDirectFBPaintDevice(scr) +#ifndef QT_NO_DIRECTFB_WM + , dfbWindow(0) +#endif + , engine(0) +{ + onscreen = widget->testAttribute(Qt::WA_PaintOnScreen); + if (onscreen) + setSurfaceFlags(Opaque | RegionReserved); + else + setSurfaceFlags(Opaque | Buffered); +} + +QDirectFBSurface::~QDirectFBSurface() +{ +} + +bool QDirectFBSurface::isValid() const +{ + return true; +} + +#ifndef QT_NO_DIRECTFB_WM +void QDirectFBSurface::createWindow() +{ + IDirectFBDisplayLayer *layer = screen->dfbDisplayLayer(); + if (!layer) + qFatal("QDirectFBWindowSurface: Unable to get primary display layer!"); + + DFBWindowDescription description; + description.caps = DFBWindowCapabilities(DWCAPS_NODECORATION | + DWCAPS_ALPHACHANNEL); + description.flags = DWDESC_CAPS; + + DFBResult result = layer->CreateWindow(layer, &description, &dfbWindow); + if (result != DFB_OK) + DirectFBErrorFatal("QDirectFBWindowSurface::createWindow", result); + + if (dfbSurface) + dfbSurface->Release(dfbSurface); + + dfbWindow->GetSurface(dfbWindow, &dfbSurface); +} +#endif // QT_NO_DIRECTFB_WM + +void QDirectFBSurface::setGeometry(const QRect &rect, const QRegion &mask) +{ + if (rect.isNull()) { +#ifndef QT_NO_DIRECTFB_WM + if (dfbWindow) { + dfbWindow->Release(dfbWindow); + dfbWindow = 0; + } +#endif + if (dfbSurface) { + dfbSurface->Release(dfbSurface); + dfbSurface = 0; + } + } else if (rect != geometry()) { + const bool isResize = rect.size() != geometry().size(); + DFBResult result = DFB_OK; + + // If we're in a resize, the surface shouldn't be locked + Q_ASSERT( (lockedImage == 0) || (isResize == false)); + + IDirectFBSurface *s = screen->dfbSurface(); + if (onscreen && s) { + if (dfbSurface) + dfbSurface->Release(dfbSurface); + + DFBRectangle r = { rect.x(), rect.y(), + rect.width(), rect.height() }; + result = s->GetSubSurface(s, &r, &dfbSurface); + } else { +#ifdef QT_NO_DIRECTFB_WM + if (isResize) { + if (dfbSurface) + dfbSurface->Release(dfbSurface); + + IDirectFB *dfb = screen->dfb(); + if (!dfb) { + qFatal("QDirectFBWindowSurface::setGeometry(): " + "Unable to get DirectFB handle!"); + } + + DFBSurfaceDescription description; + description.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | + DSDESC_HEIGHT | + DSDESC_PIXELFORMAT); + description.width = rect.width(); + description.height = rect.height(); + description.pixelformat = DSPF_ARGB; + + dfbSurface = QDirectFBScreen::instance()->createDFBSurface(&description, false); + } else { + Q_ASSERT(dfbSurface); + } +#else + const QRect oldRect = geometry(); + const bool isMove = oldRect.isEmpty() || + rect.topLeft() != oldRect.topLeft(); + + if (!dfbWindow) + createWindow(); + + 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()); +#endif + } + + if (result != DFB_OK) + DirectFBErrorFatal("QDirectFBSurface::setGeometry()", result); + } + + QWSWindowSurface::setGeometry(rect, mask); +} + +QByteArray QDirectFBSurface::permanentState() const +{ + QByteArray array; +#ifdef QT_NO_DIRECTFB_WM + array.resize(sizeof(SurfaceFlags) + sizeof(IDirectFBSurface*)); +#else + array.resize(sizeof(SurfaceFlags)); +#endif + char *ptr = array.data(); + + *reinterpret_cast<SurfaceFlags*>(ptr) = surfaceFlags(); + ptr += sizeof(SurfaceFlags); + +#ifdef QT_NO_DIRECTFB_WM + *reinterpret_cast<IDirectFBSurface**>(ptr) = dfbSurface; +#endif + return array; +} + +void QDirectFBSurface::setPermanentState(const QByteArray &state) +{ + SurfaceFlags flags; + const char *ptr = state.constData(); + + flags = *reinterpret_cast<const SurfaceFlags*>(ptr); + setSurfaceFlags(flags); + +#ifdef QT_NO_DIRECTFB_WM + ptr += sizeof(SurfaceFlags); + dfbSurface = *reinterpret_cast<IDirectFBSurface* const*>(ptr); +#endif +} + +bool QDirectFBSurface::scroll(const QRegion ®ion, int dx, int dy) +{ + if (!dfbSurface) + return false; + + const QVector<QRect> rects = region.rects(); + const int n = rects.size(); + + QVarLengthArray<DFBRectangle, 8> dfbRects(n); + QVarLengthArray<DFBPoint, 8> dfbPoints(n); + + for (int i = 0; i < n; ++i) { + const QRect r = rects.at(i); + dfbRects[i].x = r.x(); + dfbRects[i].y = r.y(); + dfbRects[i].w = r.width(); + dfbRects[i].h = r.height(); + dfbPoints[i].x = r.x() + dx; + dfbPoints[i].y = r.y() + dy; + } + + dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); + dfbSurface->BatchBlit(dfbSurface, dfbSurface, + dfbRects.data(), dfbPoints.data(), n); + + return true; +} + +bool QDirectFBSurface::move(const QPoint &offset) +{ + QWSWindowSurface::move(offset); + +#ifdef QT_NO_DIRECTFB_WM + return true; // buffered +#else + if (!dfbWindow) + return false; + + DFBResult status = dfbWindow->Move(dfbWindow, offset.x(), offset.y()); + return (status == DFB_OK); +#endif +} + +QRegion QDirectFBSurface::move(const QPoint &offset, const QRegion &newClip) +{ +#ifdef QT_NO_DIRECTFB_WM + return QWSWindowSurface::move(offset, newClip); +#else + Q_UNUSED(offset); + Q_UNUSED(newClip); + + // DirectFB handles the entire move, so there's no need to blit. + return QRegion(); +#endif +} + +QPaintEngine* QDirectFBSurface::paintEngine() const +{ + if (!engine) { + QDirectFBSurface *that = const_cast<QDirectFBSurface*>(this); + that->engine = new QDirectFBPaintEngine(that); + return that->engine; + } + return engine; +} + +// hw: XXX: copied from QWidgetPrivate::isOpaque() +inline bool isWidgetOpaque(const QWidget *w) +{ + if (w->testAttribute(Qt::WA_OpaquePaintEvent) + || w->testAttribute(Qt::WA_PaintOnScreen)) + return true; + + const QPalette &pal = w->palette(); + + if (w->autoFillBackground()) { + const QBrush &autoFillBrush = pal.brush(w->backgroundRole()); + if (autoFillBrush.style() != Qt::NoBrush && autoFillBrush.isOpaque()) + return true; + } + + if (!w->testAttribute(Qt::WA_NoSystemBackground)) { + const QBrush &windowBrush = w->palette().brush(QPalette::Window); + if (windowBrush.style() != Qt::NoBrush && windowBrush.isOpaque()) + return true; + } + + return false; +} + +void QDirectFBSurface::flush(QWidget *widget, const QRegion ®ion, + const QPoint &offset) +{ + QWidget *win = window(); + + // hw: make sure opacity information is updated before compositing + const bool opaque = isWidgetOpaque(win); + if (opaque != isOpaque()) { + SurfaceFlags flags = Buffered; + if (opaque) + flags |= Opaque; + setSurfaceFlags(flags); + } + +#ifndef QT_NO_DIRECTFB_WM + const quint8 winOpacity = quint8(win->windowOpacity() * 255); + quint8 opacity; + + if (dfbWindow) { + dfbWindow->GetOpacity(dfbWindow, &opacity); + if (winOpacity != opacity) + dfbWindow->SetOpacity(dfbWindow, winOpacity); + } +#endif + + // XXX: have to call the base function first as the decoration is + // currently painted there + QWSWindowSurface::flush(widget, region, offset); + +#ifndef QT_NO_DIRECTFB_WM + const QRect br = region.boundingRect().translated(painterOffset()); + const DFBRegion r = { br.x(), br.y(), + br.x() + br.width(), br.y() + br.height() }; + + dfbSurface->Flip(dfbSurface, &r, DSFLIP_NONE); +#endif +} + + +void QDirectFBSurface::beginPaint(const QRegion &) +{ +} + +void QDirectFBSurface::endPaint(const QRegion &) +{ +#ifdef QT_DIRECTFB_DEBUG_SURFACES + if (bufferImages.count()) { + qDebug("QDirectFBSurface::endPaint() this=%p", this); + + foreach(QImage* bufferImg, bufferImages) + qDebug(" Deleting buffer image %p", bufferImg); + } +#endif + + qDeleteAll(bufferImages); + bufferImages.clear(); + unlockDirectFB(); +} + + +QImage* QDirectFBSurface::buffer(const QWidget *widget) +{ + if (!lockedImage) + return 0; + + const QRect rect = QRect(offset(widget), widget->size()) + & lockedImage->rect(); + if (rect.isEmpty()) + return 0; + + QImage *img = new QImage(lockedImage->scanLine(rect.y()) + + rect.x() * (lockedImage->depth() / 8), + rect.width(), rect.height(), + lockedImage->bytesPerLine(), + lockedImage->format()); + bufferImages.append(img); + +#ifdef QT_DIRECTFB_DEBUG_SURFACES + qDebug("QDirectFBSurface::buffer() Created & returned %p", img); +#endif + + return img; +} + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbsurface.h b/src/plugins/gfxdrivers/directfb/qdirectfbsurface.h new file mode 100644 index 0000000..a9cdb7d --- /dev/null +++ b/src/plugins/gfxdrivers/directfb/qdirectfbsurface.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECFBWINDOWSURFACE_H +#define QDIRECFBWINDOWSURFACE_H + +#include "qdirectfbpaintengine.h" +#include "qdirectfbpaintdevice.h" +#include "qdirectfbscreen.h" + +#include <private/qpaintengine_raster_p.h> +#include <private/qwindowsurface_qws_p.h> +#include <directfb.h> + +QT_BEGIN_HEADER + +QT_MODULE(Gui) + +class QDirectFBSurface: public QWSWindowSurface, public QDirectFBPaintDevice +{ +public: + QDirectFBSurface(QDirectFBScreen* scr); + QDirectFBSurface(QDirectFBScreen* scr, QWidget *widget); + ~QDirectFBSurface(); + + bool isValid() const; + + void setGeometry(const QRect &rect, const QRegion &mask); + + 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); + QRegion move(const QPoint &offset, const QRegion &newClip); + + QImage image() const { return QImage(); } + QPaintDevice* paintDevice() { return this; } + QPaintEngine* paintEngine() const; + + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + + void beginPaint(const QRegion &); + void endPaint(const QRegion &); + + QImage* buffer(const QWidget *widget); + +private: +#ifndef QT_NO_DIRECTFB_WM + void createWindow(); + IDirectFBWindow *dfbWindow; +#endif + QDirectFBPaintEngine *engine; + + bool onscreen; + + QList<QImage*> bufferImages; +}; + +QT_END_HEADER + +#endif // QDIRECFBWINDOWSURFACE_H diff --git a/src/plugins/gfxdrivers/gfxdrivers.pro b/src/plugins/gfxdrivers/gfxdrivers.pro new file mode 100644 index 0000000..21aaf0f --- /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, hybrid) :SUBDIRS += hybrid +contains(gfx-plugins, svgalib) :SUBDIRS += svgalib +contains(gfx-plugins, powervr) :SUBDIRS += powervr diff --git a/src/plugins/gfxdrivers/hybrid/hybrid.pro b/src/plugins/gfxdrivers/hybrid/hybrid.pro new file mode 100644 index 0000000..8b8e9ef --- /dev/null +++ b/src/plugins/gfxdrivers/hybrid/hybrid.pro @@ -0,0 +1,16 @@ +TEMPLATE = lib +CONFIG += plugin +QT += opengl + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +TARGET = hybridscreen +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +HEADERS = hybridscreen.h \ + hybridsurface.h +SOURCES = hybridscreen.cpp \ + hybridsurface.cpp \ + hybridplugin.cpp + diff --git a/src/plugins/gfxdrivers/hybrid/hybridplugin.cpp b/src/plugins/gfxdrivers/hybrid/hybridplugin.cpp new file mode 100644 index 0000000..803c4fc --- /dev/null +++ b/src/plugins/gfxdrivers/hybrid/hybridplugin.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "hybridscreen.h" + +#include <QScreenDriverPlugin> +#include <QStringList> + +class HybridPlugin : public QScreenDriverPlugin +{ +public: + HybridPlugin(); + + QStringList keys() const; + QScreen *create(const QString&, int displayId); +}; + +HybridPlugin::HybridPlugin() + : QScreenDriverPlugin() +{ +} + +QStringList HybridPlugin::keys() const +{ + return (QStringList() << "hybrid"); +} + +QScreen* HybridPlugin::create(const QString &driver, int displayId) +{ + if (driver.toLower() != "hybrid") + return 0; + + return new HybridScreen(displayId); +} + +Q_EXPORT_STATIC_PLUGIN(Hybrid) +Q_EXPORT_PLUGIN2(hybridscreendriver, HybridPlugin) diff --git a/src/plugins/gfxdrivers/hybrid/hybridscreen.cpp b/src/plugins/gfxdrivers/hybrid/hybridscreen.cpp new file mode 100644 index 0000000..3a40b4c --- /dev/null +++ b/src/plugins/gfxdrivers/hybrid/hybridscreen.cpp @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "hybridscreen.h" +#include "hybridsurface.h" + +#include <QVector> +#include <QVarLengthArray> +#include <QApplication> +#include <QColor> +#include <QWidget> + +#include <GLES/egl.h> + +class HybridScreenPrivate +{ +public: + HybridScreenPrivate(HybridScreen *owner); + + bool verbose; + EGLDisplay display; + EGLint majorEGLVersion; + EGLint minorEGLVersion; + + QScreen *screen; + +private: + HybridScreen *q_ptr; +}; + +HybridScreenPrivate::HybridScreenPrivate(HybridScreen *owner) + : display(EGL_NO_DISPLAY), majorEGLVersion(0), minorEGLVersion(0), + screen(0), q_ptr(owner) +{ +} + +HybridScreen::HybridScreen(int displayId) + : QGLScreen(displayId) +{ + d_ptr = new HybridScreenPrivate(this); +} + +HybridScreen::~HybridScreen() +{ + delete d_ptr; +} + +static void error(const char *message) +{ + const EGLint error = eglGetError(); + qWarning("HybridScreen error: %s: 0x%x", message, error); +} + +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; +} + +bool HybridScreen::connect(const QString &displaySpec) +{ + QString dspec = displaySpec; + if (dspec.startsWith(QLatin1String("hybrid:"), Qt::CaseInsensitive)) + dspec = dspec.mid(QString(QLatin1String("hybrid:")).size()); + else if (dspec.compare(QLatin1String("hybrid"), Qt::CaseInsensitive) == 0) + dspec = QString(); + + const QString displayIdSpec = QString(QLatin1String(" :%1")).arg(displayId); + if (dspec.endsWith(displayIdSpec)) + dspec = dspec.left(dspec.size() - displayIdSpec.size()); + + const QStringList args = dspec.split(QLatin1Char(':'), + QString::SkipEmptyParts); + const int id = getDisplayId(dspec); + d_ptr->screen = qt_get_screen(id, dspec.toLatin1().constData()); + + const QScreen *screen = d_ptr->screen; + d = screen->depth(); + w = screen->width(); + h = screen->height(); + dw = screen->deviceWidth(); + dh = screen->deviceHeight(); + lstep = screen->linestep(); + data = screen->base(); + physWidth = screen->physicalWidth(); + physHeight = screen->physicalHeight(); + setPixelFormat(screen->pixelFormat()); + setOffset(screen->offset()); + + d_ptr->verbose = args.contains(QLatin1String("verbose")); + + d_ptr->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (d_ptr->display == EGL_NO_DISPLAY) { + error("getting display"); + return false; + } + + EGLBoolean status; + status = eglInitialize(d_ptr->display, + &d_ptr->majorEGLVersion, &d_ptr->minorEGLVersion); + if (!status) { + error("eglInitialize"); + return false; + } + if (d_ptr->verbose) { + qDebug("Detected EGL version %d.%d", + d_ptr->majorEGLVersion, d_ptr->minorEGLVersion); + + EGLint numConfigs = 0; + eglGetConfigs(d_ptr->display, 0, 0, &numConfigs); + qDebug("%d available configurations", numConfigs); + } + + // XXX: hw: use eglQueryString to find supported APIs + + qt_screen = this; // XXX + + return true; +} + +bool HybridScreen::initDevice() +{ + if (d_ptr->screen) + return d_ptr->screen->initDevice(); + return false; +} + +void HybridScreen::shutdownDevice() +{ + if (d_ptr->screen) + d_ptr->screen->shutdownDevice(); +} + +void HybridScreen::disconnect() +{ + if (!eglTerminate(d_ptr->display)) + error("disconnecting"); + if (d_ptr->screen) { + d_ptr->screen->disconnect(); + delete d_ptr->screen; + d_ptr->screen = 0; + } + +} + +bool HybridScreen::hasOpenGLOverlays() const +{ + return true; +} + +bool HybridScreen::chooseContext(QGLContext *context, + const QGLContext *shareContext) +{ +#if 0 + // hw: update the glFormat variable. Probably needs a setter in the + // QGLWindowSurface class which can be a friend of whatever it wants. + + GLint res; + eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_LEVEL, &res); + d_ptr->glFormat.setPlane(res); + QT_EGL_ERR("eglGetConfigAttrib"); + + /* + if(deviceIsPixmap()) + res = 0; + else + eglDescribePixelFormat(fmt, EGL_DOUBLEBUFFER, &res); + d_ptr->glFormat.setDoubleBuffer(res); + */ + + eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_DEPTH_SIZE, &res); + d_ptr->glFormat.setDepth(res); + if (d_ptr->glFormat.depth()) + d_ptr->glFormat.setDepthBufferSize(res); + + //eglGetConfigAttrib(d_ptr->display,d_ptr->config, EGL_RGBA, &res); + //d_ptr->glFormat.setRgba(res); + + eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_ALPHA_SIZE, &res); + d_ptr->glFormat.setAlpha(res); + if (d_ptr->glFormat.alpha()) + d_ptr->glFormat.setAlphaBufferSize(res); + + //eglGetConfigAttrib(d_ptr->display,d_ptr->config, EGL_ACCUM_RED_SIZE, &res); + //d_ptr->glFormat.setAccum(res); + //if (d_ptr->glFormat.accum()) + // d_ptr->glFormat.setAccumBufferSize(res); + + eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_STENCIL_SIZE, &res); + d_ptr->glFormat.setStencil(res); + if (d_ptr->glFormat.stencil()) + d_ptr->glFormat.setStencilBufferSize(res); + + //eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_STEREO, &res); + //d_ptr->glFormat.setStereo(res); + + eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_SAMPLE_BUFFERS, &res); + d_ptr->glFormat.setSampleBuffers(res); + + if (d_ptr->glFormat.sampleBuffers()) { + eglGetConfigAttrib(d_ptr->display, d_ptr->config, EGL_SAMPLES, &res); + d_ptr->glFormat.setSamples(res); + } +#endif + + // hw: TODO: implement sharing of contexts + +#if 0 + if(shareContext && + (!shareContext->isValid() || !shareContext->d_func()->cx)) { + qWarning("QGLContext::chooseContext(): Cannot share with invalid context"); + shareContext = 0; + } +#endif + +#if 0 + d_ptr->cx = ctx; + if (shareContext && shareContext->d_func()->cx) { + QGLContext *share = const_cast<QGLContext *>(shareContext); + d_ptr->sharing = true; + share->d_func()->sharing = true; + } +#endif + +#if 0 + // vblank syncing + GLint interval = d_ptr->reqFormat.swapInterval(); + if (interval != -1) { + if (interval != 0) + eglSwapInterval(d_ptr->display, interval); + } +#endif + + return QGLScreen::chooseContext(context, shareContext); +} + +void HybridScreen::setDirty(const QRect& rect) +{ + d_ptr->screen->setDirty(rect); +} + +void HybridScreen::setMode(int w, int h, int d) +{ + d_ptr->screen->setMode(w, h, d); + setDirty(region().boundingRect()); +} + +bool HybridScreen::supportsDepth(int depth) const +{ + return d_ptr->screen->supportsDepth(depth); +} + +void HybridScreen::save() +{ + d_ptr->screen->save(); +} + +void HybridScreen::restore() +{ + d_ptr->screen->restore(); +} + +void HybridScreen::blank(bool on) +{ + d_ptr->screen->blank(on); +} + +bool HybridScreen::onCard(const unsigned char *ptr) const +{ + return d_ptr->screen->onCard(ptr); +} + +bool HybridScreen::onCard(const unsigned char *ptr, ulong &offset) const +{ + return d_ptr->screen->onCard(ptr, offset); +} + +bool HybridScreen::isInterlaced() const +{ + return d_ptr->screen->isInterlaced(); +} + +int HybridScreen::memoryNeeded(const QString &str) +{ + return d_ptr->screen->memoryNeeded(str); +} + +int HybridScreen::sharedRamSize(void *ptr) +{ + return d_ptr->screen->sharedRamSize(ptr); +} + +void HybridScreen::haltUpdates() +{ + d_ptr->screen->haltUpdates(); +} + +void HybridScreen::resumeUpdates() +{ + d_ptr->screen->resumeUpdates(); +} + +void HybridScreen::exposeRegion(QRegion r, int changing) +{ + d_ptr->screen->exposeRegion(r, changing); +} + +void HybridScreen::blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion) +{ + d_ptr->screen->blit(img, topLeft, region); +} + +void HybridScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + d_ptr->screen->solidFill(color, region); +} + +QWSWindowSurface* HybridScreen::createSurface(QWidget *widget) const +{ + if (qobject_cast<QGLWidget*>(widget)) + return new HybridSurface(widget, d_ptr->display); + return d_ptr->screen->createSurface(widget); +} + +QWSWindowSurface* HybridScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("hybrid")) + return new HybridSurface; + return d_ptr->screen->createSurface(key); +} + +QList<QScreen*> HybridScreen::subScreens() const +{ + return d_ptr->screen->subScreens(); +} + +QRegion HybridScreen::region() const +{ + return d_ptr->screen->region(); +} diff --git a/src/plugins/gfxdrivers/hybrid/hybridscreen.h b/src/plugins/gfxdrivers/hybrid/hybridscreen.h new file mode 100644 index 0000000..d463e12 --- /dev/null +++ b/src/plugins/gfxdrivers/hybrid/hybridscreen.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HYBRIDSCREEN_H +#define HYBRIDSCREEN_H + +#include <QtOpenGL/QGLScreen> + +class HybridScreenPrivate; + +class HybridScreen : public QGLScreen +{ +public: + HybridScreen(int displayId); + ~HybridScreen(); + + bool hasOpenGLOverlays() const; + + bool chooseContext(QGLContext *context, const QGLContext *shareContext); + bool hasOpenGL() { return true; } + + bool initDevice(); + bool connect(const QString &displaySpec); + void disconnect(); + void shutdownDevice(); + void setMode(int,int,int); + bool supportsDepth(int) const; + + void save(); + void restore(); + void blank(bool on); + + bool onCard(const unsigned char *) const; + bool onCard(const unsigned char *, ulong& out_offset) const; + + bool isInterlaced() const; + + int memoryNeeded(const QString&); + int sharedRamSize(void *); + + void haltUpdates(); + void resumeUpdates(); + + void exposeRegion(QRegion r, int changing); + + void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + void solidFill(const QColor &color, const QRegion ®ion); + void setDirty(const QRect&); + + QWSWindowSurface* createSurface(QWidget *widget) const; + QWSWindowSurface* createSurface(const QString &key) const; + + QList<QScreen*> subScreens() const; + QRegion region() const; +private: + HybridScreenPrivate *d_ptr; +}; + +#endif // HYBRIDSCREEN_H diff --git a/src/plugins/gfxdrivers/hybrid/hybridsurface.cpp b/src/plugins/gfxdrivers/hybrid/hybridsurface.cpp new file mode 100644 index 0000000..7281328 --- /dev/null +++ b/src/plugins/gfxdrivers/hybrid/hybridsurface.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "hybridsurface.h" + +#include <private/qwindowsurface_qws_p.h> +#include <private/qwslock_p.h> +#include <qscreen_qws.h> +#include <qvarlengtharray.h> + +static void error(const char *message) +{ + const EGLint error = eglGetError(); + qWarning("HybridSurface error: %s: 0x%x", message, error); +} + +static void imgToVanilla(const QImage *img, VanillaPixmap *pix) +{ + pix->width = img->width(); + pix->height = img->height(); + pix->stride = img->bytesPerLine(); + + if (img->depth() == 32) { + pix->rSize = pix->gSize = pix->bSize = pix->aSize = 8; + pix->lSize = 0; + pix->rOffset = 16; + pix->gOffset = 8; + pix->bOffset = 0; + pix->aOffset = 24; + } else if (img->format() == QImage::Format_RGB16) { + pix->rSize = 5; + pix->gSize = 6; + pix->bSize = 5; + pix->aSize = 0; + pix->lSize = 0; + pix->rOffset = 11; + pix->gOffset = 5; + pix->bOffset = 0; + pix->aOffset = 0; + } + + pix->padding = pix->padding2 = 0; + pix->pixels = const_cast<uchar*>(img->bits()); +} + +HybridSurface::HybridSurface() + : QWSGLWindowSurface(), memlock(0) +{ + setSurfaceFlags(Buffered | Opaque); +} + +HybridSurface::HybridSurface(QWidget *w, EGLDisplay disp) + : QWSGLWindowSurface(w), memlock(0), display(disp), config(0), + surface(EGL_NO_SURFACE), context(EGL_NO_CONTEXT), + pdevice(new QWSGLPaintDevice(w)) +{ + setSurfaceFlags(Buffered | Opaque); + + EGLint configAttribs[] = { + EGL_RED_SIZE, 0, + EGL_GREEN_SIZE, 0, + EGL_BLUE_SIZE, 0, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE, EGL_NONE + }; + + + EGLBoolean status; + EGLint numConfigs; + status = eglChooseConfig(display, configAttribs, 0, 0, &numConfigs); + if (!status) { + error("chooseConfig"); + return; + } + + //If there isn't any configuration good enough + if (numConfigs < 1) { + error("chooseConfig, no matching configurations found"); + return; + } + + QVarLengthArray<EGLConfig> configs(numConfigs); + + status = eglChooseConfig(display, configAttribs, configs.data(), + numConfigs, &numConfigs); + if (!status) { + error("chooseConfig"); + return; + } + + // hw: if used on an image buffer we need to check whether the resulting + // configuration matches our requirements exactly! + config = configs[0]; + + context = eglCreateContext(display, config, 0, 0); + //(shareContext ? shareContext->d_func()->cx : 0), + //configAttribs); + if (context == EGL_NO_CONTEXT) + error("eglCreateContext"); + +} + +HybridSurface::~HybridSurface() +{ +} + +bool HybridSurface::isValid() const +{ + return true; +} + +void HybridSurface::setGeometry(const QRect &rect, const QRegion &mask) +{ + const QSize size = rect.size(); + if (img.size() != size) { +// QWidget *win = window(); + QImage::Format imageFormat = QImage::Format_ARGB32_Premultiplied; + const int bytesPerPixel = 4; + + const int bpl = (size.width() * bytesPerPixel + 3) & ~3; + const int imagesize = bpl * size.height(); + + if (imagesize == 0) { + eglDestroySurface(display, surface); + mem.detach(); + img = QImage(); + } else { + mem.detach(); + if (!mem.create(imagesize)) { + perror("HybridSurface::setGeometry allocating shared memory"); + qFatal("Error creating shared memory of size %d", imagesize); + } + uchar *base = static_cast<uchar*>(mem.address()); + img = QImage(base, size.width(), size.height(), imageFormat); +// setImageMetrics(img, win); + + imgToVanilla(&img, &vanillaPix); + surface = eglCreatePixmapSurface(display, config, &vanillaPix, 0); + if (surface == EGL_NO_SURFACE) + error("setGeometry:eglCreatePixmapSurface"); + + } + } + QWSWindowSurface::setGeometry(rect, mask); +} + +QByteArray HybridSurface::permanentState() const +{ + QByteArray array; + array.resize(4 * sizeof(int) + sizeof(QImage::Format) + + sizeof(SurfaceFlags)); + + char *ptr = array.data(); + + reinterpret_cast<int*>(ptr)[0] = mem.id(); + reinterpret_cast<int*>(ptr)[1] = img.width(); + reinterpret_cast<int*>(ptr)[2] = img.height(); + reinterpret_cast<int*>(ptr)[3] = (memlock ? memlock->id() : -1); + ptr += 4 * sizeof(int); + + *reinterpret_cast<QImage::Format*>(ptr) = img.format(); + ptr += sizeof(QImage::Format); + + *reinterpret_cast<SurfaceFlags*>(ptr) = surfaceFlags(); + + return array; +} + +void HybridSurface::setPermanentState(const QByteArray &data) +{ + int memId; + int width; + int height; + int lockId; + QImage::Format format; + SurfaceFlags flags; + + const char *ptr = data.constData(); + + memId = reinterpret_cast<const int*>(ptr)[0]; + width = reinterpret_cast<const int*>(ptr)[1]; + height = reinterpret_cast<const int*>(ptr)[2]; + lockId = reinterpret_cast<const int*>(ptr)[3]; + ptr += 4 * sizeof(int); + + format = *reinterpret_cast<const QImage::Format*>(ptr); + ptr += sizeof(QImage::Format); + flags = *reinterpret_cast<const SurfaceFlags*>(ptr); + + setSurfaceFlags(flags); + +// setMemory(memId); + if (mem.id() != memId) { + mem.detach(); + if (!mem.attach(memId)) { + perror("QWSSharedMemSurface: attaching to shared memory"); + qCritical("QWSSharedMemSurface: Error attaching to" + " shared memory 0x%x", memId); + } + } + +// setLock(lockId); + if (!memlock || memlock->id() == lockId) { + delete memlock; + memlock = (lockId == -1 ? 0 : new QWSLock(lockId)); + } + + uchar *base = static_cast<uchar*>(mem.address()); + img = QImage(base, width, height, format); +} + +QImage HybridSurface::image() const +{ + return img; +} + +QPaintDevice* HybridSurface::paintDevice() +{ + return pdevice; +} + +void HybridSurface::beginPaint(const QRegion ®ion) +{ + QWSGLWindowSurface::beginPaint(region); + eglBindAPI(EGL_OPENGL_ES_API); + + EGLBoolean ok = eglMakeCurrent(display, surface, surface, context); + if (!ok) + error("qglMakeCurrent"); +} + +bool HybridSurface::lock(int timeout) +{ + Q_UNUSED(timeout); + if (!memlock) + return true; + return memlock->lock(QWSLock::BackingStore); +} + +void HybridSurface::unlock() +{ + if (memlock) + memlock->unlock(QWSLock::BackingStore); +} + +QPoint HybridSurface::painterOffset() const +{ + const QWidget *w = window(); + if (!w) + return QPoint(); + + if (w->mask().isEmpty()) + return QWSWindowSurface::painterOffset(); + + const QRegion region = w->mask() + & w->frameGeometry().translated(-w->geometry().topLeft()); + return -region.boundingRect().topLeft(); +} + diff --git a/src/plugins/gfxdrivers/hybrid/hybridsurface.h b/src/plugins/gfxdrivers/hybrid/hybridsurface.h new file mode 100644 index 0000000..d9be3b6 --- /dev/null +++ b/src/plugins/gfxdrivers/hybrid/hybridsurface.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HYBRIDSURFACE_H +#define HYBRIDSURFACE_H + +#include <private/qglwindowsurface_qws_p.h> +#include <private/qglpaintdevice_qws_p.h> +#include <GLES/egl.h> +#include <vanilla/eglVanilla.h> +#include <private/qwssharedmemory_p.h> + +class HybridPaintDevice; +class HybridSurfacePrivate; +class QWSLock; + +class HybridSurface : public QWSGLWindowSurface +{ +public: + HybridSurface(); + HybridSurface(QWidget *w, EGLDisplay display); + ~HybridSurface(); + + void beginPaint(const QRegion ®ion); + bool lock(int timeout); + void unlock(); + + bool isValid() const; + void setGeometry(const QRect &rect, const QRegion &mask); + QString key() const { return QLatin1String("hybrid"); } + + QByteArray permanentState() const; + void setPermanentState(const QByteArray &state); + + QImage image() const; + QPaintDevice *paintDevice(); + QPoint painterOffset() const; + +private: + QWSSharedMemory mem; + QImage img; + QWSLock *memlock; + EGLDisplay display; + EGLConfig config; + EGLSurface surface; + EGLContext context; + QWSGLPaintDevice *pdevice; + + VanillaPixmap vanillaPix; +}; + +#endif // HYBRIDSURFACE_H diff --git a/src/plugins/gfxdrivers/linuxfb/linuxfb.pro b/src/plugins/gfxdrivers/linuxfb/linuxfb.pro new file mode 100644 index 0000000..2a376e4 --- /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 0000000..a4bcbbf --- /dev/null +++ b/src/plugins/gfxdrivers/linuxfb/main.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qscreendriverplugin_qws.h> +#include <qscreenlinuxfb_qws.h> +#include <qstringlist.h> + +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 0000000..b62894d --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/QWSWSEGL.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +TARGET = pvrQWSWSEGL +CONFIG += dll warn_on +CONFIG -= qt + +HEADERS+=\ + pvrqwsdrawable.h \ + pvrqwsdrawable_p.h + +SOURCES+=\ + pvrqwsdrawable.c \ + pvrqwswsegl.c + +INCLUDEPATH += $$QMAKE_INCDIR_OPENGL + +for(p, QMAKE_LIBDIR_OPENGL) { + exists($$p):LIBS += -L$$p +} + +LIBS += -lpvr2d + +DESTDIR = $$QMAKE_LIBDIR_QT +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c new file mode 100644 index 0000000..5c37253 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c @@ -0,0 +1,856 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pvrqwsdrawable_p.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <linux/fb.h> +#include <fcntl.h> +#include <unistd.h> + +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].mapped) + 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 = var.xres * bytesPerPixel; + 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; + + /* 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].frameBuffer = memInfo; + pvrQwsDisplay.screens[screen].screenDrawable = 0; + pvrQwsDisplay.screens[screen].mapped = mapped; + pvrQwsDisplay.screens[screen].mappedLength = length; + pvrQwsDisplay.screens[screen].screenStart = start; + 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 (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; + } + } + + /* 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) + 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; +} + +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 + 7) & ~7; + 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; +} + +unsigned long pvrQwsGetMemoryId(PvrQwsDrawable *drawable) +{ + unsigned long addr; + unsigned long start; + unsigned long end; + unsigned long off; + unsigned long offset; + FILE *file; + char buffer[BUFSIZ]; + char flags[16]; + + if (!drawable->backBuffersValid) + return 0; + addr = (unsigned long) + (drawable->backBuffers[drawable->currentBackBuffer]->pBase); + + /* Search /proc/self/maps for the memory region that contains "addr". + The file offset for that memory region is the identifier we need */ + file = fopen("/proc/self/maps", "r"); + if (!file) { + perror("/proc/self/maps"); + return 0; + } + offset = 0; + while (fgets(buffer, sizeof(buffer), file)) { + if (sscanf(buffer, "%lx-%lx %s %lx", + &start, &end, flags, &off) < 4) + continue; + if (start <= addr && addr < end) { + offset = off; + break; + } + } + fclose(file); + return offset; +} + +void *pvrQwsMapMemory(unsigned long id, int size) +{ + void *addr; + int fd = open("/dev/pvrsrv", O_RDWR, 0); + if (fd < 0) { + perror("/dev/pvrsrv"); + return 0; + } + addr = mmap(0, (size_t)size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, (off_t)id); + if (addr == (void *)(-1)) { + perror("mmap pvr memory region"); + addr = 0; + } + close(fd); + return addr; +} + +void pvrQwsUnmapMemory(void *addr, int size) +{ + munmap(addr, size); +} diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h new file mode 100644 index 0000000..16872a9 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + +/* 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); + +/* Get a memory identifier for the indicated drawable's buffer. + The identifier can be passed to another process and then + passed to pvrQwsMapMemory() to map the drawable's buffer into + the other process's address space. Returns zero if the + memory identifier could not be determined. This should only + be used for pixmap drawables */ +unsigned long pvrQwsGetMemoryId(PvrQwsDrawable *drawable); + +/* Map the memory buffer of a foreign application's drawable, as + indicated by "id" and "size". Returns null if the map failed */ +void *pvrQwsMapMemory(unsigned long id, int size); + +/* Unmap the memory obtained from pvrQwsMapMemory() */ +void pvrQwsUnmapMemory(void *addr, int size); + +#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 0000000..d6c42a6 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <pvr2d.h> +#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; + +} 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; + 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 0000000..f46448e --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwswsegl.c @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <GLES/egltypes.h> +#include <wsegl.h> +#include <pvr2d.h> +#include <string.h> +#include <sys/mman.h> +#include "pvrqwsdrawable_p.h" + +#define WSEGL_UNUSED(x) (void)x; + +/* Capability information for the display */ +static WSEGLCaps const wseglDisplayCaps[] = { + {WSEGL_CAP_WINDOWS_USE_MBX_SYNC, 1}, + {WSEGL_CAP_PIXMAPS_USE_MBX_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; +} + +/* 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 = WSEGL_ROTATE_0; + 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 = WSEGL_ROTATE_0; + 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; + 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 0000000..b830066 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/README @@ -0,0 +1,56 @@ +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). + +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. These * +* can be obtained either through your platform provider or directly from * +* Imagination Technologies. * +*************************************************************************** + + +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 + + +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.pro b/src/plugins/gfxdrivers/powervr/powervr.pro new file mode 100644 index 0000000..f31ad04 --- /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 0000000..3a94851 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp @@ -0,0 +1,390 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pvreglscreen.h" +#include "pvreglwindowsurface.h" +#include "pvrqwsdrawable_p.h" +#include <QRegExp> +#include <qwindowsystem_qws.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/kd.h> +#include <fcntl.h> +#include <unistd.h> + +PvrEglScreen::PvrEglScreen(int displayId) + : QGLScreen(displayId) +{ + setOptions(NativeWindows); + setSupportsBlitInClients(true); + setSurfaceFunctions(new PvrEglScreenSurfaceFunctions(this, displayId)); + fd = -1; + ttyfd = -1; + doGraphicsMode = true; + oldKdMode = KD_TEXT; + if (QWSServer::instance()) + holder = new PvrEglSurfaceHolder(); + else + holder = 0; +} + +PvrEglScreen::~PvrEglScreen() +{ + if (fd >= 0) + ::close(fd); + delete holder; +} + +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); + 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; +} + +QWSWindowSurface* PvrEglScreen::createSurface(QWidget *widget) const +{ + if (qobject_cast<QGLWidget*>(widget)) + return new PvrEglWindowSurface(widget, (QScreen *)this, displayId); + + return QScreen::createSurface(widget); +} + +QWSWindowSurface* PvrEglScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("PvrEgl")) + return new PvrEglWindowSurface(holder); + + return QScreen::createSurface(key); +} + +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; +} + +bool PvrEglScreenSurfaceFunctions::createNativeWindow(QWidget *widget, EGLNativeWindowType *native) +{ + QWSWindowSurface *surface = + static_cast<QWSWindowSurface *>(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<PvrEglWindowSurface*>(surface); + *native = (EGLNativeWindowType)(nsurface->nativeDrawable()); + return true; +} + +// The PowerVR engine on the device needs to allocate about 2Mb of +// contiguous physical memory to manage drawing into a surface. +// +// The problem is that once Qtopia begins its startup sequence, +// it allocates enough memory to severely fragment the physical +// address space on the device. This leaves the PowerVR engine +// unable to allocate the necessary contiguous physical memory +// when an EGL surface is created. +// +// A solution to this is to pre-allocate a dummy surface early +// in the startup sequence before memory becomes fragmented, +// reserving it for any future EGL applications to use. +// +// However, the PowerVR engine has problems managing multiple +// surfaces concurrently, and so real EGL applications end up +// with unacceptably slow frame rates unless the dummy surface +// is destroyed while the real EGL applications are running. +// +// In summary, we need to try to ensure that there is always at +// least one EGL surface active at any given time to reserve the +// memory but destroy the temporary surface when a real surface +// is using the device. That is the purpose of PvrEglSurfaceHolder. + +PvrEglSurfaceHolder::PvrEglSurfaceHolder(QObject *parent) + : QObject(parent) +{ + numRealSurfaces = 0; + + PvrQwsRect rect; + rect.x = 0; + rect.y = 0; + rect.width = 16; + rect.height = 16; + tempSurface = pvrQwsCreateWindow(0, -1, &rect); + + dpy = EGL_NO_DISPLAY; + config = 0; + surface = EGL_NO_SURFACE; + + dpy = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY); + if (!eglInitialize(dpy, 0, 0)) { + qWarning("Could not initialize EGL display - are the drivers loaded?"); + dpy = EGL_NO_DISPLAY; + return; + } + + EGLint attribList[16]; + int temp = 0; + attribList[temp++] = EGL_LEVEL; // Framebuffer level 0 + attribList[temp++] = 0; + attribList[temp++] = EGL_SURFACE_TYPE; + attribList[temp++] = EGL_WINDOW_BIT; + attribList[temp++] = EGL_NONE; + + EGLint numConfigs = 0; + if (!eglChooseConfig(dpy, attribList, &config, 1, &numConfigs) || numConfigs != 1) { + qWarning("Could not find a matching a EGL configuration"); + eglTerminate(dpy); + dpy = EGL_NO_DISPLAY; + return; + } + + surface = eglCreateWindowSurface + (dpy, config, (EGLNativeWindowType)(-1), NULL); + if (surface == EGL_NO_SURFACE) + qWarning("Could not create the temporary EGL surface"); +} + +PvrEglSurfaceHolder::~PvrEglSurfaceHolder() +{ + if (surface != EGL_NO_SURFACE) + eglDestroySurface(dpy, surface); + if (dpy != EGL_NO_DISPLAY) + eglTerminate(dpy); + if (tempSurface) + pvrQwsDestroyDrawable(tempSurface); +} + +// Add a real EGL surface to the system. +void PvrEglSurfaceHolder::addSurface() +{ + ++numRealSurfaces; + if (numRealSurfaces == 1) { + // Destroy the temporary surface while some other application + // is making use of the EGL sub-system for 3D rendering. + if (surface != EGL_NO_SURFACE) { + eglDestroySurface(dpy, surface); + surface = EGL_NO_SURFACE; + } + } +} + +// Remove an actual EGL surface from the system. +void PvrEglSurfaceHolder::removeSurface() +{ + if (numRealSurfaces > 0) { + --numRealSurfaces; + if (numRealSurfaces == 0) { + // The last real EGL surface has been destroyed, so re-create + // the temporary surface. There is a race condition here in + // that Qtopia could allocate a lot of memory just after + // the real EGL surface is destroyed but before we could + // create the temporary surface again. + if (surface == EGL_NO_SURFACE && dpy != EGL_NO_DISPLAY) { + surface = eglCreateWindowSurface + (dpy, config, (EGLNativeWindowType)tempSurface, NULL); + if (surface == EGL_NO_SURFACE) + qWarning("Could not re-create the temporary EGL surface"); + } + } + } +} diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h new file mode 100644 index 0000000..ee27e36 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PVREGLSCREEN_H +#define PVREGLSCREEN_H + +#include <QScreen> +#include <QGLScreen> +#include "pvrqwsdrawable.h" + +class PvrEglScreenSurfaceFunctions : public QGLScreenSurfaceFunctions +{ +public: + PvrEglScreenSurfaceFunctions(QScreen *s, int screenNum) + : screen(s), displayId(screenNum) {} + + bool createNativeWindow(QWidget *widget, EGLNativeWindowType *native); + +private: + QScreen *screen; + int displayId; +}; + +class PvrEglSurfaceHolder : public QObject +{ + Q_OBJECT +public: + PvrEglSurfaceHolder(QObject *parent=0); + ~PvrEglSurfaceHolder(); + + void addSurface(); + void removeSurface(); + +private: + int numRealSurfaces; + PvrQwsDrawable *tempSurface; + EGLDisplay dpy; + EGLConfig config; + EGLSurface surface; +}; + +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; + +private: + void sync(); + void openTty(); + void closeTty(); + + int fd; + int ttyfd, oldKdMode; + PvrEglSurfaceHolder *holder; + QString ttyDevice; + bool doGraphicsMode; +}; + +#endif diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro new file mode 100644 index 0000000..691cd2d --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.pro @@ -0,0 +1,24 @@ +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 diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp new file mode 100644 index 0000000..e9748d6 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreenplugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pvreglscreen.h" + +#include <QScreenDriverPlugin> +#include <QStringList> + +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 0000000..e7f4987 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pvreglwindowsurface.h" +#include "pvreglscreen.h" +#include <QScreen> +#include <QDebug> +#include <QWSDisplay> + +PvrEglWindowSurface::PvrEglWindowSurface + (QWidget *widget, QScreen *screen, int screenNum) + : QWSGLWindowSurface(widget) +{ + setSurfaceFlags(QWSWindowSurface::Opaque); + + this->widget = widget; + this->screen = screen; + this->holder = 0; + 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(); + + // 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); +} + +PvrEglWindowSurface::PvrEglWindowSurface(PvrEglSurfaceHolder *holder) + : QWSGLWindowSurface() +{ + setSurfaceFlags(QWSWindowSurface::Opaque); + drawable = 0; + widget = 0; + screen = 0; + pdevice = 0; + + this->holder = holder; + holder->addSurface(); +} + +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); + + if (holder) + holder->removeSurface(); + 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(); + pvrQwsSetGeometry(drawable, &pvrRect); + } + 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(); + pvrQwsSetGeometry(drawable, &pvrRect); + } + 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); +} + +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), QImage::Format_RGB16); + } + } + return QImage(); +} + +QPaintDevice *PvrEglWindowSurface::paintDevice() +{ + // Return a dummy paint device because the widget itself + // cannot be painted to this way. + if (!pdevice) + pdevice = new QImage(50, 50, QImage::Format_RGB16); + return pdevice; +} + +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.numRects() == 1) { + QRect rect = region.boundingRect(); + PvrQwsRect pvrRect; + pvrRect.x = rect.x(); + pvrRect.y = rect.y(); + pvrRect.width = rect.width(); + pvrRect.height = rect.height(); + pvrQwsSetVisibleRegion(drawable, &pvrRect, 1); + if (!pvrQwsSwapBuffers(drawable, 1)) + screen->solidFill(QColor(0, 0, 0), region); + } else { + QVector<QRect> 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(); + } + pvrQwsSetVisibleRegion(drawable, pvrRects, rects.size()); + if (!pvrQwsSwapBuffers(drawable, 1)) + screen->solidFill(QColor(0, 0, 0), region); + delete [] pvrRects; + } +} diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h new file mode 100644 index 0000000..8bec796 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglwindowsurface.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PVREGLWINDOWSURFACE_H +#define PVREGLWINDOWSURFACE_H + +#include <private/qglwindowsurface_qws_p.h> +#include "pvrqwsdrawable.h" + +class QScreen; +class PvrEglSurfaceHolder; + +class PvrEglWindowSurface : public QWSGLWindowSurface +{ +public: + PvrEglWindowSurface(QWidget *widget, QScreen *screen, int screenNum); + PvrEglWindowSurface(PvrEglSurfaceHolder *holder); + ~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); + + QImage image() const; + QPaintDevice *paintDevice(); + + void setDirectRegion(const QRegion ®ion, int id); + + long nativeDrawable() const { return (long)widget; } + +private: + QWidget *widget; + PvrQwsDrawable *drawable; + QScreen *screen; + PvrEglSurfaceHolder *holder; + QPaintDevice *pdevice; +}; + +#endif diff --git a/src/plugins/gfxdrivers/qvfb/main.cpp b/src/plugins/gfxdrivers/qvfb/main.cpp new file mode 100644 index 0000000..e51e2af --- /dev/null +++ b/src/plugins/gfxdrivers/qvfb/main.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qscreendriverplugin_qws.h> +#include <qscreenvfb_qws.h> +#include <qstringlist.h> + +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 diff --git a/src/plugins/gfxdrivers/qvfb/qvfb.pro b/src/plugins/gfxdrivers/qvfb/qvfb.pro new file mode 100644 index 0000000..a0996e7 --- /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 0000000..34edce5 --- /dev/null +++ b/src/plugins/gfxdrivers/transformed/main.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qscreendriverplugin_qws.h> +#include <qscreentransformed_qws.h> +#include <qstringlist.h> + +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) +{ + if (driver.toLower() == "transformed") + return new QTransformedScreen(displayId); + + return 0; +} + +Q_EXPORT_STATIC_PLUGIN(GfxTransformedDriver) +Q_EXPORT_PLUGIN2(qgfxtransformed, GfxTransformedDriver) + +QT_END_NAMESPACE diff --git a/src/plugins/gfxdrivers/transformed/transformed.pro b/src/plugins/gfxdrivers/transformed/transformed.pro new file mode 100644 index 0000000..173f7e9 --- /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 0000000..fe10a67 --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/main.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qscreendriverplugin_qws.h> +#include <qscreenvnc_qws.h> +#include <qstringlist.h> + +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) +{ + if (driver.toLower() == "vnc") + return new QVNCScreen(displayId); + + return 0; +} + +Q_EXPORT_STATIC_PLUGIN(GfxVncDriver) +Q_EXPORT_PLUGIN2(qgfxvnc, GfxVncDriver) + +QT_END_NAMESPACE diff --git a/src/plugins/gfxdrivers/vnc/qscreenvnc_p.h b/src/plugins/gfxdrivers/vnc/qscreenvnc_p.h new file mode 100644 index 0000000..6b2b315 --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/qscreenvnc_p.h @@ -0,0 +1,522 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <QtCore/qvarlengtharray.h> +#include <QtCore/qsharedmemory.h> +#include <QtNetwork/qtcpsocket.h> +#include <QtNetwork/qtcpserver.h> + +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 T> +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; +}; + +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 SRC> class QRfbHextileEncoder; + +template <class SRC> +class QRfbSingleColorHextile +{ +public: + QRfbSingleColorHextile(QRfbHextileEncoder<SRC> *e) : encoder(e) {} + bool read(const uchar *data, int width, int height, int stride); + void write(QTcpSocket *socket) const; + +private: + QRfbHextileEncoder<SRC> *encoder; +}; + +template <class SRC> +class QRfbDualColorHextile +{ +public: + QRfbDualColorHextile(QRfbHextileEncoder<SRC> *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<SRC> *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 SRC> +class QRfbMultiColorHextile +{ +public: + QRfbMultiColorHextile(QRfbHextileEncoder<SRC> *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<quint8, maxRectsSize> rects; + + quint8 bpp; + quint8 numRects; + QRfbHextileEncoder<SRC> *encoder; +}; + +template <class SRC> +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<SRC> singleColorHextile; + QRfbDualColorHextile<SRC> dualColorHextile; + QRfbMultiColorHextile<SRC> multiColorHextile; + + SRC bg; + SRC fg; + bool newBg; + bool newFg; + + friend class QRfbSingleColorHextile<SRC>; + friend class QRfbDualColorHextile<SRC>; + friend class QRfbMultiColorHextile<SRC>; +}; + +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 0000000..b7f03ba --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.cpp @@ -0,0 +1,2297 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <QtCore/qtimer.h> +#include <QtCore/qregexp.h> +#include <QtGui/qwidget.h> +#include <QtGui/qpolygon.h> +#include <QtGui/qpainter.h> +#include <qdebug.h> +#include <private/qwindowsurface_qws_p.h> +#include <private/qwssignalhandler_p.h> +#include <private/qwidget_p.h> +#include <private/qdrawhelper_p.h> + +#include <stdlib.h> + +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<QRect> 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<QRect> 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) +{ +#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<uchar*>(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 }, + { 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) { + 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()) + 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<quint8>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_12 + case 12: + encoder = new QRfbHextileEncoder<qrgb444>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: + encoder = new QRfbHextileEncoder<qrgb555>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_16 + case 16: + encoder = new QRfbHextileEncoder<quint16>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_18 + case 18: + encoder = new QRfbHextileEncoder<qrgb666>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: + encoder = new QRfbHextileEncoder<qrgb888>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_32 + case 32: + encoder = new QRfbHextileEncoder<quint32>(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 (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 <class SRC> +bool QRfbSingleColorHextile<SRC>::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<const quint8*>(data); + if ((data8[0] & 0xf) != (data8[0] >> 4)) + return false; + width /= 2; + } // fallthrough + case 8: { + const quint8 *data8 = reinterpret_cast<const quint8*>(data); + if (data8[0] != data8[1]) + return false; + width /= 2; + } // fallthrough + case 12: + case 15: + case 16: { + const quint16 *data16 = reinterpret_cast<const quint16*>(data); + if (data16[0] != data16[1]) + return false; + width /= 2; + } // fallthrough + case 18: + case 24: + case 32: { + const quint32 *data32 = reinterpret_cast<const quint32*>(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<const SRC*>(data)[0]; + encoder->newBg |= (color != encoder->bg); + encoder->bg = color; + return true; +} + +template <class SRC> +void QRfbSingleColorHextile<SRC>::write(QTcpSocket *socket) const +{ + if (true || encoder->newBg) { + const int bpp = encoder->server->clientBytesPerPixel(); + const int padding = 3; + QVarLengthArray<char> buffer(padding + 1 + bpp); + buffer[padding] = 2; // BackgroundSpecified + encoder->server->convertPixels(buffer.data() + padding + 1, + reinterpret_cast<char*>(&encoder->bg), + 1); + socket->write(buffer.data() + padding, bpp + 1); +// encoder->newBg = false; + } else { + char subenc = 0; + socket->write(&subenc, 1); + } +} + +template <class SRC> +bool QRfbDualColorHextile<SRC>::read(const uchar *data, + int width, int height, int stride) +{ + const SRC *ptr = reinterpret_cast<const SRC*>(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<const SRC*>(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 <class SRC> +void QRfbDualColorHextile<SRC>::write(QTcpSocket *socket) const +{ + const int bpp = encoder->server->clientBytesPerPixel(); + const int padding = 3; + QVarLengthArray<char> 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 <class SRC> +void QRfbDualColorHextile<SRC>::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 <class SRC> +inline void QRfbMultiColorHextile<SRC>::setColor(SRC color) +{ + encoder->server->convertPixels(reinterpret_cast<char*>(rect(numRects)), + (const char*)&color, 1); +} + +template <class SRC> +inline bool QRfbMultiColorHextile<SRC>::beginRect() +{ + if ((rects.size() + bpp + 2) > maxRectsSize) + return false; + rects.resize(rects.size() + bpp + 2); + return true; +} + +template <class SRC> +inline void QRfbMultiColorHextile<SRC>::endRect() +{ + setHeight(numRects, 1); + ++numRects; +} + +template <class SRC> +bool QRfbMultiColorHextile<SRC>::read(const uchar *data, + int width, int height, int stride) +{ + const SRC *ptr = reinterpret_cast<const SRC*>(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 <class SRC> +void QRfbMultiColorHextile<SRC>::write(QTcpSocket *socket) const +{ + const int padding = 3; + QVarLengthArray<quint8> 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<char*>(buffer.data() + n), + reinterpret_cast<const char*>(&encoder->bg), + 1); + n += bpp; +// encoder->newBg = false; + } + + buffer[n] = numRects; + n += sizeof(numRects); + + socket->write(reinterpret_cast<const char*>(buffer.data() + padding), + n - padding); + socket->write(reinterpret_cast<const char*>(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<const quint32*>(src); + quint32 *dst32 = reinterpret_cast<quint32*>(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<const quint16*>(src); + dst32[count - 1] = 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<const qrgb444*>(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<const qrgb555*>(src)); + r = qRed(p); + g = qGreen(p); + b = qBlue(p); + src += sizeof(qrgb555); + break; + } +#endif + case 16: { + quint16 p = *reinterpret_cast<const quint16*>(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<const qrgb666*>(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<const qrgb888*>(src)); + r = qRed(p); + g = qGreen(p); + b = qBlue(p); + src += sizeof(qrgb888); + break; + } +#endif + case 32: { + quint32 p = *reinterpret_cast<const quint32*>(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 <class T> +void QVNCDirtyMapOptimized<T>::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 <class SRC> +QRfbHextileEncoder<SRC>::QRfbHextileEncoder(QVNCServer *s) + : QRfbEncoder(s), + singleColorHextile(this), dualColorHextile(this), multiColorHextile(this) +{ +} + +/* + \internal + Send dirty rects using hextile encoding. +*/ +template <class SRC> +void QRfbHextileEncoder<SRC>::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<QRect> 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()) + 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(QLatin1String("vnc:")).size()); + else if (dspec.compare(QLatin1String("vnc"), Qt::CaseInsensitive) == 0) + dspec = QString(); + + const QString displayIdSpec = QString(QLatin1String(" :%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()); + 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); + } + + 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<quint32>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: + d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb888>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_18 + case 18: + d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb666>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_16 + case 16: + d_ptr->dirty = new QVNCDirtyMapOptimized<quint16>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: + d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb555>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_12 + case 12: + d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb444>(this); + break; +#endif +#ifdef QT_QWS_DEPTH_8 + case 8: + d_ptr->dirty = new QVNCDirtyMapOptimized<quint8>(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; + +#ifdef QT_BUILD_INTERNAL + if (qgetenv("QT_VNC_NO_DISABLEPAINTING").toInt() <= 0) +#endif + // No need to do painting while there's no clients attached + 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 0000000..70ff067 --- /dev/null +++ b/src/plugins/gfxdrivers/vnc/qscreenvnc_qws.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCREENVNC_QWS_H +#define QSCREENVNC_QWS_H + +#include <QtGui/qscreenproxy_qws.h> + +#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 0000000..31da2f4 --- /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 |