diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-06-07 23:08:07 -0700 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-06-08 19:32:32 -0700 |
commit | 3a005cdf983881612a70836dcd3ab816b0ea7698 (patch) | |
tree | 615de8a3f796bde47174c6fcb9a2be6a825f3588 | |
parent | f68bab66aacded35be3d222e703e74e9db0bada3 (diff) |
Add Wayland build & stub support
Add a new platform target, called wayland, to support building a
Wayland-aware Qt library. Enabled by passing "-wayland" to configure.
Defines a new window system type, Q_WS_WAYLAND, which can be used to
pull in the right implementations or declare methods in various support
classes.
71 files changed, 13873 insertions, 20 deletions
diff --git a/.gitignore b/.gitignore index d88e69a766..8a36bd9279 100644 --- a/.gitignore +++ b/.gitignore @@ -230,3 +230,4 @@ qtc-debugging-helper src/corelib/lib src/network/lib src/xml/lib/ +*cscope* diff --git a/config.tests/wayland/client.cpp b/config.tests/wayland/client.cpp new file mode 100644 index 0000000000..40ff2cbd75 --- /dev/null +++ b/config.tests/wayland/client.cpp @@ -0,0 +1,7 @@ +#include "wayland-client.h" +#include "wayland-glib.h" + +int main(int, char **) +{ + return 0; +} diff --git a/config.tests/wayland/client.pro b/config.tests/wayland/client.pro new file mode 100644 index 0000000000..56a6bf6d4c --- /dev/null +++ b/config.tests/wayland/client.pro @@ -0,0 +1,5 @@ +SOURCES = client.cpp +CONFIG -= qt + +QMAKE_CXXFLAGS += $$QT_CFLAGS_WAYLAND +LIBS += $$QT_LIBS_WAYLAND @@ -187,6 +187,7 @@ fi PLATFORM_X11=no PLATFORM_MAC=no PLATFORM_QWS=no +PLATFORM_WAYLAND=maybe if [ -f "$relpath"/src/gui/kernel/qapplication_mac.mm ] && [ -d /System/Library/Frameworks/Carbon.framework ]; then # Qt/Mac @@ -1311,6 +1312,9 @@ while [ "$#" -gt 0 ]; do fi PLATFORM_X11=yes ;; + wayland) + PLATFORM_WAYLAND=yes + ;; sdk) if [ "$PLATFORM_MAC" = "yes" ]; then CFG_SDK="$VAL" @@ -2710,7 +2714,7 @@ if [ '!' -f "${XQMAKESPEC}/qplatformdefs.h" ]; then fi # now look at the configs and figure out what platform we are config'd for -[ "$CFG_EMBEDDED" = "no" ] \ +[ "$CFG_EMBEDDED" = "no" -a "$PLATFORM_WAYLAND" = "no" ] \ && [ '!' -z "`getQMakeConf \"$XQMAKESPEC\" | grep QMAKE_LIBS_X11 | awk '{print $3;}'`" ] \ && PLATFORM_X11=yes ### echo "$XQMAKESPEC" | grep mkspecs/qws >/dev/null 2>&1 && PLATFORM_QWS=yes @@ -3411,6 +3415,7 @@ Usage: $relconf [-h] [-prefix <dir>] [-prefix-install] [-bindir <dir>] [-libdir [-no-openssl] [-openssl] [-openssl-linked] [-no-gtkstyle] [-gtkstyle] [-no-svg] [-svg] [-no-webkit] [-webkit] [-no-javascript-jit] [-javascript-jit] [-no-script] [-script] [-no-scripttools] [-scripttools] [-no-declarative] [-declarative] + [-wayland] [additional platform specific options (see below)] @@ -3642,6 +3647,8 @@ Third Party Libraries: -ptmalloc .......... Override the system memory allocator with ptmalloc. (Experimental.) + -wayland ........... Use Wayland as the target platform + Additional options: -make <part> ....... Add part to the list of parts to be built at make time. @@ -4077,6 +4084,8 @@ elif [ "$PLATFORM_MAC" = "yes" ]; then Platform="Qt for Mac OS X" elif echo "$XPLATFORM" | grep "symbian" > /dev/null ; then Platform="Qt for Symbian" +elif [ "$PLATFORM_WAYLAND" = "yes" ]; then + Platform="Qt for Wayland" elif [ '!' -z "`getQMakeConf \"$XQMAKESPEC\" | grep QMAKE_LIBS_X11 | awk '{print $3;}'`" ]; then PLATFORM_X11=yes Platform="Qt for Linux/X11" @@ -5180,7 +5189,7 @@ if [ "$PLATFORM_MAC" = "yes" -a ! -z "$QT_NAMESPACE" ]; then QT_NAMESPACE_MAC_CRC=`"$mactests/crc.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/mac/crc $QT_NAMESPACE $L_FLAGS $I_FLAGS $l_FLAGS` fi -if [ "$PLATFORM_X11" = "yes" -o "$PLATFORM_QWS" = "yes" ]; then +if [ "$PLATFORM_X11" = "yes" -o "$PLATFORM_QWS" = "yes" -o "$PLATFORM_WAYLAND" = "yes" ]; then # auto-detect Glib support if [ "$CFG_GLIB" != "no" ]; then @@ -5880,7 +5889,7 @@ if [ "$PLATFORM_QWS" = "yes" ]; then fi # QWS # EGL Support -if [ "$PLATFORM_X11" = "yes" -o "$PLATFORM_QWS" = "yes" ]; then +if [ "$PLATFORM_X11" = "yes" -o "$PLATFORM_QWS" = "yes" -o "$PLATFORM_WAYLAND" = "yes" ]; then if [ "$CFG_EGL" != "no" ]; then # detect EGL support if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" "config.tests/unix/egl" "EGL (EGL/egl.h)" $L_FLAGS $I_FLAGS $l_FLAGS; then @@ -6482,6 +6491,12 @@ if [ "$PLATFORM_QWS" = "yes" ]; then QT_CONFIG="$QT_CONFIG embedded" rm -f "src/.moc/$QMAKE_OUTDIR/allmoc.cpp" # needs remaking if config changes fi +if [ "$PLATFORM_WAYLAND" = "yes" ]; then + QMAKE_OUTDIR="${QMAKE_OUTDIR}-wayland" + QMAKE_CONFIG="$QMAKE_CONFIG wayland" + QT_CONFIG="$QT_CONFIG wayland" +fi + QMakeVar set PRECOMPILED_DIR ".pch/$QMAKE_OUTDIR" QMakeVar set OBJECTS_DIR ".obj/$QMAKE_OUTDIR" QMakeVar set MOC_DIR ".moc/$QMAKE_OUTDIR" @@ -7418,6 +7433,10 @@ if [ "$PLATFORM_QWS" = "yes" ]; then done fi # QWS +if [ "$PLATFORM_WAYLAND" = "yes" ]; then + QCONFIG_FLAGS="$QCONFIG_FLAGS Q_WS_WAYLAND" +fi # WAYLAND + if [ "${CFG_USE_FLOATMATH}" = "yes" ]; then QCONFIG_FLAGS="${QCONFIG_FLAGS} QT_USE_MATH_H_FLOATS" fi diff --git a/mkspecs/common/linux.conf b/mkspecs/common/linux.conf index 4fbe2dcab5..e4f1067d1f 100644 --- a/mkspecs/common/linux.conf +++ b/mkspecs/common/linux.conf @@ -9,6 +9,8 @@ QMAKE_INCDIR = QMAKE_LIBDIR = QMAKE_INCDIR_X11 = /usr/X11R6/include QMAKE_LIBDIR_X11 = /usr/X11R6/lib +QMAKE_INCDIR_WAYLAND = /usr/include +QMAKE_LIBDIR_WAYLAND = /usr/lib QMAKE_INCDIR_QT = $$[QT_INSTALL_HEADERS] QMAKE_LIBDIR_QT = $$[QT_INSTALL_LIBS] QMAKE_INCDIR_OPENGL = /usr/X11R6/include @@ -34,6 +36,7 @@ QMAKE_LIBS_OPENGL_ES1 = -lGLES_CM QMAKE_LIBS_OPENGL_ES2 = -lGLESv2 QMAKE_LIBS_OPENVG = -lOpenVG QMAKE_LIBS_THREAD = -lpthread +QMAKE_LIBS_WAYLAND = -lwayland QMAKE_MOC = $$[QT_INSTALL_BINS]/moc QMAKE_UIC = $$[QT_INSTALL_BINS]/uic diff --git a/mkspecs/common/wayland.conf b/mkspecs/common/wayland.conf new file mode 100644 index 0000000000..691634d227 --- /dev/null +++ b/mkspecs/common/wayland.conf @@ -0,0 +1,8 @@ +MAKEFILE_GENERATOR = UNIX +TARGET_PLATFORM = unix +TEMPLATE = app +CONFIG += qt warn_on release incremental link_prl +QT += core gui network +QMAKE_INCREMENTAL_STYLE = sublib + +c diff --git a/mkspecs/wayland/linux-generic-g++/qmake.conf b/mkspecs/wayland/linux-generic-g++/qmake.conf new file mode 100644 index 0000000000..0fe09a3a52 --- /dev/null +++ b/mkspecs/wayland/linux-generic-g++/qmake.conf @@ -0,0 +1,9 @@ +# +# qmake configuration for building with g++ +# + +include(../../common/g++.conf) +include(../../common/linux.conf) +include(../../common/wayland.conf) + +load(qt_config) diff --git a/mkspecs/wayland/linux-generic-g++/qplatformdefs.h b/mkspecs/wayland/linux-generic-g++/qplatformdefs.h new file mode 100644 index 0000000000..99e9a27923 --- /dev/null +++ b/mkspecs/wayland/linux-generic-g++/qplatformdefs.h @@ -0,0 +1 @@ +#include "../../linux-g++/qplatformdefs.h" diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 1eab394296..69ea69b8eb 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -817,7 +817,7 @@ namespace QT_NAMESPACE {} # if !defined(QT_NO_S60) # define Q_WS_S60 # endif -# elif !defined(Q_WS_QWS) +# elif !defined(Q_WS_QWS) && !defined(Q_WS_WAYLAND) # define Q_WS_X11 # endif #endif diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 607ab52a60..6a33afd2d7 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1651,6 +1651,8 @@ public: typedef void * HANDLE; #elif defined(Q_OS_SYMBIAN) typedef unsigned long int HANDLE; // equivalent to TUint32 +#elif defined(Q_WS_WAYLAND) + typedef void *HANDLE; #endif typedef WindowFlags WFlags; diff --git a/src/gui/egl/egl.pri b/src/gui/egl/egl.pri index c6c020d307..c39c5ea9d9 100644 --- a/src/gui/egl/egl.pri +++ b/src/gui/egl/egl.pri @@ -23,6 +23,10 @@ contains(QT_CONFIG, egl): { } } } + + wayland { + SOURCES += egl/qegl_wayland.cpp + } } else:symbian { DEFINES += QT_NO_EGL SOURCES += egl/qegl_stub.cpp diff --git a/src/gui/egl/qegl_wayland.cpp b/src/gui/egl/qegl_wayland.cpp new file mode 100644 index 0000000000..7510dabca8 --- /dev/null +++ b/src/gui/egl/qegl_wayland.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 <QtGui/qpaintdevice.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qwidget.h> + +#include "qegl_p.h" +#include "qeglcontext_p.h" + +#if !defined(QT_NO_EGL) + +#include <qscreen_wayland.h> +#include <qscreenproxy_wayland.h> +#include <qapplication.h> +#include <qdesktopwidget.h> + +QT_BEGIN_NAMESPACE + +static QScreen *screenForDevice(QPaintDevice *device) +{ + QScreen *screen = qt_screen; + if (!screen) + return 0; + if (screen->classId() == QScreen::MultiClass) { + int screenNumber; + if (device && device->devType() == QInternal::Widget) + screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device)); + else + screenNumber = 0; + screen = screen->subScreens()[screenNumber]; + } + while (screen->classId() == QScreen::ProxyClass || + screen->classId() == QScreen::TransformedClass) { + screen = static_cast<QProxyScreen *>(screen)->screen(); + } + return screen; +} + +// Set pixel format and other properties based on a paint device. +void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev) +{ + if (!dev) + return; + + // Find the QGLScreen for this paint device. + QScreen *screen = screenForDevice(dev); + if (!screen) + return; + int devType = dev->devType(); + if (devType == QInternal::Image) + setPixelFormat(static_cast<QImage *>(dev)->format()); + else + setPixelFormat(screen->pixelFormat()); +} + +EGLNativeDisplayType QEgl::nativeDisplay() +{ + return EGLNativeDisplayType(EGL_DEFAULT_DISPLAY); +} + +EGLNativeWindowType QEgl::nativeWindow(QWidget* widget) +{ + return (EGLNativeWindowType)(widget->winId()); // Might work +} + +EGLNativePixmapType QEgl::nativePixmap(QPixmap*) +{ + qWarning("QEgl: EGL pixmap surfaces not supported on QWS"); + return (EGLNativePixmapType)0; +} + + +QT_END_NAMESPACE + +#endif // !QT_NO_EGL diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 7f1cb78280..cbc2ac6577 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -5,7 +5,7 @@ DEFINES += QT_BUILD_GUI_LIB QT_NO_USING_NAMESPACE win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x65000000 irix-cc*:QMAKE_CXXFLAGS += -no_prelink -ptused -!win32:!embedded:!mac:!symbian:CONFIG += x11 +!win32:!embedded:!mac:!symbian:!wayland:CONFIG += x11 unix:QMAKE_PKGCONFIG_REQUIRES = QtCore @@ -22,6 +22,7 @@ symbian { include(kernel/symbian.pri) include(s60framework/s60framework.pri) } +wayland:include(kernel/wayland.pri) #modules include(animation/animation.pri) diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 8d75fdd078..bc1fd19dbf 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -73,6 +73,10 @@ symbian { HEADERS += image/qpixmap_s60_p.h SOURCES += image/qpixmap_s60.cpp } +wayland { + HEADERS += image/qpixmap_wayland_p.h + SOURCES += image/qpixmap_wayland.cpp +} # Built-in image format support HEADERS += \ diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 82546da6a4..f727985a5e 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -195,7 +195,7 @@ public: Qt::HANDLE macQDHandle() const; Qt::HANDLE macQDAlphaHandle() const; Qt::HANDLE macCGHandle() const; -#elif defined(Q_WS_X11) +#elif defined(Q_WS_X11) || defined(Q_WS_WAYLAND) enum ShareMode { ImplicitlyShared, ExplicitlyShared }; static QPixmap fromX11Pixmap(Qt::HANDLE pixmap, ShareMode mode = ImplicitlyShared); diff --git a/src/gui/image/qpixmap_wayland.cpp b/src/gui/image/qpixmap_wayland.cpp new file mode 100644 index 0000000000..da31f160e8 --- /dev/null +++ b/src/gui/image/qpixmap_wayland.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qplatformdefs.h" + +#include "qdebug.h" +#include "qiodevice.h" +#include "qpixmap_wayland_p.h" +#include "qbitmap.h" +#include "qcolormap.h" +#include "qimage.h" +#include "qmatrix.h" +#include "qapplication.h" +#include <private/qdrawhelper_p.h> +#include <private/qimage_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap_wayland_p.h b/src/gui/image/qpixmap_wayland_p.h new file mode 100644 index 0000000000..f481cc62d2 --- /dev/null +++ b/src/gui/image/qpixmap_wayland_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QPIXMAPDATA_WAYLAND_P_H +#define QPIXMAPDATA_WAYLAND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qpixmapdata_p.h> +#include <QtGui/private/qpixmapdatafactory_p.h> + +QT_BEGIN_NAMESPACE + +class QWaylandPaintEngine; + +class Q_GUI_EXPORT QWaylandPixmapData : public QPixmapData +{ +public: + QWaylandPixmapData(PixelType type); +// QWaylandPixmapData(PixelType type, int width, int height); +// QWaylandPixmapData(PixelType type, const QImage &image, +// Qt::ImageConversionFlags flags); + ~QWaylandPixmapData(); + + QPixmapData *createCompatiblePixmapData() const; + + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, const QRect &rect); + + void fill(const QColor &color); + QBitmap mask() const; + void setMask(const QBitmap &mask); + bool hasAlphaChannel() const; + void setAlphaChannel(const QPixmap &alphaChannel); + QPixmap alphaChannel() const; + QPixmap transformed(const QTransform &transform, + Qt::TransformationMode mode) const; + QImage toImage() const; + QPaintEngine* paintEngine() const; + + Qt::HANDLE handle() const { return hd; } + Qt::HANDLE x11ConvertToDefaultDepth(); + + static Qt::HANDLE createBitmapFromImage(const QImage &image); + + void* gl_surface; + +protected: + int metric(QPaintDevice::PaintDeviceMetric metric) const; + +private: + friend class QPixmap; + friend class QBitmap; + friend class QWaylandPaintEngine; + friend class QWaylandWindowSurface; + friend class QRasterWindowSurface; + friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags + friend class QEglContext; // Needs gl_surface + friend class QGLContext; // Needs gl_surface + friend class QWaylandGLPixmapData; // Needs gl_surface + friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs gl_surface + + void release(); + + QBitmap mask_to_bitmap(int screen) const; + static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen); + void bitmapFromImage(const QImage &image); + + Qt::HANDLE hd; + + enum Flag { + NoFlags = 0x0, + Uninitialized = 0x1, + Readonly = 0x2, + InvertedWhenBoundToTexture = 0x4, + GlSurfaceCreatedWithAlpha = 0x8 + }; + uint flags; + + Qt::HANDLE picture; + Qt::HANDLE mask_picture; + Qt::HANDLE hd2; // sorted in the default display depth + QPixmap::ShareMode share_mode; + + QWaylandPaintEngine *pengine; +}; + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_WAYLAND_P_H + diff --git a/src/gui/image/qpixmapdatafactory.cpp b/src/gui/image/qpixmapdatafactory.cpp index 8014660c94..5aa874b920 100644 --- a/src/gui/image/qpixmapdatafactory.cpp +++ b/src/gui/image/qpixmapdatafactory.cpp @@ -56,6 +56,9 @@ #ifdef Q_WS_S60 # include <private/qpixmap_s60_p.h> #endif +#ifdef Q_WS_WAYLAND +# include <private/qpixmap_wayland_p.h> +#endif #include "private/qapplication_p.h" #include "private/qgraphicssystem_p.h" @@ -84,6 +87,8 @@ QPixmapData* QSimplePixmapDataFactory::create(QPixmapData::PixelType type) return new QMacPixmapData(type); #elif defined(Q_WS_S60) return new QS60PixmapData(type); +#elif defined(Q_WS_WAYLAND) + return new QWaylandPixmapData(type); #else #error QSimplePixmapDataFactory::create() not implemented #endif diff --git a/src/gui/inputmethod/inputmethod.pri b/src/gui/inputmethod/inputmethod.pri index 02e3e57024..86470aefbc 100644 --- a/src/gui/inputmethod/inputmethod.pri +++ b/src/gui/inputmethod/inputmethod.pri @@ -28,4 +28,7 @@ symbian:contains(QT_CONFIG, s60) { SOURCES += inputmethod/qcoefepinputcontext_s60.cpp LIBS += -lfepbase -lakninputlanguage } - +embedded { + HEADERS += inputmethod/qwaylandinputcontext_p.h + SOURCES += inputmethod/qwaylandinputcontext_wayland.cpp +} diff --git a/src/gui/inputmethod/qwaylandinputcontext_p.h b/src/gui/inputmethod/qwaylandinputcontext_p.h new file mode 100644 index 0000000000..55463855e9 --- /dev/null +++ b/src/gui/inputmethod/qwaylandinputcontext_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QWSINPUTCONTEXT_P_H +#define QWSINPUTCONTEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "QtGui/qinputcontext.h" + +#ifndef QT_NO_QWS_INPUTMETHODS + +QT_BEGIN_NAMESPACE + +class QWSIMEvent; +class QWSIMQueryEvent; +class QWSIMInitEvent; + +class QWSInputContext : public QInputContext +{ + Q_OBJECT +public: + explicit QWSInputContext(QObject* parent = 0); + ~QWSInputContext() {} + + + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset(); + void update(); + void mouseHandler( int x, QMouseEvent *event); + + void setFocusWidget( QWidget *w ); + void widgetDestroyed(QWidget *w); + + bool isComposing() const; + + static QWidget *activeWidget(); + static bool translateIMEvent(QWidget *w, const QWSIMEvent *e); + static bool translateIMQueryEvent(QWidget *w, const QWSIMQueryEvent *e); + static bool translateIMInitEvent(const QWSIMInitEvent *e); + static void updateImeStatus(QWidget *w, bool hasFocus); +}; + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_INPUTMETHODS + +#endif // QWSINPUTCONTEXT_P_H diff --git a/src/gui/inputmethod/qwaylandinputcontext_wayland.cpp b/src/gui/inputmethod/qwaylandinputcontext_wayland.cpp new file mode 100644 index 0000000000..8a199249fe --- /dev/null +++ b/src/gui/inputmethod/qwaylandinputcontext_wayland.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qwsinputcontext_p.h" +#include "qinputcontext_p.h" +#include "qwsdisplay_qws.h" +#include "qwsevent_qws.h" +#include "private/qwscommand_qws_p.h" +#include "qwindowsystem_qws.h" +#include "qevent.h" +#include "qtextformat.h" + +#include <qbuffer.h> + +#include <qdebug.h> + +#ifndef QT_NO_QWS_INPUTMETHODS + +QT_BEGIN_NAMESPACE + +static QWidget* activeWidget = 0; + +//#define EXTRA_DEBUG + +QWSInputContext::QWSInputContext(QObject *parent) + :QInputContext(parent) +{ +} + +void QWSInputContext::reset() +{ + QPaintDevice::qwsDisplay()->resetIM(); +} + + +void QWSInputContext::setFocusWidget( QWidget *w ) +{ + QWidget *oldFocus = focusWidget(); + if (oldFocus == w) + return; + + if (w) { + QWSInputContext::updateImeStatus(w, true); + } else { + if (oldFocus) + QWSInputContext::updateImeStatus(oldFocus, false); + } + + if (oldFocus) { + QWidget *tlw = oldFocus->window(); + int winid = tlw->internalWinId(); + + int widgetid = oldFocus->internalWinId(); + QPaintDevice::qwsDisplay()->sendIMUpdate(QWSInputMethod::FocusOut, winid, widgetid); + } + + QInputContext::setFocusWidget(w); + + if (!w) + return; + + QWidget *tlw = w->window(); + int winid = tlw->winId(); + + int widgetid = w->winId(); + QPaintDevice::qwsDisplay()->sendIMUpdate(QWSInputMethod::FocusIn, winid, widgetid); + + //setfocus ??? + + update(); +} + + +void QWSInputContext::widgetDestroyed(QWidget *w) +{ + if (w == QT_PREPEND_NAMESPACE(activeWidget)) + QT_PREPEND_NAMESPACE(activeWidget) = 0; + QInputContext::widgetDestroyed(w); +} + +void QWSInputContext::update() +{ + QWidget *w = focusWidget(); + if (!w) + return; + + QWidget *tlw = w->window(); + int winid = tlw->winId(); + + int widgetid = w->winId(); + QPaintDevice::qwsDisplay()->sendIMUpdate(QWSInputMethod::Update, winid, widgetid); + +} + +void QWSInputContext::mouseHandler( int x, QMouseEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) + QPaintDevice::qwsDisplay()->sendIMMouseEvent( x, event->type() == QEvent::MouseButtonPress ); +} + +QWidget *QWSInputContext::activeWidget() +{ + return QT_PREPEND_NAMESPACE(activeWidget); +} + + +bool QWSInputContext::isComposing() const +{ + return QT_PREPEND_NAMESPACE(activeWidget) != 0; +} + +bool QWSInputContext::translateIMQueryEvent(QWidget *w, const QWSIMQueryEvent *e) +{ + Qt::InputMethodQuery type = static_cast<Qt::InputMethodQuery>(e->simpleData.property); + QVariant result = w->inputMethodQuery(type); + QWidget *tlw = w->window(); + int winId = tlw->winId(); + + if ( type == Qt::ImMicroFocus ) { + // translate to relative to tlw + QRect mf = result.toRect(); + mf.moveTopLeft(w->mapTo(tlw,mf.topLeft())); + result = mf; + } + + QPaintDevice::qwsDisplay()->sendIMResponse(winId, e->simpleData.property, result); + + return false; +} + +bool QWSInputContext::translateIMInitEvent(const QWSIMInitEvent *e) +{ + Q_UNUSED(e); + qDebug("### QWSInputContext::translateIMInitEvent not implemented ###"); + return false; +} + +bool QWSInputContext::translateIMEvent(QWidget *w, const QWSIMEvent *e) +{ + QDataStream stream(e->streamingData); + QString preedit; + QString commit; + + stream >> preedit; + stream >> commit; + + if (preedit.isEmpty() && QT_PREPEND_NAMESPACE(activeWidget)) + w = QT_PREPEND_NAMESPACE(activeWidget); + + QInputContext *qic = w->inputContext(); + if (!qic) + return false; + + QList<QInputMethodEvent::Attribute> attrs; + + + while (!stream.atEnd()) { + int type = -1; + int start = -1; + int length = -1; + QVariant data; + stream >> type >> start >> length >> data; + if (stream.status() != QDataStream::Ok) { + qWarning("corrupted QWSIMEvent"); + //qic->reset(); //??? + return false; + } + if (type == QInputMethodEvent::TextFormat) + data = qic->standardFormat(static_cast<QInputContext::StandardFormat>(data.toInt())); + attrs << QInputMethodEvent::Attribute(static_cast<QInputMethodEvent::AttributeType>(type), start, length, data); + } +#ifdef EXTRA_DEBUG + qDebug() << "preedit" << preedit << "len" << preedit.length() <<"commit" << commit << "len" << commit.length() + << "n attr" << attrs.count(); +#endif + + if (preedit.isEmpty()) + QT_PREPEND_NAMESPACE(activeWidget) = 0; + else + QT_PREPEND_NAMESPACE(activeWidget) = w; + + + QInputMethodEvent ime(preedit, attrs); + if (!commit.isEmpty() || e->simpleData.replaceLength > 0) + ime.setCommitString(commit, e->simpleData.replaceFrom, e->simpleData.replaceLength); + + + extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_qws.cpp + qt_sendSpontaneousEvent(w, &ime); + + return true; +} + +Q_GUI_EXPORT void (*qt_qws_inputMethodStatusChanged)(QWidget*) = 0; + +void QWSInputContext::updateImeStatus(QWidget *w, bool hasFocus) +{ + Q_UNUSED(hasFocus); + + if (!w || !qt_qws_inputMethodStatusChanged) + return; + qt_qws_inputMethodStatusChanged(w); +} + + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_INPUTMETHODS diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 6fd45adf44..ba7618c237 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -171,6 +171,31 @@ unix:x11 { kernel/qeventdispatcher_x11_p.h } +wayland { + HEADERS += \ + kernel/qeventdispatcher_wayland_p.h + + SOURCES += \ + kernel/qapplication_wayland.cpp \ + kernel/qclipboard_wayland.cpp \ + kernel/qcursor_wayland.cpp \ + kernel/qdesktopwidget_wayland.cpp \ + kernel/qdnd_wayland.cpp \ + kernel/qeventdispatcher_wayland.cpp \ + kernel/qsound_wayland.cpp \ + kernel/qwidget_wayland.cpp \ + kernel/qkeymapper_wayland.cpp \ + + contains(QT_CONFIG, glib) { + SOURCES += \ + kernel/qeventdispatcher_glib_wayland.cpp + HEADERS += \ + kernel/qeventdispatcher_glib_wayland_p.h + QMAKE_CXXFLAGS += $$QT_CFLAGS_GLIB + LIBS_PRIVATE +=$$QT_LIBS_GLIB + } +} + embedded { HEADERS += \ kernel/qeventdispatcher_qws_p.h diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index cb1d063cb3..03cb32ffdd 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -235,10 +235,15 @@ public: virtual bool macEventFilter(EventHandlerCallRef, EventRef); #endif #if defined(Q_WS_X11) +#error foo virtual bool x11EventFilter(XEvent *); virtual int x11ClientMessage(QWidget*, XEvent*, bool passive_only); int x11ProcessEvent(XEvent*); #endif +#if defined(Q_WS_WAYLAND) + virtual bool waylandEventFilter(struct wl_message *); + int waylandProcessEvent(const struct wl_message *); +#endif #if defined(Q_OS_SYMBIAN) int symbianProcessEvent(const QSymbianEvent *event); virtual bool symbianEventFilter(const QSymbianEvent *event); diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 3a3f816097..570111a824 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -303,6 +303,9 @@ public: static bool qws_apply_settings(); static QWidget *findWidget(const QObjectList&, const QPoint &, bool rec); #endif +#if defined(Q_WS_WAYLAND) + static QWidget *findWidget(const QObjectList&, const QPoint &, bool rec); +#endif static bool quitOnLastWindowClosed; static void emitLastWindowClosed(); #ifdef Q_WS_WINCE diff --git a/src/gui/kernel/qapplication_wayland.cpp b/src/gui/kernel/qapplication_wayland.cpp new file mode 100644 index 0000000000..bffeb2e613 --- /dev/null +++ b/src/gui/kernel/qapplication_wayland.cpp @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qglobal.h" +#include "qlibrary.h" +#include "qcursor.h" +#include "qapplication.h" +#include "private/qapplication_p.h" +#include "qwidget.h" +#include "qbitarray.h" +#include "qpainter.h" +#include "qpixmapcache.h" +#include "qdatetime.h" +#include "qtextcodec.h" +#include "qdatastream.h" +#include "qbuffer.h" +#include "qsocketnotifier.h" +#include "qsessionmanager.h" +#include "qclipboard.h" +#include "qbitmap.h" + +#include "qfile.h" +#include "qhash.h" +#include "qdesktopwidget.h" +#include "qcolormap.h" +#include "private/qcursor_p.h" +#include "qsettings.h" +#include "qdebug.h" +#include "qeventdispatcher_wayland_p.h" +#if !defined(QT_NO_GLIB) +# include "qeventdispatcher_glib_wayland_p.h" +#endif + + +#include "private/qwidget_p.h" +#include "private/qbackingstore_p.h" +#include "private/qwindowsurface_wayland_p.h" +#include "private/qfont_p.h" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <errno.h> +#include <fcntl.h> +#ifdef Q_OS_VXWORKS +# include <sys/times.h> +#else +# include <sys/time.h> +#endif +#include <sys/stat.h> +#include <sys/types.h> + +#include <qvfbhdr.h> + +#ifndef QT_NO_QWS_MULTIPROCESS +#ifdef QT_NO_QSHM +#include <sys/ipc.h> +#include <sys/shm.h> +#ifndef Q_OS_DARWIN +# include <sys/sem.h> +#endif +#include <sys/socket.h> +#endif +#endif + +QT_BEGIN_NAMESPACE + +static QString appName; +int *qt_last_x = 0; +int *qt_last_y = 0; + +void QApplicationPrivate::createEventDispatcher() +{ +} + +void QApplicationPrivate::initializeWidgetPaletteHash() +{ +} + +static void init_display() +{ + // FIXME: Connect to Wayland server + + // Get display parameters + // Set paintdevice parameters + // XXX initial info sent from server + // Misc. initialization + + QColormap::initialize(); + QFont::initialize(); +#ifndef QT_NO_CURSOR + QCursorData::initialize(); +#endif + + qApp->setObjectName(appName); + + if (!QApplicationPrivate::sys_font) { +#ifdef QT_NO_FREETYPE + QFont f = QFont(QLatin1String("helvetica"), 10); +#else + QFont f = QFont(QLatin1String("DejaVu Sans"), 12); +#endif + QApplicationPrivate::setSystemFont(f); + } +} + +void qt_init_display() +{ + qt_is_gui_used = true; + init_display(); +} + +static bool read_bool_env_var(const char *var, bool defaultvalue) +{ + // returns true if env variable is set to non-zero + // returns false if env var is set to zero + // returns defaultvalue if env var not set + char *x = ::getenv(var); + return (x && *x) ? (strcmp(x,"0") != 0) : defaultvalue; +} + +static int read_int_env_var(const char *var, int defaultvalue) +{ + bool ok; + int r = qgetenv(var).toInt(&ok); + return ok ? r : defaultvalue; +} + +void qt_init(QApplicationPrivate *priv, int type) +{ +#ifdef QT_NO_QWS_MULTIPROCESS + if (type == QApplication::GuiClient) + type = QApplication::GuiServer; +#endif + if (type == QApplication::GuiServer) + qt_is_gui_used = false; //we'll turn it on in a second + + priv->inputContext = 0; + + int flags = 0; + char *p; + int argc = priv->argc; + char **argv = priv->argv; + int j; + + // Set application name + + if (argv && *argv) { //apparently, we allow people to pass 0 on the other platforms + p = strrchr(argv[0], '/'); + appName = QString::fromLocal8Bit(p ? p + 1 : argv[0]); + } + + // Get command line params + + j = argc ? 1 : 0; + QString decoration; + for (int i=1; i<argc; i++) { + if (argv[i] && *argv[i] != '-') { + argv[j++] = argv[i]; + continue; + } + } + if(j < priv->argc) { + priv->argv[j] = 0; + priv->argc = j; + } +} + +/***************************************************************************** + qt_cleanup() - cleans up when the application is finished + *****************************************************************************/ + +void qt_cleanup() +{ + QPixmapCache::clear(); +#ifndef QT_NO_CURSOR + QCursorData::cleanup(); +#endif + QFont::cleanup(); + QColormap::cleanup(); +} + + +/***************************************************************************** + Platform specific global and internal functions + *****************************************************************************/ + +QString QApplicationPrivate::appName() const // get application name +{ + return QT_PREPEND_NAMESPACE(appName); +} + +/***************************************************************************** + Platform specific QApplication members + *****************************************************************************/ + +#define NoValue 0x0000 +#define XValue 0x0001 +#define YValue 0x0002 +#define WidthValue 0x0004 +#define HeightValue 0x0008 +#define AllValues 0x000F +#define XNegative 0x0010 +#define YNegative 0x0020 + +/* Copyright notice for ReadInteger and parseGeometry + +Copyright (c) 1985, 1986, 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ +/* + * XParseGeometry parses strings of the form + * "=<width>x<height>{+-}<xoffset>{+-}<yoffset>", where + * width, height, xoffset, and yoffset are unsigned integers. + * Example: "=80x24+300-49" + * The equal sign is optional. + * It returns a bitmask that indicates which of the four values + * were actually found in the string. For each value found, + * the corresponding argument is updated; for each value + * not found, the corresponding argument is left unchanged. + */ + +static int +ReadInteger(char *string, char **NextString) +{ + register int Result = 0; + int Sign = 1; + + if (*string == '+') + string++; + else if (*string == '-') + { + string++; + Sign = -1; + } + for (; (*string >= '0') && (*string <= '9'); string++) + { + Result = (Result * 10) + (*string - '0'); + } + *NextString = string; + if (Sign >= 0) + return Result; + else + return -Result; +} + +static int parseGeometry(const char* string, + int* x, int* y, int* width, int* height) +{ + int mask = NoValue; + register char *strind; + unsigned int tempWidth=0, tempHeight=0; + int tempX=0, tempY=0; + char *nextCharacter; + + if (!string || (*string == '\0')) return mask; + if (*string == '=') + string++; /* ignore possible '=' at beg of geometry spec */ + + strind = const_cast<char *>(string); + if (*strind != '+' && *strind != '-' && *strind != 'x') { + tempWidth = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return 0; + strind = nextCharacter; + mask |= WidthValue; + } + + if (*strind == 'x' || *strind == 'X') { + strind++; + tempHeight = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return 0; + strind = nextCharacter; + mask |= HeightValue; + } + + if ((*strind == '+') || (*strind == '-')) { + if (*strind == '-') { + strind++; + tempX = -ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return 0; + strind = nextCharacter; + mask |= XNegative; + + } + else + { strind++; + tempX = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return 0; + strind = nextCharacter; + } + mask |= XValue; + if ((*strind == '+') || (*strind == '-')) { + if (*strind == '-') { + strind++; + tempY = -ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return 0; + strind = nextCharacter; + mask |= YNegative; + + } + else + { + strind++; + tempY = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return 0; + strind = nextCharacter; + } + mask |= YValue; + } + } + + /* If strind isn't at the end of the string then it's an invalid + geometry specification. */ + + if (*strind != '\0') return 0; + + if (mask & XValue) + *x = tempX; + if (mask & YValue) + *y = tempY; + if (mask & WidthValue) + *width = tempWidth; + if (mask & HeightValue) + *height = tempHeight; + return mask; +} + +#ifdef QT3_SUPPORT +void QApplication::setMainWidget(QWidget *mainWidget) +{ + QApplicationPrivate::main_widget = mainWidget; +} +#endif + +/***************************************************************************** + QApplication cursor stack + *****************************************************************************/ +#ifndef QT_NO_CURSOR +void QApplication::setOverrideCursor(const QCursor &cursor) +{ +} + +void QApplication::restoreOverrideCursor() +{ +} +#endif// QT_NO_CURSOR + + + +/***************************************************************************** + Routines to find a Qt widget from a screen position + *****************************************************************************/ + +/*! + \internal +*/ +QWidget *QApplicationPrivate::findWidget(const QObjectList& list, + const QPoint &pos, bool rec) +{ + QWidget *w; + + for (int i = list.size()-1; i >= 0; --i) { + if (list.at(i)->isWidgetType()) { + w = static_cast<QWidget*>(list.at(i)); + if (w->isVisible() && !w->testAttribute(Qt::WA_TransparentForMouseEvents) && w->geometry().contains(pos) + && (!w->d_func()->extra || w->d_func()->extra->mask.isEmpty() || w->d_func()->extra->mask.contains(pos - w->geometry().topLeft()) )) { + if (!rec) + return w; + QWidget *c = w->childAt(w->mapFromParent(pos)); + return c ? c : w; + } + } + } + return 0; +} + + +QWidget *QApplication::topLevelAt(const QPoint &pos) +{ + //### QWSDisplay::windowAt() is currently only implemented in the server process + //int winId = QPaintDevice::qwsDisplay()->windowAt(pos); + //if (winId !=0) + //return QWidget::find(winId); + +#if 1 + // fallback implementation for client processes +//### This is slightly wrong: we have no guarantee that the list is in +//### stacking order, so if the topmost window is transparent, we may +//### return the wrong widget + + QWidgetList list = topLevelWidgets(); + for (int i = list.size()-1; i >= 0; --i) { + QWidget *w = list[i]; + if (w != QApplication::desktop() && w->isVisible()) + return w; + } +#endif + return 0; +} + +void QApplication::beep() +{ +} + +void QApplication::alert(QWidget *, int) +{ +} + +int QApplication::waylandProcessEvent(const struct wl_message *msg) +{ +} + +bool QApplication::waylandEventFilter(struct wl_message *msg) +{ + return false; +} + +bool QApplicationPrivate::modalState() +{ + return false; +} + +void QApplicationPrivate::enterModal_sys(QWidget *widget) +{ +} + +void QApplicationPrivate::leaveModal_sys(QWidget *widget) +{ +} + +void QApplicationPrivate::openPopup(QWidget *popup) +{ +} + +void QApplicationPrivate::closePopup(QWidget *popup) +{ +} + +void QApplication::setCursorFlashTime(int msecs) +{ + QApplicationPrivate::cursor_flash_time = msecs; +} + + +int QApplication::cursorFlashTime() +{ + return QApplicationPrivate::cursor_flash_time; +} + +void QApplication::setDoubleClickInterval(int ms) +{ + QApplicationPrivate::mouse_double_click_time = ms; +} + +int QApplication::doubleClickInterval() +{ + return QApplicationPrivate::mouse_double_click_time; +} + +void QApplication::setKeyboardInputInterval(int ms) +{ + QApplicationPrivate::keyboard_input_time = ms; +} + +int QApplication::keyboardInputInterval() +{ + return QApplicationPrivate::keyboard_input_time; +} + +#ifndef QT_NO_WHEELEVENT +void QApplication::setWheelScrollLines(int lines) +{ + QApplicationPrivate::wheel_scroll_lines = lines; +} + +int QApplication::wheelScrollLines() +{ + return QApplicationPrivate::wheel_scroll_lines; +} +#endif + +void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) +{ + switch (effect) { + case Qt::UI_AnimateMenu: + QApplicationPrivate::animate_menu = enable; + break; + case Qt::UI_FadeMenu: + if (enable) + QApplicationPrivate::animate_menu = true; + QApplicationPrivate::fade_menu = enable; + break; + case Qt::UI_AnimateCombo: + QApplicationPrivate::animate_combo = enable; + break; + case Qt::UI_AnimateTooltip: + QApplicationPrivate::animate_tooltip = enable; + break; + case Qt::UI_FadeTooltip: + if (enable) + QApplicationPrivate::animate_tooltip = true; + QApplicationPrivate::fade_tooltip = enable; + break; + case Qt::UI_AnimateToolBox: + QApplicationPrivate::animate_toolbox = enable; + break; + default: + QApplicationPrivate::animate_ui = enable; + break; + } +} + +bool QApplication::isEffectEnabled(Qt::UIEffect effect) +{ + if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui) + return false; + + switch(effect) { + case Qt::UI_AnimateMenu: + return QApplicationPrivate::animate_menu; + case Qt::UI_FadeMenu: + return QApplicationPrivate::fade_menu; + case Qt::UI_AnimateCombo: + return QApplicationPrivate::animate_combo; + case Qt::UI_AnimateTooltip: + return QApplicationPrivate::animate_tooltip; + case Qt::UI_FadeTooltip: + return QApplicationPrivate::fade_tooltip; + case Qt::UI_AnimateToolBox: + return QApplicationPrivate::animate_toolbox; + default: + return QApplicationPrivate::animate_ui; + } +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qclipboard_wayland.cpp b/src/gui/kernel/qclipboard_wayland.cpp new file mode 100644 index 0000000000..f9b17d9690 --- /dev/null +++ b/src/gui/kernel/qclipboard_wayland.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qclipboard.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qapplication.h" +#include "qbitmap.h" +#include "qdatetime.h" +#include "qbuffer.h" +#include "qwidget.h" +#include "qevent.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +static const int TextClipboard=424242; +static bool init = false; + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } +#if 0 + void addTransferredPixmap(QPixmap pm) + { /* TODO: queue them */ + transferred[tindex] = pm; + tindex=(tindex+1)%2; + } + void clearTransfers() + { + transferred[0] = QPixmap(); + transferred[1] = QPixmap(); + } +#endif + + void clear(); + +private: + QMimeData* src; + +#if 0 + QPixmap transferred[2]; + int tindex; +#endif +}; + +QClipboardData::QClipboardData() +{ + src = 0; +#if 0 + tindex=0; +#endif +} + +QClipboardData::~QClipboardData() +{ + delete src; +} + +void QClipboardData::clear() +{ + delete src; + src = 0; +} + + +static QClipboardData *internalCbData = 0; + +static void cleanupClipboardData() +{ + delete internalCbData; + internalCbData = 0; +} + +static QClipboardData *clipboardData() +{ + if (internalCbData == 0) { + internalCbData = new QClipboardData; + qAddPostRoutine(cleanupClipboardData); + } + return internalCbData; +} + + +/***************************************************************************** + QClipboard member functions for FB. + *****************************************************************************/ + +#if 0 + +QString QClipboard::text() const +{ +} + +void QClipboard::setText(const QString &text) +{ +} + +QString QClipboard::text(QString& subtype) const +{ + QString r; + if (subtype == "plain") + r = text(); + return r; +} + +#endif + +void QClipboard::clear(Mode mode) +{ + setText(QString(), mode); +} + + +bool QClipboard::event(QEvent *e) +{ + static bool recursionWatch = false; + if (e->type() != QEvent::Clipboard || recursionWatch) + return QObject::event(e); + + recursionWatch = true; +#if 0 + if (event) { //&& event->simpleData.state == QWSPropertyNotifyEvent::PropertyNewValue) { + QClipboardData *d = clipboardData(); + QString t("foo"); + if( (d->source() == 0 && !t.isEmpty()) || (d->source() != 0 && d->source()->text() != t) ) { + if( !d->source() ) + d->setSource(new QMimeData); + d->source()->setText( t ); + emitChanged(QClipboard::Clipboard); + } + } +#endif + recursionWatch = false; + return true; +} + +const QMimeData* QClipboard::mimeData(Mode mode) const +{ + if (mode != Clipboard) return 0; + + QClipboardData *d = clipboardData(); + // Try and get data from QWSProperty if no mime data has been set on us. + if( !d->source() ) { + QString t("foo"); + if( !t.isEmpty() ) { + QMimeData* nd = new QMimeData; + nd->setText( t ); + d->setSource( nd ); + } + } + return d->source(); +} + +void QClipboard::setMimeData(QMimeData* src, Mode mode) +{ + if (mode != Clipboard) return; + + QClipboardData *d = clipboardData(); + + /* Propagate text data to other QWSClients */ + + QString newText; + if( src != 0 ) + newText = src->text(); + QString oldText; + if( d->source() != 0 ) + oldText = d->source()->text(); + + d->setSource(src); +#if 0 + if( oldText != newText ) { + if( d->source() == 0 ) { + qwsSetClipboardText( QString() ); + } else { + qwsSetClipboardText( d->source()->text() ); + } + } +#endif + emitChanged(QClipboard::Clipboard); +} + +bool QClipboard::supportsMode(Mode mode) const +{ + return (mode == Clipboard); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + if (mode == Clipboard) + qWarning("QClipboard::ownsClipboard: UNIMPLEMENTED!"); + return false; +} + +void QClipboard::connectNotify( const char * ) +{ +} + +void QClipboard::ownerDestroyed() +{ +} + +#endif // QT_NO_CLIPBOARD + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qcursor_wayland.cpp b/src/gui/kernel/qcursor_wayland.cpp new file mode 100644 index 0000000000..92589960af --- /dev/null +++ b/src/gui/kernel/qcursor_wayland.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 <qcursor.h> +#include <private/qcursor_p.h> +#include <qbitmap.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +#ifndef QT_NO_CURSOR + +static int nextCursorId = Qt::BitmapCursor; + +/***************************************************************************** + Internal QCursorData class + *****************************************************************************/ + +QCursorData::QCursorData(Qt::CursorShape s) + : cshape(s), bm(0), bmm(0), hx(0), hy(0) +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + delete bm; + delete bmm; + QT_TRY { + /* FIXME: call wayland cursor destroy */ + //QPaintDevice::qwsDisplay()->destroyCursor(id); + } QT_CATCH(const std::bad_alloc &) { + // do nothing. + } +} + + +/***************************************************************************** + Global cursors + *****************************************************************************/ + +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + QCursorData *d = new QCursorData; + d->bm = new QBitmap(bitmap); + d->bmm = new QBitmap(mask); + d->cshape = Qt::BitmapCursor; + d->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + + return d; +} + +void QCursorData::update() +{ +} + +#endif //QT_NO_CURSOR + +extern int *qt_last_x,*qt_last_y; + +QPoint QCursor::pos() +{ + // This doesn't know about hotspots yet so we disable it + //qt_accel_update_cursor(); + if (qt_last_x) + return QPoint(*qt_last_x, *qt_last_y); + else + return QPoint(); +} + +void QCursor::setPos(int x, int y) +{ + // Need to check, since some X servers generate null mouse move + // events, causing looping in applications which call setPos() on + // every mouse move event. + // + if (pos() == QPoint(x, y)) + return; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qdesktopwidget_wayland.cpp b/src/gui/kernel/qdesktopwidget_wayland.cpp new file mode 100644 index 0000000000..5d05d8df31 --- /dev/null +++ b/src/gui/kernel/qdesktopwidget_wayland.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qdesktopwidget.h" +#include "private/qapplication_p.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +QDesktopWidget::QDesktopWidget() + : QWidget(0, Qt::Desktop) +{ + setObjectName(QLatin1String("desktop")); +} + +QDesktopWidget::~QDesktopWidget() +{ +} + +bool QDesktopWidget::isVirtualDesktop() const +{ + return true; +} + +int QDesktopWidget::primaryScreen() const +{ + return 0; +} + +int QDesktopWidget::numScreens() const +{ +} + +QWidget *QDesktopWidget::screen(int) +{ + return this; +} + +const QRect QDesktopWidget::availableGeometry(int screenNo) const +{ +} + +const QRect QDesktopWidget::screenGeometry(int screenNo) const +{ +} + +int QDesktopWidget::screenNumber(const QWidget *w) const +{ +} + +int QDesktopWidget::screenNumber(const QPoint &p) const +{ +} + +void QDesktopWidget::resizeEvent(QResizeEvent *) +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qdnd_wayland.cpp b/src/gui/kernel/qdnd_wayland.cpp new file mode 100644 index 0000000000..0dfc6dc968 --- /dev/null +++ b/src/gui/kernel/qdnd_wayland.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qapplication.h" + +#ifndef QT_NO_DRAGANDDROP + +#include "qwidget.h" +#include "qdatetime.h" +#include "qbitmap.h" +#include "qcursor.h" +#include "qevent.h" +#include "qpainter.h" +#include "qdnd_p.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +static QPixmap *defaultPm = 0; +static const int default_pm_hotx = -2; +static const int default_pm_hoty = -16; +static const char *const default_pm[] = { +"13 9 3 1", +". c None", +" c #000000", +"X c #FFFFFF", +"X X X X X X X", +" X X X X X X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X X X X X X ", +"X X X X X X X", +}; + +// Shift/Ctrl handling, and final drop status +static Qt::DropAction global_accepted_action = Qt::CopyAction; +static Qt::DropActions possible_actions = Qt::IgnoreAction; + + +// static variables in place of a proper cross-process solution +static QDrag *drag_object; +static bool qt_qws_dnd_dragging = false; + + +static Qt::KeyboardModifiers oldstate; + +class QShapedPixmapWidget : public QWidget { + QPixmap pixmap; +public: + QShapedPixmapWidget() : + QWidget(0, Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint) + { + // ### Temporary workaround for 4.2-rc1!!! To prevent flickering when + // using drag'n drop in a client application. (task 126956) + // setAttribute() should be done unconditionally! + if (QApplication::type() == QApplication::GuiServer) + setAttribute(Qt::WA_TransparentForMouseEvents); + } + + void setPixmap(QPixmap pm) + { + pixmap = pm; + if (!pixmap.mask().isNull()) { + setMask(pixmap.mask()); + } else { + clearMask(); + } + resize(pm.width(),pm.height()); + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.drawPixmap(0,0,pixmap); + } +}; + + +static QShapedPixmapWidget *qt_qws_dnd_deco = 0; + + +void QDragManager::updatePixmap() +{ + if (qt_qws_dnd_deco) { + QPixmap pm; + QPoint pm_hot(default_pm_hotx,default_pm_hoty); + if (drag_object) { + pm = drag_object->pixmap(); + if (!pm.isNull()) + pm_hot = drag_object->hotSpot(); + } + if (pm.isNull()) { + if (!defaultPm) + defaultPm = new QPixmap(default_pm); + pm = *defaultPm; + } + //qt_qws_dnd_deco->setPixmap(pm); + //qt_qws_dnd_deco->move(QCursor::pos()-pm_hot); + if (willDrop) { + //qt_qws_dnd_deco->show(); + } else { + //qt_qws_dnd_deco->hide(); + } + } +} + +void QDragManager::timerEvent(QTimerEvent *) { } + +void QDragManager::move(const QPoint &) { } + +void QDragManager::updateCursor() +{ +#ifndef QT_NO_CURSOR + if (willDrop) { + if (qt_qws_dnd_deco) + ; + //qt_qws_dnd_deco->show(); + } else { + QCursor *overrideCursor = QApplication::overrideCursor(); + if (!overrideCursor || overrideCursor->shape() != Qt::ForbiddenCursor) { + QApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor)); + } + if (qt_qws_dnd_deco) + ; + //qt_qws_dnd_deco->hide(); + } +#endif +} + + +bool QDragManager::eventFilter(QObject *o, QEvent *e) +{ + if (beingCancelled) { + if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) { + qApp->removeEventFilter(this); + Q_ASSERT(object == 0); + beingCancelled = false; + eventLoop->exit(); + return true; // block the key release + } + return false; + } + + + + if (!o->isWidgetType()) + return false; + + switch(e->type()) { + case QEvent::ShortcutOverride: + // prevent accelerators from firing while dragging + e->accept(); + return true; + + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + QKeyEvent *ke = ((QKeyEvent*)e); + if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + eventLoop->exit(); + } else { + updateCursor(); + } + return true; // Eat all key events + } + + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + { + if (!object) { //#### this should not happen + qWarning("QDragManager::eventFilter: No object"); + return true; + } + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + if (manager->object) + possible_actions = manager->dragPrivate()->possible_actions; + else + possible_actions = Qt::IgnoreAction; + + QMouseEvent *me = (QMouseEvent *)e; + if (me->buttons()) { + Qt::DropAction prevAction = global_accepted_action; + QWidget *cw = QApplication::widgetAt(me->globalPos()); + + // Fix for when we move mouse on to the deco widget + if (qt_qws_dnd_deco && cw == qt_qws_dnd_deco) + cw = object->target(); + + while (cw && !cw->acceptDrops() && !cw->isWindow()) + cw = cw->parentWidget(); + + if (object->target() != cw) { + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + willDrop = false; + global_accepted_action = Qt::IgnoreAction; + updateCursor(); + restoreCursor = true; + object->d_func()->target = 0; + } + if (cw && cw->acceptDrops()) { + object->d_func()->target = cw; + QDragEnterEvent dee(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + QApplication::sendEvent(object->target(), &dee); + willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction; + global_accepted_action = willDrop ? dee.dropAction() : Qt::IgnoreAction; + updateCursor(); + restoreCursor = true; + } + } else if (cw) { + QDragMoveEvent dme(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + if (global_accepted_action != Qt::IgnoreAction) { + dme.setDropAction(global_accepted_action); + dme.accept(); + } + QApplication::sendEvent(cw, &dme); + willDrop = dme.isAccepted(); + global_accepted_action = willDrop ? dme.dropAction() : Qt::IgnoreAction; + updatePixmap(); + updateCursor(); + } + if (global_accepted_action != prevAction) + emitActionChanged(global_accepted_action); + } + return true; // Eat all mouse events + } + + case QEvent::MouseButtonRelease: + { + qApp->removeEventFilter(this); + if (restoreCursor) { + willDrop = false; +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + restoreCursor = false; + } + if (object && object->target()) { + QMouseEvent *me = (QMouseEvent *)e; + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + + QDropEvent de(object->target()->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + QApplication::sendEvent(object->target(), &de); + if (de.isAccepted()) + global_accepted_action = de.dropAction(); + else + global_accepted_action = Qt::IgnoreAction; + + if (object) + object->deleteLater(); + drag_object = object = 0; + } + eventLoop->exit(); + return true; // Eat all mouse events + } + + default: + break; + } + + return false; +} + +Qt::DropAction QDragManager::drag(QDrag *o) +{ + if (object == o || !o || !o->source()) + return Qt::IgnoreAction; + + if (object) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + } + + object = drag_object = o; + qt_qws_dnd_deco = new QShapedPixmapWidget(); + oldstate = Qt::NoModifier; // #### Should use state that caused the drag +// drag_mode = mode; + + willDrop = false; + updatePixmap(); + updateCursor(); + restoreCursor = true; + object->d_func()->target = 0; + qApp->installEventFilter(this); + + global_accepted_action = Qt::CopyAction; +#ifndef QT_NO_CURSOR + qApp->setOverrideCursor(Qt::ArrowCursor); + restoreCursor = true; + updateCursor(); +#endif + + qt_qws_dnd_dragging = true; + + eventLoop = new QEventLoop; + (void) eventLoop->exec(); + delete eventLoop; + eventLoop = 0; + + delete qt_qws_dnd_deco; + qt_qws_dnd_deco = 0; + qt_qws_dnd_dragging = false; + + + return global_accepted_action; +} + + +void QDragManager::cancel(bool deleteSource) +{ +// qDebug("QDragManager::cancel"); + beingCancelled = true; + + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + } + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif + + if (drag_object) { + if (deleteSource) + object->deleteLater(); + drag_object = object = 0; + } + + delete qt_qws_dnd_deco; + qt_qws_dnd_deco = 0; + + global_accepted_action = Qt::IgnoreAction; +} + + +void QDragManager::drop() +{ +} + +QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const +{ + if (!drag_object) + return QVariant(); + QByteArray data = drag_object->mimeData()->data(mimetype); + if (type == QVariant::String) + return QString::fromUtf8(data); + return data; +} + +bool QDropData::hasFormat_sys(const QString &format) const +{ + return formats().contains(format); +} + +QStringList QDropData::formats_sys() const +{ + if (drag_object) + return drag_object->mimeData()->formats(); + return QStringList(); +} + + +#endif // QT_NO_DRAGANDDROP + + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_glib_wayland.cpp b/src/gui/kernel/qeventdispatcher_glib_wayland.cpp new file mode 100644 index 0000000000..9b4ce0eee6 --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_glib_wayland.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qeventdispatcher_glib_wayland_p.h" + +#include "qapplication.h" + +#include "qplatformdefs.h" +#include "qapplication.h" + +#include <glib.h> + +QT_BEGIN_NAMESPACE + +struct GWaylandEventSource +{ + GSource source; + QEventLoop::ProcessEventsFlags flags; + QWaylandEventDispatcherGlib *q; + QWaylandEventDispatcherGlibPrivate *d; +}; + +class QWaylandEventDispatcherGlibPrivate : public QEventDispatcherGlibPrivate +{ + Q_DECLARE_PUBLIC(QWaylandEventDispatcherGlib) + +public: + QWaylandEventDispatcherGlibPrivate(); + GWaylandEventSource *waylandEventSource; + QList<struct wl_message*> queuedUserInputEvents; +}; + +static gboolean waylandEventSourcePrepare(GSource *s, gint *timeout) +{ + return false; +} + +static gboolean waylandEventSourceCheck(GSource *s) +{ + return true; +} + +static gboolean waylandEventSourceDispatch(GSource *s, GSourceFunc callback, gpointer user_data) +{ + return true; +} + +static GSourceFuncs waylandEventSourceFuncs = { + waylandEventSourcePrepare, + waylandEventSourceCheck, + waylandEventSourceDispatch, + NULL, + NULL, + NULL +}; + +QWaylandEventDispatcherGlibPrivate::QWaylandEventDispatcherGlibPrivate() +{ + //g_source_attach(&waylandEventSource->source, mainContext); +} + +QWaylandEventDispatcherGlib::QWaylandEventDispatcherGlib(QObject *parent) + : QEventDispatcherGlib(*new QWaylandEventDispatcherGlibPrivate, parent) +{ +} + +QWaylandEventDispatcherGlib::~QWaylandEventDispatcherGlib() +{ + Q_D(QWaylandEventDispatcherGlib); + + //g_source_destroy(&d->waylandEventSource->source); +} + +bool +QWaylandEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QWaylandEventDispatcherGlib); + QEventLoop::ProcessEventsFlags saved_flags = d->waylandEventSource->flags; + d->waylandEventSource->flags = flags; + bool returnValue = QEventDispatcherGlib::processEvents(flags); + d->waylandEventSource->flags = saved_flags; + return returnValue; +} + +void QWaylandEventDispatcherGlib::startingUp() +{ + Q_D(QWaylandEventDispatcherGlib); + d->waylandEventSource->q = this; + d->waylandEventSource->d = d; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_glib_wayland_p.h b/src/gui/kernel/qeventdispatcher_glib_wayland_p.h new file mode 100644 index 0000000000..b15fddb0eb --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_glib_wayland_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef WAYLANDEVENTDISPATCHER_GLIB_P_H +#define WAYLANDEVENTDISPATCHER_GLIB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qeventdispatcher_glib_p.h> + +QT_BEGIN_NAMESPACE + +class QWaylandEventDispatcherGlibPrivate; + +class QWaylandEventDispatcherGlib : public QEventDispatcherGlib +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandEventDispatcherGlib) + +public: + explicit QWaylandEventDispatcherGlib(QObject *parent = 0); + ~QWaylandEventDispatcherGlib(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + + void startingUp(); +}; + +QT_END_NAMESPACE + +#endif // WAYLANDEVENTDISPATCHER_GLIB_P_H diff --git a/src/gui/kernel/qeventdispatcher_wayland.cpp b/src/gui/kernel/qeventdispatcher_wayland.cpp new file mode 100644 index 0000000000..b7c1764f3e --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_wayland.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qplatformdefs.h" +#include "qapplication.h" +#include "qeventdispatcher_wayland_p.h" +#include "private/qeventdispatcher_unix_p.h" +#ifndef QT_NO_THREAD +# include "qmutex.h" +#endif + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +class QEventDispatcherWaylandPrivate : public QEventDispatcherUNIXPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherWayland) +public: + inline QEventDispatcherWaylandPrivate() + { } + QList<struct wl_message*> queuedUserInputEvents; +}; + + +QEventDispatcherWayland::QEventDispatcherWayland(QObject *parent) + : QEventDispatcherUNIX(*new QEventDispatcherWaylandPrivate, parent) +{ } + +QEventDispatcherWayland::~QEventDispatcherWayland() +{ } + +bool QEventDispatcherWayland::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + return true; +} + +bool QEventDispatcherWayland::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return qGlobalPostedEventsCount(); +} + +void QEventDispatcherWayland::startingUp() +{ + +} + +void QEventDispatcherWayland::closingDown() +{ + +} + +void QEventDispatcherWayland::flush() +{ + if(qApp) + qApp->sendPostedEvents(); +} + + +int QEventDispatcherWayland::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout) +{ + return QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_wayland_p.h b/src/gui/kernel/qeventdispatcher_wayland_p.h new file mode 100644 index 0000000000..342d2776dd --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_wayland_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_WAYLAND_P_H +#define QEVENTDISPATCHER_WAYLAND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qeventdispatcher_unix_p.h" + +QT_BEGIN_NAMESPACE + +class QEventDispatcherWaylandPrivate; + +class QEventDispatcherWayland : public QEventDispatcherUNIX +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherWayland) + +public: + explicit QEventDispatcherWayland(QObject *parent = 0); + ~QEventDispatcherWayland(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void flush(); + + void startingUp(); + void closingDown(); + +protected: + int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_WAYLAND_P_H diff --git a/src/gui/kernel/qkeymapper_wayland.cpp b/src/gui/kernel/qkeymapper_wayland.cpp new file mode 100644 index 0000000000..5b6b1c45ee --- /dev/null +++ b/src/gui/kernel/qkeymapper_wayland.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qkeymapper_p.h" +#include <qdebug.h> +#include <private/qevent_p.h> +#include <private/qlocale_p.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + + +QKeyMapperPrivate::QKeyMapperPrivate() +{ + keyboardInputLocale = QLocale::system(); + keyboardInputDirection = Qt::RightToLeft; +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ + // clearMappings(); +} + +void QKeyMapperPrivate::clearMappings() +{ +} + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e) +{ + QList<int> result; + if (e->key() && (e->key() != Qt::Key_unknown)) + result << int(e->key() + e->modifiers()); + else if (!e->text().isEmpty()) + result << int(e->text().at(0).unicode() + e->modifiers()); + return result; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qsound_wayland.cpp b/src/gui/kernel/qsound_wayland.cpp new file mode 100644 index 0000000000..08daad0337 --- /dev/null +++ b/src/gui/kernel/qsound_wayland.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qapplication.h" + +#ifndef QT_NO_SOUND + +#include "qsound.h" +#include "qpaintdevice.h" +#include "qsound_p.h" + +#include "qhash.h" +#include "qfileinfo.h" + +#include "qbytearray.h" +#include "quuid.h" +#include "qdatastream.h" +#include "qbuffer.h" + + +QT_BEGIN_NAMESPACE + +#endif // QT_NO_SOUND + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidget_wayland.cpp b/src/gui/kernel/qwidget_wayland.cpp new file mode 100644 index 0000000000..fd538aca4a --- /dev/null +++ b/src/gui/kernel/qwidget_wayland.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qcursor.h" +#include "qapplication.h" +#include "qapplication_p.h" +#include "qpainter.h" +#include "qbitmap.h" +#include "qimage.h" +#include "qhash.h" +#include "qstack.h" +#include "qlayout.h" +#include "qtextcodec.h" +#include "qinputcontext.h" +#include "qdesktopwidget.h" + +#include "qpaintengine.h" + +#include "qdebug.h" + +#include "qwidget_p.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + + +/***************************************************************************** + QWidget member functions + *****************************************************************************/ + +void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool /*destroyOldWindow*/) +{ +} + + +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ +} + + +void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) +{ +} + + +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ +} + +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ +} + +void QWidgetPrivate::updateSystemBackground() {} + +#ifndef QT_NO_CURSOR +void QWidgetPrivate::setCursor_sys(const QCursor &cursor) +{ +} + +void QWidgetPrivate::unsetCursor_sys() +{ +} +#endif //QT_NO_CURSOR + +void QWidgetPrivate::setWindowTitle_sys(const QString &caption) +{ +} + +void QWidgetPrivate::setWindowIcon_sys(bool /*forceReset*/) +{ +} + +void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) +{ + Q_UNUSED(iconText); +} + +void QWidget::grabMouse() +{ +} + +#ifndef QT_NO_CURSOR +void QWidget::grabMouse(const QCursor &cursor) +{ +} +#endif + +void QWidget::releaseMouse() +{ +} + +void QWidget::grabKeyboard() +{ +} + +void QWidget::releaseKeyboard() +{ +} + + +QWidget *QWidget::mouseGrabber() +{ +} + + +QWidget *QWidget::keyboardGrabber() +{ +} + +void QWidget::activateWindow() +{ +} + +void QWidgetPrivate::show_sys() +{ +} + + +void QWidgetPrivate::hide_sys() +{ +} + +void QWidget::setWindowState(Qt::WindowStates newstate) +{ +} + +void QWidgetPrivate::setFocus_sys() +{ +} + +void QWidgetPrivate::raise_sys() +{ +} + +void QWidgetPrivate::lower_sys() +{ +} + +void QWidgetPrivate::stackUnder_sys(QWidget*) +{ +} + +void QWidgetPrivate::setConstraints_sys() +{ +} + +void QWidgetPrivate::scroll_sys(int dx, int dy) +{ +} + +int QWidget::metric(PaintDeviceMetric m) const +{ +} + +void QWidgetPrivate::createSysExtra() +{ +} + +void QWidgetPrivate::deleteSysExtra() +{ +} + +void QWidgetPrivate::createTLSysExtra() +{ +} + +void QWidgetPrivate::deleteTLSysExtra() +{ +} + +void QWidgetPrivate::registerDropSite(bool on) +{ + Q_UNUSED(on); +} + +inline bool QRect::intersects(const QRect &r) const +{ +} + +void QWidgetPrivate::updateFrameStrut() +{ +} + +void QWidgetPrivate::setWindowOpacity_sys(qreal level) +{ +} + +QPaintEngine *QWidget::paintEngine() const +{ + qWarning("QWidget::paintEngine: Should no longer be called"); + return 0; +} + +QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() +{ +} + +void QWidgetPrivate::setModal_sys() +{ +} + + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qwindowdefs.h b/src/gui/kernel/qwindowdefs.h index a721c7da98..5b1b99e303 100644 --- a/src/gui/kernel/qwindowdefs.h +++ b/src/gui/kernel/qwindowdefs.h @@ -131,6 +131,10 @@ QT_END_HEADER #endif // Q_WS_QWS +#if defined(Q_WS_WAYLAND) +typedef unsigned long WId; +#endif // Q_WS_WAYLAND + #if defined(Q_OS_SYMBIAN) class CCoeControl; typedef CCoeControl * WId; diff --git a/src/gui/kernel/wayland.pri b/src/gui/kernel/wayland.pri new file mode 100644 index 0000000000..897cf53a6e --- /dev/null +++ b/src/gui/kernel/wayland.pri @@ -0,0 +1,3 @@ +wayland { +} + diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 123af1ce15..eff9944fb5 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -367,7 +367,10 @@ embedded { SOURCES += painting/qwindowsurface_qws.cpp } - +wayland { + HEADERS += painting/qwindowsurface_wayland_p.h + SOURCES += painting/qwindowsurface_wayland.cpp +} symbian { HEADERS += painting/qwindowsurface_s60_p.h diff --git a/src/gui/painting/qgraphicssystem.cpp b/src/gui/painting/qgraphicssystem.cpp index 2ea3d3318b..a3ec8ab5c3 100644 --- a/src/gui/painting/qgraphicssystem.cpp +++ b/src/gui/painting/qgraphicssystem.cpp @@ -53,6 +53,9 @@ #ifdef Q_WS_S60 # include <private/qpixmap_s60_p.h> #endif +#ifdef Q_WS_WAYLAND +# include <private/qpixmap_wayland_p.h> +#endif QT_BEGIN_NAMESPACE @@ -73,6 +76,8 @@ QPixmapData *QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixelType typ return new QMacPixmapData(type); #elif defined(Q_WS_S60) return new QS60PixmapData(type); +#elif defined(Q_WS_WAYLAND) + return new QWaylandPixmapData(type); #elif !defined(Q_WS_QWS) #error QGraphicsSystem::createDefaultPixmapData() not implemented #endif diff --git a/src/gui/painting/qgraphicssystem_wayland.cpp b/src/gui/painting/qgraphicssystem_wayland.cpp new file mode 100644 index 0000000000..db3d4b1a76 --- /dev/null +++ b/src/gui/painting/qgraphicssystem_wayland.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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_wayland.h> +#include "qgraphicssystem_wayland_p.h" +#include <private/qpixmap_raster_p.h> +#include <private/qwindowsurface_wayland_p.h> + +QT_BEGIN_NAMESPACE + +QPixmapData *QWSGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const +{ + if (screen->pixmapDataFactory()) + return screen->pixmapDataFactory()->create(type); //### For 4.4 compatability + else + return new QRasterPixmapData(type); +} + +QWindowSurface *QWSGraphicsSystem::createWindowSurface(QWidget *widget) const +{ + return screen->createSurface(widget); +} + +QT_END_NAMESPACE diff --git a/src/gui/painting/qgraphicssystem_wayland_p.h b/src/gui/painting/qgraphicssystem_wayland_p.h new file mode 100644 index 0000000000..d078f54a5a --- /dev/null +++ b/src/gui/painting/qgraphicssystem_wayland_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSYSTEM_QWS_P_H +#define QGRAPHICSSYSTEM_QWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicssystem_p.h" + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QWSGraphicsSystem : public QGraphicsSystem +{ +public: + + QWSGraphicsSystem() + : screen(QScreen::instance()) {} + + QWSGraphicsSystem(QScreen* s) + : screen(s) {} + + virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + QWindowSurface *createWindowSurface(QWidget *widget) const; + +private: + QScreen* screen; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/painting/qprintengine_wayland.cpp b/src/gui/painting/qprintengine_wayland.cpp new file mode 100644 index 0000000000..26847905f6 --- /dev/null +++ b/src/gui/painting/qprintengine_wayland.cpp @@ -0,0 +1,886 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 <private/qprintengine_wayland_p.h> + +#ifndef QT_NO_PRINTER + +#include <private/qpaintengine_raster_p.h> +#include <qimage.h> +#include <qfile.h> +#include <qdebug.h> +#include <QCopChannel> + +QT_BEGIN_NAMESPACE + +#define MM(n) int((n * 720 + 127) / 254) +#define IN(n) int(n * 72) + +extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size); + +QtopiaPrintEngine::QtopiaPrintEngine(QPrinter::PrinterMode mode) + : QPaintEngine(*(new QtopiaPrintEnginePrivate( mode ))) +{ + d_func()->initialize(); +} + +bool QtopiaPrintEngine::begin(QPaintDevice *) +{ + Q_D(QtopiaPrintEngine); + Q_ASSERT_X(d->printerState == QPrinter::Idle, "QtopiaPrintEngine", "printer already active"); + + // Create a new off-screen monochrome image to handle the drawing process. + QSize size = paperRect().size(); + if ( d->pageImage ) + delete d->pageImage; + d->pageImage = new QImage( size, QImage::Format_RGB32 ); + if ( !(d->pageImage) ) + return false; + + // Recreate the paint engine on the new image. + delete d->_paintEngine; + d->_paintEngine = 0; + d->paintEngine()->state = state; + + // Begin the paint process on the image. + if (!d->paintEngine()->begin(d->pageImage)) + return false; + + // Clear the first page to all-white. + clearPage(); + + // Clear the print buffer and output the image header. + d->buffer.clear(); + d->writeG3FaxHeader(); + + // The print engine is currently active. + d->printerState = QPrinter::Active; + return true; +} + +bool QtopiaPrintEngine::end() +{ + Q_D(QtopiaPrintEngine); + + d->paintEngine()->end(); + + // Flush the last page. + flushPage(); + + // Output the fax data to a file (TODO: send to the print queuing daemon). + QString filename; + if ( !d->outputFileName.isEmpty() ) + filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/Documents/") + d->outputFileName; + else + filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/tmp/qwsfax.tiff"); + + setProperty(QPrintEngine::PPK_OutputFileName, filename); + QFile file( filename ); + if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { + qDebug( "Failed to open %s for printer output", + filename.toLatin1().constData() ); + } else { + file.write( d->buffer.data() ); + file.close(); + } + + // Free up the memory for the image buffer. + d->buffer.clear(); + + // Finalize the print job. + d->printerState = QPrinter::Idle; + + // call qcop service + QMap<QString, QVariant> map; + for ( int x = 0; x <= QPrintEngine::PPK_Duplex; x++ ) + map.insert( QString::number(x), property((QPrintEngine::PrintEnginePropertyKey)(x))); + QVariant variant(map); + + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + out << variant; + QCopChannel::send(QLatin1String("QPE/Service/Print"), QLatin1String("print(QVariant)"), data); + + return true; +} + +QPaintEngine *QtopiaPrintEngine::paintEngine() const +{ + return const_cast<QtopiaPrintEnginePrivate *>(d_func())->paintEngine(); +} + +void QtopiaPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + Q_D(QtopiaPrintEngine); + Q_ASSERT(d->printerState == QPrinter::Active); + d->paintEngine()->drawPixmap(r, pm, sr); +} + +void QtopiaPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti) +{ + Q_D(QtopiaPrintEngine); + Q_ASSERT(d->printerState == QPrinter::Active); + d->paintEngine()->drawTextItem(p, ti); +} + +void QtopiaPrintEngine::updateState(const QPaintEngineState &state) +{ + Q_D(QtopiaPrintEngine); + d->paintEngine()->updateState(state); +} + +QRect QtopiaPrintEngine::paperRect() const +{ + QSizeF s = qt_paperSizeToQSizeF(d_func()->paperSize); + s.rwidth() = MM(s.width()); + s.rheight() = MM(s.height()); + int w = qRound(s.width()*d_func()->resolution/72.); + int h = qRound(s.height()*d_func()->resolution/72.); + if (d_func()->orientation == QPrinter::Portrait) + return QRect(0, 0, w, h); + else + return QRect(0, 0, h, w); +} + +QRect QtopiaPrintEngine::pageRect() const +{ + QRect r = paperRect(); + if (d_func()->fullPage) + return r; + // would be nice to get better margins than this. + return QRect(d_func()->resolution/3, d_func()->resolution/3, r.width()-2*d_func()->resolution/3, r.height()-2*d_func()->resolution/3); +} + +bool QtopiaPrintEngine::newPage() +{ + flushPage(); + clearPage(); + ++(d_func()->pageNumber); + return true; +} + +bool QtopiaPrintEngine::abort() +{ + return false; +} + +QPrinter::PrinterState QtopiaPrintEngine::printerState() const +{ + return d_func()->printerState; +} + +int QtopiaPrintEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const +{ + int val; + QRect r = d_func()->fullPage ? paperRect() : pageRect(); + switch (metricType) { + case QPaintDevice::PdmWidth: + val = r.width(); + break; + case QPaintDevice::PdmHeight: + val = r.height(); + break; + case QPaintDevice::PdmDpiX: + val = d_func()->resolution; + break; + case QPaintDevice::PdmDpiY: + val = d_func()->resolution; + break; + case QPaintDevice::PdmPhysicalDpiX: + case QPaintDevice::PdmPhysicalDpiY: + val = QT_QWS_PRINTER_DEFAULT_DPI; + break; + case QPaintDevice::PdmWidthMM: + val = qRound(r.width()*25.4/d_func()->resolution); + break; + case QPaintDevice::PdmHeightMM: + val = qRound(r.height()*25.4/d_func()->resolution); + break; + case QPaintDevice::PdmNumColors: + val = 2; + break; + case QPaintDevice::PdmDepth: + val = 1; + break; + default: + qWarning("QtopiaPrintEngine::metric: Invalid metric command"); + return 0; + } + return val; +} + +QVariant QtopiaPrintEngine::property(PrintEnginePropertyKey key) const +{ + Q_D(const QtopiaPrintEngine); + QVariant ret; + + switch (key) { + case PPK_CollateCopies: + ret = d->collateCopies; + break; + case PPK_ColorMode: + ret = d->colorMode; + break; + case PPK_Creator: + ret = d->creator; + break; + case PPK_DocumentName: + ret = d->docName; + break; + case PPK_FullPage: + ret = d->fullPage; + break; + case PPK_CopyCount: // fallthrough + case PPK_NumberOfCopies: + ret = d->numCopies; + break; + case PPK_SupportsMultipleCopies: + ret = false; + break; + case PPK_Orientation: + ret = d->orientation; + break; + case PPK_OutputFileName: + ret = d->outputFileName; + break; + case PPK_PageOrder: + ret = d->pageOrder; + break; + case PPK_PageRect: + ret = pageRect(); + break; + case PPK_PaperSize: + ret = d->paperSize; + break; + case PPK_PaperRect: + ret = paperRect(); + break; + case PPK_PaperSource: + ret = d->paperSource; + break; + case PPK_PrinterName: + ret = d->printerName; + break; + case PPK_PrinterProgram: + ret = d->printProgram; + break; + case PPK_Resolution: + ret = d->resolution; + break; + case PPK_SupportedResolutions: + ret = QList<QVariant>() << QT_QWS_PRINTER_DEFAULT_DPI; + break; + default: + break; + } + return ret; +} + +void QtopiaPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value) +{ + Q_D(QtopiaPrintEngine); + switch (key) { + case PPK_CollateCopies: + d->collateCopies = value.toBool(); + break; + case PPK_ColorMode: + d->colorMode = QPrinter::ColorMode(value.toInt()); + break; + case PPK_Creator: + d->creator = value.toString(); + break; + case PPK_DocumentName: + d->docName = value.toString(); + break; + case PPK_FullPage: + d->fullPage = value.toBool(); + break; + case PPK_CopyCount: // fallthrough + case PPK_NumberOfCopies: + d->numCopies = value.toInt(); + break; + case PPK_Orientation: + d->orientation = QPrinter::Orientation(value.toInt()); + break; + case PPK_OutputFileName: + d->outputFileName = value.toString(); + break; + case PPK_PageOrder: + d->pageOrder = QPrinter::PageOrder(value.toInt()); + break; + case PPK_PaperSize: + d->paperSize = QPrinter::PaperSize(value.toInt()); + break; + case PPK_PaperSource: + d->paperSource = QPrinter::PaperSource(value.toInt()); + case PPK_PrinterName: + d->printerName = value.toString(); + break; + case PPK_PrinterProgram: + d->printProgram = value.toString(); + break; + case PPK_Resolution: + d->resolution = value.toInt(); + break; + default: + break; + } +} + +void QtopiaPrintEngine::clearPage() +{ + d_func()->pageImage->fill(QColor(255, 255, 255).rgb()); +} + +void QtopiaPrintEngine::flushPage() +{ + d_func()->writeG3FaxPage(); +} + +QtopiaPrintEnginePrivate::~QtopiaPrintEnginePrivate() +{ + if ( pageImage ) + delete pageImage; +} + +void QtopiaPrintEnginePrivate::initialize() +{ + _paintEngine = 0; +} + +QPaintEngine *QtopiaPrintEnginePrivate::paintEngine() +{ + if (!_paintEngine) + _paintEngine = new QRasterPaintEngine(pageImage); + return _paintEngine; +} + +void QtopiaPrintEnginePrivate::writeG3FaxHeader() +{ + // Write the TIFF file magic number (little-endian TIFF). + buffer.append( (char)'I' ); + buffer.append( (char)'I' ); + buffer.append( (char)42 ); + buffer.append( (char)0 ); + + // Leave a place-holder for the IFD offset of the first page. + ifdPatch = buffer.size(); + buffer.append( (int)0 ); +} + +// Tag values, from RFC 2301. +#define TIFF_IFD_NEW_SUB_FILE_TYPE 254 +#define TIFF_IFD_IMAGE_WIDTH 256 +#define TIFF_IFD_IMAGE_LENGTH 257 +#define TIFF_IFD_BITS_PER_SAMPLE 258 +#define TIFF_IFD_COMPRESSION 259 +#define TIFF_IFD_PHOTOMETRIC_INTERP 262 +#define TIFF_IFD_FILL_ORDER 266 +#define TIFF_IFD_STRIP_OFFSETS 273 +#define TIFF_IFD_ORIENTATION 274 +#define TIFF_IFD_SAMPLES_PER_PIXEL 277 +#define TIFF_IFD_ROWS_PER_STRIP 278 +#define TIFF_IFD_STRIP_BYTE_COUNTS 279 +#define TIFF_IFD_X_RESOLUTION 282 +#define TIFF_IFD_Y_RESOLUTION 283 +#define TIFF_IFD_PLANAR_CONFIG 284 +#define TIFF_IFD_T4_OPTIONS 292 +#define TIFF_IFD_RESOLUTION_UNIT 296 +#define TIFF_IFD_PAGE_NUMBER 297 +#define TIFF_IFD_CLEAN_FAX_DATA 327 + +// IFD type values. +#define TIFF_TYPE_SHORT 3 +#define TIFF_TYPE_LONG 4 +#define TIFF_TYPE_RATIONAL 5 + +// Construct a SHORT pair from two values. +#define TIFF_SHORT_PAIR(a,b) (((a) & 0xFFFF) | ((b) << 16)) + +// Width of a FAX page in pixels, in the baseline specification from RFC 2301. +// This must be hard-wired, as per the RFC. We truncate any pixels that +// are beyond this limit, or pad lines to reach this limit. +#define TIFF_FAX_WIDTH 1728 + +void QtopiaPrintEnginePrivate::writeG3FaxPage() +{ + // Pad the image file to a word boundary, just in case. + buffer.pad(); + + // Back-patch the IFD link for the previous page. + buffer.patch( ifdPatch, buffer.size() ); + + // Output the contents of the IFD for this page (these must be + // in ascending order of tag value). + buffer.append( (short)19 ); // Number of IFD entries. + writeG3IFDEntry( TIFF_IFD_NEW_SUB_FILE_TYPE, TIFF_TYPE_LONG, 1, 2 ); + writeG3IFDEntry( TIFF_IFD_IMAGE_WIDTH, TIFF_TYPE_LONG, 1, TIFF_FAX_WIDTH ); + writeG3IFDEntry + ( TIFF_IFD_IMAGE_LENGTH, TIFF_TYPE_LONG, 1, pageImage->height() ); + writeG3IFDEntry( TIFF_IFD_BITS_PER_SAMPLE, TIFF_TYPE_SHORT, 1, 1 ); + writeG3IFDEntry( TIFF_IFD_COMPRESSION, TIFF_TYPE_SHORT, 1, 3 ); + writeG3IFDEntry( TIFF_IFD_PHOTOMETRIC_INTERP, TIFF_TYPE_SHORT, 1, 0 ); + writeG3IFDEntry( TIFF_IFD_FILL_ORDER, TIFF_TYPE_SHORT, 1, 1 ); + int stripOffsets = + writeG3IFDEntry( TIFF_IFD_STRIP_OFFSETS, TIFF_TYPE_LONG, 1, 0 ); + writeG3IFDEntry( TIFF_IFD_ORIENTATION, TIFF_TYPE_SHORT, 1, 1 ); + writeG3IFDEntry( TIFF_IFD_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, 1 ); + writeG3IFDEntry + ( TIFF_IFD_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, pageImage->height() ); + int stripBytes = writeG3IFDEntry + ( TIFF_IFD_STRIP_BYTE_COUNTS, TIFF_TYPE_LONG, 1, 0 ); + int xres = + writeG3IFDEntry( TIFF_IFD_X_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 ); + int yres = + writeG3IFDEntry( TIFF_IFD_Y_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 ); + writeG3IFDEntry( TIFF_IFD_PLANAR_CONFIG, TIFF_TYPE_SHORT, 1, 1 ); + writeG3IFDEntry( TIFF_IFD_T4_OPTIONS, TIFF_TYPE_LONG, 1, 2 ); + writeG3IFDEntry( TIFF_IFD_RESOLUTION_UNIT, TIFF_TYPE_SHORT, 1, 2 ); + writeG3IFDEntry( TIFF_IFD_PAGE_NUMBER, TIFF_TYPE_SHORT, 2, + TIFF_SHORT_PAIR( pageNumber, 0 ) ); + writeG3IFDEntry( TIFF_IFD_CLEAN_FAX_DATA, TIFF_TYPE_SHORT, 1, 0 ); + + // Leave a place-holder for the IFD offset of the next page. + ifdPatch = buffer.size(); + buffer.append( (int)0 ); + + // Output the X and Y resolutions, as rational values (usually 200/1). + buffer.patch( xres, buffer.size() ); + buffer.append( (int)resolution ); + buffer.append( (int)1 ); + buffer.patch( yres, buffer.size() ); + buffer.append( (int)resolution ); + buffer.append( (int)1 ); + + // We are now at the start of the image data - set the strip offset. + int start = buffer.size(); + buffer.patch( stripOffsets, start ); + + // Output the image data. + int width = pageImage->width(); + QImage::Format imageFormat = pageImage->format(); + for ( int y = 0; y < pageImage->height(); ++y ) { + unsigned char *scan = pageImage->scanLine(y); + int prev, pixel, len; + writeG3EOL(); + prev = 0; + len = 0; + + uint currentColor = qRgb(255, 255, 255); // start with white + + for ( int x = 0; x < width && x < TIFF_FAX_WIDTH; ++x ) { + if ( imageFormat == QImage::Format_RGB32 ) { + // read color of the current pixel + uint *p = (uint *)scan + x; + + if ( *p == currentColor ) { // if it is the same color + len++; // imcrement length + } else { // otherwise write color into the buffer + if ( len > 0 ) { + if ( currentColor == qRgb(0, 0, 0) ) + writeG3BlackRun( len ); + else + writeG3WhiteRun( len ); + } + // initialise length and color; + len = 1; + currentColor = *p; + } + } else if ( imageFormat == QImage::Format_Mono ) { + pixel = ((scan[x >> 3] & (1 << (x & 7))) != 0); + if ( pixel != prev ) { + if ( prev ) { + writeG3BlackRun( len ); + } else { + writeG3WhiteRun( len ); + } + prev = pixel; + len = 1; + } else { + ++len; + } + } + } + + if ( imageFormat == QImage::Format_RGB32 ) { + // Output the last run on the line, and pad to TIFF_FAX_WIDTH. + if ( len != 0 ) { + if ( currentColor == qRgb(0, 0, 0) ) + writeG3BlackRun( len ); + else + writeG3WhiteRun( len ); + } + if ( width < TIFF_FAX_WIDTH ) + writeG3WhiteRun( TIFF_FAX_WIDTH - width ); + } else if ( imageFormat == QImage::Format_Mono ) { + if ( len != 0 ) { + if ( prev ) { + writeG3BlackRun( len ); + if ( width < TIFF_FAX_WIDTH ) { + writeG3WhiteRun( TIFF_FAX_WIDTH - width ); + } + } else { + if ( width < TIFF_FAX_WIDTH ) { + writeG3WhiteRun( len + ( TIFF_FAX_WIDTH - width ) ); + } else { + writeG3WhiteRun( len ); + } + } + } + } + } + + // Flush the last partial byte, which is padded with zero fill bits. + if ( partialBits > 0 ) { + buffer.append( (char)( partialByte << ( 8 - partialBits ) ) ); + partialByte = 0; + partialBits = 0; + } + + // end of page add six EOLs + for ( int i = 0; i < 6; i++ ) + writeG3EOL(); + + // Update the byte count for the image data strip. + buffer.patch( stripBytes, buffer.size() - start ); +} + +int QtopiaPrintEnginePrivate::writeG3IFDEntry + ( int tag, int type, int count, int value ) +{ + buffer.append( (short)tag ); + buffer.append( (short)type ); + buffer.append( count ); + buffer.append( value ); + return buffer.size() - 4; // Offset of the value for back-patching. +} + +void QtopiaPrintEnginePrivate::writeG3Code( int code, int bits ) +{ + partialByte = ( ( partialByte << bits ) | code ); + partialBits += bits; + while ( partialBits >= 8 ) { + partialBits -= 8; + buffer.append( (char)( partialByte >> partialBits ) ); + } +} + +void QtopiaPrintEnginePrivate::writeG3WhiteRun( int len ) +{ + static struct { + unsigned short code; + unsigned short bits; + } whiteCodes[64 + 27] = { + {0x0035, 8}, // 0 + {0x0007, 6}, + {0x0007, 4}, + {0x0008, 4}, + {0x000B, 4}, + {0x000C, 4}, + {0x000E, 4}, + {0x000F, 4}, + {0x0013, 5}, // 8 + {0x0014, 5}, + {0x0007, 5}, + {0x0008, 5}, + {0x0008, 6}, + {0x0003, 6}, + {0x0034, 6}, + {0x0035, 6}, + {0x002A, 6}, // 16 + {0x002B, 6}, + {0x0027, 7}, + {0x000C, 7}, + {0x0008, 7}, + {0x0017, 7}, + {0x0003, 7}, + {0x0004, 7}, + {0x0028, 7}, // 24 + {0x002B, 7}, + {0x0013, 7}, + {0x0024, 7}, + {0x0018, 7}, + {0x0002, 8}, + {0x0003, 8}, + {0x001A, 8}, + {0x001B, 8}, // 32 + {0x0012, 8}, + {0x0013, 8}, + {0x0014, 8}, + {0x0015, 8}, + {0x0016, 8}, + {0x0017, 8}, + {0x0028, 8}, + {0x0029, 8}, // 40 + {0x002A, 8}, + {0x002B, 8}, + {0x002C, 8}, + {0x002D, 8}, + {0x0004, 8}, + {0x0005, 8}, + {0x000A, 8}, + {0x000B, 8}, // 48 + {0x0052, 8}, + {0x0053, 8}, + {0x0054, 8}, + {0x0055, 8}, + {0x0024, 8}, + {0x0025, 8}, + {0x0058, 8}, + {0x0059, 8}, // 56 + {0x005A, 8}, + {0x005B, 8}, + {0x004A, 8}, + {0x004B, 8}, + {0x0032, 8}, + {0x0033, 8}, + {0x0034, 8}, + {0x001B, 5}, // Make up codes: 64 + {0x0012, 5}, // 128 + {0x0017, 6}, // 192 + {0x0037, 7}, // 256 + {0x0036, 8}, // 320 + {0x0037, 8}, // 384 + {0x0064, 8}, // 448 + {0x0065, 8}, // 512 + {0x0068, 8}, // 576 + {0x0067, 8}, // 640 + {0x00CC, 9}, // 704 + {0x00CD, 9}, // 768 + {0x00D2, 9}, // 832 + {0x00D3, 9}, // 896 + {0x00D4, 9}, // 960 + {0x00D5, 9}, // 1024 + {0x00D6, 9}, // 1088 + {0x00D7, 9}, // 1152 + {0x00D8, 9}, // 1216 + {0x00D9, 9}, // 1280 + {0x00DA, 9}, // 1344 + {0x00DB, 9}, // 1408 + {0x0098, 9}, // 1472 + {0x0099, 9}, // 1536 + {0x009A, 9}, // 1600 + {0x0018, 6}, // 1664 + {0x009B, 9}, // 1728 + }; + if ( len >= 64 ) { + int index = 63 + (len >> 6); + writeG3Code( whiteCodes[index].code, whiteCodes[index].bits ); + len &= 63; + } + writeG3Code( whiteCodes[len].code, whiteCodes[len].bits ); +} + +void QtopiaPrintEnginePrivate::writeG3BlackRun( int len ) +{ + static struct { + unsigned short code; + unsigned short bits; + } blackCodes[64 + 27] = { + {0x0037, 10}, // 0 + {0x0002, 3}, + {0x0003, 2}, + {0x0002, 2}, + {0x0003, 3}, + {0x0003, 4}, + {0x0002, 4}, + {0x0003, 5}, + {0x0005, 6}, // 8 + {0x0004, 6}, + {0x0004, 7}, + {0x0005, 7}, + {0x0007, 7}, + {0x0004, 8}, + {0x0007, 8}, + {0x0018, 9}, + {0x0017, 10}, // 16 + {0x0018, 10}, + {0x0008, 10}, + {0x0067, 11}, + {0x0068, 11}, + {0x006C, 11}, + {0x0037, 11}, + {0x0028, 11}, + {0x0017, 11}, // 24 + {0x0018, 11}, + {0x00CA, 12}, + {0x00CB, 12}, + {0x00CC, 12}, + {0x00CD, 12}, + {0x0068, 12}, + {0x0069, 12}, + {0x006A, 12}, // 32 + {0x006B, 12}, + {0x00D2, 12}, + {0x00D3, 12}, + {0x00D4, 12}, + {0x00D5, 12}, + {0x00D6, 12}, + {0x00D7, 12}, + {0x006C, 12}, // 40 + {0x006D, 12}, + {0x00DA, 12}, + {0x00DB, 12}, + {0x0054, 12}, + {0x0055, 12}, + {0x0056, 12}, + {0x0057, 12}, + {0x0064, 12}, // 48 + {0x0065, 12}, + {0x0052, 12}, + {0x0053, 12}, + {0x0024, 12}, + {0x0037, 12}, + {0x0038, 12}, + {0x0027, 12}, + {0x0028, 12}, // 56 + {0x0058, 12}, + {0x0059, 12}, + {0x002B, 12}, + {0x002C, 12}, + {0x005A, 12}, + {0x0066, 12}, + {0x0067, 12}, + {0x000F, 10}, // Make up codes: 64 + {0x00C8, 12}, // 128 + {0x00C9, 12}, // 192 + {0x005B, 12}, // 256 + {0x0033, 12}, // 320 + {0x0034, 12}, // 384 + {0x0035, 12}, // 448 + {0x006C, 13}, // 512 + {0x006D, 13}, // 576 + {0x004A, 13}, // 640 + {0x004B, 13}, // 704 + {0x004C, 13}, // 768 + {0x004D, 13}, // 832 + {0x0072, 13}, // 896 + {0x0073, 13}, // 960 + {0x0074, 13}, // 1024 + {0x0075, 13}, // 1088 + {0x0076, 13}, // 1152 + {0x0077, 13}, // 1216 + {0x0052, 13}, // 1280 + {0x0053, 13}, // 1344 + {0x0054, 13}, // 1408 + {0x0055, 13}, // 1472 + {0x005A, 13}, // 1536 + {0x005B, 13}, // 1600 + {0x0064, 13}, // 1664 + {0x0065, 13}, // 1728 + }; + if ( len >= 64 ) { + int index = 63 + (len >> 6); + writeG3Code( blackCodes[index].code, blackCodes[index].bits ); + len &= 63; + } + writeG3Code( blackCodes[len].code, blackCodes[len].bits ); +} + +void QtopiaPrintEnginePrivate::writeG3EOL() +{ + int bitToPad; + if ( partialBits <= 4 ) { + bitToPad = 4 - partialBits; + } else { + bitToPad = 8 - partialBits + 4; + } + + partialByte = ((partialByte << (bitToPad + 12)) | 0x0001); + partialBits += bitToPad + 12; + + while ( partialBits >= 8 ) { + partialBits -= 8; + buffer.append( (char)(partialByte >> partialBits ) ); + } +// writeG3Code( 0x0001, 12 ); +} + +void QtopiaPrintBuffer::append( short value ) +{ + if ( _bigEndian ) { + _data.append( (char)(value >> 8) ); + _data.append( (char)value ); + } else { + _data.append( (char)value ); + _data.append( (char)(value >> 8) ); + } +} + +void QtopiaPrintBuffer::append( int value ) +{ + if ( _bigEndian ) { + _data.append( (char)(value >> 24) ); + _data.append( (char)(value >> 16) ); + _data.append( (char)(value >> 8) ); + _data.append( (char)value ); + } else { + _data.append( (char)value ); + _data.append( (char)(value >> 8) ); + _data.append( (char)(value >> 16) ); + _data.append( (char)(value >> 24) ); + } +} + +void QtopiaPrintBuffer::patch( int posn, int value ) +{ + if ( _bigEndian ) { + _data[posn] = (char)(value >> 24); + _data[posn + 1] = (char)(value >> 16); + _data[posn + 2] = (char)(value >> 8); + _data[posn + 3] = (char)value; + } else { + _data[posn] = (char)value; + _data[posn + 1] = (char)(value >> 8); + _data[posn + 2] = (char)(value >> 16); + _data[posn + 3] = (char)(value >> 24); + } +} + +void QtopiaPrintBuffer::pad() +{ + while ( ( _data.size() % 4 ) != 0 ) + _data.append( (char)0 ); +} + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER diff --git a/src/gui/painting/qprintengine_wayland_p.h b/src/gui/painting/qprintengine_wayland_p.h new file mode 100644 index 0000000000..e08fbcd9e1 --- /dev/null +++ b/src/gui/painting/qprintengine_wayland_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QPRINTENGINE_QWS_P_H +#define QPRINTENGINE_QWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtGui/qprinter.h" + +#ifndef QT_NO_PRINTER + +#include "QtGui/qprintengine.h" +#include "QtCore/qbytearray.h" +#include "private/qpaintengine_p.h" + +QT_BEGIN_NAMESPACE + +class QtopiaPrintEnginePrivate; +class QRasterPaintEngine; +class QPrinterPrivate; +class QImage; + +class QtopiaPrintEngine : public QPaintEngine, public QPrintEngine +{ + Q_DECLARE_PRIVATE(QtopiaPrintEngine) +public: + QtopiaPrintEngine(QPrinter::PrinterMode mode); + + // override QWSPaintEngine + bool begin(QPaintDevice *dev); + bool end(); + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + void drawTextItem(const QPointF &p, const QTextItem &ti); + QPaintEngine::Type type() const { return QPaintEngine::X11; } + + QPaintEngine *paintEngine() const; + + void updateState(const QPaintEngineState &state); + + QRect paperRect() const; + QRect pageRect() const; + + bool newPage(); + bool abort(); + + QPrinter::PrinterState printerState() const; + + int metric(QPaintDevice::PaintDeviceMetric metricType) const; + + QVariant property(PrintEnginePropertyKey key) const; + void setProperty(PrintEnginePropertyKey key, const QVariant &value); + +private: + friend class QPrintDialog; + friend class QPageSetupDialog; + + void clearPage(); + void flushPage(); +}; + +class QtopiaPrintBuffer +{ +public: + QtopiaPrintBuffer( bool bigEndian=FALSE ) { _bigEndian = bigEndian; } + ~QtopiaPrintBuffer() {} + + const QByteArray& data() const { return _data; } + + int size() const { return _data.size(); } + + void clear() { _data.clear(); } + + void append( char value ) { _data.append( value ); } + void append( short value ); + void append( int value ); + void append( const QByteArray& array ) { _data.append( array ); } + + void patch( int posn, int value ); + + void pad(); + +private: + QByteArray _data; + bool _bigEndian; +}; + +#define QT_QWS_PRINTER_DEFAULT_DPI 200 + +class QtopiaPrintEnginePrivate : public QPaintEnginePrivate +{ + Q_DECLARE_PUBLIC(QtopiaPrintEngine) +public: + QtopiaPrintEnginePrivate(QPrinter::PrinterMode m) : + mode(m), + printerState(QPrinter::Idle), + orientation(QPrinter::Portrait), + paperSize(QPrinter::A4), + pageOrder(QPrinter::FirstPageFirst), + colorMode(QPrinter::GrayScale), + paperSource(QPrinter::OnlyOne), + resolution(QT_QWS_PRINTER_DEFAULT_DPI), + _paintEngine(0), + numCopies(1), + outputToFile(false), + fullPage(false), + collateCopies(false), + pageNumber(0), + pageImage(0), + partialByte(0), + partialBits(0) + { + } + ~QtopiaPrintEnginePrivate(); + + void initialize(); + QPaintEngine *paintEngine(); + + QPrinter::PrinterMode mode; + + QString printerName; + QString outputFileName; + QString printProgram; + QString docName; + QString creator; + + QPrinter::PrinterState printerState; + + QPrinter::Orientation orientation; + QPrinter::PaperSize paperSize; + QPrinter::PageOrder pageOrder; + QPrinter::ColorMode colorMode; + QPrinter::PaperSource paperSource; + + int resolution; + QPaintEngine *_paintEngine; + int numCopies; + + bool outputToFile; + bool fullPage; + bool collateCopies; + + int pageNumber; + + QImage *pageImage; + + QtopiaPrintBuffer buffer; + + // Definitions that are only relevant to G3FAX output. + int ifdPatch; + int partialByte; + int partialBits; + void writeG3FaxHeader(); + void writeG3FaxPage(); + int writeG3IFDEntry( int tag, int type, int count, int value ); + void writeG3Code( int code, int bits ); + void writeG3WhiteRun( int len ); + void writeG3BlackRun( int len ); + void writeG3EOL(); +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER + +#endif // QPRINTENGINE_QWS_P_H diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index bc4da285bb..8c74732788 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -59,7 +59,7 @@ QT_MODULE(Gui) template <class T> class QVector; class QVariant; -#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) || defined(Q_WS_WAYLAND) struct QRegionPrivate; #endif @@ -203,7 +203,7 @@ private: #elif defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) mutable RgnHandle unused; // Here for binary compatability reasons. ### Qt 5 remove. #endif -#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) || defined(Q_WS_WAYLAND) QRegionPrivate *qt_rgn; #endif }; diff --git a/src/gui/painting/qregion_wayland.cpp b/src/gui/painting/qregion_wayland.cpp new file mode 100644 index 0000000000..4299c4689c --- /dev/null +++ b/src/gui/painting/qregion_wayland.cpp @@ -0,0 +1,3183 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +// XXX - add appropriate friendship relationships +#define private public +#include "qregion.h" +#undef private +#include "qpainterpath.h" +#include "qpolygon.h" +#include "qbuffer.h" +#include "qimage.h" +#include <qdebug.h> +#include "qbitmap.h" +#include <stdlib.h> +#include <qatomic.h> +#include <qsemaphore.h> + +QT_BEGIN_NAMESPACE + +class QFastMutex +{ + QAtomicInt contenders; + QSemaphore semaphore; +public: + inline QFastMutex() + : contenders(0), semaphore(0) + { } + inline void lock() + { + if (contenders.fetchAndAddAcquire(1) != 0) { + semaphore.acquire(); + contenders.deref(); + } + } + inline bool tryLock() + { + return contenders.testAndSetAcquire(0, 1); + } + inline void unlock() + { + if (!contenders.testAndSetRelease(1, 0)) + semaphore.release(); + } +}; + + +/* + * 1 if r1 contains r2 + * 0 if r1 does not completely contain r2 + */ +#define CONTAINSCHECK(r1, r2) \ + ((r2).left() >= (r1).left() && (r2).right() <= (r1).right() && \ + (r2).top() >= (r1).top() && (r2).bottom() <= (r1).bottom()) + +/* + * clip region + */ +struct QRegionPrivate : public QRegion::QRegionData { + enum { Single, Vector } mode; + int numRects; + QVector<QRect> rects; + QRect single; + QRect extents; + QRect innerRect; + union { + int innerArea; + QRegionPrivate *next; + }; + + inline void vector() + { + if(mode != Vector && numRects) { + if(rects.size() < 1) rects.resize(1); + rects[0] = single; + } + mode = Vector; + } + + inline QRegionPrivate() : mode(Single), numRects(0), innerArea(-1) {} + inline QRegionPrivate(const QRect &r) : mode(Single) { + numRects = 1; +// rects[0] = r; + single = r; + extents = r; + innerRect = r; + innerArea = r.width() * r.height(); + } + + inline QRegionPrivate(const QRegionPrivate &r) { + mode = r.mode; + rects = r.rects; + single = r.single; + numRects = r.numRects; + extents = r.extents; + innerRect = r.innerRect; + innerArea = r.innerArea; + } + + inline QRegionPrivate &operator=(const QRegionPrivate &r) { + mode = r.mode; + rects = r.rects; + single = r.single; + numRects = r.numRects; + extents = r.extents; + innerRect = r.innerRect; + innerArea = r.innerArea; + return *this; + } + + /* + * Returns true if r is guaranteed to be fully contained in this region. + * A false return value does not guarantee the opposite. + */ + inline bool contains(const QRegionPrivate &r) const { + const QRect &r1 = innerRect; + const QRect &r2 = r.extents; + return CONTAINSCHECK(r1, r2); + } + + inline void updateInnerRect(const QRect &rect) { + const int area = rect.width() * rect.height(); + if (area > innerArea) { + innerArea = area; + innerRect = rect; + } + } + + void append(const QRegionPrivate *r); + void prepend(const QRegionPrivate *r); + inline bool canAppend(const QRegionPrivate *r) const; + inline bool canPrepend(const QRegionPrivate *r) const; +}; + +static QRegionPrivate *qt_nextRegionPtr = 0; +static QFastMutex qt_nextRegionLock; + +static QRegionPrivate *qt_allocRegionMemory() +{ + QRegionPrivate *rv = 0; + qt_nextRegionLock.lock(); + + if(qt_nextRegionPtr) { + rv = qt_nextRegionPtr; + qt_nextRegionPtr = rv->next; + } else { + qt_nextRegionPtr = + (QRegionPrivate *)malloc(256 * sizeof(QRegionPrivate)); + for(int ii = 0; ii < 256; ++ii) { + if(ii == 255) { + qt_nextRegionPtr[ii].next = 0; + } else { + qt_nextRegionPtr[ii].next = &qt_nextRegionPtr[ii + 1]; + } + } + + rv = qt_nextRegionPtr; + qt_nextRegionPtr = rv->next; + } + + qt_nextRegionLock.unlock(); + return rv; +} + +static void qt_freeRegionMemory(QRegionPrivate *rp) +{ + qt_nextRegionLock.lock(); + rp->next = qt_nextRegionPtr; + qt_nextRegionPtr = rp; + qt_nextRegionLock.unlock(); +} + +static QRegionPrivate *qt_allocRegion() +{ + QRegionPrivate *mem = qt_allocRegionMemory(); + return new (mem) QRegionPrivate; +} + +static QRegionPrivate *qt_allocRegion(const QRect &r) +{ + QRegionPrivate *mem = qt_allocRegionMemory(); + return new (mem) QRegionPrivate(r); +} + +static QRegionPrivate *qt_allocRegion(const QRegionPrivate &r) +{ + QRegionPrivate *mem = qt_allocRegionMemory(); + return new (mem) QRegionPrivate(r); +} + +void qt_freeRegion(QRegionPrivate *rp) +{ + rp->~QRegionPrivate(); + qt_freeRegionMemory(rp); +// delete rp; +} + +static inline bool isEmptyHelper(const QRegionPrivate *preg) +{ + return !preg || preg->numRects == 0; +} + +void QRegionPrivate::append(const QRegionPrivate *r) +{ + Q_ASSERT(!isEmptyHelper(r)); + + vector(); + QRect *destRect = rects.data() + numRects; + const QRect *srcRect = (r->mode==Vector)?r->rects.constData():&r->single; + int numAppend = r->numRects; + + // test for merge in x direction + { + const QRect *rFirst = srcRect; + QRect *myLast = rects.data() + (numRects - 1); + if (rFirst->top() == myLast->top() + && rFirst->height() == myLast->height() + && rFirst->left() == (myLast->right() + 1)) + { + myLast->setWidth(myLast->width() + rFirst->width()); + updateInnerRect(*myLast); + ++srcRect; + --numAppend; + } + } + + // append rectangles + const int newNumRects = numRects + numAppend; + if (newNumRects > rects.size()) { + rects.resize(newNumRects); + destRect = rects.data() + numRects; + } + memcpy(destRect, srcRect, numAppend * sizeof(QRect)); + + // update inner rectangle + if (innerArea < r->innerArea) { + innerArea = r->innerArea; + innerRect = r->innerRect; + } + + // update extents + destRect = &extents; + srcRect = &r->extents; + extents.setCoords(qMin(destRect->left(), srcRect->left()), + qMin(destRect->top(), srcRect->top()), + qMax(destRect->right(), srcRect->right()), + qMax(destRect->bottom(), srcRect->bottom())); + + numRects = newNumRects; +} + +void QRegionPrivate::prepend(const QRegionPrivate *r) +{ +#if 1 + Q_UNUSED(r); +#else + // XXX ak: does not respect vectorization of region + + Q_ASSERT(!isEmpty(r)); + + // move existing rectangles + memmove(rects.data() + r->numRects, rects.constData(), + numRects * sizeof(QRect)); + + // prepend new rectangles + memcpy(rects.data(), r->rects.constData(), r->numRects * sizeof(QRect)); + + // update inner rectangle + if (innerArea < r->innerArea) { + innerArea = r->innerArea; + innerRect = r->innerRect; + } + + // update extents + destRect = &extents; + srcRect = &r->extents; + extents.setCoords(qMin(destRect->left(), srcRect->left()), + qMin(destRect->top(), srcRect->top()), + qMax(destRect->right(), srcRect->right()), + qMax(destRect->bottom(), srcRect->bottom())); + + numRects = newNumRects; +#endif +} + +bool QRegionPrivate::canAppend(const QRegionPrivate *r) const +{ + Q_ASSERT(!isEmptyHelper(r)); + + const QRect *rFirst = (r->mode==Vector)?r->rects.constData():&r->single; + const QRect *myLast = (mode==Vector)?(rects.constData() + (numRects - 1)):&single; + // XXX: possible improvements: + // - nFirst->top() == myLast->bottom() + 1, must possibly merge bands + if (rFirst->top() > (myLast->bottom() + 1) + || (rFirst->top() == myLast->top() + && rFirst->height() == myLast->height() + && rFirst->left() > myLast->right())) + { + return true; + } + + return false; +} + +bool QRegionPrivate::canPrepend(const QRegionPrivate *r) const +{ +#if 1 + Q_UNUSED(r); + return false; +#else + return r->canAppend(this); +#endif +} + +#if defined(Q_WS_X11) +QT_BEGIN_INCLUDE_NAMESPACE +# include "qregion_x11.cpp" +QT_END_INCLUDE_NAMESPACE +#elif defined(Q_WS_MAC) +QT_BEGIN_INCLUDE_NAMESPACE +# include "qregion_mac.cpp" +QT_END_INCLUDE_NAMESPACE +#elif defined(Q_WS_QWS) +static QRegionPrivate qrp; +QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), &qrp}; +#endif + +typedef void (*OverlapFunc)(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End, + register const QRect *r2, const QRect *r2End, register int y1, register int y2); +typedef void (*NonOverlapFunc)(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd, + register int y1, register int y2); + +static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2); +static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest); +static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2, + OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func, + NonOverlapFunc nonOverlap2Func); + +#define RectangleOut 0 +#define RectangleIn 1 +#define RectanglePart 2 +#define EvenOddRule 0 +#define WindingRule 1 + +// START OF region.h extract +/* $XConsortium: region.h,v 11.14 94/04/17 20:22:20 rws Exp $ */ +/************************************************************************ + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ + +#ifndef _XREGION_H +#define _XREGION_H + +QT_BEGIN_INCLUDE_NAMESPACE +#include <limits.h> +QT_END_INCLUDE_NAMESPACE + +/* 1 if two BOXs overlap. + * 0 if two BOXs do not overlap. + * Remember, x2 and y2 are not in the region + */ +#define EXTENTCHECK(r1, r2) \ + ((r1)->right() >= (r2)->left() && \ + (r1)->left() <= (r2)->right() && \ + (r1)->bottom() >= (r2)->top() && \ + (r1)->top() <= (r2)->bottom()) + +/* + * update region extents + */ +#define EXTENTS(r,idRect){\ + if((r)->left() < (idRect)->extents.left())\ + (idRect)->extents.setLeft((r)->left());\ + if((r)->top() < (idRect)->extents.top())\ + (idRect)->extents.setTop((r)->top());\ + if((r)->right() > (idRect)->extents.right())\ + (idRect)->extents.setRight((r)->right());\ + if((r)->bottom() > (idRect)->extents.bottom())\ + (idRect)->extents.setBottom((r)->bottom());\ + } + +/* + * Check to see if there is enough memory in the present region. + */ +#define MEMCHECK(dest, rect, firstrect){\ + if ((dest).numRects >= ((dest).rects.size()-1)){\ + firstrect.resize(firstrect.size() * 2); \ + (rect) = (firstrect).data() + (dest).numRects;\ + }\ + } + + +/* + * number of points to buffer before sending them off + * to scanlines(): Must be an even number + */ +#define NUMPTSTOBUFFER 200 + +/* + * used to allocate buffers for points and link + * the buffers together + */ +typedef struct _POINTBLOCK { + QPoint pts[NUMPTSTOBUFFER]; + struct _POINTBLOCK *next; +} POINTBLOCK; + +#endif +// END OF region.h extract + +// START OF Region.c extract +/* $XConsortium: Region.c /main/30 1996/10/22 14:21:24 kaleb $ */ +/************************************************************************ + +Copyright (c) 1987, 1988 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* + * The functions in this file implement the Region abstraction, similar to one + * used in the X11 sample server. A Region is simply an area, as the name + * implies, and is implemented as a "y-x-banded" array of rectangles. To + * explain: Each Region is made up of a certain number of rectangles sorted + * by y coordinate first, and then by x coordinate. + * + * Furthermore, the rectangles are banded such that every rectangle with a + * given upper-left y coordinate (y1) will have the same lower-right y + * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it + * will span the entire vertical distance of the band. This means that some + * areas that could be merged into a taller rectangle will be represented as + * several shorter rectangles to account for shorter rectangles to its left + * or right but within its "vertical scope". + * + * An added constraint on the rectangles is that they must cover as much + * horizontal area as possible. E.g. no two rectangles in a band are allowed + * to touch. + * + * Whenever possible, bands will be merged together to cover a greater vertical + * distance (and thus reduce the number of rectangles). Two bands can be merged + * only if the bottom of one touches the top of the other and they have + * rectangles in the same places (of the same width, of course). This maintains + * the y-x-banding that's so nice to have... + */ +/* $XFree86: xc/lib/X11/Region.c,v 1.1.1.2.2.2 1998/10/04 15:22:50 hohndel Exp $ */ + +static void UnionRectWithRegion(register const QRect *rect, const QRegionPrivate *source, + QRegionPrivate &dest) +{ + if (!rect->width() || !rect->height()) + return; + + QRegionPrivate region(*rect); + + Q_ASSERT(EqualRegion(source, &dest)); + Q_ASSERT(!isEmptyHelper(®ion)); + + if (dest.numRects == 0) + dest = region; + else if (dest.canAppend(®ion)) + dest.append(®ion); + else + UnionRegion(®ion, source, dest); +} + +/*- + *----------------------------------------------------------------------- + * miSetExtents -- + * Reset the extents and innerRect of a region to what they should be. + * Called by miSubtract and miIntersect b/c they can't figure it out + * along the way or do so easily, as miUnion can. + * + * Results: + * None. + * + * Side Effects: + * The region's 'extents' and 'innerRect' structure is overwritten. + * + *----------------------------------------------------------------------- + */ +static void miSetExtents(QRegionPrivate &dest) +{ + register const QRect *pBox, + *pBoxEnd; + register QRect *pExtents; + + dest.innerRect.setCoords(0, 0, -1, -1); + dest.innerArea = -1; + if (dest.numRects == 0) { + dest.extents.setCoords(0, 0, 0, 0); + return; + } + + pExtents = &dest.extents; + pBox = (dest.mode==QRegionPrivate::Vector)?(dest.rects.constData()):(&dest.single); + pBoxEnd = (dest.mode==QRegionPrivate::Vector)?(&pBox[dest.numRects - 1]):(&dest.single); + + /* + * Since pBox is the first rectangle in the region, it must have the + * smallest y1 and since pBoxEnd is the last rectangle in the region, + * it must have the largest y2, because of banding. Initialize x1 and + * x2 from pBox and pBoxEnd, resp., as good things to initialize them + * to... + */ + pExtents->setLeft(pBox->left()); + pExtents->setTop(pBox->top()); + pExtents->setRight(pBoxEnd->right()); + pExtents->setBottom(pBoxEnd->bottom()); + + Q_ASSERT(pExtents->top() <= pExtents->bottom()); + while (pBox <= pBoxEnd) { + if (pBox->left() < pExtents->left()) + pExtents->setLeft(pBox->left()); + if (pBox->right() > pExtents->right()) + pExtents->setRight(pBox->right()); + dest.updateInnerRect(*pBox); + ++pBox; + } + Q_ASSERT(pExtents->left() <= pExtents->right()); +} + +/* TranslateRegion(pRegion, x, y) + translates in place + added by raymond +*/ + +static void OffsetRegion(register QRegionPrivate ®ion, register int x, register int y) +{ + register int nbox; + register QRect *pbox; + + if(region.mode == QRegionPrivate::Single) { + region.single.translate(x, y); + } else { + pbox = region.rects.data(); + nbox = region.numRects; + + while (nbox--) { + pbox->translate(x, y); + ++pbox; + } + } + region.extents.translate(x, y); + region.innerRect.translate(x, y); +} + +/*====================================================================== + * Region Intersection + *====================================================================*/ +/*- + *----------------------------------------------------------------------- + * miIntersectO -- + * Handle an overlapping band for miIntersect. + * + * Results: + * None. + * + * Side Effects: + * Rectangles may be added to the region. + * + *----------------------------------------------------------------------- + */ +static void miIntersectO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End, + register const QRect *r2, const QRect *r2End, int y1, int y2) +{ + register int x1; + register int x2; + register QRect *pNextRect; + + pNextRect = dest.rects.data() + dest.numRects; + + while (r1 != r1End && r2 != r2End) { + x1 = qMax(r1->left(), r2->left()); + x2 = qMin(r1->right(), r2->right()); + + /* + * If there's any overlap between the two rectangles, add that + * overlap to the new region. + * There's no need to check for subsumption because the only way + * such a need could arise is if some region has two rectangles + * right next to each other. Since that should never happen... + */ + if (x1 <= x2) { + Q_ASSERT(y1 <= y2); + MEMCHECK(dest, pNextRect, dest.rects) + pNextRect->setCoords(x1, y1, x2, y2); + ++dest.numRects; + ++pNextRect; + } + + /* + * Need to advance the pointers. Shift the one that extends + * to the right the least, since the other still has a chance to + * overlap with that region's next rectangle, if you see what I mean. + */ + if (r1->right() < r2->right()) { + ++r1; + } else if (r2->right() < r1->right()) { + ++r2; + } else { + ++r1; + ++r2; + } + } +} + +/*====================================================================== + * Generic Region Operator + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miCoalesce -- + * Attempt to merge the boxes in the current band with those in the + * previous one. Used only by miRegionOp. + * + * Results: + * The new index for the previous band. + * + * Side Effects: + * If coalescing takes place: + * - rectangles in the previous band will have their y2 fields + * altered. + * - dest.numRects will be decreased. + * + *----------------------------------------------------------------------- + */ +static int miCoalesce(register QRegionPrivate &dest, int prevStart, int curStart) +{ + register QRect *pPrevBox; /* Current box in previous band */ + register QRect *pCurBox; /* Current box in current band */ + register QRect *pRegEnd; /* End of region */ + int curNumRects; /* Number of rectangles in current band */ + int prevNumRects; /* Number of rectangles in previous band */ + int bandY1; /* Y1 coordinate for current band */ + QRect *rData = dest.rects.data(); + + pRegEnd = rData + dest.numRects; + + pPrevBox = rData + prevStart; + prevNumRects = curStart - prevStart; + + /* + * Figure out how many rectangles are in the current band. Have to do + * this because multiple bands could have been added in miRegionOp + * at the end when one region has been exhausted. + */ + pCurBox = rData + curStart; + bandY1 = pCurBox->top(); + for (curNumRects = 0; pCurBox != pRegEnd && pCurBox->top() == bandY1; ++curNumRects) { + ++pCurBox; + } + + if (pCurBox != pRegEnd) { + /* + * If more than one band was added, we have to find the start + * of the last band added so the next coalescing job can start + * at the right place... (given when multiple bands are added, + * this may be pointless -- see above). + */ + --pRegEnd; + while ((pRegEnd - 1)->top() == pRegEnd->top()) + --pRegEnd; + curStart = pRegEnd - rData; + pRegEnd = rData + dest.numRects; + } + + if (curNumRects == prevNumRects && curNumRects != 0) { + pCurBox -= curNumRects; + /* + * The bands may only be coalesced if the bottom of the previous + * matches the top scanline of the current. + */ + if (pPrevBox->bottom() == pCurBox->top() - 1) { + /* + * Make sure the bands have boxes in the same places. This + * assumes that boxes have been added in such a way that they + * cover the most area possible. I.e. two boxes in a band must + * have some horizontal space between them. + */ + do { + if (pPrevBox->left() != pCurBox->left() || pPrevBox->right() != pCurBox->right()) { + // The bands don't line up so they can't be coalesced. + return curStart; + } + ++pPrevBox; + ++pCurBox; + --prevNumRects; + } while (prevNumRects != 0); + + dest.numRects -= curNumRects; + pCurBox -= curNumRects; + pPrevBox -= curNumRects; + + /* + * The bands may be merged, so set the bottom y of each box + * in the previous band to that of the corresponding box in + * the current band. + */ + do { + pPrevBox->setBottom(pCurBox->bottom()); + dest.updateInnerRect(*pPrevBox); + ++pPrevBox; + ++pCurBox; + curNumRects -= 1; + } while (curNumRects != 0); + + /* + * If only one band was added to the region, we have to backup + * curStart to the start of the previous band. + * + * If more than one band was added to the region, copy the + * other bands down. The assumption here is that the other bands + * came from the same region as the current one and no further + * coalescing can be done on them since it's all been done + * already... curStart is already in the right place. + */ + if (pCurBox == pRegEnd) { + curStart = prevStart; + } else { + do { + *pPrevBox++ = *pCurBox++; + dest.updateInnerRect(*pPrevBox); + } while (pCurBox != pRegEnd); + } + } + } + return curStart; +} + +/*- + *----------------------------------------------------------------------- + * miRegionOp -- + * Apply an operation to two regions. Called by miUnion, miInverse, + * miSubtract, miIntersect... + * + * Results: + * None. + * + * Side Effects: + * The new region is overwritten. + * + * Notes: + * The idea behind this function is to view the two regions as sets. + * Together they cover a rectangle of area that this function divides + * into horizontal bands where points are covered only by one region + * or by both. For the first case, the nonOverlapFunc is called with + * each the band and the band's upper and lower extents. For the + * second, the overlapFunc is called to process the entire band. It + * is responsible for clipping the rectangles in the band, though + * this function provides the boundaries. + * At the end of each band, the new region is coalesced, if possible, + * to reduce the number of rectangles in the region. + * + *----------------------------------------------------------------------- + */ +static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2, + OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func, + NonOverlapFunc nonOverlap2Func) +{ + register const QRect *r1; // Pointer into first region + register const QRect *r2; // Pointer into 2d region + const QRect *r1End; // End of 1st region + const QRect *r2End; // End of 2d region + register int ybot; // Bottom of intersection + register int ytop; // Top of intersection + int prevBand; // Index of start of previous band in dest + int curBand; // Index of start of current band in dest + register const QRect *r1BandEnd; // End of current band in r1 + register const QRect *r2BandEnd; // End of current band in r2 + int top; // Top of non-overlapping band + int bot; // Bottom of non-overlapping band + + /* + * Initialization: + * set r1, r2, r1End and r2End appropriately, preserve the important + * parts of the destination region until the end in case it's one of + * the two source regions, then mark the "new" region empty, allocating + * another array of rectangles for it to use. + */ + r1 = (reg1->mode==QRegionPrivate::Vector)?reg1->rects.data():®1->single; + r2 = (reg2->mode==QRegionPrivate::Vector)?reg2->rects.data():®2->single; + r1End = r1 + reg1->numRects; + r2End = r2 + reg2->numRects; + + dest.vector(); + QVector<QRect> oldRects = dest.rects; + + dest.numRects = 0; + + /* + * Allocate a reasonable number of rectangles for the new region. The idea + * is to allocate enough so the individual functions don't need to + * reallocate and copy the array, which is time consuming, yet we don't + * have to worry about using too much memory. I hope to be able to + * nuke the realloc() at the end of this function eventually. + */ + dest.rects.resize(qMax(reg1->numRects,reg2->numRects) * 2); + + /* + * Initialize ybot and ytop. + * In the upcoming loop, ybot and ytop serve different functions depending + * on whether the band being handled is an overlapping or non-overlapping + * band. + * In the case of a non-overlapping band (only one of the regions + * has points in the band), ybot is the bottom of the most recent + * intersection and thus clips the top of the rectangles in that band. + * ytop is the top of the next intersection between the two regions and + * serves to clip the bottom of the rectangles in the current band. + * For an overlapping band (where the two regions intersect), ytop clips + * the top of the rectangles of both regions and ybot clips the bottoms. + */ + if (reg1->extents.top() < reg2->extents.top()) + ybot = reg1->extents.top() - 1; + else + ybot = reg2->extents.top() - 1; + + /* + * prevBand serves to mark the start of the previous band so rectangles + * can be coalesced into larger rectangles. qv. miCoalesce, above. + * In the beginning, there is no previous band, so prevBand == curBand + * (curBand is set later on, of course, but the first band will always + * start at index 0). prevBand and curBand must be indices because of + * the possible expansion, and resultant moving, of the new region's + * array of rectangles. + */ + prevBand = 0; + + do { + curBand = dest.numRects; + + /* + * This algorithm proceeds one source-band (as opposed to a + * destination band, which is determined by where the two regions + * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the + * rectangle after the last one in the current band for their + * respective regions. + */ + r1BandEnd = r1; + while (r1BandEnd != r1End && r1BandEnd->top() == r1->top()) + ++r1BandEnd; + + r2BandEnd = r2; + while (r2BandEnd != r2End && r2BandEnd->top() == r2->top()) + ++r2BandEnd; + + /* + * First handle the band that doesn't intersect, if any. + * + * Note that attention is restricted to one band in the + * non-intersecting region at once, so if a region has n + * bands between the current position and the next place it overlaps + * the other, this entire loop will be passed through n times. + */ + if (r1->top() < r2->top()) { + top = qMax(r1->top(), ybot + 1); + bot = qMin(r1->bottom(), r2->top() - 1); + + if (nonOverlap1Func != 0 && bot >= top) + (*nonOverlap1Func)(dest, r1, r1BandEnd, top, bot); + ytop = r2->top(); + } else if (r2->top() < r1->top()) { + top = qMax(r2->top(), ybot + 1); + bot = qMin(r2->bottom(), r1->top() - 1); + + if (nonOverlap2Func != 0 && bot >= top) + (*nonOverlap2Func)(dest, r2, r2BandEnd, top, bot); + ytop = r1->top(); + } else { + ytop = r1->top(); + } + + /* + * If any rectangles got added to the region, try and coalesce them + * with rectangles from the previous band. Note we could just do + * this test in miCoalesce, but some machines incur a not + * inconsiderable cost for function calls, so... + */ + if (dest.numRects != curBand) + prevBand = miCoalesce(dest, prevBand, curBand); + + /* + * Now see if we've hit an intersecting band. The two bands only + * intersect if ybot >= ytop + */ + ybot = qMin(r1->bottom(), r2->bottom()); + curBand = dest.numRects; + if (ybot >= ytop) + (*overlapFunc)(dest, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot); + + if (dest.numRects != curBand) + prevBand = miCoalesce(dest, prevBand, curBand); + + /* + * If we've finished with a band (y2 == ybot) we skip forward + * in the region to the next band. + */ + if (r1->bottom() == ybot) + r1 = r1BandEnd; + if (r2->bottom() == ybot) + r2 = r2BandEnd; + } while (r1 != r1End && r2 != r2End); + + /* + * Deal with whichever region still has rectangles left. + */ + curBand = dest.numRects; + if (r1 != r1End) { + if (nonOverlap1Func != 0) { + do { + r1BandEnd = r1; + while (r1BandEnd < r1End && r1BandEnd->top() == r1->top()) + ++r1BandEnd; + (*nonOverlap1Func)(dest, r1, r1BandEnd, qMax(r1->top(), ybot + 1), r1->bottom()); + r1 = r1BandEnd; + } while (r1 != r1End); + } + } else if ((r2 != r2End) && (nonOverlap2Func != 0)) { + do { + r2BandEnd = r2; + while (r2BandEnd < r2End && r2BandEnd->top() == r2->top()) + ++r2BandEnd; + (*nonOverlap2Func)(dest, r2, r2BandEnd, qMax(r2->top(), ybot + 1), r2->bottom()); + r2 = r2BandEnd; + } while (r2 != r2End); + } + + if (dest.numRects != curBand) + (void)miCoalesce(dest, prevBand, curBand); + + /* + * A bit of cleanup. To keep regions from growing without bound, + * we shrink the array of rectangles to match the new number of + * rectangles in the region. + * + * Only do this stuff if the number of rectangles allocated is more than + * twice the number of rectangles in the region (a simple optimization). + */ + if (qMax(4, dest.numRects) < (dest.rects.size() >> 1)) + dest.rects.resize(dest.numRects); +} + +/*====================================================================== + * Region Union + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miUnionNonO -- + * Handle a non-overlapping band for the union operation. Just + * Adds the rectangles into the region. Doesn't have to check for + * subsumption or anything. + * + * Results: + * None. + * + * Side Effects: + * dest.numRects is incremented and the final rectangles overwritten + * with the rectangles we're passed. + * + *----------------------------------------------------------------------- + */ + +static void miUnionNonO(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd, + register int y1, register int y2) +{ + register QRect *pNextRect; + + pNextRect = dest.rects.data() + dest.numRects; + + Q_ASSERT(y1 <= y2); + + while (r != rEnd) { + Q_ASSERT(r->left() <= r->right()); + MEMCHECK(dest, pNextRect, dest.rects) + pNextRect->setCoords(r->left(), y1, r->right(), y2); + dest.numRects++; + ++pNextRect; + ++r; + } +} + + +/*- + *----------------------------------------------------------------------- + * miUnionO -- + * Handle an overlapping band for the union operation. Picks the + * left-most rectangle each time and merges it into the region. + * + * Results: + * None. + * + * Side Effects: + * Rectangles are overwritten in dest.rects and dest.numRects will + * be changed. + * + *----------------------------------------------------------------------- + */ + +static void miUnionO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End, + register const QRect *r2, const QRect *r2End, register int y1, register int y2) +{ + register QRect *pNextRect; + + pNextRect = dest.rects.data() + dest.numRects; + +#define MERGERECT(r) \ + if ((dest.numRects != 0) && \ + (pNextRect[-1].top() == y1) && \ + (pNextRect[-1].bottom() == y2) && \ + (pNextRect[-1].right() >= r->left()-1)) { \ + if (pNextRect[-1].right() < r->right()) { \ + pNextRect[-1].setRight(r->right()); \ + dest.updateInnerRect(pNextRect[-1]); \ + Q_ASSERT(pNextRect[-1].left() <= pNextRect[-1].right()); \ + } \ + } else { \ + MEMCHECK(dest, pNextRect, dest.rects) \ + pNextRect->setCoords(r->left(), y1, r->right(), y2); \ + dest.updateInnerRect(*pNextRect); \ + dest.numRects++; \ + pNextRect++; \ + } \ + r++; + + Q_ASSERT(y1 <= y2); + while (r1 != r1End && r2 != r2End) { + if (r1->left() < r2->left()) { + MERGERECT(r1) + } else { + MERGERECT(r2) + } + } + + if (r1 != r1End) { + do { + MERGERECT(r1) + } while (r1 != r1End); + } else { + while (r2 != r2End) { + MERGERECT(r2) + } + } +} + +static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest) +{ + Q_ASSERT(!isEmptyHelper(reg1) && !isEmptyHelper(reg2)); + Q_ASSERT(!reg1->contains(*reg2)); + Q_ASSERT(!reg2->contains(*reg1)); + Q_ASSERT(!EqualRegion(reg1, reg2)); + Q_ASSERT(!reg1->canAppend(reg2)); + Q_ASSERT(!reg2->canAppend(reg1)); + + if (reg1->innerArea > reg2->innerArea) { + dest.innerArea = reg1->innerArea; + dest.innerRect = reg1->innerRect; + } else { + dest.innerArea = reg2->innerArea; + dest.innerRect = reg2->innerRect; + } + miRegionOp(dest, reg1, reg2, miUnionO, miUnionNonO, miUnionNonO); + + dest.extents.setCoords(qMin(reg1->extents.left(), reg2->extents.left()), + qMin(reg1->extents.top(), reg2->extents.top()), + qMax(reg1->extents.right(), reg2->extents.right()), + qMax(reg1->extents.bottom(), reg2->extents.bottom())); +} + +/*====================================================================== + * Region Subtraction + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miSubtractNonO -- + * Deal with non-overlapping band for subtraction. Any parts from + * region 2 we discard. Anything from region 1 we add to the region. + * + * Results: + * None. + * + * Side Effects: + * dest may be affected. + * + *----------------------------------------------------------------------- + */ + +static void miSubtractNonO1(register QRegionPrivate &dest, register const QRect *r, + const QRect *rEnd, register int y1, register int y2) +{ + register QRect *pNextRect; + + pNextRect = dest.rects.data() + dest.numRects; + + Q_ASSERT(y1<=y2); + + while (r != rEnd) { + Q_ASSERT(r->left() <= r->right()); + MEMCHECK(dest, pNextRect, dest.rects) + pNextRect->setCoords(r->left(), y1, r->right(), y2); + ++dest.numRects; + ++pNextRect; + ++r; + } +} + +/*- + *----------------------------------------------------------------------- + * miSubtractO -- + * Overlapping band subtraction. x1 is the left-most point not yet + * checked. + * + * Results: + * None. + * + * Side Effects: + * dest may have rectangles added to it. + * + *----------------------------------------------------------------------- + */ + +static void miSubtractO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End, + register const QRect *r2, const QRect *r2End, register int y1, register int y2) +{ + register QRect *pNextRect; + register int x1; + + x1 = r1->left(); + + Q_ASSERT(y1 <= y2); + pNextRect = dest.rects.data() + dest.numRects; + + while (r1 != r1End && r2 != r2End) { + if (r2->right() < x1) { + /* + * Subtrahend missed the boat: go to next subtrahend. + */ + ++r2; + } else if (r2->left() <= x1) { + /* + * Subtrahend precedes minuend: nuke left edge of minuend. + */ + x1 = r2->right() + 1; + if (x1 > r1->right()) { + /* + * Minuend completely covered: advance to next minuend and + * reset left fence to edge of new minuend. + */ + ++r1; + if (r1 != r1End) + x1 = r1->left(); + } else { + // Subtrahend now used up since it doesn't extend beyond minuend + ++r2; + } + } else if (r2->left() <= r1->right()) { + /* + * Left part of subtrahend covers part of minuend: add uncovered + * part of minuend to region and skip to next subtrahend. + */ + Q_ASSERT(x1 < r2->left()); + MEMCHECK(dest, pNextRect, dest.rects) + pNextRect->setCoords(x1, y1, r2->left() - 1, y2); + ++dest.numRects; + ++pNextRect; + + x1 = r2->right() + 1; + if (x1 > r1->right()) { + /* + * Minuend used up: advance to new... + */ + ++r1; + if (r1 != r1End) + x1 = r1->left(); + } else { + // Subtrahend used up + ++r2; + } + } else { + /* + * Minuend used up: add any remaining piece before advancing. + */ + if (r1->right() >= x1) { + MEMCHECK(dest, pNextRect, dest.rects) + pNextRect->setCoords(x1, y1, r1->right(), y2); + ++dest.numRects; + ++pNextRect; + } + ++r1; + if (r1 != r1End) + x1 = r1->left(); + } + } + + /* + * Add remaining minuend rectangles to region. + */ + while (r1 != r1End) { + Q_ASSERT(x1 <= r1->right()); + MEMCHECK(dest, pNextRect, dest.rects) + pNextRect->setCoords(x1, y1, r1->right(), y2); + ++dest.numRects; + ++pNextRect; + + ++r1; + if (r1 != r1End) + x1 = r1->left(); + } +} + +/*- + *----------------------------------------------------------------------- + * miSubtract -- + * Subtract regS from regM and leave the result in regD. + * S stands for subtrahend, M for minuend and D for difference. + * + * Side Effects: + * regD is overwritten. + * + *----------------------------------------------------------------------- + */ + +static void SubtractRegion(QRegionPrivate *regM, QRegionPrivate *regS, + register QRegionPrivate &dest) +{ + Q_ASSERT(!isEmptyHelper(regM)); + Q_ASSERT(!isEmptyHelper(regS)); + Q_ASSERT(EXTENTCHECK(®M->extents, ®S->extents)); + Q_ASSERT(!regS->contains(*regM)); + Q_ASSERT(!EqualRegion(regM, regS)); + + miRegionOp(dest, regM, regS, miSubtractO, miSubtractNonO1, 0); + + /* + * Can't alter dest's extents before we call miRegionOp because + * it might be one of the source regions and miRegionOp depends + * on the extents of those regions being the unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents(dest); +} + +static void XorRegion(QRegionPrivate *sra, QRegionPrivate *srb, QRegionPrivate &dest) +{ + Q_ASSERT(!isEmptyHelper(sra) && !isEmptyHelper(srb)); + Q_ASSERT(EXTENTCHECK(&sra->extents, &srb->extents)); + Q_ASSERT(!EqualRegion(sra, srb)); + + QRegionPrivate tra, trb; + + if (!srb->contains(*sra)) + SubtractRegion(sra, srb, tra); + if (!sra->contains(*srb)) + SubtractRegion(srb, sra, trb); + + Q_ASSERT(isEmptyHelper(&trb) || !tra.contains(trb)); + Q_ASSERT(isEmptyHelper(&tra) || !trb.contains(tra)); + + if (isEmptyHelper(&tra)) { + dest = trb; + } else if (isEmptyHelper(&trb)) { + dest = tra; + } else if (tra.canAppend(&trb)) { + dest = tra; + dest.append(&trb); + } else if (trb.canAppend(&tra)) { + dest = trb; + dest.append(&tra); + } else { + UnionRegion(&tra, &trb, dest); + } +} + +/* + * Check to see if two regions are equal + */ +static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2) +{ + if (r1->numRects != r2->numRects) { + return false; + } else if (r1->numRects == 0) { + return true; + } else if (r1->extents != r2->extents) { + return false; + } else if (r1->mode == QRegionPrivate::Single && r2->mode == QRegionPrivate::Single) { + return r1->single == r2->single; + } else { + const QRect *rr1 = (r1->mode==QRegionPrivate::Vector)?r1->rects.constData():&r1->single; + const QRect *rr2 = (r2->mode==QRegionPrivate::Vector)?r2->rects.constData():&r2->single; + for (int i = 0; i < r1->numRects; ++i, ++rr1, ++rr2) { + if (*rr1 != *rr2) + return false; + } + } + + return true; +} + +static bool PointInRegion(QRegionPrivate *pRegion, int x, int y) +{ + int i; + + if (pRegion->mode == QRegionPrivate::Single) + return pRegion->single.contains(x, y); + if (isEmptyHelper(pRegion)) + return false; + if (!pRegion->extents.contains(x, y)) + return false; + if (pRegion->innerRect.contains(x, y)) + return true; + for (i = 0; i < pRegion->numRects; ++i) { + if (pRegion->rects[i].contains(x, y)) + return true; + } + return false; +} + +static bool RectInRegion(register QRegionPrivate *region, int rx, int ry, uint rwidth, uint rheight) +{ + register const QRect *pbox; + register const QRect *pboxEnd; + QRect rect(rx, ry, rwidth, rheight); + register QRect *prect = ▭ + int partIn, partOut; + + if (!region || region->numRects == 0 || !EXTENTCHECK(®ion->extents, prect)) + return RectangleOut; + + partOut = false; + partIn = false; + + /* can stop when both partOut and partIn are true, or we reach prect->y2 */ + for (pbox = (region->mode==QRegionPrivate::Vector)?region->rects.constData():®ion->single, pboxEnd = pbox + region->numRects; + pbox < pboxEnd; ++pbox) { + if (pbox->bottom() < ry) + continue; + + if (pbox->top() > ry) { + partOut = true; + if (partIn || pbox->top() > prect->bottom()) + break; + ry = pbox->top(); + } + + if (pbox->right() < rx) + continue; /* not far enough over yet */ + + if (pbox->left() > rx) { + partOut = true; /* missed part of rectangle to left */ + if (partIn) + break; + } + + if (pbox->left() <= prect->right()) { + partIn = true; /* definitely overlap */ + if (partOut) + break; + } + + if (pbox->right() >= prect->right()) { + ry = pbox->bottom() + 1; /* finished with this band */ + if (ry > prect->bottom()) + break; + rx = prect->left(); /* reset x out to left again */ + } else { + /* + * Because boxes in a band are maximal width, if the first box + * to overlap the rectangle doesn't completely cover it in that + * band, the rectangle must be partially out, since some of it + * will be uncovered in that band. partIn will have been set true + * by now... + */ + break; + } + } + return partIn ? ((ry <= prect->bottom()) ? RectanglePart : RectangleIn) : RectangleOut; +} +// END OF Region.c extract +// START OF poly.h extract +/* $XConsortium: poly.h,v 1.4 94/04/17 20:22:19 rws Exp $ */ +/************************************************************************ + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ + +/* + * This file contains a few macros to help track + * the edge of a filled object. The object is assumed + * to be filled in scanline order, and thus the + * algorithm used is an extension of Bresenham's line + * drawing algorithm which assumes that y is always the + * major axis. + * Since these pieces of code are the same for any filled shape, + * it is more convenient to gather the library in one + * place, but since these pieces of code are also in + * the inner loops of output primitives, procedure call + * overhead is out of the question. + * See the author for a derivation if needed. + */ + + +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + int minor_axis; /* minor axis */ + int d; /* decision variable */ + int m, m1; /* slope and slope+1 */ + int incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2) + + + +/* + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counter-clockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* + * for the winding number rule + */ +#define CLOCKWISE 1 +#define COUNTERCLOCKWISE -1 + +typedef struct _EdgeTableEntry { + int ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + int scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + int ymax; /* ymax for the polygon */ + int ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + + + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres) \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres) \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} +// END OF poly.h extract +// START OF PolyReg.c extract +/* $XConsortium: PolyReg.c,v 11.23 94/11/17 21:59:37 converse Exp $ */ +/************************************************************************ + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* $XFree86: xc/lib/X11/PolyReg.c,v 1.1.1.2.8.2 1998/10/04 15:22:49 hohndel Exp $ */ + +#define LARGE_COORDINATE 1000000 +#define SMALL_COORDINATE -LARGE_COORDINATE + +/* + * InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +static void InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, int scanline, + ScanLineListBlock **SLLBlock, int *iSLLBlock) +{ + register EdgeTableEntry *start, *prev; + register ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = + (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock)); + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = (ScanLineListBlock *)NULL; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = (EdgeTableEntry *)NULL; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = 0; + start = pSLL->edgelist; + while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; +} + +/* + * CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ + +static void CreateETandAET(register int count, register const QPoint *pts, + EdgeTable *ET, EdgeTableEntry *AET, register EdgeTableEntry *pETEs, + ScanLineListBlock *pSLLBlock) +{ + register const QPoint *top, + *bottom, + *PrevPt, + *CurrPt; + int iSLLBlock = 0; + int dy; + + if (count < 2) + return; + + /* + * initialize the Active Edge Table + */ + AET->next = 0; + AET->back = 0; + AET->nextWETE = 0; + AET->bres.minor_axis = SMALL_COORDINATE; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = 0; + ET->ymax = SMALL_COORDINATE; + ET->ymin = LARGE_COORDINATE; + pSLLBlock->next = 0; + + PrevPt = &pts[count - 1]; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y() > CurrPt->y()) { + bottom = PrevPt; + top = CurrPt; + pETEs->ClockWise = 0; + } else { + bottom = CurrPt; + top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y() != top->y()) { + pETEs->ymax = bottom->y() - 1; /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y() - top->y(); + BRESINITPGONSTRUCT(dy, top->x(), bottom->x(), pETEs->bres) + + InsertEdgeInET(ET, pETEs, top->y(), &pSLLBlock, &iSLLBlock); + + if (PrevPt->y() > ET->ymax) + ET->ymax = PrevPt->y(); + if (PrevPt->y() < ET->ymin) + ET->ymin = PrevPt->y(); + ++pETEs; + } + + PrevPt = CurrPt; + } +} + +/* + * loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ + +static void loadAET(register EdgeTableEntry *AET, register EdgeTableEntry *ETEs) +{ + register EdgeTableEntry *pPrevAET; + register EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) { + while (AET && AET->bres.minor_axis < ETEs->bres.minor_axis) { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/* + * computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +static void computeWAET(register EdgeTableEntry *AET) +{ + register EdgeTableEntry *pWETE; + register int inside = 1; + register int isInside = 0; + + AET->nextWETE = 0; + pWETE = AET; + AET = AET->next; + while (AET) { + if (AET->ClockWise) + ++isInside; + else + --isInside; + + if (!inside && !isInside || inside && isInside) { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = 0; +} + +/* + * InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ + +static int InsertionSort(register EdgeTableEntry *AET) +{ + register EdgeTableEntry *pETEchase; + register EdgeTableEntry *pETEinsert; + register EdgeTableEntry *pETEchaseBackTMP; + register int changed = 0; + + AET = AET->next; + while (AET) { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = 1; + } + } + return changed; +} + +/* + * Clean up our act. + */ +static void FreeStorage(register ScanLineListBlock *pSLLBlock) +{ + register ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) { + tmpSLLBlock = pSLLBlock->next; + free(pSLLBlock); + pSLLBlock = tmpSLLBlock; + } +} + +/* + * Create an array of rectangles from a list of points. + * If indeed these things (POINTS, RECTS) are the same, + * then this proc is still needed, because it allocates + * storage for the array, which was allocated on the + * stack by the calling procedure. + * + */ +static void PtsToRegion(register int numFullPtBlocks, register int iCurPtBlock, + POINTBLOCK *FirstPtBlock, QRegionPrivate *reg) +{ + register QRect *rects; + register QPoint *pts; + register POINTBLOCK *CurPtBlock; + register int i; + register QRect *extents; + register int numRects; + + extents = ®->extents; + numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1; + + reg->rects.resize(numRects); + + CurPtBlock = FirstPtBlock; + rects = reg->rects.data() - 1; + numRects = 0; + extents->setLeft(INT_MAX); + extents->setRight(INT_MIN); + reg->innerArea = -1; + + for (; numFullPtBlocks >= 0; --numFullPtBlocks) { + /* the loop uses 2 points per iteration */ + i = NUMPTSTOBUFFER >> 1; + if (!numFullPtBlocks) + i = iCurPtBlock >> 1; + if(i) { + for (pts = CurPtBlock->pts; i--; pts += 2) { + if (pts->x() == pts[1].x()) + continue; + if (numRects && pts->x() == rects->left() && pts->y() == rects->bottom() + 1 + && pts[1].x() == rects->right()+1 && (numRects == 1 || rects[-1].top() != rects->top()) + && (i && pts[2].y() > pts[1].y())) { + rects->setBottom(pts[1].y()); + reg->updateInnerRect(*rects); + continue; + } + ++numRects; + ++rects; + rects->setCoords(pts->x(), pts->y(), pts[1].x() - 1, pts[1].y()); + if (rects->left() < extents->left()) + extents->setLeft(rects->left()); + if (rects->right() > extents->right()) + extents->setRight(rects->right()); + reg->updateInnerRect(*rects); + } + } + CurPtBlock = CurPtBlock->next; + } + + if (numRects) { + extents->setTop(reg->rects[0].top()); + extents->setBottom(rects->bottom()); + } else { + extents->setCoords(0, 0, 0, 0); + } + reg->numRects = numRects; +} + +/* + * polytoregion + * + * Scan converts a polygon by returning a run-length + * encoding of the resultant bitmap -- the run-length + * encoding is in the form of an array of rectangles. + */ +static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule, + QRegionPrivate *region) + //Point *Pts; /* the pts */ + //int Count; /* number of pts */ + //int rule; /* winding rule */ +{ + register EdgeTableEntry *pAET; /* Active Edge Table */ + register int y; /* current scanline */ + register int iPts = 0; /* number of pts in buffer */ + register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/ + register ScanLineList *pSLL; /* current scanLineList */ + register QPoint *pts; /* output buffer */ + EdgeTableEntry *pPrevAET; /* ptr to previous AET */ + EdgeTable ET; /* header node for ET */ + EdgeTableEntry AET; /* header node for AET */ + EdgeTableEntry *pETEs; /* EdgeTableEntries pool */ + ScanLineListBlock SLLBlock; /* header for scanlinelist */ + int fixWAET = false; + POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */ + POINTBLOCK *tmpPtBlock; + int numFullPtBlocks = 0; + + region->vector(); + + /* special case a rectangle */ + if (((Count == 4) || + ((Count == 5) && (Pts[4].x() == Pts[0].x()) && (Pts[4].y() == Pts[0].y()))) + && (((Pts[0].y() == Pts[1].y()) && (Pts[1].x() == Pts[2].x()) && (Pts[2].y() == Pts[3].y()) + && (Pts[3].x() == Pts[0].x())) || ((Pts[0].x() == Pts[1].x()) + && (Pts[1].y() == Pts[2].y()) && (Pts[2].x() == Pts[3].x()) + && (Pts[3].y() == Pts[0].y())))) { + int x = qMin(Pts[0].x(), Pts[2].x()); + region->extents.setLeft(x); + int y = qMin(Pts[0].y(), Pts[2].y()); + region->extents.setTop(y); + region->extents.setWidth(qMax(Pts[0].x(), Pts[2].x()) - x); + region->extents.setHeight(qMax(Pts[0].y(), Pts[2].y()) - y); + if ((region->extents.left() <= region->extents.right()) && + (region->extents.top() <= region->extents.bottom())) { + region->numRects = 1; + region->rects.resize(1); + region->rects[0] = region->extents; + region->innerRect = region->extents; + region->innerArea = region->innerRect.width() * region->innerRect.height(); + } + return region; + } + + if (!(pETEs = static_cast<EdgeTableEntry *>(malloc(sizeof(EdgeTableEntry) * Count)))) + return 0; + + pts = FirstPtBlock.pts; + CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock); + pSLL = ET.scanlines.next; + curPtBlock = &FirstPtBlock; + + if (rule == EvenOddRule) { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; ++y) { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) { + loadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) { + pts->setX(pAET->bres.minor_axis); + pts->setY(y); + ++pts; + ++iPts; + + /* + * send out the buffer + */ + if (iPts == NUMPTSTOBUFFER) { + tmpPtBlock = (POINTBLOCK *)malloc(sizeof(POINTBLOCK)); + curPtBlock->next = tmpPtBlock; + curPtBlock = tmpPtBlock; + pts = curPtBlock->pts; + ++numFullPtBlocks; + iPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + } + InsertionSort(&AET); + } + } else { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; ++y) { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) { + loadAET(&AET, pSLL->edgelist); + computeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) { + /* + * add to the buffer only those edges that + * are in the Winding active edge table. + */ + if (pWETE == pAET) { + pts->setX(pAET->bres.minor_axis); + pts->setY(y); + ++pts; + ++iPts; + + /* + * send out the buffer + */ + if (iPts == NUMPTSTOBUFFER) { + tmpPtBlock = static_cast<POINTBLOCK *>(malloc(sizeof(POINTBLOCK))); + curPtBlock->next = tmpPtBlock; + curPtBlock = tmpPtBlock; + pts = curPtBlock->pts; + ++numFullPtBlocks; + iPts = 0; + } + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) + } + + /* + * recompute the winding active edge table if + * we just resorted or have exited an edge. + */ + if (InsertionSort(&AET) || fixWAET) { + computeWAET(&AET); + fixWAET = false; + } + } + } + FreeStorage(SLLBlock.next); + PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region); + for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) { + tmpPtBlock = curPtBlock->next; + free(curPtBlock); + curPtBlock = tmpPtBlock; + } + free(pETEs); + return region; +} +// END OF PolyReg.c extract + +QRegionPrivate *qt_bitmapToRegion(const QBitmap& bitmap, QRegionPrivate *region) +{ + region->vector(); + + QImage image = bitmap.toImage(); + + QRect xr; + +#define AddSpan \ + { \ + xr.setCoords(prev1, y, x-1, y); \ + UnionRectWithRegion(&xr, region, *region); \ + } + + const uchar zero = 0; + bool little = image.format() == QImage::Format_MonoLSB; + + int x, + y; + for (y = 0; y < image.height(); ++y) { + uchar *line = image.scanLine(y); + int w = image.width(); + uchar all = zero; + int prev1 = -1; + for (x = 0; x < w;) { + uchar byte = line[x / 8]; + if (x > w - 8 || byte!=all) { + if (little) { + for (int b = 8; b > 0 && x < w; --b) { + if (!(byte & 0x01) == !all) { + // More of the same + } else { + // A change. + if (all!=zero) { + AddSpan + all = zero; + } else { + prev1 = x; + all = ~zero; + } + } + byte >>= 1; + ++x; + } + } else { + for (int b = 8; b > 0 && x < w; --b) { + if (!(byte & 0x80) == !all) { + // More of the same + } else { + // A change. + if (all != zero) { + AddSpan + all = zero; + } else { + prev1 = x; + all = ~zero; + } + } + byte <<= 1; + ++x; + } + } + } else { + x += 8; + } + } + if (all != zero) { + AddSpan + } + } +#undef AddSpan + + return region; +} + +/* + Constructs an empty region. + + \sa isEmpty() +*/ + +QRegion::QRegion() + : d(&shared_empty) +{ + d->ref.ref(); +} + +/* + \overload + + Create a region based on the rectange \a r with region type \a t. + + If the rectangle is invalid a null region will be created. + + \sa QRegion::RegionType +*/ + +QRegion::QRegion(const QRect &r, RegionType t) +{ + if (r.isEmpty()) { + d = &shared_empty; + d->ref.ref(); + } else { +// d = new QRegionData; + QRegionPrivate *rp = 0; + if (t == Rectangle) { +// rp = new QRegionPrivate(r); + rp = qt_allocRegion(r); + } else if (t == Ellipse) { + QPainterPath path; + path.addEllipse(r.x(), r.y(), r.width(), r.height()); + QPolygon a = path.toSubpathPolygons().at(0).toPolygon(); + rp = qt_allocRegion(); +// rp = new QRegionPrivate; + PolygonRegion(a.constData(), a.size(), EvenOddRule, rp); + } + d = rp; + d->ref = 1; +#if defined(Q_WS_X11) + d->rgn = 0; + d->xrectangles = 0; +#elif defined(Q_WS_MAC) + d->rgn = 0; +#endif + d->qt_rgn = rp; + } +} + +/* + Constructs a polygon region from the point array \a a with the fill rule + specified by \a fillRule. + + If \a fillRule is \l{Qt::WindingFill}, the polygon region is defined + using the winding algorithm; if it is \l{Qt::OddEvenFill}, the odd-even fill + algorithm is used. + + \warning This constructor can be used to create complex regions that will + slow down painting when used. +*/ + +QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule) +{ + if (a.count() > 2) { + //d = new QRegionData; + // QRegionPrivate *rp = new QRegionPrivate; + QRegionPrivate *rp = qt_allocRegion(); + PolygonRegion(a.constData(), a.size(), + fillRule == Qt::WindingFill ? WindingRule : EvenOddRule, rp); + d = rp; + d->ref = 1; +#if defined(Q_WS_X11) + d->rgn = 0; + d->xrectangles = 0; +#elif defined(Q_WS_MAC) + d->rgn = 0; +#endif + d->qt_rgn = rp; + } else { + d = &shared_empty; + d->ref.ref(); + } +} + + +/* + Constructs a new region which is equal to region \a r. +*/ + +QRegion::QRegion(const QRegion &r) +{ + d = r.d; + d->ref.ref(); +} + + +/* + Constructs a region from the bitmap \a bm. + + The resulting region consists of the pixels in bitmap \a bm that + are Qt::color1, as if each pixel was a 1 by 1 rectangle. + + This constructor may create complex regions that will slow down + painting when used. Note that drawing masked pixmaps can be done + much faster using QPixmap::setMask(). +*/ +QRegion::QRegion(const QBitmap &bm) +{ + if (bm.isNull()) { + d = &shared_empty; + d->ref.ref(); + } else { + // d = new QRegionData; +// QRegionPrivate *rp = new QRegionPrivate; + QRegionPrivate *rp = qt_allocRegion(); + + qt_bitmapToRegion(bm, rp); + d = rp; + d->ref = 1; +#if defined(Q_WS_X11) + d->rgn = 0; + d->xrectangles = 0; +#elif defined(Q_WS_MAC) + d->rgn = 0; +#endif + d->qt_rgn = rp; + } +} + +void QRegion::cleanUp(QRegion::QRegionData *x) +{ + // delete x->qt_rgn; +#if defined(Q_WS_X11) + if (x->rgn) + XDestroyRegion(x->rgn); + if (x->xrectangles) + free(x->xrectangles); +#elif defined(Q_WS_MAC) + if (x->rgn) + qt_mac_dispose_rgn(x->rgn); +#endif + if(x->qt_rgn) { +// delete x->qt_rgn; + qt_freeRegion(x->qt_rgn); + } else { + delete x; + } +} + +/* + Destroys the region. +*/ + +QRegion::~QRegion() +{ + if (!d->ref.deref()) + cleanUp(d); +} + + +/* + Assigns \a r to this region and returns a reference to the region. +*/ + +QRegion &QRegion::operator=(const QRegion &r) +{ + r.d->ref.ref(); + if (!d->ref.deref()) + cleanUp(d); + d = r.d; + return *this; +} + + +/* + \internal +*/ + +QRegion QRegion::copy() const +{ + QRegion r; + QRegionData *x = 0; // new QRegionData; + QRegionPrivate *rp = 0; + if (d->qt_rgn) +// rp = new QRegionPrivate(*d->qt_rgn); + rp = qt_allocRegion(*d->qt_rgn); + else + rp = qt_allocRegion(); + x = rp; + x->qt_rgn = rp; + x->ref = 1; +#if defined(Q_WS_X11) + x->rgn = 0; + x->xrectangles = 0; +#elif defined(Q_WS_MAC) + x->rgn = 0; +#endif + + if (!r.d->ref.deref()) + cleanUp(r.d); + r.d = x; + return r; +} + +/* + Returns true if the region is empty; otherwise returns false. An + empty region is a region that contains no points. + + Example: + \snippet doc/src/snippets/code/src.gui.painting.qregion_qws.cpp 0 +*/ + +bool QRegion::isEmpty() const +{ + return d == &shared_empty || d->qt_rgn->numRects == 0; +} + + +/* + Returns true if the region contains the point \a p; otherwise + returns false. +*/ + +bool QRegion::contains(const QPoint &p) const +{ + return PointInRegion(d->qt_rgn, p.x(), p.y()); +} + +/* + \overload + + Returns true if the region overlaps the rectangle \a r; otherwise + returns false. +*/ + +bool QRegion::contains(const QRect &r) const +{ + if(!d->qt_rgn) + return false; + if(d->qt_rgn->mode == QRegionPrivate::Single) + return d->qt_rgn->single.contains(r); + + return RectInRegion(d->qt_rgn, r.left(), r.top(), r.width(), r.height()) != RectangleOut; +} + + + +/* + Translates (moves) the region \a dx along the X axis and \a dy + along the Y axis. +*/ + +void QRegion::translate(int dx, int dy) +{ + if ((dx == 0 && dy == 0) || isEmptyHelper(d->qt_rgn)) + return; + + detach(); + OffsetRegion(*d->qt_rgn, dx, dy); +#if defined(Q_WS_X11) + if (d->xrectangles) { + free(d->xrectangles); + d->xrectangles = 0; + } +#elif defined(Q_WS_MAC) + if(d->rgn) { + qt_mac_dispose_rgn(d->rgn); + d->rgn = 0; + } +#endif +} + +/* + \fn QRegion QRegion::unite(const QRegion &r) const + \obsolete + + Use united(\a r) instead. +*/ + +/* + \fn QRegion QRegion::united(const QRegion &r) const + \since 4.2 + + Returns a region which is the union of this region and \a r. + + \img runion.png Region Union + + The figure shows the union of two elliptical regions. + + \sa intersected(), subtracted(), xored() +*/ + +QRegion QRegion::unite(const QRegion &r) const +{ + if (isEmptyHelper(d->qt_rgn)) + return r; + if (isEmptyHelper(r.d->qt_rgn)) + return *this; + + if (d->qt_rgn->contains(*r.d->qt_rgn)) { + return *this; + } else if (r.d->qt_rgn->contains(*d->qt_rgn)) { + return r; + } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) { + QRegion result(*this); + result.detach(); + result.d->qt_rgn->append(r.d->qt_rgn); + return result; + } else if (r.d->qt_rgn->canAppend(d->qt_rgn)) { + QRegion result(r); + result.detach(); + result.d->qt_rgn->append(d->qt_rgn); + return result; + } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) { + return *this; + } else { + QRegion result; + result.detach(); + UnionRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn); + return result; + } +} + +QRegion& QRegion::operator+=(const QRegion &r) +{ + if (isEmptyHelper(d->qt_rgn)) + return *this = r; + if (isEmptyHelper(r.d->qt_rgn)) + return *this; + + if (d->qt_rgn->contains(*r.d->qt_rgn)) { + return *this; + } else if (r.d->qt_rgn->contains(*d->qt_rgn)) { + return *this = r; + } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) { + detach(); + d->qt_rgn->append(r.d->qt_rgn); + return *this; + } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) { + detach(); + d->qt_rgn->prepend(r.d->qt_rgn); + return *this; + } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) { + return *this; + } + + return *this = unite(r); +} + +/* + \fn QRegion QRegion::intersect(const QRegion &r) const + \obsolete + + Use intersected(\a r) instead. +*/ + +/* + \fn QRegion QRegion::intersected(const QRegion &r) const + \since 4.2 + + Returns a region which is the intersection of this region and \a r. + + \img rintersect.png Region Intersection + + The figure shows the intersection of two elliptical regions. +*/ + +QRegion QRegion::intersect(const QRegion &r) const +{ + if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn) + || !EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) + return QRegion(); + + /* this is fully contained in r */ + if (r.d->qt_rgn->contains(*d->qt_rgn)) + return *this; + + /* r is fully contained in this */ + if (d->qt_rgn->contains(*r.d->qt_rgn)) + return r; + + if(r.d->qt_rgn->mode == QRegionPrivate::Single && + d->qt_rgn->mode == QRegionPrivate::Single) + return QRegion(r.d->qt_rgn->single.intersected(d->qt_rgn->single)); +#ifdef QT_GREENPHONE_OPT + else if(r.d->qt_rgn->mode == QRegionPrivate::Single) + return intersect(r.d->qt_rgn->single); + else if(d->qt_rgn->mode == QRegionPrivate::Single) + return r.intersect(d->qt_rgn->single); +#endif + + QRegion result; + result.detach(); + miRegionOp(*result.d->qt_rgn, d->qt_rgn, r.d->qt_rgn, miIntersectO, 0, 0); + + /* + * Can't alter dest's extents before we call miRegionOp because + * it might be one of the source regions and miRegionOp depends + * on the extents of those regions being the same. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents(*result.d->qt_rgn); + return result; +} + +#ifdef QT_GREENPHONE_OPT +/* + \overload + */ +QRegion QRegion::intersect(const QRect &r) const +{ + // No intersection + if(r.isEmpty() || isEmpty() || !EXTENTCHECK(&r, &d->qt_rgn->extents)) + return QRegion(); + + // This is fully contained in r + if(CONTAINSCHECK(r, d->qt_rgn->extents)) + return *this; + + // r is fully contained in this + if(CONTAINSCHECK(d->qt_rgn->innerRect, r)) + return QRegion(r); + + if(d->qt_rgn->mode == QRegionPrivate::Single) { + return QRegion(d->qt_rgn->single & r); + } else { + QRegion rv(*this); + rv.detach(); + + rv.d->qt_rgn->extents &= r; + rv.d->qt_rgn->innerRect &= r; + rv.d->qt_rgn->innerArea = rv.d->qt_rgn->innerRect.height() * + rv.d->qt_rgn->innerRect.width(); + + int numRects = 0; + for(int ii = 0; ii < rv.d->qt_rgn->numRects; ++ii) { + QRect result = rv.d->qt_rgn->rects[ii] & r; + if(!result.isEmpty()) + rv.d->qt_rgn->rects[numRects++] = result; + } + rv.d->qt_rgn->numRects = numRects; + return rv; + } +} + +/* + \overload + */ +const QRegion QRegion::operator&(const QRect &r) const +{ + return intersect(r); +} + +/* + \overload + */ +QRegion& QRegion::operator&=(const QRect &r) +{ + if(isEmpty() || CONTAINSCHECK(r, d->qt_rgn->extents)) { + // Do nothing + } else if(r.isEmpty() || !EXTENTCHECK(&r, &d->qt_rgn->extents)) { + *this = QRegion(); + } else if(CONTAINSCHECK(d->qt_rgn->innerRect, r)) { + *this = QRegion(r); + } else { + detach(); + if(d->qt_rgn->mode == QRegionPrivate::Single) { + QRect result = d->qt_rgn->single & r; + d->qt_rgn->single = result; + d->qt_rgn->extents = result; + d->qt_rgn->innerRect = result; + d->qt_rgn->innerArea = result.height() * result.width(); + } else { + d->qt_rgn->extents &= r; + d->qt_rgn->innerRect &= r; + d->qt_rgn->innerArea = d->qt_rgn->innerRect.height() * + d->qt_rgn->innerRect.width(); + + int numRects = 0; + for(int ii = 0; ii < d->qt_rgn->numRects; ++ii) { + QRect result = d->qt_rgn->rects[ii] & r; + if(!result.isEmpty()) + d->qt_rgn->rects[numRects++] = result; + } + d->qt_rgn->numRects = numRects; + } + } + return *this; +} +#endif + +/* + \fn QRegion QRegion::subtract(const QRegion &r) const + \obsolete + + Use subtracted(\a r) instead. +*/ + +/* + \fn QRegion QRegion::subtracted(const QRegion &r) const + \since 4.2 + + Returns a region which is \a r subtracted from this region. + + \img rsubtract.png Region Subtraction + + The figure shows the result when the ellipse on the right is + subtracted from the ellipse on the left (\c {left - right}). + + \sa intersected(), united(), xored() +*/ + +QRegion QRegion::subtract(const QRegion &r) const +{ + if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn)) + return *this; + if (r.d->qt_rgn->contains(*d->qt_rgn)) + return QRegion(); + if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) + return *this; + if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) + return QRegion(); + + QRegion result; + result.detach(); + SubtractRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn); + return result; +} + +/* + \fn QRegion QRegion::eor(const QRegion &r) const + \obsolete + + Use xored(\a r) instead. +*/ + +/* + \fn QRegion QRegion::xored(const QRegion &r) const + \since 4.2 + + Returns a region which is the exclusive or (XOR) of this region + and \a r. + + \img rxor.png Region XORed + + The figure shows the exclusive or of two elliptical regions. + + \sa intersected(), united(), subtracted() +*/ + +QRegion QRegion::eor(const QRegion &r) const +{ + if (isEmptyHelper(d->qt_rgn)) { + return r; + } else if (isEmptyHelper(r.d->qt_rgn)) { + return *this; + } else if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) { + return (*this + r); + } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) { + return QRegion(); + } else { + QRegion result; + result.detach(); + XorRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn); + return result; + } +} + +/* + Returns the bounding rectangle of this region. An empty region + gives a rectangle that is QRect::isNull(). +*/ + +QRect QRegion::boundingRect() const +{ + if (isEmpty()) + return QRect(); + return d->qt_rgn->extents; +} + +/* \internal + Returns true if \a rect is guaranteed to be fully contained in \a region. + A false return value does not guarantee the opposite. +*/ +bool qt_region_strictContains(const QRegion ®ion, const QRect &rect) +{ + if (isEmptyHelper(region.d->qt_rgn) || !rect.isValid()) + return false; + +#if 0 // TEST_INNERRECT + static bool guard = false; + if (guard) + return QRect(); + guard = true; + QRegion inner = region.d->qt_rgn->innerRect; + Q_ASSERT((inner - region).isEmpty()); + guard = false; + + int maxArea = 0; + for (int i = 0; i < region.d->qt_rgn->numRects; ++i) { + const QRect r = region.d->qt_rgn->rects.at(i); + if (r.width() * r.height() > maxArea) + maxArea = r.width() * r.height(); + } + + if (maxArea > region.d->qt_rgn->innerArea) { + qDebug() << "not largest rectangle" << region << region.d->qt_rgn->innerRect; + } + Q_ASSERT(maxArea <= region.d->qt_rgn->innerArea); +#endif + + const QRect r1 = region.d->qt_rgn->innerRect; + return (rect.left() >= r1.left() && rect.right() <= r1.right() + && rect.top() >= r1.top() && rect.bottom() <= r1.bottom()); +} + +/* + Returns an array of non-overlapping rectangles that make up the + region. + + The union of all the rectangles is equal to the original region. +*/ +QVector<QRect> QRegion::rects() const +{ + if (d->qt_rgn) { + d->qt_rgn->vector(); + d->qt_rgn->rects.resize(d->qt_rgn->numRects); + return d->qt_rgn->rects; + } else { + return QVector<QRect>(); + } +} + +/* + \fn void QRegion::setRects(const QRect *rects, int number) + + Sets the region using the array of rectangles specified by \a rects and + \a number. + The rectangles \e must be optimally Y-X sorted and follow these restrictions: + + \list + \o The rectangles must not intersect. + \o All rectangles with a given top coordinate must have the same height. + \o No two rectangles may abut horizontally (they should be combined + into a single wider rectangle in that case). + \o The rectangles must be sorted in ascending order, with Y as the major + sort key and X as the minor sort key. + \endlist + \omit + Only some platforms have these restrictions (Qt for Embedded Linux, X11 and Mac OS X). + \endomit +*/ +void QRegion::setRects(const QRect *rects, int num) +{ + *this = QRegion(); + if (!rects || num == 0 || (num == 1 && rects->isEmpty())) + return; + + detach(); + + if(num == 1) { + d->qt_rgn->single = *rects; + d->qt_rgn->mode = QRegionPrivate::Single; + d->qt_rgn->numRects = num; + d->qt_rgn->extents = *rects; + d->qt_rgn->innerRect = *rects; + } else { + d->qt_rgn->mode = QRegionPrivate::Vector; + d->qt_rgn->rects.resize(num); + d->qt_rgn->numRects = num; + int left = INT_MAX, + right = INT_MIN, + top = INT_MAX, + bottom = INT_MIN; + for (int i = 0; i < num; ++i) { + const QRect &rect = rects[i]; + d->qt_rgn->rects[i] = rect; + left = qMin(rect.left(), left); + right = qMax(rect.right(), right); + top = qMin(rect.top(), top); + bottom = qMax(rect.bottom(), bottom); + d->qt_rgn->updateInnerRect(rect); + } + d->qt_rgn->extents = QRect(QPoint(left, top), QPoint(right, bottom)); + } +} + +/* + Returns true if the region is equal to \a r; otherwise returns + false. +*/ + +bool QRegion::operator==(const QRegion &r) const +{ + if (!d->qt_rgn || !r.d->qt_rgn) + return r.d->qt_rgn == d->qt_rgn; + + if (d == r.d) + return true; + else + return EqualRegion(d->qt_rgn, r.d->qt_rgn); +} + +#ifdef QT_GREENPHONE_OPT +bool QRegion::isRect() const +{ + return d->qt_rgn && d->qt_rgn->mode == QRegionPrivate::Single; +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/painting/qwindowsurface_wayland.cpp b/src/gui/painting/qwindowsurface_wayland.cpp new file mode 100644 index 0000000000..bf2a7e2429 --- /dev/null +++ b/src/gui/painting/qwindowsurface_wayland.cpp @@ -0,0 +1,487 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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_wayland_p.h" +#include <qwidget.h> +#include <qapplication.h> +#include <qrgb.h> +#include <qpaintengine.h> +#include <qdesktopwidget.h> +#include <private/qapplication_p.h> +#include <private/qwidget_p.h> +#include <private/qbackingstore_p.h> +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +QWaylandWindowSurface* qt_findWindowSurface(int winId) +{ + return NULL; +} + +void QWaylandWindowSurface::invalidateBuffer() +{ +} + +QWaylandWindowSurfacePrivate::QWaylandWindowSurfacePrivate() + : flags(0), +#ifdef QT_QWS_CLIENTBLIT + directId(-1), +#endif + winId(0) +{ +} + +void QWaylandWindowSurfacePrivate::setWinId(int id) +{ + winId = id; +} + +int QWaylandWindowSurface::winId() const +{ + return d_ptr->winId; +} + +void QWaylandWindowSurface::setWinId(int id) +{ + d_ptr->winId = id; +} + +/*! + \class QWaylandWindowSurface + \since 4.2 + \ingroup qws + \preliminary + \internal + + \brief The QWaylandWindowSurface 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. QWaylandWindowSurface 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 QWaylandWindowSurface 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, QWaylandWindowSurface + 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 + (QWaylandWindowSurface 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 + + QWaylandWindowSurface 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, + QWaylandWindowSurface provides the isBuffered(), isOpaque() and + isRegionReserved() convenience functions. + + \sa {Qt for Embedded Linux Architecture#Drawing on Screen}{Qt for + Embedded Linux Architecture} +*/ + +/*! + \enum QWaylandWindowSurface::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 QWaylandWindowSurface::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 QWaylandWindowSurface::key() const + + Implement this function to return a string that uniquely + identifies the class of this surface. + + \sa window(), isValid() +*/ + +/*! + \fn QByteArray QWaylandWindowSurface::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 QWaylandWindowSurface::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 QWaylandWindowSurface::image() const + + Implement this function to return an image of the top-level window. + + \sa geometry() +*/ + +/*! + \fn bool QWaylandWindowSurface::isRegionReserved() const + + Returns true if the QWaylandWindowSurface::RegionReserved is set; otherwise + returns false. + + \sa surfaceFlags() +*/ + +/*! + \fn bool QWaylandWindowSurface::isBuffered() const + + Returns true if the QWaylandWindowSurface::Buffered is set; otherwise returns false. + + \sa surfaceFlags() +*/ + +/*! + \fn bool QWaylandWindowSurface::isOpaque() const + + Returns true if the QWaylandWindowSurface::Opaque is set; otherwise + returns false. + + \sa surfaceFlags() +*/ + + +/*! + Constructs an empty surface. +*/ +QWaylandWindowSurface::QWaylandWindowSurface() + : QWindowSurface(0), d_ptr(new QWaylandWindowSurfacePrivate) +{ +} + +/*! + Constructs an empty surface for the given top-level \a widget. +*/ +QWaylandWindowSurface::QWaylandWindowSurface(QWidget *widget) + : QWindowSurface(widget), d_ptr(new QWaylandWindowSurfacePrivate) +{ +} + +QWaylandWindowSurface::~QWaylandWindowSurface() +{ +#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 QWaylandWindowSurface::painterOffset() const +{ + const QWidget *w = window(); + if (!w) + return QPoint(); + return w->geometry().topLeft() - w->frameGeometry().topLeft(); +} + +void QWaylandWindowSurface::beginPaint(const QRegion &) +{ + lock(); +} + +void QWaylandWindowSurface::endPaint(const QRegion &) +{ + unlock(); +} + +// XXX: documentation!!! +QByteArray QWaylandWindowSurface::transientState() const +{ + return QByteArray(); +} + +QByteArray QWaylandWindowSurface::permanentState() const +{ + return QByteArray(); +} + +void QWaylandWindowSurface::setTransientState(const QByteArray &state) +{ + Q_UNUSED(state); +} + +void QWaylandWindowSurface::setPermanentState(const QByteArray &state) +{ + Q_UNUSED(state); +} + +bool QWaylandWindowSurface::lock(int timeout) +{ + Q_UNUSED(timeout); + return true; +} + +void QWaylandWindowSurface::unlock() +{ +} + +#ifdef QT_QWS_CLIENTBLIT +/*! \internal */ +const QRegion QWaylandWindowSurface::directRegion() const +{ + return d_ptr->direct; +} + +/*! \internal */ +int QWaylandWindowSurface::directRegionId() const +{ + return d_ptr->directId; +} + +/*! \internal */ +void QWaylandWindowSurface::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 QWaylandWindowSurface::clipRegion() const +{ + return d_ptr->clip; +} + +/*! + Sets the region currently visible on the screen to be the given \a + clip region. + + \sa clipRegion() +*/ +void QWaylandWindowSurface::setClipRegion(const QRegion &clip) +{ +} + +/*! + Returns the surface flags describing the contents of this surface. + + \sa isBuffered(), isOpaque(), isRegionReserved() +*/ +QWaylandWindowSurface::SurfaceFlags QWaylandWindowSurface::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 QWaylandWindowSurface::setSurfaceFlags(SurfaceFlags flags) +{ + d_ptr->flags = flags; +} + +void QWaylandWindowSurface::setGeometry(const QRect &rect) +{ +} + +void QWaylandWindowSurface::setGeometry(const QRect &rect, const QRegion &mask) +{ +} + +static inline void flushUpdate(QWidget *widget, const QRegion ®ion, + const QPoint &offset) +{ +} + +void QWaylandWindowSurface::flush(QWidget *widget, const QRegion ®ion, + const QPoint &offset) +{ +} + +/*! + 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 QWaylandWindowSurface::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 QWaylandWindowSurface::move(const QPoint &offset, const QRegion &newClip) +{ + const QRegion oldGeometry = geometry(); + QWindowSurface::setGeometry(geometry().translated(offset)); + return oldGeometry + newClip; +} + +void QWaylandWindowSurface::releaseSurface() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/painting/qwindowsurface_wayland_p.h b/src/gui/painting/qwindowsurface_wayland_p.h new file mode 100644 index 0000000000..668796a601 --- /dev/null +++ b/src/gui/painting/qwindowsurface_wayland_p.h @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QWINDOWSURFACE_QWAYLAND_P_H +#define QWINDOWSURFACE_QWAYLAND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwindowsurface_p.h" +#include <qregion.h> +#include <qimage.h> +#include <qmutex.h> + +QT_BEGIN_NAMESPACE + +class QScreen; +class QWaylandWindowSurfacePrivate; + +class Q_GUI_EXPORT QWaylandWindowSurface : public QWindowSurface +{ +public: + QWaylandWindowSurface(); + QWaylandWindowSurface(QWidget *widget); + ~QWaylandWindowSurface(); + + virtual bool isValid() const = 0; + + virtual void setGeometry(const QRect &rect); + virtual void setGeometry(const QRect &rect, const QRegion &mask); + virtual void flush(QWidget *widget, const QRegion ®ion, + const QPoint &offset); + + virtual bool move(const QPoint &offset); + virtual QRegion move(const QPoint &offset, const QRegion &newClip); + + virtual QPoint painterOffset() const; // remove!!! + + virtual void beginPaint(const QRegion &); + virtual void endPaint(const QRegion &); + + virtual bool lock(int timeout = -1); + virtual void unlock(); + + virtual QString key() const = 0; + + // XXX: not good enough + virtual QByteArray transientState() const; + virtual QByteArray permanentState() const; + virtual void setTransientState(const QByteArray &state); + virtual void setPermanentState(const QByteArray &state); + + virtual QImage image() const = 0; + virtual QPaintDevice *paintDevice() = 0; + + const QRegion clipRegion() const; + void setClipRegion(const QRegion &); + + enum SurfaceFlag { + RegionReserved = 0x1, + Buffered = 0x2, + Opaque = 0x4 + }; + Q_DECLARE_FLAGS(SurfaceFlags, SurfaceFlag) + + SurfaceFlags surfaceFlags() const; + + inline bool isRegionReserved() const { + return surfaceFlags() & RegionReserved; + } + inline bool isBuffered() const { return surfaceFlags() & Buffered; } + inline bool isOpaque() const { return surfaceFlags() & Opaque; } + + int winId() const; + virtual void releaseSurface(); + +protected: + void setSurfaceFlags(SurfaceFlags type); + void setWinId(int id); + +private: + friend class QWidgetPrivate; + + void invalidateBuffer(); + + QWaylandWindowSurfacePrivate *d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandWindowSurface::SurfaceFlags) + +class QWaylandWindowSurfacePrivate +{ +public: + QWaylandWindowSurfacePrivate(); + + void setWinId(int id); + + QWaylandWindowSurface::SurfaceFlags flags; + QRegion clip; +#ifdef QT_QWayland_CLIENTBLIT + QRegion direct; + int directId; +#endif + + int winId; +}; + +class QWaylandLock; + +class Q_GUI_EXPORT QWaylandMemorySurface : public QWaylandWindowSurface +{ +public: + QWaylandMemorySurface(); + QWaylandMemorySurface(QWidget *widget); + ~QWaylandMemorySurface(); + + bool isValid() const; + + QPaintDevice *paintDevice() { return &img; } + bool scroll(const QRegion &area, int dx, int dy); + + QImage image() const { return img; } + QPoint painterOffset() const; + + void beginPaint(const QRegion &rgn); + + bool lock(int timeout = -1); + void unlock(); + +protected: + QImage::Format preferredImageFormat(const QWidget *widget) const; + +#ifndef QT_NO_QWayland_MULTIPROCESS + void setLock(int lockId); + QWaylandLock *memlock; +#endif +#ifndef QT_NO_THREAD + QMutex threadLock; +#endif + + QImage img; +}; + +class Q_GUI_EXPORT QWaylandLocalMemSurface : public QWaylandMemorySurface +{ +public: + QWaylandLocalMemSurface(); + QWaylandLocalMemSurface(QWidget *widget); + ~QWaylandLocalMemSurface(); + + void setGeometry(const QRect &rect); + + QString key() const { return QLatin1String("mem"); } + QByteArray permanentState() const; + + void setPermanentState(const QByteArray &data); + virtual void releaseSurface(); +protected: + uchar *mem; + int memsize; +}; + +#ifndef QT_NO_QWayland_MULTIPROCESS +class Q_GUI_EXPORT QWaylandSharedMemSurface : public QWaylandMemorySurface +{ +public: + QWaylandSharedMemSurface(); + QWaylandSharedMemSurface(QWidget *widget); + ~QWaylandSharedMemSurface(); + + void setGeometry(const QRect &rect); + + QString key() const { return QLatin1String("shm"); } + QByteArray permanentState() const; + + void setPermanentState(const QByteArray &data); + +#ifdef QT_QWayland_CLIENTBLIT + virtual void setDirectRegion(const QRegion &, int); + virtual const QRegion directRegion() const; +#endif + virtual void releaseSurface(); + +private: + bool setMemory(int memId); +}; +#endif // QT_NO_QWayland_MULTIPROCESS + +#ifndef QT_NO_PAINTONSCREEN +class Q_GUI_EXPORT QWaylandOnScreenSurface : public QWaylandMemorySurface +{ +public: + QWaylandOnScreenSurface(); + QWaylandOnScreenSurface(QWidget *widget); + ~QWaylandOnScreenSurface(); + + bool isValid() const; + QPoint painterOffset() const; + + QString key() const { return QLatin1String("OnScreen"); } + QByteArray permanentState() const; + + void setPermanentState(const QByteArray &data); + +private: + void attachToScreen(const QScreen *screen); + + const QScreen *screen; +}; +#endif // QT_NO_PAINTONSCREEN + +#ifndef QT_NO_PAINT_DEBUG +class Q_GUI_EXPORT QWaylandYellowSurface : public QWaylandWindowSurface +{ +public: + QWaylandYellowSurface(bool isClient = false); + ~QWaylandYellowSurface(); + + void setDelay(int msec) { delay = msec; } + + bool isValid() const { return true; } + + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + + QString key() const { return QLatin1String("Yellow"); } + QByteArray permanentState() const; + + void setPermanentState(const QByteArray &data); + + QPaintDevice *paintDevice() { return &img; } + QImage image() const { return img; } + +private: + int delay; + QSize surfaceSize; // client side + QImage img; // server side +}; +#endif // QT_NO_PAINT_DEBUG + +QT_END_NAMESPACE + +#endif // QWINDOWSURFACE_QWAYLAND_P_H diff --git a/src/gui/text/qabstractfontengine_wayland.cpp b/src/gui/text/qabstractfontengine_wayland.cpp new file mode 100644 index 0000000000..6b632b4150 --- /dev/null +++ b/src/gui/text/qabstractfontengine_wayland.cpp @@ -0,0 +1,746 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qabstractfontengine_qws.h" +#include "qabstractfontengine_p.h" + +#include <private/qtextengine_p.h> +#include <private/qpaintengine_raster_p.h> + +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +class QFontEngineInfoPrivate +{ +public: + inline QFontEngineInfoPrivate() + : pixelSize(0), weight(QFont::Normal), style(QFont::StyleNormal) + {} + + QString family; + qreal pixelSize; + int weight; + QFont::Style style; + QList<QFontDatabase::WritingSystem> writingSystems; +}; + +/*! + \class QFontEngineInfo + \preliminary + \brief The QFontEngineInfo class describes a specific font provided by a font engine plugin. + \since 4.3 + \ingroup qws + + \tableofcontents + + QFontEngineInfo is used to describe a request of a font to a font engine plugin as well as to + describe the actual fonts a plugin provides. + + \sa QAbstractFontEngine, QFontEnginePlugin +*/ + +/*! + Constructs a new empty QFontEngineInfo. +*/ +QFontEngineInfo::QFontEngineInfo() +{ + d = new QFontEngineInfoPrivate; +} + +/*! + Constructs a new QFontEngineInfo with the specified \a family. + The resulting object represents a freely scalable font with normal + weight and style. +*/ +QFontEngineInfo::QFontEngineInfo(const QString &family) +{ + d = new QFontEngineInfoPrivate; + d->family = family; +} + +/*! + Creates a new font engine info object with the same attributes as \a other. +*/ +QFontEngineInfo::QFontEngineInfo(const QFontEngineInfo &other) + : d(new QFontEngineInfoPrivate(*other.d)) +{ +} + +/*! + Assigns \a other to this font engine info object, and returns a reference + to this. +*/ +QFontEngineInfo &QFontEngineInfo::operator=(const QFontEngineInfo &other) +{ + *d = *other.d; + return *this; +} + +/*! + Destroys this QFontEngineInfo object. +*/ +QFontEngineInfo::~QFontEngineInfo() +{ + delete d; +} + +/*! + \property QFontEngineInfo::family + the family name of the font +*/ + +void QFontEngineInfo::setFamily(const QString &family) +{ + d->family = family; +} + +QString QFontEngineInfo::family() const +{ + return d->family; +} + +/*! + \property QFontEngineInfo::pixelSize + the pixel size of the font + + A pixel size of 0 represents a freely scalable font. +*/ + +void QFontEngineInfo::setPixelSize(qreal size) +{ + d->pixelSize = size; +} + +qreal QFontEngineInfo::pixelSize() const +{ + return d->pixelSize; +} + +/*! + \property QFontEngineInfo::weight + the weight of the font + + The value should be from the \l{QFont::Weight} enumeration. +*/ + +void QFontEngineInfo::setWeight(int weight) +{ + d->weight = weight; +} + +int QFontEngineInfo::weight() const +{ + return d->weight; +} + +/*! + \property QFontEngineInfo::style + the style of the font +*/ + +void QFontEngineInfo::setStyle(QFont::Style style) +{ + d->style = style; +} + +QFont::Style QFontEngineInfo::style() const +{ + return d->style; +} + +/*! + \property QFontEngineInfo::writingSystems + the writing systems supported by the font + + An empty list means that any writing system is supported. +*/ + +QList<QFontDatabase::WritingSystem> QFontEngineInfo::writingSystems() const +{ + return d->writingSystems; +} + +void QFontEngineInfo::setWritingSystems(const QList<QFontDatabase::WritingSystem> &writingSystems) +{ + d->writingSystems = writingSystems; +} + +class QFontEnginePluginPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QFontEnginePlugin) + + QString foundry; +}; + +/*! + \class QFontEnginePlugin + \preliminary + \brief The QFontEnginePlugin class is the base class for font engine factory plugins in Qt for Embedded Linux. + \since 4.3 + \ingroup qws + \ingroup plugins + + \tableofcontents + + QFontEnginePlugin is provided by font engine plugins to create + instances of subclasses of QAbstractFontEngine. + + The member functions create() and availableFontEngines() must be + implemented. + + \sa QAbstractFontEngine, QFontEngineInfo +*/ + +/*! + Creates a font engine plugin that creates font engines with the + specified \a foundry and \a parent. +*/ +QFontEnginePlugin::QFontEnginePlugin(const QString &foundry, QObject *parent) + : QObject(*new QFontEnginePluginPrivate, parent) +{ + Q_D(QFontEnginePlugin); + d->foundry = foundry; +} + +/*! + Destroys this font engine plugin. +*/ +QFontEnginePlugin::~QFontEnginePlugin() +{ +} + +/*! + Returns a list of foundries the font engine plugin provides. + The default implementation returns the foundry specified with the constructor. +*/ +QStringList QFontEnginePlugin::keys() const +{ + Q_D(const QFontEnginePlugin); + return QStringList(d->foundry); +} + +/*! + \fn QAbstractFontEngine *QFontEnginePlugin::create(const QFontEngineInfo &info) + + Implemented in subclasses to create a new font engine that provides a font that + matches \a info. +*/ + +/*! + \fn QList<QFontEngineInfo> QFontEnginePlugin::availableFontEngines() const + + Implemented in subclasses to return a list of QFontEngineInfo objects that represents all font + engines the plugin can create. +*/ + +class QAbstractFontEnginePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractFontEngine) +public: +}; + +//The <classname> class is|provides|contains|specifies... +/*! + \class QAbstractFontEngine + \preliminary + \brief The QAbstractFontEngine class is the base class for font engine plugins in Qt for Embedded Linux. + \since 4.3 + \ingroup qws + + \tableofcontents + + QAbstractFontEngine is implemented by font engine plugins through QFontEnginePlugin. + + \sa QFontEnginePlugin, QFontEngineInfo +*/ + +/*! + \enum QAbstractFontEngine::Capability + + This enum describes the capabilities of a font engine. + + \value CanRenderGlyphs_Gray The font engine can render individual glyphs into 8 bpp images. + \value CanRenderGlyphs_Mono The font engine can render individual glyphs into 1 bpp images. + \value CanRenderGlyphs The font engine can render individual glyphs into images. + \value CanOutlineGlyphs The font engine can convert glyphs to painter paths. +*/ + +/*! + \enum QAbstractFontEngine::FontProperty + + This enum describes the properties of a font provided by a font engine. + + \value Ascent The ascent of the font, specified as a 26.6 fixed point value. + \value Descent The descent of the font, specified as a 26.6 fixed point value. + \value Leading The leading of the font, specified as a 26.6 fixed point value. + \value XHeight The 'x' height of the font, specified as a 26.6 fixed point value. + \value AverageCharWidth The average character width of the font, specified as a 26.6 fixed point value. + \value LineThickness The thickness of the underline and strikeout lines for the font, specified as a 26.6 fixed point value. + \value UnderlinePosition The distance from the base line to the underline position for the font, specified as a 26.6 fixed point value. + \value MaxCharWidth The width of the widest character in the font, specified as a 26.6 fixed point value. + \value MinLeftBearing The minimum left bearing of the font, specified as a 26.6 fixed point value. + \value MinRightBearing The maximum right bearing of the font, specified as a 26.6 fixed point value. + \value GlyphCount The number of glyphs in the font, specified as an integer value. + \value CacheGlyphsHint A boolean value specifying whether rendered glyphs should be cached by Qt. + \value OutlineGlyphsHint A boolean value specifying whether the font engine prefers outline drawing over image rendering for uncached glyphs. +*/ + +/*! + \enum QAbstractFontEngine::TextShapingFlag + + This enum describes flags controlling conversion of characters to glyphs and their metrics. + + \value RightToLeft The text is used in a right-to-left context. + \value ReturnDesignMetrics Return font design metrics instead of pixel metrics. +*/ + +/*! + \typedef QAbstractFontEngine::Fixed + + This type is \c int, interpreted as a 26.6 fixed point value. +*/ + +/*! + \class QAbstractFontEngine::GlyphMetrics + \brief QAbstractFontEngine::GlyphMetrics defines the metrics of a single glyph. + \preliminary + \since 4.3 +*/ + +/*! + \variable QAbstractFontEngine::GlyphMetrics::x + + The horizontal offset from the origin. +*/ + +/*! + \fn QAbstractFontEngine::GlyphMetrics::GlyphMetrics() + + Constructs an empty glyph metrics object with all values + set to zero. +*/ + +/*! + \variable QAbstractFontEngine::GlyphMetrics::y + + The vertical offset from the origin (baseline). +*/ + +/*! + \variable QAbstractFontEngine::GlyphMetrics::width + + The width of the glyph. +*/ + +/*! + \variable QAbstractFontEngine::GlyphMetrics::height + + The height of the glyph. +*/ + +/*! + \variable QAbstractFontEngine::GlyphMetrics::advance + + The advance of the glyph. +*/ + +/*! + \class QAbstractFontEngine::FixedPoint + \brief QAbstractFontEngine::FixedPoint defines a point in the place using 26.6 fixed point precision. + \preliminary + \since 4.3 +*/ + +/*! + \variable QAbstractFontEngine::FixedPoint::x + + The x coordinate of this point. +*/ + +/*! + \variable QAbstractFontEngine::FixedPoint::y + + The y coordinate of this point. +*/ + +/*! + Constructs a new QAbstractFontEngine with the given \a parent. +*/ +QAbstractFontEngine::QAbstractFontEngine(QObject *parent) + : QObject(*new QAbstractFontEnginePrivate, parent) +{ +} + +/*! + Destroys this QAbstractFontEngine object. +*/ +QAbstractFontEngine::~QAbstractFontEngine() +{ +} + +/*! + \fn QAbstractFontEngine::Capabilities QAbstractFontEngine::capabilities() const + + Implemented in subclasses to specify the font engine's capabilities. The return value + may be cached by the caller and is expected not to change during the lifetime of the + font engine. +*/ + +/*! + \fn QVariant QAbstractFontEngine::fontProperty(FontProperty property) const + + Implemented in subclasses to return the value of the font attribute \a property. The return + value may be cached by the caller and is expected not to change during the lifetime of the font + engine. +*/ + +/*! + \fn bool QAbstractFontEngine::convertStringToGlyphIndices(const QChar *string, int length, uint *glyphs, int *numGlyphs, TextShapingFlags flags) const + + Implemented in subclasses to convert the characters specified by \a string and \a length to + glyph indicies, using \a flags. The glyph indicies should be returned in the \a glyphs array + provided by the caller. The maximum size of \a glyphs is specified by the value pointed to by \a + numGlyphs. If successful, the subclass implementation sets the value pointed to by \a numGlyphs + to the actual number of glyph indices generated, and returns true. Otherwise, e.g. if there is + not enough space in the provided \a glyphs array, it should set \a numGlyphs to the number of + glyphs needed for the conversion and return false. +*/ + +/*! + \fn void QAbstractFontEngine::getGlyphAdvances(const uint *glyphs, int numGlyphs, Fixed *advances, TextShapingFlags flags) const + + Implemented in subclasses to retrieve the advances of the array specified by \a glyphs and \a + numGlyphs, using \a flags. The result is returned in \a advances, which is allocated by the + caller and contains \a numGlyphs elements. +*/ + +/*! + \fn QAbstractFontEngine::GlyphMetrics QAbstractFontEngine::glyphMetrics(uint glyph) const + + Implemented in subclass to return the metrics for \a glyph. +*/ + +/*! + Implemented in subclasses to render the specified \a glyph into a \a buffer with the given \a depth , + \a bytesPerLine and \a height. + + Returns true if rendering succeeded, false otherwise. +*/ +bool QAbstractFontEngine::renderGlyph(uint glyph, int depth, int bytesPerLine, int height, uchar *buffer) +{ + Q_UNUSED(glyph) + Q_UNUSED(depth) + Q_UNUSED(bytesPerLine) + Q_UNUSED(height) + Q_UNUSED(buffer) + qWarning("QAbstractFontEngine: renderGlyph is not implemented in font plugin!"); + return false; +} + +/*! + Implemented in subclasses to add the outline of the glyphs specified by \a glyphs and \a + numGlyphs at the specified \a positions to the painter path \a path. +*/ +void QAbstractFontEngine::addGlyphOutlinesToPath(uint *glyphs, int numGlyphs, FixedPoint *positions, QPainterPath *path) +{ + Q_UNUSED(glyphs) + Q_UNUSED(numGlyphs) + Q_UNUSED(positions) + Q_UNUSED(path) + qWarning("QAbstractFontEngine: addGlyphOutlinesToPath is not implemented in font plugin!"); +} + +/* +bool QAbstractFontEngine::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension) + return false; +} + +QVariant QAbstractFontEngine::extension(Extension extension, const QVariant &argument) +{ + Q_UNUSED(argument) + Q_UNUSED(extension) + return QVariant(); +} +*/ + +QProxyFontEngine::QProxyFontEngine(QAbstractFontEngine *customEngine, const QFontDef &def) + : engine(customEngine) +{ + fontDef = def; + engineCapabilities = engine->capabilities(); +} + +QProxyFontEngine::~QProxyFontEngine() +{ + delete engine; +} + +bool QProxyFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + + QVarLengthArray<uint> glyphIndicies(*nglyphs); + if (!engine->convertStringToGlyphIndices(str, len, glyphIndicies.data(), nglyphs, QAbstractFontEngine::TextShapingFlags(int(flags)))) + return false; + + // ### use memcopy instead + for (int i = 0; i < *nglyphs; ++i) { + glyphs->glyphs[i] = glyphIndicies[i]; + } + glyphs->numGlyphs = *nglyphs; + + recalcAdvances(glyphs, flags); + return true; +} + +void QProxyFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + const int nglyphs = glyphs->numGlyphs; + + QVarLengthArray<QAbstractFontEngine::Fixed> advances(nglyphs); + engine->getGlyphAdvances(glyphs->glyphs, nglyphs, advances.data(), QAbstractFontEngine::TextShapingFlags(int(flags))); + + + // ### use memcopy instead + for (int i = 0; i < nglyphs; ++i) { + glyphs->advances_x[i] = QFixed::fromFixed(advances[i]); + glyphs->advances_y[i] = 0; + } +} + + +static QImage alphaMapFromPath(QFontEngine *fe, glyph_t glyph) +{ + glyph_metrics_t gm = fe->boundingBox(glyph); + int glyph_x = qFloor(gm.x.toReal()); + int glyph_y = qFloor(gm.y.toReal()); + int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x; + int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y; + + if (glyph_width <= 0 || glyph_height <= 0) + return QImage(); + QFixedPoint pt; + pt.x = 0; + pt.y = -glyph_y; // the baseline + QPainterPath path; + QImage im(glyph_width + qAbs(glyph_x) + 4, glyph_height, QImage::Format_ARGB32_Premultiplied); + im.fill(Qt::transparent); + QPainter p(&im); + p.setRenderHint(QPainter::Antialiasing); + fe->addGlyphsToPath(&glyph, &pt, 1, &path, 0); + p.setPen(Qt::NoPen); + p.setBrush(Qt::black); + p.drawPath(path); + p.end(); + + QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); + QVector<QRgb> colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + for (int y=0; y<im.height(); ++y) { + uchar *dst = (uchar *) indexed.scanLine(y); + uint *src = (uint *) im.scanLine(y); + for (int x=0; x<im.width(); ++x) + dst[x] = qAlpha(src[x]); + } + + return indexed; +} + + +QImage QProxyFontEngine::alphaMapForGlyph(glyph_t glyph) +{ + if (!(engineCapabilities & QAbstractFontEngine::CanRenderGlyphs_Gray)) + return alphaMapFromPath(this, glyph); + + QAbstractFontEngine::GlyphMetrics metrics = engine->glyphMetrics(glyph); + if (metrics.width <= 0 || metrics.height <= 0) + return QImage(); + + QImage img(metrics.width >> 6, metrics.height >> 6, QImage::Format_Indexed8); + + // ### we should have QImage::Format_GrayScale8 + static QVector<QRgb> colorMap; + if (colorMap.isEmpty()) { + colorMap.resize(256); + for (int i=0; i<256; ++i) + colorMap[i] = qRgba(0, 0, 0, i); + } + + img.setColorTable(colorMap); + + engine->renderGlyph(glyph, /*depth*/8, img.bytesPerLine(), img.height(), img.bits()); + + return img; +} + +void QProxyFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (engineCapabilities & QAbstractFontEngine::CanOutlineGlyphs) + engine->addGlyphOutlinesToPath(glyphs, nglyphs, reinterpret_cast<QAbstractFontEngine::FixedPoint *>(positions), path); + else + QFontEngine::addGlyphsToPath(glyphs, positions, nglyphs, path, flags); +} + +glyph_metrics_t QProxyFontEngine::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) + w += glyphs.effectiveAdvance(i); + + return glyph_metrics_t(0, -ascent(), w, ascent() + descent(), w, 0); +} + +glyph_metrics_t QProxyFontEngine::boundingBox(glyph_t glyph) +{ + glyph_metrics_t m; + + QAbstractFontEngine::GlyphMetrics metrics = engine->glyphMetrics(glyph); + m.x = QFixed::fromFixed(metrics.x); + m.y = QFixed::fromFixed(metrics.y); + m.width = QFixed::fromFixed(metrics.width); + m.height = QFixed::fromFixed(metrics.height); + m.xoff = QFixed::fromFixed(metrics.advance); + + return m; +} + +QFixed QProxyFontEngine::ascent() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::Ascent).toInt()); +} + +QFixed QProxyFontEngine::descent() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::Descent).toInt()); +} + +QFixed QProxyFontEngine::leading() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::Leading).toInt()); +} + +QFixed QProxyFontEngine::xHeight() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::XHeight).toInt()); +} + +QFixed QProxyFontEngine::averageCharWidth() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::AverageCharWidth).toInt()); +} + +QFixed QProxyFontEngine::lineThickness() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::LineThickness).toInt()); +} + +QFixed QProxyFontEngine::underlinePosition() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::UnderlinePosition).toInt()); +} + +qreal QProxyFontEngine::maxCharWidth() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::MaxCharWidth).toInt()).toReal(); +} + +qreal QProxyFontEngine::minLeftBearing() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::MinLeftBearing).toInt()).toReal(); +} + +qreal QProxyFontEngine::minRightBearing() const +{ + return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::MinRightBearing).toInt()).toReal(); +} + +int QProxyFontEngine::glyphCount() const +{ + return engine->fontProperty(QAbstractFontEngine::GlyphCount).toInt(); +} + +bool QProxyFontEngine::canRender(const QChar *string, int len) +{ + QVarLengthArray<uint> glyphs(len); + int numGlyphs = len; + + if (!engine->convertStringToGlyphIndices(string, len, glyphs.data(), &numGlyphs, /*flags*/0)) + return false; + + for (int i = 0; i < numGlyphs; ++i) + if (!glyphs[i]) + return false; + + return true; +} + +void QProxyFontEngine::draw(QPaintEngine *p, qreal _x, qreal _y, const QTextItemInt &si) +{ + +} + +/* + * This is only called when we use the proxy fontengine directly (without sharing the rendered + * glyphs). So we prefer outline rendering over rendering of unshared glyphs. That decision is + * done in qfontdatabase_qws.cpp by looking at the ShareGlyphsHint and the pixel size of the font. + */ +bool QProxyFontEngine::drawAsOutline() const +{ + if (!(engineCapabilities & QAbstractFontEngine::CanOutlineGlyphs)) + return false; + + QVariant outlineHint = engine->fontProperty(QAbstractFontEngine::OutlineGlyphsHint); + return !outlineHint.isValid() || outlineHint.toBool(); +} + +QT_END_NAMESPACE diff --git a/src/gui/text/qabstractfontengine_wayland.h b/src/gui/text/qabstractfontengine_wayland.h new file mode 100644 index 0000000000..b2eadd94a1 --- /dev/null +++ b/src/gui/text/qabstractfontengine_wayland.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#ifndef QABSTRACTFONTENGINE_QWS_H +#define QABSTRACTFONTENGINE_QWS_H + +#include <QtCore/qobject.h> +#include <QtCore/qhash.h> +#include <QtCore/qvariant.h> +#include <QtCore/qfactoryinterface.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/qfontdatabase.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QFontEngineInfoPrivate; + +class Q_GUI_EXPORT QFontEngineInfo +{ +public: + QDOC_PROPERTY(QString family READ family WRITE setFamily) + QDOC_PROPERTY(qreal pixelSize READ pixelSize WRITE setPixelSize) + QDOC_PROPERTY(int weight READ weight WRITE setWeight) + QDOC_PROPERTY(QFont::Style style READ style WRITE setStyle) + QDOC_PROPERTY(QList<QFontDatabase::WritingSystem> writingSystems READ writingSystems WRITE setWritingSystems) + + QFontEngineInfo(); + explicit QFontEngineInfo(const QString &family); + QFontEngineInfo(const QFontEngineInfo &other); + QFontEngineInfo &operator=(const QFontEngineInfo &other); + ~QFontEngineInfo(); + + void setFamily(const QString &name); + QString family() const; + + void setPixelSize(qreal size); + qreal pixelSize() const; + + void setWeight(int weight); + int weight() const; + + void setStyle(QFont::Style style); + QFont::Style style() const; + + QList<QFontDatabase::WritingSystem> writingSystems() const; + void setWritingSystems(const QList<QFontDatabase::WritingSystem> &writingSystems); + +private: + QFontEngineInfoPrivate *d; +}; + +class QAbstractFontEngine; + +struct Q_GUI_EXPORT QFontEngineFactoryInterface : public QFactoryInterface +{ + virtual QAbstractFontEngine *create(const QFontEngineInfo &info) = 0; + virtual QList<QFontEngineInfo> availableFontEngines() const = 0; +}; + +#define QFontEngineFactoryInterface_iid "com.trolltech.Qt.QFontEngineFactoryInterface" +Q_DECLARE_INTERFACE(QFontEngineFactoryInterface, QFontEngineFactoryInterface_iid) + +class QFontEnginePluginPrivate; + +class Q_GUI_EXPORT QFontEnginePlugin : public QObject, public QFontEngineFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QFontEngineFactoryInterface:QFactoryInterface) +public: + QFontEnginePlugin(const QString &foundry, QObject *parent = 0); + ~QFontEnginePlugin(); + + virtual QStringList keys() const; + + virtual QAbstractFontEngine *create(const QFontEngineInfo &info) = 0; + virtual QList<QFontEngineInfo> availableFontEngines() const = 0; + +private: + Q_DECLARE_PRIVATE(QFontEnginePlugin) + Q_DISABLE_COPY(QFontEnginePlugin) +}; + +class QAbstractFontEnginePrivate; + +class Q_GUI_EXPORT QAbstractFontEngine : public QObject +{ + Q_OBJECT +public: + enum Capability { + CanOutlineGlyphs = 1, + CanRenderGlyphs_Mono = 2, + CanRenderGlyphs_Gray = 4, + CanRenderGlyphs = CanRenderGlyphs_Mono | CanRenderGlyphs_Gray + }; + Q_DECLARE_FLAGS(Capabilities, Capability) + + explicit QAbstractFontEngine(QObject *parent = 0); + ~QAbstractFontEngine(); + + typedef int Fixed; // 26.6 + + struct FixedPoint + { + Fixed x; + Fixed y; + }; + + struct GlyphMetrics + { + inline GlyphMetrics() + : x(0), y(0), width(0), height(0), + advance(0) {} + Fixed x; + Fixed y; + Fixed width; + Fixed height; + Fixed advance; + }; + + enum FontProperty { + Ascent, + Descent, + Leading, + XHeight, + AverageCharWidth, + LineThickness, + UnderlinePosition, + MaxCharWidth, + MinLeftBearing, + MinRightBearing, + GlyphCount, + + // hints + CacheGlyphsHint, + OutlineGlyphsHint + }; + + // keep in sync with QTextEngine::ShaperFlag!! + enum TextShapingFlag { + RightToLeft = 0x0001, + ReturnDesignMetrics = 0x0002 + }; + Q_DECLARE_FLAGS(TextShapingFlags, TextShapingFlag) + + virtual Capabilities capabilities() const = 0; + virtual QVariant fontProperty(FontProperty property) const = 0; + + virtual bool convertStringToGlyphIndices(const QChar *string, int length, uint *glyphs, int *numGlyphs, TextShapingFlags flags) const = 0; + + virtual void getGlyphAdvances(const uint *glyphs, int numGlyphs, Fixed *advances, TextShapingFlags flags) const = 0; + + virtual GlyphMetrics glyphMetrics(uint glyph) const = 0; + + virtual bool renderGlyph(uint glyph, int depth, int bytesPerLine, int height, uchar *buffer); + + virtual void addGlyphOutlinesToPath(uint *glyphs, int numGlyphs, FixedPoint *positions, QPainterPath *path); + + /* + enum Extension { + GetTrueTypeTable + }; + + virtual bool supportsExtension(Extension extension) const; + virtual QVariant extension(Extension extension, const QVariant &argument = QVariant()); + */ + +private: + Q_DECLARE_PRIVATE(QAbstractFontEngine) + Q_DISABLE_COPY(QAbstractFontEngine) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractFontEngine::Capabilities) +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractFontEngine::TextShapingFlags) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index 6f624246b9..a952cf4c55 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -46,7 +46,7 @@ #include <QtCore/qstring.h> #include <QtCore/qsharedpointer.h> -#if defined(Q_WS_X11) || defined(Q_WS_QWS) +#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_WAYLAND) typedef struct FT_FaceRec_* FT_Face; #endif @@ -233,7 +233,7 @@ public: #ifdef Q_WS_MAC quint32 macFontID() const; #endif -#if defined(Q_WS_X11) || defined(Q_WS_QWS) +#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_WAYLAND) FT_Face freetypeFace() const; #endif diff --git a/src/gui/text/qfont_wayland.cpp b/src/gui/text/qfont_wayland.cpp new file mode 100644 index 0000000000..256de8447c --- /dev/null +++ b/src/gui/text/qfont_wayland.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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$ +** +****************************************************************************/ + +#define QT_FATAL_ASSERT + +#include "qplatformdefs.h" + +#include "qfont.h" +#include "qapplication.h" +#include "qfontinfo.h" +#include "qfontdatabase.h" +#include "qfontmetrics.h" +#include "qpaintdevice.h" +#include "qtextcodec.h" +#include "qiodevice.h" +#include "qhash.h" + +#include <private/qunicodetables_p.h> +#include "qfont_p.h" +#include "qfontengine_p.h" +#include "qtextengine_p.h" + +#include <time.h> +#include <stdlib.h> +#include <ctype.h> + +#define QFONTLOADER_DEBUG +#define QFONTLOADER_DEBUG_VERBOSE + +QT_BEGIN_NAMESPACE + +void QFont::initialize() +{ +} + +void QFont::cleanup() +{ + QFontCache::cleanup(); +} + +Qt::HANDLE QFont::handle() const +{ + return 0; +} + + +FT_Face QFont::freetypeFace() const +{ + return 0; +} + +QString QFont::rawName() const +{ + QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); + Q_ASSERT(engine != 0); + if (engine->type() == QFontEngine::Multi) + engine = static_cast<QFontEngineMulti *>(engine)->engine(0); + if (engine->type() == QFontEngine::XLFD) + return QString::fromLatin1(engine->name()); + return QString(); +} +struct QtFontDesc; + +void QFont::setRawName(const QString &name) +{ + detach(); +} + +QString QFont::lastResortFamily() const +{ + return QString::fromLatin1("Helvetica"); +} + +QString QFont::defaultFamily() const +{ + switch (d->request.styleHint) { + case QFont::Times: + return QString::fromLatin1("Times"); + + case QFont::Courier: + return QString::fromLatin1("Courier"); + + case QFont::Decorative: + return QString::fromLatin1("Old English"); + + case QFont::Helvetica: + case QFont::System: + default: + return QString::fromLatin1("Helvetica"); + } +} + +/* + Returns a last resort raw font name for the font matching algorithm. + This is used if even the last resort family is not available. It + returns \e something, almost no matter what. The current + implementation tries a wide variety of common fonts, returning the + first one it finds. The implementation may change at any time. +*/ +static const char * const tryFonts[] = { + "-*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*", + "-*-courier-medium-r-*-*-*-120-*-*-*-*-*-*", + "-*-times-medium-r-*-*-*-120-*-*-*-*-*-*", + "-*-lucida-medium-r-*-*-*-120-*-*-*-*-*-*", + "-*-helvetica-*-*-*-*-*-120-*-*-*-*-*-*", + "-*-courier-*-*-*-*-*-120-*-*-*-*-*-*", + "-*-times-*-*-*-*-*-120-*-*-*-*-*-*", + "-*-lucida-*-*-*-*-*-120-*-*-*-*-*-*", + "-*-helvetica-*-*-*-*-*-*-*-*-*-*-*-*", + "-*-courier-*-*-*-*-*-*-*-*-*-*-*-*", + "-*-times-*-*-*-*-*-*-*-*-*-*-*-*", + "-*-lucida-*-*-*-*-*-*-*-*-*-*-*-*", + "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-*", + "6x13", + "7x13", + "8x13", + "9x15", + "fixed", + 0 +}; + +// Returns true if the font exists, false otherwise +static bool fontExists(const QString &fontName) +{ + return false; +} + +QString QFont::lastResortFont() const +{ + static QString last; + + // already found + if (! last.isNull()) + return last; + + int i = 0; + const char* f; + + while ((f = tryFonts[i])) { + last = QString::fromLatin1(f); + + if (fontExists(last)) + return last; + + i++; + } + +#if defined(CHECK_NULL) + qFatal("QFontPrivate::lastResortFont: Cannot find any reasonable font"); +#endif + return last; +} + +QT_END_NAMESPACE diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index ff29462d3f..4cc90dcc9d 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -153,7 +153,7 @@ struct QtFontSize QtFontEncoding *encodingID(int id, uint xpoint = 0, uint xres = 0, uint yres = 0, uint avgwidth = 0, bool add = false); #endif // Q_WS_X11 -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) || defined(Q_WS_WAYLAND) QByteArray fileName; int fileIndex; #endif // defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) @@ -255,7 +255,7 @@ struct QtFontStyle const char *weightName; const char *setwidthName; #endif // Q_WS_X11 -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) || defined(Q_WS_WAYLAND) bool antialiased; #endif @@ -662,7 +662,7 @@ public: bool loadFromCache(const QString &fontPath); void addQPF2File(const QByteArray &file); #endif // Q_WS_QWS -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) +#if defined(Q_WS_QWS) || (defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE)) || defined(Q_WS_WAYLAND) void addFont(const QString &familyname, const char *foundryname, int weight, bool italic, int pixelSize, const QByteArray &file, int fileIndex, bool antialiased, @@ -884,7 +884,7 @@ static const int scriptForWritingSystem[] = { }; -#if defined Q_WS_QWS || (defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG)) || defined(Q_WS_WIN) +#if defined Q_WS_QWS || (defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG)) || defined(Q_WS_WIN) || defined(Q_WS_WAYLAND) static inline bool requiresOpenType(int writingSystem) { return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala) @@ -1050,6 +1050,8 @@ QT_BEGIN_INCLUDE_NAMESPACE # include "qfontdatabase_qws.cpp" #elif defined(Q_OS_SYMBIAN) # include "qfontdatabase_s60.cpp" +#elif defined(Q_WS_WAYLAND) +# include "qfontdatabase_wayland.cpp" #endif QT_END_INCLUDE_NAMESPACE diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h index 719242cb60..e00658fcc1 100644 --- a/src/gui/text/qfontdatabase.h +++ b/src/gui/text/qfontdatabase.h @@ -152,7 +152,7 @@ public: private: static void createDatabase(); static void parseFontName(const QString &name, QString &foundry, QString &family); -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) || defined(Q_WS_WAYLAND) static QFontEngine *findFont(int script, const QFontPrivate *fp, const QFontDef &request); #endif static void load(const QFontPrivate *d, int script); diff --git a/src/gui/text/qfontdatabase_wayland.cpp b/src/gui/text/qfontdatabase_wayland.cpp new file mode 100644 index 0000000000..f2e396f3ea --- /dev/null +++ b/src/gui/text/qfontdatabase_wayland.cpp @@ -0,0 +1,662 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qdir.h" +#include "qlibraryinfo.h" +#include "qabstractfileengine.h" +#include <QtCore/qsettings.h> +#include "qfontengine_ft_p.h" + +#include <ft2build.h> +#include FT_FREETYPE_H + +#include "private/qfactoryloader_p.h" +#include "private/qcore_unix_p.h" // overrides QT_OPEN +#include "qabstractfontengine_wayland.h" +#include "qabstractfontengine_p.h" +#include <qdatetime.h> +#include "qplatformdefs.h" + +// for mmap +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +class QFontEngineFT; + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_LIBRARY +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QFontEngineFactoryInterface_iid, QLatin1String("/fontengines"), Qt::CaseInsensitive)) +#endif + +const quint8 DatabaseVersion = 4; + +// QFontDatabasePrivate::addFont() went into qfontdatabase.cpp + +// QFontDatabasePrivate::addTTFile() went into qfontdatabase.cpp + +static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt); + +extern QString qws_fontCacheDir(); + +/*! + \internal +*/ + +static QString qwsFontPath() +{ + QString fontpath = QString::fromLocal8Bit(qgetenv("QT_QWS_FONTDIR")); + if (fontpath.isEmpty()) { +#ifdef QT_FONTS_ARE_RESOURCES + fontpath = QLatin1String(":/qt/fonts"); +#else +#ifndef QT_NO_SETTINGS + fontpath = QLibraryInfo::location(QLibraryInfo::LibrariesPath); + fontpath += QLatin1String("/fonts"); +#else + fontpath = QLatin1String("/lib/fonts"); +#endif +#endif //QT_FONTS_ARE_RESOURCES + } + + return fontpath; +} + +#if defined(QFONTDATABASE_DEBUG) && defined(QT_FONTS_ARE_RESOURCES) +class FriendlyResource : public QResource +{ +public: + bool isDir () const { return QResource::isDir(); } + bool isFile () const { return QResource::isFile(); } + QStringList children () const { return QResource::children(); } +}; +#endif +/*! + \internal +*/ +static void initializeDb() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db || db->count) + return; + + QString fontpath = qwsFontPath(); +#ifndef QT_FONTS_ARE_RESOURCES + QString fontDirFile = fontpath + QLatin1String("/fontdir"); + + if(!QFile::exists(fontpath)) { + qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", + fontpath.toLocal8Bit().constData()); + } + + if (db->reregisterAppFonts) { + db->reregisterAppFonts = false; + for (int i = 0; i < db->applicationFonts.count(); ++i) + if (!db->applicationFonts.at(i).families.isEmpty()) { + registerFont(&db->applicationFonts[i]); + } + } + + QString dbFileName = qws_fontCacheDir() + QLatin1String("/fontdb"); + + QFile binaryDb(dbFileName + QLatin1String(".tmp")); + binaryDb.open(QIODevice::WriteOnly | QIODevice::Truncate); + + // Load in font definition file + FILE* fontdef=fopen(fontDirFile.toLocal8Bit().constData(),"r"); + if (fontdef) { + char buf[200]=""; + char name[200]=""; + char render[200]=""; + char file[200]=""; + char isitalic[10]=""; + char flags[10]=""; + do { + fgets(buf,200,fontdef); + if (buf[0] != '#') { + int weight=50; + int size=0; + sscanf(buf,"%s %s %s %s %d %d %s",name,file,render,isitalic,&weight,&size,flags); + QString filename; + if (file[0] != '/') + filename.append(fontpath).append(QLatin1Char('/')); + filename += QLatin1String(file); + bool italic = isitalic[0] == 'y'; + bool smooth = QByteArray(flags).contains('s'); + } + } while (!feof(fontdef)); + fclose(fontdef); + } + + + QDir dir(fontpath, QLatin1String("*.qpf")); + for (int i=0; i<int(dir.count()); i++) { + int u0 = dir[i].indexOf(QLatin1Char('_')); + int u1 = dir[i].indexOf(QLatin1Char('_'), u0+1); + int u2 = dir[i].indexOf(QLatin1Char('_'), u1+1); + int u3 = dir[i].indexOf(QLatin1Char('.'), u1+1); + if (u2 < 0) u2 = u3; + + QString familyname = dir[i].left(u0); + int pixelSize = dir[i].mid(u0+1,u1-u0-1).toInt()/10; + bool italic = dir[i].mid(u2-1,1) == QLatin1String("i"); + int weight = dir[i].mid(u1+1,u2-u1-1-(italic?1:0)).toInt(); + } + +#ifndef QT_NO_FREETYPE + dir.setNameFilters(QStringList() << QLatin1String("*.ttf") + << QLatin1String("*.ttc") << QLatin1String("*.pfa") + << QLatin1String("*.pfb")); + dir.refresh(); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); +// qDebug() << "looking at" << file; + db->addTTFile(file); + } +#endif + +#else //QT_FONTS_ARE_RESOURCES +#ifdef QFONTDATABASE_DEBUG + { + QResource fontdir(fontpath); + FriendlyResource *fr = static_cast<FriendlyResource*>(&fontdir); + qDebug() << "fontdir" << fr->isValid() << fr->isDir() << fr->children(); + + } +#endif + QDir dir(fontpath, QLatin1String("*.qpf2")); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); + //qDebug() << "looking at" << file; + db->addQPF2File(file); + } +#endif //QT_FONTS_ARE_RESOURCES + + +#ifdef QFONTDATABASE_DEBUG + // print the database + for (int f = 0; f < db->count; f++) { + QtFontFamily *family = db->families[f]; + FD_DEBUG("'%s' %s", qPrintable(family->name), (family->fixedPitch ? "fixed" : "")); +#if 0 + for (int i = 0; i < QFont::LastPrivateScript; ++i) { + FD_DEBUG("\t%s: %s", qPrintable(QFontDatabase::scriptName((QFont::Script) i)), + ((family->scripts[i] & QtFontFamily::Supported) ? "Supported" : + (family->scripts[i] & QtFontFamily::UnSupported) == QtFontFamily::UnSupported ? + "UnSupported" : "Unknown")); + } +#endif + + for (int fd = 0; fd < family->count; fd++) { + QtFontFoundry *foundry = family->foundries[fd]; + FD_DEBUG("\t\t'%s'", qPrintable(foundry->name)); + for (int s = 0; s < foundry->count; s++) { + QtFontStyle *style = foundry->styles[s]; + FD_DEBUG("\t\t\tstyle: style=%d weight=%d\n" + "\t\t\tstretch=%d", + style->key.style, style->key.weight, + style->key.stretch); + if (style->smoothScalable) + FD_DEBUG("\t\t\t\tsmooth scalable"); + else if (style->bitmapScalable) + FD_DEBUG("\t\t\t\tbitmap scalable"); + if (style->pixelSizes) { + FD_DEBUG("\t\t\t\t%d pixel sizes", style->count); + for (int z = 0; z < style->count; ++z) { + QtFontSize *size = style->pixelSizes + z; + FD_DEBUG("\t\t\t\t size %5d", + size->pixelSize); + } + } + } + } + } +#endif // QFONTDATABASE_DEBUG + +#ifndef QT_NO_LIBRARY + QStringList pluginFoundries = loader()->keys(); +// qDebug() << "plugin foundries:" << pluginFoundries; + for (int i = 0; i < pluginFoundries.count(); ++i) { + const QString foundry(pluginFoundries.at(i)); + + QFontEngineFactoryInterface *factory = qobject_cast<QFontEngineFactoryInterface *>(loader()->instance(foundry)); + if (!factory) { + qDebug() << "Could not load plugin for foundry" << foundry; + continue; + } + + QList<QFontEngineInfo> fonts = factory->availableFontEngines(); + for (int i = 0; i < fonts.count(); ++i) { + QFontEngineInfo info = fonts.at(i); + + int weight = info.weight(); + if (weight <= 0) + weight = QFont::Normal; + + db->addFont(info.family(), foundry.toLatin1().constData(), + weight, info.style() != QFont::StyleNormal, + qRound(info.pixelSize()), /*file*/QByteArray(), + /*fileIndex*/0, /*antiAliased*/true, + info.writingSystems()); + } + } +#endif + + { + bool coveredWritingSystems[QFontDatabase::WritingSystemsCount] = { 0 }; + + for (int i = 0; i < db->count; ++i) { + QtFontFamily *family = db->families[i]; + bool add = false; + if (family->count == 0) + continue; + for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) { + if (coveredWritingSystems[ws]) + continue; + if (family->writingSystems[ws] & QtFontFamily::Supported) { + coveredWritingSystems[ws] = true; + add = true; + } + } + } + } +} + +// called from qwindowsystem_qws.cpp +void qt_qws_init_fontdb() +{ + initializeDb(); +} + +#ifndef QT_NO_SETTINGS +// called from qapplication_qws.cpp +void qt_applyFontDatabaseSettings(const QSettings &settings) +{ + initializeDb(); + QFontDatabasePrivate *db = privateDb(); +} +#endif // QT_NO_SETTINGS + +static inline void load(const QString & = QString(), int = -1) +{ + initializeDb(); +} + +#ifndef QT_NO_FREETYPE + +#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105 +#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem) +#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem) +#else +#define X_SIZE(face,i) ((face)->available_sizes[i].width << 6) +#define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6) +#endif + +#endif // QT_NO_FREETYPE + +static +QFontEngine *loadSingleEngine(int script, const QFontPrivate *fp, + const QFontDef &request, + QtFontFamily *family, QtFontFoundry *foundry, + QtFontStyle *style, QtFontSize *size) +{ + Q_UNUSED(script); + Q_UNUSED(fp); + Q_UNUSED(family); + Q_ASSERT(size); + + int pixelSize = size->pixelSize; + if (!pixelSize || (style->smoothScalable && pixelSize == SMOOTH_SCALABLE)) + pixelSize = request.pixelSize; + + int f = ::open(size->fileName, O_RDONLY, 0); + if (f >= 0) { + qDebug() << "fontengine is not valid!"; + } + if ( foundry->name != QLatin1String("qt") ) { ///#### is this the best way???? + QFontDef def = request; + def.pixelSize = pixelSize; + + QScopedPointer<QFontEngine> engine; + +#ifndef QT_NO_LIBRARY + QFontEngineFactoryInterface *factory = qobject_cast<QFontEngineFactoryInterface *>(loader()->instance(foundry->name)); + if (factory) { + QFontEngineInfo info; + info.setFamily(request.family); + info.setPixelSize(request.pixelSize); + info.setStyle(QFont::Style(request.style)); + info.setWeight(request.weight); + // #### antialiased + + QAbstractFontEngine *customEngine = factory->create(info); + if (customEngine) { + engine.reset(new QProxyFontEngine(customEngine, def)); + } + } +#endif // QT_NO_LIBRARY + if ((engine.isNull())) { + QFontEngine::FaceId faceId; + faceId.index = size->fileIndex; + } + if (!engine.isNull()) { + return engine.take(); + } + } else + { + } + return new QFontEngineBox(pixelSize); +} + +static +QFontEngine *loadEngine(int script, const QFontPrivate *fp, + const QFontDef &request, + QtFontFamily *family, QtFontFoundry *foundry, + QtFontStyle *style, QtFontSize *size) +{ + QScopedPointer<QFontEngine> engine(loadSingleEngine(script, fp, request, family, foundry, + style, size)); + if (!engine.isNull() + && script == QUnicodeTables::Common + && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) { + + engine.take(); + } + return engine.take(); +} + +static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) +{ + QFontDatabasePrivate *db = privateDb(); + db->reregisterAppFonts = true; +} + +bool QFontDatabase::removeApplicationFont(int handle) +{ + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (handle < 0 || handle >= db->applicationFonts.count()) + return false; + + db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont(); + + db->reregisterAppFonts = true; + db->invalidate(); + return true; +} + +bool QFontDatabase::removeAllApplicationFonts() +{ + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (db->applicationFonts.isEmpty()) + return false; + + db->applicationFonts.clear(); + db->invalidate(); + return true; +} + +bool QFontDatabase::supportsThreadedFontRendering() +{ + return true; +} + +/*! + \internal +*/ +QFontEngine * +QFontDatabase::findFont(int script, const QFontPrivate *fp, + const QFontDef &request) +{ + QMutexLocker locker(fontDatabaseMutex()); + + const int force_encoding_id = -1; + + if (!privateDb()->count) + initializeDb(); + + QScopedPointer<QFontEngine> fe; + if (fp) { + if (fp->rawMode) { + fe.reset(loadEngine(script, fp, request, 0, 0, 0, 0)); + + // if we fail to load the rawmode font, use a 12pixel box engine instead + if (fe.isNull()) + fe.reset(new QFontEngineBox(12)); + return fe.take(); + } + + QFontCache::Key key(request, script); + fe.reset(QFontCache::instance()->findEngine(key)); + if (! fe.isNull()) + return fe.take(); + } + + QString family_name, foundry_name; + QtFontStyle::Key styleKey; + styleKey.style = request.style; + styleKey.weight = request.weight; + styleKey.stretch = request.stretch; + char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p'; + + parseFontName(request.family, foundry_name, family_name); + + FM_DEBUG("QFontDatabase::findFont\n" + " request:\n" + " family: %s [%s], script: %d\n" + " weight: %d, style: %d\n" + " stretch: %d\n" + " pixelSize: %g\n" + " pitch: %c", + family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(), + foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), + script, request.weight, request.style, request.stretch, request.pixelSize, pitch); + + if (qt_enable_test_font && request.family == QLatin1String("__Qt__Box__Engine__")) { + fe.reset(new QTestFontEngine(request.pixelSize)); + fe->fontDef = request; + } + + if (fe.isNull()) + { + QtFontDesc desc; + match(script, request, family_name, foundry_name, force_encoding_id, &desc); + + if (desc.family != 0 && desc.foundry != 0 && desc.style != 0 + ) { + FM_DEBUG(" BEST:\n" + " family: %s [%s]\n" + " weight: %d, style: %d\n" + " stretch: %d\n" + " pixelSize: %d\n" + " pitch: %c\n" + " encoding: %d\n", + desc.family->name.toLatin1().constData(), + desc.foundry->name.isEmpty() ? "-- none --" : desc.foundry->name.toLatin1().constData(), + desc.style->key.weight, desc.style->key.style, + desc.style->key.stretch, desc.size ? desc.size->pixelSize : 0xffff, + 'p', 0 + ); + + fe.reset(loadEngine(script, fp, request, desc.family, desc.foundry, desc.style, desc.size + )); + } else { + FM_DEBUG(" NO MATCH FOUND\n"); + } + } + +#ifndef QT_NO_FREETYPE + if (! fe.isNull()) { + if (scriptRequiresOpenType(script) && fe->type() == QFontEngine::Freetype) { + HB_Face hbFace = static_cast<QFontEngineFT *>(fe.data())->harfbuzzFace(); + if (!hbFace || !hbFace->supported_scripts[script]) { + FM_DEBUG(" OpenType support missing for script\n"); + fe.reset(0); + } + } + } +#endif + + if (! fe.isNull()) { + if (fp) { + QFontDef def = request; + if (def.family.isEmpty()) { + def.family = fp->request.family; + def.family = def.family.left(def.family.indexOf(QLatin1Char(','))); + } + QFontCache::Key key(def, script); + QFontCache::instance()->insertEngine(key, fe.data()); + } + } + + if (fe.isNull()) { + if (!request.family.isEmpty()) + return 0; + + FM_DEBUG("returning box engine"); + + fe.reset(new QFontEngineBox(request.pixelSize)); + + if (fp) { + QFontCache::Key key(request, script); + QFontCache::instance()->insertEngine(key, fe.data()); + } + } + + if (fp && fp->dpi > 0) { + fe->fontDef.pointSize = qreal(double((fe->fontDef.pixelSize * 72) / fp->dpi)); + } else { + fe->fontDef.pointSize = request.pointSize; + } + + return fe.take(); +} + +void QFontDatabase::load(const QFontPrivate *d, int script) +{ + QFontDef req = d->request; + + if (req.pixelSize == -1) + req.pixelSize = qRound(req.pointSize*d->dpi/72); + if (req.pointSize < 0) + req.pointSize = req.pixelSize*72.0/d->dpi; + + if (!d->engineData) { + QFontCache::Key key(req, script); + + // look for the requested font in the engine data cache + d->engineData = QFontCache::instance()->findEngineData(key); + + if (!d->engineData) { + // create a new one + d->engineData = new QFontEngineData; + QT_TRY { + QFontCache::instance()->insertEngineData(key, d->engineData); + } QT_CATCH(...) { + delete d->engineData; + d->engineData = 0; + QT_RETHROW; + } + } else { + d->engineData->ref.ref(); + } + } + + // the cached engineData could have already loaded the engine we want + if (d->engineData->engines[script]) return; + + // double scale = 1.0; // ### TODO: fix the scale calculations + + // list of families to try + QStringList family_list; + + if (!req.family.isEmpty()) { + family_list = req.family.split(QLatin1Char(',')); + + // append the substitute list for each family in family_list + QStringList subs_list; + QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd(); + for (; it != end; ++it) + subs_list += QFont::substitutes(*it); + family_list += subs_list; + + // append the default fallback font for the specified script + // family_list << ... ; ########### + + // add the default family + QString defaultFamily = QApplication::font().family(); + if (! family_list.contains(defaultFamily)) + family_list << defaultFamily; + + // add QFont::defaultFamily() to the list, for compatibility with + // previous versions + family_list << QApplication::font().defaultFamily(); + } + + // null family means find the first font matching the specified script + family_list << QString(); + + // load the font + QFontEngine *engine = 0; + QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd(); + for (; !engine && it != end; ++it) { + req.family = *it; + + engine = QFontDatabase::findFont(script, d, req); + if (engine && (engine->type()==QFontEngine::Box) && !req.family.isEmpty()) + engine = 0; + } + + engine->ref.ref(); + d->engineData->engines[script] = engine; +} + + +QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index 922acfb34c..9818189823 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -235,7 +235,7 @@ public: bool symbol; mutable HB_FontRec hbFont; mutable HB_Face hbFace; -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) || defined(Q_WS_WAYLAND) struct KernPair { uint left_right; QFixed adjust; diff --git a/src/gui/text/qfontengine_wayland.cpp b/src/gui/text/qfontengine_wayland.cpp new file mode 100644 index 0000000000..414ef8ebf0 --- /dev/null +++ b/src/gui/text/qfontengine_wayland.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qfontengine_p.h" +#include <private/qunicodetables_p.h> +#include <qwsdisplay_qws.h> +#include <qvarlengtharray.h> +#include <private/qpainter_p.h> +#include <private/qpaintengine_raster_p.h> +#include <private/qpdf_p.h> +#include "qtextengine_p.h" +#include "private/qcore_unix_p.h" // overrides QT_OPEN + +#include <qdebug.h> + + +#ifndef QT_NO_QWS_QPF + +#include "qfile.h" +#include "qdir.h" + +#define QT_USE_MMAP +#include <stdlib.h> + +#ifdef QT_USE_MMAP +// for mmap +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +# if defined(QT_LINUXBASE) && !defined(MAP_FILE) + // LSB 3.2 does not define MAP_FILE +# define MAP_FILE 0 +# endif + +#endif + +#endif // QT_NO_QWS_QPF + +QT_BEGIN_NAMESPACE + +QT_END_NAMESPACE diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp index e49f5b4554..22e2d7ec8c 100644 --- a/src/gui/text/qfontsubset.cpp +++ b/src/gui/text/qfontsubset.cpp @@ -50,7 +50,7 @@ #endif #ifndef QT_NO_FREETYPE -#if defined(Q_WS_X11) || defined(Q_WS_QWS) +#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_WAYLAND) # include "private/qfontengine_ft_p.h" #endif #include <ft2build.h> diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index 9ec314299c..068a437319 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -129,6 +129,20 @@ symbian { } } +wayland { + SOURCES += \ + text/qfont_wayland.cpp \ + text/qfontengine_wayland.cpp \ + text/qfontengine_ft.cpp \ + text/qabstractfontengine_wayland.cpp + HEADERS += \ + text/qfontengine_ft_p.h \ + text/qabstractfontengine_wayland.h \ + text/qabstractfontengine_p.h + DEFINES += QT_NO_FONTCONFIG +} + + contains(QT_CONFIG, freetype) { SOURCES += \ ../3rdparty/freetype/src/base/ftbase.c \ diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp index 8b36b32476..52bac8c8bf 100644 --- a/src/gui/util/qdesktopservices.cpp +++ b/src/gui/util/qdesktopservices.cpp @@ -55,6 +55,8 @@ #include "qdesktopservices_mac.cpp" #elif defined(Q_OS_SYMBIAN) #include "qdesktopservices_s60.cpp" +#elif defined(Q_WS_WAYLAND) +#include "qdesktopservices_wayland.cpp" #endif #include <qhash.h> diff --git a/src/gui/util/qdesktopservices_wayland.cpp b/src/gui/util/qdesktopservices_wayland.cpp new file mode 100644 index 0000000000..777dc05862 --- /dev/null +++ b/src/gui/util/qdesktopservices_wayland.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 <qcoreapplication.h> +#include <qdir.h> + +QT_BEGIN_NAMESPACE + +static bool launchWebBrowser(const QUrl &url) +{ + Q_UNUSED(url); + qWarning("QDesktopServices::launchWebBrowser not implemented"); + return false; +} + +static bool openDocument(const QUrl &file) +{ + Q_UNUSED(file); + qWarning("QDesktopServices::openDocument not implemented"); + return false; +} + + +QString QDesktopServices::storageLocation(StandardLocation type) +{ + qWarning("QDesktopServices::storageLocation %d not implemented", type); + return QString(); +} + +QString QDesktopServices::displayName(StandardLocation type) +{ + Q_UNUSED(type); + qWarning("QDesktopServices::displayName not implemented"); + return QString(); +} + +QT_END_NAMESPACE diff --git a/src/gui/util/qsystemtrayicon_wayland.cpp b/src/gui/util/qsystemtrayicon_wayland.cpp new file mode 100644 index 0000000000..b1b895ba8c --- /dev/null +++ b/src/gui/util/qsystemtrayicon_wayland.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the 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 "qsystemtrayicon_p.h" + +#ifndef QT_NO_SYSTEMTRAYICON + +QT_BEGIN_NAMESPACE + +void QSystemTrayIconPrivate::install_sys() +{ +} + +void QSystemTrayIconPrivate::remove_sys() +{ +} + +QRect QSystemTrayIconPrivate::geometry_sys() const +{ + return QRect(); +} + +void QSystemTrayIconPrivate::updateIcon_sys() +{ +} + +void QSystemTrayIconPrivate::updateMenu_sys() +{ +} + +void QSystemTrayIconPrivate::updateToolTip_sys() +{ +} + +bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() +{ + return false; +} + +void QSystemTrayIconPrivate::showMessage_sys(const QString &message, + const QString &title, + QSystemTrayIcon::MessageIcon icon, + int msecs) +{ + Q_UNUSED(message); + Q_UNUSED(title); + Q_UNUSED(icon); + Q_UNUSED(msecs); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SYSTEMTRAYICON diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri index be8db93d39..f3ce448ee5 100644 --- a/src/gui/util/util.pri +++ b/src/gui/util/util.pri @@ -33,6 +33,11 @@ unix:x11 { util/qsystemtrayicon_x11.cpp } +wayland { + SOURCHES += \ + util/qsystemtrayicon_wayland.cpp +} + embedded { SOURCES += \ util/qsystemtrayicon_qws.cpp diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index d6011cf2f9..2d2ed15899 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -11,7 +11,7 @@ unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui include(../qbase.pri) -!win32:!embedded:!mac:CONFIG += x11 +!wayland:!win32:!embedded:!mac:CONFIG += x11 contains(QT_CONFIG, opengl):CONFIG += opengl contains(QT_CONFIG, opengles1):CONFIG += opengles1 contains(QT_CONFIG, opengles2):CONFIG += opengles2 @@ -111,6 +111,40 @@ x11 { LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD } +wayland { + contains(QT_CONFIG, egl) { + SOURCES += qgl_waylandegl.cpp \ + qglpixelbuffer_egl.cpp \ + qgl_egl.cpp \ + qpixmapdata_waylandgl_egl.cpp \ + qwindowsurface_waylandgl.cpp + + HEADERS += qgl_egl_p.h \ + qpixmapdata_waylandgl_p.h \ + qwindowsurface_waylandgl_p.h + + } else { + SOURCES += qgl_wayland.cpp \ + qglpixelbuffer_wayland.cpp + } + + contains(QT_CONFIG, fontconfig) { + contains(QT_CONFIG, system-freetype) { + embedded:CONFIG += opentype + # pull in the proper freetype2 include directory + include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri) + LIBS_PRIVATE += -lfreetype + } else { + ### Note: how does this compile with a non-system freetype? + # This probably does not compile + } + } else { + DEFINES *= QT_NO_FREETYPE + } + + LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD +} + mac { OBJECTIVE_SOURCES += qgl_mac.mm \ qglpixelbuffer_mac.mm diff --git a/src/opengl/qgl_wayland.cpp b/src/opengl/qgl_wayland.cpp new file mode 100644 index 0000000000..d203646803 --- /dev/null +++ b/src/opengl/qgl_wayland.cpp @@ -0,0 +1,1815 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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 "qgl.h" +#include "qgl_p.h" + +#include "qmap.h" +#include "qapplication.h" +#include "qcolormap.h" +#include "qdesktopwidget.h" +#include "qpixmap.h" +#include "qhash.h" +#include "qlibrary.h" +#include "qdebug.h" +#include <private/qfontengine_ft_p.h> +#include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> +#ifdef Q_OS_HPUX +// for GLXPBuffer +#include <private/qglpixelbuffer_p.h> +#endif + +// We always define GLX_EXT_texture_from_pixmap ourselves because +// we can't trust system headers to do it properly +#define GLX_EXT_texture_from_pixmap 1 + +#define INT8 dummy_INT8 +#define INT32 dummy_INT32 +#include <GL/glx.h> +#undef INT8 +#undef INT32 + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> +#ifdef Q_OS_VXWORS +# ifdef open +# undef open +# endif +# ifdef getpid +# undef getpid +# endif +#endif // Q_OS_VXWORKS +#include <X11/Xatom.h> + +#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) +#include <dlfcn.h> +#endif + +QT_BEGIN_NAMESPACE + +extern Drawable qt_x11Handle(const QPaintDevice *pd); +extern const QX11Info *qt_x11Info(const QPaintDevice *pd); + +#ifndef GLX_ARB_multisample +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif + +#ifndef GLX_TEXTURE_2D_BIT_EXT +#define GLX_TEXTURE_2D_BIT_EXT 0x00000002 +#define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004 +#define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0 +#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1 +#define GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2 +#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3 +#define GLX_Y_INVERTED_EXT 0x20D4 +#define GLX_TEXTURE_FORMAT_EXT 0x20D5 +#define GLX_TEXTURE_TARGET_EXT 0x20D6 +#define GLX_MIPMAP_TEXTURE_EXT 0x20D7 +#define GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8 +#define GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9 +#define GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA +#define GLX_TEXTURE_2D_EXT 0x20DC +#define GLX_TEXTURE_RECTANGLE_EXT 0x20DD +#define GLX_FRONT_LEFT_EXT 0x20DE +#endif + +/* + The qt_gl_choose_cmap function is internal and used by QGLWidget::setContext() + and GLX (not Windows). If the application can't find any sharable + colormaps, it must at least create as few colormaps as possible. The + dictionary solution below ensures only one colormap is created per visual. + Colormaps are also deleted when the application terminates. +*/ + +struct QCMapEntry { + QCMapEntry(); + ~QCMapEntry(); + + Colormap cmap; + bool alloc; + XStandardColormap scmap; +}; + +QCMapEntry::QCMapEntry() +{ + cmap = 0; + alloc = false; + scmap.colormap = 0; +} + +QCMapEntry::~QCMapEntry() +{ + if (alloc) + XFreeColormap(X11->display, cmap); +} +typedef QHash<int, QCMapEntry *> CMapEntryHash; +typedef QHash<int, QMap<int, QRgb> > GLCMapHash; +static bool mesa_gl = false; +static bool first_time = true; + +static void cleanup_cmaps(); + +struct QGLCMapCleanupHandler { + QGLCMapCleanupHandler() { + cmap_hash = new CMapEntryHash; + qglcmap_hash = new GLCMapHash; + } + ~QGLCMapCleanupHandler() { + delete cmap_hash; + delete qglcmap_hash; + } + CMapEntryHash *cmap_hash; + GLCMapHash *qglcmap_hash; +}; +Q_GLOBAL_STATIC(QGLCMapCleanupHandler, cmap_handler) + +static void cleanup_cmaps() +{ + CMapEntryHash *hash = cmap_handler()->cmap_hash; + QHash<int, QCMapEntry *>::ConstIterator it = hash->constBegin(); + while (it != hash->constEnd()) { + delete it.value(); + ++it; + } + + hash->clear(); + cmap_handler()->qglcmap_hash->clear(); +} + +Colormap qt_gl_choose_cmap(Display *dpy, XVisualInfo *vi) +{ + if (first_time) { + const char *v = glXQueryServerString(dpy, vi->screen, GLX_VERSION); + if (v) + mesa_gl = (strstr(v, "Mesa") != 0); + first_time = false; + } + + CMapEntryHash *hash = cmap_handler()->cmap_hash; + CMapEntryHash::ConstIterator it = hash->constFind((long) vi->visualid + (vi->screen * 256)); + if (it != hash->constEnd()) + return it.value()->cmap; // found colormap for visual + + if (vi->visualid == + XVisualIDFromVisual((Visual *) QX11Info::appVisual(vi->screen))) { + // qDebug("Using x11AppColormap"); + return QX11Info::appColormap(vi->screen); + } + + QCMapEntry *x = new QCMapEntry(); + + XStandardColormap *c; + int n, i; + + // qDebug("Choosing cmap for vID %0x", vi->visualid); + + if (mesa_gl) { // we're using MesaGL + Atom hp_cmaps = XInternAtom(dpy, "_HP_RGB_SMOOTH_MAP_LIST", true); + if (hp_cmaps && vi->visual->c_class == TrueColor && vi->depth == 8) { + if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n, + hp_cmaps)) { + i = 0; + while (i < n && x->cmap == 0) { + if (c[i].visualid == vi->visual->visualid) { + x->cmap = c[i].colormap; + x->scmap = c[i]; + //qDebug("Using HP_RGB scmap"); + + } + i++; + } + XFree((char *)c); + } + } + } + if (!x->cmap) { + if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n, + XA_RGB_DEFAULT_MAP)) { + for (int i = 0; i < n && x->cmap == 0; ++i) { + if (!c[i].red_max || + !c[i].green_max || + !c[i].blue_max || + !c[i].red_mult || + !c[i].green_mult || + !c[i].blue_mult) + continue; // invalid stdcmap + if (c[i].visualid == vi->visualid) { + x->cmap = c[i].colormap; + x->scmap = c[i]; + //qDebug("Using RGB_DEFAULT scmap"); + } + } + XFree((char *)c); + } + } + if (!x->cmap) { // no shared cmap found + x->cmap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), vi->visual, + AllocNone); + x->alloc = true; + // qDebug("Allocating cmap"); + } + + // colormap hash should be cleanup only when the QApplication dtor is called + if (hash->isEmpty()) + qAddPostRoutine(cleanup_cmaps); + + // associate cmap with visualid + hash->insert((long) vi->visualid + (vi->screen * 256), x); + return x->cmap; +} + +struct QTransColor +{ + VisualID vis; + int screen; + long color; +}; + +static QVector<QTransColor> trans_colors; +static int trans_colors_init = false; + +static void find_trans_colors() +{ + struct OverlayProp { + long visual; + long type; + long value; + long layer; + }; + + trans_colors_init = true; + + Display* appDisplay = X11->display; + + int scr; + int lastsize = 0; + for (scr = 0; scr < ScreenCount(appDisplay); scr++) { + QWidget* rootWin = QApplication::desktop()->screen(scr); + if (!rootWin) + return; // Should not happen + Atom overlayVisualsAtom = XInternAtom(appDisplay, + "SERVER_OVERLAY_VISUALS", True); + if (overlayVisualsAtom == XNone) + return; // Server has no overlays + + Atom actualType; + int actualFormat; + ulong nItems; + ulong bytesAfter; + unsigned char *retval = 0; + int res = XGetWindowProperty(appDisplay, rootWin->winId(), + overlayVisualsAtom, 0, 10000, False, + overlayVisualsAtom, &actualType, + &actualFormat, &nItems, &bytesAfter, + &retval); + + if (res != Success || actualType != overlayVisualsAtom + || actualFormat != 32 || nItems < 4 || !retval) + return; // Error reading property + + OverlayProp *overlayProps = (OverlayProp *)retval; + + int numProps = nItems / 4; + trans_colors.resize(lastsize + numProps); + int j = lastsize; + for (int i = 0; i < numProps; i++) { + if (overlayProps[i].type == 1) { + trans_colors[j].vis = (VisualID)overlayProps[i].visual; + trans_colors[j].screen = scr; + trans_colors[j].color = (int)overlayProps[i].value; + j++; + } + } + XFree(overlayProps); + lastsize = j; + trans_colors.resize(lastsize); + } +} + +/***************************************************************************** + QGLFormat UNIX/GLX-specific code + *****************************************************************************/ + +void* qglx_getProcAddress(const char* procName) +{ + // On systems where the GL driver is pluggable (like Mesa), we have to use + // the glXGetProcAddressARB extension to resolve other function pointers as + // the symbols wont be in the GL library, but rather in a plugin loaded by + // the GL library. + typedef void* (*qt_glXGetProcAddressARB)(const char *); + static qt_glXGetProcAddressARB glXGetProcAddressARB = 0; + static bool triedResolvingGlxGetProcAddress = false; + if (!triedResolvingGlxGetProcAddress) { + triedResolvingGlxGetProcAddress = true; + QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)); + if (extensions.match("GLX_ARB_get_proc_address")) { +#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) + void *handle = dlopen(NULL, RTLD_LAZY); + if (handle) { + glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB"); + dlclose(handle); + } + if (!glXGetProcAddressARB) +#endif + { +#if !defined(QT_NO_LIBRARY) + extern const QString qt_gl_library_name(); + QLibrary lib(qt_gl_library_name()); + glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB"); +#endif + } + } + } + + void *procAddress = 0; + if (glXGetProcAddressARB) + procAddress = glXGetProcAddressARB(procName); + + // If glXGetProcAddress didn't work, try looking the symbol up in the GL library +#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) + if (!procAddress) { + void *handle = dlopen(NULL, RTLD_LAZY); + if (handle) { + procAddress = dlsym(handle, procName); + dlclose(handle); + } + } +#endif +#if !defined(QT_NO_LIBRARY) + if (!procAddress) { + extern const QString qt_gl_library_name(); + QLibrary lib(qt_gl_library_name()); + procAddress = lib.resolve(procName); + } +#endif + + return procAddress; +} + +bool QGLFormat::hasOpenGL() +{ + return glXQueryExtension(X11->display, 0, 0) != 0; +} + + +bool QGLFormat::hasOpenGLOverlays() +{ + if (!trans_colors_init) + find_trans_colors(); + return trans_colors.size() > 0; +} + +/***************************************************************************** + QGLContext UNIX/GLX-specific code + *****************************************************************************/ + +bool QGLContext::chooseContext(const QGLContext* shareContext) +{ + Q_D(QGLContext); + const QX11Info *xinfo = qt_x11Info(d->paintDevice); + + Display* disp = xinfo->display(); + d->vi = chooseVisual(); + if (!d->vi) + return false; + + if (deviceIsPixmap() && + (((XVisualInfo*)d->vi)->depth != xinfo->depth() || + ((XVisualInfo*)d->vi)->screen != xinfo->screen())) + { + XFree(d->vi); + XVisualInfo appVisInfo; + memset(&appVisInfo, 0, sizeof(XVisualInfo)); + appVisInfo.visualid = XVisualIDFromVisual((Visual *) xinfo->visual()); + appVisInfo.screen = xinfo->screen(); + int nvis; + d->vi = XGetVisualInfo(disp, VisualIDMask | VisualScreenMask, &appVisInfo, &nvis); + if (!d->vi) + return false; + + int useGL; + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_USE_GL, &useGL); + if (!useGL) + return false; //# Chickening out already... + } + int res; + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_LEVEL, &res); + d->glFormat.setPlane(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DOUBLEBUFFER, &res); + d->glFormat.setDoubleBuffer(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DEPTH_SIZE, &res); + d->glFormat.setDepth(res); + if (d->glFormat.depth()) + d->glFormat.setDepthBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RGBA, &res); + d->glFormat.setRgba(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RED_SIZE, &res); + d->glFormat.setRedBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_GREEN_SIZE, &res); + d->glFormat.setGreenBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BLUE_SIZE, &res); + d->glFormat.setBlueBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ALPHA_SIZE, &res); + d->glFormat.setAlpha(res); + if (d->glFormat.alpha()) + d->glFormat.setAlphaBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ACCUM_RED_SIZE, &res); + d->glFormat.setAccum(res); + if (d->glFormat.accum()) + d->glFormat.setAccumBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STENCIL_SIZE, &res); + d->glFormat.setStencil(res); + if (d->glFormat.stencil()) + d->glFormat.setStencilBufferSize(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STEREO, &res); + d->glFormat.setStereo(res); + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLE_BUFFERS_ARB, &res); + d->glFormat.setSampleBuffers(res); + if (d->glFormat.sampleBuffers()) { + glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLES_ARB, &res); + d->glFormat.setSamples(res); + } + + Bool direct = format().directRendering() ? True : False; + + if (shareContext && + (!shareContext->isValid() || !shareContext->d_func()->cx)) { + qWarning("QGLContext::chooseContext(): Cannot share with invalid context"); + shareContext = 0; + } + + // 1. Sharing between rgba and color-index will give wrong colors. + // 2. Contexts cannot be shared btw. direct/non-direct renderers. + // 3. Pixmaps cannot share contexts that are set up for direct rendering. + // 4. If the contexts are not created on the same screen, they can't be shared + + if (shareContext + && (format().rgba() != shareContext->format().rgba() + || (deviceIsPixmap() && glXIsDirect(disp, (GLXContext)shareContext->d_func()->cx)) + || (shareContext->d_func()->screen != xinfo->screen()))) + { + shareContext = 0; + } + + d->cx = 0; + if (shareContext) { + d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi, + (GLXContext)shareContext->d_func()->cx, direct); + d->screen = ((XVisualInfo*)d->vi)->screen; + if (d->cx) { + QGLContext *share = const_cast<QGLContext *>(shareContext); + d->sharing = true; + share->d_func()->sharing = true; + } + } + if (!d->cx) { + d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi, NULL, direct); + d->screen = ((XVisualInfo*)d->vi)->screen; + } + if (!d->cx) + return false; + d->glFormat.setDirectRendering(glXIsDirect(disp, (GLXContext)d->cx)); + if (deviceIsPixmap()) { +#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT) + d->gpm = glXCreateGLXPixmapMESA(disp, (XVisualInfo *)d->vi, + qt_x11Handle(d->paintDevice), + qt_gl_choose_cmap(disp, (XVisualInfo *)d->vi)); +#else + d->gpm = (quint32)glXCreateGLXPixmap(disp, (XVisualInfo *)d->vi, + qt_x11Handle(d->paintDevice)); +#endif + if (!d->gpm) + return false; + } + QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen())); + if (extensions.match("GLX_SGI_video_sync")) { + if (d->glFormat.swapInterval() == -1) + d->glFormat.setSwapInterval(0); + } else { + d->glFormat.setSwapInterval(-1); + } + return true; +} + +/* + See qgl.cpp for qdoc comment. + */ +void *QGLContext::chooseVisual() +{ + Q_D(QGLContext); + static const int bufDepths[] = { 8, 4, 2, 1 }; // Try 16, 12 also? + //todo: if pixmap, also make sure that vi->depth == pixmap->depth + void* vis = 0; + int i = 0; + bool fail = false; + QGLFormat fmt = format(); + bool tryDouble = !fmt.doubleBuffer(); // Some GL impl's only have double + bool triedDouble = false; + bool triedSample = false; + if (fmt.sampleBuffers()) + fmt.setSampleBuffers(QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers); + while(!fail && !(vis = tryVisual(fmt, bufDepths[i]))) { + if (!fmt.rgba() && bufDepths[i] > 1) { + i++; + continue; + } + if (tryDouble) { + fmt.setDoubleBuffer(true); + tryDouble = false; + triedDouble = true; + continue; + } else if (triedDouble) { + fmt.setDoubleBuffer(false); + triedDouble = false; + } + if (!triedSample && fmt.sampleBuffers()) { + fmt.setSampleBuffers(false); + triedSample = true; + continue; + } + if (fmt.stereo()) { + fmt.setStereo(false); + continue; + } + if (fmt.accum()) { + fmt.setAccum(false); + continue; + } + if (fmt.stencil()) { + fmt.setStencil(false); + continue; + } + if (fmt.alpha()) { + fmt.setAlpha(false); + continue; + } + if (fmt.depth()) { + fmt.setDepth(false); + continue; + } + if (fmt.doubleBuffer()) { + fmt.setDoubleBuffer(false); + continue; + } + fail = true; + } + d->glFormat = fmt; + return vis; +} + +/* + See qgl.cpp for qdoc comment. + */ +void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth) +{ + Q_D(QGLContext); + int spec[45]; + int i = 0; + spec[i++] = GLX_LEVEL; + spec[i++] = f.plane(); + const QX11Info *xinfo = qt_x11Info(d->paintDevice); + bool useFBConfig = false; + +#if defined(GLX_VERSION_1_3) && !defined(QT_NO_XRENDER) && !defined(Q_OS_HPUX) + /* + HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions. + Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented. + */ + QWidget* widget = 0; + if (d->paintDevice->devType() == QInternal::Widget) + widget = static_cast<QWidget*>(d->paintDevice); + + // Only use glXChooseFBConfig for widgets if we're trying to get an ARGB visual + if (widget && widget->testAttribute(Qt::WA_TranslucentBackground) && X11->use_xrender) + useFBConfig = true; +#endif + +#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info) + static bool useTranspExt = false; + static bool useTranspExtChecked = false; + if (f.plane() && !useTranspExtChecked && d->paintDevice) { + QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen())); + useTranspExt = extensions.match("GLX_EXT_visual_info"); + //# (A bit simplistic; that could theoretically be a substring) + if (useTranspExt) { + QByteArray cstr(glXGetClientString(xinfo->display(), GLX_VENDOR)); + useTranspExt = !cstr.contains("Xi Graphics"); // bug workaround + if (useTranspExt) { + // bug workaround - some systems (eg. FireGL) refuses to return an overlay + // visual if the GLX_TRANSPARENT_TYPE_EXT attribute is specified, even if + // the implementation supports transparent overlays + int tmpSpec[] = { GLX_LEVEL, f.plane(), GLX_TRANSPARENT_TYPE_EXT, + f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT, + XNone }; + XVisualInfo * vinf = glXChooseVisual(xinfo->display(), xinfo->screen(), tmpSpec); + if (!vinf) { + useTranspExt = false; + } + } + } + + useTranspExtChecked = true; + } + if (f.plane() && useTranspExt && !useFBConfig) { + // Required to avoid non-transparent overlay visual(!) on some systems + spec[i++] = GLX_TRANSPARENT_TYPE_EXT; + spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT; + } +#endif + +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + // GLX_RENDER_TYPE is only in glx >=1.3 + if (useFBConfig) { + spec[i++] = GLX_RENDER_TYPE; + spec[i++] = f.rgba() ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT; + } +#endif + + if (f.doubleBuffer()) + spec[i++] = GLX_DOUBLEBUFFER; + if (useFBConfig) + spec[i++] = True; + if (f.depth()) { + spec[i++] = GLX_DEPTH_SIZE; + spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize(); + } + if (f.stereo()) { + spec[i++] = GLX_STEREO; + if (useFBConfig) + spec[i++] = True; + } + if (f.stencil()) { + spec[i++] = GLX_STENCIL_SIZE; + spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize(); + } + if (f.rgba()) { + if (!useFBConfig) + spec[i++] = GLX_RGBA; + spec[i++] = GLX_RED_SIZE; + spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize(); + spec[i++] = GLX_GREEN_SIZE; + spec[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize(); + spec[i++] = GLX_BLUE_SIZE; + spec[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize(); + if (f.alpha()) { + spec[i++] = GLX_ALPHA_SIZE; + spec[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize(); + } + if (f.accum()) { + spec[i++] = GLX_ACCUM_RED_SIZE; + spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + spec[i++] = GLX_ACCUM_GREEN_SIZE; + spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + spec[i++] = GLX_ACCUM_BLUE_SIZE; + spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + if (f.alpha()) { + spec[i++] = GLX_ACCUM_ALPHA_SIZE; + spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + } + } + } else { + spec[i++] = GLX_BUFFER_SIZE; + spec[i++] = bufDepth; + } + + if (f.sampleBuffers()) { + spec[i++] = GLX_SAMPLE_BUFFERS_ARB; + spec[i++] = 1; + spec[i++] = GLX_SAMPLES_ARB; + spec[i++] = f.samples() == -1 ? 4 : f.samples(); + } + +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + if (useFBConfig) { + spec[i++] = GLX_DRAWABLE_TYPE; + switch(d->paintDevice->devType()) { + case QInternal::Pixmap: + spec[i++] = GLX_PIXMAP_BIT; + break; + case QInternal::Pbuffer: + spec[i++] = GLX_PBUFFER_BIT; + break; + default: + qWarning("QGLContext: Unknown paint device type %d", d->paintDevice->devType()); + // Fall-through & assume it's a window + case QInternal::Widget: + spec[i++] = GLX_WINDOW_BIT; + break; + }; + } +#endif + + spec[i] = XNone; + + + XVisualInfo* chosenVisualInfo = 0; + +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + while (useFBConfig) { + GLXFBConfig *configs; + int configCount = 0; + configs = glXChooseFBConfig(xinfo->display(), xinfo->screen(), spec, &configCount); + + if (!configs) + break; // fallback to trying glXChooseVisual + + for (i = 0; i < configCount; ++i) { + XVisualInfo* vi; + vi = glXGetVisualFromFBConfig(xinfo->display(), configs[i]); + if (!vi) + continue; + +#if !defined(QT_NO_XRENDER) + QWidget* w = 0; + if (d->paintDevice->devType() == QInternal::Widget) + w = static_cast<QWidget*>(d->paintDevice); + + if (w && w->testAttribute(Qt::WA_TranslucentBackground) && f.alpha()) { + // Attempt to find a config who's visual has a proper alpha channel + XRenderPictFormat *pictFormat; + pictFormat = XRenderFindVisualFormat(xinfo->display(), vi->visual); + + if (pictFormat && (pictFormat->type == PictTypeDirect) && pictFormat->direct.alphaMask) { + // The pict format for the visual matching the FBConfig indicates ARGB + if (chosenVisualInfo) + XFree(chosenVisualInfo); + chosenVisualInfo = vi; + break; + } + } else +#endif //QT_NO_XRENDER + if (chosenVisualInfo) { + // If we've got a visual we can use and we're not trying to find one with a + // real alpha channel, we might as well just use the one we've got + break; + } + + if (!chosenVisualInfo) + chosenVisualInfo = vi; // Have something to fall back to + else + XFree(vi); + } + + XFree(configs); + break; + } +#endif // defined(GLX_VERSION_1_3) + + if (!chosenVisualInfo) + chosenVisualInfo = glXChooseVisual(xinfo->display(), xinfo->screen(), spec); + + return chosenVisualInfo; +} + + +void QGLContext::reset() +{ + Q_D(QGLContext); + if (!d->valid) + return; + d->cleanup(); + const QX11Info *xinfo = qt_x11Info(d->paintDevice); + doneCurrent(); + if (d->gpm) + glXDestroyGLXPixmap(xinfo->display(), (GLXPixmap)d->gpm); + d->gpm = 0; + glXDestroyContext(xinfo->display(), (GLXContext)d->cx); + if (d->vi) + XFree(d->vi); + d->vi = 0; + d->cx = 0; + d->crWin = false; + d->sharing = false; + d->valid = false; + d->transpColor = QColor(); + d->initDone = false; + QGLContextGroup::removeShare(this); +} + + +void QGLContext::makeCurrent() +{ + Q_D(QGLContext); + if (!d->valid) { + qWarning("QGLContext::makeCurrent(): Cannot make invalid context current."); + return; + } + const QX11Info *xinfo = qt_x11Info(d->paintDevice); + bool ok = true; + if (d->paintDevice->devType() == QInternal::Pixmap) { + ok = glXMakeCurrent(xinfo->display(), (GLXPixmap)d->gpm, (GLXContext)d->cx); + } else if (d->paintDevice->devType() == QInternal::Pbuffer) { + ok = glXMakeCurrent(xinfo->display(), (GLXPbuffer)d->pbuf, (GLXContext)d->cx); + } else if (d->paintDevice->devType() == QInternal::Widget) { + ok = glXMakeCurrent(xinfo->display(), ((QWidget *)d->paintDevice)->winId(), (GLXContext)d->cx); + } + if (!ok) + qWarning("QGLContext::makeCurrent(): Failed."); + + if (ok) + QGLContextPrivate::setCurrentContext(this); +} + +void QGLContext::doneCurrent() +{ + Q_D(QGLContext); + glXMakeCurrent(qt_x11Info(d->paintDevice)->display(), 0, 0); + QGLContextPrivate::setCurrentContext(0); +} + + +void QGLContext::swapBuffers() const +{ + Q_D(const QGLContext); + if (!d->valid) + return; + if (!deviceIsPixmap()) { + int interval = d->glFormat.swapInterval(); + if (interval > 0) { + typedef int (*qt_glXGetVideoSyncSGI)(uint *); + typedef int (*qt_glXWaitVideoSyncSGI)(int, int, uint *); + static qt_glXGetVideoSyncSGI glXGetVideoSyncSGI = 0; + static qt_glXWaitVideoSyncSGI glXWaitVideoSyncSGI = 0; + static bool resolved = false; + if (!resolved) { + const QX11Info *xinfo = qt_x11Info(d->paintDevice); + QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen())); + if (extensions.match("GLX_SGI_video_sync")) { + glXGetVideoSyncSGI = (qt_glXGetVideoSyncSGI)qglx_getProcAddress("glXGetVideoSyncSGI"); + glXWaitVideoSyncSGI = (qt_glXWaitVideoSyncSGI)qglx_getProcAddress("glXWaitVideoSyncSGI"); + } + resolved = true; + } + if (glXGetVideoSyncSGI && glXWaitVideoSyncSGI) { + uint counter; + if (!glXGetVideoSyncSGI(&counter)) + glXWaitVideoSyncSGI(interval + 1, (counter + interval) % (interval + 1), &counter); + } + } + glXSwapBuffers(qt_x11Info(d->paintDevice)->display(), + static_cast<QWidget *>(d->paintDevice)->winId()); + } +} + +QColor QGLContext::overlayTransparentColor() const +{ + if (isValid()) + return Qt::transparent; + return QColor(); // Invalid color +} + +static uint qt_transparent_pixel(VisualID id, int screen) +{ + for (int i = 0; i < trans_colors.size(); i++) { + if (trans_colors[i].vis == id && trans_colors[i].screen == screen) + return trans_colors[i].color; + } + return 0; +} + +uint QGLContext::colorIndex(const QColor& c) const +{ + Q_D(const QGLContext); + int screen = ((XVisualInfo *)d->vi)->screen; + QColormap colmap = QColormap::instance(screen); + if (isValid()) { + if (format().plane() && c == Qt::transparent) { + return qt_transparent_pixel(((XVisualInfo *)d->vi)->visualid, + ((XVisualInfo *)d->vi)->screen); + } + if (((XVisualInfo*)d->vi)->visualid == + XVisualIDFromVisual((Visual *) QX11Info::appVisual(screen))) + return colmap.pixel(c); // We're using QColor's cmap + + XVisualInfo *info = (XVisualInfo *) d->vi; + CMapEntryHash *hash = cmap_handler()->cmap_hash; + CMapEntryHash::ConstIterator it = hash->constFind(long(info->visualid) + + (info->screen * 256)); + QCMapEntry *x = 0; + if (it != hash->constEnd()) + x = it.value(); + if (x && !x->alloc) { // It's a standard colormap + int rf = (int)(((float)c.red() * (x->scmap.red_max+1))/256.0); + int gf = (int)(((float)c.green() * (x->scmap.green_max+1))/256.0); + int bf = (int)(((float)c.blue() * (x->scmap.blue_max+1))/256.0); + uint p = x->scmap.base_pixel + + (rf * x->scmap.red_mult) + + (gf * x->scmap.green_mult) + + (bf * x->scmap.blue_mult); + return p; + } else { + QMap<int, QRgb> &cmap = (*cmap_handler()->qglcmap_hash)[(long)info->visualid]; + + // already in the map? + QRgb target = c.rgb(); + QMap<int, QRgb>::Iterator it = cmap.begin(); + for (; it != cmap.end(); ++it) { + if ((*it) == target) + return it.key(); + } + + // need to alloc color + unsigned long plane_mask[2]; + unsigned long color_map_entry; + if (!XAllocColorCells (QX11Info::display(), x->cmap, true, plane_mask, 0, + &color_map_entry, 1)) + return colmap.pixel(c); + + XColor col; + col.flags = DoRed | DoGreen | DoBlue; + col.pixel = color_map_entry; + col.red = (ushort)((qRed(c.rgb()) / 255.0) * 65535.0 + 0.5); + col.green = (ushort)((qGreen(c.rgb()) / 255.0) * 65535.0 + 0.5); + col.blue = (ushort)((qBlue(c.rgb()) / 255.0) * 65535.0 + 0.5); + XStoreColor(QX11Info::display(), x->cmap, &col); + + cmap.insert(color_map_entry, target); + return color_map_entry; + } + } + return 0; +} + +#ifndef QT_NO_FONTCONFIG +/*! \internal + This is basically a substitute for glxUseXFont() which can only + handle XLFD fonts. This version relies on freetype to render the + glyphs, but it works with all fonts that fontconfig provides - both + antialiased and aliased bitmap and outline fonts. +*/ +static void qgl_use_font(QFontEngineFT *engine, int first, int count, int listBase) +{ + GLfloat color[4]; + glGetFloatv(GL_CURRENT_COLOR, color); + + // save the pixel unpack state + GLint gl_swapbytes, gl_lsbfirst, gl_rowlength, gl_skiprows, gl_skippixels, gl_alignment; + glGetIntegerv (GL_UNPACK_SWAP_BYTES, &gl_swapbytes); + glGetIntegerv (GL_UNPACK_LSB_FIRST, &gl_lsbfirst); + glGetIntegerv (GL_UNPACK_ROW_LENGTH, &gl_rowlength); + glGetIntegerv (GL_UNPACK_SKIP_ROWS, &gl_skiprows); + glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &gl_skippixels); + glGetIntegerv (GL_UNPACK_ALIGNMENT, &gl_alignment); + + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + const bool antialiased = engine->drawAntialiased(); + FT_Face face = engine->lockFace(); + + // start generating font glyphs + for (int i = first; i < count; ++i) { + int list = listBase + i; + GLfloat x0, y0, dx, dy; + + FT_Error err; + + err = FT_Load_Glyph(face, FT_Get_Char_Index(face, i), FT_LOAD_DEFAULT); + if (err) { + qDebug("failed loading glyph %d from font", i); + Q_ASSERT(!err); + } + err = FT_Render_Glyph(face->glyph, (antialiased ? FT_RENDER_MODE_NORMAL + : FT_RENDER_MODE_MONO)); + if (err) { + qDebug("failed rendering glyph %d from font", i); + Q_ASSERT(!err); + } + + FT_Bitmap bm = face->glyph->bitmap; + x0 = face->glyph->metrics.horiBearingX >> 6; + y0 = (face->glyph->metrics.height - face->glyph->metrics.horiBearingY) >> 6; + dx = face->glyph->metrics.horiAdvance >> 6; + dy = 0; + int sz = bm.pitch * bm.rows; + uint *aa_glyph = 0; + uchar *ua_glyph = 0; + + if (antialiased) + aa_glyph = new uint[sz]; + else + ua_glyph = new uchar[sz]; + + // convert to GL format + for (int y = 0; y < bm.rows; ++y) { + for (int x = 0; x < bm.pitch; ++x) { + int c1 = y*bm.pitch + x; + int c2 = (bm.rows - y - 1) > 0 ? (bm.rows-y-1)*bm.pitch + x : x; + if (antialiased) { + aa_glyph[c1] = (int(color[0]*255) << 24) + | (int(color[1]*255) << 16) + | (int(color[2]*255) << 8) | bm.buffer[c2]; + } else { + ua_glyph[c1] = bm.buffer[c2]; + } + } + } + + glNewList(list, GL_COMPILE); + if (antialiased) { + // calling glBitmap() is just a trick to move the current + // raster pos, since glGet*() won't work in display lists + glBitmap(0, 0, 0, 0, x0, -y0, 0); + glDrawPixels(bm.pitch, bm.rows, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, aa_glyph); + glBitmap(0, 0, 0, 0, dx-x0, y0, 0); + } else { + glBitmap(bm.pitch*8, bm.rows, -x0, y0, dx, dy, ua_glyph); + } + glEndList(); + antialiased ? delete[] aa_glyph : delete[] ua_glyph; + } + + engine->unlockFace(); + + // restore pixel unpack settings + glPixelStorei(GL_UNPACK_SWAP_BYTES, gl_swapbytes); + glPixelStorei(GL_UNPACK_LSB_FIRST, gl_lsbfirst); + glPixelStorei(GL_UNPACK_ROW_LENGTH, gl_rowlength); + glPixelStorei(GL_UNPACK_SKIP_ROWS, gl_skiprows); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, gl_skippixels); + glPixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment); +} +#endif + +#undef d +void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase) +{ + QFont f(fnt); + QFontEngine *engine = f.d->engineForScript(QUnicodeTables::Common); + + if (engine->type() == QFontEngine::Multi) + engine = static_cast<QFontEngineMulti *>(engine)->engine(0); +#ifndef QT_NO_FONTCONFIG + if(engine->type() == QFontEngine::Freetype) { + qgl_use_font(static_cast<QFontEngineFT *>(engine), 0, 256, listBase); + return; + } +#endif + // glXUseXFont() only works with XLFD font structures and a few GL + // drivers crash if 0 is passed as the font handle + f.setStyleStrategy(QFont::OpenGLCompatible); + if (f.handle() && engine->type() == QFontEngine::XLFD) + glXUseXFont(static_cast<Font>(f.handle()), 0, 256, listBase); +} + +void *QGLContext::getProcAddress(const QString &proc) const +{ + typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *); + static qt_glXGetProcAddressARB glXGetProcAddressARB = 0; + static bool resolved = false; + + if (resolved && !glXGetProcAddressARB) + return 0; + if (!glXGetProcAddressARB) { + QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)); + if (extensions.match("GLX_ARB_get_proc_address")) { +#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) + void *handle = dlopen(NULL, RTLD_LAZY); + if (handle) { + glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB"); + dlclose(handle); + } + if (!glXGetProcAddressARB) +#endif + { +#if !defined(QT_NO_LIBRARY) + extern const QString qt_gl_library_name(); + QLibrary lib(qt_gl_library_name()); + glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB"); +#endif + } + } + resolved = true; + } + if (!glXGetProcAddressARB) + return 0; + return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(proc.toLatin1().data())); +} + +/* + QGLTemporaryContext implementation +*/ + +class QGLTemporaryContextPrivate { +public: + bool initialized; + Window drawable; + GLXContext context; + GLXDrawable oldDrawable; + GLXContext oldContext; +}; + +QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *) + : d(new QGLTemporaryContextPrivate) +{ + d->initialized = false; + d->oldDrawable = 0; + d->oldContext = 0; + int screen = 0; + + int attribs[] = {GLX_RGBA, XNone}; + XVisualInfo *vi = glXChooseVisual(X11->display, screen, attribs); + if (!vi) { + qWarning("QGLTempContext: No GL capable X visuals available."); + return; + } + + int useGL; + glXGetConfig(X11->display, vi, GLX_USE_GL, &useGL); + if (!useGL) { + XFree(vi); + return; + } + + d->oldDrawable = glXGetCurrentDrawable(); + d->oldContext = glXGetCurrentContext(); + + XSetWindowAttributes a; + a.colormap = qt_gl_choose_cmap(X11->display, vi); + d->drawable = XCreateWindow(X11->display, RootWindow(X11->display, screen), + 0, 0, 1, 1, 0, + vi->depth, InputOutput, vi->visual, + CWColormap, &a); + d->context = glXCreateContext(X11->display, vi, 0, True); + if (d->context && glXMakeCurrent(X11->display, d->drawable, d->context)) { + d->initialized = true; + } else { + qWarning("QGLTempContext: Unable to create GL context."); + XDestroyWindow(X11->display, d->drawable); + } + XFree(vi); +} + +QGLTemporaryContext::~QGLTemporaryContext() +{ + if (d->initialized) { + glXMakeCurrent(X11->display, 0, 0); + glXDestroyContext(X11->display, d->context); + XDestroyWindow(X11->display, d->drawable); + } + if (d->oldDrawable && d->oldContext) + glXMakeCurrent(X11->display, d->oldDrawable, d->oldContext); +} + +/***************************************************************************** + QGLOverlayWidget (Internal overlay class for X11) + *****************************************************************************/ + +class QGLOverlayWidget : public QGLWidget +{ + Q_OBJECT +public: + QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent, const QGLWidget* shareWidget=0); + +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); + bool x11Event(XEvent *e) { return realWidget->x11Event(e); } + +private: + QGLWidget* realWidget; + +private: + Q_DISABLE_COPY(QGLOverlayWidget) +}; + + +QGLOverlayWidget::QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent, + const QGLWidget* shareWidget) + : QGLWidget(format, parent, shareWidget ? shareWidget->d_func()->olw : 0) +{ + setAttribute(Qt::WA_X11OpenGLOverlay); + realWidget = parent; +} + + + +void QGLOverlayWidget::initializeGL() +{ + QColor transparentColor = context()->overlayTransparentColor(); + if (transparentColor.isValid()) + qglClearColor(transparentColor); + else + qWarning("QGLOverlayWidget::initializeGL(): Could not get transparent color"); + realWidget->initializeOverlayGL(); +} + + +void QGLOverlayWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); + realWidget->resizeOverlayGL(w, h); +} + + +void QGLOverlayWidget::paintGL() +{ + realWidget->paintOverlayGL(); +} + +#undef Bool +QT_BEGIN_INCLUDE_NAMESPACE +#include "qgl_x11.moc" +QT_END_INCLUDE_NAMESPACE + +/***************************************************************************** + QGLWidget UNIX/GLX-specific code + *****************************************************************************/ +void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget) +{ + Q_Q(QGLWidget); + initContext(context, shareWidget); + olw = 0; + + if (q->isValid() && context->format().hasOverlay()) { + QString olwName = q->objectName(); + olwName += QLatin1String("-QGL_internal_overlay_widget"); + olw = new QGLOverlayWidget(QGLFormat::defaultOverlayFormat(), q, shareWidget); + olw->setObjectName(olwName); + if (olw->isValid()) { + olw->setAutoBufferSwap(false); + olw->setFocusProxy(q); + } + else { + delete olw; + olw = 0; + glcx->d_func()->glFormat.setOverlay(false); + } + } +} + +bool QGLWidgetPrivate::renderCxPm(QPixmap* pm) +{ + Q_Q(QGLWidget); + if (((XVisualInfo*)glcx->d_func()->vi)->depth != pm->depth()) + return false; + + GLXPixmap glPm; +#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT) + glPm = glXCreateGLXPixmapMESA(X11->display, + (XVisualInfo*)glcx->vi, + (Pixmap)pm->handle(), + qt_gl_choose_cmap(pm->X11->display, + (XVisualInfo*)glcx->vi)); +#else + glPm = (quint32)glXCreateGLXPixmap(X11->display, + (XVisualInfo*)glcx->d_func()->vi, + (Pixmap)pm->handle()); +#endif + + if (!glXMakeCurrent(X11->display, glPm, (GLXContext)glcx->d_func()->cx)) { + glXDestroyGLXPixmap(X11->display, glPm); + return false; + } + + glDrawBuffer(GL_FRONT); + if (!glcx->initialized()) + q->glInit(); + q->resizeGL(pm->width(), pm->height()); + q->paintGL(); + glFlush(); + q->makeCurrent(); + glXDestroyGLXPixmap(X11->display, glPm); + q->resizeGL(q->width(), q->height()); + return true; +} + +/*! \internal + Free up any allocated colormaps. This fn is only called for + top-level widgets. +*/ +void QGLWidgetPrivate::cleanupColormaps() +{ + if (!cmap.handle()) { + return; + } else { + XFreeColormap(X11->display, (Colormap) cmap.handle()); + cmap.setHandle(0); + } +} + +void QGLWidget::setMouseTracking(bool enable) +{ + Q_D(QGLWidget); + if (d->olw) + d->olw->setMouseTracking(enable); + QWidget::setMouseTracking(enable); +} + + +void QGLWidget::resizeEvent(QResizeEvent *) +{ + Q_D(QGLWidget); + if (!isValid()) + return; + makeCurrent(); + if (!d->glcx->initialized()) + glInit(); + glXWaitX(); + resizeGL(width(), height()); + if (d->olw) + d->olw->setGeometry(rect()); +} + +const QGLContext* QGLWidget::overlayContext() const +{ + Q_D(const QGLWidget); + if (d->olw) + return d->olw->context(); + else + return 0; +} + + +void QGLWidget::makeOverlayCurrent() +{ + Q_D(QGLWidget); + if (d->olw) + d->olw->makeCurrent(); +} + + +void QGLWidget::updateOverlayGL() +{ + Q_D(QGLWidget); + if (d->olw) + d->olw->updateGL(); +} + +/*! + \internal + + Sets a new QGLContext, \a context, for this QGLWidget, using the + shared context, \a shareContext. If \a deleteOldContext is true, + the original context is deleted; otherwise it is overridden. +*/ +void QGLWidget::setContext(QGLContext *context, + const QGLContext* shareContext, + bool deleteOldContext) +{ + Q_D(QGLWidget); + if (context == 0) { + qWarning("QGLWidget::setContext: Cannot set null context"); + return; + } + if (!context->deviceIsPixmap() && context->device() != this) { + qWarning("QGLWidget::setContext: Context must refer to this widget"); + return; + } + + if (d->glcx) + d->glcx->doneCurrent(); + QGLContext* oldcx = d->glcx; + d->glcx = context; + + if (parentWidget()) { + // force creation of delay-created widgets + parentWidget()->winId(); + if (parentWidget()->x11Info().screen() != x11Info().screen()) + d_func()->xinfo = parentWidget()->d_func()->xinfo; + } + + // If the application has set WA_TranslucentBackground and not explicitly set + // the alpha buffer size to zero, modify the format so it have an alpha channel + QGLFormat& fmt = d->glcx->d_func()->glFormat; + if (testAttribute(Qt::WA_TranslucentBackground) && fmt.alphaBufferSize() == -1) + fmt.setAlphaBufferSize(1); + + bool createFailed = false; + if (!d->glcx->isValid()) { + if (!d->glcx->create(shareContext ? shareContext : oldcx)) + createFailed = true; + } + if (createFailed) { + if (deleteOldContext) + delete oldcx; + return; + } + + if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) { + if (deleteOldContext) + delete oldcx; + return; + } + + bool visible = isVisible(); + if (visible) + hide(); + + XVisualInfo *vi = (XVisualInfo*)d->glcx->d_func()->vi; + XSetWindowAttributes a; + + QColormap colmap = QColormap::instance(vi->screen); + a.colormap = qt_gl_choose_cmap(QX11Info::display(), vi); // find best colormap + a.background_pixel = colmap.pixel(palette().color(backgroundRole())); + a.border_pixel = colmap.pixel(Qt::black); + Window p = RootWindow(X11->display, vi->screen); + if (parentWidget()) + p = parentWidget()->winId(); + + Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(), + 0, vi->depth, InputOutput, vi->visual, + CWBackPixel|CWBorderPixel|CWColormap, &a); + Window *cmw; + Window *cmwret; + int count; + if (XGetWMColormapWindows(X11->display, window()->winId(), + &cmwret, &count)) { + cmw = new Window[count+1]; + memcpy((char *)cmw, (char *)cmwret, sizeof(Window)*count); + XFree((char *)cmwret); + int i; + for (i=0; i<count; i++) { + if (cmw[i] == winId()) { // replace old window + cmw[i] = w; + break; + } + } + if (i >= count) // append new window + cmw[count++] = w; + } else { + count = 1; + cmw = new Window[count]; + cmw[0] = w; + } + +#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT) + if (oldcx && oldcx->windowCreated()) + glXReleaseBuffersMESA(X11->display, winId()); +#endif + if (deleteOldContext) + delete oldcx; + oldcx = 0; + + if (testAttribute(Qt::WA_WState_Created)) + create(w); + else + d->createWinId(w); + XSetWMColormapWindows(X11->display, window()->winId(), cmw, count); + delete [] cmw; + + // calling QWidget::create() will always result in a new paint + // engine being created - get rid of it and replace it with our + // own + + if (visible) + show(); + XFlush(X11->display); + d->glcx->setWindowCreated(true); +} + +const QGLColormap & QGLWidget::colormap() const +{ + Q_D(const QGLWidget); + return d->cmap; +} + +/*\internal + Store color values in the given colormap. +*/ +static void qStoreColors(QWidget * tlw, Colormap cmap, + const QGLColormap & cols) +{ + Q_UNUSED(tlw); + XColor c; + QRgb color; + + for (int i = 0; i < cols.size(); i++) { + color = cols.entryRgb(i); + c.pixel = i; + c.red = (ushort)((qRed(color) / 255.0) * 65535.0 + 0.5); + c.green = (ushort)((qGreen(color) / 255.0) * 65535.0 + 0.5); + c.blue = (ushort)((qBlue(color) / 255.0) * 65535.0 + 0.5); + c.flags = DoRed | DoGreen | DoBlue; + XStoreColor(X11->display, cmap, &c); + } +} + +/*\internal + Check whether the given visual supports dynamic colormaps or not. +*/ +static bool qCanAllocColors(QWidget * w) +{ + bool validVisual = false; + int numVisuals; + long mask; + XVisualInfo templ; + XVisualInfo * visuals; + VisualID id = XVisualIDFromVisual((Visual *) w->window()->x11Info().visual()); + + mask = VisualScreenMask; + templ.screen = w->x11Info().screen(); + visuals = XGetVisualInfo(X11->display, mask, &templ, &numVisuals); + + for (int i = 0; i < numVisuals; i++) { + if (visuals[i].visualid == id) { + switch (visuals[i].c_class) { + case TrueColor: + case StaticColor: + case StaticGray: + case XGrayScale: + validVisual = false; + break; + case DirectColor: + case PseudoColor: + validVisual = true; + break; + } + break; + } + } + XFree(visuals); + + if (!validVisual) + return false; + return true; +} + + +void QGLWidget::setColormap(const QGLColormap & c) +{ + Q_D(QGLWidget); + QWidget * tlw = window(); // must return a valid widget + + d->cmap = c; + if (!d->cmap.handle()) + return; + + if (!qCanAllocColors(this)) { + qWarning("QGLWidget::setColormap: Cannot create a read/write " + "colormap for this visual"); + return; + } + + // If the child GL widget is not of the same visual class as the + // toplevel widget we will get in trouble.. + Window wid = tlw->winId(); + Visual * vis = (Visual *) tlw->x11Info().visual();; + VisualID cvId = XVisualIDFromVisual((Visual *) x11Info().visual()); + VisualID tvId = XVisualIDFromVisual((Visual *) tlw->x11Info().visual()); + if (cvId != tvId) { + wid = winId(); + vis = (Visual *) x11Info().visual(); + } + + if (!d->cmap.handle()) // allocate a cmap if necessary + d->cmap.setHandle(XCreateColormap(X11->display, wid, vis, AllocAll)); + + qStoreColors(this, (Colormap) d->cmap.handle(), c); + XSetWindowColormap(X11->display, wid, (Colormap) d->cmap.handle()); + + // tell the wm that this window has a special colormap + Window * cmw; + Window * cmwret; + int count; + if (XGetWMColormapWindows(X11->display, tlw->winId(), &cmwret, &count)) + { + cmw = new Window[count+1]; + memcpy((char *) cmw, (char *) cmwret, sizeof(Window) * count); + XFree((char *) cmwret); + int i; + for (i = 0; i < count; i++) { + if (cmw[i] == winId()) { + break; + } + } + if (i >= count) // append new window only if not in the list + cmw[count++] = winId(); + } else { + count = 1; + cmw = new Window[count]; + cmw[0] = winId(); + } + XSetWMColormapWindows(X11->display, tlw->winId(), cmw, count); + delete [] cmw; +} + +// Solaris defines glXBindTexImageEXT as part of the GL library +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) +typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*); +typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int); +static qt_glXBindTexImageEXT glXBindTexImageEXT = 0; +static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0; + +static bool qt_resolveTextureFromPixmap(QPaintDevice *paintDevice) +{ + static bool resolvedTextureFromPixmap = false; + + if (!resolvedTextureFromPixmap) { + resolvedTextureFromPixmap = true; + + // Check to see if we have NPOT texture support + if ( !(QGLExtensions::glExtensions() & QGLExtensions::NPOTTextures) && + !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)) + { + return false; // Can't use TFP without NPOT + } + + const QX11Info *xinfo = qt_x11Info(paintDevice); + Display *display = xinfo ? xinfo->display() : X11->display; + int screen = xinfo ? xinfo->screen() : X11->defaultScreen; + + QGLExtensionMatcher serverExtensions(glXQueryExtensionsString(display, screen)); + QGLExtensionMatcher clientExtensions(glXGetClientString(display, GLX_EXTENSIONS)); + if (serverExtensions.match("GLX_EXT_texture_from_pixmap") + && clientExtensions.match("GLX_EXT_texture_from_pixmap")) + { + glXBindTexImageEXT = (qt_glXBindTexImageEXT) qglx_getProcAddress("glXBindTexImageEXT"); + glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) qglx_getProcAddress("glXReleaseTexImageEXT"); + } + } + + return glXBindTexImageEXT && glXReleaseTexImageEXT; +} +#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + + +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key, + QGLContext::BindOptions options) +{ +#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) + return 0; +#else + + // Check we have GLX 1.3, as it is needed for glXCreatePixmap & glXDestroyPixmap + int majorVersion = 0; + int minorVersion = 0; + glXQueryVersion(X11->display, &majorVersion, &minorVersion); + if (majorVersion < 1 || (majorVersion == 1 && minorVersion < 3)) + return 0; + + Q_Q(QGLContext); + + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data()); + Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class); + + if (!qt_resolveTextureFromPixmap(paintDevice)) + return 0; + + const QX11Info &x11Info = pixmapData->xinfo; + + // Store the configs (Can be static because configs aren't dependent on current context) + static GLXFBConfig glxRGBPixmapConfig = 0; + static bool RGBConfigInverted = false; + static GLXFBConfig glxRGBAPixmapConfig = 0; + static bool RGBAConfigInverted = false; + + bool hasAlpha = pixmapData->hasAlphaChannel(); + + // Check to see if we need a config + if ( (hasAlpha && !glxRGBAPixmapConfig) || (!hasAlpha && !glxRGBPixmapConfig) ) { + GLXFBConfig *configList = 0; + int configCount = 0; + + int configAttribs[] = { + hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can: + GLX_Y_INVERTED_EXT, options & QGLContext::CanFlipNativePixmapBindOption ? GLX_DONT_CARE : False, + XNone + }; + configList = glXChooseFBConfig(x11Info.display(), x11Info.screen(), configAttribs, &configCount); + if (!configList) + return 0; + + int yInv; + glXGetFBConfigAttrib(x11Info.display(), configList[0], GLX_Y_INVERTED_EXT, &yInv); + + if (hasAlpha) { + glxRGBAPixmapConfig = configList[0]; + RGBAConfigInverted = yInv; + } + else { + glxRGBPixmapConfig = configList[0]; + RGBConfigInverted = yInv; + } + + XFree(configList); + } + + // Check to see if the surface is still valid + if (pixmapData->gl_surface && + hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + // Surface is invalid! + destroyGlSurfaceForPixmap(pixmapData); + } + + // Check to see if we need a surface + if (!pixmapData->gl_surface) { + GLXPixmap glxPixmap; + int pixmapAttribs[] = { + GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT, + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_MIPMAP_TEXTURE_EXT, False, // Maybe needs to be don't care + XNone + }; + + // Wrap the X Pixmap into a GLXPixmap: + glxPixmap = glXCreatePixmap(x11Info.display(), + hasAlpha ? glxRGBAPixmapConfig : glxRGBPixmapConfig, + pixmapData->handle(), pixmapAttribs); + + if (!glxPixmap) + return 0; + + pixmapData->gl_surface = (void*)glxPixmap; + + // Make sure the cleanup hook gets called so we can delete the glx pixmap + QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData); + } + + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + glXBindTexImageEXT(x11Info.display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT, 0); + + glBindTexture(GL_TEXTURE_2D, textureId); + + if (!((hasAlpha && RGBAConfigInverted) || (!hasAlpha && RGBConfigInverted))) + options &= ~QGLContext::InvertedYBindOption; + + QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options); + if (texture->options & QGLContext::InvertedYBindOption) + pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + + // We assume the cost of bound pixmaps is zero + QGLTextureCache::instance()->insert(q, key, texture, 0); + + return texture; +#endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) +} + + +void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd) +{ +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) { + glXDestroyPixmap(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface); + pixmapData->gl_surface = 0; + } +#endif +} + +void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd) +{ +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + Q_ASSERT(QGLContext::currentContext()); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) + glXReleaseTexImageEXT(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT); +#endif +} + +QT_END_NAMESPACE diff --git a/src/opengl/qgl_waylandegl.cpp b/src/opengl/qgl_waylandegl.cpp new file mode 100644 index 0000000000..9d28de0b02 --- /dev/null +++ b/src/opengl/qgl_waylandegl.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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 "qgl.h" +#include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> +#include <private/qgl_p.h> +#include <private/qpaintengine_opengl_p.h> +#include "qgl_egl_p.h" +#include "qcolormap.h" +#include <QDebug> +#include <QPixmap> + + +QT_BEGIN_NAMESPACE + + +/* + QGLTemporaryContext implementation +*/ + +class QGLTemporaryContextPrivate +{ +public: + bool initialized; + Window window; + EGLContext context; + EGLSurface surface; + EGLDisplay display; +}; + +QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *) + : d(new QGLTemporaryContextPrivate) +{ + d->initialized = false; + d->window = 0; + d->context = 0; + d->surface = 0; + int screen = 0; + + d->display = QEgl::display(); + + EGLConfig config; + int numConfigs = 0; + EGLint attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, +#ifdef QT_OPENGL_ES_2 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#endif + EGL_NONE + }; + + eglChooseConfig(d->display, attribs, &config, 1, &numConfigs); + if (!numConfigs) { + qWarning("QGLTemporaryContext: No EGL configurations available."); + return; + } + + XVisualInfo visualInfo; + XVisualInfo *vi; + int numVisuals; + EGLint id = 0; + + visualInfo.visualid = QEgl::getCompatibleVisualId(config); + vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals); + if (!vi || numVisuals < 1) { + qWarning("QGLTemporaryContext: Unable to get X11 visual info id."); + return; + } + + d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen), + 0, 0, 1, 1, 0, + vi->depth, InputOutput, vi->visual, + 0, 0); + + d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL); + + if (d->surface == EGL_NO_SURFACE) { + qWarning("QGLTemporaryContext: Error creating EGL surface."); + XFree(vi); + XDestroyWindow(X11->display, d->window); + return; + } + + EGLint contextAttribs[] = { +#ifdef QT_OPENGL_ES_2 + EGL_CONTEXT_CLIENT_VERSION, 2, +#endif + EGL_NONE + }; + d->context = eglCreateContext(d->display, config, 0, contextAttribs); + if (d->context != EGL_NO_CONTEXT + && eglMakeCurrent(d->display, d->surface, d->surface, d->context)) + { + d->initialized = true; + } else { + qWarning("QGLTemporaryContext: Error creating EGL context."); + eglDestroySurface(d->display, d->surface); + XDestroyWindow(X11->display, d->window); + } + XFree(vi); +} + +QGLTemporaryContext::~QGLTemporaryContext() +{ + if (d->initialized) { + eglMakeCurrent(d->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(d->display, d->context); + eglDestroySurface(d->display, d->surface); + XDestroyWindow(X11->display, d->window); + } +} + +bool QGLFormat::hasOpenGLOverlays() +{ + return false; +} + +// Chooses the EGL config and creates the EGL context +bool QGLContext::chooseContext(const QGLContext* shareContext) +{ + Q_D(QGLContext); + + if (!device()) + return false; + + int devType = device()->devType(); + + QX11PixmapData *x11PixmapData = 0; + if (devType == QInternal::Pixmap) { + QPixmapData *pmd = static_cast<QPixmap*>(device())->data_ptr().data(); + if (pmd->classId() == QPixmapData::X11Class) + x11PixmapData = static_cast<QX11PixmapData*>(pmd); + else { + // TODO: Replace the pixmap's data with a new QX11PixmapData + qWarning("WARNING: Creating a QGLContext on a QPixmap is only supported for X11 pixmap backend"); + return false; + } + } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) { + qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType); + return false; + } + + // Only create the eglContext if we don't already have one: + if (d->eglContext == 0) { + d->eglContext = new QEglContext(); + d->ownsEglContext = true; + d->eglContext->setApi(QEgl::OpenGL); + + // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat + // has the alpha channel option set: + if (devType == QInternal::Widget) { + QWidget* widget = static_cast<QWidget*>(device()); + if (widget->testAttribute(Qt::WA_TranslucentBackground)) + d->glFormat.setAlpha(true); + } + + // Construct the configuration we need for this surface. + QEglProperties configProps; + configProps.setDeviceType(devType); + configProps.setRenderableType(QEgl::OpenGL); + qt_eglproperties_set_glformat(configProps, d->glFormat); + + // Set buffer preserved for regular QWidgets, QGLWidgets are ok with either preserved or destroyed: + if ((devType == QInternal::Widget) && qobject_cast<QGLWidget*>(static_cast<QWidget*>(device())) == 0) + configProps.setValue(EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); + + if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) { + delete d->eglContext; + d->eglContext = 0; + return false; + } + + // Create a new context for the configuration. + QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0; + if (!d->eglContext->createContext(eglSharedContext)) { + delete d->eglContext; + d->eglContext = 0; + return false; + } + d->sharing = d->eglContext->isSharing(); + if (d->sharing && shareContext) + const_cast<QGLContext *>(shareContext)->d_func()->sharing = true; + } + + // Inform the higher layers about the actual format properties + qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config()); + + // Do don't create the EGLSurface for everything. + // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface + // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface + // QPixmap - yes, create the EGLSurface but store it in QX11PixmapData::gl_surface + // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf + + if (devType == QInternal::Widget) { + if (d->eglSurface != EGL_NO_SURFACE) + eglDestroySurface(d->eglContext->display(), d->eglSurface); + d->eglSurface = QEgl::createSurface(device(), d->eglContext->config()); + XFlush(X11->display); + setWindowCreated(true); + } + + if (x11PixmapData) { + // TODO: Actually check to see if the existing surface can be re-used + if (x11PixmapData->gl_surface) + eglDestroySurface(d->eglContext->display(), (EGLSurface)x11PixmapData->gl_surface); + + x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config()); + } + + return true; +} + +void QGLWidget::resizeEvent(QResizeEvent *) +{ + Q_D(QGLWidget); + if (!isValid()) + return; + makeCurrent(); + if (!d->glcx->initialized()) + glInit(); + resizeGL(width(), height()); + //handle overlay +} + +const QGLContext* QGLWidget::overlayContext() const +{ + return 0; +} + +void QGLWidget::makeOverlayCurrent() +{ + //handle overlay +} + +void QGLWidget::updateOverlayGL() +{ + //handle overlay +} + +void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext) +{ + Q_D(QGLWidget); + if (context == 0) { + qWarning("QGLWidget::setContext: Cannot set null context"); + return; + } + if (!context->deviceIsPixmap() && context->device() != this) { + qWarning("QGLWidget::setContext: Context must refer to this widget"); + return; + } + + if (d->glcx) + d->glcx->doneCurrent(); + QGLContext* oldcx = d->glcx; + d->glcx = context; + + bool createFailed = false; + if (!d->glcx->isValid()) { + // Create the QGLContext here, which in turn chooses the EGL config + // and creates the EGL context: + if (!d->glcx->create(shareContext ? shareContext : oldcx)) + createFailed = true; + } + if (createFailed) { + if (deleteOldContext) + delete oldcx; + return; + } + + + d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for +} + +void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget) +{ + Q_Q(QGLWidget); + + initContext(context, shareWidget); + + if (q->isValid() && glcx->format().hasOverlay()) { + //no overlay + qWarning("QtOpenGL ES doesn't currently support overlays"); + } +} + +void QGLWidgetPrivate::cleanupColormaps() +{ +} + +const QGLColormap & QGLWidget::colormap() const +{ + return d_func()->cmap; +} + +void QGLWidget::setColormap(const QGLColormap &) +{ +} + +// Re-creates the EGL surface if the window ID has changed or if there isn't a surface +void QGLWidgetPrivate::recreateEglSurface() +{ + Q_Q(QGLWidget); + + Window currentId = q->winId(); + + // If the window ID has changed since the surface was created, we need to delete the + // old surface before re-creating a new one. Note: This should not be the case as the + // surface should be deleted before the old window id. + if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) { + qWarning("EGL surface for deleted window %x was not destroyed", eglSurfaceWindowId); + glcx->d_func()->destroyEglSurfaceForDevice(); + } + + if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) { + glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q); + eglSurfaceWindowId = currentId; + } +} + + +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key, + QGLContext::BindOptions options) +{ + Q_Q(QGLContext); + + // The EGL texture_from_pixmap has no facility to invert the y coordinate + if (!(options & QGLContext::CanFlipNativePixmapBindOption)) + return 0; + + + static bool checkedForTFP = false; + static bool haveTFP = false; + static bool checkedForEglImageTFP = false; + static bool haveEglImageTFP = false; + + + if (!checkedForEglImageTFP) { + checkedForEglImageTFP = true; + + // We need to be able to create an EGLImage from a native pixmap, which was split + // into a seperate EGL extension, EGL_KHR_image_pixmap. It is possible to have + // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must + // check we have the EGLImage from pixmap functionality. + if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) { + + // Being able to create an EGLImage from a native pixmap is also pretty useless + // without the ability to bind that EGLImage as a texture, which is provided by + // the GL_OES_EGL_image extension, which we try to resolve here: + haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q); + + if (haveEglImageTFP) + qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!"); + } + } + + if (!checkedForTFP) { + // Check for texture_from_pixmap egl extension + checkedForTFP = true; + if (QEgl::hasExtension("EGL_NOKIA_texture_from_pixmap") || + QEgl::hasExtension("EGL_EXT_texture_from_pixmap")) + { + qDebug("Found texture_from_pixmap EGL extension!"); + haveTFP = true; + } + } + + if (!haveTFP && !haveEglImageTFP) + return 0; + + + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data()); + Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class); + bool hasAlpha = pixmapData->hasAlphaChannel(); + bool pixmapHasValidSurface = false; + bool textureIsBound = false; + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + + if (haveTFP && pixmapData->gl_surface && + hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + pixmapHasValidSurface = true; + } + + // If we already have a valid EGL surface for the pixmap, we should use it + if (pixmapHasValidSurface) { + EGLBoolean success; + success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); + eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + pixmapData->gl_surface = (void*)EGL_NO_SURFACE; + } else + textureIsBound = true; + } + + // If the pixmap doesn't already have a valid surface, try binding it via EGLImage + // first, as going through EGLImage should be faster and better supported: + if (!textureIsBound && haveEglImageTFP) { + EGLImageKHR eglImage; + + EGLint attribs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE + }; + eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs); + + QGLContext* ctx = q; + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage); + + GLint err = glGetError(); + if (err == GL_NO_ERROR) + textureIsBound = true; + + // Once the egl image is bound, the texture becomes a new sibling image and we can safely + // destroy the EGLImage we created for the pixmap: + if (eglImage != EGL_NO_IMAGE_KHR) + QEgl::eglDestroyImageKHR(QEgl::display(), eglImage); + } + + if (!textureIsBound && haveTFP) { + // Check to see if the surface is still valid + if (pixmapData->gl_surface && + hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + // Surface is invalid! + destroyGlSurfaceForPixmap(pixmapData); + } + + if (pixmapData->gl_surface == 0) { + EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap, + QEgl::OpenGL, + hasAlpha ? QEgl::Translucent : QEgl::NoOptions); + + pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config); + if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE) + return false; + } + + EGLBoolean success; + success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); + eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + pixmapData->gl_surface = (void*)EGL_NO_SURFACE; + haveTFP = false; // If TFP isn't working, disable it's use + } else + textureIsBound = true; + } + + QGLTexture *texture = 0; + + if (textureIsBound) { + texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options); + pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + + // We assume the cost of bound pixmaps is zero + QGLTextureCache::instance()->insert(q, key, texture, 0); + + glBindTexture(GL_TEXTURE_2D, textureId); + } else + glDeleteTextures(1, &textureId); + + return texture; +} + + +void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd) +{ + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) { + EGLBoolean success; + success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + if (success == EGL_FALSE) { + qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: " + << QEgl::errorString(); + } + pixmapData->gl_surface = 0; + } +} + +void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd) +{ + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) { + EGLBoolean success; + success = eglReleaseTexImage(QEgl::display(), + (EGLSurface)pixmapData->gl_surface, + EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: " + << QEgl::errorString(); + } + } +} + +QT_END_NAMESPACE diff --git a/src/opengl/qglpixelbuffer_wayland.cpp b/src/opengl/qglpixelbuffer_wayland.cpp new file mode 100644 index 0000000000..32a42a2323 --- /dev/null +++ b/src/opengl/qglpixelbuffer_wayland.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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 <qlibrary.h> +#include <qdebug.h> +#include <private/qgl_p.h> +#include <private/qt_x11_p.h> +#include <private/qpaintengine_opengl_p.h> + +#include <qx11info_x11.h> +#include <GL/glx.h> +#include <qimage.h> + +#include "qglpixelbuffer.h" +#include "qglpixelbuffer_p.h" + +#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) +#include <dlfcn.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifndef GLX_VERSION_1_3 +#define GLX_RGBA_BIT 0x00000002 +#define GLX_PBUFFER_BIT 0x00000004 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_PBUFFER_HEIGHT 0x8040 +#define GLX_PBUFFER_WIDTH 0x8041 +#endif + +#ifndef GLX_ARB_multisample +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif + +typedef GLXFBConfig* (*_glXChooseFBConfig) (Display *dpy, int screen, const int *attrib_list, int *nelements); +typedef int (*_glXGetFBConfigAttrib) (Display *dpy, GLXFBConfig config, int attribute, int *value); +typedef GLXPbuffer (*_glXCreatePbuffer) (Display *dpy, GLXFBConfig config, const int *attrib_list); +typedef void (*_glXDestroyPbuffer) (Display *dpy, GLXPbuffer pbuf); +typedef GLXContext (*_glXCreateNewContext) (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); +typedef Bool (*_glXMakeContextCurrent) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); + +static _glXChooseFBConfig qt_glXChooseFBConfig = 0; +static _glXCreateNewContext qt_glXCreateNewContext = 0; +static _glXCreatePbuffer qt_glXCreatePbuffer = 0; +static _glXDestroyPbuffer qt_glXDestroyPbuffer = 0; +static _glXGetFBConfigAttrib qt_glXGetFBConfigAttrib = 0; +static _glXMakeContextCurrent qt_glXMakeContextCurrent = 0; + +#define glXChooseFBConfig qt_glXChooseFBConfig +#define glXCreateNewContext qt_glXCreateNewContext +#define glXCreatePbuffer qt_glXCreatePbuffer +#define glXDestroyPbuffer qt_glXDestroyPbuffer +#define glXGetFBConfigAttrib qt_glXGetFBConfigAttrib +#define glXMakeContextCurrent qt_glXMakeContextCurrent + +extern void* qglx_getProcAddress(const char* procName); // in qgl_x11.cpp + +static bool qt_resolve_pbuffer_extensions() +{ + static int resolved = false; + if (resolved && qt_glXMakeContextCurrent) + return true; + else if (resolved) + return false; + + qt_glXChooseFBConfig = (_glXChooseFBConfig) qglx_getProcAddress("glXChooseFBConfig"); + qt_glXCreateNewContext = (_glXCreateNewContext) qglx_getProcAddress("glXCreateNewContext"); + qt_glXCreatePbuffer = (_glXCreatePbuffer) qglx_getProcAddress("glXCreatePbuffer"); + qt_glXDestroyPbuffer = (_glXDestroyPbuffer) qglx_getProcAddress("glXDestroyPbuffer"); + qt_glXGetFBConfigAttrib = (_glXGetFBConfigAttrib) qglx_getProcAddress("glXGetFBConfigAttrib"); + qt_glXMakeContextCurrent = (_glXMakeContextCurrent) qglx_getProcAddress("glXMakeContextCurrent"); + + resolved = qt_glXMakeContextCurrent ? true : false; + return resolved; +} + +static void qt_format_to_attrib_list(const QGLFormat &f, int attribs[]) +{ + int i = 0; + attribs[i++] = GLX_RENDER_TYPE; + attribs[i++] = GLX_RGBA_BIT; + attribs[i++] = GLX_DRAWABLE_TYPE; + attribs[i++] = GLX_PBUFFER_BIT; + attribs[i++] = GLX_RED_SIZE; + attribs[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize(); + attribs[i++] = GLX_GREEN_SIZE; + attribs[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize(); + attribs[i++] = GLX_BLUE_SIZE; + attribs[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize(); + if (f.doubleBuffer()) { + attribs[i++] = GLX_DOUBLEBUFFER; + attribs[i++] = true; + } + if (f.depth()) { + attribs[i++] = GLX_DEPTH_SIZE; + attribs[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize(); + } + if (f.stereo()) { + attribs[i++] = GLX_STEREO; + attribs[i++] = true; + } + if (f.stencil()) { + attribs[i++] = GLX_STENCIL_SIZE; + attribs[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize(); + } + if (f.alpha()) { + attribs[i++] = GLX_ALPHA_SIZE; + attribs[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize(); + } + if (f.accum()) { + attribs[i++] = GLX_ACCUM_RED_SIZE; + attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + attribs[i++] = GLX_ACCUM_GREEN_SIZE; + attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + attribs[i++] = GLX_ACCUM_BLUE_SIZE; + attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + if (f.alpha()) { + attribs[i++] = GLX_ACCUM_ALPHA_SIZE; + attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize(); + } + } + if (f.sampleBuffers()) { + attribs[i++] = GLX_SAMPLE_BUFFERS_ARB; + attribs[i++] = 1; + attribs[i++] = GLX_SAMPLES_ARB; + attribs[i++] = f.samples() == -1 ? 4 : f.samples(); + } + + attribs[i] = XNone; +} + +bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget) +{ + if (!qt_resolve_pbuffer_extensions()) { + qWarning("QGLPixelBuffer: pbuffers are not supported on this system."); + return false; + } + + int attribs[40]; + int num_configs = 0; + + qt_format_to_attrib_list(f, attribs); + + int screen = X11->defaultScreen; + if (shareWidget) + screen = shareWidget->x11Info().screen(); + + GLXFBConfig *configs = glXChooseFBConfig(X11->display, screen, attribs, &num_configs); + if (configs && num_configs) { + int res; + glXGetFBConfigAttrib(X11->display, configs[0], GLX_LEVEL, &res); + format.setPlane(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_DOUBLEBUFFER, &res); + format.setDoubleBuffer(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_DEPTH_SIZE, &res); + format.setDepth(res); + if (format.depth()) + format.setDepthBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_RGBA, &res); + format.setRgba(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_RED_SIZE, &res); + format.setRedBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_GREEN_SIZE, &res); + format.setGreenBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_BLUE_SIZE, &res); + format.setBlueBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_ALPHA_SIZE, &res); + format.setAlpha(res); + if (format.alpha()) + format.setAlphaBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_ACCUM_RED_SIZE, &res); + format.setAccum(res); + if (format.accum()) + format.setAccumBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_STENCIL_SIZE, &res); + format.setStencil(res); + if (format.stencil()) + format.setStencilBufferSize(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_STEREO, &res); + format.setStereo(res); + glXGetFBConfigAttrib(X11->display, configs[0], GLX_SAMPLE_BUFFERS_ARB, &res); + format.setSampleBuffers(res); + if (format.sampleBuffers()) { + glXGetFBConfigAttrib(X11->display, configs[0], GLX_SAMPLES_ARB, &res); + format.setSamples(res); + } + + int pb_attribs[] = {GLX_PBUFFER_WIDTH, size.width(), GLX_PBUFFER_HEIGHT, size.height(), XNone}; + GLXContext shareContext = 0; + if (shareWidget && shareWidget->d_func()->glcx) + shareContext = (GLXContext) shareWidget->d_func()->glcx->d_func()->cx; + + pbuf = glXCreatePbuffer(QX11Info::display(), configs[0], pb_attribs); + ctx = glXCreateNewContext(QX11Info::display(), configs[0], GLX_RGBA_TYPE, shareContext, true); + + XFree(configs); + if (!pbuf || !ctx) { + qWarning("QGLPixelBuffer: Unable to create a pbuffer/context - giving up."); + return false; + } + return true; + } else { + qWarning("QGLPixelBuffer: Unable to find a context/format match - giving up."); + return false; + } +} + +bool QGLPixelBufferPrivate::cleanup() +{ + glXDestroyPbuffer(QX11Info::display(), pbuf); + return true; +} + +bool QGLPixelBuffer::bindToDynamicTexture(GLuint) +{ + return false; +} + +void QGLPixelBuffer::releaseFromDynamicTexture() +{ +} + +bool QGLPixelBuffer::hasOpenGLPbuffers() +{ + bool ret = qt_resolve_pbuffer_extensions(); + + if (!ret) + return false; + + int attribs[40]; + int num_configs = 0; + + qt_format_to_attrib_list(QGLFormat::defaultFormat(), attribs); + + GLXFBConfig *configs = glXChooseFBConfig(X11->display, X11->defaultScreen, attribs, &num_configs); + GLXPbuffer pbuf = 0; + GLXContext ctx = 0; + + if (configs && num_configs) { + int pb_attribs[] = {GLX_PBUFFER_WIDTH, 128, GLX_PBUFFER_HEIGHT, 128, XNone}; + pbuf = glXCreatePbuffer(X11->display, configs[0], pb_attribs); + ctx = glXCreateNewContext(X11->display, configs[0], GLX_RGBA_TYPE, 0, true); + XFree(configs); + glXDestroyContext(X11->display, ctx); + glXDestroyPbuffer(X11->display, pbuf); + } + return pbuf && ctx; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qpixmapdata_waylandgl_egl.cpp b/src/opengl/qpixmapdata_waylandgl_egl.cpp new file mode 100644 index 0000000000..2c11a0b441 --- /dev/null +++ b/src/opengl/qpixmapdata_waylandgl_egl.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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 <QDebug> + +#include <QtGui/private/qt_x11_p.h> +#include <QtGui/private/qegl_p.h> +#include <QtGui/private/qeglproperties_p.h> +#include <QtGui/private/qeglcontext_p.h> + +#if !defined(QT_OPENGL_ES_1) +#include <QtOpenGL/private/qpaintengineex_opengl2_p.h> +#endif + +#ifndef QT_OPENGL_ES_2 +#include <QtOpenGL/private/qpaintengine_opengl_p.h> +#endif + +#include <QtOpenGL/private/qgl_p.h> +#include <QtOpenGL/private/qgl_egl_p.h> + +#include "qpixmapdata_x11gl_p.h" + +QT_BEGIN_NAMESPACE + + +class QX11GLSharedContexts +{ +public: + QX11GLSharedContexts() + : rgbContext(0) + , argbContext(0) + , sharedQGLContext(0) + , sharePixmap(0) + { + EGLint rgbConfigId; + EGLint argbConfigId; + + do { + EGLConfig rgbConfig = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL, QEgl::Renderable); + EGLConfig argbConfig = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL, + QEgl::Renderable | QEgl::Translucent); + + eglGetConfigAttrib(QEgl::display(), rgbConfig, EGL_CONFIG_ID, &rgbConfigId); + eglGetConfigAttrib(QEgl::display(), argbConfig, EGL_CONFIG_ID, &argbConfigId); + + rgbContext = new QEglContext; + rgbContext->setConfig(rgbConfig); + rgbContext->createContext(); + + if (!rgbContext->isValid()) + break; + + // If the RGB & ARGB configs are the same, use the same egl context for both: + if (rgbConfig == argbConfig) + argbContext = rgbContext; + + // Otherwise, create a seperate context to be used for ARGB pixmaps: + if (!argbContext) { + argbContext = new QEglContext; + argbContext->setConfig(argbConfig); + bool success = argbContext->createContext(rgbContext); + if (!success) { + qWarning("QX11GLPixmapData - RGB & ARGB contexts aren't shared"); + success = argbContext->createContext(); + if (!success) + argbContext = rgbContext; // Might work, worth a shot at least. + } + } + + if (!argbContext->isValid()) + break; + + // Create the pixmap which will be used to create the egl surface for the share QGLContext + QX11PixmapData *rgbPixmapData = new QX11PixmapData(QPixmapData::PixmapType); + rgbPixmapData->resize(8, 8); + rgbPixmapData->fill(Qt::red); + sharePixmap = new QPixmap(rgbPixmapData); + EGLSurface sharePixmapSurface = QEgl::createSurface(sharePixmap, rgbConfig); + rgbPixmapData->gl_surface = (void*)sharePixmapSurface; + + // Create the actual QGLContext which will be used for sharing + sharedQGLContext = new QGLContext(QX11GLPixmapData::glFormat()); + sharedQGLContext->d_func()->eglContext = rgbContext; + sharedQGLContext->d_func()->eglSurface = sharePixmapSurface; + sharedQGLContext->d_func()->valid = true; + qt_glformat_from_eglconfig(sharedQGLContext->d_func()->glFormat, rgbConfig); + + + valid = rgbContext->makeCurrent(sharePixmapSurface); + + // If the ARGB & RGB configs are different, check ARGB works too: + if (argbConfig != rgbConfig) { + QX11PixmapData *argbPixmapData = new QX11PixmapData(QPixmapData::PixmapType); + argbPixmapData->resize(8, 8); + argbPixmapData->fill(Qt::transparent); // Force ARGB + QPixmap argbPixmap(argbPixmapData); // destroys pixmap data when goes out of scope + EGLSurface argbPixmapSurface = QEgl::createSurface(&argbPixmap, argbConfig); + valid = argbContext->makeCurrent(argbPixmapSurface); + argbContext->doneCurrent(); + eglDestroySurface(QEgl::display(), argbPixmapSurface); + argbPixmapData->gl_surface = 0; + } + + if (!valid) { + qWarning() << "Unable to make pixmap surface current:" << QEgl::errorString(); + break; + } + + // The pixmap surface destruction hooks are installed by QGLTextureCache, so we + // must make sure this is instanciated: + QGLTextureCache::instance(); + } while(0); + + if (!valid) + cleanup(); + else + qDebug("Using QX11GLPixmapData with EGL config %d for ARGB and config %d for RGB", argbConfigId, rgbConfigId); + + } + + ~QX11GLSharedContexts() { + cleanup(); + } + + void cleanup() { + if (sharedQGLContext) { + delete sharedQGLContext; + sharedQGLContext = 0; + } + if (argbContext && argbContext != rgbContext) + delete argbContext; + argbContext = 0; + + if (rgbContext) { + delete rgbContext; + rgbContext = 0; + } + + // Deleting the QPixmap will fire the pixmap destruction cleanup hooks which in turn + // will destroy the egl surface: + if (sharePixmap) { + delete sharePixmap; + sharePixmap = 0; + } + } + + bool isValid() { return valid;} + + // On 16bpp systems, RGB & ARGB pixmaps are different bit-depths and therefore need + // different contexts: + QEglContext *rgbContext; + QEglContext *argbContext; + + // The share context wraps the rgbContext and is used as the master of the context share + // group. As all other contexts will have the same egl context (or a shared one if rgb != argb) + // all QGLContexts will actually be sharing and can be in the same context group. + QGLContext *sharedQGLContext; +private: + QPixmap *sharePixmap; + bool valid; +}; + +static void qt_cleanup_x11gl_share_contexts(); + +Q_GLOBAL_STATIC_WITH_INITIALIZER(QX11GLSharedContexts, qt_x11gl_share_contexts, + { + qAddPostRoutine(qt_cleanup_x11gl_share_contexts); + }) + +static void qt_cleanup_x11gl_share_contexts() +{ + qt_x11gl_share_contexts()->cleanup(); +} + + +QX11GLSharedContexts* QX11GLPixmapData::sharedContexts() +{ + return qt_x11gl_share_contexts(); +} + +bool QX11GLPixmapData::hasX11GLPixmaps() +{ + static bool checkedForX11GLPixmaps = false; + static bool haveX11GLPixmaps = false; + + if (checkedForX11GLPixmaps) + return haveX11GLPixmaps; + + haveX11GLPixmaps = qt_x11gl_share_contexts()->isValid(); + checkedForX11GLPixmaps = true; + + return haveX11GLPixmaps; +} + +QX11GLPixmapData::QX11GLPixmapData() + : QX11PixmapData(QPixmapData::PixmapType), + ctx(0) +{ +} + +QX11GLPixmapData::~QX11GLPixmapData() +{ + if (ctx) + delete ctx; +} + + +void QX11GLPixmapData::fill(const QColor &color) +{ + if (ctx) { + ctx->makeCurrent(); + glFinish(); + eglWaitClient(); + } + + QX11PixmapData::fill(color); + XSync(X11->display, False); + + if (ctx) { + ctx->makeCurrent(); + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + } +} + +void QX11GLPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + if (ctx) { + ctx->makeCurrent(); + glFinish(); + eglWaitClient(); + } + + QX11PixmapData::copy(data, rect); + XSync(X11->display, False); + + if (ctx) { + ctx->makeCurrent(); + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + } +} + +bool QX11GLPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + if (ctx) { + ctx->makeCurrent(); + glFinish(); + eglWaitClient(); + } + + bool success = QX11PixmapData::scroll(dx, dy, rect); + XSync(X11->display, False); + + if (ctx) { + ctx->makeCurrent(); + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + } + + return success; +} + +#if !defined(QT_OPENGL_ES_1) +Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_pixmap_2_engine) +#endif + +#ifndef QT_OPENGL_ES_2 +Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_pixmap_engine) +#endif + + +QPaintEngine* QX11GLPixmapData::paintEngine() const +{ + // We need to create the context before beginPaint - do it here: + if (!ctx) { + ctx = new QGLContext(glFormat()); + Q_ASSERT(ctx->d_func()->eglContext == 0); + ctx->d_func()->eglContext = hasAlphaChannel() ? sharedContexts()->argbContext : sharedContexts()->rgbContext; + + // While we use a seperate QGLContext for each pixmap, the underlying QEglContext is + // the same. So we must use a "fake" QGLContext and fool the texture cache into thinking + // each pixmap's QGLContext is sharing with this central one. The only place this is + // going to fail is where we the underlying EGL RGB and ARGB contexts aren't sharing. + ctx->d_func()->sharing = true; + QGLContextGroup::addShare(ctx, sharedContexts()->sharedQGLContext); + + // Update the glFormat for the QGLContext: + qt_glformat_from_eglconfig(ctx->d_func()->glFormat, ctx->d_func()->eglContext->config()); + } + + QPaintEngine* engine; + +#if defined(QT_OPENGL_ES_1) + engine = qt_gl_pixmap_engine(); +#elif defined(QT_OPENGL_ES_2) + engine = qt_gl_pixmap_2_engine(); +#else + if (qt_gl_preferGL2Engine()) + engine = qt_gl_pixmap_2_engine(); + else + engine = qt_gl_pixmap_engine(); +#endif + + + + // Support multiple painters on multiple pixmaps simultaniously + if (engine->isActive()) { + qWarning("Pixmap paint engine already active"); + +#if defined(QT_OPENGL_ES_1) + engine = new QOpenGLPaintEngine; +#elif defined(QT_OPENGL_ES_2) + engine = new QGL2PaintEngineEx; +#else + if (qt_gl_preferGL2Engine()) + engine = new QGL2PaintEngineEx; + else + engine = new QOpenGLPaintEngine; +#endif + + engine->setAutoDestruct(true); + return engine; + } + + return engine; +} + +void QX11GLPixmapData::beginPaint() +{ +// qDebug("QX11GLPixmapData::beginPaint()"); + // TODO: Check to see if the surface is renderable + if ((EGLSurface)gl_surface == EGL_NO_SURFACE) { + QPixmap tmpPixmap(this); + EGLConfig cfg = ctx->d_func()->eglContext->config(); + Q_ASSERT(cfg != QEGL_NO_CONFIG); + +// qDebug("QX11GLPixmapData - using EGL Config ID %d", ctx->d_func()->eglContext->configAttrib(EGL_CONFIG_ID)); + EGLSurface surface = QEgl::createSurface(&tmpPixmap, cfg); + if (surface == EGL_NO_SURFACE) { + qWarning() << "Error creating EGL surface for pixmap:" << QEgl::errorString(); + return; + } + gl_surface = (void*)surface; + ctx->d_func()->eglSurface = surface; + ctx->d_func()->valid = true; + } + QGLPaintDevice::beginPaint(); +} + +QGLContext* QX11GLPixmapData::context() const +{ + return ctx; +} + +QSize QX11GLPixmapData::size() const +{ + return QSize(w, h); +} + + +QGLFormat QX11GLPixmapData::glFormat() +{ + return QGLFormat::defaultFormat(); //### +} + +QT_END_NAMESPACE diff --git a/src/opengl/qpixmapdata_waylandgl_p.h b/src/opengl/qpixmapdata_waylandgl_p.h new file mode 100644 index 0000000000..2d1336bef1 --- /dev/null +++ b/src/opengl/qpixmapdata_waylandgl_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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$ +** +****************************************************************************/ + +#ifndef QPIXMAPDATA_X11GL_P_H +#define QPIXMAPDATA_X11GL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qpixmapdata_p.h> +#include <private/qpixmap_x11_p.h> +#include <private/qglpaintdevice_p.h> + +#include <qgl.h> + +#ifndef QT_NO_EGL +#include <QtGui/private/qeglcontext_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QX11GLSharedContexts; + +class QX11GLPixmapData : public QX11PixmapData, public QGLPaintDevice +{ +public: + QX11GLPixmapData(); + virtual ~QX11GLPixmapData(); + + // Re-implemented from QX11PixmapData: + void fill(const QColor &color); + void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, const QRect &rect); + + // Re-implemented from QGLPaintDevice + QPaintEngine* paintEngine() const; // Also re-implements QX11PixmapData::paintEngine + void beginPaint(); + QGLContext* context() const; + QSize size() const; + + static bool hasX11GLPixmaps(); + static QGLFormat glFormat(); + static QX11GLSharedContexts* sharedContexts(); + +private: + mutable QGLContext* ctx; +}; + + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_X11GL_P_H diff --git a/src/opengl/qwindowsurface_waylandgl.cpp b/src/opengl/qwindowsurface_waylandgl.cpp new file mode 100644 index 0000000000..3de6cae056 --- /dev/null +++ b/src/opengl/qwindowsurface_waylandgl.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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 <QTime> +#include <QDebug> + +#include <private/qt_x11_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> + +#include "qwindowsurface_x11gl_p.h" +#include "qpixmapdata_x11gl_p.h" + +QT_BEGIN_NAMESPACE + +QX11GLWindowSurface::QX11GLWindowSurface(QWidget* window) + : QWindowSurface(window), m_windowGC(0), m_pixmapGC(0), m_window(window) +{ +} + +QX11GLWindowSurface::~QX11GLWindowSurface() +{ + if (m_windowGC) + XFree(m_windowGC); + if (m_pixmapGC) + XFree(m_pixmapGC); +} + +QPaintDevice *QX11GLWindowSurface::paintDevice() +{ + return &m_backBuffer; +} + +extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp + +void QX11GLWindowSurface::flush(QWidget *widget, const QRegion &widgetRegion, const QPoint &offset) +{ + // We don't need to know the widget which initiated the flush. Instead we just use the offset + // to translate the widgetRegion: + Q_UNUSED(widget); + + if (m_backBuffer.isNull()) { + qDebug("QX11GLWindowSurface::flush() - backBuffer is null, not flushing anything"); + return; + } + + Q_ASSERT(window()->size() != m_backBuffer.size()); + + // Wait for all GL rendering to the back buffer pixmap to complete before trying to + // copy it to the window. We do this by making sure the pixmap's context is current + // and then call eglWaitClient. The EGL 1.4 spec says eglWaitClient doesn't have to + // block, just that "All rendering calls...are guaranteed to be executed before native + // rendering calls". This makes it potentially less expensive than glFinish. + QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context(); + if (QGLContext::currentContext() != ctx && ctx && ctx->isValid()) + ctx->makeCurrent(); + eglWaitClient(); + + if (m_windowGC == 0) { + XGCValues attribs; + attribs.graphics_exposures = False; + m_windowGC = XCreateGC(X11->display, m_window->handle(), GCGraphicsExposures, &attribs); + } + + int rectCount; + XRectangle *rects = (XRectangle *)qt_getClipRects(widgetRegion, rectCount); + if (rectCount <= 0) + return; + + XSetClipRectangles(X11->display, m_windowGC, 0, 0, rects, rectCount, YXBanded); + + QRect dirtyRect = widgetRegion.boundingRect().translated(-offset); + XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_windowGC, + dirtyRect.x(), dirtyRect.y(), dirtyRect.width(), dirtyRect.height(), + dirtyRect.x(), dirtyRect.y()); + + // Make sure the blit of the update from the back buffer to the window completes + // before allowing rendering to start again to the back buffer. Otherwise the GPU + // might start rendering to the back buffer again while the blit takes place. + eglWaitNative(EGL_CORE_NATIVE_ENGINE); +} + +void QX11GLWindowSurface::setGeometry(const QRect &rect) +{ + if (rect.width() > m_backBuffer.size().width() || rect.height() > m_backBuffer.size().height()) { + QX11GLPixmapData *pd = new QX11GLPixmapData; + QSize newSize = rect.size(); + pd->resize(newSize.width(), newSize.height()); + m_backBuffer = QPixmap(pd); + if (window()->testAttribute(Qt::WA_TranslucentBackground)) + m_backBuffer.fill(Qt::transparent); + if (m_pixmapGC) { + XFreeGC(X11->display, m_pixmapGC); + m_pixmapGC = 0; + } + } + + QWindowSurface::setGeometry(rect); +} + +bool QX11GLWindowSurface::scroll(const QRegion &area, int dx, int dy) +{ + if (m_backBuffer.isNull()) + return false; + + Q_ASSERT(m_backBuffer.data_ptr()->classId() == QPixmapData::X11Class); + + // Make sure all GL rendering is complete before starting the scroll operation: + QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context(); + if (QGLContext::currentContext() != ctx && ctx && ctx->isValid()) + ctx->makeCurrent(); + eglWaitClient(); + + if (!m_pixmapGC) + m_pixmapGC = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0); + + foreach (const QRect& rect, area.rects()) { + XCopyArea(X11->display, m_backBuffer.handle(), m_backBuffer.handle(), m_pixmapGC, + rect.x(), rect.y(), rect.width(), rect.height(), + rect.x()+dx, rect.y()+dy); + } + + // Make sure the scroll operation is complete before allowing GL rendering to resume + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + + return true; +} + + +QPixmap QX11GLWindowSurface::grabWidget(const QWidget *widget, const QRect& rect) const +{ + if (!widget || m_backBuffer.isNull()) + return QPixmap(); + + QRect srcRect; + + // make sure the rect is inside the widget & clip to widget's rect + if (!rect.isEmpty()) + srcRect = rect & widget->rect(); + else + srcRect = widget->rect(); + + if (srcRect.isEmpty()) + return QPixmap(); + + // If it's a child widget we have to translate the coordinates + if (widget != window()) + srcRect.translate(widget->mapTo(window(), QPoint(0, 0))); + + QPixmap::x11SetDefaultScreen(widget->x11Info().screen()); + + QX11PixmapData *pmd = new QX11PixmapData(QPixmapData::PixmapType); + pmd->resize(srcRect.width(), srcRect.height()); + QPixmap px(pmd); + + GC tmpGc = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0); + + // Make sure all GL rendering is complete before copying the window + QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.pixmapData())->context(); + if (QGLContext::currentContext() != ctx && ctx && ctx->isValid()) + ctx->makeCurrent(); + eglWaitClient(); + + // Copy srcRect from the backing store to the new pixmap + XSetGraphicsExposures(X11->display, tmpGc, False); + XCopyArea(X11->display, m_backBuffer.handle(), px.handle(), tmpGc, + srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0); + XFreeGC(X11->display, tmpGc); + + // Wait until the copy has finised before allowing more rendering into the back buffer + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + + return px; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qwindowsurface_waylandgl_p.h b/src/opengl/qwindowsurface_waylandgl_p.h new file mode 100644 index 0000000000..4d493d0b8f --- /dev/null +++ b/src/opengl/qwindowsurface_waylandgl_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL 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$ +** +****************************************************************************/ + +#ifndef QWINDOWSURFACE_X11GL_P_H +#define QWINDOWSURFACE_X11GL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qwindowsurface_p.h> + +QT_BEGIN_NAMESPACE + +class QX11GLWindowSurface : public QWindowSurface +{ +public: + QX11GLWindowSurface(QWidget* window); + virtual ~QX11GLWindowSurface(); + + // Inherreted from QWindowSurface + QPaintDevice *paintDevice(); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void setGeometry(const QRect &rect); + bool scroll(const QRegion &area, int dx, int dy); + QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const; + +private: + GC m_windowGC; + GC m_pixmapGC; + QPixmap m_backBuffer; + QWidget *m_window; +}; + + +QT_END_NAMESPACE + +#endif // QWINDOWSURFACE_X11GL_P_H |