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

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