/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qdirectfbscreen.h"
#include "qdirectfbwindowsurface.h"
#include "qdirectfbpixmap.h"
#include "qdirectfbmouse.h"
#include "qdirectfbkeyboard.h"
#include <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>

#ifndef QT_NO_QWS_DIRECTFB

QT_BEGIN_NAMESPACE

class QDirectFBScreenPrivate : public QObject, public QWSGraphicsSystem
{
    Q_OBJECT
public:
    QDirectFBScreenPrivate(QDirectFBScreen *qptr);
    ~QDirectFBScreenPrivate();

    void setFlipFlags(const QStringList &args);
    QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
public slots:
#ifdef QT_DIRECTFB_WM
    void onWindowEvent(QWSWindow *window, QWSServer::WindowEvent event);
#endif
public:
    IDirectFB *dfb;
    DFBSurfaceFlipFlags flipFlags;
    QDirectFBScreen::DirectFBFlags directFBFlags;
    QImage::Format alphaPixmapFormat;
    IDirectFBScreen *dfbScreen;
#ifdef QT_NO_DIRECTFB_WM
    IDirectFBSurface *primarySurface;
    QColor backgroundColor;
#endif
#ifndef QT_NO_DIRECTFB_LAYER
    IDirectFBDisplayLayer *dfbLayer;
#endif
    QSet<IDirectFBSurface*> allocatedSurfaces;

#ifndef QT_NO_DIRECTFB_MOUSE
    QDirectFBMouseHandler *mouse;
#endif
#ifndef QT_NO_DIRECTFB_KEYBOARD
    QDirectFBKeyboardHandler *keyboard;
#endif
#if defined QT_DIRECTFB_IMAGEPROVIDER && defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
    IDirectFBImageProvider *imageProvider;
#endif
    IDirectFBSurface *cursorSurface;
    qint64 cursorImageKey;

    QDirectFBScreen *q;
    static QDirectFBScreen *instance;
};

QDirectFBScreen *QDirectFBScreenPrivate::instance = 0;

QDirectFBScreenPrivate::QDirectFBScreenPrivate(QDirectFBScreen *qptr)
    : QWSGraphicsSystem(qptr), dfb(0), flipFlags(DSFLIP_NONE),
      directFBFlags(QDirectFBScreen::NoFlags), alphaPixmapFormat(QImage::Format_Invalid),
      dfbScreen(0)
#ifdef QT_NO_DIRECTFB_WM
    , primarySurface(0)
#endif
#ifndef QT_NO_DIRECTFB_LAYER
    , dfbLayer(0)
#endif
#ifndef QT_NO_DIRECTFB_MOUSE
    , mouse(0)
#endif
#ifndef QT_NO_DIRECTFB_KEYBOARD
    , keyboard(0)
#endif
#if defined QT_DIRECTFB_IMAGEPROVIDER && defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
    , imageProvider(0)
#endif
    , cursorSurface(0)
    , cursorImageKey(0)
    , q(qptr)
{
#ifndef QT_NO_QWS_SIGNALHANDLER
    QWSSignalHandler::instance()->addObject(this);
#endif
#ifdef QT_DIRECTFB_WM
    connect(QWSServer::instance(), SIGNAL(windowEvent(QWSWindow*,QWSServer::WindowEvent)),
            this, SLOT(onWindowEvent(QWSWindow*,QWSServer::WindowEvent)));
#endif
}

QDirectFBScreenPrivate::~QDirectFBScreenPrivate()
{
#ifndef QT_NO_DIRECTFB_MOUSE
    delete mouse;
#endif
#ifndef QT_NO_DIRECTFB_KEYBOARD
    delete keyboard;
#endif
#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
    if (imageProvider)
        imageProvider->Release(imageProvider);
#endif

    for (QSet<IDirectFBSurface*>::const_iterator it = allocatedSurfaces.begin(); it != allocatedSurfaces.end(); ++it) {
        (*it)->Release(*it);
    }

#ifdef QT_NO_DIRECTFB_WM
    if (primarySurface)
        primarySurface->Release(primarySurface);
#endif

#ifndef QT_NO_DIRECTFB_LAYER
    if (dfbLayer)
        dfbLayer->Release(dfbLayer);
#endif

    if (dfbScreen)
        dfbScreen->Release(dfbScreen);

    if (dfb)
        dfb->Release(dfb);
}

IDirectFBSurface *QDirectFBScreen::createDFBSurface(const QImage &image, QImage::Format format, SurfaceCreationOptions options, DFBResult *resultPtr)
{
    if (image.isNull()) // assert?
        return 0;

    if (QDirectFBScreen::getSurfacePixelFormat(format) == DSPF_UNKNOWN) {
        format = QDirectFBPixmapData::hasAlphaChannel(image) ? d_ptr->alphaPixmapFormat : pixelFormat();
    }
    if (image.format() != format) {
        return createDFBSurface(image.convertToFormat(format), format, options | NoPreallocated, resultPtr);
    }

    DFBSurfaceDescription description;
    memset(&description, 0, sizeof(DFBSurfaceDescription));
    description.width = image.width();
    description.height = image.height();
    description.flags = DSDESC_WIDTH|DSDESC_HEIGHT|DSDESC_PIXELFORMAT;
    initSurfaceDescriptionPixelFormat(&description, format);
    bool doMemCopy = true;
#ifdef QT_DIRECTFB_PREALLOCATED
    if (!(options & NoPreallocated)) {
        doMemCopy = false;
        description.flags |= DSDESC_PREALLOCATED;
        description.preallocated[0].data = const_cast<uchar*>(image.bits());
        description.preallocated[0].pitch = image.bytesPerLine();
        description.preallocated[1].data = 0;
        description.preallocated[1].pitch = 0;
    }
#endif
    DFBResult result;
    IDirectFBSurface *surface = createDFBSurface(description, options, &result);
    if (resultPtr)
        *resultPtr = result;
    if (!surface) {
        DirectFBError("Couldn't create surface createDFBSurface(QImage, QImage::Format, SurfaceCreationOptions)", result);
        return 0;
    }
    if (doMemCopy) {
        int bplDFB;
        uchar *mem = QDirectFBScreen::lockSurface(surface, DSLF_WRITE, &bplDFB);
        if (mem) {
            const int height = image.height();
            const int bplQt = image.bytesPerLine();
            if (bplQt == bplDFB && bplQt == (image.width() * image.depth() / 8)) {
                memcpy(mem, image.bits(), image.byteCount());
            } else {
                for (int i=0; i<height; ++i) {
                    memcpy(mem, image.scanLine(i), bplQt);
                    mem += bplDFB;
                }
            }
            surface->Unlock(surface);
        }
    }
#ifdef QT_DIRECTFB_PALETTE
    if (image.colorCount() != 0 && surface)
        QDirectFBScreen::setSurfaceColorTable(surface, image);
#endif
    return surface;
}

IDirectFBSurface *QDirectFBScreen::copyDFBSurface(IDirectFBSurface *src,
                                                  QImage::Format format,
                                                  SurfaceCreationOptions options,
                                                  DFBResult *result)
{
    Q_ASSERT(src);
    QSize size;
    src->GetSize(src, &size.rwidth(), &size.rheight());
    IDirectFBSurface *surface = createDFBSurface(size, format, options, result);
    DFBSurfaceBlittingFlags flags = QDirectFBScreen::hasAlphaChannel(surface)
                                    ? DSBLIT_BLEND_ALPHACHANNEL
                                    : DSBLIT_NOFX;
    if (flags & DSBLIT_BLEND_ALPHACHANNEL)
        surface->Clear(surface, 0, 0, 0, 0);

    surface->SetBlittingFlags(surface, flags);
    surface->Blit(surface, src, 0, 0, 0);
#if (Q_DIRECTFB_VERSION >= 0x010000)
    surface->ReleaseSource(surface);
#endif
    return surface;
}

