/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module 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 "qscreen_qws.h"

#include "qcolormap.h"
#include "qscreendriverfactory_qws.h"
#include "qwindowsystem_qws.h"
#include "qwidget.h"
#include "qcolor.h"
#include "qpixmap.h"
#include "qvarlengtharray.h"
#include "qwsdisplay_qws.h"
#include "qpainter.h"
#include <private/qdrawhelper_p.h>
#include <private/qpaintengine_raster_p.h>
#include <private/qpixmap_raster_p.h>
#include <private/qwindowsurface_qws_p.h>
#include <private/qpainter_p.h>
#include <private/qwidget_p.h>
#include <private/qgraphicssystem_qws_p.h>

QT_BEGIN_NAMESPACE

// #define QT_USE_MEMCPY_DUFF

#ifndef QT_NO_QWS_CURSOR
bool qt_sw_cursor=false;
Q_GUI_EXPORT QScreenCursor * qt_screencursor = 0;
#endif
Q_GUI_EXPORT QScreen * qt_screen = 0;

ClearCacheFunc QScreen::clearCacheFunc = 0;

#ifndef QT_NO_QWS_CURSOR
/*!
    \class QScreenCursor
    \ingroup qws

    \brief The QScreenCursor class is a base class for screen cursors
    in Qt for Embedded Linux.

    Note that this class is non-portable, and that it is only
    available in \l{Qt for Embedded Linux}.

    QScreenCursor implements a software cursor, but can be subclassed
    to support hardware cursors as well. When deriving from the
    QScreenCursor class it is important to maintain the cursor's
    image, position, hot spot (the point within the cursor's image
    that will be the position of the associated mouse events) and
    visibility as well as informing whether it is hardware accelerated
    or not.

    Note that there may only be one screen cursor at a time. Use the
    static instance() function to retrieve a pointer to the current
    screen cursor. Typically, the cursor is constructed by the QScreen
    class or one of its descendants when it is initializing the
    device; the QScreenCursor class should never be instantiated
    explicitly.

    Use the move() function to change the position of the cursor, and
    the set() function to alter its image or its hot spot. In
    addition, you can find out whether the cursor is accelerated or
    not, using the isAccelerated() function, and the boundingRect()
    function returns the cursor's bounding rectangle.

    The cursor's appearance can be controlled using the isVisible(),
    hide() and show() functions; alternatively the QWSServer class
    provides some means of controlling the cursor's appearance using
    the QWSServer::isCursorVisible() and QWSServer::setCursorVisible()
    functions.

    \sa QScreen, QWSServer
*/

/*!
    \fn static QScreenCursor* QScreenCursor::instance()
    \since 4.2

    Returns a pointer to the application's unique screen cursor.
*/

extern bool qws_sw_cursor;

/*!
    Constructs a screen cursor
*/
QScreenCursor::QScreenCursor()
{
    pos = QPoint(qt_screen->deviceWidth()/2, qt_screen->deviceHeight()/2);
    size = QSize(0,0);
    enable = true;
    hwaccel = false;
    supportsAlpha = true;
}

/*!
    Destroys the screen cursor.
*/
QScreenCursor::~QScreenCursor()
{
}

/*!
    Hides the cursor from the screen.

    \sa show()
*/
void QScreenCursor::hide()
{
    if (enable) {
        enable = false;
        if (!hwaccel)
            qt_screen->exposeRegion(boundingRect(), 0);
    }
}

/*!
    Shows the mouse cursor.

    \sa hide()
*/
void QScreenCursor::show()
{
    if (!enable) {
        enable = true;
        if (!hwaccel)
            qt_screen->exposeRegion(boundingRect(), 0);
    }
}

/*!
    Sets the cursor's image to be the given \a image.

    The \a hotx and \a hoty parameters define the cursor's hot spot,
    i.e., the point within the cursor's image that will be the
    position of the associated mouse events.

    \sa move()
*/
void QScreenCursor::set(const QImage &image, int hotx, int hoty)
{
    const QRect r = boundingRect();

    hotspot = QPoint(hotx, hoty);
    // These are in almost all cases the fastest formats to blend
    QImage::Format f;
    switch (qt_screen->depth()) {
    case 12:
        f = QImage::Format_ARGB4444_Premultiplied;
        break;
    case 15:
        f =  QImage::Format_ARGB8555_Premultiplied;
        break;
    case 16:
        f = QImage::Format_ARGB8565_Premultiplied;
        break;
    case 18:
        f = QImage::Format_ARGB6666_Premultiplied;
        break;
    default:
        f =  QImage::Format_ARGB32_Premultiplied;
    }

    cursor = image.convertToFormat(f);

    size = image.size();

    if (enable && !hwaccel)
        qt_screen->exposeRegion(r | boundingRect(), 0);
}

/*!
    Moves the mouse cursor to the given position, i.e., (\a x, \a y).

    Note that the given position defines the top-left corner of the
    cursor's image, i.e., not the cursor's hot spot (the position of
    the associated mouse events).

    \sa set()
*/
void QScreenCursor::move(int x, int y)
{
    QRegion r = boundingRect();
    pos = QPoint(x,y);
    if (enable && !hwaccel) {
        r |= boundingRect();
        qt_screen->exposeRegion(r, 0);
    }
}


/*!
    \fn void QScreenCursor::initSoftwareCursor ()

    Initializes the screen cursor.

    This function is typically called from the screen driver when
    initializing the device. Alternatively, the cursor can be set
    directly using the pointer returned by the static instance()
    function.

    \sa QScreen::initDevice()
*/
void QScreenCursor::initSoftwareCursor()
{
    qt_screencursor = new QScreenCursor;
}


#endif // QT_NO_QWS_CURSOR


/*!
    \fn QRect QScreenCursor::boundingRect () const

    Returns the cursor's bounding rectangle.
*/

/*!
    \internal
    \fn bool QScreenCursor::enabled ()
*/

/*!
    \fn QImage QScreenCursor::image () const

    Returns the cursor's image.
*/


/*!
    \fn bool QScreenCursor::isAccelerated () const

    Returns true if the cursor is accelerated; otherwise false.
*/

/*!
    \fn bool QScreenCursor::isVisible () const

    Returns true if the cursor is visible; otherwise false.
*/

/*!
    \internal
    \fn bool QScreenCursor::supportsAlphaCursor () const
*/

/*
    \variable QScreenCursor::cursor

    \brief the cursor's image.

    \sa image()
*/

/*
    \variable QScreenCursor::size

    \brief the cursor's size
*/

/*
    \variable QScreenCursor::pos

    \brief the cursor's position, i.e., the position of the top-left
    corner of the crsor's image

    \sa set(), move()
*/

/*
    \variable QScreenCursor::hotspot

    \brief the cursor's hotspot, i.e., the point within the cursor's
    image that will be the position of the associated mouse events.

    \sa set(), move()
*/

/*
    \variable QScreenCursor::enable

    \brief whether the cursor is visible or not

    \sa isVisible()
*/

/*
    \variable QScreenCursor::hwaccel

    \brief holds whether the cursor is accelerated or not

    If the cursor is not accelerated, its image will be included by
    the screen when it composites the window surfaces.

    \sa isAccelerated()

*/

/*
    \variable QScreenCursor::supportsAlpha
*/

/*!
    \internal
    \macro qt_screencursor
    \relates QScreenCursor

    A global pointer referring to the unique screen cursor. It is
    equivalent to the pointer returned by the
    QScreenCursor::instance() function.
*/



class QScreenPrivate
{
public:
    QScreenPrivate(QScreen *parent, QScreen::ClassId id = QScreen::CustomClass);
    ~QScreenPrivate();

    inline QImage::Format preferredImageFormat() const;

    typedef void (*SolidFillFunc)(QScreen*, const QColor&, const QRegion&);
    typedef void (*BlitFunc)(QScreen*, const QImage&, const QPoint&, const QRegion&);

    SolidFillFunc solidFill;
    BlitFunc blit;

    QPoint offset;
    QList<QScreen*> subScreens;
    QPixmapDataFactory* pixmapFactory;
    QGraphicsSystem* graphicsSystem;
    QWSGraphicsSystem defaultGraphicsSystem; //###
    QImage::Format pixelFormat;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
    bool fb_is_littleEndian;
#endif
#ifdef QT_QWS_CLIENTBLIT
    bool supportsBlitInClients;
#endif
    int classId;
    QScreen *q_ptr;
};

template <typename T>
static void solidFill_template(QScreen *screen, const QColor &color,
                               const QRegion &region)
{
    T *dest = reinterpret_cast<T*>(screen->base());
    const T c = qt_colorConvert<T, quint32>(color.rgba(), 0);
    const int stride = screen->linestep();
    const QVector<QRect> rects = region.rects();

    for (int i = 0; i < rects.size(); ++i) {
        const QRect r = rects.at(i);
        qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride);
    }
}

#ifdef QT_QWS_DEPTH_GENERIC
static void solidFill_rgb_32bpp(QScreen *screen, const QColor &color,
                                const QRegion &region)
{
    quint32 *dest = reinterpret_cast<quint32*>(screen->base());
    const quint32 c = qt_convertToRgb<quint32>(color.rgba());

    const int stride = screen->linestep();
    const QVector<QRect> rects = region.rects();

    for (int i = 0; i < rects.size(); ++i) {
        const QRect r = rects.at(i);
        qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride);
    }
}

static void solidFill_rgb_16bpp(QScreen *screen, const QColor &color,
                                const QRegion &region)
{
    quint16 *dest = reinterpret_cast<quint16*>(screen->base());
    const quint16 c = qt_convertToRgb<quint32>(color.rgba());

    const int stride = screen->linestep();
    const QVector<QRect> rects = region.rects();

    for (int i = 0; i < rects.size(); ++i) {
        const QRect r = rects.at(i);
        qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride);
    }
}
#endif // QT_QWS_DEPTH_GENERIC

#ifdef QT_QWS_DEPTH_4
static inline void qt_rectfill_gray4(quint8 *dest, quint8 value,
                                     int x, int y, int width, int height,
                                     int stride)
{
    const int pixelsPerByte = 2;
    dest += y * stride + x / pixelsPerByte;
    const int doAlign = x & 1;
    const int doTail = (width - doAlign) & 1;
    const int width8 = (width - doAlign) / pixelsPerByte;

    for (int j = 0; j < height; ++j) {
        if (doAlign)
            *dest = (*dest & 0xf0) | (value & 0x0f);
        if (width8)
            qt_memfill<quint8>(dest + doAlign, value, width8);
        if (doTail) {
            quint8 *d = dest + doAlign + width8;
            *d = (*d & 0x0f) | (value & 0xf0);
        }
        dest += stride;
    }
}

static void solidFill_gray4(QScreen *screen, const QColor &color,
                            const QRegion &region)
{
    quint8 *dest = reinterpret_cast<quint8*>(screen->base());
    const quint8 c = qGray(color.rgba()) >> 4;
    const quint8 c8 = (c << 4) | c;

    const int stride = screen->linestep();
    const QVector<QRect> rects = region.rects();

    for (int i = 0; i < rects.size(); ++i) {
        const QRect r = rects.at(i);
        qt_rectfill_gray4(dest, c8, r.x(), r.y(), r.width(), r.height(),
                          stride);
    }
}
#endif // QT_QWS_DEPTH_4

#ifdef QT_QWS_DEPTH_1
static inline void qt_rectfill_mono(quint8 *dest, quint8 value,
                                    int x, int y, int width, int height,
                                    int stride)
{
    const int pixelsPerByte = 8;
    const int alignWidth = qMin(width, (8 - (x & 7)) & 7);
    const int doAlign = (alignWidth > 0 ? 1 : 0);
    const int alignStart = pixelsPerByte - 1 - (x & 7);
    const int alignStop = alignStart - (alignWidth - 1);
    const quint8 alignMask = ((1 << alignWidth) - 1) << alignStop;
    const int tailWidth = (width - alignWidth) & 7;
    const int doTail = (tailWidth > 0 ? 1 : 0);
    const quint8 tailMask = (1 << (pixelsPerByte - tailWidth)) - 1;
    const int width8 = (width - alignWidth) / pixelsPerByte;

    dest += y * stride + x / pixelsPerByte;
    stride -= (doAlign + width8);

    for (int j = 0; j < height; ++j) {
        if (doAlign) {
            *dest = (*dest & ~alignMask) | (value & alignMask);
            ++dest;
        }
        if (width8) {
            qt_memfill<quint8>(dest, value, width8);
            dest += width8;
        }
        if (doTail)
            *dest = (*dest & tailMask) | (value & ~tailMask);
        dest += stride;
    }
}

static void solidFill_mono(QScreen *screen, const QColor &color,
                           const QRegion &region)
{
    quint8 *dest = reinterpret_cast<quint8*>(screen->base());
    const quint8 c8 = (qGray(color.rgba()) >> 7) * 0xff;

    const int stride = screen->linestep();
    const QVector<QRect> rects = region.rects();

    for (int i = 0; i < rects.size(); ++i) {
        const QRect r = rects.at(i);
        qt_rectfill_mono(dest, c8, r.x(), r.y(), r.width(), r.height(),
                         stride);
    }
}
#endif // QT_QWS_DEPTH_1

void qt_solidFill_setup(QScreen *screen, const QColor &color,
                        const QRegion &region)
{
    switch (screen->depth()) {
#ifdef QT_QWS_DEPTH_32
    case 32:
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->solidFill = solidFill_template<quint32>;
        else
            screen->d_ptr->solidFill = solidFill_template<qabgr8888>;
        break;
#endif
#ifdef QT_QWS_DEPTH_24
    case 24:
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->solidFill = solidFill_template<qrgb888>;
        else
            screen->d_ptr->solidFill = solidFill_template<quint24>;
        break;
#endif
#ifdef QT_QWS_DEPTH_18
    case 18:
        screen->d_ptr->solidFill = solidFill_template<quint18>;
        break;
#endif
#ifdef QT_QWS_DEPTH_16
    case 16:
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->solidFill = solidFill_template<quint16>;
        else
            screen->d_ptr->solidFill = solidFill_template<qbgr565>;
        break;
#endif
#ifdef QT_QWS_DEPTH_15
    case 15:
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->solidFill = solidFill_template<qrgb555>;
        else
            screen->d_ptr->solidFill = solidFill_template<qbgr555>;
        break;
#endif
#ifdef QT_QWS_DEPTH_12
    case 12:
        screen->d_ptr->solidFill = solidFill_template<qrgb444>;
        break;
#endif
#ifdef QT_QWS_DEPTH_8
    case 8:
        screen->d_ptr->solidFill = solidFill_template<quint8>;
        break;
#endif
#ifdef QT_QWS_DEPTH_4
    case 4:
        screen->d_ptr->solidFill = solidFill_gray4;
        break;
#endif
#ifdef QT_QWS_DEPTH_1
    case 1:
        screen->d_ptr->solidFill = solidFill_mono;
        break;
#endif
     default:
        qFatal("solidFill_setup(): Screen depth %d not supported!",
               screen->depth());
        screen->d_ptr->solidFill = 0;
        break;
    }
    screen->d_ptr->solidFill(screen, color, region);
}

template <typename DST, typename SRC>
static void blit_template(QScreen *screen, const QImage &image,
                          const QPoint &topLeft, const QRegion &region)
{
    DST *dest = reinterpret_cast<DST*>(screen->base());
    const int screenStride = screen->linestep();
    const int imageStride = image.bytesPerLine();

    if (region.numRects() == 1) {
        const QRect r = region.boundingRect();
        const SRC *src = reinterpret_cast<const SRC*>(image.scanLine(r.y()))
                         + r.x();
        qt_rectconvert<DST, SRC>(dest, src,
                                 r.x() + topLeft.x(), r.y() + topLeft.y(),
                                 r.width(), r.height(),
                                 screenStride, imageStride);
    } else {
        const QVector<QRect> rects = region.rects();

        for (int i = 0; i < rects.size(); ++i) {
            const QRect r = rects.at(i);
            const SRC *src = reinterpret_cast<const SRC*>(image.scanLine(r.y()))
                             + r.x();
            qt_rectconvert<DST, SRC>(dest, src,
                                     r.x() + topLeft.x(), r.y() + topLeft.y(),
                                     r.width(), r.height(),
                                     screenStride, imageStride);
        }
    }
}

#ifdef QT_QWS_DEPTH_32
static void blit_32(QScreen *screen, const QImage &image,
                    const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<quint32, quint32>(screen, image, topLeft, region);
        return;
#ifdef QT_QWS_DEPTH_16
    case QImage::Format_RGB16:
        blit_template<quint32, quint16>(screen, image, topLeft, region);
        return;
#endif
    default:
        qCritical("blit_32(): Image format %d not supported!", image.format());
    }
}
#endif // QT_QWS_DEPTH_32

#ifdef QT_QWS_DEPTH_24
static void blit_24(QScreen *screen, const QImage &image,
                    const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<quint24, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB888:
        blit_template<quint24, qrgb888>(screen, image, topLeft, region);
        return;
#ifdef QT_QWS_DEPTH_16
    case QImage::Format_RGB16:
        blit_template<quint24, quint16>(screen, image, topLeft, region);
        return;
#endif
    default:
        qCritical("blit_24(): Image format %d not supported!", image.format());
    }
}

static void blit_qrgb888(QScreen *screen, const QImage &image,
                         const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<qrgb888, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB888:
        blit_template<qrgb888, qrgb888>(screen, image, topLeft, region);
        return;
#ifdef QT_QWS_DEPTH_16
    case QImage::Format_RGB16:
        blit_template<qrgb888, quint16>(screen, image, topLeft, region);
        return;
#endif
    default:
        qCritical("blit_24(): Image format %d not supported!", image.format());
        break;
    }
}
#endif // QT_QWS_DEPTH_24

#ifdef QT_QWS_DEPTH_18
static void blit_18(QScreen *screen, const QImage &image,
                    const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<qrgb666, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB666:
        blit_template<qrgb666, qrgb666>(screen, image, topLeft, region);
        return;
#ifdef QT_QWS_DEPTH_16
    case QImage::Format_RGB16:
        blit_template<qrgb666, quint16>(screen, image, topLeft, region);
        return;
#endif
    default:
        qCritical("blit_18(): Image format %d not supported!", image.format());
    }
}
#endif // QT_QWS_DEPTH_18

#if (Q_BYTE_ORDER == Q_BIG_ENDIAN) && (defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_15))
class quint16LE
{
public:
    inline quint16LE(quint32 v) {
        data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8);
    }

    inline quint16LE(int v) {
        data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8);
    }

    inline quint16LE(quint16 v) {
        data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8);
    }

    inline quint16LE(qrgb555 v) {
        data = (( (quint16)v & 0xff00) >> 8) |
               (( (quint16)v & 0x00ff) << 8);
    }

    inline bool operator==(const quint16LE &v) const
    {
        return data == v.data;
    }

private:
    quint16 data;
};
#endif

#ifdef QT_QWS_DEPTH_16
static void blit_16(QScreen *screen, const QImage &image,
                    const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        // ### This probably doesn't work but it's a case which should never happen
        blit_template<quint16, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<quint16, quint16>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_16(): Image format %d not supported!", image.format());
    }
}

#if Q_BYTE_ORDER == Q_BIG_ENDIAN
static void blit_16_bigToLittleEndian(QScreen *screen, const QImage &image,
                                      const QPoint &topLeft,
                                      const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<quint16LE, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<quint16LE, quint16>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_16_bigToLittleEndian(): Image format %d not supported!", image.format());
    }
}

#endif // Q_BIG_ENDIAN
#endif // QT_QWS_DEPTH_16

#ifdef QT_QWS_DEPTH_15
static void blit_15(QScreen *screen, const QImage &image,
                    const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<qrgb555, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB555:
        blit_template<qrgb555, qrgb555>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<qrgb555, quint16>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_15(): Image format %d not supported!", image.format());
    }
}

#if Q_BYTE_ORDER == Q_BIG_ENDIAN
static void blit_15_bigToLittleEndian(QScreen *screen, const QImage &image,
                                      const QPoint &topLeft,
                                      const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB555:
        blit_template<quint16LE, qrgb555>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_15_bigToLittleEndian(): Image format %d not supported!", image.format());
    }
}
#endif // Q_BIG_ENDIAN
#endif // QT_QWS_DEPTH_15


#ifdef QT_QWS_DEPTH_12
static void blit_12(QScreen *screen, const QImage &image,
                    const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_ARGB4444_Premultiplied:
        blit_template<qrgb444, qargb4444>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB444:
        blit_template<qrgb444, qrgb444>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_12(): Image format %d not supported!", image.format());
    }
}
#endif // QT_QWS_DEPTH_12

#ifdef QT_QWS_DEPTH_8
static void blit_8(QScreen *screen, const QImage &image,
                   const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<quint8, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<quint8, quint16>(screen, image, topLeft, region);
        return;
    case QImage::Format_ARGB4444_Premultiplied:
        blit_template<quint8, qargb4444>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB444:
        blit_template<quint8, qrgb444>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_8(): Image format %d not supported!", image.format());
    }
}
#endif // QT_QWS_DEPTH_8

#ifdef QT_QWS_DEPTH_4

struct qgray4 { quint8 dummy; } Q_PACKED;

template <typename SRC>
Q_STATIC_TEMPLATE_FUNCTION inline quint8 qt_convertToGray4(SRC color);

template <>
inline quint8 qt_convertToGray4(quint32 color)
{
    return qGray(color) >> 4;
}

template <>
inline quint8 qt_convertToGray4(quint16 color)
{
    const int r = (color & 0xf800) >> 11;
    const int g = (color & 0x07e0) >> 6; // only keep 5 bit
    const int b = (color & 0x001f);
    return (r * 11 + g * 16 + b * 5) >> 6;
}

template <>
inline quint8 qt_convertToGray4(qrgb444 color)
{
    return qt_convertToGray4(quint32(color));
}

template <>
inline quint8 qt_convertToGray4(qargb4444 color)
{
    return qt_convertToGray4(quint32(color));
}

