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