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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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