IDirectFBSurface *QDirectFBScreen::createDFBSurface(const QSize &size,
                                                    QImage::Format format,
                                                    SurfaceCreationOptions options,
                                                    DFBResult *result)
{
    DFBSurfaceDescription desc;
    memset(&desc, 0, sizeof(DFBSurfaceDescription));
    desc.flags |= DSDESC_WIDTH|DSDESC_HEIGHT;
    if (!QDirectFBScreen::initSurfaceDescriptionPixelFormat(&desc, format))
        return 0;
    desc.width = size.width();
    desc.height = size.height();
    return createDFBSurface(desc, options, result);
}

IDirectFBSurface *QDirectFBScreen::createDFBSurface(DFBSurfaceDescription desc, SurfaceCreationOptions options, DFBResult *resultPtr)
{
    DFBResult tmp;
    DFBResult &result = (resultPtr ? *resultPtr : tmp);
    result = DFB_OK;
    IDirectFBSurface *newSurface = 0;

    if (!d_ptr->dfb) {
        qWarning("QDirectFBScreen::createDFBSurface() - not connected");
        return 0;
    }

    if (d_ptr->directFBFlags & VideoOnly
        && !(desc.flags & DSDESC_PREALLOCATED)
        && (!(desc.flags & DSDESC_CAPS) || !(desc.caps & DSCAPS_SYSTEMONLY))) {
        // Add the video only capability. This means the surface will be created in video ram
        if (!(desc.flags & DSDESC_CAPS)) {
            desc.caps = DSCAPS_VIDEOONLY;
            desc.flags |= DSDESC_CAPS;
        } else {
            desc.caps |= DSCAPS_VIDEOONLY;
        }
        result = d_ptr->dfb->CreateSurface(d_ptr->dfb, &desc, &newSurface);
        if (result != DFB_OK
#ifdef QT_NO_DEBUG
            && (desc.flags & DSDESC_CAPS) && (desc.caps & DSCAPS_PRIMARY)
#endif
            ) {
            qWarning("QDirectFBScreen::createDFBSurface() Failed to create surface in video memory!\n"
                     "   Flags %0x Caps %0x width %d height %d pixelformat %0x %d preallocated %p %d\n%s",
                     desc.flags, desc.caps, desc.width, desc.height,
                     desc.pixelformat, DFB_PIXELFORMAT_INDEX(desc.pixelformat),
                     desc.preallocated[0].data, desc.preallocated[0].pitch,
                     DirectFBErrorString(result));
        }
        desc.caps &= ~DSCAPS_VIDEOONLY;
    }

    if (d_ptr->directFBFlags & SystemOnly)
        desc.caps |= DSCAPS_SYSTEMONLY;

    if (!newSurface)
        result = d_ptr->dfb->CreateSurface(d_ptr->dfb, &desc, &newSurface);

    if (result != DFB_OK) {
        qWarning("QDirectFBScreen::createDFBSurface() Failed!\n"
                 "   Flags %0x Caps %0x width %d height %d pixelformat %0x %d preallocated %p %d\n%s",
                 desc.flags, desc.caps, desc.width, desc.height,
                 desc.pixelformat, DFB_PIXELFORMAT_INDEX(desc.pixelformat),
                 desc.preallocated[0].data, desc.preallocated[0].pitch,
                 DirectFBErrorString(result));
        return 0;
    }

    Q_ASSERT(newSurface);

    if (options & TrackSurface) {
        d_ptr->allocatedSurfaces.insert(newSurface);
    }

    return newSurface;
}

#ifdef QT_DIRECTFB_SUBSURFACE
IDirectFBSurface *QDirectFBScreen::getSubSurface(IDirectFBSurface *surface,
                                                 const QRect &rect,
                                                 SurfaceCreationOptions options,
                                                 DFBResult *resultPtr)
{
    Q_ASSERT(!(options & NoPreallocated));
    Q_ASSERT(surface);
    DFBResult res;
    DFBResult &result = (resultPtr ? *resultPtr : res);
    IDirectFBSurface *subSurface = 0;
    if (rect.isNull()) {
        result = surface->GetSubSurface(surface, 0, &subSurface);
    } else {
        const DFBRectangle subRect = { rect.x(), rect.y(), rect.width(), rect.height() };
        result = surface->GetSubSurface(surface, &subRect, &subSurface);
    }
    if (result != DFB_OK) {
        DirectFBError("Can't get sub surface", result);
    } else if (options & TrackSurface) {
        d_ptr->allocatedSurfaces.insert(subSurface);
    }
    return subSurface;
}
#endif


void QDirectFBScreen::releaseDFBSurface(IDirectFBSurface *surface)
{
    Q_ASSERT(QDirectFBScreen::instance());
    Q_ASSERT(surface);
    surface->Release(surface);
    if (!d_ptr->allocatedSurfaces.remove(surface))
        qWarning("QDirectFBScreen::releaseDFBSurface() - %p not in list", surface);

    //qDebug("Released surface at %p. New count = %d", surface, d_ptr->allocatedSurfaces.count());
}

QDirectFBScreen::DirectFBFlags QDirectFBScreen::directFBFlags() const
{
    return d_ptr->directFBFlags;
}

IDirectFB *QDirectFBScreen::dfb()
{
    return d_ptr->dfb;
}

#ifdef QT_NO_DIRECTFB_WM
IDirectFBSurface *QDirectFBScreen::primarySurface()
{
    return d_ptr->primarySurface;
}
#endif

#ifndef QT_NO_DIRECTFB_LAYER
IDirectFBDisplayLayer *QDirectFBScreen::dfbDisplayLayer()
{
    return d_ptr->dfbLayer;
}
#endif

DFBSurfacePixelFormat QDirectFBScreen::getSurfacePixelFormat(QImage::Format format)
{
    switch (format) {
#ifndef QT_NO_DIRECTFB_PALETTE
    case QImage::Format_Indexed8:
        return DSPF_LUT8;
#endif
    case QImage::Format_RGB888:
        return DSPF_RGB24;
    case QImage::Format_ARGB4444_Premultiplied:
        return DSPF_ARGB4444;
#if (Q_DIRECTFB_VERSION >= 0x010100)
    case QImage::Format_RGB444:
        return DSPF_RGB444;
    case QImage::Format_RGB555:
        return DSPF_RGB555;
#endif
    case QImage::Format_RGB16:
        return DSPF_RGB16;
#if (Q_DIRECTFB_VERSION >= 0x010000)
    case QImage::Format_ARGB6666_Premultiplied:
        return DSPF_ARGB6666;
    case QImage::Format_RGB666:
        return DSPF_RGB18;
#endif
    case QImage::Format_RGB32:
        return DSPF_RGB32;
    case QImage::Format_ARGB32_Premultiplied:
    case QImage::Format_ARGB32:
        return DSPF_ARGB;
    default:
        return DSPF_UNKNOWN;
    };
}

QImage::Format QDirectFBScreen::getImageFormat(IDirectFBSurface *surface)
{
    DFBSurfacePixelFormat format;
    surface->GetPixelFormat(surface, &format);

    switch (format) {
    case DSPF_LUT8:
        return QImage::Format_Indexed8;
    case DSPF_RGB24:
        return QImage::Format_RGB888;
    case DSPF_ARGB4444:
        return QImage::Format_ARGB4444_Premultiplied;
#if (Q_DIRECTFB_VERSION >= 0x010100)
    case DSPF_RGB444:
        return QImage::Format_RGB444;
    case DSPF_RGB555:
#endif
    case DSPF_ARGB1555:
        return QImage::Format_RGB555;
    case DSPF_RGB16:
        return QImage::Format_RGB16;
#if (Q_DIRECTFB_VERSION >= 0x010000)
    case DSPF_ARGB6666:
        return QImage::Format_ARGB6666_Premultiplied;
    case DSPF_RGB18:
        return QImage::Format_RGB666;
#endif
    case DSPF_RGB32:
        return QImage::Format_RGB32;
    case DSPF_ARGB: {
        DFBSurfaceCapabilities caps;
        const DFBResult result = surface->GetCapabilities(surface, &caps);
        Q_ASSERT(result == DFB_OK);
        Q_UNUSED(result);
        return (caps & DSCAPS_PREMULTIPLIED
                ? QImage::Format_ARGB32_Premultiplied
                : QImage::Format_ARGB32); }
    default:
        break;
    }
    return QImage::Format_Invalid;
}

DFBSurfaceDescription QDirectFBScreen::getSurfaceDescription(const uint *buffer,
                                                             int length)
{
    DFBSurfaceDescription description;
    memset(&description, 0, sizeof(DFBSurfaceDescription));

    description.flags = DSDESC_CAPS|DSDESC_WIDTH|DSDESC_HEIGHT|DSDESC_PIXELFORMAT|DSDESC_PREALLOCATED;
    description.caps = DSCAPS_PREMULTIPLIED;
    description.width = length;
    description.height = 1;
    description.pixelformat = DSPF_ARGB;
    description.preallocated[0].data = (void*)buffer;
    description.preallocated[0].pitch = length * sizeof(uint);
    description.preallocated[1].data = 0;
    description.preallocated[1].pitch = 0;
    return description;
}

#ifndef QT_NO_DIRECTFB_PALETTE
void QDirectFBScreen::setSurfaceColorTable(IDirectFBSurface *surface,
                                           const QImage &image)
{
    if (!surface)
        return;

    const int numColors = image.colorCount();
    if (numColors == 0)
        return;

    QVarLengthArray<DFBColor, 256> colors(numColors);
    for (int i = 0; i < numColors; ++i) {
        QRgb c = image.color(i);
        colors[i].a = qAlpha(c);
        colors[i].r = qRed(c);
        colors[i].g = qGreen(c);
        colors[i].b = qBlue(c);
    }

    IDirectFBPalette *palette;
    DFBResult result;
    result = surface->GetPalette(surface, &palette);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreen::setSurfaceColorTable GetPalette",
                      result);
        return;
    }
    result = palette->SetEntries(palette, colors.data(), numColors, 0);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreen::setSurfaceColorTable SetEntries",
                      result);
    }
    palette->Release(palette);
}

#endif // QT_NO_DIRECTFB_PALETTE

#if defined QT_DIRECTFB_CURSOR
class Q_GUI_EXPORT QDirectFBScreenCursor : public QScreenCursor
{
public:
    QDirectFBScreenCursor();
    virtual void set(const QImage &image, int hotx, int hoty);
    virtual void move(int x, int y);
    virtual void show();
    virtual void hide();
private:
#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR
    ~QDirectFBScreenCursor();
    bool createWindow();
    IDirectFBWindow *window;
#endif
    IDirectFBDisplayLayer *layer;
};

QDirectFBScreenCursor::QDirectFBScreenCursor()
{
    IDirectFB *fb = QDirectFBScreen::instance()->dfb();
    if (!fb)
        qFatal("QDirectFBScreenCursor: DirectFB not initialized");

    layer = QDirectFBScreen::instance()->dfbDisplayLayer();
    Q_ASSERT(layer);

    enable = false;
    hwaccel = true;
    supportsAlpha = true;
#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR
    window = 0;
    DFBResult result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::hide: "
                      "Unable to set cooperative level", result);
    }
    result = layer->SetCursorOpacity(layer, 0);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::hide: "
                      "Unable to set cursor opacity", result);
    }

    result = layer->SetCooperativeLevel(layer, DLSCL_SHARED);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::hide: "
                      "Unable to set cooperative level", result);
    }
#endif
}

#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR
QDirectFBScreenCursor::~QDirectFBScreenCursor()
{
    if (window) {
        window->Release(window);
        window = 0;
    }
}

bool QDirectFBScreenCursor::createWindow()
{
    Q_ASSERT(!window);
    Q_ASSERT(!cursor.isNull());
    DFBWindowDescription description;
    memset(&description, 0, sizeof(DFBWindowDescription));
    description.flags = DWDESC_POSX|DWDESC_POSY|DWDESC_WIDTH|DWDESC_HEIGHT|DWDESC_CAPS|DWDESC_PIXELFORMAT|DWDESC_SURFACE_CAPS;
    description.width = cursor.width();
    description.height = cursor.height();
    description.posx = pos.x() - hotspot.x();
    description.posy = pos.y() - hotspot.y();
#if (Q_DIRECTFB_VERSION >= 0x010100)
    description.flags |= DWDESC_OPTIONS;
    description.options = DWOP_GHOST|DWOP_ALPHACHANNEL;
#endif
    description.caps = DWCAPS_NODECORATION|DWCAPS_DOUBLEBUFFER;
    const QImage::Format format = QDirectFBScreen::instance()->alphaPixmapFormat();
    description.pixelformat = QDirectFBScreen::getSurfacePixelFormat(format);
    if (QDirectFBScreen::isPremultiplied(format))
        description.surface_caps = DSCAPS_PREMULTIPLIED;

    DFBResult result = layer->CreateWindow(layer, &description, &window);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::createWindow: Unable to create window", result);
        return false;
    }
    result = window->SetOpacity(window, 255);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::createWindow: Unable to set opacity ", result);
        return false;
    }

    result = window->SetStackingClass(window, DWSC_UPPER);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::createWindow: Unable to set stacking class ", result);
        return false;
    }

    result = window->RaiseToTop(window);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreenCursor::createWindow: Unable to raise window ", result);
        return false;
    }

    return true;
}
#endif

void QDirectFBScreenCursor::move(int x, int y)
{
    pos = QPoint(x, y);
#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR
    if (window) {
        const QPoint p = pos - hotspot;
        DFBResult result = window->MoveTo(window, p.x(), p.y());
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::move: Unable to move window", result);
        }
    }
#else
    layer->WarpCursor(layer, x, y);
#endif
}

void QDirectFBScreenCursor::hide()
{
    if (enable) {
        enable = false;
        DFBResult result;
#ifndef QT_DIRECTFB_WINDOW_AS_CURSOR
        result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::hide: "
                          "Unable to set cooperative level", result);
        }
        result = layer->SetCursorOpacity(layer, 0);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::hide: "
                          "Unable to set cursor opacity", result);
        }
        result = layer->SetCooperativeLevel(layer, DLSCL_SHARED);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::hide: "
                          "Unable to set cooperative level", result);
        }
#else
        if (window) {
            result = window->SetOpacity(window, 0);
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreenCursor::hide: "
                              "Unable to set window opacity", result);
            }
        }
#endif
    }
}

void QDirectFBScreenCursor::show()
{
    if (!enable) {
        enable = true;
        DFBResult result;
        result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::show: "
                          "Unable to set cooperative level", result);
        }
        result = layer->SetCursorOpacity(layer,
#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR
                                         0
#else
                                         255
#endif
            );
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::show: "
                          "Unable to set cursor shape", result);
        }
        result = layer->SetCooperativeLevel(layer, DLSCL_SHARED);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::show: "
                          "Unable to set cooperative level", result);
        }
#ifdef QT_DIRECTFB_WINDOW_AS_CURSOR
        if (window) {
            DFBResult result = window->SetOpacity(window, 255);
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreenCursor::show: "
                              "Unable to set window opacity", result);
            }
        }
#endif
    }
}

void QDirectFBScreenCursor::set(const QImage &image, int hotx, int hoty)
{
    QDirectFBScreen *screen = QDirectFBScreen::instance();
    if (!screen)
        return;

    if (image.isNull()) {
        cursor = QImage();
        hide();
    } else {
        cursor = image.convertToFormat(screen->alphaPixmapFormat());
        size = cursor.size();
        hotspot = QPoint(hotx, hoty);
        DFBResult result = DFB_OK;
        IDirectFBSurface *surface = screen->createDFBSurface(cursor, screen->alphaPixmapFormat(),
                                                             QDirectFBScreen::DontTrackSurface, &result);
        if (!surface) {
            DirectFBError("QDirectFBScreenCursor::set: Unable to create surface", result);
            return;
        }
#ifndef QT_DIRECTFB_WINDOW_AS_CURSOR
        result = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::show: "
                          "Unable to set cooperative level", result);
        }
        result = layer->SetCursorShape(layer, surface, hotx, hoty);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::show: "
                          "Unable to set cursor shape", result);
        }
        result = layer->SetCooperativeLevel(layer, DLSCL_SHARED);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::show: "
                          "Unable to set cooperative level", result);
        }
