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