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
54 QT_BEGIN_NAMESPACE
55
56 class QOffscreenX11Info
57 {
58 public:
QOffscreenX11Info(QOffscreenX11Connection * connection)59 QOffscreenX11Info(QOffscreenX11Connection *connection)
60 : m_connection(connection)
61 {
62 }
63
display() const64 Display *display() const {
65 return (Display *)m_connection->display();
66 }
67
root() const68 Window root() const {
69 return DefaultRootWindow(display());
70 }
71
screenNumber() const72 int screenNumber() const {
73 return m_connection->screenNumber();
74 }
75
76 private:
77 QOffscreenX11Connection *m_connection;
78 };
79
80 QOffscreenX11Integration::~QOffscreenX11Integration() = default;
81
hasCapability(QPlatformIntegration::Capability cap) const82 bool 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
createPlatformOpenGLContext(QOpenGLContext * context) const92 QPlatformOpenGLContext *QOffscreenX11Integration::createPlatformOpenGLContext(QOpenGLContext *context) const
93 {
94 auto &connection = nativeInterface()->m_connection;
95
96 if (!connection)
97 connection.reset(new QOffscreenX11Connection);
98
99 if (!connection->display())
100 return nullptr;
101
102 return new QOffscreenX11GLXContext(connection->x11Info(), context);
103 }
104
nativeInterface() const105 QOffscreenX11PlatformNativeInterface *QOffscreenX11Integration::nativeInterface() const
106 {
107 if (!m_nativeInterface)
108 m_nativeInterface.reset(new QOffscreenX11PlatformNativeInterface);
109 return static_cast<QOffscreenX11PlatformNativeInterface *>(m_nativeInterface.data());
110 }
111
112 QOffscreenX11PlatformNativeInterface::~QOffscreenX11PlatformNativeInterface() = default;
113
nativeResourceForScreen(const QByteArray & resource,QScreen * screen)114 void *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(new QOffscreenX11Connection);
120 return m_connection->display();
121 }
122 return nullptr;
123 }
124
125 #ifndef QT_NO_OPENGL
nativeResourceForContext(const QByteArray & resource,QOpenGLContext * context)126 void *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
QOffscreenX11Connection()145 QOffscreenX11Connection::QOffscreenX11Connection()
146 {
147 XInitThreads();
148
149 QByteArray displayName = qgetenv("DISPLAY");
150 Display *display = XOpenDisplay(displayName.constData());
151 m_display = display;
152 m_screenNumber = m_display ? DefaultScreen(m_display) : -1;
153 }
154
~QOffscreenX11Connection()155 QOffscreenX11Connection::~QOffscreenX11Connection()
156 {
157 if (m_display)
158 XCloseDisplay((Display *)m_display);
159 }
160
x11Info()161 QOffscreenX11Info *QOffscreenX11Connection::x11Info()
162 {
163 if (!m_x11Info)
164 m_x11Info.reset(new QOffscreenX11Info(this));
165 return m_x11Info.data();
166 }
167
168 class QOffscreenX11GLXContextData
169 {
170 public:
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
createDummyWindow(QOffscreenX11Info * x11,XVisualInfo * visualInfo)179 static 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
createDummyWindow(QOffscreenX11Info * x11,GLXFBConfig config)196 static Window createDummyWindow(QOffscreenX11Info *x11, GLXFBConfig config)
197 {
198 XVisualInfo *visualInfo = glXGetVisualFromFBConfig(x11->display(), config);
199 if (Q_UNLIKELY(!visualInfo))
200 qFatal("Could not initialize GLX");
201 Window window = createDummyWindow(x11, visualInfo);
202 XFree(visualInfo);
203 return window;
204 }
205
QOffscreenX11GLXContext(QOffscreenX11Info * x11,QOpenGLContext * context)206 QOffscreenX11GLXContext::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(x11->display(), x11->screenNumber(), d->format);
224 d->config = config;
225
226 if (config) {
227 d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, d->shareContext, true);
228 if (!d->context && d->shareContext) {
229 d->shareContext = nullptr;
230 // re-try without a shared glx context
231 d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, nullptr, true);
232 }
233
234 // Get the basic surface format details
235 if (d->context)
236 qglx_surfaceFormatFromGLXFBConfig(&d->format, 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(x11->display(), 0, &d->format);
242 if (Q_UNLIKELY(!visualInfo))
243 qFatal("Could not initialize GLX");
244 d->context = glXCreateContext(x11->display(), visualInfo, d->shareContext, true);
245 if (!d->context && d->shareContext) {
246 // re-try without a shared glx context
247 d->shareContext = nullptr;
248 d->context = glXCreateContext(x11->display(), visualInfo, nullptr, true);
249 }
250
251 d->window = createDummyWindow(x11, visualInfo);
252 XFree(visualInfo);
253 }
254 if (d->context)
255 context->setNativeHandle(QVariant::fromValue<QGLXNativeContext>(QGLXNativeContext(d->context)));
256
257 }
258
~QOffscreenX11GLXContext()259 QOffscreenX11GLXContext::~QOffscreenX11GLXContext()
260 {
261 glXDestroyContext(d->x11->display(), d->context);
262 XDestroyWindow(d->x11->display(), d->window);
263 }
264
makeCurrent(QPlatformSurface * surface)265 bool 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(d->x11->display(), d->window, d->context)) {
273 glViewport(0, 0, size.width(), size.height());
274 return true;
275 }
276
277 return false;
278 }
279
doneCurrent()280 void QOffscreenX11GLXContext::doneCurrent()
281 {
282 glXMakeCurrent(d->x11->display(), 0, nullptr);
283 }
284
swapBuffers(QPlatformSurface *)285 void QOffscreenX11GLXContext::swapBuffers(QPlatformSurface *)
286 {
287 }
288
getProcAddress(const char * procName)289 QFunctionPointer QOffscreenX11GLXContext::getProcAddress(const char *procName)
290 {
291 return (QFunctionPointer)glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName));
292 }
293
format() const294 QSurfaceFormat QOffscreenX11GLXContext::format() const
295 {
296 return d->format;
297 }
298
isSharing() const299 bool QOffscreenX11GLXContext::isSharing() const
300 {
301 return d->shareContext;
302 }
303
isValid() const304 bool QOffscreenX11GLXContext::isValid() const
305 {
306 return d->context && d->window;
307 }
308
glxContext() const309 void *QOffscreenX11GLXContext::glxContext() const
310 {
311 return d->context;
312 }
313
glxConfig() const314 void *QOffscreenX11GLXContext::glxConfig() const
315 {
316 return d->config;
317 }
318
319 QT_END_NAMESPACE
320