#else
        if (window || createWindow()) {
            QSize windowSize;
            result = window->GetSize(window, &windowSize.rwidth(), &windowSize.rheight());
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreenCursor::set: "
                              "Unable to get window size", result);
            }
            result = window->Resize(window, size.width(), size.height());
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreenCursor::set: Unable to resize window", result);
            }

            IDirectFBSurface *windowSurface;
            result = window->GetSurface(window, &windowSurface);
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreenCursor::set: Unable to get window surface", result);
            } else {
                result = windowSurface->Clear(windowSurface, 0, 0, 0, 0);
                if (result != DFB_OK) {
                    DirectFBError("QDirectFBScreenCursor::set: Unable to clear surface", result);
                }

                result = windowSurface->Blit(windowSurface, surface, 0, 0, 0);
                if (result != DFB_OK) {
                    DirectFBError("QDirectFBScreenCursor::set: Unable to blit to surface", result);
                }
            }
            result = windowSurface->Flip(windowSurface, 0, DSFLIP_NONE);
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreenCursor::set: Unable to flip window", result);
            }

            windowSurface->Release(windowSurface);
        }
#endif
        surface->Release(surface);
        show();
    }

}
#endif // QT_DIRECTFB_CURSOR

QDirectFBScreen::QDirectFBScreen(int display_id)
    : QScreen(display_id, DirectFBClass), d_ptr(new QDirectFBScreenPrivate(this))
{
    QDirectFBScreenPrivate::instance = this;
}

QDirectFBScreen::~QDirectFBScreen()
{
    if (QDirectFBScreenPrivate::instance == this)
        QDirectFBScreenPrivate::instance = 0;
    delete d_ptr;
}

QDirectFBScreen *QDirectFBScreen::instance()
{
    return QDirectFBScreenPrivate::instance;
}

int QDirectFBScreen::depth(DFBSurfacePixelFormat format)
{
    switch (format) {
    case DSPF_A1:
        return 1;
    case DSPF_A8:
    case DSPF_RGB332:
    case DSPF_LUT8:
    case DSPF_ALUT44:
        return 8;
    case DSPF_I420:
    case DSPF_YV12:
    case DSPF_NV12:
    case DSPF_NV21:
#if (Q_DIRECTFB_VERSION >= 0x010100)
    case DSPF_RGB444:
#endif
        return 12;
#if (Q_DIRECTFB_VERSION >= 0x010100)
    case DSPF_RGB555:
        return 15;
#endif
    case DSPF_ARGB1555:
    case DSPF_RGB16:
    case DSPF_YUY2:
    case DSPF_UYVY:
    case DSPF_NV16:
    case DSPF_ARGB2554:
    case DSPF_ARGB4444:
        return 16;
    case DSPF_RGB24:
        return 24;
    case DSPF_RGB32:
    case DSPF_ARGB:
    case DSPF_AiRGB:
        return 32;
    case DSPF_UNKNOWN:
    default:
        return 0;
    };
    return 0;
}

int QDirectFBScreen::depth(QImage::Format format)
{
    int depth = 0;
    switch(format) {
    case QImage::Format_Invalid:
    case QImage::NImageFormats:
        Q_ASSERT(false);
    case QImage::Format_Mono:
    case QImage::Format_MonoLSB:
        depth = 1;
        break;
    case QImage::Format_Indexed8:
        depth = 8;
        break;
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        depth = 32;
        break;
    case QImage::Format_RGB555:
    case QImage::Format_RGB16:
    case QImage::Format_RGB444:
    case QImage::Format_ARGB4444_Premultiplied:
        depth = 16;
        break;
    case QImage::Format_RGB666:
    case QImage::Format_ARGB6666_Premultiplied:
    case QImage::Format_ARGB8565_Premultiplied:
    case QImage::Format_ARGB8555_Premultiplied:
    case QImage::Format_RGB888:
        depth = 24;
        break;
    }
    return depth;
}

void QDirectFBScreenPrivate::setFlipFlags(const QStringList &args)
{
    QRegExp flipRegexp(QLatin1String("^flip=([\\w,]*)$"));
    int index = args.indexOf(flipRegexp);
    if (index >= 0) {
        const QStringList flips = flipRegexp.cap(1).split(QLatin1Char(','),
                                                          QString::SkipEmptyParts);
        flipFlags = DSFLIP_NONE;
        foreach(const QString &flip, flips) {
            if (flip == QLatin1String("wait"))
                flipFlags |= DSFLIP_WAIT;
            else if (flip == QLatin1String("blit"))
                flipFlags |= DSFLIP_BLIT;
            else if (flip == QLatin1String("onsync"))
                flipFlags |= DSFLIP_ONSYNC;
            else if (flip == QLatin1String("pipeline"))
                flipFlags |= DSFLIP_PIPELINE;
            else
                qWarning("QDirectFBScreen: Unknown flip argument: %s",
                         qPrintable(flip));
        }
    } else {
        flipFlags = DSFLIP_BLIT;
    }
}

#ifdef QT_DIRECTFB_WM
void QDirectFBScreenPrivate::onWindowEvent(QWSWindow *window, QWSServer::WindowEvent event)
{
    if (event == QWSServer::Raise) {
        QWSWindowSurface *windowSurface = window->windowSurface();
        if (windowSurface && windowSurface->key() == QLatin1String("directfb")) {
            static_cast<QDirectFBWindowSurface*>(windowSurface)->raise();
        }
    }
}
#endif

QPixmapData *QDirectFBScreenPrivate::createPixmapData(QPixmapData::PixelType type) const
{
    if (type == QPixmapData::BitmapType)
        return QWSGraphicsSystem::createPixmapData(type);

    return new QDirectFBPixmapData(q, type);
}

#if (Q_DIRECTFB_VERSION >= 0x000923)
#ifdef QT_NO_DEBUG
struct FlagDescription;
static const FlagDescription *accelerationDescriptions = 0;
static const FlagDescription *blitDescriptions = 0;
static const FlagDescription *drawDescriptions = 0;
#else
struct FlagDescription {
    const char *name;
    uint flag;
};

static const FlagDescription accelerationDescriptions[] = {
    { "DFXL_NONE", DFXL_NONE },
    { "DFXL_FILLRECTANGLE", DFXL_FILLRECTANGLE },
    { "DFXL_DRAWRECTANGLE", DFXL_DRAWRECTANGLE },
    { "DFXL_DRAWLINE", DFXL_DRAWLINE },
    { "DFXL_FILLTRIANGLE", DFXL_FILLTRIANGLE },
    { "DFXL_BLIT", DFXL_BLIT },
    { "DFXL_STRETCHBLIT", DFXL_STRETCHBLIT },
    { "DFXL_TEXTRIANGLES", DFXL_TEXTRIANGLES },
    { "DFXL_DRAWSTRING", DFXL_DRAWSTRING },
    { 0, 0 }
};

static const FlagDescription blitDescriptions[] = {
    { "DSBLIT_NOFX", DSBLIT_NOFX },
    { "DSBLIT_BLEND_ALPHACHANNEL", DSBLIT_BLEND_ALPHACHANNEL },
    { "DSBLIT_BLEND_COLORALPHA", DSBLIT_BLEND_COLORALPHA },
    { "DSBLIT_COLORIZE", DSBLIT_COLORIZE },
    { "DSBLIT_SRC_COLORKEY", DSBLIT_SRC_COLORKEY },
    { "DSBLIT_DST_COLORKEY", DSBLIT_DST_COLORKEY },
    { "DSBLIT_SRC_PREMULTIPLY", DSBLIT_SRC_PREMULTIPLY },
    { "DSBLIT_DST_PREMULTIPLY", DSBLIT_DST_PREMULTIPLY },
    { "DSBLIT_DEMULTIPLY", DSBLIT_DEMULTIPLY },
    { "DSBLIT_DEINTERLACE", DSBLIT_DEINTERLACE },
#if (Q_DIRECTFB_VERSION >= 0x000923)
    { "DSBLIT_SRC_PREMULTCOLOR", DSBLIT_SRC_PREMULTCOLOR },
    { "DSBLIT_XOR", DSBLIT_XOR },
#endif
#if (Q_DIRECTFB_VERSION >= 0x010000)
    { "DSBLIT_INDEX_TRANSLATION", DSBLIT_INDEX_TRANSLATION },
#endif
    { 0, 0 }
};

