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(lcQpaXInputDevices, "qt.qpa.input.devices" ) |
48 | Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events" ) |
49 | Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen" ) |
50 | Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events" ) |
51 | Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader" ) |
52 | Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker" ) |
53 | Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard" ) |
54 | Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard" ) |
55 | Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd" ) |
56 | |
57 | QXcbConnection::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 | |
103 | QXcbConnection::~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 | |
126 | QXcbScreen *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 | |
136 | void QXcbConnection::addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener) |
137 | { |
138 | m_mapper.insert(key: id, value: eventListener); |
139 | } |
140 | |
141 | void QXcbConnection::removeWindowEventListener(xcb_window_t id) |
142 | { |
143 | m_mapper.remove(key: id); |
144 | } |
145 | |
146 | QXcbWindowEventListener *QXcbConnection::windowEventListenerFromId(xcb_window_t id) |
147 | { |
148 | return m_mapper.value(key: id, defaultValue: nullptr); |
149 | } |
150 | |
151 | QXcbWindow *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 | } \ |
168 | break; |
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 | } \ |
179 | break; |
180 | |
181 | void 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 | |
293 | const 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 | |
316 | const 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 | |
441 | void 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 | |
451 | void 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 | |
464 | static 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 | |
476 | void QXcbConnection::setButtonState(Qt::MouseButton button, bool down) |
477 | { |
478 | m_buttonState.setFlag(flag: button, on: down); |
479 | m_button = button; |
480 | } |
481 | |
482 | Qt::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 | |
517 | namespace { |
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 | |
533 | void 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 | |
764 | void QXcbConnection::setFocusWindow(QWindow *w) |
765 | { |
766 | m_focusWindow = w ? static_cast<QXcbWindow *>(w->handle()) : nullptr; |
767 | } |
768 | void QXcbConnection::setMouseGrabber(QXcbWindow *w) |
769 | { |
770 | m_mouseGrabber = w; |
771 | m_mousePressWindow = nullptr; |
772 | } |
773 | void QXcbConnection::setMousePressWindow(QXcbWindow *w) |
774 | { |
775 | m_mousePressWindow = w; |
776 | } |
777 | |
778 | QByteArray QXcbConnection::startupId() const |
779 | { |
780 | return m_startupId; |
781 | } |
782 | void 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 | |
800 | void QXcbConnection::grabServer() |
801 | { |
802 | if (m_canGrabServer) |
803 | xcb_grab_server(c: xcb_connection()); |
804 | } |
805 | |
806 | void QXcbConnection::ungrabServer() |
807 | { |
808 | if (m_canGrabServer) |
809 | xcb_ungrab_server(c: xcb_connection()); |
810 | } |
811 | |
812 | QString QXcbConnection::windowManagerName() const |
813 | { |
814 | QXcbVirtualDesktop *pvd = primaryVirtualDesktop(); |
815 | if (pvd) |
816 | return pvd->windowManagerName().toLower(); |
817 | |
818 | return QString(); |
819 | } |
820 | |
821 | xcb_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 | |
861 | xcb_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 | |
872 | xcb_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 | |
896 | xcb_window_t QXcbConnection::rootWindow() |
897 | { |
898 | QXcbScreen *s = primaryScreen(); |
899 | return s ? s->root() : 0; |
900 | } |
901 | |
902 | xcb_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 | */ |
962 | bool 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 | |
1028 | bool 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 | |
1066 | void 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 | |
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 | Qt::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 | |
1151 | QXcbGlIntegration *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 | |
1187 | bool 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 | |
1201 | void QXcbSyncWindowRequest::invalidate() |
1202 | { |
1203 | if (m_window) { |
1204 | m_window->clearSyncWindowRequest(); |
1205 | m_window = nullptr; |
1206 | } |
1207 | } |
1208 | |
1209 | QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection) |
1210 | :m_connection(connection) |
1211 | { |
1212 | connection->grabServer(); |
1213 | } |
1214 | |
1215 | QXcbConnectionGrabber::~QXcbConnectionGrabber() |
1216 | { |
1217 | if (m_connection) |
1218 | m_connection->ungrabServer(); |
1219 | } |
1220 | |
1221 | void QXcbConnectionGrabber::release() |
1222 | { |
1223 | if (m_connection) { |
1224 | m_connection->ungrabServer(); |
1225 | m_connection = nullptr; |
1226 | } |
1227 | } |
1228 | |
1229 | QT_END_NAMESPACE |
1230 | |
1231 | #include "moc_qxcbconnection.cpp" |
1232 | |