| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| // We have to include this before the X11 headers dragged in by |
| // qglxconvenience_p.h. |
| #include <QtCore/QByteArray> |
| #include <QtCore/QScopedPointer> |
| |
| #include <QtCore/qmetatype.h> |
| #include <QtCore/qtextstream.h> |
| #include "qglxconvenience_p.h" |
| |
| #include <QtCore/QLoggingCategory> |
| #include <QtCore/QVector> |
| #include <QtCore/QVarLengthArray> |
| |
| #include <GL/glxext.h> |
| |
| Q_LOGGING_CATEGORY(lcGlx, "qt.glx") |
| |
| enum { |
| XFocusOut = FocusOut, |
| XFocusIn = FocusIn, |
| XKeyPress = KeyPress, |
| XKeyRelease = KeyRelease, |
| XNone = None, |
| XRevertToParent = RevertToParent, |
| XGrayScale = GrayScale, |
| XCursorShape = CursorShape |
| }; |
| #undef FocusOut |
| #undef FocusIn |
| #undef KeyPress |
| #undef KeyRelease |
| #undef None |
| #undef RevertToParent |
| #undef GrayScale |
| #undef CursorShape |
| |
| #ifdef FontChange |
| #undef FontChange |
| #endif |
| |
| #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB |
| #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2 |
| #endif |
| |
| QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit, int flags) |
| { |
| QVector<int> spec; |
| |
| spec << GLX_LEVEL |
| << 0 |
| |
| << GLX_RENDER_TYPE |
| << GLX_RGBA_BIT |
| |
| << GLX_RED_SIZE |
| << qMax(1, format.redBufferSize()) |
| |
| << GLX_GREEN_SIZE |
| << qMax(1, format.greenBufferSize()) |
| |
| << GLX_BLUE_SIZE |
| << qMax(1, format.blueBufferSize()) |
| |
| << GLX_ALPHA_SIZE |
| << qMax(0, format.alphaBufferSize()); |
| |
| if (format.swapBehavior() != QSurfaceFormat::SingleBuffer) |
| spec << GLX_DOUBLEBUFFER |
| << True; |
| |
| if (format.stereo()) |
| spec << GLX_STEREO |
| << True; |
| |
| if (format.depthBufferSize() != -1) |
| spec << GLX_DEPTH_SIZE |
| << format.depthBufferSize(); |
| |
| if (format.stencilBufferSize() != -1) |
| spec << GLX_STENCIL_SIZE |
| << format.stencilBufferSize(); |
| |
| if (format.samples() > 1) |
| spec << GLX_SAMPLE_BUFFERS_ARB |
| << 1 |
| << GLX_SAMPLES_ARB |
| << format.samples(); |
| |
| if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace) |
| spec << GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB |
| << True; |
| |
| spec << GLX_DRAWABLE_TYPE |
| << drawableBit |
| |
| << XNone; |
| |
| return spec; |
| } |
| |
| namespace { |
| struct QXcbSoftwareOpenGLEnforcer { |
| QXcbSoftwareOpenGLEnforcer() { |
| // Allow forcing LIBGL_ALWAYS_SOFTWARE for Qt 5 applications only. |
| // This is most useful with drivers that only support OpenGL 1. |
| // We need OpenGL 2, but the user probably doesn't want |
| // LIBGL_ALWAYS_SOFTWARE in OpenGL 1 apps. |
| |
| if (!checkedForceSoftwareOpenGL) { |
| // If LIBGL_ALWAYS_SOFTWARE is already set, don't mess with it. |
| // We want to unset LIBGL_ALWAYS_SOFTWARE at the end so it does not |
| // get inherited by other processes, of course only if it wasn't |
| // already set before. |
| if (!qEnvironmentVariableIsEmpty("QT_XCB_FORCE_SOFTWARE_OPENGL") |
| && !qEnvironmentVariableIsSet("LIBGL_ALWAYS_SOFTWARE")) |
| forceSoftwareOpenGL = true; |
| |
| checkedForceSoftwareOpenGL = true; |
| } |
| |
| if (forceSoftwareOpenGL) |
| qputenv("LIBGL_ALWAYS_SOFTWARE", QByteArrayLiteral("1")); |
| } |
| |
| ~QXcbSoftwareOpenGLEnforcer() { |
| // unset LIBGL_ALWAYS_SOFTWARE now so other processes don't inherit it |
| if (forceSoftwareOpenGL) |
| qunsetenv("LIBGL_ALWAYS_SOFTWARE"); |
| } |
| |
| static bool checkedForceSoftwareOpenGL; |
| static bool forceSoftwareOpenGL; |
| }; |
| |
| bool QXcbSoftwareOpenGLEnforcer::checkedForceSoftwareOpenGL = false; |
| bool QXcbSoftwareOpenGLEnforcer::forceSoftwareOpenGL = false; |
| |
| template <class T> |
| struct QXlibScopedPointerDeleter { |
| static inline void cleanup(T *pointer) { |
| if (pointer) |
| XFree(pointer); |
| } |
| }; |
| |
| template <class T> |
| using QXlibPointer = QScopedPointer<T, QXlibScopedPointerDeleter<T>>; |
| |
| template <class T> |
| using QXlibArrayPointer = QScopedArrayPointer<T, QXlibScopedPointerDeleter<T>>; |
| } |
| |
| GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit, int flags) |
| { |
| QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer; |
| |
| GLXFBConfig config = 0; |
| |
| do { |
| const QVector<int> spec = qglx_buildSpec(format, drawableBit, flags); |
| |
| int confcount = 0; |
| QXlibArrayPointer<GLXFBConfig> configs(glXChooseFBConfig(display, screen, spec.constData(), &confcount)); |
| |
| if (!config && confcount > 0) { |
| config = configs[0]; |
| if (highestPixelFormat && !format.hasAlpha()) |
| break; |
| } |
| |
| const int requestedRed = qMax(0, format.redBufferSize()); |
| const int requestedGreen = qMax(0, format.greenBufferSize()); |
| const int requestedBlue = qMax(0, format.blueBufferSize()); |
| const int requestedAlpha = qMax(0, format.alphaBufferSize()); |
| |
| GLXFBConfig compatibleCandidate = nullptr; |
| for (int i = 0; i < confcount; i++) { |
| GLXFBConfig candidate = configs[i]; |
| |
| if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace) { |
| int srgbCapable = 0; |
| glXGetFBConfigAttrib(display, candidate, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); |
| if (!srgbCapable) |
| continue; |
| } |
| |
| QXlibPointer<XVisualInfo> visual(glXGetVisualFromFBConfig(display, candidate)); |
| int actualRed; |
| int actualGreen; |
| int actualBlue; |
| int actualAlpha; |
| glXGetFBConfigAttrib(display, candidate, GLX_RED_SIZE, &actualRed); |
| glXGetFBConfigAttrib(display, candidate, GLX_GREEN_SIZE, &actualGreen); |
| glXGetFBConfigAttrib(display, candidate, GLX_BLUE_SIZE, &actualBlue); |
| glXGetFBConfigAttrib(display, candidate, GLX_ALPHA_SIZE, &actualAlpha); |
| // Sometimes the visuals don't have a depth that includes the alpha channel. |
| actualAlpha = qMin(actualAlpha, visual->depth - actualRed - actualGreen - actualBlue); |
| |
| if (requestedRed && actualRed < requestedRed) |
| continue; |
| if (requestedGreen && actualGreen < requestedGreen) |
| continue; |
| if (requestedBlue && actualBlue < requestedBlue) |
| continue; |
| if (requestedAlpha && actualAlpha < requestedAlpha) |
| continue; |
| if (!compatibleCandidate) // Only pick up the first compatible one offered by the server |
| compatibleCandidate = candidate; |
| |
| if (requestedRed && actualRed != requestedRed) |
| continue; |
| if (requestedGreen && actualGreen != requestedGreen) |
| continue; |
| if (requestedBlue && actualBlue != requestedBlue) |
| continue; |
| if (requestedAlpha && actualAlpha != requestedAlpha) |
| continue; |
| |
| return candidate; |
| } |
| if (compatibleCandidate) { |
| qCDebug(lcGlx) << "qglx_findConfig: Found non-matching but compatible FBConfig"; |
| return compatibleCandidate; |
| } |
| } while (qglx_reduceFormat(&format)); |
| |
| if (!config) |
| qCWarning(lcGlx) << "qglx_findConfig: Failed to finding matching FBConfig for" << format; |
| |
| return config; |
| } |
| |
| XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit, int flags) |
| { |
| Q_ASSERT(format); |
| |
| XVisualInfo *visualInfo = 0; |
| |
| GLXFBConfig config = qglx_findConfig(display, screen, *format, false, drawableBit, flags); |
| if (config) |
| visualInfo = glXGetVisualFromFBConfig(display, config); |
| |
| if (visualInfo) { |
| qglx_surfaceFormatFromGLXFBConfig(format, display, config, flags); |
| return visualInfo; |
| } |
| |
| // attempt to fall back to glXChooseVisual |
| do { |
| QVector<int> attribs = qglx_buildSpec(*format, drawableBit, flags); |
| visualInfo = glXChooseVisual(display, screen, attribs.data()); |
| |
| if (visualInfo) { |
| qglx_surfaceFormatFromVisualInfo(format, display, visualInfo, flags); |
| return visualInfo; |
| } |
| } while (qglx_reduceFormat(format)); |
| |
| return visualInfo; |
| } |
| |
| void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, int flags) |
| { |
| int redSize = 0; |
| int greenSize = 0; |
| int blueSize = 0; |
| int alphaSize = 0; |
| int depthSize = 0; |
| int stencilSize = 0; |
| int sampleBuffers = 0; |
| int sampleCount = 0; |
| int stereo = 0; |
| int srgbCapable = 0; |
| |
| glXGetFBConfigAttrib(display, config, GLX_RED_SIZE, &redSize); |
| glXGetFBConfigAttrib(display, config, GLX_GREEN_SIZE, &greenSize); |
| glXGetFBConfigAttrib(display, config, GLX_BLUE_SIZE, &blueSize); |
| glXGetFBConfigAttrib(display, config, GLX_ALPHA_SIZE, &alphaSize); |
| glXGetFBConfigAttrib(display, config, GLX_DEPTH_SIZE, &depthSize); |
| glXGetFBConfigAttrib(display, config, GLX_STENCIL_SIZE, &stencilSize); |
| glXGetFBConfigAttrib(display, config, GLX_SAMPLE_BUFFERS_ARB, &sampleBuffers); |
| glXGetFBConfigAttrib(display, config, GLX_STEREO, &stereo); |
| if (flags & QGLX_SUPPORTS_SRGB) |
| glXGetFBConfigAttrib(display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); |
| |
| format->setRedBufferSize(redSize); |
| format->setGreenBufferSize(greenSize); |
| format->setBlueBufferSize(blueSize); |
| format->setAlphaBufferSize(alphaSize); |
| format->setDepthBufferSize(depthSize); |
| format->setStencilBufferSize(stencilSize); |
| if (sampleBuffers) { |
| glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleCount); |
| format->setSamples(sampleCount); |
| } |
| format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace); |
| |
| format->setStereo(stereo); |
| } |
| |
| void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo, int flags) |
| { |
| int redSize = 0; |
| int greenSize = 0; |
| int blueSize = 0; |
| int alphaSize = 0; |
| int depthSize = 0; |
| int stencilSize = 0; |
| int sampleBuffers = 0; |
| int sampleCount = 0; |
| int stereo = 0; |
| int srgbCapable = 0; |
| |
| glXGetConfig(display, visualInfo, GLX_RED_SIZE, &redSize); |
| glXGetConfig(display, visualInfo, GLX_GREEN_SIZE, &greenSize); |
| glXGetConfig(display, visualInfo, GLX_BLUE_SIZE, &blueSize); |
| glXGetConfig(display, visualInfo, GLX_ALPHA_SIZE, &alphaSize); |
| glXGetConfig(display, visualInfo, GLX_DEPTH_SIZE, &depthSize); |
| glXGetConfig(display, visualInfo, GLX_STENCIL_SIZE, &stencilSize); |
| glXGetConfig(display, visualInfo, GLX_SAMPLE_BUFFERS_ARB, &sampleBuffers); |
| glXGetConfig(display, visualInfo, GLX_STEREO, &stereo); |
| if (flags & QGLX_SUPPORTS_SRGB) |
| glXGetConfig(display, visualInfo, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); |
| |
| format->setRedBufferSize(redSize); |
| format->setGreenBufferSize(greenSize); |
| format->setBlueBufferSize(blueSize); |
| format->setAlphaBufferSize(alphaSize); |
| format->setDepthBufferSize(depthSize); |
| format->setStencilBufferSize(stencilSize); |
| if (sampleBuffers) { |
| glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleCount); |
| format->setSamples(sampleCount); |
| } |
| format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace); |
| |
| format->setStereo(stereo); |
| } |
| |
| bool qglx_reduceFormat(QSurfaceFormat *format) |
| { |
| Q_ASSERT(format); |
| if (std::max(std::max(format->redBufferSize(), format->greenBufferSize()), format->blueBufferSize()) > 8) { |
| if (format->alphaBufferSize() > 2) { |
| // First try to match 10 10 10 2 |
| format->setAlphaBufferSize(2); |
| return true; |
| } |
| |
| format->setRedBufferSize(std::min(format->redBufferSize(), 8)); |
| format->setGreenBufferSize(std::min(format->greenBufferSize(), 8)); |
| format->setBlueBufferSize(std::min(format->blueBufferSize(), 8)); |
| return true; |
| } |
| |
| if (format->redBufferSize() > 1) { |
| format->setRedBufferSize(1); |
| return true; |
| } |
| |
| if (format->greenBufferSize() > 1) { |
| format->setGreenBufferSize(1); |
| return true; |
| } |
| |
| if (format->blueBufferSize() > 1) { |
| format->setBlueBufferSize(1); |
| return true; |
| } |
| |
| if (format->swapBehavior() != QSurfaceFormat::SingleBuffer){ |
| format->setSwapBehavior(QSurfaceFormat::SingleBuffer); |
| return true; |
| } |
| |
| if (format->samples() > 1) { |
| format->setSamples(qMin(16, format->samples() / 2)); |
| return true; |
| } |
| |
| if (format->depthBufferSize() >= 32) { |
| format->setDepthBufferSize(24); |
| return true; |
| } |
| |
| if (format->depthBufferSize() > 1) { |
| format->setDepthBufferSize(1); |
| return true; |
| } |
| |
| if (format->depthBufferSize() > 0) { |
| format->setDepthBufferSize(0); |
| return true; |
| } |
| |
| if (format->hasAlpha()) { |
| format->setAlphaBufferSize(0); |
| return true; |
| } |
| |
| if (format->stencilBufferSize() > 1) { |
| format->setStencilBufferSize(1); |
| return true; |
| } |
| |
| if (format->stencilBufferSize() > 0) { |
| format->setStencilBufferSize(0); |
| return true; |
| } |
| |
| if (format->stereo()) { |
| format->setStereo(false); |
| return true; |
| } |
| |
| if (format->colorSpace() == QSurfaceFormat::sRGBColorSpace) { |
| format->setColorSpace(QSurfaceFormat::DefaultColorSpace); |
| return true; |
| } |
| |
| return false; |
| } |