static const FlagDescription drawDescriptions[] = {
    { "DSDRAW_NOFX", DSDRAW_NOFX },
    { "DSDRAW_BLEND", DSDRAW_BLEND },
    { "DSDRAW_DST_COLORKEY", DSDRAW_DST_COLORKEY },
    { "DSDRAW_SRC_PREMULTIPLY", DSDRAW_SRC_PREMULTIPLY },
    { "DSDRAW_DST_PREMULTIPLY", DSDRAW_DST_PREMULTIPLY },
    { "DSDRAW_DEMULTIPLY", DSDRAW_DEMULTIPLY },
    { "DSDRAW_XOR", DSDRAW_XOR },
    { 0, 0 }
};
#endif

static const QByteArray flagDescriptions(uint mask, const FlagDescription *flags)
{
#ifdef QT_NO_DEBUG
    Q_UNUSED(mask);
    Q_UNUSED(flags);
    return QByteArray("");
#else
    if (!mask)
        return flags[0].name;

    QStringList list;
    for (int i=1; flags[i].name; ++i) {
        if (mask & flags[i].flag) {
            list.append(QString::fromLatin1(flags[i].name));
        }
    }
    Q_ASSERT(!list.isEmpty());
    return (QLatin1Char(' ') + list.join(QLatin1String("|"))).toLatin1();
#endif
}
static void printDirectFBInfo(IDirectFB *fb, IDirectFBSurface *primarySurface)
{
    DFBResult result;
    DFBGraphicsDeviceDescription dev;

    result = fb->GetDeviceDescription(fb, &dev);
    if (result != DFB_OK) {
        DirectFBError("Error reading graphics device description", result);
        return;
    }

    DFBSurfacePixelFormat pixelFormat;
    primarySurface->GetPixelFormat(primarySurface, &pixelFormat);

    qDebug("Device: %s (%s), Driver: %s v%i.%i (%s) Pixelformat: %d (%d)\n"
           "acceleration: 0x%x%s\nblit: 0x%x%s\ndraw: 0x%0x%s\nvideo: %iKB\n",
           dev.name, dev.vendor, dev.driver.name, dev.driver.major,
           dev.driver.minor, dev.driver.vendor, DFB_PIXELFORMAT_INDEX(pixelFormat),
           QDirectFBScreen::getImageFormat(primarySurface), dev.acceleration_mask,
           flagDescriptions(dev.acceleration_mask, accelerationDescriptions).constData(),
           dev.blitting_flags, flagDescriptions(dev.blitting_flags, blitDescriptions).constData(),
           dev.drawing_flags, flagDescriptions(dev.drawing_flags, drawDescriptions).constData(),
           (dev.video_memory >> 10));
}
#endif

static inline bool setIntOption(const QStringList &arguments, const QString &variable, int *value)
{
    Q_ASSERT(value);
    QRegExp rx(QString::fromLatin1("%1=?(\\d+)").arg(variable));
    rx.setCaseSensitivity(Qt::CaseInsensitive);
    if (arguments.indexOf(rx) != -1) {
        *value = rx.cap(1).toInt();
        return true;
    }
    return false;
}

static inline QColor colorFromName(const QString &name)
{
    QRegExp rx(QLatin1String("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])"));
    rx.setCaseSensitivity(Qt::CaseInsensitive);
    if (rx.exactMatch(name)) {
        Q_ASSERT(rx.captureCount() == 4);
        int ints[4];
        int i;
        for (i=0; i<4; ++i) {
            bool ok;
            ints[i] = rx.cap(i + 1).toUInt(&ok, 16);
            if (!ok || ints[i] > 255)
                break;
        }
        if (i == 4)
            return QColor(ints[0], ints[1], ints[2], ints[3]);
    }
    return QColor(name);
}

bool QDirectFBScreen::connect(const QString &displaySpec)
{
    DFBResult result = DFB_OK;

    {   // pass command line arguments to DirectFB
        const QStringList args = QCoreApplication::arguments();
        int argc = args.size();
        char **argv = new char*[argc];

        for (int i = 0; i < argc; ++i)
            argv[i] = qstrdup(args.at(i).toLocal8Bit().constData());

        result = DirectFBInit(&argc, &argv);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreen: error initializing DirectFB",
                          result);
        }
        delete[] argv;
    }

    const QStringList displayArgs = displaySpec.split(QLatin1Char(':'),
                                                      QString::SkipEmptyParts);

    d_ptr->setFlipFlags(displayArgs);

    result = DirectFBCreate(&d_ptr->dfb);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreen: error creating DirectFB interface",
                      result);
        return false;
    }

    if (displayArgs.contains(QLatin1String("videoonly"), Qt::CaseInsensitive))
        d_ptr->directFBFlags |= VideoOnly;

    if (displayArgs.contains(QLatin1String("systemonly"), Qt::CaseInsensitive)) {
        if (d_ptr->directFBFlags & VideoOnly) {
            qWarning("QDirectFBScreen: error. videoonly and systemonly are mutually exclusive");
        } else {
            d_ptr->directFBFlags |= SystemOnly;
        }
    }

    if (displayArgs.contains(QLatin1String("boundingrectflip"), Qt::CaseInsensitive)) {
        d_ptr->directFBFlags |= BoundingRectFlip;
    }

#ifdef QT_DIRECTFB_IMAGECACHE
    int imageCacheSize = 4 * 1024 * 1024; // 4 MB
    setIntOption(displayArgs, QLatin1String("imagecachesize"), &imageCacheSize);
    QDirectFBPaintEngine::initImageCache(imageCacheSize);
#endif

#ifndef QT_NO_DIRECTFB_WM
    if (displayArgs.contains(QLatin1String("fullscreen")))
#endif
        d_ptr->dfb->SetCooperativeLevel(d_ptr->dfb, DFSCL_FULLSCREEN);

    DFBSurfaceDescription description;
    memset(&description, 0, sizeof(DFBSurfaceDescription));
    IDirectFBSurface *surface;

#ifdef QT_NO_DIRECTFB_WM
    description.flags = DSDESC_CAPS;
    if (::setIntOption(displayArgs, QLatin1String("width"), &description.width))
        description.flags |= DSDESC_WIDTH;
    if (::setIntOption(displayArgs, QLatin1String("height"), &description.height))
        description.flags |= DSDESC_HEIGHT;

    description.caps = DSCAPS_PRIMARY|DSCAPS_DOUBLE;
    struct {
        const char *name;
        const DFBSurfaceCapabilities cap;
    } const capabilities[] = {
        { "static_alloc", DSCAPS_STATIC_ALLOC },
        { "triplebuffer", DSCAPS_TRIPLE },
        { "interlaced", DSCAPS_INTERLACED },
        { "separated", DSCAPS_SEPARATED },
//        { "depthbuffer", DSCAPS_DEPTH }, // only makes sense with TextureTriangles which are not supported
        { 0, DSCAPS_NONE }
    };
    for (int i=0; capabilities[i].name; ++i) {
        if (displayArgs.contains(QString::fromLatin1(capabilities[i].name), Qt::CaseInsensitive))
            description.caps |= capabilities[i].cap;
    }

    if (displayArgs.contains(QLatin1String("forcepremultiplied"), Qt::CaseInsensitive)) {
        description.caps |= DSCAPS_PREMULTIPLIED;
    }

    // We don't track the primary surface as it's released in disconnect
    d_ptr->primarySurface = createDFBSurface(description, DontTrackSurface, &result);
    if (!d_ptr->primarySurface) {
        DirectFBError("QDirectFBScreen: error creating primary surface",
                      result);
        return false;
    }

    surface = d_ptr->primarySurface;
