/****************************************************************************
**
** 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

#ifndef GLX_ARB_create_context
#define GLX_CONTEXT_DEBUG_BIT_ARB          0x00000001
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
#define GLX_CONTEXT_MAJOR_VERSION_ARB      0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB      0x2092
#define GLX_CONTEXT_FLAGS_ARB              0x2094
#endif

#ifndef GLX_ARB_create_context_profile
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB   0x00000001
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define GLX_CONTEXT_PROFILE_MASK_ARB       0x9126
#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;
}

static bool buildSpec(int* spec, const QGLFormat& f, QPaintDevice* paintDevice,
                      int bufDepth, bool onlyFBConfig = false)
{
    int i = 0;
    spec[i++] = GLX_LEVEL;
    spec[i++] = f.plane();
    const QX11Info *xinfo = qt_x11Info(paintDevice);
    bool useFBConfig = onlyFBConfig;

#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 (paintDevice->devType() == QInternal::Widget)
        widget = static_cast<QWidget*>(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 && 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(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", paintDevice->devType());
            // Fall-through & assume it's a window
        case QInternal::Widget:
            spec[i++] = GLX_WINDOW_BIT;
            break;
        };
    }
#endif

    spec[i] = XNone;
    return useFBConfig;
}

/*****************************************************************************
  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;
    }

    const int major = d->reqFormat.majorVersion();
    const int minor = d->reqFormat.minorVersion();
    const int profile = d->reqFormat.profile() == QGLFormat::CompatibilityProfile
        ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
        : GLX_CONTEXT_CORE_PROFILE_BIT_ARB;

    d->cx = 0;

#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
    /*
      HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
      Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
     */
    if ((major == 3 && minor >= 2) || major > 3) {
        QGLTemporaryContext *tmpContext = 0;
        if (!QGLContext::currentContext())
            tmpContext = new QGLTemporaryContext;

        int attributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, major,
                             GLX_CONTEXT_MINOR_VERSION_ARB, minor,
                             GLX_CONTEXT_PROFILE_MASK_ARB, profile,
                             0 };

        typedef GLXContext ( * Q_PFNGLXCREATECONTEXTATTRIBSARBPROC)
            (Display* dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);


        Q_PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs =
            (Q_PFNGLXCREATECONTEXTATTRIBSARBPROC) qglx_getProcAddress("glXCreateContextAttribsARB");

        if (glXCreateContextAttribs) {
            int spec[45];
            glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BUFFER_SIZE, &res);
            buildSpec(spec, format(), d->paintDevice, res, true);

            GLXFBConfig *configs;
            int configCount = 0;
            configs = glXChooseFBConfig(disp, xinfo->screen(), spec, &configCount);

            if (configs && configCount > 0) {
                d->cx = glXCreateContextAttribs(disp, configs[0],
                    shareContext ? (GLXContext)shareContext->d_func()->cx : 0, direct, attributes);
                if (!d->cx && shareContext) {
                    shareContext = 0;
                    d->cx = glXCreateContextAttribs(disp, configs[0], 0, direct, attributes);
                }
                d->screen = ((XVisualInfo*)d->vi)->screen;
            }
            XFree(configs);
        } else {
            qWarning("QGLContext::chooseContext(): OpenGL %d.%d is not supported", major, minor);
        }

        if (tmpContext)
            delete tmpContext;
    }
#else
    Q_UNUSED(major);
    Q_UNUSED(minor);
    Q_UNUSED(profile);
#endif

    if (!d->cx && shareContext) {
        d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi,
                               (GLXContext)shareContext->d_func()->cx, direct);
        d->screen = ((XVisualInfo*)d->vi)->screen;
    }
    if (!d->cx) {
        d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi, NULL, direct);
        d->screen = ((XVisualInfo*)d->vi)->screen;
        shareContext = 0;
    }

    if (shareContext && d->cx) {
        QGLContext *share = const_cast<QGLContext *>(shareContext);
        d->sharing = true;
        share->d_func()->sharing = true;
    }

    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];
    const QX11Info *xinfo = qt_x11Info(d->paintDevice);
    bool useFBConfig = buildSpec(spec, f, d->paintDevice, bufDepth, false);

    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 (int 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)->internalWinId(), (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);
    GLuint filtering = (options & QGLContext::LinearFilteringBindOption) ? GL_LINEAR : GL_NEAREST;
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);

    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
