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 "qxcbglxintegration.h"
5
6#if QT_CONFIG(xcb_glx)
7#include <xcb/glx.h>
8#endif
9
10#include "qxcbnativeinterface.h"
11#include "qxcbglxwindow.h"
12#include "qxcbscreen.h"
13#include "qglxintegration.h"
14
15#include <QtCore/QVersionNumber>
16#include <QtGui/QOpenGLContext>
17#include <QtGui/private/qopenglcontext_p.h>
18
19#include "qxcbglxnativeinterfacehandler.h"
20
21#define register /* C++17 deprecated register */
22#include <X11/Xlibint.h>
23#undef register
24
25QT_BEGIN_NAMESPACE
26
27#if QT_CONFIG(xcb_glx)
28 #define QT_XCB_GLX_REQUIRED_MAJOR 1
29 #define QT_XCB_GLX_REQUIRED_MINOR 3
30
31 #if XCB_GLX_MAJOR_VERSION == 1 && XCB_GLX_MINOR_VERSION < 4
32 #define XCB_GLX_BUFFER_SWAP_COMPLETE 1
33 typedef struct xcb_glx_buffer_swap_complete_event_t {
34 uint8_t response_type;
35 uint8_t pad0;
36 uint16_t sequence;
37 uint16_t event_type;
38 uint8_t pad1[2];
39 xcb_glx_drawable_t drawable;
40 uint32_t ust_hi;
41 uint32_t ust_lo;
42 uint32_t msc_hi;
43 uint32_t msc_lo;
44 uint32_t sbc;
45 } xcb_glx_buffer_swap_complete_event_t;
46 #endif
47 typedef struct {
48 int type;
49 unsigned long serial; /* # of last request processed by server */
50 Bool send_event; /* true if this came from a SendEvent request */
51 Display *display; /* Display the event was read from */
52 Drawable drawable; /* drawable on which event was requested in event mask */
53 int event_type;
54 int64_t ust;
55 int64_t msc;
56 int64_t sbc;
57 } QGLXBufferSwapComplete;
58#endif
59
60QXcbGlxIntegration::QXcbGlxIntegration()
61 : m_connection(nullptr)
62 , m_glx_first_event(0)
63{
64 qCDebug(lcQpaGl) << "Xcb GLX gl-integration created";
65}
66
67QXcbGlxIntegration::~QXcbGlxIntegration()
68{
69}
70
71bool QXcbGlxIntegration::initialize(QXcbConnection *connection)
72{
73 m_connection = connection;
74#if QT_CONFIG(xcb_glx)
75
76 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(c: m_connection->xcb_connection(), ext: &xcb_glx_id);
77 if (!reply || !reply->present)
78 return false;
79
80 m_glx_first_event = reply->first_event;
81
82 auto xglx_query = Q_XCB_REPLY(xcb_glx_query_version, m_connection->xcb_connection(),
83 XCB_GLX_MAJOR_VERSION,
84 XCB_GLX_MINOR_VERSION);
85 if ((!xglx_query)
86 || (QVersionNumber(xglx_query->major_version, xglx_query->minor_version)
87 < QVersionNumber(QT_XCB_GLX_REQUIRED_MAJOR, QT_XCB_GLX_REQUIRED_MINOR))) {
88 qCWarning(lcQpaGl) << "QXcbConnection: Failed to initialize GLX";
89 return false;
90 }
91#endif
92
93 m_native_interface_handler.reset(other: new QXcbGlxNativeInterfaceHandler(connection->nativeInterface()));
94
95 qCDebug(lcQpaGl) << "Xcb GLX gl-integration successfully initialized";
96 return true;
97}
98
99bool QXcbGlxIntegration::handleXcbEvent(xcb_generic_event_t *event, uint responseType)
100{
101 bool handled = false;
102 // Check if a custom XEvent constructor was registered in xlib for this event type, and call it discarding the constructed XEvent if any.
103 // XESetWireToEvent might be used by libraries to intercept messages from the X server e.g. the OpenGL lib waiting for DRI2 events.
104 Display *xdisplay = static_cast<Display *>(m_connection->xlib_display());
105 XLockDisplay(xdisplay);
106 bool locked = true;
107 Bool (*proc)(Display*, XEvent*, xEvent*) = XESetWireToEvent(xdisplay, responseType, nullptr);
108 if (proc) {
109 XESetWireToEvent(xdisplay, responseType, proc);
110 XEvent dummy;
111 event->sequence = LastKnownRequestProcessed(xdisplay);
112 if (proc(xdisplay, &dummy, (xEvent*)event)) {
113#if QT_CONFIG(xcb_glx)
114 // DRI2 clients don't receive GLXBufferSwapComplete events on the wire.
115 // Instead the GLX event is synthesized from the DRI2BufferSwapComplete event
116 // by DRI2WireToEvent(). For an application to be able to see the event
117 // we have to convert it to an xcb_glx_buffer_swap_complete_event_t and
118 // pass it to the native event filter.
119 const uint swap_complete = m_glx_first_event + XCB_GLX_BUFFER_SWAP_COMPLETE;
120 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
121 if (dispatcher && uint(dummy.type) == swap_complete && responseType != swap_complete) {
122 QGLXBufferSwapComplete *xev = reinterpret_cast<QGLXBufferSwapComplete *>(&dummy);
123 xcb_glx_buffer_swap_complete_event_t ev;
124 memset(s: &ev, c: 0, n: sizeof(xcb_glx_buffer_swap_complete_event_t));
125 ev.response_type = xev->type;
126 ev.sequence = xev->serial;
127 ev.event_type = xev->event_type;
128 ev.drawable = xev->drawable;
129 ev.ust_hi = xev->ust >> 32;
130 ev.ust_lo = xev->ust & 0xffffffff;
131 ev.msc_hi = xev->msc >> 32;
132 ev.msc_lo = xev->msc & 0xffffffff;
133 ev.sbc = xev->sbc & 0xffffffff;
134 // Unlock the display before calling the native event filter
135 XUnlockDisplay(xdisplay);
136 locked = false;
137 auto eventType = m_connection->nativeInterface()->nativeEventType();
138 qintptr result = 0;
139 handled = dispatcher->filterNativeEvent(eventType, message: &ev, result: &result);
140 }
141#endif
142 }
143 }
144 if (locked)
145 XUnlockDisplay(xdisplay);
146 return handled;
147}
148
149QXcbWindow *QXcbGlxIntegration::createWindow(QWindow *window) const
150{
151 return new QXcbGlxWindow(window);
152}
153
154QPlatformOpenGLContext *QXcbGlxIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
155{
156 QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle());
157 return new QGLXContext(static_cast<Display *>(m_connection->xlib_display()),
158 screen, screen->surfaceFormatFor(format: context->format()), context->shareHandle());
159}
160
161QOpenGLContext *QXcbGlxIntegration::createOpenGLContext(GLXContext glxContext, void *visualInfo, QOpenGLContext *shareContext) const
162{
163 if (!glxContext)
164 return nullptr;
165
166 QPlatformOpenGLContext *shareHandle = shareContext ? shareContext->handle() : nullptr;
167
168 auto *context = new QOpenGLContext;
169 auto *contextPrivate = QOpenGLContextPrivate::get(context);
170 auto *display = static_cast<Display *>(m_connection->xlib_display());
171 contextPrivate->adopt(new QGLXContext(display, glxContext, visualInfo, shareHandle));
172 return context;
173}
174
175QPlatformOffscreenSurface *QXcbGlxIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
176{
177 static bool vendorChecked = false;
178 static bool glxPbufferUsable = true;
179 if (!vendorChecked) {
180 vendorChecked = true;
181 Display *display = glXGetCurrentDisplay();
182 if (!display)
183 display = static_cast<Display *>(m_connection->xlib_display());
184
185 const char *glxvendor = glXGetClientString(dpy: display, GLX_VENDOR);
186 if (glxvendor) {
187 if (!strcmp(s1: glxvendor, s2: "ATI") || !strcmp(s1: glxvendor, s2: "Chromium"))
188 glxPbufferUsable = false;
189 }
190 }
191 if (glxPbufferUsable)
192 return new QGLXPbuffer(surface);
193 else
194 return nullptr; // trigger fallback to hidden QWindow
195
196}
197
198bool QXcbGlxIntegration::supportsThreadedOpenGL() const
199{
200 return QGLXContext::supportsThreading();
201}
202
203bool QXcbGlxIntegration::supportsSwitchableWidgetComposition() const
204{
205 static bool vendorChecked = false;
206 static bool isSwitchableWidgetCompositionAvailable = true;
207 if (!vendorChecked) {
208 vendorChecked = true;
209 Display *display = glXGetCurrentDisplay();
210 if (!display)
211 display = static_cast<Display *>(m_connection->xlib_display());
212
213 const char *glxvendor = glXGetClientString(dpy: display, GLX_VENDOR);
214 if (glxvendor) {
215 if (!strcmp(s1: glxvendor, s2: "Parallels Inc"))
216 isSwitchableWidgetCompositionAvailable = false;
217 }
218 }
219
220 return isSwitchableWidgetCompositionAvailable;
221}
222
223
224QT_END_NAMESPACE
225

source code of qtbase/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp