src/gui/painting/qwindowsurface_qws.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/painting/qwindowsurface_qws.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1412 @@
+/****************************************************************************
+**
+** 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 "qwindowsurface_qws_p.h"
+#include <qwidget.h>
+#include <qscreen_qws.h>
+#include <qwsmanager_qws.h>
+#include <qapplication.h>
+#include <qwsdisplay_qws.h>
+#include <qrgb.h>
+#include <qpaintengine.h>
+#include <qdesktopwidget.h>
+#include <private/qapplication_p.h>
+#include <private/qwsdisplay_qws_p.h>
+#include <private/qwidget_p.h>
+#include <private/qwsmanager_p.h>
+#include <private/qwslock_p.h>
+#include <private/qbackingstore_p.h>
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+
+typedef QMap<int, QWSWindowSurface*> SurfaceMap;
+Q_GLOBAL_STATIC(SurfaceMap, winIdToSurfaceMap);
+
+QWSWindowSurface* qt_findWindowSurface(int winId)
+{
+    return winIdToSurfaceMap()->value(winId);
+}
+
+static void qt_insertWindowSurface(int winId, QWSWindowSurface *surface)
+{
+    if (!surface)
+        winIdToSurfaceMap()->remove(winId);
+    else
+        winIdToSurfaceMap()->insert(winId, surface);
+}
+
+#endif // Q_BACKINGSTORE_SUBSURFACES
+
+inline bool isWidgetOpaque(const QWidget *w)
+{
+    return w->d_func()->isOpaque;
+}
+
+static inline QScreen *getScreen(const QWidget *w)
+{
+    const QList<QScreen*> subScreens = qt_screen->subScreens();
+    if (subScreens.isEmpty())
+        return qt_screen;
+
+    const int screen = QApplication::desktop()->screenNumber(w);
+
+    return qt_screen->subScreens().at(screen < 0 ? 0 : screen);
+}
+
+static int bytesPerPixel(QImage::Format format)
+{
+    switch (format) {
+    case QImage::Format_Invalid:
+        return 0;
+#ifndef QT_NO_DEBUG
+    case QImage::Format_Mono:
+    case QImage::Format_MonoLSB:
+        qFatal("QWSWindowSurface: Invalid backingstore format: %i",
+               int(format));
+#endif
+    case QImage::Format_Indexed8:
+        return 1;
+    case QImage::Format_RGB32:
+    case QImage::Format_ARGB32:
+    case QImage::Format_ARGB32_Premultiplied:
+        return 4;
+    case QImage::Format_RGB16:
+    case QImage::Format_RGB555:
+    case QImage::Format_RGB444:
+    case QImage::Format_ARGB4444_Premultiplied:
+        return 2;
+    case QImage::Format_ARGB8565_Premultiplied:
+    case QImage::Format_ARGB8555_Premultiplied:
+    case QImage::Format_ARGB6666_Premultiplied:
+    case QImage::Format_RGB666:
+    case QImage::Format_RGB888:
+        return 3;
+    default:
+#ifndef QT_NO_DEBUG
+        qFatal("QWSWindowSurface: Invalid backingstore format: %i",
+               int(format));
+#endif
+        return 0;
+    }
+}
+
+static inline int nextMulOf4(int n)
+{
+    return ((n + 3) & 0xfffffffc);
+}
+
+static inline void setImageMetrics(QImage &img, QWidget *window) {
+    QScreen *myScreen = getScreen(window);
+    if (myScreen) {
+        int dpmx = myScreen->width()*1000 / myScreen->physicalWidth();
+        int dpmy = myScreen->height()*1000 / myScreen->physicalHeight();
+        img.setDotsPerMeterX(dpmx);
+        img.setDotsPerMeterY(dpmy);
+    }
+}
+
+void QWSWindowSurface::invalidateBuffer()
+{
+
+    QWidget *win = window();
+    if (win) {
+        win->d_func()->invalidateBuffer(win->rect());
+#ifndef QT_NO_QWS_MANAGER
+        QTLWExtra *topextra = win->d_func()->extra->topextra;
+        QWSManager *manager = topextra->qwsManager;
+        if (manager)
+            manager->d_func()->dirtyRegion(QDecoration::All,
+                                           QDecoration::Normal);
+#endif
+    }
+}
+
+QWSWindowSurfacePrivate::QWSWindowSurfacePrivate()
+    : flags(0),
+#ifdef QT_QWS_CLIENTBLIT
+    directId(-1),
+#endif
+    winId(0)
+{
+}
+
+void QWSWindowSurfacePrivate::setWinId(int id)
+{
+    winId = id;
+}
+
+int QWSWindowSurface::winId() const
+{
+    // XXX: the widget winId may change during the lifetime of the widget!!!
+
+    const QWidget *win = window();
+    if (win && win->isWindow())
+        return win->internalWinId();
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+    if (!d_ptr->winId) {
+        QWSWindowSurface *that = const_cast<QWSWindowSurface*>(this);
+        QWSDisplay *display = QWSDisplay::instance();
+        const int id = display->takeId();
+        qt_insertWindowSurface(id, that);
+        that->d_ptr->winId = id;
+
+        if (win)
+            display->nameRegion(id, win->objectName(), win->windowTitle());
+        else
+            display->nameRegion(id, QString(), QString());
+
+        display->setAltitude(id, 1, true); // XXX
+    }
+#endif
+
+    return d_ptr->winId;
+}
+
+void QWSWindowSurface::setWinId(int id)
+{
+    d_ptr->winId = id;
+}
+
+/*!
+    \class QWSWindowSurface
+    \since 4.2
+    \ingroup qws
+    \preliminary
+    \internal
+
+    \brief The QWSWindowSurface class provides the drawing area for top-level
+    windows in Qt for Embedded Linux.
+
+    Note that this class is only available in Qt for Embedded Linux.
+
+    In \l{Qt for Embedded Linux}, the default behavior is for each client to
+    render its widgets into memory while the server is responsible for
+    putting the contents of the memory onto the
+    screen. QWSWindowSurface is used by the window system to implement
+    the associated memory allocation.
+
+    When a screen update is required, the 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 uses the screen driver to
+    copy the content of the memory to the screen. To locate the
+    relevant parts of memory, the driver is provided with the list of
+    top-level windows that intersect with the given region. Associated
+    with each of the top-level windows there is a window surface
+    representing the drawing area of the window.
+
+    When deriving from the QWSWindowSurface class, e.g., when adding
+    an \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+    {accelerated graphics driver}, there are several pure virtual
+    functions that must be implemented. In addition, QWSWindowSurface
+    provides several virtual functions that can be reimplemented to
+    customize the drawing process.
+
+    \tableofcontents
+
+    \section1 Pure Virtual Functions
+
+    There are in fact two window surface instances for each top-level
+    window; one used by the application when drawing a window, and
+    another used by the server application to perform window
+    compositioning. Implement the attach() to create the server-side
+    representation of the surface. The data() function must be
+    implemented to provide the required data.
+
+    Implement the key() function to uniquely identify the surface
+    class, and the isValid() function to determine is a surface
+    corresponds to a given widget.
+
+    The geometry() function must be implemented to let the window
+    system determine the area required by the window surface
+    (QWSWindowSurface also provides a corresponding virtual
+    setGeometry() function that is called whenever the area necessary
+    for the top-level window to be drawn, changes). The image()
+    function is called by the window system during window
+    compositioning, and must be implemented to return an image of the
+    top-level window.
+
+    Finally, the paintDevice() function must be implemented to return
+    the appropriate paint device, and the scroll() function must be
+    implemented to scroll the given region of the surface the given
+    number of pixels.
+
+    \section1 Virtual Functions
+
+    When painting onto the surface, the window system will always call
+    the beginPaint() function before any painting operations are
+    performed. Likewise the endPaint() function is automatically
+    called when the painting is done. Reimplement the painterOffset()
+    function to alter the offset that is applied when drawing.
+
+    The window system uses the flush() function to put a given region
+    of the widget onto the screen, and the release() function to
+    deallocate the screen region corresponding to this window surface.
+
+    \section1 Other Members
+
+    QWSWindowSurface provides the window() function returning a
+    pointer to the top-level window the surface is representing. The
+    currently visible region of the associated widget can be retrieved
+    and set using the clipRegion() and setClipRegion() functions,
+    respectively.
+
+    When the window system performs the window compositioning, it uses
+    the SurfaceFlag enum describing the surface content. The currently
+    set surface flags can be retrieved and altered using the
+    surfaceFlags() and setSurfaceFlags() functions. In addition,
+    QWSWindowSurface provides the isBuffered(), isOpaque() and
+    isRegionReserved() convenience functions.
+
+    \sa {Qt for Embedded Linux Architecture#Drawing on Screen}{Qt for
+    Embedded Linux Architecture}
+*/
+
+/*!
+    \enum QWSWindowSurface::SurfaceFlag
+
+    This enum is used to describe the window surface's contents.  It
+    is used by the screen driver to handle region allocation and
+    composition.
+
+    \value RegionReserved The surface contains a reserved area. Once
+    allocated, a reserved area can not not be changed by the window
+    system, i.e., no other widgets can be drawn on top of this.
+
+    \value Buffered
+    The surface is in a memory area which is not part of a framebuffer.
+    (A top-level window with QWidget::windowOpacity() other than 1.0 must use
+    a buffered surface in order to making blending with the background work.)
+
+    \value Opaque
+    The surface contains only opaque pixels.
+
+    \sa surfaceFlags(), setSurfaceFlags()
+*/
+
+/*!
+    \fn bool QWSWindowSurface::isValid() const
+    \since 4.3
+
+    Implement this function to return true if the surface is a valid
+    surface for the given top-level \a window; otherwise return
+    false.
+
+    \sa window(), key()
+*/
+
+/*!
+    \fn QString QWSWindowSurface::key() const
+
+    Implement this function to return a string that uniquely
+    identifies the class of this surface.
+
+    \sa window(), isValid()
+*/
+
+/*!
+    \fn QByteArray QWSWindowSurface::permanentState() const
+    \since 4.3
+
+    Implement this function to return the data required for creating a
+    server-side representation of the surface.
+
+    \sa attach()
+*/
+
+/*!
+    \fn void QWSWindowSurface::setPermanentState(const QByteArray &data)
+    \since 4.3
+
+    Implement this function to attach a server-side surface instance
+    to the corresponding client side instance using the given \a
+    data. Return true if successful; otherwise return false.
+
+    \sa data()
+*/
+
+/*!
+    \fn const QImage QWSWindowSurface::image() const
+
+    Implement this function to return an image of the top-level window.
+
+    \sa geometry()
+*/
+
+/*!
+    \fn bool QWSWindowSurface::isRegionReserved() const
+
+    Returns true if the QWSWindowSurface::RegionReserved is set; otherwise
+    returns false.
+
+    \sa surfaceFlags()
+*/
+
+/*!
+    \fn bool QWSWindowSurface::isBuffered() const
+
+    Returns true if the QWSWindowSurface::Buffered is set; otherwise returns false.
+
+    \sa surfaceFlags()
+*/
+
+/*!
+    \fn bool QWSWindowSurface::isOpaque() const
+
+    Returns true if the QWSWindowSurface::Opaque is set; otherwise
+    returns false.
+
+    \sa surfaceFlags()
+*/
+
+
+/*!
+    Constructs an empty surface.
+*/
+QWSWindowSurface::QWSWindowSurface()
+    : QWindowSurface(0), d_ptr(new QWSWindowSurfacePrivate)
+{
+}
+
+/*!
+    Constructs an empty surface for the given top-level \a widget.
+*/
+QWSWindowSurface::QWSWindowSurface(QWidget *widget)
+    : QWindowSurface(widget), d_ptr(new QWSWindowSurfacePrivate)
+{
+}
+
+QWSWindowSurface::~QWSWindowSurface()
+{
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+    if (d_ptr->winId)
+        winIdToSurfaceMap()->remove(d_ptr->winId);
+#endif
+
+    delete d_ptr;
+}
+
+/*!
+    Returns the offset to be used when painting.
+
+    \sa paintDevice()
+*/
+QPoint QWSWindowSurface::painterOffset() const
+{
+    const QWidget *w = window();
+    if (!w)
+        return QPoint();
+    return w->geometry().topLeft() - w->frameGeometry().topLeft();
+}
+
+void QWSWindowSurface::beginPaint(const QRegion &)
+{
+    lock();
+}
+
+void QWSWindowSurface::endPaint(const QRegion &)
+{
+    unlock();
+}
+
+// XXX: documentation!!!
+QByteArray QWSWindowSurface::transientState() const
+{
+    return QByteArray();
+}
+
+QByteArray QWSWindowSurface::permanentState() const
+{
+    return QByteArray();
+}
+
+void QWSWindowSurface::setTransientState(const QByteArray &state)
+{
+    Q_UNUSED(state);
+}
+
+void QWSWindowSurface::setPermanentState(const QByteArray &state)
+{
+    Q_UNUSED(state);
+}
+
+bool QWSWindowSurface::lock(int timeout)
+{
+    Q_UNUSED(timeout);
+    return true;
+}
+
+void QWSWindowSurface::unlock()
+{
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+/*! \internal */
+const QRegion QWSWindowSurface::directRegion() const
+{
+    return d_ptr->direct;
+}
+
+/*! \internal */
+int QWSWindowSurface::directRegionId() const
+{
+    return d_ptr->directId;
+}
+
+/*! \internal */
+void QWSWindowSurface::setDirectRegion(const QRegion &r, int id)
+{
+    d_ptr->direct = r;
+    d_ptr->directId = id;
+}
+#endif
+
+/*!
+    Returns the region currently visible on the screen.
+
+    \sa setClipRegion()
+*/
+const QRegion QWSWindowSurface::clipRegion() const
+{
+    return d_ptr->clip;
+}
+
+/*!
+    Sets the region currently visible on the screen to be the given \a
+    clip region.
+
+    \sa clipRegion()
+*/
+void QWSWindowSurface::setClipRegion(const QRegion &clip)
+{
+    if (clip == d_ptr->clip)
+        return;
+
+    QRegion expose = (clip - d_ptr->clip);
+    d_ptr->clip = clip;
+
+    if (expose.isEmpty() || clip.isEmpty())
+        return; // No repaint or flush required.
+
+    QWidget *win = window();
+    if (!win)
+        return;
+
+    if (isBuffered()) {
+        // No repaint required. Flush exposed area via the backing store.
+        win->d_func()->syncBackingStore(expose);
+        return;
+    }
+
+#ifndef QT_NO_QWS_MANAGER
+    // Invalidate exposed decoration area.
+    if (win && win->isWindow()) {
+        QTLWExtra *topextra = win->d_func()->extra->topextra;
+        if (QWSManager *manager = topextra->qwsManager) {
+            QRegion decorationExpose(manager->region());
+            decorationExpose.translate(-win->geometry().topLeft());
+            decorationExpose &= expose;
+            if (!decorationExpose.isEmpty()) {
+                expose -= decorationExpose;
+                manager->d_func()->dirtyRegion(QDecoration::All, QDecoration::Normal, decorationExpose);
+            }
+        }
+    }
+#endif
+
+    // Invalidate exposed widget area.
+    win->d_func()->invalidateBuffer(expose);
+}
+
+/*!
+    Returns the surface flags describing the contents of this surface.
+
+    \sa isBuffered(), isOpaque(), isRegionReserved()
+*/
+QWSWindowSurface::SurfaceFlags QWSWindowSurface::surfaceFlags() const
+{
+    return d_ptr->flags;
+}
+
+/*!
+    Sets the surface flags describing the contents of this surface, to
+    be the given \a flags.
+
+    \sa surfaceFlags()
+*/
+void QWSWindowSurface::setSurfaceFlags(SurfaceFlags flags)
+{
+    d_ptr->flags = flags;
+}
+
+void QWSWindowSurface::setGeometry(const QRect &rect)
+{
+    QRegion mask = rect;
+
+    const QWidget *win = window();
+    if (win) {
+#ifndef QT_NO_QWS_MANAGER
+        if (win->isWindow()) {
+            QTLWExtra *topextra = win->d_func()->extra->topextra;
+            QWSManager *manager = topextra->qwsManager;
+
+            if (manager) {
+                // The frame geometry is the bounding rect of manager->region,
+                // which could be too much, so we need to clip.
+                mask &= (manager->region() + win->geometry());
+            }
+        }
+#endif
+
+        const QRegion winMask = win->mask();
+        if (!winMask.isEmpty())
+            mask &= winMask.translated(win->geometry().topLeft());
+    }
+
+    setGeometry(rect, mask);
+}
+
+void QWSWindowSurface::setGeometry(const QRect &rect, const QRegion &mask)
+{
+    if (rect == geometry()) // XXX: && mask == prevMask
+        return;
+
+    const bool isResize = rect.size() != geometry().size();
+    const bool needsRepaint = isResize || !isBuffered();
+
+    QWindowSurface::setGeometry(rect);
+
+    const QRegion region = mask & rect;
+    QWidget *win = window();
+    // Only request regions for widgets visible on the screen.
+    // (Added the !win check for compatibility reasons, because
+    // there was no "if (win)" check before).
+    const bool requestQWSRegion = !win || !win->testAttribute(Qt::WA_DontShowOnScreen);
+    if (requestQWSRegion)
+        QWidget::qwsDisplay()->requestRegion(winId(), key(), permanentState(), region);
+
+    if (needsRepaint)
+        invalidateBuffer();
+
+    if (!requestQWSRegion) {
+        // We didn't request a region, hence we won't get a QWSRegionEvent::Allocation
+        // event back from the server so we set the clip directly. We have to
+        // do this after the invalidateBuffer() call above, as it might trigger a
+        // backing store sync, resulting in too many update requests.
+        setClipRegion(region);
+    }
+}
+
+static inline void flushUpdate(QWidget *widget, const QRegion &region,
+                               const QPoint &offset)
+{
+#ifdef QT_NO_PAINT_DEBUG
+    Q_UNUSED(widget);
+    Q_UNUSED(region);
+    Q_UNUSED(offset);
+#else
+    static int delay = -1;
+
+    if (delay == -1)
+        delay = qgetenv("QT_FLUSH_UPDATE").toInt() * 10;
+
+    if (delay == 0)
+        return;
+
+    static QWSYellowSurface surface(true);
+    surface.setDelay(delay);
+    surface.flush(widget, region, offset);
+#endif // QT_NO_PAINT_DEBUG
+}
+
+void QWSWindowSurface::flush(QWidget *widget, const QRegion &region,
+                             const QPoint &offset)
+{
+    const QWidget *win = window();
+    if (!win)
+        return;
+
+    QWExtra *extra = win->d_func()->extra;
+    if (extra && extra->proxyWidget)
+        return;
+
+    Q_UNUSED(offset);
+
+    const bool opaque = isOpaque();
+#ifdef QT_QWS_DISABLE_FLUSHCLIPPING
+    QRegion toFlush = region;
+#else
+    QRegion toFlush = region & d_ptr->clip;
+#endif
+
+    if (!toFlush.isEmpty()) {
+        flushUpdate(widget, toFlush, QPoint(0, 0));
+        QPoint globalZero = win->mapToGlobal(QPoint(0, 0));
+        toFlush.translate(globalZero);
+
+#ifdef QT_QWS_CLIENTBLIT
+        bool needRepaint = true;
+        if (opaque) {
+            QScreen* widgetScreen = getScreen(widget);
+            if (widgetScreen->supportsBlitInClients()) {
+
+                QWSDisplay::grab();
+                if(directRegion().intersected(toFlush) == toFlush) {
+                    QPoint translate = -globalZero + painterOffset() + geometry().topLeft();
+                    QRegion flushRegion = toFlush.translated(translate);
+                    widgetScreen->blit(image(), geometry().topLeft(), flushRegion);
+                    widgetScreen->setDirty(toFlush.boundingRect());
+                    needRepaint = false;
+                }
+                QWSDisplay::ungrab();
+            }
+        }
+
+        if(needRepaint)
+#endif
+            win->qwsDisplay()->repaintRegion(winId(), win->windowFlags(), opaque, toFlush);
+    }
+}
+
+/*!
+    Move the surface with the given \a offset.
+
+    A subclass may reimplement this function to enable accelerated window move.
+    It must return true if the move was successful and no repaint is necessary,
+    false otherwise.
+
+    The default implementation updates the QWindowSurface geometry and
+    returns true if the surface is buffered; false otherwise.
+
+    This function is called by the window system on the client instance.
+
+    \sa isBuffered()
+*/
+bool QWSWindowSurface::move(const QPoint &offset)
+{
+    QWindowSurface::setGeometry(geometry().translated(offset));
+    return isBuffered();
+}
+
+/*!
+    Move the surface with the given \a offset.
+
+    The new visible region after the window move is given by \a newClip
+    in screen coordinates.
+
+    A subclass may reimplement this function to enable accelerated window move.
+    The returned region indicates the area that still needs to be composed
+    on the screen.
+
+    The default implementation updates the QWindowSurface geometry and
+    returns the union of the old and new geometry.
+
+    This function is called by the window system on the server instance.
+*/
+QRegion QWSWindowSurface::move(const QPoint &offset, const QRegion &newClip)
+{
+    const QRegion oldGeometry = geometry();
+    QWindowSurface::setGeometry(geometry().translated(offset));
+    return oldGeometry + newClip;
+}
+
+void QWSWindowSurface::releaseSurface()
+{
+}
+
+bool QWSMemorySurface::lock(int timeout)
+{
+    Q_UNUSED(timeout);
+#ifndef QT_NO_QWS_MULTIPROCESS
+    if (memlock && !memlock->lock(QWSLock::BackingStore))
+        return false;
+#endif
+#ifndef QT_NO_THREAD
+    threadLock.lock();
+#endif
+    return true;
+}
+
+void QWSMemorySurface::unlock()
+{
+#ifndef QT_NO_THREAD
+    threadLock.unlock();
+#endif
+#ifndef QT_NO_QWS_MULTIPROCESS
+    if (memlock)
+        memlock->unlock(QWSLock::BackingStore);
+#endif
+}
+
+QWSMemorySurface::QWSMemorySurface()
+    : QWSWindowSurface()
+#ifndef QT_NO_QWS_MULTIPROCESS
+    , memlock(0)
+#endif
+{
+    setSurfaceFlags(Buffered);
+}
+
+QWSMemorySurface::QWSMemorySurface(QWidget *w)
+    : QWSWindowSurface(w)
+{
+    SurfaceFlags flags = Buffered;
+    if (isWidgetOpaque(w))
+        flags |= Opaque;
+    setSurfaceFlags(flags);
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+    memlock = QWSDisplay::Data::getClientLock();
+#endif
+}
+
+QWSMemorySurface::~QWSMemorySurface()
+{
+}
+
+
+QImage::Format
+QWSMemorySurface::preferredImageFormat(const QWidget *widget) const
+{
+    QScreen *screen = getScreen(widget);
+    const int depth = screen->depth();
+    const bool opaque = isWidgetOpaque(widget);
+
+    if (!opaque) {
+        if (depth <= 12)
+            return QImage::Format_ARGB4444_Premultiplied;
+        else if (depth <= 15)
+            return QImage::Format_ARGB8555_Premultiplied;
+        else if (depth <= 16)
+            return QImage::Format_ARGB8565_Premultiplied;
+        else if (depth <= 18)
+            return QImage::Format_ARGB6666_Premultiplied;
+        else
+            return QImage::Format_ARGB32_Premultiplied;
+    }
+
+    QImage::Format format = screen->pixelFormat();
+    if (format > QImage::Format_Indexed8) // ### assumes all new image formats supports a QPainter
+        return format;
+
+    if (depth <= 12)
+        return QImage::Format_RGB444;
+    else if (depth <= 15)
+        return QImage::Format_RGB555;
+    else if (depth <= 16)
+        return QImage::Format_RGB16;
+    else if (depth <= 18)
+        return QImage::Format_RGB666;
+    else if (depth <= 24)
+        return QImage::Format_RGB888;
+    else
+        return QImage::Format_ARGB32_Premultiplied;
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSMemorySurface::setLock(int lockId)
+{
+    if (memlock && memlock->id() == lockId)
+        return;
+    delete memlock;
+    memlock = (lockId == -1 ? 0 : new QWSLock(lockId));
+    return;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+bool QWSMemorySurface::isValid() const
+{
+    if (img.isNull())
+        return true;
+
+    const QWidget *win = window();
+    if (preferredImageFormat(win) != img.format())
+        return false;
+
+    if (isOpaque() != isWidgetOpaque(win)) // XXX: use QWidgetPrivate::isOpaque()
+        return false;
+
+    return true;
+}
+
+// from qwindowsurface.cpp
+extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+bool QWSMemorySurface::scroll(const QRegion &area, int dx, int dy)
+{
+    const QVector<QRect> rects = area.rects();
+    for (int i = 0; i < rects.size(); ++i)
+        qt_scrollRectInImage(img, rects.at(i), QPoint(dx, dy));
+
+    return true;
+}
+
+QPoint QWSMemorySurface::painterOffset() const
+{
+    const QWidget *w = window();
+    if (!w)
+        return QPoint();
+
+    if (w->mask().isEmpty())
+        return QWSWindowSurface::painterOffset();
+
+    const QRegion region = w->mask()
+                           & w->frameGeometry().translated(-w->geometry().topLeft());
+    return -region.boundingRect().topLeft();
+}
+
+QWSLocalMemSurface::QWSLocalMemSurface()
+    : QWSMemorySurface(), mem(0), memsize(0)
+{
+}
+
+QWSLocalMemSurface::QWSLocalMemSurface(QWidget *w)
+    : QWSMemorySurface(w), mem(0), memsize(0)
+{
+}
+
+QWSLocalMemSurface::~QWSLocalMemSurface()
+{
+    if (memsize)
+        delete[] mem;
+}
+
+void QWSLocalMemSurface::setGeometry(const QRect &rect)
+{
+    QSize size = rect.size();
+
+    QWidget *win = window();
+    if (win && !win->mask().isEmpty()) {
+        const QRegion region = win->mask()
+                               & rect.translated(-win->geometry().topLeft());
+        size = region.boundingRect().size();
+    }
+
+    uchar *deleteLater = 0;
+    // In case of a Hide event we need to delete the memory after sending the
+    // event to the server in order to let the server animate the event.
+    if (size.isEmpty()) {
+        deleteLater = mem;
+        mem = 0;
+    }
+
+    if (img.size() != size) {
+        delete[] mem;
+        if (size.isEmpty()) {
+            mem = 0;
+            img = QImage();
+        } else {
+            const QImage::Format format = preferredImageFormat(win);
+            const int bpl = nextMulOf4(bytesPerPixel(format) * size.width());
+            const int memsize = bpl * size.height();
+            mem = new uchar[memsize];
+            img = QImage(mem, size.width(), size.height(), bpl, format);
+            setImageMetrics(img, win);
+        }
+    }
+
+    QWSWindowSurface::setGeometry(rect);
+    delete[] deleteLater;
+}
+
+QByteArray QWSLocalMemSurface::permanentState() const
+{
+    QByteArray array;
+    array.resize(sizeof(uchar*) + 3 * sizeof(int) +
+                 sizeof(SurfaceFlags));
+
+    char *ptr = array.data();
+
+    *reinterpret_cast<uchar**>(ptr) = mem;
+    ptr += sizeof(uchar*);
+
+    reinterpret_cast<int*>(ptr)[0] = img.width();
+    reinterpret_cast<int*>(ptr)[1] = img.height();
+    ptr += 2 * sizeof(int);
+
+    *reinterpret_cast<int *>(ptr) = img.format();
+    ptr += sizeof(int);
+
+    *reinterpret_cast<SurfaceFlags*>(ptr) = surfaceFlags();
+
+    return array;
+}
+
+void QWSLocalMemSurface::setPermanentState(const QByteArray &data)
+{
+    int width;
+    int height;
+    QImage::Format format;
+    SurfaceFlags flags;
+
+    const char *ptr = data.constData();
+
+    mem = *reinterpret_cast<uchar* const*>(ptr);
+    ptr += sizeof(uchar*);
+
+    width = reinterpret_cast<const int*>(ptr)[0];
+    height = reinterpret_cast<const int*>(ptr)[1];
+    ptr += 2 * sizeof(int);
+
+    format = QImage::Format(*reinterpret_cast<const int*>(ptr));
+    ptr += sizeof(int);
+
+    flags = *reinterpret_cast<const SurfaceFlags*>(ptr);
+
+    const int bpl = nextMulOf4(bytesPerPixel(format) * width);
+    QWSMemorySurface::img = QImage(mem, width, height, bpl, format);
+    setSurfaceFlags(flags);
+}
+
+void QWSLocalMemSurface::releaseSurface()
+{
+    mem = 0;
+    img = QImage();
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+QWSSharedMemSurface::QWSSharedMemSurface()
+    : QWSMemorySurface()
+{
+}
+
+QWSSharedMemSurface::QWSSharedMemSurface(QWidget *widget)
+    : QWSMemorySurface(widget)
+{
+}
+
+QWSSharedMemSurface::~QWSSharedMemSurface()
+{
+    // mem.detach() is done automatically by ~QSharedMemory
+}
+
+bool QWSSharedMemSurface::setMemory(int memId)
+{
+    if (mem.id() == memId)
+        return true;
+
+    mem.detach();
+    if (!mem.attach(memId)) {
+        perror("QWSSharedMemSurface: attaching to shared memory");
+        qCritical("QWSSharedMemSurface: Error attaching to"
+                  " shared memory 0x%x", memId);
+        return false;
+    }
+
+    return true;
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+void QWSSharedMemSurface::setDirectRegion(const QRegion &r, int id)
+{
+    QWSMemorySurface::setDirectRegion(r, id);
+    if(mem.address())
+        *(uint *)mem.address() = id;
+}
+
+const QRegion QWSSharedMemSurface::directRegion() const
+{
+    QWSSharedMemory *cmem = const_cast<QWSSharedMemory *>(&mem);
+    if (cmem->address() && ((int*)cmem->address())[0] == directRegionId())
+        return QWSMemorySurface::directRegion();
+    else
+        return QRegion();
+}
+#endif
+
+void QWSSharedMemSurface::setPermanentState(const QByteArray &data)
+{
+    int memId;
+    int width;
+    int height;
+    int lockId;
+    QImage::Format format;
+    SurfaceFlags flags;
+
+    const int *ptr = reinterpret_cast<const int*>(data.constData());
+
+    memId = ptr[0];
+    width = ptr[1];
+    height = ptr[2];
+    lockId = ptr[3];
+    format = QImage::Format(ptr[4]);
+    flags = SurfaceFlags(ptr[5]);
+
+    setSurfaceFlags(flags);
+    setMemory(memId);
+    setLock(lockId);
+
+#ifdef QT_QWS_CLIENTBLIT
+    uchar *base = static_cast<uchar*>(mem.address()) + sizeof(uint);
+#else
+    uchar *base = static_cast<uchar*>(mem.address());
+#endif
+    const int bpl = nextMulOf4(bytesPerPixel(format) * width);
+    QWSMemorySurface::img = QImage(base, width, height, bpl, format);
+}
+
+void QWSSharedMemSurface::setGeometry(const QRect &rect)
+{
+    const QSize size = rect.size();
+    if (img.size() != size) {
+        if (size.isEmpty()) {
+            mem.detach();
+            img = QImage();
+        } else {
+            mem.detach();
+
+            QWidget *win = window();
+            const QImage::Format format = preferredImageFormat(win);
+            const int bpl = nextMulOf4(bytesPerPixel(format) * size.width());
+#ifdef QT_QWS_CLIENTBLIT
+            const int imagesize = bpl * size.height() + sizeof(uint);
+#else
+            const int imagesize = bpl * size.height();
+#endif
+            if (!mem.create(imagesize)) {
+                perror("QWSSharedMemSurface::setGeometry allocating shared memory");
+                qFatal("Error creating shared memory of size %d", imagesize);
+            }
+#ifdef QT_QWS_CLIENTBLIT
+            *((uint *)mem.address()) = 0;
+            uchar *base = static_cast<uchar*>(mem.address()) + sizeof(uint);
+#else
+            uchar *base = static_cast<uchar*>(mem.address());
+#endif
+            img = QImage(base, size.width(), size.height(), bpl, format);
+            setImageMetrics(img, win);
+        }
+    }
+
+    QWSWindowSurface::setGeometry(rect);
+}
+
+QByteArray QWSSharedMemSurface::permanentState() const
+{
+    QByteArray array;
+    array.resize(6 * sizeof(int));
+
+    int *ptr = reinterpret_cast<int*>(array.data());
+
+    ptr[0] = mem.id();
+    ptr[1] = img.width();
+    ptr[2] = img.height();
+    ptr[3] = (memlock ? memlock->id() : -1);
+    ptr[4] = int(img.format());
+    ptr[5] = int(surfaceFlags());
+
+    return array;
+}
+
+void QWSSharedMemSurface::releaseSurface()
+{
+    mem.detach();
+    img = QImage();
+}
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#ifndef QT_NO_PAINTONSCREEN
+
+QWSOnScreenSurface::QWSOnScreenSurface(QWidget *w)
+    : QWSMemorySurface(w)
+{
+    attachToScreen(getScreen(w));
+    setSurfaceFlags(Opaque);
+}
+
+QWSOnScreenSurface::QWSOnScreenSurface()
+    : QWSMemorySurface()
+{
+    setSurfaceFlags(Opaque);
+}
+
+void QWSOnScreenSurface::attachToScreen(const QScreen *s)
+{
+    screen = s;
+    uchar *base = screen->base();
+    QImage::Format format  = screen->pixelFormat();
+
+    if (format == QImage::Format_Invalid || format == QImage::Format_Indexed8) {
+        //### currently we have no paint engine for indexed image formats
+        qFatal("QWSOnScreenSurface::attachToScreen(): screen depth %d "
+               "not implemented", screen->depth());
+        return;
+    }
+    QWSMemorySurface::img = QImage(base, screen->width(), screen->height(),
+                                   screen->linestep(), format );
+}
+
+QWSOnScreenSurface::~QWSOnScreenSurface()
+{
+}
+
+QPoint QWSOnScreenSurface::painterOffset() const
+{
+    return geometry().topLeft() + QWSWindowSurface::painterOffset();
+}
+
+bool QWSOnScreenSurface::isValid() const
+{
+    const QWidget *win = window();
+    if (screen != getScreen(win))
+        return false;
+    if (img.isNull())
+        return false;
+    return QScreen::isWidgetPaintOnScreen(win);
+}
+
+QByteArray QWSOnScreenSurface::permanentState() const
+{
+    QByteArray array;
+    array.resize(sizeof(int));
+    int *ptr = reinterpret_cast<int*>(array.data());
+    ptr[0] = QApplication::desktop()->screenNumber(window());
+    return array;
+}
+
+void QWSOnScreenSurface::setPermanentState(const QByteArray &data)
+{
+    const int *ptr = reinterpret_cast<const int*>(data.constData());
+    const int screenNo = ptr[0];
+
+    QScreen *screen = qt_screen;
+    if (screenNo > 0)
+        screen = qt_screen->subScreens().at(screenNo);
+    attachToScreen(screen);
+}
+
+#endif // QT_NO_PAINTONSCREEN
+
+#ifndef QT_NO_PAINT_DEBUG
+
+QWSYellowSurface::QWSYellowSurface(bool isClient)
+    : QWSWindowSurface(), delay(10)
+{
+    if (isClient) {
+        setWinId(QWidget::qwsDisplay()->takeId());
+        QWidget::qwsDisplay()->nameRegion(winId(),
+                                          QLatin1String("Debug flush paint"),
+                                          QLatin1String("Silly yellow thing"));
+        QWidget::qwsDisplay()->setAltitude(winId(), 1, true);
+    }
+    setSurfaceFlags(Buffered);
+}
+
+QWSYellowSurface::~QWSYellowSurface()
+{
+}
+
+QByteArray QWSYellowSurface::permanentState() const
+{
+    QByteArray array;
+    array.resize(2 * sizeof(int));
+
+    int *ptr = reinterpret_cast<int*>(array.data());
+    ptr[0] = surfaceSize.width();
+    ptr[1] = surfaceSize.height();
+
+    return array;
+}
+
+void QWSYellowSurface::setPermanentState(const QByteArray &data)
+{
+    const int *ptr = reinterpret_cast<const int*>(data.constData());
+
+    const int width = ptr[0];
+    const int height = ptr[1];
+
+    img = QImage(width, height, QImage::Format_ARGB32);
+    img.fill(qRgba(255,255,31,127));
+}
+
+void QWSYellowSurface::flush(QWidget *widget, const QRegion &region,
+                             const QPoint &offset)
+{
+    Q_UNUSED(offset);
+
+    QWSDisplay *display = QWidget::qwsDisplay();
+    QRegion rgn = region;
+
+    if (widget)
+        rgn.translate(widget->mapToGlobal(QPoint(0, 0)));
+
+    surfaceSize = region.boundingRect().size();
+
+    const int id = winId();
+    display->requestRegion(id, key(), permanentState(), rgn);
+    display->setAltitude(id, 1, true);
+    display->repaintRegion(id, 0, false, rgn);
+
+    ::usleep(500 * delay);
+    display->requestRegion(id, key(), permanentState(), QRegion());
+    ::usleep(500 * delay);
+}
+
+#endif // QT_NO_PAINT_DEBUG
+
+#ifndef QT_NO_DIRECTPAINTER
+
+static inline QScreen *getPrimaryScreen()
+{
+    QScreen *screen = QScreen::instance();
+    if (!screen->base()) {
+        QList<QScreen*> subScreens = screen->subScreens();
+        if (subScreens.size() < 1)
+            return 0;
+        screen = subScreens.at(0);
+    }
+    return screen;
+}
+
+QWSDirectPainterSurface::QWSDirectPainterSurface(bool isClient,
+                                                 QDirectPainter::SurfaceFlag flags)
+    : QWSWindowSurface(), flushingRegionEvents(false), doLocking(false)
+{
+    setSurfaceFlags(Opaque);
+    synchronous = (flags == QDirectPainter::ReservedSynchronous);
+
+    if (isClient) {
+        setWinId(QWidget::qwsDisplay()->takeId());
+        QWidget::qwsDisplay()->nameRegion(winId(),
+                                          QLatin1String("QDirectPainter reserved space"),
+                                          QLatin1String("reserved"));
+    } else {
+        setWinId(0);
+    }
+    _screen = QScreen::instance();
+    if (!_screen->base()) {
+        QList<QScreen*> subScreens = _screen->subScreens();
+        if (subScreens.size() < 1)
+            _screen = 0;
+        else
+            _screen = subScreens.at(0);
+    }
+}
+
+QWSDirectPainterSurface::~QWSDirectPainterSurface()
+{
+    if (winId() && QWSDisplay::instance()) // make sure not in QApplication destructor
+        QWidget::qwsDisplay()->destroyRegion(winId());
+}
+
+void QWSDirectPainterSurface::setRegion(const QRegion &region)
+{
+    const int id = winId();
+    QWidget::qwsDisplay()->requestRegion(id, key(), permanentState(), region);
+#ifndef QT_NO_QWS_MULTIPROCESS
+    if (synchronous)
+        QWSDisplay::instance()->d->waitForRegionAck(id);
+#endif
+}
+
+void QWSDirectPainterSurface::flush(QWidget *, const QRegion &r, const QPoint &)
+{
+    QWSDisplay::instance()->repaintRegion(winId(), 0, true, r);
+}
+
+QByteArray QWSDirectPainterSurface::permanentState() const
+{
+    QByteArray res;
+    if (isRegionReserved())
+        res.append( 'r');
+    return res;
+}
+
+void QWSDirectPainterSurface::setPermanentState(const QByteArray &ba)
+{
+    if (ba.size() > 0 && ba.at(0) == 'r')
+        setReserved();
+    setSurfaceFlags(surfaceFlags() | Opaque);
+}
+
+void QWSDirectPainterSurface::beginPaint(const QRegion &region)
+{
+    QWSWindowSurface::beginPaint(region);
+#ifndef QT_NO_QWS_MULTIPROCESS
+    if (!synchronous) {
+        flushingRegionEvents = true;
+        QWSDisplay::instance()->d->waitForRegionEvents(winId(), doLocking);
+        flushingRegionEvents = false;
+    }
+#endif
+}
+
+bool QWSDirectPainterSurface::hasPendingRegionEvents() const
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+    if (synchronous)
+        return false;
+
+    return QWSDisplay::instance()->d->hasPendingRegionEvents();
+#else
+    return false;
+#endif
+}
+
+bool QWSDirectPainterSurface::lock(int timeout)
+{
+#ifndef QT_NO_THREAD
+    threadLock.lock();
+#endif
+    Q_UNUSED(timeout);
+    if (doLocking)
+        QWSDisplay::grab(true);
+    return true;
+}
+
+void QWSDirectPainterSurface::unlock()
+{
+    if (doLocking)
+        QWSDisplay::ungrab();
+#ifndef QT_NO_THREAD
+    threadLock.unlock();
+#endif
+}
+
+#endif // QT_NO_DIRECTPAINTER
+
+QT_END_NAMESPACE