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

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