diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qglxintegration.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qglxintegration.cpp | 172 |
1 files changed, 151 insertions, 21 deletions
diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index 9a56455940..3ce0041aeb 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -54,6 +54,7 @@ #include "qglxintegration.h" #include <QtPlatformSupport/private/qglxconvenience_p.h> +#include <QtPlatformHeaders/QGLXNativeContext> #if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) #include <dlfcn.h> @@ -83,31 +84,32 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXC #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 #endif -static Window createDummyWindow(QXcbScreen *screen, XVisualInfo *visualInfo) +static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin) { - Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(screen), screen->root(), visualInfo->visual, AllocNone); + Colormap cmap = XCreateColormap(dpy, rootWin, visualInfo->visual, AllocNone); XSetWindowAttributes a; - a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(screen), screen->screenNumber()); - a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(screen), screen->screenNumber()); + a.background_pixel = WhitePixel(dpy, screenNumber); + a.border_pixel = BlackPixel(dpy, screenNumber); a.colormap = cmap; + a.override_redirect = true; - Window window = XCreateWindow(DISPLAY_FROM_XCB(screen), screen->root(), + Window window = XCreateWindow(dpy, rootWin, 0, 0, 100, 100, 0, visualInfo->depth, InputOutput, visualInfo->visual, - CWBackPixel|CWBorderPixel|CWColormap, &a); + CWBackPixel|CWBorderPixel|CWColormap|CWOverrideRedirect, &a); #ifndef QT_NO_DEBUG - XStoreName(DISPLAY_FROM_XCB(screen), window, "Qt GLX dummy window"); + XStoreName(dpy, window, "Qt GLX dummy window"); #endif - XFreeColormap(DISPLAY_FROM_XCB(screen), cmap); + XFreeColormap(dpy, cmap); return window; } -static Window createDummyWindow(QXcbScreen *screen, GLXFBConfig config) +static Window createDummyWindow(Display *dpy, GLXFBConfig config, int screenNumber, Window rootWin) { - XVisualInfo *visualInfo = glXGetVisualFromFBConfig(DISPLAY_FROM_XCB(screen), config); + XVisualInfo *visualInfo = glXGetVisualFromFBConfig(dpy, config); if (!visualInfo) qFatal("Could not initialize GLX"); - Window window = createDummyWindow(screen, visualInfo); + Window window = createDummyWindow(dpy, visualInfo, screenNumber, rootWin); XFree(visualInfo); return window; } @@ -160,14 +162,25 @@ static void updateFormatFromContext(QSurfaceFormat &format) } } -QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share) +QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share, + const QVariant &nativeHandle) : QPlatformOpenGLContext() , m_screen(screen) + , m_config(0) , m_context(0) , m_shareContext(0) , m_format(format) , m_isPBufferCurrent(false) , m_swapInterval(-1) + , m_ownsContext(nativeHandle.isNull()) +{ + if (nativeHandle.isNull()) + init(screen, share); + else + init(screen, share, nativeHandle); +} + +void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) { if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType) m_format.setRenderableType(QSurfaceFormat::OpenGL); @@ -178,6 +191,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat m_shareContext = static_cast<const QGLXContext*>(share)->glxContext(); GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),m_format); + m_config = config; XVisualInfo *visualInfo = 0; Window window = 0; // Temporary window used to query OpenGL context @@ -195,7 +209,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat && (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains("GLX_EXT_create_context_es2_profile")))) { // Try to create an OpenGL context for each known OpenGL version in descending // order from the requested version. - const int requestedVersion = format.majorVersion() * 10 + qMin(format.minorVersion(), 9); + const int requestedVersion = m_format.majorVersion() * 10 + qMin(m_format.minorVersion(), 9); QVector<int> glVersions; if (m_format.renderableType() == QSurfaceFormat::OpenGL) { @@ -282,10 +296,10 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat // Get the basic surface format details if (m_context) - qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config, m_context); + qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config); // Create a temporary window so that we can make the new context current - window = createDummyWindow(screen, config); + window = createDummyWindow(DISPLAY_FROM_XCB(screen), config, screen->screenNumber(), screen->root()); } else { // requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out if (m_format.renderableType() == QSurfaceFormat::OpenGLES) @@ -303,7 +317,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat } // Create a temporary window so that we can make the new context current - window = createDummyWindow(screen, visualInfo); + window = createDummyWindow(DISPLAY_FROM_XCB(screen), visualInfo, screen->screenNumber(), screen->root()); XFree(visualInfo); } @@ -320,9 +334,125 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat XDestroyWindow(DISPLAY_FROM_XCB(screen), window); } +void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share, const QVariant &nativeHandle) +{ + if (!nativeHandle.canConvert<QGLXNativeContext>()) { + qWarning("QGLXContext: Requires a QGLXNativeContext"); + return; + } + QGLXNativeContext handle = nativeHandle.value<QGLXNativeContext>(); + GLXContext context = handle.context(); + if (!context) { + qWarning("QGLXContext: No GLXContext given"); + return; + } + + // Use the provided Display, if available. If not, use our own. It may still work. + Display *dpy = handle.display(); + if (!dpy) + dpy = DISPLAY_FROM_XCB(screen); + + // Legacy contexts created using glXCreateContext are created using a visual + // and the FBConfig cannot be queried. The only way to adapt these contexts + // is to figure out the visual id. + XVisualInfo *vinfo = 0; + // If the VisualID is provided use it. + VisualID vid = handle.visualId(); + if (!vid) { + // In the absence of the VisualID figure it out from the window. + Window wnd = handle.window(); + if (wnd) { + XWindowAttributes attrs; + XGetWindowAttributes(dpy, wnd, &attrs); + vid = XVisualIDFromVisual(attrs.visual); + } + } + if (vid) { + XVisualInfo v; + v.screen = screen->screenNumber(); + v.visualid = vid; + int n = 0; + vinfo = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask, &v, &n); + if (n < 1) { + XFree(vinfo); + vinfo = 0; + } + } + + // For contexts created with an FBConfig using the modern functions providing the + // visual or window is not mandatory. Just query the config from the context. + GLXFBConfig config = 0; + if (!vinfo) { + int configId = 0; + if (glXQueryContext(dpy, context, GLX_FBCONFIG_ID, &configId) != Success) { + qWarning("QGLXContext: Failed to query config from the provided context"); + return; + } + + GLXFBConfig *configs; + int numConfigs = 0; + static const int attribs[] = { GLX_FBCONFIG_ID, configId, None }; + configs = glXChooseFBConfig(dpy, screen->screenNumber(), attribs, &numConfigs); + if (!configs || numConfigs < 1) { + qWarning("QGLXContext: Failed to find config"); + return; + } + if (configs && numConfigs > 1) // this is suspicious so warn but let it continue + qWarning("QGLXContext: Multiple configs for FBConfig ID %d", configId); + + config = configs[0]; + // Store the config. + m_config = config; + } + + Q_ASSERT(vinfo || config); + + int screenNumber = DefaultScreen(dpy); + Window window; + if (vinfo) + window = createDummyWindow(dpy, vinfo, screenNumber, RootWindow(dpy, screenNumber)); + else + window = createDummyWindow(dpy, config, screenNumber, RootWindow(dpy, screenNumber)); + if (!window) { + qWarning("QGLXContext: Failed to create dummy window"); + return; + } + + // Update OpenGL version and buffer sizes in our format. + if (!glXMakeCurrent(dpy, window, context)) { + qWarning("QGLXContext: Failed to make provided context current"); + return; + } + m_format = QSurfaceFormat(); + m_format.setRenderableType(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL + ? QSurfaceFormat::OpenGL : QSurfaceFormat::OpenGLES); + updateFormatFromContext(m_format); + if (vinfo) + qglx_surfaceFormatFromVisualInfo(&m_format, dpy, vinfo); + else + qglx_surfaceFormatFromGLXFBConfig(&m_format, dpy, config); + glXMakeCurrent(dpy, 0, 0); + XDestroyWindow(dpy, window); + + if (vinfo) + XFree(vinfo); + + // Success. Store the context. From this point on isValid() is true. + m_context = context; + + if (share) + m_shareContext = static_cast<const QGLXContext*>(share)->glxContext(); +} + QGLXContext::~QGLXContext() { - glXDestroyContext(DISPLAY_FROM_XCB(m_screen), m_context); + if (m_ownsContext) + glXDestroyContext(DISPLAY_FROM_XCB(m_screen), m_context); +} + +QVariant QGLXContext::nativeHandle() const +{ + return QVariant::fromValue<QGLXNativeContext>(QGLXNativeContext(m_context)); } bool QGLXContext::makeCurrent(QPlatformSurface *surface) @@ -393,11 +523,11 @@ void QGLXContext::swapBuffers(QPlatformSurface *surface) if (surface->surface()->surfaceClass() == QSurface::Window) { QXcbWindow *platformWindow = static_cast<QXcbWindow *>(surface); - // OpenGL context might be bound to a non-gui thread - // use QueuedConnection to sync the window from the platformWindow's thread - // as QXcbWindow is no QObject, a wrapper slot in QXcbConnection is used. + // OpenGL context might be bound to a non-gui thread use QueuedConnection to sync + // the window from the platformWindow's thread as QXcbWindow is no QObject, an + // event is sent to QXcbConnection. (this is faster than a metacall) if (platformWindow->needsSync()) - QMetaObject::invokeMethod(m_screen->connection(), "syncWindow", Qt::QueuedConnection, Q_ARG(QXcbWindow*, platformWindow)); + platformWindow->postSyncWindowRequest(); } } |