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#include "qoffscreenintegration_x11.h"
41
42#include <QByteArray>
43#include <QOpenGLContext>
44#include <QtPlatformHeaders/QGLXNativeContext>
45
46#include <X11/Xlib.h>
47#include <GL/glx.h>
48
49#include <QtGlxSupport/private/qglxconvenience_p.h>
50
51#include <qpa/qplatformsurface.h>
52#include <qsurface.h>
53
54QT_BEGIN_NAMESPACE
55
56class QOffscreenX11Info
57{
58public:
59 QOffscreenX11Info(QOffscreenX11Connection *connection)
60 : m_connection(connection)
61 {
62 }
63
64 Display *display() const {
65 return (Display *)m_connection->display();
66 }
67
68 Window root() const {
69 return DefaultRootWindow(display());
70 }
71
72 int screenNumber() const {
73 return m_connection->screenNumber();
74 }
75
76private:
77 QOffscreenX11Connection *m_connection;
78};
79
80QOffscreenX11Integration::~QOffscreenX11Integration() = default;
81
82bool QOffscreenX11Integration::hasCapability(QPlatformIntegration::Capability cap) const
83{
84 switch (cap) {
85 case OpenGL: return true;
86 case ThreadedOpenGL: return true;
87 case RasterGLSurface: return true;
88 default: return QOffscreenIntegration::hasCapability(cap);
89 }
90}
91
92QPlatformOpenGLContext *QOffscreenX11Integration::createPlatformOpenGLContext(QOpenGLContext *context) const
93{
94 auto &connection = nativeInterface()->m_connection;
95
96 if (!connection)
97 connection.reset(other: new QOffscreenX11Connection);
98
99 if (!connection->display())
100 return nullptr;
101
102 return new QOffscreenX11GLXContext(connection->x11Info(), context);
103}
104
105QOffscreenX11PlatformNativeInterface *QOffscreenX11Integration::nativeInterface() const
106{
107 if (!m_nativeInterface)
108 m_nativeInterface.reset(other: new QOffscreenX11PlatformNativeInterface);
109 return static_cast<QOffscreenX11PlatformNativeInterface *>(m_nativeInterface.data());
110}
111
112QOffscreenX11PlatformNativeInterface::~QOffscreenX11PlatformNativeInterface() = default;
113
114void *QOffscreenX11PlatformNativeInterface::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
115{
116 Q_UNUSED(screen)
117 if (resource.toLower() == QByteArrayLiteral("display") ) {
118 if (!m_connection)
119 m_connection.reset(other: new QOffscreenX11Connection);
120 return m_connection->display();
121 }
122 return nullptr;
123}
124
125#ifndef QT_NO_OPENGL
126void *QOffscreenX11PlatformNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) {
127 if (resource.toLower() == QByteArrayLiteral("glxconfig") ) {
128 if (context) {
129 QOffscreenX11GLXContext *glxPlatformContext = static_cast<QOffscreenX11GLXContext *>(context->handle());
130 if (glxPlatformContext)
131 return glxPlatformContext->glxConfig();
132 }
133 }
134 if (resource.toLower() == QByteArrayLiteral("glxcontext") ) {
135 if (context) {
136 QOffscreenX11GLXContext *glxPlatformContext = static_cast<QOffscreenX11GLXContext *>(context->handle());
137 if (glxPlatformContext)
138 return glxPlatformContext->glxContext();
139 }
140 }
141 return nullptr;
142}
143#endif
144
145QOffscreenX11Connection::QOffscreenX11Connection()
146{
147 XInitThreads();
148
149 QByteArray displayName = qgetenv(varName: "DISPLAY");
150 Display *display = XOpenDisplay(displayName.constData());
151 m_display = display;
152 m_screenNumber = m_display ? DefaultScreen(m_display) : -1;
153}
154
155QOffscreenX11Connection::~QOffscreenX11Connection()
156{
157 if (m_display)
158 XCloseDisplay((Display *)m_display);
159}
160
161QOffscreenX11Info *QOffscreenX11Connection::x11Info()
162{
163 if (!m_x11Info)
164 m_x11Info.reset(other: new QOffscreenX11Info(this));
165 return m_x11Info.data();
166}
167
168class QOffscreenX11GLXContextData
169{
170public:
171 QOffscreenX11Info *x11 = nullptr;
172 QSurfaceFormat format;
173 GLXContext context = nullptr;
174 GLXContext shareContext = nullptr;
175 GLXFBConfig config = nullptr;
176 Window window = 0;
177};
178
179static Window createDummyWindow(QOffscreenX11Info *x11, XVisualInfo *visualInfo)
180{
181 Colormap cmap = XCreateColormap(x11->display(), x11->root(), visualInfo->visual, AllocNone);
182 XSetWindowAttributes a;
183 a.background_pixel = WhitePixel(x11->display(), x11->screenNumber());
184 a.border_pixel = BlackPixel(x11->display(), x11->screenNumber());
185 a.colormap = cmap;
186
187
188 Window window = XCreateWindow(x11->display(), x11->root(),
189 0, 0, 100, 100,
190 0, visualInfo->depth, InputOutput, visualInfo->visual,
191 CWBackPixel|CWBorderPixel|CWColormap, &a);
192 XFreeColormap(x11->display(), cmap);
193 return window;
194}
195
196static Window createDummyWindow(QOffscreenX11Info *x11, GLXFBConfig config)
197{
198 XVisualInfo *visualInfo = glXGetVisualFromFBConfig(dpy: x11->display(), config);
199 if (Q_UNLIKELY(!visualInfo))
200 qFatal(msg: "Could not initialize GLX");
201 Window window = createDummyWindow(x11, visualInfo);
202 XFree(visualInfo);
203 return window;
204}
205
206QOffscreenX11GLXContext::QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context)
207 : d(new QOffscreenX11GLXContextData)
208{
209
210 d->x11 = x11;
211 d->format = context->format();
212
213 if (d->format.renderableType() == QSurfaceFormat::DefaultRenderableType)
214 d->format.setRenderableType(QSurfaceFormat::OpenGL);
215
216 if (d->format.renderableType() != QSurfaceFormat::OpenGL)
217 return;
218
219 d->shareContext = nullptr;
220 if (context->shareHandle())
221 d->shareContext = static_cast<QOffscreenX11GLXContext *>(context->shareHandle())->d->context;
222
223 GLXFBConfig config = qglx_findConfig(display: x11->display(), screen: x11->screenNumber(), format: d->format);
224 d->config = config;
225
226 if (config) {
227 d->context = glXCreateNewContext(dpy: x11->display(), config, GLX_RGBA_TYPE, shareList: d->shareContext, direct: true);
228 if (!d->context && d->shareContext) {
229 d->shareContext = nullptr;
230 // re-try without a shared glx context
231 d->context = glXCreateNewContext(dpy: x11->display(), config, GLX_RGBA_TYPE, shareList: nullptr, direct: true);
232 }
233
234 // Get the basic surface format details
235 if (d->context)
236 qglx_surfaceFormatFromGLXFBConfig(format: &d->format, display: x11->display(), config);
237
238 // Create a temporary window so that we can make the new context current
239 d->window = createDummyWindow(x11, config);
240 } else {
241 XVisualInfo *visualInfo = qglx_findVisualInfo(display: x11->display(), screen: 0, format: &d->format);
242 if (Q_UNLIKELY(!visualInfo))
243 qFatal(msg: "Could not initialize GLX");
244 d->context = glXCreateContext(dpy: x11->display(), vis: visualInfo, shareList: d->shareContext, direct: true);
245 if (!d->context && d->shareContext) {
246 // re-try without a shared glx context
247 d->shareContext = nullptr;
248 d->context = glXCreateContext(dpy: x11->display(), vis: visualInfo, shareList: nullptr, direct: true);
249 }
250
251 d->window = createDummyWindow(x11, visualInfo);
252 XFree(visualInfo);
253 }
254 if (d->context)
255 context->setNativeHandle(QVariant::fromValue<QGLXNativeContext>(value: QGLXNativeContext(d->context)));
256
257}
258
259QOffscreenX11GLXContext::~QOffscreenX11GLXContext()
260{
261 glXDestroyContext(dpy: d->x11->display(), ctx: d->context);
262 XDestroyWindow(d->x11->display(), d->window);
263}
264
265bool QOffscreenX11GLXContext::makeCurrent(QPlatformSurface *surface)
266{
267 QSize size = surface->surface()->size();
268
269 XResizeWindow(d->x11->display(), d->window, size.width(), size.height());
270 XSync(d->x11->display(), true);
271
272 if (glXMakeCurrent(dpy: d->x11->display(), drawable: d->window, ctx: d->context)) {
273 glViewport(x: 0, y: 0, width: size.width(), height: size.height());
274 return true;
275 }
276
277 return false;
278}
279
280void QOffscreenX11GLXContext::doneCurrent()
281{
282 glXMakeCurrent(dpy: d->x11->display(), drawable: 0, ctx: nullptr);
283}
284
285void QOffscreenX11GLXContext::swapBuffers(QPlatformSurface *)
286{
287}
288
289QFunctionPointer QOffscreenX11GLXContext::getProcAddress(const char *procName)
290{
291 return (QFunctionPointer)glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName));
292}
293
294QSurfaceFormat QOffscreenX11GLXContext::format() const
295{
296 return d->format;
297}
298
299bool QOffscreenX11GLXContext::isSharing() const
300{
301 return d->shareContext;
302}
303
304bool QOffscreenX11GLXContext::isValid() const
305{
306 return d->context && d->window;
307}
308
309void *QOffscreenX11GLXContext::glxContext() const
310{
311 return d->context;
312}
313
314void *QOffscreenX11GLXContext::glxConfig() const
315{
316 return d->config;
317}
318
319QT_END_NAMESPACE
320

source code of qtbase/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp