summaryrefslogtreecommitdiffstats
path: root/src/opengl/qgl_x11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl/qgl_x11.cpp')
-rw-r--r--src/opengl/qgl_x11.cpp353
1 files changed, 340 insertions, 13 deletions
diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp
index 76a057a..a7376b2 100644
--- a/src/opengl/qgl_x11.cpp
+++ b/src/opengl/qgl_x11.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtOpenGL module of the Qt Toolkit.
@@ -21,9 +20,10 @@
** 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.
+** 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.
@@ -52,19 +52,33 @@
#include "qdebug.h"
#include <private/qfontengine_ft_p.h>
#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_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)
@@ -81,6 +95,25 @@ extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
#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
@@ -129,7 +162,7 @@ struct QGLCMapCleanupHandler {
CMapEntryHash *cmap_hash;
GLCMapHash *qglcmap_hash;
};
-Q_GLOBAL_STATIC(QGLCMapCleanupHandler, cmap_handler);
+Q_GLOBAL_STATIC(QGLCMapCleanupHandler, cmap_handler)
static void cleanup_cmaps()
{
@@ -433,7 +466,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
if (!d->gpm)
return false;
}
- QString glxExt = QString(QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)));
+ QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
if (glxExt.contains(QLatin1String("GLX_SGI_video_sync"))) {
if (d->glFormat.swapInterval() == -1)
d->glFormat.setSwapInterval(0);
@@ -515,11 +548,26 @@ void *QGLContext::chooseVisual()
void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
{
Q_D(QGLContext);
- int spec[40];
+ 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;
@@ -547,28 +595,41 @@ void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
useTranspExtChecked = true;
}
- if (f.plane() && useTranspExt) {
+ 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()) {
- spec[i++] = GLX_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;
@@ -603,8 +664,86 @@ void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
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;
- return glXChooseVisual(xinfo->display(), xinfo->screen(), spec);
+
+
+ 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;
}
@@ -685,7 +824,7 @@ void QGLContext::swapBuffers() const
static qt_glXWaitVideoSyncSGI glXWaitVideoSyncSGI = 0;
static bool resolved = false;
if (!resolved) {
- QString glxExt = QString(QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)));
+ QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
if (glxExt.contains(QLatin1String("GLX_SGI_video_sync"))) {
#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
void *handle = dlopen(NULL, RTLD_LAZY);
@@ -697,10 +836,12 @@ void QGLContext::swapBuffers() const
if (!glXGetVideoSyncSGI)
#endif
{
+#if !defined(QT_NO_LIBRARY)
extern const QString qt_gl_library_name();
QLibrary lib(qt_gl_library_name());
glXGetVideoSyncSGI = (qt_glXGetVideoSyncSGI) lib.resolve("glXGetVideoSyncSGI");
glXWaitVideoSyncSGI = (qt_glXWaitVideoSyncSGI) lib.resolve("glXWaitVideoSyncSGI");
+#endif
}
}
resolved = true;
@@ -930,7 +1071,7 @@ void *QGLContext::getProcAddress(const QString &proc) const
if (resolved && !glXGetProcAddressARB)
return 0;
if (!glXGetProcAddressARB) {
- QString glxExt = QString(QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)));
+ QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
if (glxExt.contains(QLatin1String("GLX_ARB_get_proc_address"))) {
#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
void *handle = dlopen(NULL, RTLD_LAZY);
@@ -941,9 +1082,11 @@ void *QGLContext::getProcAddress(const QString &proc) const
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;
@@ -1165,7 +1308,6 @@ void QGLWidget::setContext(QGLContext *context,
QGLContext* oldcx = d->glcx;
d->glcx = context;
-
if (parentWidget()) {
// force creation of delay-created widgets
parentWidget()->winId();
@@ -1173,6 +1315,12 @@ void QGLWidget::setContext(QGLContext *context,
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))
@@ -1404,4 +1552,183 @@ void QGLExtensions::init()
}
}
+// 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;
+
+bool qt_resolveTextureFromPixmap()
+{
+ 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
+ }
+
+ QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
+ if (glxExt.contains(QLatin1String("GLX_EXT_texture_from_pixmap"))) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ glXBindTexImageEXT = (qt_glXBindTexImageEXT) dlsym(handle, "glXBindTexImageEXT");
+ glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) dlsym(handle, "glXReleaseTexImageEXT");
+ dlclose(handle);
+ }
+ if (!glXBindTexImageEXT)
+#endif
+ {
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ glXBindTexImageEXT = (qt_glXBindTexImageEXT) lib.resolve("glXBindTexImageEXT");
+ glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) lib.resolve("glXReleaseTexImageEXT");
+ }
+ }
+ }
+
+ return glXBindTexImageEXT && glXReleaseTexImageEXT;
+}
+#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+
+
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, const qint64 key,
+ QGLContext::BindOptions options)
+{
+#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
+ return 0;
+#else
+ Q_Q(QGLContext);
+
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+
+ if (!qt_resolveTextureFromPixmap())
+ return 0;
+
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ 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 = (Qt::HANDLE)glxPixmap;
+
+ // Make sure the cleanup hook gets called so we can delete the glx pixmap
+ pixmapData->is_cached = true;
+ }
+
+ 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