template <typename SRC>
Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectconvert_gray4(qgray4 *dest4, const SRC *src,
                                        int x, int y, int width, int height,
                                        int dstStride, int srcStride)
{
    const int pixelsPerByte = 2;
    quint8 *dest8 = reinterpret_cast<quint8*>(dest4)
                    + y * dstStride + x / pixelsPerByte;
    const int doAlign = x & 1;
    const int doTail = (width - doAlign) & 1;
    const int width8 = (width - doAlign) / pixelsPerByte;
    const int count8 = (width8 + 3) / 4;

    srcStride = srcStride / sizeof(SRC) - width;
    dstStride -= (width8 + doAlign);

    for (int i = 0; i < height; ++i) {
        if (doAlign) {
            *dest8 = (*dest8 & 0xf0) | qt_convertToGray4<SRC>(*src++);
            ++dest8;
        }
        if (count8) {
            int n = count8;
            switch (width8 & 0x03) // duff's device
            {
            case 0: do { *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
                                    | qt_convertToGray4<SRC>(src[1]);
                         src += 2;
            case 3:      *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
                                    | qt_convertToGray4<SRC>(src[1]);
                         src += 2;
            case 2:      *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
                                    | qt_convertToGray4<SRC>(src[1]);
                         src += 2;
            case 1:      *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
                                    | qt_convertToGray4<SRC>(src[1]);
                         src += 2;
            } while (--n > 0);
            }
        }

        if (doTail)
            *dest8 = qt_convertToGray4<SRC>(*src++) << 4 | (*dest8 & 0x0f);

        dest8 += dstStride;
        src += srcStride;
    }
}

template <>
void qt_rectconvert(qgray4 *dest, const quint32 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_gray4<quint32>(dest, src, x, y, width, height,
                                  dstStride, srcStride);
}

template <>
void qt_rectconvert(qgray4 *dest, const quint16 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_gray4<quint16>(dest, src, x, y, width, height,
                                  dstStride, srcStride);
}

template <>
void qt_rectconvert(qgray4 *dest, const qrgb444 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_gray4<qrgb444>(dest, src, x, y, width, height,
                                  dstStride, srcStride);
}

template <>
void qt_rectconvert(qgray4 *dest, const qargb4444 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_gray4<qargb4444>(dest, src, x, y, width, height,
                                    dstStride, srcStride);
}

static void blit_4(QScreen *screen, const QImage &image,
                   const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<qgray4, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<qgray4, quint16>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB444:
        blit_template<qgray4, qrgb444>(screen, image, topLeft, region);
        return;
    case QImage::Format_ARGB4444_Premultiplied:
        blit_template<qgray4, qargb4444>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_4(): Image format %d not supported!", image.format());
    }
}
#endif // QT_QWS_DEPTH_4

#ifdef QT_QWS_DEPTH_1

struct qmono { quint8 dummy; } Q_PACKED;

template <typename SRC>
Q_STATIC_TEMPLATE_FUNCTION inline quint8 qt_convertToMono(SRC color);

template <>
inline quint8 qt_convertToMono(quint32 color)
{
    return qGray(color) >> 7;
}

template <>
inline quint8 qt_convertToMono(quint16 color)
{
    return (qGray(qt_colorConvert<quint32, quint16>(color, 0)) >> 7);
}

template <>
inline quint8 qt_convertToMono(qargb4444 color)
{
    return (qGray(quint32(color)) >> 7);
}

template <>
inline quint8 qt_convertToMono(qrgb444 color)
{
    return (qGray(quint32(color)) >> 7);
}

template <typename SRC>
inline void qt_rectconvert_mono(qmono *dest, const SRC *src,
                                       int x, int y, int width, int height,
                                       int dstStride, int srcStride)
{
    const int pixelsPerByte = 8;
    quint8 *dest8 = reinterpret_cast<quint8*>(dest)
                    + y * dstStride + x / pixelsPerByte;
    const int alignWidth = qMin(width, (8 - (x & 7)) & 7);
    const int doAlign = (alignWidth > 0 ? 1 : 0);
    const int alignStart = pixelsPerByte - 1 - (x & 7);
    const int alignStop = alignStart - (alignWidth - 1);
    const quint8 alignMask = ((1 << alignWidth) - 1) << alignStop;
    const int tailWidth = (width - alignWidth) & 7;
    const int doTail = (tailWidth > 0 ? 1 : 0);
    const quint8 tailMask = (1 << (pixelsPerByte - tailWidth)) - 1;
    const int width8 = (width - alignWidth) / pixelsPerByte;

    srcStride = srcStride / sizeof(SRC) - (width8 * 8 + alignWidth);
    dstStride -= (width8 + doAlign);

    for (int j = 0;  j < height; ++j) {
        if (doAlign) {
            quint8 d = *dest8 & ~alignMask;
            for (int i = alignStart; i >= alignStop; --i)
                d |= qt_convertToMono<SRC>(*src++) << i;
            *dest8++ = d;
        }
        for (int i = 0; i < width8; ++i) {
            *dest8 = (qt_convertToMono<SRC>(src[0]) << 7)
                     | (qt_convertToMono<SRC>(src[1]) << 6)
                     | (qt_convertToMono<SRC>(src[2]) << 5)
                     | (qt_convertToMono<SRC>(src[3]) << 4)
                     | (qt_convertToMono<SRC>(src[4]) << 3)
                     | (qt_convertToMono<SRC>(src[5]) << 2)
                     | (qt_convertToMono<SRC>(src[6]) << 1)
                     | (qt_convertToMono<SRC>(src[7]));
            src += 8;
            ++dest8;
        }
        if (doTail) {
            quint8 d = *dest8 & tailMask;
            switch (tailWidth) {
            case 7: d |= qt_convertToMono<SRC>(src[6]) << 1;
            case 6: d |= qt_convertToMono<SRC>(src[5]) << 2;
            case 5: d |= qt_convertToMono<SRC>(src[4]) << 3;
            case 4: d |= qt_convertToMono<SRC>(src[3]) << 4;
            case 3: d |= qt_convertToMono<SRC>(src[2]) << 5;
            case 2: d |= qt_convertToMono<SRC>(src[1]) << 6;
            case 1: d |= qt_convertToMono<SRC>(src[0]) << 7;
            }
            *dest8 = d;
        }

        dest8 += dstStride;
        src += srcStride;
    }
}

template <>
void qt_rectconvert(qmono *dest, const quint32 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_mono<quint32>(dest, src, x, y, width, height,
                                 dstStride, srcStride);
}

template <>
void qt_rectconvert(qmono *dest, const quint16 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_mono<quint16>(dest, src, x, y, width, height,
                                 dstStride, srcStride);
}

template <>
void qt_rectconvert(qmono *dest, const qrgb444 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_mono<qrgb444>(dest, src, x, y, width, height,
                                 dstStride, srcStride);
}

template <>
void qt_rectconvert(qmono *dest, const qargb4444 *src,
                    int x, int y, int width, int height,
                    int dstStride, int srcStride)
{
    qt_rectconvert_mono<qargb4444>(dest, src, x, y, width, height,
                                   dstStride, srcStride);
}

static void blit_1(QScreen *screen, const QImage &image,
                   const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<qmono, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<qmono, quint16>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB444:
        blit_template<qmono, qrgb444>(screen, image, topLeft, region);
        return;
    case QImage::Format_ARGB4444_Premultiplied:
        blit_template<qmono, qargb4444>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_1(): Image format %d not supported!", image.format());
    }
}
#endif // QT_QWS_DEPTH_1

#ifdef QT_QWS_DEPTH_GENERIC

static void blit_rgb(QScreen *screen, const QImage &image,
                     const QPoint &topLeft, const QRegion &region)
{
    switch (image.format()) {
    case QImage::Format_ARGB32_Premultiplied:
        blit_template<qrgb, quint32>(screen, image, topLeft, region);
        return;
    case QImage::Format_RGB16:
        blit_template<qrgb, quint16>(screen, image, topLeft, region);
        return;
    default:
        qCritical("blit_rgb(): Image format %d not supported!", image.format());
    }
}

void qt_set_generic_blit(QScreen *screen, int bpp,
                         int len_red, int len_green, int len_blue, int len_alpha,
                         int off_red, int off_green, int off_blue, int off_alpha)
{
    qrgb::bpp = bpp / 8;
    qrgb::len_red = len_red;
    qrgb::len_green = len_green;
    qrgb::len_blue = len_blue;
    qrgb::len_alpha = len_alpha;
    qrgb::off_red = off_red;
    qrgb::off_green = off_green;
    qrgb::off_blue = off_blue;
    qrgb::off_alpha = off_alpha;
    screen->d_ptr->blit = blit_rgb;
    if (bpp == 16)
        screen->d_ptr->solidFill = solidFill_rgb_16bpp;
    else if (bpp == 32)
        screen->d_ptr->solidFill = solidFill_rgb_32bpp;
}

#endif // QT_QWS_DEPTH_GENERIC

void qt_blit_setup(QScreen *screen, const QImage &image,
                   const QPoint &topLeft, const QRegion &region)
{
    switch (screen->depth()) {
#ifdef QT_QWS_DEPTH_32
    case 32:
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->blit = blit_32;
        else
            screen->d_ptr->blit = blit_template<qabgr8888, quint32>;
        break;
#endif
#ifdef QT_QWS_DEPTH_24
    case 24:
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->blit = blit_qrgb888;
        else
            screen->d_ptr->blit = blit_24;
        break;
#endif
#ifdef QT_QWS_DEPTH_18
    case 18:
        screen->d_ptr->blit = blit_18;
        break;
#endif
#ifdef QT_QWS_DEPTH_16
    case 16:
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
        if (screen->d_ptr->fb_is_littleEndian)
            screen->d_ptr->blit = blit_16_bigToLittleEndian;
        else
#endif
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->blit = blit_16;
        else
            screen->d_ptr->blit = blit_template<qbgr565, quint16>;
        break;
#endif
#ifdef QT_QWS_DEPTH_15
    case 15:
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
        if (screen->d_ptr->fb_is_littleEndian)
            screen->d_ptr->blit = blit_15_bigToLittleEndian;
        else
#endif // Q_BIG_ENDIAN
        if (screen->pixelType() == QScreen::NormalPixel)
            screen->d_ptr->blit = blit_15;
        else
            screen->d_ptr->blit = blit_template<qbgr555, qrgb555>;
        break;
#endif
#ifdef QT_QWS_DEPTH_12
    case 12:
        screen->d_ptr->blit = blit_12;
        break;
#endif
#ifdef QT_QWS_DEPTH_8
    case 8:
        screen->d_ptr->blit = blit_8;
        break;
#endif
#ifdef QT_QWS_DEPTH_4
    case 4:
        screen->d_ptr->blit = blit_4;
        break;
#endif
#ifdef QT_QWS_DEPTH_1
    case 1:
        screen->d_ptr->blit = blit_1;
        break;
#endif
    default:
        qFatal("blit_setup(): Screen depth %d not supported!",
               screen->depth());
        screen->d_ptr->blit = 0;
        break;
    }
    screen->d_ptr->blit(screen, image, topLeft, region);
}