#else
    description.flags = DSDESC_WIDTH|DSDESC_HEIGHT;
    description.width = description.height = 1;
    surface = createDFBSurface(description, DontTrackSurface, &result);
    if (!surface) {
        DirectFBError("QDirectFBScreen: error creating surface", result);
        return false;
    }
#endif
    // Work out what format we're going to use for surfaces with an alpha channel
    QImage::Format pixelFormat = QDirectFBScreen::getImageFormat(surface);
    d_ptr->alphaPixmapFormat = pixelFormat;

    switch (pixelFormat) {
    case QImage::Format_RGB666:
        d_ptr->alphaPixmapFormat = QImage::Format_ARGB6666_Premultiplied;
        break;
    case QImage::Format_RGB444:
        d_ptr->alphaPixmapFormat = QImage::Format_ARGB4444_Premultiplied;
        break;
    case QImage::Format_RGB32:
        pixelFormat = d_ptr->alphaPixmapFormat = QImage::Format_ARGB32_Premultiplied;
        // ### Format_RGB32 doesn't work so well with Qt. Force ARGB32 for windows/pixmaps
        break;
    case QImage::Format_Indexed8:
        qWarning("QDirectFBScreen::connect(). Qt/DirectFB does not work with the LUT8  pixelformat.");
        return false;
    case QImage::NImageFormats:
    case QImage::Format_Invalid:
    case QImage::Format_Mono:
    case QImage::Format_MonoLSB:
    case QImage::Format_RGB888:
    case QImage::Format_RGB16:
    case QImage::Format_RGB555:
        d_ptr->alphaPixmapFormat = QImage::Format_ARGB32_Premultiplied;
        break;
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
    case QImage::Format_ARGB4444_Premultiplied:
    case QImage::Format_ARGB8555_Premultiplied:
    case QImage::Format_ARGB8565_Premultiplied:
    case QImage::Format_ARGB6666_Premultiplied:
        // works already
        break;
    }
    setPixelFormat(pixelFormat);
    QScreen::d = QDirectFBScreen::depth(pixelFormat);
    data = 0;
    lstep = 0;
    size = 0;

    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreen::connect: "
                      "Unable to get screen!", result);
        return false;
    }
    const QString qws_size = QString::fromLatin1(qgetenv("QWS_SIZE"));
    if (!qws_size.isEmpty()) {
        QRegExp rx(QLatin1String("(\\d+)x(\\d+)"));
        if (!rx.exactMatch(qws_size)) {
            qWarning("QDirectFBScreen::connect: Can't parse QWS_SIZE=\"%s\"", qPrintable(qws_size));
        } else {
            int *ints[2] = { &w, &h };
            for (int i=0; i<2; ++i) {
                *ints[i] = rx.cap(i + 1).toInt();
                if (*ints[i] <= 0) {
                    qWarning("QDirectFBScreen::connect: %s is not a positive integer",
                             qPrintable(rx.cap(i + 1)));
                    w = h = 0;
                    break;
                }
            }
        }
    }

    setIntOption(displayArgs, QLatin1String("width"), &w);
    setIntOption(displayArgs, QLatin1String("height"), &h);

#ifndef QT_NO_DIRECTFB_LAYER
    int layerId = DLID_PRIMARY;
    setIntOption(displayArgs, QLatin1String("layerid"), &layerId);

    result = d_ptr->dfb->GetDisplayLayer(d_ptr->dfb, static_cast<DFBDisplayLayerID>(layerId),
                                         &d_ptr->dfbLayer);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreen::connect: "
                      "Unable to get display layer!", result);
        return false;
    }
    result = d_ptr->dfbLayer->GetScreen(d_ptr->dfbLayer, &d_ptr->dfbScreen);
#else
    result = d_ptr->dfb->GetScreen(d_ptr->dfb, 0, &d_ptr->dfbScreen);
#endif

    if (w <= 0 || h <= 0) {
#ifdef QT_NO_DIRECTFB_WM
        result = d_ptr->primarySurface->GetSize(d_ptr->primarySurface, &w, &h);
#elif (Q_DIRECTFB_VERSION >= 0x010000)
        IDirectFBSurface *layerSurface;
        if (d_ptr->dfbLayer->GetSurface(d_ptr->dfbLayer, &layerSurface) != DFB_OK) {
            result = layerSurface->GetSize(layerSurface, &w, &h);
            layerSurface->Release(layerSurface);
        }
        if (w <= 0 || h <= 0) {
            result = d_ptr->dfbScreen->GetSize(d_ptr->dfbScreen, &w, &h);
        }
#else
        qWarning("QDirectFBScreen::connect: DirectFB versions prior to 1.0 do not offer a way\n"
                 "query the size of the primary surface in windowed mode. You have to specify\n"
                 "the size of the display using QWS_SIZE=[0-9]x[0-9] or\n"
                 "QWS_DISPLAY=directfb:width=[0-9]:height=[0-9]");
        return false;
#endif
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreen::connect: "
                          "Unable to get screen size!", result);
            return false;
        }
    }


    dw = w;
    dh = h;

    Q_ASSERT(dw != 0 && dh != 0);

    physWidth = physHeight = -1;
    setIntOption(displayArgs, QLatin1String("mmWidth"), &physWidth);
    setIntOption(displayArgs, QLatin1String("mmHeight"), &physHeight);
    const int dpi = 72;
    if (physWidth < 0)
        physWidth = qRound(dw * 25.4 / dpi);
    if (physHeight < 0)
        physHeight = qRound(dh * 25.4 / dpi);

    setGraphicsSystem(d_ptr);

#if (Q_DIRECTFB_VERSION >= 0x000923)
    if (displayArgs.contains(QLatin1String("debug"), Qt::CaseInsensitive))
        printDirectFBInfo(d_ptr->dfb, surface);
#endif
#ifdef QT_DIRECTFB_WM
    surface->Release(surface);
    QColor backgroundColor;
#else
    QColor &backgroundColor = d_ptr->backgroundColor;
#endif

    QRegExp backgroundColorRegExp(QLatin1String("bgcolor=(.+)"));
    backgroundColorRegExp.setCaseSensitivity(Qt::CaseInsensitive);
    if (displayArgs.indexOf(backgroundColorRegExp) != -1) {
        backgroundColor = colorFromName(backgroundColorRegExp.cap(1));
    }
#ifdef QT_NO_DIRECTFB_WM
    if (!backgroundColor.isValid())
        backgroundColor = Qt::green;
    d_ptr->primarySurface->Clear(d_ptr->primarySurface, backgroundColor.red(),
                                 backgroundColor.green(), backgroundColor.blue(),
                                 backgroundColor.alpha());
    d_ptr->primarySurface->Flip(d_ptr->primarySurface, 0, d_ptr->flipFlags);
#else
    if (backgroundColor.isValid()) {
        DFBResult result = d_ptr->dfbLayer->SetCooperativeLevel(d_ptr->dfbLayer, DLSCL_ADMINISTRATIVE);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreen::connect "
                          "Unable to set cooperative level", result);
        }
        result = d_ptr->dfbLayer->SetBackgroundColor(d_ptr->dfbLayer, backgroundColor.red(), backgroundColor.green(),
                                                     backgroundColor.blue(), backgroundColor.alpha());
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::connect: "
                          "Unable to set background color", result);
        }

        result = d_ptr->dfbLayer->SetBackgroundMode(d_ptr->dfbLayer, DLBM_COLOR);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreenCursor::connect: "
                          "Unable to set background mode", result);
        }

        result = d_ptr->dfbLayer->SetCooperativeLevel(d_ptr->dfbLayer, DLSCL_SHARED);
        if (result != DFB_OK) {
            DirectFBError("QDirectFBScreen::connect "
                          "Unable to set cooperative level", result);
        }

    }
#endif

    return true;
}

