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