QScreenPrivate::QScreenPrivate(QScreen *parent, QScreen::ClassId id)
    : defaultGraphicsSystem(QWSGraphicsSystem(parent)),
      pixelFormat(QImage::Format_Invalid),
#ifdef QT_QWS_CLIENTBLIT
      supportsBlitInClients(false),
#endif
      classId(id), q_ptr(parent)
{
    solidFill = qt_solidFill_setup;
    blit = qt_blit_setup;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
    fb_is_littleEndian = false;
#endif
    pixmapFactory = 0;
    graphicsSystem = &defaultGraphicsSystem;
}

QScreenPrivate::~QScreenPrivate()
{
}

QImage::Format QScreenPrivate::preferredImageFormat() const
{
    if (pixelFormat > QImage::Format_Indexed8)
        return pixelFormat;

    if (q_ptr->depth() <= 16)
        return QImage::Format_RGB16;
    else
        return QImage::Format_ARGB32_Premultiplied;
}

/*!
    \class QScreen
    \ingroup qws

    \brief The QScreen class is a base class for screen drivers in
    Qt for Embedded Linux.

    Note that this class is only available in \l{Qt for Embedded Linux}.

    \l{Qt for Embedded Linux} provides ready-made drivers for several screen
    protocols, see the \l{Qt for Embedded Linux Display Management}{display
    management} documentation for details. Custom screen drivers can
    be implemented by subclassing the QScreen class and creating a
    screen driver plugin (derived from QScreenDriverPlugin). The
    default implementation of the QScreenDriverFactory class
    will automatically detect the plugin, and load the driver into the
    server application at run-time using Qt's \l {How to Create Qt
    Plugins}{plugin system}.

    When rendering, the default behavior is for each
    client to render its widgets as well as its decorations into
    memory, while the server copies the memory content to the device's
    framebuffer using the screen driver. See the \l{Qt for Embedded Linux
    Architecture} overview for details (note that it is possible for
    the clients to manipulate and control the underlying hardware
    directly as well).

    Starting with Qt 4.2, it is also possible to add an
    accelerated graphics driver to take advantage of available
    hardware resources. See the \l{Adding an Accelerated Graphics
    Driver to Qt for Embedded Linux} documentation for details.

    \tableofcontents

    \section1 Framebuffer Management

    When a \l{Qt for Embedded Linux} application starts running, it
    calls the screen driver's connect() function to map the
    framebuffer and the accelerated drivers that the graphics card
    control registers. The connect() function should then read out the
    parameters of the framebuffer and use them as required to set this
    class's protected variables.

    The initDevice() function can be reimplemented to initialize the
    graphics card. Note, however, that connect() is called \e before
    the initDevice() function, so, for some hardware configurations,
    some of the initialization that would normally be done in the
    initDevice() function might have to be done in the connect()
    function.

    Likewise, just before a \l{Qt for Embedded Linux} application
    exits, it calls the screen driver's disconnect() function. The
    server application will in addition call the shutdownDevice()
    function before it calls disconnect(). Note that the default
    implementation of the shutdownDevice() function only hides the
    mouse cursor.

    QScreen also provides the save() and restore() functions, making
    it possible to save and restore the state of the graphics
    card. Note that the default implementations do nothing. Hardware
    screen drivers should reimplement these functions to save (and
    restore) its registers, enabling switching between virtual
    consoles.

    In addition, you can use the base() function to retrieve a pointer
    to the beginning of the framebuffer, and the region() function to
    retrieve the framebuffer's region. Use the onCard() function to
    determine whether the framebuffer is within the graphics card's
    memory, and the totalSize() function to determine the size of the
    available graphics card memory (including the screen). Finally,
    you can use the offset() function to retrieve the offset between
    the framebuffer's coordinates and the application's coordinate
    system.

    \section1 Palette Management

    QScreen provides several functions to retrieve information about
    the color palette: The clut() function returns a pointer to the
    color lookup table (i.e. its color palette). Use the numCols()
    function to determine the number of entries in this table, and the
    alloc() function to retrieve the palette index of the color that
    is the closest match to a given RGB value.

    To determine if the screen driver supports a given color depth,
    use the supportsDepth() function that returns true of the
    specified depth is supported.

    \section1 Drawing on Screen

    When a screen update is required, the \l{Qt for Embedded Linux} server runs
    through all the top-level windows that intersect with the region
    that is about to be updated, and ensures that the associated
    clients have updated their memory buffer. Then the server calls
    the exposeRegion() function that composes the window surfaces and
    copies the content of memory to screen by calling the blit() and
    solidFill() functions.

    The blit() function copies a given region in a given image to a
    specified point using device coordinates, while the solidFill()
    function fills the given region of the screen with the specified
    color. Note that normally there is no need to call either of these
    functions explicitly.

    In addition, QScreen provides the blank() function that can be
    reimplemented to prevent any contents from being displayed on the
    screen, and the setDirty() function that can be reimplemented to
    indicate that a given rectangle of the screen has been
    altered. Note that the default implementations of these functions
    do nothing.

    Reimplement the mapFromDevice() and mapToDevice() functions to
    map objects from the framebuffer coordinate system to the
    coordinate space used by the application, and vice versa. Be aware
    that the default implementations simply return the given objects
    as they are.

    \section1 Properties

    \table
    \header \o Property \o Functions
    \row
    \o Size
    \o

    The size of the screen can be retrieved using the screenSize()
    function. The size is returned in bytes.

    The framebuffer's logical width and height can be retrieved using
    width() and height(), respectively. These functions return values
    are given in pixels. Alternatively, the physicalWidth() and
    physicalHeight() function returns the same metrics in
    millimeters. QScreen also provides the deviceWidth() and
    deviceHeight() functions returning the physical width and height
    of the device in pixels. Note that the latter metrics can differ
    from the ones used if the display is centered within the
    framebuffer.

    \row
    \o Resolution
    \o

    Reimplement the setMode() function to be able to set the
    framebuffer to a new resolution (width and height) and bit depth.

    The current depth of the framebuffer can be always be retrieved
    using the depth() function. Use the pixmapDepth() function to
    obtain the preferred depth for pixmaps.

    \row
    \o Pixmap Alignment
    \o

    Use the pixmapOffsetAlignment() function to retrieve the value to
    which the start address of pixmaps held in the graphics card's
    memory, should be aligned.

    Use the pixmapLinestepAlignment() to retrieve the value to which
    the \e {individual scanlines} of pixmaps should be aligned.

    \row
    \o Image Display
    \o

    The isInterlaced() function tells whether the screen is displaying
    images progressively, and the isTransformed() function whether it
    is rotated. The transformOrientation() function can be
    reimplemented to return the current rotation.

    \row
    \o Scanlines
    \o

    Use the linestep() function to retrieve the length of each
    scanline of the framebuffer.

    \row
    \o Pixel Type
    \o

    The pixelType() function returns the screen's pixel storage format as
    described by the PixelType enum.

    \endtable

    \section1 Subclassing and Initial Values

    You need to set the following members when implementing a subclass of QScreen:

    \table
    \header \o Member \o Initial Value
    \row \o \l{QScreen::}{data} \o A pointer to the framebuffer if possible;
    0 otherwise.
    \row \o \l{QScreen::}{lstep} \o The number of bytes between each scanline
    in the framebuffer.
    \row \o \l{QScreen::}{w} \o The logical screen width in pixels.
    \row \o \l{QScreen::}{h} \o The logical screen height in pixels.
    \row \o \l{QScreen::}{dw} \o The real screen width in pixels.
    \row \o \l{QScreen::}{dh} \o The real screen height in pixels.
    \row \o \l{QScreen::}{d} \o The number of bits per pixel.
    \row \o \l{QScreen::}{physWidth} \o The screen width in millimeters.
    \row \o \l{QScreen::}{physHeight} \o The screen height in millimeters.
    \endtable

    The logical screen values are the same as the real screen values unless the
    screen is transformed in some way; e.g., rotated.

    See also the \l{Accelerated Graphics Driver Example} for an example that
    shows how to initialize these values.

    \sa QScreenDriverPlugin, QScreenDriverFactory, {Qt for Embedded Linux Display
    Management}
*/

/*!
    \enum QScreen::PixelType

    This enum describes the pixel storage format of the screen,
    i.e. the order of the red (R), green (G) and blue (B) components
    of a pixel.

    \value NormalPixel Red-green-blue (RGB)
    \value BGRPixel Blue-green-red (BGR)

    \sa pixelType()
*/

/*!
    \enum QScreen::ClassId

    This enum defines the class identifiers for the known screen subclasses.

    \value LinuxFBClass QLinuxFBScreen
    \value TransformedClass QTransformedScreen
    \value VNCClass QVNCScreen
    \value MultiClass QMultiScreen
    \value VFbClass QVFbScreen
    \value DirectFBClass QDirectFBScreen
    \value SvgalibClass QSvgalibScreen
    \value ProxyClass QProxyScreen
    \value GLClass QGLScreen
    \value CustomClass Unknown QScreen subclass

    \sa classId()
*/

/*!
  \variable QScreen::screenclut
  \brief the color table

  Initialize this variable in a subclass using a paletted screen mode,
  and initialize its partner, QScreen::screencols.

  \sa screencols
*/

/*!
  \variable QScreen::screencols
  \brief the number of entries in the color table

  Initialize this variable in a subclass using a paletted screen mode,
  and initialize its partner, QScreen::screenclut.

  \sa screenclut
*/

/*!
  \variable QScreen::data
  \brief points to the first visible pixel in the frame buffer.

  You must initialize this variable if you are using the default
  implementation of non-buffered painting Qt::WA_PaintOnScreen,
  QPixmap::grabWindow() or QDirectPainter::frameBuffer(). If you
  initialize this variable, you must also initialize QScreen::size and
  QScreen::mapsize.

  \sa QScreen::size, QScreen::mapsize
*/

/*!
  \variable QScreen::w
  \brief the logical width of the screen.

  This variable \e{must} be initialized by a subclass.
*/

/*!
  \variable QScreen::lstep
  \brief the number of bytes representing a line in the frame buffer.

  i.e., \e{line step}. \c {data[lstep * 2]} is the address of the
  first visible pixel in the third line of the frame buffer.

  \sa data
*/

/*!
  \variable QScreen::h
  \brief the logical height of the screen.

  This variable \e{must} be initialized by a subclass.
*/

/*!
  \variable QScreen::d
  \brief the pixel depth

  This is the number of significant bits used to set a pixel
  color. This variable \e{must} be initialized by a subclass.
*/

/*!
  \variable QScreen::pixeltype
  \brief set to BGRPixel

  Set this variable to BGRPixel in a subclass, if the screen pixel
  format is a BGR type and you have used setPixelFormat() to set the
  pixel format to the corresponding RGB format. e.g., you have set the
  pixel format to QImage::Format_RGB555, but your screen really uses
  BGR, not RGB.
*/

/*!
  \variable QScreen::grayscale
  \brief the gray scale screen mode flag

  Set this variable to true in a subclass, if you are using a
  grayscale screen mode. e.g., in an 8-bit mode where you don't want
  to use the palette, but you want to use the grayscales.
*/

