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

source code of qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp