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