/*!
  \variable QScreen::dw
  \brief the device width

  This is the number of pixels in a row of the physical screen.  It
  \e{must} be initialized by a subclass. Normally, it should be set to
  the logical width QScreen::w, but it might be different, e.g., if
  you are doing rotations in software.

  \sa QScreen::w
*/

/*!
  \variable QScreen::dh
  \brief the device height

  This is the number of pixels in a column of the physical screen.  It
  \e{must} be initialized by a subclass. Normally, it should be set to
  the logical height QScreen::h, but it might be different, e.g., if
  you are doing rotations in software.

  \sa QScreen::h
*/

/*!
  \variable QScreen::size
  \brief the number of bytes in the visible region of the frame buffer

  This is the number of bytes in the visible part of the block pointed
  to by the QScreen::data pointer. You must initialize this variable
  if you initialize the QScreen::data pointer.

  \sa QScreen::data, QScreen::mapsize
*/

/*!
  \variable QScreen::mapsize
  \brief the total number of bytes in the frame buffer

  This is the total number of bytes in the block pointed to by the
  QScreen::data pointer. You must initialize this variable if you
  initialize the QScreen::data pointer.

  \sa QScreen::data, QScreen::size
*/

/*!
  \variable QScreen::physWidth
  \brief the physical width of the screen in millimeters.

  Currently, this variable is used when calculating the screen DPI,
  which in turn is used when deciding the actual font size Qt is
  using.
*/

/*!
  \variable QScreen::physHeight
  \brief the physical height of the screen in millimeters.

  Currently, this variable is used when calculating the screen DPI,
  which in turn is used when deciding the actual font size Qt is
  using.
*/

/*!
    \fn static QScreen* QScreen::instance()

    Returns a pointer to the application's QScreen instance.

    If this screen consists of several subscreens, operations to the
    returned instance will affect all its subscreens. Use the
    subscreens() function to retrieve access to a particular
    subscreen.

    \sa subScreens(), subScreenIndexAt()
*/

/*!
    \fn QList<QScreen*> QScreen::subScreens() const
    \since 4.2

    Returns a list of this screen's subscreens. Use the
    subScreenIndexAt() function to retrieve the index of a screen at a
    given position.

    Note that if \e this screen consists of several subscreens,
    operations to \e this instance will affect all subscreens by
    default.

    \sa instance(), subScreenIndexAt()
*/

/*!
    \fn int QScreen::physicalWidth() const
    \since 4.2

    Returns the physical width of the screen in millimeters.

    \sa width(), deviceWidth(), physicalHeight()
*/

/*!
    \fn int QScreen::physicalHeight() const
    \since 4.2

    Returns the physical height of the screen in millimeters.

    \sa height(), deviceHeight(), physicalWidth()
*/

/*!
    \fn virtual bool QScreen::initDevice() = 0

    This function is called by the \l{Qt for Embedded Linux} server to
    initialize the framebuffer. Note that a server application will call the
    connect() function prior to this function.

    Implement this function to make accelerated drivers set up the
    graphics card. Return true to indicate success and false to indicate
    failure.

    \sa shutdownDevice(), connect()
*/

/*!
    \fn virtual bool QScreen::connect(const QString &displaySpec) = 0

    This function is called by every \l{Qt for Embedded Linux}
    application on startup, and must be implemented to map in the
    framebuffer and the accelerated drivers that the graphics card
    control registers.  Note that coonnect must be called \e before
    the initDevice() function.

    Ensure that true is returned if a connection to the screen device
    is made. Otherwise, return false. Upon making the connection, the
    function should read out the parameters of the framebuffer and use
    them as required to set this class's protected variables.

    The \a displaySpec argument is passed by the QWS_DISPLAY
    environment variable or the -display command line parameter, and
    has the following syntax:

    \snippet doc/src/snippets/code/src_gui_embedded_qscreen_qws.cpp 0

    For example, to use the mach64 driver on fb1 as display 2:

    \snippet doc/src/snippets/code/src_gui_embedded_qscreen_qws.cpp 1

    See \l{Qt for Embedded Linux Display Management} for more details.

    \sa disconnect(), initDevice(), {Running Qt for Embedded Linux Applications}
*/

/*!
    \fn QScreen::disconnect()

    This function is called by every \l{Qt for Embedded Linux} application
    before exiting, and must be implemented to unmap the
    framebuffer. Note that a server application will call the
    shutdownDevice() function prior to this function.

    \sa connect(), shutdownDevice(), {Running Qt for Embedded Linux
    Applications}
*/

/*!
    \fn QScreen::setMode(int width, int height, int depth)

    Implement this function to reset the framebuffer's resolution (\a
    width and \a height) and bit \a depth.

    After the resolution has been set, existing paint engines will be
    invalid and the framebuffer should be completely redrawn. In a
    multiple-process situation, all other applications must be
    notified to reset their mode and update themselves accordingly.
*/

/*!
    \fn QScreen::blank(bool on)

    Prevents the screen driver form displaying any content on the
    screen.

    Note that the default implementation does nothing.

    Reimplement this function to prevent the screen driver from
    displaying any contents on the screen if \a on is true; otherwise
    the contents is expected to be shown.

    \sa blit()
*/

/*!
    \fn int QScreen::pixmapOffsetAlignment()

    Returns the value (in bits) to which the start address of pixmaps
    held in the graphics card's memory, should be aligned.

    Note that the default implementation returns 64; reimplement this
    function to override the return value, e.g., when implementing an
    accelerated driver (see the \l {Adding an Accelerated Graphics
    Driver to Qt for Embedded Linux}{Adding an Accelerated Graphics Driver}
    documentation for details).

    \sa pixmapLinestepAlignment()
*/

/*!
    \fn int QScreen::pixmapLinestepAlignment()

    Returns the value (in bits) to which individual scanlines of
    pixmaps held in the graphics card's memory, should be
    aligned.

    Note that the default implementation returns 64; reimplement this
    function to override the return value, e.g., when implementing an
    accelerated driver (see the \l {Adding an Accelerated Graphics
    Driver to Qt for Embedded Linux}{Adding an Accelerated Graphics Driver}
    documentation for details).

    \sa pixmapOffsetAlignment()
*/

/*!
    \fn QScreen::width() const

    Returns the logical width of the framebuffer in pixels.

    \sa deviceWidth(), physicalWidth(), height()
*/

/*!
    \fn int QScreen::height() const

    Returns the logical height of the framebuffer in pixels.

    \sa deviceHeight(), physicalHeight(), width()
*/

/*!
    \fn QScreen::depth() const

    Returns the depth of the framebuffer, in bits per pixel.

    Note that the returned depth is the number of bits each pixel
    fills rather than the number of significant bits, so 24bpp and
    32bpp express the same range of colors (8 bits of red, green and
    blue).

    \sa clut(), pixmapDepth()
*/

/*!
    \fn int QScreen::pixmapDepth() const

    Returns the preferred depth for pixmaps, in bits per pixel.

    \sa depth()
*/

/*!
    \fn QScreen::linestep() const

    Returns the length of each scanline of the framebuffer in bytes.

    \sa isInterlaced()
*/

/*!
    \fn QScreen::deviceWidth() const

    Returns the physical width of the framebuffer device in pixels.

    Note that the returned width can differ from the width which
    \l{Qt for Embedded Linux} will actually use, that is if the display is
    centered within the framebuffer.

    \sa width(), physicalWidth(), deviceHeight()
*/

/*!
    \fn QScreen::deviceHeight() const

    Returns the full height of the framebuffer device in pixels.

    Note that the returned height can differ from the height which
    \l{Qt for Embedded Linux} will actually use, that is if the display is
    centered within the framebuffer.

    \sa height(), physicalHeight(), deviceWidth()
*/

/*!
    \fn uchar *QScreen::base() const

    Returns a pointer to the beginning of the framebuffer.

    \sa onCard(), region(), totalSize()
*/

/*!
    \fn uchar *QScreen::cache(int)

    \internal

    This function is used to store pixmaps in graphics memory for the
    use of the accelerated drivers. See QLinuxFbScreen (where the
    caching is implemented) for more information.
*/

/*!
    \fn QScreen::uncache(uchar *)

    \internal

    This function is called on pixmap destruction to remove them from
    graphics card memory.
*/

/*!
    \fn QScreen::screenSize() const

    Returns the size of the screen in bytes.

    The screen size is always located at the beginning of framebuffer
    memory, i.e. it can also be retrieved using the base() function.

    \sa base(), region()
*/

/*!
    \fn QScreen::totalSize() const

    Returns the size of the available graphics card memory (including
    the screen) in bytes.

    \sa onCard()
*/

// Unaccelerated screen/driver setup. Can be overridden by accelerated
// drivers

/*!
    \fn QScreen::QScreen(int displayId)

    Constructs a new screen driver.

    The \a displayId identifies the \l{Qt for Embedded Linux} server to connect
    to.
*/

/*!
    \fn QScreen::clut()

    Returns a pointer to the screen's color lookup table (i.e. its
    color palette).

    Note that this function only apply in paletted modes like 8-bit,
    i.e. in modes where only the palette indexes (and not the actual
    color values) are stored in memory.

    \sa alloc(), depth(), numCols()
*/

/*!
    \fn int QScreen::numCols()

    Returns the number of entries in the screen's color lookup table
    (i.e. its color palette). A pointer to the color table can be
    retrieved using the clut() function.

    \sa clut(), alloc()
*/

/*!
    \since 4.4

    Constructs a new screen driver.

    The \a display_id identifies the \l{Qt for Embedded Linux}
    server to connect to. The \a classId specifies the class
    identifier.
*/
QScreen::QScreen(int display_id, ClassId classId)
    : screencols(0), data(0), entries(0), entryp(0), lowest(0),
      w(0), lstep(0), h(0), d(1), pixeltype(NormalPixel), grayscale(false),
      dw(0), dh(0), size(0), mapsize(0), displayId(display_id),
      physWidth(0), physHeight(0), d_ptr(new QScreenPrivate(this, classId))
{
    clearCacheFunc = 0;
}

QScreen::QScreen(int display_id)
    : screencols(0), data(0), entries(0), entryp(0), lowest(0),
      w(0), lstep(0), h(0), d(1), pixeltype(NormalPixel), grayscale(false),
      dw(0), dh(0), size(0), mapsize(0), displayId(display_id),
      physWidth(0), physHeight(0), d_ptr(new QScreenPrivate(this))
{
    clearCacheFunc = 0;
}

/*!
    Destroys this screen driver.
*/

QScreen::~QScreen()
{
    delete d_ptr;
}

/*!
    This function is called by the \l{Qt for Embedded Linux} server before it
    calls the disconnect() function when exiting.

    Note that the default implementation only hides the mouse cursor;
    reimplement this function to do the necessary graphics card
    specific cleanup.

    \sa initDevice(), disconnect()
*/

void QScreen::shutdownDevice()
{
#ifndef QT_NO_QWS_CURSOR
    if (qt_screencursor)
        qt_screencursor->hide();
#endif
}

extern bool qws_accel; //in qapplication_qws.cpp

/*!
    \fn PixelType QScreen::pixelType() const

    Returns the pixel storage format of the screen.
*/

/*!
  Returns the pixel format of the screen, or \c QImage::Format_Invalid
  if the pixel format is not a supported image format.

*/
QImage::Format QScreen::pixelFormat() const
{
    return d_ptr->pixelFormat;
}

/*!
  Sets the screen's pixel format to \a format.
 */
void QScreen::setPixelFormat(QImage::Format format)
{
    d_ptr->pixelFormat = format;
}


/*!
    \fn int QScreen::alloc(unsigned int red, unsigned int green, unsigned int blue)

    Returns the index in the screen's palette which is the closest
    match to the given RGB value (\a red, \a green, \a blue).

    Note that this function only apply in paletted modes like 8-bit,
    i.e. in modes where only the palette indexes (and not the actual
    color values) are stored in memory.

    \sa clut(), numCols()
*/

int QScreen::alloc(unsigned int r,unsigned int g,unsigned int b)
{
    int ret = 0;
    if (d == 8) {
        if (grayscale)
            return qGray(r, g, b);

        // First we look to see if we match a default color
        const int pos = (r + 25) / 51 * 36 + (g + 25) / 51 * 6 + (b + 25) / 51;
        if (pos < screencols && screenclut[pos] == qRgb(r, g, b)) {
            return pos;
        }

        // search for nearest color
        unsigned int mindiff = 0xffffffff;
        unsigned int diff;
        int dr,dg,db;

        for (int loopc = 0; loopc < screencols; ++loopc) {
            dr = qRed(screenclut[loopc]) - r;
            dg = qGreen(screenclut[loopc]) - g;
            db = qBlue(screenclut[loopc]) - b;
            diff = dr*dr + dg*dg + db*db;

            if (diff < mindiff) {
                ret = loopc;
                if (!diff)
                    break;
                mindiff = diff;
            }
        }
    } else if (d == 4) {
        ret = qGray(r, g, b) >> 4;
    } else if (d == 1) {
        ret = qGray(r, g, b) >= 128;
    } else {
        qFatal("cannot alloc %dbpp color", d);
    }

    return ret;
}

/*!
    Saves the current state of the graphics card.

    For example, hardware screen drivers should reimplement the save()
    and restore() functions to save and restore its registers,
    enabling swintching between virtual consoles.

    Note that the default implementation does nothing.

    \sa restore()
*/

void QScreen::save()
{
}

/*!
    Restores the previously saved state of the graphics card.

    For example, hardware screen drivers should reimplement the save()
    and restore() functions to save and restore its registers,
    enabling swintching between virtual consoles.

    Note that the default implementation does nothing.

    \sa save()
*/

void QScreen::restore()
{
}

void QScreen::blank(bool)
{
}

/*!
    \internal
*/

void QScreen::set(unsigned int, unsigned int, unsigned int, unsigned int)
{
}

/*!
    \fn bool QScreen::supportsDepth(int depth) const

    Returns true if the screen supports the specified color \a depth;
    otherwise returns false.

    \sa clut()
*/

bool QScreen::supportsDepth(int d) const
{
    if (false) {
        //Just to simplify the ifdeffery
#ifdef QT_QWS_DEPTH_1
    } else if(d==1) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_4
    } else if(d==4) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_8
    } else if(d==8) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_16
    } else if(d==16) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_15
    } else if (d == 15) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_18
    } else if(d==18 || d==19) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_24
    } else if(d==24) {
        return true;
#endif
#ifdef QT_QWS_DEPTH_32
    } else if(d==32) {
        return true;
#endif
    }
    return false;
}

/*!
    \fn bool QScreen::onCard(const unsigned char *buffer) const

    Returns true if the specified \a buffer is within the graphics
    card's memory; otherwise returns false (i.e. if it's in main RAM).

    \sa base(), totalSize()
*/

bool QScreen::onCard(const unsigned char * p) const
{
    long t=(unsigned long)p;
    long bmin=(unsigned long)data;
    if (t < bmin)
        return false;
    if(t >= bmin+mapsize)
        return false;
    return true;
}

/*!
    \fn bool QScreen::onCard(const unsigned char * buffer, ulong& offset) const
    \overload

    If the specified \a buffer is within the graphics card's memory,
    this function stores the offset from the start of graphics card
    memory (in bytes), in the location specified by the \a offset
    parameter.
*/

bool QScreen::onCard(const unsigned char * p, ulong& offset) const
{
    long t=(unsigned long)p;
    long bmin=(unsigned long)data;
    if (t < bmin)
        return false;
    long o = t - bmin;
    if (o >= mapsize)
        return false;
    offset = o;
    return true;
}

/*
#if !defined(QT_NO_QWS_REPEATER)
    { "Repeater", qt_get_screen_repeater, 0 },
#endif
#if defined(QT_QWS_EE)
    { "EE", qt_get_screen_ee, 0 },
#endif

*/

/*
Given a display_id (number of the \l{Qt for Embedded Linux} server to connect to)
and a spec (e.g. Mach64:/dev/fb0) return a QScreen-descendant.
The QScreenDriverFactory is queried for a suitable driver and, if found,
asked to create a driver.
People writing new graphics drivers should either hook their own
QScreen-descendant into QScreenDriverFactory or use the QScreenDriverPlugin
to make a dynamically loadable driver.
*/

Q_GUI_EXPORT QScreen* qt_get_screen(int display_id, const char *spec)
{
    QString displaySpec = QString::fromAscii(spec);
    QString driver = displaySpec;
    int colon = displaySpec.indexOf(QLatin1Char(':'));
    if (colon >= 0)
        driver.truncate(colon);
    driver = driver.trimmed();

    bool foundDriver = false;
    QString driverName = driver;

    QStringList driverList;
    if (!driver.isEmpty())
        driverList << driver;
    else
        driverList = QScreenDriverFactory::keys();

    for (int i = 0; i < driverList.size(); ++i) {
        const QString driverName = driverList.at(i);
        qt_screen = QScreenDriverFactory::create(driverName, display_id);
        if (qt_screen) {
            foundDriver = true;
            if (qt_screen->connect(displaySpec)) {
                return qt_screen;
            } else {
                delete qt_screen;
                qt_screen = 0;
            }
        }
    }

    if (driver.isNull())
        qFatal("No suitable driver found");
    else if (foundDriver)
        qFatal("%s: driver cannot connect", driver.toLatin1().constData());
    else
        qFatal("%s: driver not found", driver.toLatin1().constData());

    return 0;
}

#ifndef QT_NO_QWS_CURSOR
static void blendCursor(QImage *dest, const QImage &cursor, const QPoint &offset)
{
    QRasterBuffer rb;
    rb.prepare(dest);

    QSpanData spanData;
    spanData.init(&rb, 0);
    spanData.type = QSpanData::Texture;
    spanData.initTexture(&cursor, 256);
    spanData.dx = -offset.x();
    spanData.dy = -offset.y();
    if (!spanData.blend)
        return;

    const QRect rect = QRect(offset, cursor.size())
                       & QRect(QPoint(0, 0), dest->size());
    const int w = rect.width();
    const int h = rect.height();

    QVarLengthArray<QT_FT_Span, 32> spans(h);
    for (int i = 0; i < h; ++i) {
        spans[i].x = rect.x();
        spans[i].len = w;
        spans[i].y = rect.y() + i;
        spans[i].coverage = 255;
    }
    spanData.blend(h, spans.constData(), &spanData);
}
#endif // QT_NO_QWS_CURSOR

/*!
    \fn void QScreen::exposeRegion(QRegion region, int windowIndex)

    This function is called by the \l{Qt for Embedded Linux} server whenever a
    screen update is required. \a region is the area on the screen
    that must be updated, and \a windowIndex is the index into
    QWSServer::clientWindows() of the window that required the
    update. QWSWindow::state() gives more information about the cause.

    The default implementation composes the
    affected windows and paints the given \a region on screen by
    calling the blit() and solidFill() functions

    This function can be reimplemented to perform composition in
    hardware, or to perform transition effects.
    For simpler hardware acceleration, or to interface with
    this is typically done by reimplementing the blit() and
    solidFill() functions instead.

    Note that there is no need to call this function explicitly.

    \sa blit(), solidFill(), blank()
*/
void QScreen::exposeRegion(QRegion r, int windowIndex)
{
    r &= region();
    if (r.isEmpty())
        return;

    int changing = windowIndex;
    // when we have just lowered a window, we have to expose all the windows below where the
    // window used to be.
    if (changing && qwsServer->clientWindows().at(changing)->state() == QWSWindow::Lowering)
        changing = 0;
#ifdef QTOPIA_PERFTEST
    static enum { PerfTestUnknown, PerfTestOn, PerfTestOff } perfTestState = PerfTestUnknown;
    if(PerfTestUnknown == perfTestState) {
        if(::getenv("QTOPIA_PERFTEST"))
            perfTestState = PerfTestOn;
        else
            perfTestState = PerfTestOff;
    }
    if(PerfTestOn == perfTestState) {
        QWSWindow *changed = qwsServer->clientWindows().at(changing);
        if(!changed->client()->identity().isEmpty())
            qDebug() << "Performance  :  expose_region  :"
                     << changed->client()->identity()
                     << r.boundingRect() << ": "
                     << qPrintable( QTime::currentTime().toString( "h:mm:ss.zzz" ) );
    }
#endif

    const QRect bounds = r.boundingRect();
    QRegion blendRegion;
    QImage *blendBuffer = 0;

#ifndef QT_NO_QWS_CURSOR
    if (qt_screencursor && !qt_screencursor->isAccelerated()) {
        blendRegion = r & qt_screencursor->boundingRect();
    }
#endif
    compose(0, r, blendRegion, &blendBuffer, changing);

    if (blendBuffer && !blendBuffer->isNull()) {
        const QPoint offset = blendRegion.boundingRect().topLeft();
#ifndef QT_NO_QWS_CURSOR
        if (qt_screencursor && !qt_screencursor->isAccelerated()) {
            const QRect cursorRect = qt_screencursor->boundingRect();
            if (blendRegion.intersects(cursorRect)) {
                blendCursor(blendBuffer, qt_screencursor->image(),
                            cursorRect.topLeft() - offset);
            }
        }
#endif // QT_NO_QWS_CURSOR
        blit(*blendBuffer, offset, blendRegion);
        delete blendBuffer;
    }

    if (r.numRects() == 1) {
        setDirty(r.boundingRect());
    } else {
        const QVector<QRect> rects = r.rects();
        for (int i = 0; i < rects.size(); ++i)
            setDirty(rects.at(i));
    }
}

/*!
    \fn void QScreen::blit(const QImage &image, const QPoint &topLeft, const QRegion &region)

    Copies the given \a region in the given \a image to the point
    specified by \a topLeft using device coordinates.

    This function is called from the exposeRegion() function; it is
    not intended to be called explicitly.

    Reimplement this function to make use of \l{Adding an Accelerated
    Graphics Driver to Qt for Embedded Linux}{accelerated hardware}. Note that
    this function must be reimplemented if the framebuffer format is
    not supported by \l{Qt for Embedded Linux} (See the
    \l{Qt for Embedded Linux Display Management}{Display Management}
    documentation for more details).

    \sa exposeRegion(), solidFill(), blank()
*/
void QScreen::blit(const QImage &img, const QPoint &topLeft, const QRegion &reg)
{
    const QRect bound = (region() & QRect(topLeft, img.size())).boundingRect();
    QWSDisplay::grab();
    d_ptr->blit(this, img, topLeft - offset(),
            (reg & bound).translated(-topLeft));
    QWSDisplay::ungrab();
}

#ifdef QT_QWS_CLIENTBLIT
/*!
  Returns true if this screen driver supports calling QScreen::blit() and
  QScreen::setDirty() directly from non-server applications, otherwise returns
  false.

  If available, this is used to optimize the performance of non-occluded, opaque
  client windows by removing the server round trip when they are updated.

  \sa setSupportsBlitInClients()
 */
bool QScreen::supportsBlitInClients() const
{
    return d_ptr->supportsBlitInClients;
}

/*!
  If \a supported, the screen driver is marked as supporting blitting directly
  from non-server applications.

  \sa supportsBlitInClients()
 */
void QScreen::setSupportsBlitInClients(bool supported)
{
    d_ptr->supportsBlitInClients = supported;
}
#endif

/*!
    \internal
*/

void QScreen::blit(QWSWindow *win, const QRegion &clip)
{
    QWSWindowSurface *surface = win->windowSurface();
    if (!surface)
        return;

    const QImage &img = surface->image();
    if (img.isNull())
        return;

    const QRegion rgn = clip & win->paintedRegion();
    if (rgn.isEmpty())
        return;

    surface->lock();
    blit(img, win->requestedRegion().boundingRect().topLeft(), rgn);
    surface->unlock();
}

struct fill_data {
    quint32 color;
    uchar *data;
    int lineStep;
    int x;
    int y;
    int w;
    int h;
};

/*!
    Fills the given \a region of the screen with the specified \a
    color.

    This function is called from the exposeRegion() function; it is
    not intended to be called explicitly.

    Reimplement this function to make use of \l{Adding an Accelerated
    Graphics Driver to Qt for Embedded Linux}{accelerated hardware}. Note that
    this function must be reimplemented if the framebuffer format is
    not supported by \l{Qt for Embedded Linux} (See the
    \l{Qt for Embedded Linux Display Management}{Display Management}
    documentation for more details).

    \sa exposeRegion(), blit(), blank()
*/
// the base class implementation works in device coordinates, so that transformed drivers can use it
void QScreen::solidFill(const QColor &color, const QRegion &region)
{
    QWSDisplay::grab();
    d_ptr->solidFill(this, color,
                     region.translated(-offset()) & QRect(0, 0, dw, dh));
    QWSDisplay::ungrab();
}

/*!
    \since 4.2

    Creates and returns a new window surface matching the given \a
    key.

    The server application will call this function whenever it needs
    to create a server side representation of a window, e.g. when
    copying the content of memory to the screen using the screen
    driver.

    Note that this function must be reimplemented when adding an
    accelerated graphics driver. See the
    \l{Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
    {Adding an Accelerated Graphics Driver} documentation for details.

    \sa {Qt for Embedded Linux Architecture}
*/
QWSWindowSurface* QScreen::createSurface(const QString &key) const
{
#ifndef QT_NO_PAINTONSCREEN
    if (key == QLatin1String("OnScreen"))
        return new QWSOnScreenSurface;
    else
#endif
    if (key == QLatin1String("mem"))
        return new QWSLocalMemSurface;
#ifndef QT_NO_QWS_MULTIPROCESS
    else if (key == QLatin1String("shm"))
        return new QWSSharedMemSurface;
#endif
#ifndef QT_NO_PAINT_DEBUG
    else if (key == QLatin1String("Yellow"))
        return new QWSYellowSurface;
#endif
#ifndef QT_NO_DIRECTPAINTER
    else if (key == QLatin1String("DirectPainter"))
        return new QWSDirectPainterSurface;
#endif

    return 0;
}

#ifndef QT_NO_PAINTONSCREEN
bool QScreen::isWidgetPaintOnScreen(const QWidget *w)
{
    static int doOnScreen = -1;
    if (doOnScreen == -1) {
        const QByteArray env = qgetenv("QT_ONSCREEN_PAINT");
        if (env == "force")
            doOnScreen = 2;
        else
            doOnScreen = (env.toInt() > 0 ? 1 : 0);
    }

    if (doOnScreen == 2) // force
        return true;

    if (doOnScreen == 0 && !w->testAttribute(Qt::WA_PaintOnScreen))
        return false;

    return w->d_func()->isOpaque;
}
#endif

/*!
    \overload

    Creates and returns a new window surface for the given \a widget.
*/
QWSWindowSurface* QScreen::createSurface(QWidget *widget) const
{
#ifndef QT_NO_PAINTONSCREEN
    if (isWidgetPaintOnScreen(widget) && base())
        return new QWSOnScreenSurface(widget);
    else
#endif
    if (QApplication::type() == QApplication::GuiServer)
        return new QWSLocalMemSurface(widget);
#ifndef QT_NO_QWS_MULTIPROCESS
    else
        return new QWSSharedMemSurface(widget);
#endif

    return 0;
}

void QScreen::compose(int level, const QRegion &exposed, QRegion &blend,
                      QImage **blendbuffer, int changing_level)
{
    QRect exposed_bounds = exposed.boundingRect();
    QWSWindow *win = 0;
    do {
        win = qwsServer->clientWindows().value(level); // null is background
        ++level;
    } while (win && !win->paintedRegion().boundingRect().intersects(exposed_bounds));

    QWSWindowSurface *surface = (win ? win->windowSurface() : 0);
    bool above_changing = level <= changing_level; // 0 is topmost

    QRegion exposedBelow = exposed;
    bool opaque = true;

    if (win) {
        opaque = win->isOpaque() || !surface->isBuffered();
        if (opaque) {
            exposedBelow -= win->paintedRegion();
            if (above_changing || !surface->isBuffered())
                blend -= exposed & win->paintedRegion();
        } else {
            blend += exposed & win->paintedRegion();
        }
    }
    if (win && !exposedBelow.isEmpty()) {
        compose(level, exposedBelow, blend, blendbuffer, changing_level);
    } else {
        QSize blendSize = blend.boundingRect().size();
        if (!blendSize.isNull()) {
            *blendbuffer = new QImage(blendSize, d_ptr->preferredImageFormat());
        }
    }

    const QRegion blitRegion = exposed - blend;
    if (!win)
        paintBackground(blitRegion);
    else if (!above_changing && surface->isBuffered())
        blit(win, blitRegion);

    QRegion blendRegion = exposed & blend;

    if (win)
        blendRegion &= win->paintedRegion();
    if (!blendRegion.isEmpty()) {

        QPoint off = blend.boundingRect().topLeft();

        QRasterBuffer rb;
        rb.prepare(*blendbuffer);
        QSpanData spanData;
        spanData.init(&rb, 0);
        if (!win) {
            const QImage::Format format = (*blendbuffer)->format();
            switch (format) {
            case QImage::Format_ARGB32_Premultiplied:
            case QImage::Format_ARGB32:
            case QImage::Format_ARGB8565_Premultiplied:
            case QImage::Format_ARGB8555_Premultiplied:
            case QImage::Format_ARGB6666_Premultiplied:
            case QImage::Format_ARGB4444_Premultiplied:
                spanData.rasterBuffer->compositionMode = QPainter::CompositionMode_Source;
                break;
            default:
                break;
            }
            spanData.setup(qwsServer->backgroundBrush(), 256, QPainter::CompositionMode_SourceOver);
            spanData.dx = off.x();
            spanData.dy = off.y();
        } else if (!surface->isBuffered()) {
                return;
        } else {
            const QImage &img = surface->image();
            QPoint winoff = off - win->requestedRegion().boundingRect().topLeft();
            // convert win->opacity() from scale [0..255] to [0..256]
            int const_alpha = win->opacity();
            const_alpha += (const_alpha >> 7);
            spanData.type = QSpanData::Texture;
            spanData.initTexture(&img, const_alpha);
            spanData.dx = winoff.x();
            spanData.dy = winoff.y();
        }
        if (!spanData.blend)
            return;

        if (surface)
            surface->lock();
        const QVector<QRect> rects = blendRegion.rects();
        const int nspans = 256;
        QT_FT_Span spans[nspans];
        for (int i = 0; i < rects.size(); ++i) {
            int y = rects.at(i).y() - off.y();
            int ye = y + rects.at(i).height();
            int x = rects.at(i).x() - off.x();
            int len = rects.at(i).width();
            while (y < ye) {
                int n = qMin(nspans, ye - y);
                int i = 0;
                while (i < n) {
                    spans[i].x = x;
                    spans[i].len = len;
                    spans[i].y = y + i;
                    spans[i].coverage = 255;
                    ++i;
                }
                spanData.blend(n, spans, &spanData);
                y += n;
            }
        }
        if (surface)
            surface->unlock();
    }
}

void QScreen::paintBackground(const QRegion &r)
{
    const QBrush &bg = qwsServer->backgroundBrush();
    Qt::BrushStyle bs = bg.style();
    if (bs == Qt::NoBrush || r.isEmpty())
        return;

    if (bs == Qt::SolidPattern) {
        solidFill(bg.color(), r);
    } else {
        const QRect br = r.boundingRect();
        QImage img(br.size(), d_ptr->preferredImageFormat());
        QPoint off = br.topLeft();
        QRasterBuffer rb;
        rb.prepare(&img);
        QSpanData spanData;
        spanData.init(&rb, 0);
        spanData.setup(bg, 256, QPainter::CompositionMode_Source);
        spanData.dx = off.x();
        spanData.dy = off.y();
        Q_ASSERT(spanData.blend);

        const QVector<QRect> rects = r.rects();
        const int nspans = 256;
        QT_FT_Span spans[nspans];
        for (int i = 0; i < rects.size(); ++i) {
            int y = rects.at(i).y() - off.y();
            int ye = y + rects.at(i).height();
            int x = rects.at(i).x() - off.x();
            int len = rects.at(i).width();
            while (y < ye) {
                int n = qMin(nspans, ye - y);
                int i = 0;
                while (i < n) {
                    spans[i].x = x;
                    spans[i].len = len;
                    spans[i].y = y + i;
                    spans[i].coverage = 255;
                    ++i;
                }
                spanData.blend(n, spans, &spanData);
                y += n;
            }
        }
        blit(img, br.topLeft(), r);
    }
}

/*!
    \fn virtual int QScreen::sharedRamSize(void *)

    \internal
*/

/*!
    \fn QScreen::setDirty(const QRect& rectangle)

    Marks the given \a rectangle as dirty.

    Note that the default implementation does nothing; reimplement
    this function to indicate that the given \a rectangle has been
    altered.
*/

void QScreen::setDirty(const QRect&)
{
}

/*!
    \fn QScreen::isTransformed() const

    Returns true if the screen is transformed (for instance, rotated
    90 degrees); otherwise returns false.

    \sa transformOrientation(), isInterlaced()
*/

bool QScreen::isTransformed() const
{
    return false;
}

/*!
    \fn QScreen::isInterlaced() const

    Returns true if the display is interlaced (i.e. is displaying
    images progressively like a television screen); otherwise returns
    false.

    If the display is interlaced, the drawing is altered to look
    better.

    \sa isTransformed(), linestep()
*/

bool QScreen::isInterlaced() const
{
    return false;//qws_screen_is_interlaced;;
}

/*!
    \fn QScreen::mapToDevice(const QSize &size) const

    Maps the given \a size from the coordinate space used by the
    application to the framebuffer coordinate system. Note that the
    default implementation simply returns the given \a size as it is.

    Reimplement this function to use the given device's coordinate
    system when mapping.

    \sa mapFromDevice()
*/

QSize QScreen::mapToDevice(const QSize &s) const
{
    return s;
}

/*!
    \fn QScreen::mapFromDevice(const QSize &size) const

    Maps the given \a size from the framebuffer coordinate system to
    the coordinate space used by the application. Note that the
    default implementation simply returns the given \a size as it is.

    Reimplement this function to use the given device's coordinate
    system when mapping.

    \sa mapToDevice()
*/

QSize QScreen::mapFromDevice(const QSize &s) const
{
    return s;
}

/*!
    \fn QScreen::mapToDevice(const QPoint &point, const QSize &screenSize) const
    \overload

    Maps the given \a point from the coordinate space used by the
    application to the framebuffer coordinate system, passing the
    device's \a screenSize as argument. Note that the default
    implementation returns the given \a point as it is.
*/

QPoint QScreen::mapToDevice(const QPoint &p, const QSize &) const
{
    return p;
}

/*!
    \fn QScreen::mapFromDevice(const QPoint &point, const QSize &screenSize) const
    \overload

    Maps the given \a point from the framebuffer coordinate system to
    the coordinate space used by the application, passing the device's
    \a screenSize as argument. Note that the default implementation
    simply returns the given \a point as it is.
*/

QPoint QScreen::mapFromDevice(const QPoint &p, const QSize &) const
{
    return p;
}

/*!
    \fn QScreen::mapToDevice(const QRect &rectangle, const QSize &screenSize) const
    \overload

    Maps the given \a rectangle from the coordinate space used by the
    application to the framebuffer coordinate system, passing the
    device's \a screenSize as argument. Note that the default
    implementation returns the given \a rectangle as it is.
*/

QRect QScreen::mapToDevice(const QRect &r, const QSize &) const
{
    return r;
}

/*!
    \fn QScreen::mapFromDevice(const QRect &rectangle, const QSize &screenSize) const
    \overload

    Maps the given \a rectangle from the framebuffer coordinate system to
    the coordinate space used by the application, passing the device's
    \a screenSize as argument. Note that the default implementation
    simply returns the given \a rectangle as it is.
*/

QRect QScreen::mapFromDevice(const QRect &r, const QSize &) const
{
    return r;
}

/*!
    \fn QScreen::mapToDevice(const QImage &image) const
    \overload

    Maps the given \a image from the coordinate space used by the
    application to the framebuffer coordinate system. Note that the
    default implementation returns the given \a image as it is.
*/

QImage QScreen::mapToDevice(const QImage &i) const
{
    return i;
}

/*!
    \fn QScreen::mapFromDevice(const QImage &image) const
    \overload

    Maps the given \a image from the framebuffer coordinate system to
    the coordinate space used by the application. Note that the
    default implementation simply returns the given \a image as it is.
*/

QImage QScreen::mapFromDevice(const QImage &i) const
{
    return i;
}

/*!
    \fn QScreen::mapToDevice(const QRegion &region, const QSize &screenSize) const
    \overload

    Maps the given \a region from the coordinate space used by the
    application to the framebuffer coordinate system, passing the
    device's \a screenSize as argument. Note that the default
    implementation returns the given \a region as it is.
*/

QRegion QScreen::mapToDevice(const QRegion &r, const QSize &) const
{
    return r;
}

/*!
    \fn QScreen::mapFromDevice(const QRegion &region, const QSize &screenSize) const
    \overload

    Maps the given \a region from the framebuffer coordinate system to
    the coordinate space used by the application, passing the device's
    \a screenSize as argument. Note that the default implementation
    simply returns the given \a region as it is.
*/

QRegion QScreen::mapFromDevice(const QRegion &r, const QSize &) const
{
    return r;
}

/*!
    \fn QScreen::transformOrientation() const

    Returns the current rotation as an integer value.

    Note that the default implementation returns 0; reimplement this
    function to override this value.

    \sa isTransformed()
*/

int QScreen::transformOrientation() const
{
    return 0;
}

int QScreen::pixmapDepth() const
{
    return depth();
}

/*!
    \internal
*/
int QScreen::memoryNeeded(const QString&)
{
    return 0;
}

/*!
    \internal
*/
void QScreen::haltUpdates()
{
}

/*!
    \internal
*/
void QScreen::resumeUpdates()
{
}

/*!
    \fn QRegion QScreen::region() const
    \since 4.2

    Returns the region covered by this screen driver.

    \sa base(), screenSize()
*/

/*!
    \internal
*/
void QScreen::setOffset(const QPoint &p)
{
    d_ptr->offset = p;
}

/*!
    \since 4.2

    Returns the logical offset of the screen, i.e., the offset between
    (0,0) in screen coordinates and the application coordinate system.
*/
QPoint QScreen::offset() const
{
    return d_ptr->offset;
}

#if Q_BYTE_ORDER == Q_BIG_ENDIAN
void QScreen::setFrameBufferLittleEndian(bool littleEndian)
{
    d_ptr->fb_is_littleEndian = littleEndian;
}

bool QScreen::frameBufferLittleEndian() const
{
    return d_ptr->fb_is_littleEndian;
}
#endif

/*!
    \fn int QScreen::subScreenIndexAt(const QPoint &position) const
    \since 4.2

    Returns the index of the subscreen at the given \a position;
    returns -1 if no screen is found.

    The index identifies the subscreen in the list of pointers
    returned by the subScreens() function.

    \sa instance(), subScreens()
*/
int QScreen::subScreenIndexAt(const QPoint &p) const
{
    const QList<QScreen*> screens = subScreens();
    const int n = screens.count();
    for (int i = 0; i < n; ++i) {
        if (screens.at(i)->region().contains(p))
            return i;
    }

    return -1;
}

#if 0
#ifdef QT_LOADABLE_MODULES

// ### needs update after driver init changes

static QScreen * qt_dodriver(char * driver,char * a,unsigned char * b)

{
    char buf[200];
    strcpy(buf,"/etc/qws/drivers/");
    qstrcpy(buf+17,driver);
    qDebug("Attempting driver %s",driver);

    void * handle;
    handle=dlopen(buf,RTLD_LAZY);
    if(handle==0) {
        qFatal("Module load error");
    }
    QScreen *(*qt_get_screen_func)(char *,unsigned char *);
    qt_get_screen_func=dlsym(handle,"qt_get_screen");
    if(qt_get_screen_func==0) {
        qFatal("Couldn't get symbol");
    }
    QScreen * ret=qt_get_screen_func(a,b);
    return ret;
}

static QScreen * qt_do_entry(char * entry)
{
    unsigned char config[256];

    FILE * f=fopen(entry,"r");
    if(!f) {
        return 0;
    }

    int r=fread(config,256,1,f);
    if(r<1)
        return 0;

    fclose(f);

    unsigned short vendorid=*((unsigned short int *)config);
    unsigned short deviceid=*(((unsigned short int *)config)+1);
    if(config[0xb]!=3)
        return 0;

    if(vendorid==0x1002) {
        if(deviceid==0x4c4d) {
            qDebug("Compaq Armada/IBM Thinkpad's Mach64 card");
            return qt_dodriver("mach64.so",entry,config);
        } else if(deviceid==0x4742) {
            qDebug("Desktop Rage Pro Mach64 card");
            return qt_dodriver("mach64.so",entry,config);
        } else {
            qDebug("Unrecognised ATI card id %x",deviceid);
            return 0;
        }
    } else {
        qDebug("Unrecognised vendor");
    }
    return 0;
}

extern bool qws_accel;

/// ** NOT SUPPPORTED **

QScreen * qt_probe_bus()
{
    if(!qws_accel) {
        return qt_dodriver("unaccel.so",0,0);
    }

    DIR * dirptr=opendir("/proc/bus/pci");
    if(!dirptr)
        return qt_dodriver("unaccel.so",0,0);
    DIR * dirptr2;
    dirent * cards;

    dirent * busses=readdir(dirptr);

    while(busses) {
        if(busses->d_name[0]!='.') {
            char buf[100];
            strcpy(buf,"/proc/bus/pci/");
            qstrcpy(buf+14,busses->d_name);
            int p=strlen(buf);
            dirptr2=opendir(buf);
            if(dirptr2) {
                cards=readdir(dirptr2);
                while(cards) {
                    if(cards->d_name[0]!='.') {
                        buf[p]='/';
                        qstrcpy(buf+p+1,cards->d_name);
                        QScreen * ret=qt_do_entry(buf);
                        if(ret)
                            return ret;
                    }
                    cards=readdir(dirptr2);
                }
                closedir(dirptr2);
            }
        }
        busses=readdir(dirptr);
    }
    closedir(dirptr);

    return qt_dodriver("unaccel.so",0,0);
}

#else

char *qt_qws_hardcoded_slot = "/proc/bus/pci/01/00.0";

const unsigned char* qt_probe_bus()
{
    const char * slot;
    slot=::getenv("QWS_CARD_SLOT");
    if(!slot)
        slot=qt_qws_hardcoded_slot;
    if (slot) {
        static unsigned char config[256];
        FILE * f=fopen(slot,"r");
        if(!f) {
            qDebug("Open failure for %s",slot);
            slot=0;
        } else {
            int r=fread((char*)config,256,1,f);
            fclose(f);
            if(r<1) {
                qDebug("Read failure");
                return 0;
            } else {
                return config;
            }
        }
    }
    return 0;
}

#endif

#endif // 0

/*!
    \internal
    \since 4.4
*/
void QScreen::setPixmapDataFactory(QPixmapDataFactory *factory)
{
    static bool shownWarning = false;
    if (!shownWarning) {
        qWarning("QScreen::setPixmapDataFactory() is deprecated - use setGraphicsSystem() instead");
        shownWarning = true;
    }

    d_ptr->pixmapFactory = factory;
}

/*!
    \internal
    \since 4.4
*/
QPixmapDataFactory* QScreen::pixmapDataFactory() const
{
    return d_ptr->pixmapFactory;
}

/*!
    \internal
    \since 4.5
*/
void QScreen::setGraphicsSystem(QGraphicsSystem* system)
{
    d_ptr->graphicsSystem = system;
}

/*!
    \internal
    \since 4.5
*/
QGraphicsSystem* QScreen::graphicsSystem() const
{
    return d_ptr->graphicsSystem;
}

/*!
    \since 4.4

    Returns the class identifier for the screen object.
*/
QScreen::ClassId QScreen::classId() const
{
    return static_cast<ClassId>(d_ptr->classId);
}

QT_END_NAMESPACE