void QDirectFBScreen::disconnect()
{
#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
    if (d_ptr->imageProvider)
        d_ptr->imageProvider->Release(d_ptr->imageProvider);
#endif
#ifdef QT_NO_DIRECTFB_WM
    d_ptr->primarySurface->Release(d_ptr->primarySurface);
    d_ptr->primarySurface = 0;
#endif

    foreach (IDirectFBSurface *surf, d_ptr->allocatedSurfaces)
        surf->Release(surf);
    d_ptr->allocatedSurfaces.clear();

#ifndef QT_NO_DIRECTFB_LAYER
    d_ptr->dfbLayer->Release(d_ptr->dfbLayer);
    d_ptr->dfbLayer = 0;
#endif

    d_ptr->dfbScreen->Release(d_ptr->dfbScreen);
    d_ptr->dfbScreen = 0;

    d_ptr->dfb->Release(d_ptr->dfb);
    d_ptr->dfb = 0;
}

bool QDirectFBScreen::initDevice()
{
#ifndef QT_NO_DIRECTFB_MOUSE
    if (qgetenv("QWS_MOUSE_PROTO").isEmpty()) {
        QWSServer::instance()->setDefaultMouse("None");
        d_ptr->mouse = new QDirectFBMouseHandler;
    }
#endif
#ifndef QT_NO_DIRECTFB_KEYBOARD
    if (qgetenv("QWS_KEYBOARD").isEmpty()) {
        QWSServer::instance()->setDefaultKeyboard("None");
        d_ptr->keyboard = new QDirectFBKeyboardHandler(QString());
    }
#endif

#ifdef QT_DIRECTFB_CURSOR
    qt_screencursor = new QDirectFBScreenCursor;
#elif !defined QT_NO_QWS_CURSOR
    QScreenCursor::initSoftwareCursor();
#endif
    return true;
}

void QDirectFBScreen::shutdownDevice()
{
#ifndef QT_NO_DIRECTFB_MOUSE
    delete d_ptr->mouse;
    d_ptr->mouse = 0;
#endif
#ifndef QT_NO_DIRECTFB_KEYBOARD
    delete d_ptr->keyboard;
    d_ptr->keyboard = 0;
#endif

#ifndef QT_NO_QWS_CURSOR
    delete qt_screencursor;
    qt_screencursor = 0;
#endif
}

void QDirectFBScreen::setMode(int width, int height, int depth)
{
    d_ptr->dfb->SetVideoMode(d_ptr->dfb, width, height, depth);
}

void QDirectFBScreen::blank(bool on)
{
    d_ptr->dfbScreen->SetPowerMode(d_ptr->dfbScreen,
                                   (on ? DSPM_ON : DSPM_SUSPEND));
}

QWSWindowSurface *QDirectFBScreen::createSurface(QWidget *widget) const
{
#ifdef QT_NO_DIRECTFB_WM
    if (QApplication::type() == QApplication::GuiServer) {
        return new QDirectFBWindowSurface(d_ptr->flipFlags, const_cast<QDirectFBScreen*>(this), widget);
    } else {
        return QScreen::createSurface(widget);
    }
#else
    return new QDirectFBWindowSurface(d_ptr->flipFlags, const_cast<QDirectFBScreen*>(this), widget);
#endif
}

QWSWindowSurface *QDirectFBScreen::createSurface(const QString &key) const
{
    if (key == QLatin1String("directfb")) {
        return new QDirectFBWindowSurface(d_ptr->flipFlags, const_cast<QDirectFBScreen*>(this));
    }
    return QScreen::createSurface(key);
}

#if defined QT_NO_DIRECTFB_WM
struct PaintCommand {
    PaintCommand() : dfbSurface(0), windowOpacity(255), blittingFlags(DSBLIT_NOFX) {}
    IDirectFBSurface *dfbSurface;
    QImage image;
    QPoint windowPosition;
    QRegion source;
    quint8 windowOpacity;
    DFBSurfaceBlittingFlags blittingFlags;
};

static inline void initParameters(DFBRectangle &source, const QRect &sourceGlobal, const QPoint &pos)
{
    source.x = sourceGlobal.x() - pos.x();
    source.y = sourceGlobal.y() - pos.y();
    source.w = sourceGlobal.width();
    source.h = sourceGlobal.height();
}
#endif

void QDirectFBScreen::exposeRegion(QRegion r, int)
{
    Q_UNUSED(r);
#if defined QT_NO_DIRECTFB_WM

    r &= region();
    if (r.isEmpty()) {
        return;
    }
    r = r.boundingRect();

    IDirectFBSurface *primary = d_ptr->primarySurface;
    const QList<QWSWindow*> windows = QWSServer::instance()->clientWindows();
    QVarLengthArray<PaintCommand, 4> commands(windows.size());
    QRegion region = r;
    int idx = 0;
    for (int i=0; i<windows.size(); ++i) {
        QWSWindowSurface *surface = windows.at(i)->windowSurface();
        if (!surface)
            continue;

        const QRect windowGeometry = surface->geometry();
        const QRegion intersection = region & windowGeometry;
        if (intersection.isEmpty()) {
            continue;
        }

        PaintCommand &cmd = commands[idx];

        if (surface->key() == QLatin1String("directfb")) {
            const QDirectFBWindowSurface *ws = static_cast<QDirectFBWindowSurface*>(surface);
            cmd.dfbSurface = ws->directFBSurface();

            if (!cmd.dfbSurface) {
                continue;
            }
        } else {
            cmd.image = surface->image();
            if (cmd.image.isNull()) {
                continue;
            }
        }
        ++idx;

        cmd.windowPosition = windowGeometry.topLeft();
        cmd.source = intersection;
        if (windows.at(i)->isOpaque()) {
            region -= intersection;
            if (region.isEmpty())
                break;
        } else {
            cmd.windowOpacity = windows.at(i)->opacity();
            cmd.blittingFlags = cmd.windowOpacity == 255
                                ? DSBLIT_BLEND_ALPHACHANNEL
                                : (DSBLIT_BLEND_ALPHACHANNEL|DSBLIT_BLEND_COLORALPHA);
        }
    }
    if (!region.isEmpty()) {
        solidFill(d_ptr->backgroundColor, region);
    }

    while (idx > 0) {
        const PaintCommand &cmd = commands[--idx];
        Q_ASSERT(cmd.dfbSurface || !cmd.image.isNull());
        IDirectFBSurface *surface;
        if (cmd.dfbSurface) {
            surface = cmd.dfbSurface;
        } else {
            Q_ASSERT(!cmd.image.isNull());
            DFBResult result;
            surface = createDFBSurface(cmd.image, cmd.image.format(), DontTrackSurface, &result);
            Q_ASSERT((result != DFB_OK) == !surface);
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreen::exposeRegion: Can't create surface from image", result);
                continue;
            }
        }

        primary->SetBlittingFlags(primary, cmd.blittingFlags);
        if (cmd.blittingFlags & DSBLIT_BLEND_COLORALPHA) {
            primary->SetColor(primary, 0xff, 0xff, 0xff, cmd.windowOpacity);
        }
        const QRegion &region = cmd.source;
        const int rectCount = region.rectCount();
        DFBRectangle source;
        if (rectCount == 1) {
            ::initParameters(source, region.boundingRect(), cmd.windowPosition);
            primary->Blit(primary, surface, &source, cmd.windowPosition.x() + source.x, cmd.windowPosition.y() + source.y);
        } else {
            const QVector<QRect> rects = region.rects();
            for (int i=0; i<rectCount; ++i) {
                ::initParameters(source, rects.at(i), cmd.windowPosition);
                primary->Blit(primary, surface, &source, cmd.windowPosition.x() + source.x, cmd.windowPosition.y() + source.y);
            }
        }
        if (surface != cmd.dfbSurface) {
            surface->Release(surface);
        }
    }

    primary->SetColor(primary, 0xff, 0xff, 0xff, 0xff);

