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 <QtGui/private/qguiapplication_p.h>
41 #include <QtCore/QDebug>
42 #include <QtCore/QCoreApplication>
43 
44 #include "qxcbconnection.h"
45 #include "qxcbkeyboard.h"
46 #include "qxcbwindow.h"
47 #include "qxcbclipboard.h"
48 #if QT_CONFIG(draganddrop)
49 #include "qxcbdrag.h"
50 #endif
51 #include "qxcbwmsupport.h"
52 #include "qxcbnativeinterface.h"
53 #include "qxcbintegration.h"
54 #include "qxcbsystemtraytracker.h"
55 #include "qxcbglintegrationfactory.h"
56 #include "qxcbglintegration.h"
57 #include "qxcbcursor.h"
58 #include "qxcbbackingstore.h"
59 #include "qxcbeventqueue.h"
60 
61 #include <QAbstractEventDispatcher>
62 #include <QByteArray>
63 #include <QScopedPointer>
64 
65 #include <stdio.h>
66 #include <errno.h>
67 
68 #include <xcb/xfixes.h>
69 #define explicit dont_use_cxx_explicit
70 #include <xcb/xkb.h>
71 #undef explicit
72 #include <xcb/xinput.h>
73 
74 QT_BEGIN_NAMESPACE
75 
76 Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input")
77 Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
78 Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
79 Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
80 Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
81 Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader")
82 Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker")
83 Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard")
84 Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard")
85 Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd")
86 
QXcbConnection(QXcbNativeInterface * nativeInterface,bool canGrabServer,xcb_visualid_t defaultVisualId,const char * displayName)87 QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName)
88     : QXcbBasicConnection(displayName)
89     , m_canGrabServer(canGrabServer)
90     , m_defaultVisualId(defaultVisualId)
91     , m_nativeInterface(nativeInterface)
92 {
93     if (!isConnected())
94         return;
95 
96     m_eventQueue = new QXcbEventQueue(this);
97 
98     m_xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toLower();
99 
100     if (hasXRandr())
101         xrandrSelectEvents();
102 
103     initializeScreens();
104 
105     if (hasXInput2()) {
106         xi2SetupDevices();
107         xi2SelectStateEvents();
108     }
109 
110     m_wmSupport.reset(new QXcbWMSupport(this));
111     m_keyboard = new QXcbKeyboard(this);
112 #ifndef QT_NO_CLIPBOARD
113     m_clipboard = new QXcbClipboard(this);
114 #endif
115 #if QT_CONFIG(draganddrop)
116     m_drag = new QXcbDrag(this);
117 #endif
118 
119     m_startupId = qgetenv("DESKTOP_STARTUP_ID");
120     if (!m_startupId.isNull())
121         qunsetenv("DESKTOP_STARTUP_ID");
122 
123     const int focusInDelay = 100;
124     m_focusInTimer.setSingleShot(true);
125     m_focusInTimer.setInterval(focusInDelay);
126     m_focusInTimer.callOnTimeout([]() {
127         // No FocusIn events for us, proceed with FocusOut normally.
128         QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
129     });
130 
131     sync();
132 }
133 
~QXcbConnection()134 QXcbConnection::~QXcbConnection()
135 {
136 #ifndef QT_NO_CLIPBOARD
137     delete m_clipboard;
138 #endif
139 #if QT_CONFIG(draganddrop)
140     delete m_drag;
141 #endif
142     if (m_eventQueue)
143         delete m_eventQueue;
144 
145     // Delete screens in reverse order to avoid crash in case of multiple screens
146     while (!m_screens.isEmpty())
147         QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast());
148 
149     while (!m_virtualDesktops.isEmpty())
150         delete m_virtualDesktops.takeLast();
151 
152     delete m_glIntegration;
153 
154     delete m_keyboard;
155 }
156 
primaryScreen() const157 QXcbScreen *QXcbConnection::primaryScreen() const
158 {
159     if (!m_screens.isEmpty()) {
160         Q_ASSERT(m_screens.first()->screenNumber() == primaryScreenNumber());
161         return m_screens.first();
162     }
163 
164     return nullptr;
165 }
166 
addWindowEventListener(xcb_window_t id,QXcbWindowEventListener * eventListener)167 void QXcbConnection::addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener)
168 {
169     m_mapper.insert(id, eventListener);
170 }
171 
removeWindowEventListener(xcb_window_t id)172 void QXcbConnection::removeWindowEventListener(xcb_window_t id)
173 {
174     m_mapper.remove(id);
175 }
176 
windowEventListenerFromId(xcb_window_t id)177 QXcbWindowEventListener *QXcbConnection::windowEventListenerFromId(xcb_window_t id)
178 {
179     return m_mapper.value(id, 0);
180 }
181 
platformWindowFromId(xcb_window_t id)182 QXcbWindow *QXcbConnection::platformWindowFromId(xcb_window_t id)
183 {
184     QXcbWindowEventListener *listener = m_mapper.value(id, 0);
185     if (listener)
186         return listener->toWindow();
187     return nullptr;
188 }
189 
190 #define HANDLE_PLATFORM_WINDOW_EVENT(event_t, windowMember, handler) \
191 { \
192     auto e = reinterpret_cast<event_t *>(event); \
193     if (QXcbWindowEventListener *eventListener = windowEventListenerFromId(e->windowMember))  { \
194         if (eventListener->handleNativeEvent(event)) \
195             return; \
196         eventListener->handler(e); \
197     } \
198 } \
199 break;
200 
201 #define HANDLE_KEYBOARD_EVENT(event_t, handler) \
202 { \
203     auto e = reinterpret_cast<event_t *>(event); \
204     if (QXcbWindowEventListener *eventListener = windowEventListenerFromId(e->event)) { \
205         if (eventListener->handleNativeEvent(event)) \
206             return; \
207         m_keyboard->handler(e); \
208     } \
209 } \
210 break;
211 
printXcbEvent(const QLoggingCategory & log,const char * message,xcb_generic_event_t * event) const212 void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *message,
213                                    xcb_generic_event_t *event) const
214 {
215     quint8 response_type = event->response_type & ~0x80;
216     quint16 sequence = event->sequence;
217 
218 #define PRINT_AND_RETURN(name) { \
219     qCDebug(log, "%s | %s(%d) | sequence: %d", message, name, response_type, sequence); \
220     return; \
221 }
222 #define CASE_PRINT_AND_RETURN(name) case name : PRINT_AND_RETURN(#name);
223 
224     switch (response_type) {
225     CASE_PRINT_AND_RETURN( XCB_KEY_PRESS );
226     CASE_PRINT_AND_RETURN( XCB_KEY_RELEASE );
227     CASE_PRINT_AND_RETURN( XCB_BUTTON_PRESS );
228     CASE_PRINT_AND_RETURN( XCB_BUTTON_RELEASE );
229     CASE_PRINT_AND_RETURN( XCB_MOTION_NOTIFY );
230     CASE_PRINT_AND_RETURN( XCB_ENTER_NOTIFY );
231     CASE_PRINT_AND_RETURN( XCB_LEAVE_NOTIFY );
232     CASE_PRINT_AND_RETURN( XCB_FOCUS_IN );
233     CASE_PRINT_AND_RETURN( XCB_FOCUS_OUT );
234     CASE_PRINT_AND_RETURN( XCB_KEYMAP_NOTIFY );
235     CASE_PRINT_AND_RETURN( XCB_EXPOSE );
236     CASE_PRINT_AND_RETURN( XCB_GRAPHICS_EXPOSURE );
237     CASE_PRINT_AND_RETURN( XCB_NO_EXPOSURE );
238     CASE_PRINT_AND_RETURN( XCB_VISIBILITY_NOTIFY );
239     CASE_PRINT_AND_RETURN( XCB_CREATE_NOTIFY );
240     CASE_PRINT_AND_RETURN( XCB_DESTROY_NOTIFY );
241     CASE_PRINT_AND_RETURN( XCB_UNMAP_NOTIFY );
242     CASE_PRINT_AND_RETURN( XCB_MAP_NOTIFY );
243     CASE_PRINT_AND_RETURN( XCB_MAP_REQUEST );
244     CASE_PRINT_AND_RETURN( XCB_REPARENT_NOTIFY );
245     CASE_PRINT_AND_RETURN( XCB_CONFIGURE_NOTIFY );
246     CASE_PRINT_AND_RETURN( XCB_CONFIGURE_REQUEST );
247     CASE_PRINT_AND_RETURN( XCB_GRAVITY_NOTIFY );
248     CASE_PRINT_AND_RETURN( XCB_RESIZE_REQUEST );
249     CASE_PRINT_AND_RETURN( XCB_CIRCULATE_NOTIFY );
250     CASE_PRINT_AND_RETURN( XCB_CIRCULATE_REQUEST );
251     CASE_PRINT_AND_RETURN( XCB_PROPERTY_NOTIFY );
252     CASE_PRINT_AND_RETURN( XCB_SELECTION_CLEAR );
253     CASE_PRINT_AND_RETURN( XCB_SELECTION_REQUEST );
254     CASE_PRINT_AND_RETURN( XCB_SELECTION_NOTIFY );
255     CASE_PRINT_AND_RETURN( XCB_COLORMAP_NOTIFY );
256     CASE_PRINT_AND_RETURN( XCB_CLIENT_MESSAGE );
257     CASE_PRINT_AND_RETURN( XCB_MAPPING_NOTIFY );
258     CASE_PRINT_AND_RETURN( XCB_GE_GENERIC );
259     }
260     // XFixes
261     if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY))
262           PRINT_AND_RETURN("XCB_XFIXES_SELECTION_NOTIFY");
263 
264     // XRandR
265     if (isXRandrType(response_type, XCB_RANDR_NOTIFY))
266         PRINT_AND_RETURN("XCB_RANDR_NOTIFY");
267     if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY))
268         PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY");
269 
270     // XKB
271     if (isXkbType(response_type))
272         PRINT_AND_RETURN("XCB_XKB_* event");
273 
274     // UNKNOWN
275     qCDebug(log, "%s | unknown(%d) | sequence: %d", message, response_type, sequence);
276 
277 #undef PRINT_AND_RETURN
278 #undef CASE_PRINT_AND_RETURN
279 }
280 
281 const char *xcb_errors[] =
282 {
283     "Success",
284     "BadRequest",
285     "BadValue",
286     "BadWindow",
287     "BadPixmap",
288     "BadAtom",
289     "BadCursor",
290     "BadFont",
291     "BadMatch",
292     "BadDrawable",
293     "BadAccess",
294     "BadAlloc",
295     "BadColor",
296     "BadGC",
297     "BadIDChoice",
298     "BadName",
299     "BadLength",
300     "BadImplementation",
301     "Unknown"
302 };
303 
304 const char *xcb_protocol_request_codes[] =
305 {
306     "Null",
307     "CreateWindow",
308     "ChangeWindowAttributes",
309     "GetWindowAttributes",
310     "DestroyWindow",
311     "DestroySubwindows",
312     "ChangeSaveSet",
313     "ReparentWindow",
314     "MapWindow",
315     "MapSubwindows",
316     "UnmapWindow",
317     "UnmapSubwindows",
318     "ConfigureWindow",
319     "CirculateWindow",
320     "GetGeometry",
321     "QueryTree",
322     "InternAtom",
323     "GetAtomName",
324     "ChangeProperty",
325     "DeleteProperty",
326     "GetProperty",
327     "ListProperties",
328     "SetSelectionOwner",
329     "GetSelectionOwner",
330     "ConvertSelection",
331     "SendEvent",
332     "GrabPointer",
333     "UngrabPointer",
334     "GrabButton",
335     "UngrabButton",
336     "ChangeActivePointerGrab",
337     "GrabKeyboard",
338     "UngrabKeyboard",
339     "GrabKey",
340     "UngrabKey",
341     "AllowEvents",
342     "GrabServer",
343     "UngrabServer",
344     "QueryPointer",
345     "GetMotionEvents",
346     "TranslateCoords",
347     "WarpPointer",
348     "SetInputFocus",
349     "GetInputFocus",
350     "QueryKeymap",
351     "OpenFont",
352     "CloseFont",
353     "QueryFont",
354     "QueryTextExtents",
355     "ListFonts",
356     "ListFontsWithInfo",
357     "SetFontPath",
358     "GetFontPath",
359     "CreatePixmap",
360     "FreePixmap",
361     "CreateGC",
362     "ChangeGC",
363     "CopyGC",
364     "SetDashes",
365     "SetClipRectangles",
366     "FreeGC",
367     "ClearArea",
368     "CopyArea",
369     "CopyPlane",
370     "PolyPoint",
371     "PolyLine",
372     "PolySegment",
373     "PolyRectangle",
374     "PolyArc",
375     "FillPoly",
376     "PolyFillRectangle",
377     "PolyFillArc",
378     "PutImage",
379     "GetImage",
380     "PolyText8",
381     "PolyText16",
382     "ImageText8",
383     "ImageText16",
384     "CreateColormap",
385     "FreeColormap",
386     "CopyColormapAndFree",
387     "InstallColormap",
388     "UninstallColormap",
389     "ListInstalledColormaps",
390     "AllocColor",
391     "AllocNamedColor",
392     "AllocColorCells",
393     "AllocColorPlanes",
394     "FreeColors",
395     "StoreColors",
396     "StoreNamedColor",
397     "QueryColors",
398     "LookupColor",
399     "CreateCursor",
400     "CreateGlyphCursor",
401     "FreeCursor",
402     "RecolorCursor",
403     "QueryBestSize",
404     "QueryExtension",
405     "ListExtensions",
406     "ChangeKeyboardMapping",
407     "GetKeyboardMapping",
408     "ChangeKeyboardControl",
409     "GetKeyboardControl",
410     "Bell",
411     "ChangePointerControl",
412     "GetPointerControl",
413     "SetScreenSaver",
414     "GetScreenSaver",
415     "ChangeHosts",
416     "ListHosts",
417     "SetAccessControl",
418     "SetCloseDownMode",
419     "KillClient",
420     "RotateProperties",
421     "ForceScreenSaver",
422     "SetPointerMapping",
423     "GetPointerMapping",
424     "SetModifierMapping",
425     "GetModifierMapping",
426     "Unknown"
427 };
428 
handleXcbError(xcb_generic_error_t * error)429 void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
430 {
431 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
432     qintptr result = 0;
433 #else
434     long result = 0;
435 #endif
436     QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
437     if (dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), error, &result))
438         return;
439 
440     printXcbError("QXcbConnection: XCB error", error);
441 }
442 
printXcbError(const char * message,xcb_generic_error_t * error)443 void QXcbConnection::printXcbError(const char *message, xcb_generic_error_t *error)
444 {
445     uint clamped_error_code = qMin<uint>(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1);
446     uint clamped_major_code = qMin<uint>(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1);
447 
448     qCWarning(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d",
449              message,
450              int(error->error_code), xcb_errors[clamped_error_code],
451              int(error->sequence), int(error->resource_id),
452              int(error->major_code), xcb_protocol_request_codes[clamped_major_code],
453              int(error->minor_code));
454 }
455 
translateMouseButtons(int s)456 static Qt::MouseButtons translateMouseButtons(int s)
457 {
458     Qt::MouseButtons ret;
459     if (s & XCB_BUTTON_MASK_1)
460         ret |= Qt::LeftButton;
461     if (s & XCB_BUTTON_MASK_2)
462         ret |= Qt::MiddleButton;
463     if (s & XCB_BUTTON_MASK_3)
464         ret |= Qt::RightButton;
465     return ret;
466 }
467 
setButtonState(Qt::MouseButton button,bool down)468 void QXcbConnection::setButtonState(Qt::MouseButton button, bool down)
469 {
470     m_buttonState.setFlag(button, down);
471     m_button = button;
472 }
473 
translateMouseButton(xcb_button_t s)474 Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s)
475 {
476     switch (s) {
477     case 1: return Qt::LeftButton;
478     case 2: return Qt::MiddleButton;
479     case 3: return Qt::RightButton;
480     // Button values 4-7 were already handled as Wheel events, and won't occur here.
481     case 8: return Qt::BackButton;      // Also known as Qt::ExtraButton1
482     case 9: return Qt::ForwardButton;   // Also known as Qt::ExtraButton2
483     case 10: return Qt::ExtraButton3;
484     case 11: return Qt::ExtraButton4;
485     case 12: return Qt::ExtraButton5;
486     case 13: return Qt::ExtraButton6;
487     case 14: return Qt::ExtraButton7;
488     case 15: return Qt::ExtraButton8;
489     case 16: return Qt::ExtraButton9;
490     case 17: return Qt::ExtraButton10;
491     case 18: return Qt::ExtraButton11;
492     case 19: return Qt::ExtraButton12;
493     case 20: return Qt::ExtraButton13;
494     case 21: return Qt::ExtraButton14;
495     case 22: return Qt::ExtraButton15;
496     case 23: return Qt::ExtraButton16;
497     case 24: return Qt::ExtraButton17;
498     case 25: return Qt::ExtraButton18;
499     case 26: return Qt::ExtraButton19;
500     case 27: return Qt::ExtraButton20;
501     case 28: return Qt::ExtraButton21;
502     case 29: return Qt::ExtraButton22;
503     case 30: return Qt::ExtraButton23;
504     case 31: return Qt::ExtraButton24;
505     default: return Qt::NoButton;
506     }
507 }
508 
509 namespace {
510     typedef union {
511         /* All XKB events share these fields. */
512         struct {
513             uint8_t response_type;
514             uint8_t xkbType;
515             uint16_t sequence;
516             xcb_timestamp_t time;
517             uint8_t deviceID;
518         } any;
519         xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
520         xcb_xkb_map_notify_event_t map_notify;
521         xcb_xkb_state_notify_event_t state_notify;
522     } _xkb_event;
523 }
524 
handleXcbEvent(xcb_generic_event_t * event)525 void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
526 {
527     if (Q_UNLIKELY(lcQpaEvents().isDebugEnabled()))
528         printXcbEvent(lcQpaEvents(), "Event", event);
529 
530 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
531     qintptr result = 0; // Used only by MS Windows
532 #else
533     long result = 0; // Used only by MS Windows
534 #endif
535     if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) {
536         if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result))
537             return;
538     }
539 
540     uint response_type = event->response_type & ~0x80;
541 
542     bool handled = true;
543     switch (response_type) {
544     case XCB_EXPOSE:
545         HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
546     case XCB_BUTTON_PRESS: {
547         auto ev = reinterpret_cast<xcb_button_press_event_t *>(event);
548         m_keyboard->updateXKBStateFromCore(ev->state);
549         // the event explicitly contains the state of the three first buttons,
550         // the rest we need to manage ourselves
551         m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
552         setButtonState(translateMouseButton(ev->detail), true);
553         if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
554             qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X",
555                     ev->detail, static_cast<unsigned int>(m_buttonState));
556         HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
557     }
558     case XCB_BUTTON_RELEASE: {
559         auto ev = reinterpret_cast<xcb_button_release_event_t *>(event);
560         m_keyboard->updateXKBStateFromCore(ev->state);
561         m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
562         setButtonState(translateMouseButton(ev->detail), false);
563         if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
564             qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X",
565                     ev->detail, static_cast<unsigned int>(m_buttonState));
566         HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
567     }
568     case XCB_MOTION_NOTIFY: {
569         auto ev = reinterpret_cast<xcb_motion_notify_event_t *>(event);
570         m_keyboard->updateXKBStateFromCore(ev->state);
571         m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
572         if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
573             qCDebug(lcQpaXInputEvents, "legacy mouse move %d,%d button %d state %X",
574                     ev->event_x, ev->event_y, ev->detail, static_cast<unsigned int>(m_buttonState));
575         HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
576     }
577     case XCB_CONFIGURE_NOTIFY:
578         HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
579     case XCB_MAP_NOTIFY:
580         HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent);
581     case XCB_UNMAP_NOTIFY:
582         HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
583     case XCB_DESTROY_NOTIFY:
584         HANDLE_PLATFORM_WINDOW_EVENT(xcb_destroy_notify_event_t, event, handleDestroyNotifyEvent);
585     case XCB_CLIENT_MESSAGE: {
586         auto clientMessage = reinterpret_cast<xcb_client_message_event_t *>(event);
587         if (clientMessage->format != 32)
588             return;
589 #if QT_CONFIG(draganddrop)
590         if (clientMessage->type == atom(QXcbAtom::XdndStatus))
591             drag()->handleStatus(clientMessage);
592         else if (clientMessage->type == atom(QXcbAtom::XdndFinished))
593             drag()->handleFinished(clientMessage);
594 #endif
595         if (m_systemTrayTracker && clientMessage->type == atom(QXcbAtom::MANAGER))
596             m_systemTrayTracker->notifyManagerClientMessageEvent(clientMessage);
597         HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent);
598     }
599     case XCB_ENTER_NOTIFY:
600         if (hasXInput2() && !xi2MouseEventsDisabled())
601             break;
602         HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
603     case XCB_LEAVE_NOTIFY:
604         if (hasXInput2() && !xi2MouseEventsDisabled())
605             break;
606         m_keyboard->updateXKBStateFromCore(reinterpret_cast<xcb_leave_notify_event_t *>(event)->state);
607         HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
608     case XCB_FOCUS_IN:
609         HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
610     case XCB_FOCUS_OUT:
611         HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent);
612     case XCB_KEY_PRESS:
613     {
614         auto keyPress = reinterpret_cast<xcb_key_press_event_t *>(event);
615         m_keyboard->updateXKBStateFromCore(keyPress->state);
616         setTime(keyPress->time);
617         HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
618     }
619     case XCB_KEY_RELEASE:
620         m_keyboard->updateXKBStateFromCore(reinterpret_cast<xcb_key_release_event_t *>(event)->state);
621         HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
622     case XCB_MAPPING_NOTIFY:
623         m_keyboard->updateKeymap(reinterpret_cast<xcb_mapping_notify_event_t *>(event));
624         break;
625     case XCB_SELECTION_REQUEST:
626     {
627 #if QT_CONFIG(draganddrop) || QT_CONFIG(clipboard)
628         auto selectionRequest = reinterpret_cast<xcb_selection_request_event_t *>(event);
629 #endif
630 #if QT_CONFIG(draganddrop)
631         if (selectionRequest->selection == atom(QXcbAtom::XdndSelection))
632             m_drag->handleSelectionRequest(selectionRequest);
633         else
634 #endif
635         {
636 #ifndef QT_NO_CLIPBOARD
637             m_clipboard->handleSelectionRequest(selectionRequest);
638 #endif
639         }
640         break;
641     }
642     case XCB_SELECTION_CLEAR:
643         setTime((reinterpret_cast<xcb_selection_clear_event_t *>(event))->time);
644 #ifndef QT_NO_CLIPBOARD
645         m_clipboard->handleSelectionClearRequest(reinterpret_cast<xcb_selection_clear_event_t *>(event));
646 #endif
647         break;
648     case XCB_SELECTION_NOTIFY:
649         setTime((reinterpret_cast<xcb_selection_notify_event_t *>(event))->time);
650         break;
651     case XCB_PROPERTY_NOTIFY:
652     {
653 #ifndef QT_NO_CLIPBOARD
654         if (m_clipboard->handlePropertyNotify(event))
655             break;
656 #endif
657         auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
658         if (propertyNotify->atom == atom(QXcbAtom::_NET_WORKAREA)) {
659             QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window);
660             if (virtualDesktop)
661                 virtualDesktop->updateWorkArea();
662         } else {
663             HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
664         }
665         break;
666     }
667     case XCB_GE_GENERIC:
668         // Here the windowEventListener is invoked from xi2HandleEvent()
669         if (hasXInput2() && isXIEvent(event))
670             xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
671         break;
672     default:
673         handled = false; // event type not recognized
674         break;
675     }
676 
677     if (handled)
678         return;
679 
680     handled = true;
681     if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) {
682         auto notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event);
683         setTime(notify_event->timestamp);
684 #ifndef QT_NO_CLIPBOARD
685         m_clipboard->handleXFixesSelectionRequest(notify_event);
686 #endif
687         for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
688             virtualDesktop->handleXFixesSelectionNotify(notify_event);
689     } else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) {
690         updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event));
691     } else if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
692         auto change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event);
693         if (auto virtualDesktop = virtualDesktopForRootWindow(change_event->root))
694             virtualDesktop->handleScreenChange(change_event);
695     } else if (isXkbType(response_type)) {
696         auto xkb_event = reinterpret_cast<_xkb_event *>(event);
697         if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) {
698             switch (xkb_event->any.xkbType) {
699                 // XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
700                 // updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent recompilations.
701                 case XCB_XKB_STATE_NOTIFY:
702                     m_keyboard->updateXKBState(&xkb_event->state_notify);
703                     break;
704                 case XCB_XKB_MAP_NOTIFY:
705                     m_keyboard->updateKeymap();
706                     break;
707                 case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
708                     xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify;
709                     if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
710                         m_keyboard->updateKeymap();
711                     break;
712                 }
713                 default:
714                     break;
715             }
716         }
717     } else {
718         handled = false; // event type still not recognized
719     }
720 
721     if (handled)
722         return;
723 
724     if (m_glIntegration)
725         m_glIntegration->handleXcbEvent(event, response_type);
726 }
727 
setFocusWindow(QWindow * w)728 void QXcbConnection::setFocusWindow(QWindow *w)
729 {
730     m_focusWindow = w ? static_cast<QXcbWindow *>(w->handle()) : nullptr;
731 }
setMouseGrabber(QXcbWindow * w)732 void QXcbConnection::setMouseGrabber(QXcbWindow *w)
733 {
734     m_mouseGrabber = w;
735     m_mousePressWindow = nullptr;
736 }
setMousePressWindow(QXcbWindow * w)737 void QXcbConnection::setMousePressWindow(QXcbWindow *w)
738 {
739     m_mousePressWindow = w;
740 }
741 
grabServer()742 void QXcbConnection::grabServer()
743 {
744     if (m_canGrabServer)
745         xcb_grab_server(xcb_connection());
746 }
747 
ungrabServer()748 void QXcbConnection::ungrabServer()
749 {
750     if (m_canGrabServer)
751         xcb_ungrab_server(xcb_connection());
752 }
753 
getTimestamp()754 xcb_timestamp_t QXcbConnection::getTimestamp()
755 {
756     // send a dummy event to myself to get the timestamp from X server.
757     xcb_window_t window = rootWindow();
758     xcb_atom_t dummyAtom = atom(QXcbAtom::CLIP_TEMPORARY);
759     xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, window, dummyAtom,
760                         XCB_ATOM_INTEGER, 32, 0, nullptr);
761 
762     connection()->flush();
763 
764     xcb_generic_event_t *event = nullptr;
765 
766     // When disconnection is caused by X server, event will never be able to hold
767     // a valid pointer. isConnected(), which calls xcb_connection_has_error(),
768     // can handle this type of disconnection and properly quits the loop.
769     while (isConnected() && !event) {
770         connection()->sync();
771         event = eventQueue()->peek([window, dummyAtom](xcb_generic_event_t *event, int type) {
772             if (type != XCB_PROPERTY_NOTIFY)
773                 return false;
774             auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
775             return propertyNotify->window == window && propertyNotify->atom == dummyAtom;
776         });
777     }
778 
779     if (!event) {
780         // https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#glossary
781         // > One timestamp value (named CurrentTime) is never generated by the
782         // > server. This value is reserved for use in requests to represent the
783         // > current server time.
784         return XCB_CURRENT_TIME;
785     }
786 
787     xcb_property_notify_event_t *pn = reinterpret_cast<xcb_property_notify_event_t *>(event);
788     xcb_timestamp_t timestamp = pn->time;
789     free(event);
790 
791     xcb_delete_property(xcb_connection(), window, dummyAtom);
792 
793     return timestamp;
794 }
795 
getSelectionOwner(xcb_atom_t atom) const796 xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const
797 {
798     return Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom)->owner;
799 }
800 
getQtSelectionOwner()801 xcb_window_t QXcbConnection::getQtSelectionOwner()
802 {
803     if (!m_qtSelectionOwner) {
804         xcb_screen_t *xcbScreen = primaryVirtualDesktop()->screen();
805         int16_t x = 0, y = 0;
806         uint16_t w = 3, h = 3;
807         m_qtSelectionOwner = xcb_generate_id(xcb_connection());
808         xcb_create_window(xcb_connection(),
809                           XCB_COPY_FROM_PARENT,               // depth -- same as root
810                           m_qtSelectionOwner,                 // window id
811                           xcbScreen->root,                    // parent window id
812                           x, y, w, h,
813                           0,                                  // border width
814                           XCB_WINDOW_CLASS_INPUT_OUTPUT,      // window class
815                           xcbScreen->root_visual,             // visual
816                           0,                                  // value mask
817                           nullptr);                                 // value list
818 
819         QXcbWindow::setWindowTitle(connection(), m_qtSelectionOwner,
820                                    QLatin1String("Qt Selection Owner for ") + QCoreApplication::applicationName());
821     }
822     return m_qtSelectionOwner;
823 }
824 
rootWindow()825 xcb_window_t QXcbConnection::rootWindow()
826 {
827     QXcbScreen *s = primaryScreen();
828     return s ? s->root() : 0;
829 }
830 
clientLeader()831 xcb_window_t QXcbConnection::clientLeader()
832 {
833     if (m_clientLeader == 0) {
834         m_clientLeader = xcb_generate_id(xcb_connection());
835         QXcbScreen *screen = primaryScreen();
836         xcb_create_window(xcb_connection(),
837                           XCB_COPY_FROM_PARENT,
838                           m_clientLeader,
839                           screen->root(),
840                           0, 0, 1, 1,
841                           0,
842                           XCB_WINDOW_CLASS_INPUT_OUTPUT,
843                           screen->screen()->root_visual,
844                           0, nullptr);
845 
846 
847         QXcbWindow::setWindowTitle(connection(), m_clientLeader,
848                                    QGuiApplication::applicationDisplayName());
849 
850         xcb_change_property(xcb_connection(),
851                             XCB_PROP_MODE_REPLACE,
852                             m_clientLeader,
853                             atom(QXcbAtom::WM_CLIENT_LEADER),
854                             XCB_ATOM_WINDOW,
855                             32,
856                             1,
857                             &m_clientLeader);
858 
859 #if QT_CONFIG(xcb_sm)
860         // If we are session managed, inform the window manager about it
861         QByteArray session = qGuiApp->sessionId().toLatin1();
862         if (!session.isEmpty()) {
863             xcb_change_property(xcb_connection(),
864                                 XCB_PROP_MODE_REPLACE,
865                                 m_clientLeader,
866                                 atom(QXcbAtom::SM_CLIENT_ID),
867                                 XCB_ATOM_STRING,
868                                 8,
869                                 session.length(),
870                                 session.constData());
871         }
872 #endif
873     }
874     return m_clientLeader;
875 }
876 
877 /*! \internal
878 
879     Compresses events of the same type to avoid swamping the event queue.
880     If event compression is not desired there are several options what developers can do:
881 
882     1) Write responsive applications. We drop events that have been buffered in the event
883        queue while waiting on unresponsive GUI thread.
884     2) Use QAbstractNativeEventFilter to get all events from X connection. This is not optimal
885        because it requires working with native event types.
886     3) Or add public API to Qt for disabling event compression QTBUG-44964
887 
888 */
compressEvent(xcb_generic_event_t * event) const889 bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const
890 {
891     if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
892         return false;
893 
894     uint responseType = event->response_type & ~0x80;
895 
896     if (responseType == XCB_MOTION_NOTIFY) {
897         // compress XCB_MOTION_NOTIFY notify events
898         return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
899                                   [](xcb_generic_event_t *, int type) {
900             return type == XCB_MOTION_NOTIFY;
901         });
902     }
903 
904     // compress XI_* events
905     if (responseType == XCB_GE_GENERIC) {
906         if (!hasXInput2())
907             return false;
908 
909         // compress XI_Motion
910         if (isXIType(event, XCB_INPUT_MOTION)) {
911 #if QT_CONFIG(tabletevent)
912             auto xdev = reinterpret_cast<xcb_input_motion_event_t *>(event);
913             if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) &&
914                     const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid))
915                 return false;
916 #endif // QT_CONFIG(tabletevent)
917             return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
918                                       [this](xcb_generic_event_t *next, int) {
919                 return isXIType(next, XCB_INPUT_MOTION);
920             });
921         }
922 
923         // compress XI_TouchUpdate for the same touch point id
924         if (isXIType(event, XCB_INPUT_TOUCH_UPDATE)) {
925             auto touchUpdateEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(event);
926             uint32_t id = touchUpdateEvent->detail % INT_MAX;
927 
928             return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
929                                       [this, &id](xcb_generic_event_t *next, int) {
930                 if (!isXIType(next, XCB_INPUT_TOUCH_UPDATE))
931                     return false;
932                 auto touchUpdateNextEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(next);
933                 return id == touchUpdateNextEvent->detail % INT_MAX;
934             });
935         }
936 
937         return false;
938     }
939 
940     if (responseType == XCB_CONFIGURE_NOTIFY) {
941         // compress multiple configure notify events for the same window
942         return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
943                                   [event](xcb_generic_event_t *next, int type) {
944             if (type != XCB_CONFIGURE_NOTIFY)
945                 return false;
946             auto currentEvent = reinterpret_cast<xcb_configure_notify_event_t *>(event);
947             auto nextEvent = reinterpret_cast<xcb_configure_notify_event_t *>(next);
948             return currentEvent->event == nextEvent->event;
949         });
950     }
951 
952     return false;
953 }
954 
isUserInputEvent(xcb_generic_event_t * event) const955 bool QXcbConnection::isUserInputEvent(xcb_generic_event_t *event) const
956 {
957     auto eventType = event->response_type & ~0x80;
958     bool isInputEvent = eventType == XCB_BUTTON_PRESS ||
959                         eventType == XCB_BUTTON_RELEASE ||
960                         eventType == XCB_KEY_PRESS ||
961                         eventType == XCB_KEY_RELEASE ||
962                         eventType == XCB_MOTION_NOTIFY ||
963                         eventType == XCB_ENTER_NOTIFY ||
964                         eventType == XCB_LEAVE_NOTIFY;
965     if (isInputEvent)
966         return true;
967 
968     if (connection()->hasXInput2()) {
969         isInputEvent = isXIType(event, XCB_INPUT_BUTTON_PRESS) ||
970                        isXIType(event, XCB_INPUT_BUTTON_RELEASE) ||
971                        isXIType(event, XCB_INPUT_MOTION) ||
972                        isXIType(event, XCB_INPUT_TOUCH_BEGIN) ||
973                        isXIType(event, XCB_INPUT_TOUCH_UPDATE) ||
974                        isXIType(event, XCB_INPUT_TOUCH_END) ||
975                        isXIType(event, XCB_INPUT_ENTER) ||
976                        isXIType(event, XCB_INPUT_LEAVE) ||
977                        // wacom driver's way of reporting tool proximity
978                        isXIType(event, XCB_INPUT_PROPERTY);
979     }
980     if (isInputEvent)
981         return true;
982 
983     if (eventType == XCB_CLIENT_MESSAGE) {
984         auto clientMessage = reinterpret_cast<const xcb_client_message_event_t *>(event);
985         if (clientMessage->format == 32 && clientMessage->type == atom(QXcbAtom::WM_PROTOCOLS))
986             if (clientMessage->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW))
987                 isInputEvent = true;
988     }
989 
990     return isInputEvent;
991 }
992 
processXcbEvents(QEventLoop::ProcessEventsFlags flags)993 void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags)
994 {
995     int connection_error = xcb_connection_has_error(xcb_connection());
996     if (connection_error) {
997         qWarning("The X11 connection broke (error %d). Did the X11 server die?", connection_error);
998         exit(1);
999     }
1000 
1001     m_eventQueue->flushBufferedEvents();
1002 
1003     while (xcb_generic_event_t *event = m_eventQueue->takeFirst(flags)) {
1004         QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event);
1005 
1006         if (!(event->response_type & ~0x80)) {
1007             handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event));
1008             continue;
1009         }
1010 
1011         if (compressEvent(event))
1012             continue;
1013 
1014         handleXcbEvent(event);
1015 
1016         // The lock-based solution used to free the lock inside this loop,
1017         // hence allowing for more events to arrive. ### Check if we want
1018         // this flush here after QTBUG-70095
1019         m_eventQueue->flushBufferedEvents();
1020     }
1021 
1022     xcb_flush(xcb_connection());
1023 }
1024 
formatForDepth(uint8_t depth) const1025 const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const
1026 {
1027     xcb_format_iterator_t iterator =
1028         xcb_setup_pixmap_formats_iterator(setup());
1029 
1030     while (iterator.rem) {
1031         xcb_format_t *format = iterator.data;
1032         if (format->depth == depth)
1033             return format;
1034         xcb_format_next(&iterator);
1035     }
1036 
1037     qWarning() << "XCB failed to find an xcb_format_t for depth:" << depth;
1038     return nullptr;
1039 }
1040 
sync()1041 void QXcbConnection::sync()
1042 {
1043     // from xcb_aux_sync
1044     xcb_get_input_focus_cookie_t cookie = xcb_get_input_focus(xcb_connection());
1045     free(xcb_get_input_focus_reply(xcb_connection(), cookie, nullptr));
1046 }
1047 
systemTrayTracker() const1048 QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const
1049 {
1050     if (!m_systemTrayTracker) {
1051         QXcbConnection *self = const_cast<QXcbConnection *>(this);
1052         if ((self->m_systemTrayTracker = QXcbSystemTrayTracker::create(self))) {
1053             connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)),
1054                     QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)));
1055         }
1056     }
1057     return m_systemTrayTracker;
1058 }
1059 
queryMouseButtons() const1060 Qt::MouseButtons QXcbConnection::queryMouseButtons() const
1061 {
1062     int stateMask = 0;
1063     QXcbCursor::queryPointer(connection(), nullptr, nullptr, &stateMask);
1064     return translateMouseButtons(stateMask);
1065 }
1066 
queryKeyboardModifiers() const1067 Qt::KeyboardModifiers QXcbConnection::queryKeyboardModifiers() const
1068 {
1069     int stateMask = 0;
1070     QXcbCursor::queryPointer(connection(), nullptr, nullptr, &stateMask);
1071     return keyboard()->translateModifiers(stateMask);
1072 }
1073 
glIntegration() const1074 QXcbGlIntegration *QXcbConnection::glIntegration() const
1075 {
1076     if (m_glIntegrationInitialized)
1077         return m_glIntegration;
1078 
1079     QStringList glIntegrationNames;
1080     glIntegrationNames << QStringLiteral("xcb_glx") << QStringLiteral("xcb_egl");
1081     QString glIntegrationName = QString::fromLocal8Bit(qgetenv("QT_XCB_GL_INTEGRATION"));
1082     if (!glIntegrationName.isEmpty()) {
1083         qCDebug(lcQpaGl) << "QT_XCB_GL_INTEGRATION is set to" << glIntegrationName;
1084         if (glIntegrationName != QLatin1String("none")) {
1085             glIntegrationNames.removeAll(glIntegrationName);
1086             glIntegrationNames.prepend(glIntegrationName);
1087         } else {
1088             glIntegrationNames.clear();
1089         }
1090     }
1091 
1092     if (!glIntegrationNames.isEmpty()) {
1093         qCDebug(lcQpaGl) << "Choosing xcb gl-integration based on following priority\n" << glIntegrationNames;
1094         for (int i = 0; i < glIntegrationNames.size() && !m_glIntegration; i++) {
1095             m_glIntegration = QXcbGlIntegrationFactory::create(glIntegrationNames.at(i));
1096             if (m_glIntegration && !m_glIntegration->initialize(const_cast<QXcbConnection *>(this))) {
1097                 qCDebug(lcQpaGl) << "Failed to initialize xcb gl-integration" << glIntegrationNames.at(i);
1098                 delete m_glIntegration;
1099                 m_glIntegration = nullptr;
1100             }
1101         }
1102         if (!m_glIntegration)
1103             qCDebug(lcQpaGl) << "Failed to create xcb gl-integration";
1104     }
1105 
1106     m_glIntegrationInitialized = true;
1107     return m_glIntegration;
1108 }
1109 
event(QEvent * e)1110 bool QXcbConnection::event(QEvent *e)
1111 {
1112     if (e->type() == QEvent::User + 1) {
1113         QXcbSyncWindowRequest *ev = static_cast<QXcbSyncWindowRequest *>(e);
1114         QXcbWindow *w = ev->window();
1115         if (w) {
1116             w->updateSyncRequestCounter();
1117             ev->invalidate();
1118         }
1119         return true;
1120     }
1121     return QObject::event(e);
1122 }
1123 
invalidate()1124 void QXcbSyncWindowRequest::invalidate()
1125 {
1126     if (m_window) {
1127         m_window->clearSyncWindowRequest();
1128         m_window = nullptr;
1129     }
1130 }
1131 
QXcbConnectionGrabber(QXcbConnection * connection)1132 QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection)
1133     :m_connection(connection)
1134 {
1135     connection->grabServer();
1136 }
1137 
~QXcbConnectionGrabber()1138 QXcbConnectionGrabber::~QXcbConnectionGrabber()
1139 {
1140     if (m_connection)
1141         m_connection->ungrabServer();
1142 }
1143 
release()1144 void QXcbConnectionGrabber::release()
1145 {
1146     if (m_connection) {
1147         m_connection->ungrabServer();
1148         m_connection = nullptr;
1149     }
1150 }
1151 
1152 QT_END_NAMESPACE
1153