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