1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40// We have to include this before the X11 headers dragged in by
41// qglxconvenience_p.h.
42#include <QtCore/QByteArray>
43#include <QtCore/QScopedPointer>
44
45#include <QtCore/qmetatype.h>
46#include <QtCore/qtextstream.h>
47#include "qglxconvenience_p.h"
48
49#include <QtCore/QLoggingCategory>
50#include <QtCore/QVector>
51#include <QtCore/QVarLengthArray>
52
53#include <GL/glxext.h>
54
55Q_LOGGING_CATEGORY(lcGlx, "qt.glx")
56
57enum {
58 XFocusOut = FocusOut,
59 XFocusIn = FocusIn,
60 XKeyPress = KeyPress,
61 XKeyRelease = KeyRelease,
62 XNone = None,
63 XRevertToParent = RevertToParent,
64 XGrayScale = GrayScale,
65 XCursorShape = CursorShape
66};
67#undef FocusOut
68#undef FocusIn
69#undef KeyPress
70#undef KeyRelease
71#undef None
72#undef RevertToParent
73#undef GrayScale
74#undef CursorShape
75
76#ifdef FontChange
77#undef FontChange
78#endif
79
80#ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
81#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
82#endif
83
84QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit, int flags)
85{
86 QVector<int> spec;
87
88 spec << GLX_LEVEL
89 << 0
90
91 << GLX_RENDER_TYPE
92 << GLX_RGBA_BIT
93
94 << GLX_RED_SIZE
95 << qMax(a: 1, b: format.redBufferSize())
96
97 << GLX_GREEN_SIZE
98 << qMax(a: 1, b: format.greenBufferSize())
99
100 << GLX_BLUE_SIZE
101 << qMax(a: 1, b: format.blueBufferSize())
102
103 << GLX_ALPHA_SIZE
104 << qMax(a: 0, b: format.alphaBufferSize());
105
106 if (format.swapBehavior() != QSurfaceFormat::SingleBuffer)
107 spec << GLX_DOUBLEBUFFER
108 << True;
109
110 if (format.stereo())
111 spec << GLX_STEREO
112 << True;
113
114 if (format.depthBufferSize() != -1)
115 spec << GLX_DEPTH_SIZE
116 << format.depthBufferSize();
117
118 if (format.stencilBufferSize() != -1)
119 spec << GLX_STENCIL_SIZE
120 << format.stencilBufferSize();
121
122 if (format.samples() > 1)
123 spec << GLX_SAMPLE_BUFFERS_ARB
124 << 1
125 << GLX_SAMPLES_ARB
126 << format.samples();
127
128 if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace)
129 spec << GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
130 << True;
131
132 spec << GLX_DRAWABLE_TYPE
133 << drawableBit
134
135 << XNone;
136
137 return spec;
138}
139
140namespace {
141struct QXcbSoftwareOpenGLEnforcer {
142 QXcbSoftwareOpenGLEnforcer() {
143 // Allow forcing LIBGL_ALWAYS_SOFTWARE for Qt 5 applications only.
144 // This is most useful with drivers that only support OpenGL 1.
145 // We need OpenGL 2, but the user probably doesn't want
146 // LIBGL_ALWAYS_SOFTWARE in OpenGL 1 apps.
147
148 if (!checkedForceSoftwareOpenGL) {
149 // If LIBGL_ALWAYS_SOFTWARE is already set, don't mess with it.
150 // We want to unset LIBGL_ALWAYS_SOFTWARE at the end so it does not
151 // get inherited by other processes, of course only if it wasn't
152 // already set before.
153 if (!qEnvironmentVariableIsEmpty(varName: "QT_XCB_FORCE_SOFTWARE_OPENGL")
154 && !qEnvironmentVariableIsSet(varName: "LIBGL_ALWAYS_SOFTWARE"))
155 forceSoftwareOpenGL = true;
156
157 checkedForceSoftwareOpenGL = true;
158 }
159
160 if (forceSoftwareOpenGL)
161 qputenv(varName: "LIBGL_ALWAYS_SOFTWARE", QByteArrayLiteral("1"));
162 }
163
164 ~QXcbSoftwareOpenGLEnforcer() {
165 // unset LIBGL_ALWAYS_SOFTWARE now so other processes don't inherit it
166 if (forceSoftwareOpenGL)
167 qunsetenv(varName: "LIBGL_ALWAYS_SOFTWARE");
168 }
169
170 static bool checkedForceSoftwareOpenGL;
171 static bool forceSoftwareOpenGL;
172};
173
174bool QXcbSoftwareOpenGLEnforcer::checkedForceSoftwareOpenGL = false;
175bool QXcbSoftwareOpenGLEnforcer::forceSoftwareOpenGL = false;
176
177template <class T>
178struct QXlibScopedPointerDeleter {
179 static inline void cleanup(T *pointer) {
180 if (pointer)
181 XFree(pointer);
182 }
183};
184
185template <class T>
186using QXlibPointer = QScopedPointer<T, QXlibScopedPointerDeleter<T>>;
187
188template <class T>
189using QXlibArrayPointer = QScopedArrayPointer<T, QXlibScopedPointerDeleter<T>>;
190}
191
192GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit, int flags)
193{
194 QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer;
195
196 GLXFBConfig config = nullptr;
197
198 do {
199 const QVector<int> spec = qglx_buildSpec(format, drawableBit, flags);
200
201 int confcount = 0;
202 QXlibArrayPointer<GLXFBConfig> configs(glXChooseFBConfig(dpy: display, screen, attribList: spec.constData(), nitems: &confcount));
203
204 if (!config && confcount > 0) {
205 config = configs[0];
206 if (highestPixelFormat && !format.hasAlpha())
207 break;
208 }
209
210 const int requestedRed = qMax(a: 0, b: format.redBufferSize());
211 const int requestedGreen = qMax(a: 0, b: format.greenBufferSize());
212 const int requestedBlue = qMax(a: 0, b: format.blueBufferSize());
213 const int requestedAlpha = qMax(a: 0, b: format.alphaBufferSize());
214
215 GLXFBConfig compatibleCandidate = nullptr;
216 for (int i = 0; i < confcount; i++) {
217 GLXFBConfig candidate = configs[i];
218
219 if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace) {
220 int srgbCapable = 0;
221 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, value: &srgbCapable);
222 if (!srgbCapable)
223 continue;
224 }
225
226 QXlibPointer<XVisualInfo> visual(glXGetVisualFromFBConfig(dpy: display, config: candidate));
227 if (!visual)
228 continue;
229 int actualRed;
230 int actualGreen;
231 int actualBlue;
232 int actualAlpha;
233 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_RED_SIZE, value: &actualRed);
234 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_GREEN_SIZE, value: &actualGreen);
235 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_BLUE_SIZE, value: &actualBlue);
236 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_ALPHA_SIZE, value: &actualAlpha);
237 // Sometimes the visuals don't have a depth that includes the alpha channel.
238 actualAlpha = qMin(a: actualAlpha, b: visual->depth - actualRed - actualGreen - actualBlue);
239
240 if (requestedRed && actualRed < requestedRed)
241 continue;
242 if (requestedGreen && actualGreen < requestedGreen)
243 continue;
244 if (requestedBlue && actualBlue < requestedBlue)
245 continue;
246 if (requestedAlpha && actualAlpha < requestedAlpha)
247 continue;
248 if (!compatibleCandidate) // Only pick up the first compatible one offered by the server
249 compatibleCandidate = candidate;
250
251 if (requestedRed && actualRed != requestedRed)
252 continue;
253 if (requestedGreen && actualGreen != requestedGreen)
254 continue;
255 if (requestedBlue && actualBlue != requestedBlue)
256 continue;
257 if (requestedAlpha && actualAlpha != requestedAlpha)
258 continue;
259
260 return candidate;
261 }
262 if (compatibleCandidate) {
263 qCDebug(lcGlx) << "qglx_findConfig: Found non-matching but compatible FBConfig";
264 return compatibleCandidate;
265 }
266 } while (qglx_reduceFormat(format: &format));
267
268 if (!config)
269 qCWarning(lcGlx) << "qglx_findConfig: Failed to finding matching FBConfig for" << format;
270
271 return config;
272}
273
274XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit, int flags)
275{
276 Q_ASSERT(format);
277
278 XVisualInfo *visualInfo = nullptr;
279
280 GLXFBConfig config = qglx_findConfig(display, screen, format: *format, highestPixelFormat: false, drawableBit, flags);
281 if (config)
282 visualInfo = glXGetVisualFromFBConfig(dpy: display, config);
283
284 if (visualInfo) {
285 qglx_surfaceFormatFromGLXFBConfig(format, display, config, flags);
286 return visualInfo;
287 }
288
289 // attempt to fall back to glXChooseVisual
290 do {
291 QVector<int> attribs = qglx_buildSpec(format: *format, drawableBit, flags);
292 visualInfo = glXChooseVisual(dpy: display, screen, attribList: attribs.data());
293
294 if (visualInfo) {
295 qglx_surfaceFormatFromVisualInfo(format, display, visualInfo, flags);
296 return visualInfo;
297 }
298 } while (qglx_reduceFormat(format));
299
300 return visualInfo;
301}
302
303void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, int flags)
304{
305 int redSize = 0;
306 int greenSize = 0;
307 int blueSize = 0;
308 int alphaSize = 0;
309 int depthSize = 0;
310 int stencilSize = 0;
311 int sampleBuffers = 0;
312 int sampleCount = 0;
313 int stereo = 0;
314 int srgbCapable = 0;
315
316 glXGetFBConfigAttrib(dpy: display, config, GLX_RED_SIZE, value: &redSize);
317 glXGetFBConfigAttrib(dpy: display, config, GLX_GREEN_SIZE, value: &greenSize);
318 glXGetFBConfigAttrib(dpy: display, config, GLX_BLUE_SIZE, value: &blueSize);
319 glXGetFBConfigAttrib(dpy: display, config, GLX_ALPHA_SIZE, value: &alphaSize);
320 glXGetFBConfigAttrib(dpy: display, config, GLX_DEPTH_SIZE, value: &depthSize);
321 glXGetFBConfigAttrib(dpy: display, config, GLX_STENCIL_SIZE, value: &stencilSize);
322 glXGetFBConfigAttrib(dpy: display, config, GLX_SAMPLE_BUFFERS_ARB, value: &sampleBuffers);
323 glXGetFBConfigAttrib(dpy: display, config, GLX_STEREO, value: &stereo);
324 if (flags & QGLX_SUPPORTS_SRGB)
325 glXGetFBConfigAttrib(dpy: display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, value: &srgbCapable);
326
327 format->setRedBufferSize(redSize);
328 format->setGreenBufferSize(greenSize);
329 format->setBlueBufferSize(blueSize);
330 format->setAlphaBufferSize(alphaSize);
331 format->setDepthBufferSize(depthSize);
332 format->setStencilBufferSize(stencilSize);
333 if (sampleBuffers) {
334 glXGetFBConfigAttrib(dpy: display, config, GLX_SAMPLES_ARB, value: &sampleCount);
335 format->setSamples(sampleCount);
336 }
337 format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace);
338
339 format->setStereo(stereo);
340}
341
342void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo, int flags)
343{
344 int redSize = 0;
345 int greenSize = 0;
346 int blueSize = 0;
347 int alphaSize = 0;
348 int depthSize = 0;
349 int stencilSize = 0;
350 int sampleBuffers = 0;
351 int sampleCount = 0;
352 int stereo = 0;
353 int srgbCapable = 0;
354
355 glXGetConfig(dpy: display, visual: visualInfo, GLX_RED_SIZE, value: &redSize);
356 glXGetConfig(dpy: display, visual: visualInfo, GLX_GREEN_SIZE, value: &greenSize);
357 glXGetConfig(dpy: display, visual: visualInfo, GLX_BLUE_SIZE, value: &blueSize);
358 glXGetConfig(dpy: display, visual: visualInfo, GLX_ALPHA_SIZE, value: &alphaSize);
359 glXGetConfig(dpy: display, visual: visualInfo, GLX_DEPTH_SIZE, value: &depthSize);
360 glXGetConfig(dpy: display, visual: visualInfo, GLX_STENCIL_SIZE, value: &stencilSize);
361 glXGetConfig(dpy: display, visual: visualInfo, GLX_SAMPLE_BUFFERS_ARB, value: &sampleBuffers);
362 glXGetConfig(dpy: display, visual: visualInfo, GLX_STEREO, value: &stereo);
363 if (flags & QGLX_SUPPORTS_SRGB)
364 glXGetConfig(dpy: display, visual: visualInfo, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, value: &srgbCapable);
365
366 format->setRedBufferSize(redSize);
367 format->setGreenBufferSize(greenSize);
368 format->setBlueBufferSize(blueSize);
369 format->setAlphaBufferSize(alphaSize);
370 format->setDepthBufferSize(depthSize);
371 format->setStencilBufferSize(stencilSize);
372 if (sampleBuffers) {
373 glXGetConfig(dpy: display, visual: visualInfo, GLX_SAMPLES_ARB, value: &sampleCount);
374 format->setSamples(sampleCount);
375 }
376 format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace);
377
378 format->setStereo(stereo);
379}
380
381bool qglx_reduceFormat(QSurfaceFormat *format)
382{
383 Q_ASSERT(format);
384 if (std::max(a: std::max(a: format->redBufferSize(), b: format->greenBufferSize()), b: format->blueBufferSize()) > 8) {
385 if (format->alphaBufferSize() > 2) {
386 // First try to match 10 10 10 2
387 format->setAlphaBufferSize(2);
388 return true;
389 }
390
391 format->setRedBufferSize(std::min(a: format->redBufferSize(), b: 8));
392 format->setGreenBufferSize(std::min(a: format->greenBufferSize(), b: 8));
393 format->setBlueBufferSize(std::min(a: format->blueBufferSize(), b: 8));
394 return true;
395 }
396
397 if (format->redBufferSize() > 1) {
398 format->setRedBufferSize(1);
399 return true;
400 }
401
402 if (format->greenBufferSize() > 1) {
403 format->setGreenBufferSize(1);
404 return true;
405 }
406
407 if (format->blueBufferSize() > 1) {
408 format->setBlueBufferSize(1);
409 return true;
410 }
411
412 if (format->swapBehavior() != QSurfaceFormat::SingleBuffer){
413 format->setSwapBehavior(QSurfaceFormat::SingleBuffer);
414 return true;
415 }
416
417 if (format->samples() > 1) {
418 format->setSamples(qMin(a: 16, b: format->samples() / 2));
419 return true;
420 }
421
422 if (format->depthBufferSize() >= 32) {
423 format->setDepthBufferSize(24);
424 return true;
425 }
426
427 if (format->depthBufferSize() > 1) {
428 format->setDepthBufferSize(1);
429 return true;
430 }
431
432 if (format->depthBufferSize() > 0) {
433 format->setDepthBufferSize(0);
434 return true;
435 }
436
437 if (format->hasAlpha()) {
438 format->setAlphaBufferSize(0);
439 return true;
440 }
441
442 if (format->stencilBufferSize() > 1) {
443 format->setStencilBufferSize(1);
444 return true;
445 }
446
447 if (format->stencilBufferSize() > 0) {
448 format->setStencilBufferSize(0);
449 return true;
450 }
451
452 if (format->stereo()) {
453 format->setStereo(false);
454 return true;
455 }
456
457 if (format->colorSpace() == QSurfaceFormat::sRGBColorSpace) {
458 format->setColorSpace(QSurfaceFormat::DefaultColorSpace);
459 return true;
460 }
461
462 return false;
463}
464

source code of qtbase/src/platformsupport/glxconvenience/qglxconvenience.cpp