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 | |
42 | QT_BEGIN_NAMESPACE |
43 | |
44 | using namespace Qt::StringLiterals; |
45 | |
46 | Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input") |
47 | Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events") |
48 | Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") |
49 | Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") |
50 | Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader") |
51 | Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker") |
52 | Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard") |
53 | Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard") |
54 | Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd") |
55 | |
56 | QXcbConnection::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 | |
102 | QXcbConnection::~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 | |
125 | QXcbScreen *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 | |
135 | void QXcbConnection::addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener) |
136 | { |
137 | m_mapper.insert(key: id, value: eventListener); |
138 | } |
139 | |
140 | void QXcbConnection::removeWindowEventListener(xcb_window_t id) |
141 | { |
142 | m_mapper.remove(key: id); |
143 | } |
144 | |
145 | QXcbWindowEventListener *QXcbConnection::windowEventListenerFromId(xcb_window_t id) |
146 | { |
147 | return m_mapper.value(key: id, defaultValue: nullptr); |
148 | } |
149 | |
150 | QXcbWindow *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 | } \ |
167 | break; |
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 | } \ |
178 | break; |
179 | |
180 | void 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 | |
292 | const 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 | |
315 | const 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 | |
440 | void 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 | |
450 | void 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 | |
463 | static 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 | |
475 | void QXcbConnection::setButtonState(Qt::MouseButton button, bool down) |
476 | { |
477 | m_buttonState.setFlag(flag: button, on: down); |
478 | m_button = button; |
479 | } |
480 | |
481 | Qt::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 | |
516 | namespace { |
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 | |
532 | void 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 | |
763 | void QXcbConnection::setFocusWindow(QWindow *w) |
764 | { |
765 | m_focusWindow = w ? static_cast<QXcbWindow *>(w->handle()) : nullptr; |
766 | } |
767 | void QXcbConnection::setMouseGrabber(QXcbWindow *w) |
768 | { |
769 | m_mouseGrabber = w; |
770 | m_mousePressWindow = nullptr; |
771 | } |
772 | void QXcbConnection::setMousePressWindow(QXcbWindow *w) |
773 | { |
774 | m_mousePressWindow = w; |
775 | } |
776 | |
777 | QByteArray QXcbConnection::startupId() const |
778 | { |
779 | return m_startupId; |
780 | } |
781 | void 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 | |
799 | void QXcbConnection::grabServer() |
800 | { |
801 | if (m_canGrabServer) |
802 | xcb_grab_server(c: xcb_connection()); |
803 | } |
804 | |
805 | void QXcbConnection::ungrabServer() |
806 | { |
807 | if (m_canGrabServer) |
808 | xcb_ungrab_server(c: xcb_connection()); |
809 | } |
810 | |
811 | QString QXcbConnection::windowManagerName() const |
812 | { |
813 | QXcbVirtualDesktop *pvd = primaryVirtualDesktop(); |
814 | if (pvd) |
815 | return pvd->windowManagerName().toLower(); |
816 | |
817 | return QString(); |
818 | } |
819 | |
820 | xcb_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 | |
860 | xcb_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 | |
871 | xcb_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 | |
895 | xcb_window_t QXcbConnection::rootWindow() |
896 | { |
897 | QXcbScreen *s = primaryScreen(); |
898 | return s ? s->root() : 0; |
899 | } |
900 | |
901 | xcb_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 | */ |
961 | bool 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 | |
1027 | bool 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 | |
1065 | void 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 | |
1102 | const 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 | |
1118 | void 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 | |
1125 | QXcbSystemTrayTracker *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 | |
1137 | Qt::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 | |
1144 | QXcbGlIntegration *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 | |
1180 | bool 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 | |
1194 | void QXcbSyncWindowRequest::invalidate() |
1195 | { |
1196 | if (m_window) { |
1197 | m_window->clearSyncWindowRequest(); |
1198 | m_window = nullptr; |
1199 | } |
1200 | } |
1201 | |
1202 | QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection) |
1203 | :m_connection(connection) |
1204 | { |
1205 | connection->grabServer(); |
1206 | } |
1207 | |
1208 | QXcbConnectionGrabber::~QXcbConnectionGrabber() |
1209 | { |
1210 | if (m_connection) |
1211 | m_connection->ungrabServer(); |
1212 | } |
1213 | |
1214 | void QXcbConnectionGrabber::release() |
1215 | { |
1216 | if (m_connection) { |
1217 | m_connection->ungrabServer(); |
1218 | m_connection = nullptr; |
1219 | } |
1220 | } |
1221 | |
1222 | QT_END_NAMESPACE |
1223 | |
1224 | #include "moc_qxcbconnection.cpp" |
1225 |
Definitions
- lcQpaXInput
- lcQpaXInputEvents
- lcQpaScreen
- lcQpaEvents
- lcQpaEventReader
- lcQpaPeeker
- lcQpaKeyboard
- lcQpaClipboard
- lcQpaXDnd
- QXcbConnection
- ~QXcbConnection
- primaryScreen
- addWindowEventListener
- removeWindowEventListener
- windowEventListenerFromId
- platformWindowFromId
- printXcbEvent
- xcb_errors
- xcb_protocol_request_codes
- handleXcbError
- printXcbError
- translateMouseButtons
- setButtonState
- translateMouseButton
- handleXcbEvent
- setFocusWindow
- setMouseGrabber
- setMousePressWindow
- startupId
- setStartupId
- grabServer
- ungrabServer
- windowManagerName
- getTimestamp
- selectionOwner
- qtSelectionOwner
- rootWindow
- clientLeader
- compressEvent
- isUserInputEvent
- processXcbEvents
- formatForDepth
- sync
- systemTrayTracker
- queryMouseButtons
- glIntegration
- event
- invalidate
- QXcbConnectionGrabber
- ~QXcbConnectionGrabber
Start learning QML with our Intro Training
Find out more