#if defined QT_NO_DIRECTFB_CURSOR and !defined QT_NO_QWS_CURSOR
    if (QScreenCursor *cursor = QScreenCursor::instance()) {
        const QRect cursorRectangle = cursor->boundingRect();
        if (cursor->isVisible() && !cursor->isAccelerated() && r.intersects(cursorRectangle)) {
            const QImage image = cursor->image();
            if (image.cacheKey() != d_ptr->cursorImageKey) {
                if (d_ptr->cursorSurface) {
                    releaseDFBSurface(d_ptr->cursorSurface);
                }
                d_ptr->cursorSurface = createDFBSurface(image, image.format(), QDirectFBScreen::TrackSurface);
                d_ptr->cursorImageKey = image.cacheKey();
            }

            Q_ASSERT(d_ptr->cursorSurface);
            primary->SetBlittingFlags(primary, DSBLIT_BLEND_ALPHACHANNEL);
            primary->Blit(primary, d_ptr->cursorSurface, 0, cursorRectangle.x(), cursorRectangle.y());
        }
    }
#endif
    flipSurface(primary, d_ptr->flipFlags, r, QPoint());
    primary->SetBlittingFlags(primary, DSBLIT_NOFX);
#endif
}

void QDirectFBScreen::solidFill(const QColor &color, const QRegion &region)
{
#ifdef QT_DIRECTFB_WM
    Q_UNUSED(color);
    Q_UNUSED(region);
#else
    if (region.isEmpty())
        return;

    d_ptr->primarySurface->SetColor(d_ptr->primarySurface,
                                    color.red(), color.green(), color.blue(),
                                    color.alpha());
    const int n = region.rectCount();
    if (n == 1) {
        const QRect r = region.boundingRect();
        d_ptr->primarySurface->FillRectangle(d_ptr->primarySurface, r.x(), r.y(), r.width(), r.height());
    } else {
        const QVector<QRect> rects = region.rects();
        QVarLengthArray<DFBRectangle, 32> rectArray(n);
        for (int i=0; i<n; ++i) {
            const QRect &r = rects.at(i);
            rectArray[i].x = r.x();
            rectArray[i].y = r.y();
            rectArray[i].w = r.width();
            rectArray[i].h = r.height();
        }
        d_ptr->primarySurface->FillRectangles(d_ptr->primarySurface, rectArray.constData(), n);
    }
#endif
}

QImage::Format QDirectFBScreen::alphaPixmapFormat() const
{
    return d_ptr->alphaPixmapFormat;
}

bool QDirectFBScreen::initSurfaceDescriptionPixelFormat(DFBSurfaceDescription *description,
                                                        QImage::Format format)
{
    const DFBSurfacePixelFormat pixelformat = QDirectFBScreen::getSurfacePixelFormat(format);
    if (pixelformat == DSPF_UNKNOWN)
        return false;
    description->flags |= DSDESC_PIXELFORMAT;
    description->pixelformat = pixelformat;
    if (QDirectFBScreen::isPremultiplied(format)) {
        if (!(description->flags & DSDESC_CAPS)) {
            description->caps = DSCAPS_PREMULTIPLIED;
            description->flags |= DSDESC_CAPS;
        } else {
            description->caps |= DSCAPS_PREMULTIPLIED;
        }
    }
    return true;
}

uchar *QDirectFBScreen::lockSurface(IDirectFBSurface *surface, DFBSurfaceLockFlags flags, int *bpl)
{
    void *mem;
    const DFBResult result = surface->Lock(surface, flags, &mem, bpl);
    if (result != DFB_OK) {
        DirectFBError("QDirectFBScreen::lockSurface()", result);
    }

    return reinterpret_cast<uchar*>(mem);
}


void QDirectFBScreen::flipSurface(IDirectFBSurface *surface, DFBSurfaceFlipFlags flipFlags,
                                  const QRegion &region, const QPoint &offset)
{
    if (!(flipFlags & DSFLIP_BLIT)) {
        surface->Flip(surface, 0, flipFlags);
    } else {
        if (!(d_ptr->directFBFlags & BoundingRectFlip) && region.rectCount() > 1) {
            const QVector<QRect> rects = region.rects();
            const DFBSurfaceFlipFlags nonWaitFlags = flipFlags & ~DSFLIP_WAIT;
            for (int i=0; i<rects.size(); ++i) {
                const QRect &r = rects.at(i);
                const DFBRegion dfbReg = { r.x() + offset.x(), r.y() + offset.y(),
                                           r.right() + offset.x(),
                                           r.bottom() + offset.y() };
                surface->Flip(surface, &dfbReg, i + 1 < rects.size() ? nonWaitFlags : flipFlags);
            }
        } else {
            const QRect r = region.boundingRect();
            const DFBRegion dfbReg = { r.x() + offset.x(), r.y() + offset.y(),
                                       r.right() + offset.x(),
                                       r.bottom() + offset.y() };
            surface->Flip(surface, &dfbReg, flipFlags);
        }
    }
}

#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
void QDirectFBScreen::setDirectFBImageProvider(IDirectFBImageProvider *provider)
{
    Q_ASSERT(provider);
    if (d_ptr->imageProvider)
        d_ptr->imageProvider->Release(d_ptr->imageProvider);
    d_ptr->imageProvider = provider;
}
#endif

void QDirectFBScreen::waitIdle()
{
    d_ptr->dfb->WaitIdle(d_ptr->dfb);
}

#ifdef QT_DIRECTFB_WM
IDirectFBWindow *QDirectFBScreen::windowForWidget(const QWidget *widget) const
{
    if (widget) {
        const QWSWindowSurface *surface = static_cast<const QWSWindowSurface*>(widget->windowSurface());
        if (surface && surface->key() == QLatin1String("directfb")) {
            return static_cast<const QDirectFBWindowSurface*>(surface)->directFBWindow();
        }
    }
    return 0;
}
#endif

IDirectFBSurface * QDirectFBScreen::surfaceForWidget(const QWidget *widget, QRect *rect) const
{
    Q_ASSERT(widget);
    if (!widget->isVisible() || widget->size().isNull())
        return 0;

    const QWSWindowSurface *surface = static_cast<const QWSWindowSurface*>(widget->windowSurface());
    if (surface && surface->key() == QLatin1String("directfb")) {
        return static_cast<const QDirectFBWindowSurface*>(surface)->surfaceForWidget(widget, rect);
    }
    return 0;
}

#ifdef QT_DIRECTFB_SUBSURFACE
IDirectFBSurface *QDirectFBScreen::subSurfaceForWidget(const QWidget *widget, const QRect &area) const
{
    Q_ASSERT(widget);
    QRect rect;
    IDirectFBSurface *surface = surfaceForWidget(widget, &rect);
    IDirectFBSurface *subSurface = 0;
    if (surface) {
        if (!area.isNull())
            rect &= area.translated(widget->mapTo(widget->window(), QPoint(0, 0)));
        if (!rect.isNull()) {
            const DFBRectangle subRect = { rect.x(), rect.y(), rect.width(), rect.height() };
            const DFBResult result = surface->GetSubSurface(surface, &subRect, &subSurface);
            if (result != DFB_OK) {
                DirectFBError("QDirectFBScreen::subSurface(): Can't get sub surface", result);
            }
        }
    }
    return subSurface;
}
#endif

#ifndef QT_DIRECTFB_PLUGIN
Q_GUI_EXPORT IDirectFBSurface *qt_directfb_surface_for_widget(const QWidget *widget, QRect *rect)
{
    return QDirectFBScreen::instance() ? QDirectFBScreen::instance()->surfaceForWidget(widget, rect) : 0;
}
#ifdef QT_DIRECTFB_SUBSURFACE
Q_GUI_EXPORT IDirectFBSurface *qt_directfb_subsurface_for_widget(const QWidget *widget, const QRect &area)
{
    return QDirectFBScreen::instance() ? QDirectFBScreen::instance()->subSurfaceForWidget(widget, area) : 0;
}
#endif
#ifdef QT_DIRECTFB_WM
Q_GUI_EXPORT IDirectFBWindow *qt_directfb_window_for_widget(const QWidget *widget)
{
    return QDirectFBScreen::instance() ? QDirectFBScreen::instance()->windowForWidget(widget) : 0;
}

#endif
#endif

QT_END_NAMESPACE

#include "qdirectfbscreen.moc"
#endif // QT_NO_QWS_DIRECTFB

