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 "qxcbwindow.h"
5
6#include <QtDebug>
7#include <QMetaEnum>
8#include <QScreen>
9#include <QtGui/QIcon>
10#include <QtGui/QRegion>
11#include <QtGui/private/qhighdpiscaling_p.h>
12
13#include "qxcbintegration.h"
14#include "qxcbconnection.h"
15#include "qxcbscreen.h"
16#if QT_CONFIG(draganddrop)
17#include "qxcbdrag.h"
18#endif
19#include "qxcbkeyboard.h"
20#include "qxcbimage.h"
21#include "qxcbwmsupport.h"
22#include "qxcbimage.h"
23#include "qxcbnativeinterface.h"
24#include "qxcbsystemtraytracker.h"
25
26#include <qpa/qplatformintegration.h>
27#include <qpa/qplatformcursor.h>
28
29#include <algorithm>
30
31#include <xcb/xcb_icccm.h>
32#include <xcb/xfixes.h>
33#include <xcb/shape.h>
34#include <xcb/xinput.h>
35
36#include <private/qguiapplication_p.h>
37#include <private/qwindow_p.h>
38
39#include <qpa/qplatformbackingstore.h>
40#include <qpa/qwindowsysteminterface.h>
41
42#include <stdio.h>
43
44#if QT_CONFIG(xcb_xlib)
45#define register /* C++17 deprecated register */
46#include <X11/Xlib.h>
47#include <X11/Xutil.h>
48#undef register
49#endif
50
51#define XCOORD_MAX 32767
52enum {
53 defaultWindowWidth = 160,
54 defaultWindowHeight = 160
55};
56
57QT_BEGIN_NAMESPACE
58
59using namespace Qt::StringLiterals;
60
61Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
62
63Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);
64
65#undef FocusIn
66
67enum QX11EmbedFocusInDetail {
68 XEMBED_FOCUS_CURRENT = 0,
69 XEMBED_FOCUS_FIRST = 1,
70 XEMBED_FOCUS_LAST = 2
71};
72
73enum QX11EmbedInfoFlags {
74 XEMBED_MAPPED = (1 << 0),
75};
76
77enum QX11EmbedMessageType {
78 XEMBED_EMBEDDED_NOTIFY = 0,
79 XEMBED_WINDOW_ACTIVATE = 1,
80 XEMBED_WINDOW_DEACTIVATE = 2,
81 XEMBED_REQUEST_FOCUS = 3,
82 XEMBED_FOCUS_IN = 4,
83 XEMBED_FOCUS_OUT = 5,
84 XEMBED_FOCUS_NEXT = 6,
85 XEMBED_FOCUS_PREV = 7,
86 XEMBED_MODALITY_ON = 10,
87 XEMBED_MODALITY_OFF = 11,
88 XEMBED_REGISTER_ACCELERATOR = 12,
89 XEMBED_UNREGISTER_ACCELERATOR = 13,
90 XEMBED_ACTIVATE_ACCELERATOR = 14
91};
92
93const quint32 XEMBED_VERSION = 0;
94
95QXcbScreen *QXcbWindow::parentScreen()
96{
97 return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : xcbScreen();
98}
99
100QXcbScreen *QXcbWindow::initialScreen() const
101{
102 // Resolve initial screen via QWindowPrivate::screenForGeometry(),
103 // which works in platform independent coordinates, as opposed to
104 // QPlatformWindow::screenForGeometry() that uses native coordinates.
105 QWindowPrivate *windowPrivate = qt_window_private(window: window());
106 QScreen *screen = windowPrivate->screenForGeometry(rect: window()->geometry());
107 return static_cast<QXcbScreen*>(screen->handle());
108}
109
110// Returns \c true if we should set WM_TRANSIENT_FOR on \a w
111static inline bool isTransient(const QWindow *w)
112{
113 return w->type() == Qt::Dialog
114 || w->type() == Qt::Sheet
115 || w->type() == Qt::Tool
116 || w->type() == Qt::SplashScreen
117 || w->type() == Qt::ToolTip
118 || w->type() == Qt::Drawer
119 || w->type() == Qt::Popup;
120}
121
122void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual)
123{
124 if (qt_xcb_imageFormatForVisual(connection: connection(), depth: m_depth, visual, imageFormat: &m_imageFormat, needsRgbSwap: &m_imageRgbSwap))
125 return;
126
127 switch (m_depth) {
128 case 32:
129 case 24:
130 qWarning(msg: "Using RGB32 fallback, if this works your X11 server is reporting a bad screen format.");
131 m_imageFormat = QImage::Format_RGB32;
132 break;
133 case 16:
134 qWarning(msg: "Using RGB16 fallback, if this works your X11 server is reporting a bad screen format.");
135 m_imageFormat = QImage::Format_RGB16;
136 default:
137 break;
138 }
139}
140
141#if QT_CONFIG(xcb_xlib)
142static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s)
143{
144 #include <X11/Xatom.h>
145
146 static XTextProperty tp = { .value: nullptr, .encoding: 0, .format: 0, .nitems: 0 };
147 static bool free_prop = true; // we can't free tp.value in case it references
148 // the data of the static QByteArray below.
149 if (tp.value) {
150 if (free_prop)
151 XFree(tp.value);
152 tp.value = nullptr;
153 free_prop = true;
154 }
155
156 int errCode = 0;
157 QByteArray mapped = s.toLocal8Bit(); // should always be utf-8
158 char* tl[2];
159 tl[0] = mapped.data();
160 tl[1] = nullptr;
161 errCode = XmbTextListToTextProperty(display: dpy, list: tl, count: 1, style: XStdICCTextStyle, text_prop_return: &tp);
162 if (errCode < 0)
163 qCDebug(lcQpaXcb, "XmbTextListToTextProperty result code %d", errCode);
164
165 if (errCode < 0) {
166 static QByteArray qcs;
167 qcs = s.toLatin1();
168 tp.value = (uchar*)qcs.data();
169 tp.encoding = XA_STRING;
170 tp.format = 8;
171 tp.nitems = qcs.size();
172 free_prop = false;
173 }
174 return &tp;
175}
176#endif // QT_CONFIG(xcb_xlib)
177
178// TODO move this into a utility function in QWindow or QGuiApplication
179static QWindow *childWindowAt(QWindow *win, const QPoint &p)
180{
181 for (QObject *obj : win->children()) {
182 if (obj->isWindowType()) {
183 QWindow *childWin = static_cast<QWindow *>(obj);
184 if (childWin->isVisible()) {
185 if (QWindow *recurse = childWindowAt(win: childWin, p))
186 return recurse;
187 }
188 }
189 }
190 if (!win->isTopLevel()
191 && !(win->flags() & Qt::WindowTransparentForInput)
192 && win->geometry().contains(p: win->parent()->mapFromGlobal(pos: p))) {
193 return win;
194 }
195 return nullptr;
196}
197
198static const char *wm_window_type_property_id = "_q_xcb_wm_window_type";
199static const char *wm_window_role_property_id = "_q_xcb_wm_window_role";
200
201QXcbWindow::QXcbWindow(QWindow *window)
202 : QPlatformWindow(window)
203{
204 setConnection(xcbScreen()->connection());
205}
206
207enum : quint32 {
208 baseEventMask
209 = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
210 | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE,
211
212 defaultEventMask = baseEventMask
213 | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE
214 | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
215 | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW
216 | XCB_EVENT_MASK_POINTER_MOTION,
217
218 transparentForInputEventMask = baseEventMask
219 | XCB_EVENT_MASK_VISIBILITY_CHANGE
220 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
221 | XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_OWNER_GRAB_BUTTON
222};
223
224void QXcbWindow::create()
225{
226 destroy();
227
228 m_windowState = Qt::WindowNoState;
229 m_trayIconWindow = isTrayIconWindow(window: window());
230
231 Qt::WindowType type = window()->type();
232
233 QXcbScreen *currentScreen = xcbScreen();
234 QXcbScreen *platformScreen = parent() ? parentScreen() : initialScreen();
235 QRect rect = parent()
236 ? QHighDpi::toNativeLocalPosition(value: window()->geometry(), context: platformScreen)
237 : QHighDpi::toNativePixels(value: window()->geometry(), context: platformScreen);
238
239 if (type == Qt::Desktop) {
240 m_window = platformScreen->root();
241 m_depth = platformScreen->screen()->root_depth;
242 m_visualId = platformScreen->screen()->root_visual;
243 const xcb_visualtype_t *visual = nullptr;
244 if (connection()->hasDefaultVisualId()) {
245 visual = platformScreen->visualForId(visualid: connection()->defaultVisualId());
246 if (visual)
247 m_visualId = connection()->defaultVisualId();
248 if (!visual)
249 qWarning(msg: "Could not use default visual id. Falling back to root_visual for screen.");
250 }
251 if (!visual)
252 visual = platformScreen->visualForId(visualid: m_visualId);
253 setImageFormatForVisual(visual);
254 connection()->addWindowEventListener(id: m_window, eventListener: this);
255 return;
256 }
257
258 const QSize minimumSize = windowMinimumSize();
259 if (rect.width() > 0 || rect.height() > 0) {
260 rect.setWidth(qBound(min: 1, val: rect.width(), XCOORD_MAX));
261 rect.setHeight(qBound(min: 1, val: rect.height(), XCOORD_MAX));
262 } else if (minimumSize.width() > 0 || minimumSize.height() > 0) {
263 rect.setSize(minimumSize);
264 } else {
265 rect.setWidth(QHighDpi::toNativePixels(value: int(defaultWindowWidth), context: platformScreen->QPlatformScreen::screen()));
266 rect.setHeight(QHighDpi::toNativePixels(value: int(defaultWindowHeight), context: platformScreen->QPlatformScreen::screen()));
267 }
268
269 QPlatformWindow::setGeometry(rect);
270
271 if (platformScreen != currentScreen)
272 QWindowSystemInterface::handleWindowScreenChanged(window: window(), newScreen: platformScreen->QPlatformScreen::screen());
273
274 xcb_window_t xcb_parent_id = platformScreen->root();
275 if (parent()) {
276 xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window();
277 m_embedded = parent()->isForeignWindow();
278
279 QSurfaceFormat parentFormat = parent()->window()->requestedFormat();
280 if (window()->surfaceType() != QSurface::OpenGLSurface && parentFormat.hasAlpha()) {
281 window()->setFormat(parentFormat);
282 }
283 }
284
285 resolveFormat(format: platformScreen->surfaceFormatFor(format: window()->requestedFormat()));
286
287 const xcb_visualtype_t *visual = nullptr;
288
289 if (m_trayIconWindow && connection()->systemTrayTracker()) {
290 visual = platformScreen->visualForId(visualid: connection()->systemTrayTracker()->visualId());
291 } else if (connection()->hasDefaultVisualId()) {
292 visual = platformScreen->visualForId(visualid: connection()->defaultVisualId());
293 if (!visual)
294 qWarning() << "Failed to use requested visual id.";
295 }
296
297 if (parent()) {
298 // When using a Vulkan QWindow via QWidget::createWindowContainer() we
299 // must make sure the visuals are compatible. Now, the parent will be
300 // of RasterGLSurface which typically chooses a GLX/EGL compatible
301 // visual which may not be what the Vulkan window would choose.
302 // Therefore, take the parent's visual.
303 if (window()->surfaceType() == QSurface::VulkanSurface
304 && parent()->window()->surfaceType() != QSurface::VulkanSurface)
305 {
306 visual = platformScreen->visualForId(visualid: static_cast<QXcbWindow *>(parent())->visualId());
307 }
308 }
309
310 if (!visual)
311 visual = createVisual();
312
313 if (!visual) {
314 qWarning() << "Falling back to using screens root_visual.";
315 visual = platformScreen->visualForId(visualid: platformScreen->screen()->root_visual);
316 }
317
318 Q_ASSERT(visual);
319
320 m_visualId = visual->visual_id;
321 m_depth = platformScreen->depthOfVisual(visualid: m_visualId);
322 setImageFormatForVisual(visual);
323
324 quint32 mask = XCB_CW_BACK_PIXMAP
325 | XCB_CW_BORDER_PIXEL
326 | XCB_CW_BIT_GRAVITY
327 | XCB_CW_OVERRIDE_REDIRECT
328 | XCB_CW_SAVE_UNDER
329 | XCB_CW_EVENT_MASK
330 | XCB_CW_COLORMAP;
331
332 quint32 values[] = {
333 XCB_BACK_PIXMAP_NONE,
334 platformScreen->screen()->black_pixel,
335 XCB_GRAVITY_NORTH_WEST,
336 type == Qt::Popup || type == Qt::ToolTip || (window()->flags() & Qt::BypassWindowManagerHint),
337 type == Qt::Popup || type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip || type == Qt::Drawer,
338 defaultEventMask,
339 platformScreen->colormapForVisual(visualid: m_visualId)
340 };
341
342 m_window = xcb_generate_id(c: xcb_connection());
343 xcb_create_window(c: xcb_connection(),
344 depth: m_depth,
345 wid: m_window, // window id
346 parent: xcb_parent_id, // parent window id
347 x: rect.x(),
348 y: rect.y(),
349 width: rect.width(),
350 height: rect.height(),
351 border_width: 0, // border width
352 class: XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
353 visual: m_visualId, // visual
354 value_mask: mask,
355 value_list: values);
356
357 connection()->addWindowEventListener(id: m_window, eventListener: this);
358
359 propagateSizeHints();
360
361 xcb_atom_t properties[5];
362 int propertyCount = 0;
363 properties[propertyCount++] = atom(atom: QXcbAtom::AtomWM_DELETE_WINDOW);
364 properties[propertyCount++] = atom(atom: QXcbAtom::AtomWM_TAKE_FOCUS);
365 properties[propertyCount++] = atom(atom: QXcbAtom::Atom_NET_WM_PING);
366
367 if (connection()->hasXSync())
368 properties[propertyCount++] = atom(atom: QXcbAtom::Atom_NET_WM_SYNC_REQUEST);
369
370 if (window()->flags() & Qt::WindowContextHelpButtonHint)
371 properties[propertyCount++] = atom(atom: QXcbAtom::Atom_NET_WM_CONTEXT_HELP);
372
373 xcb_change_property(c: xcb_connection(),
374 mode: XCB_PROP_MODE_REPLACE,
375 window: m_window,
376 property: atom(atom: QXcbAtom::AtomWM_PROTOCOLS),
377 type: XCB_ATOM_ATOM,
378 format: 32,
379 data_len: propertyCount,
380 data: properties);
381 m_syncValue.hi = 0;
382 m_syncValue.lo = 0;
383
384 const QByteArray wmClass = QXcbIntegration::instance()->wmClass();
385 if (!wmClass.isEmpty()) {
386 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE,
387 window: m_window, property: atom(atom: QXcbAtom::AtomWM_CLASS),
388 type: XCB_ATOM_STRING, format: 8, data_len: wmClass.size(), data: wmClass.constData());
389 }
390
391 if (connection()->hasXSync()) {
392 m_syncCounter = xcb_generate_id(c: xcb_connection());
393 xcb_sync_create_counter(c: xcb_connection(), id: m_syncCounter, initial_value: m_syncValue);
394
395 xcb_change_property(c: xcb_connection(),
396 mode: XCB_PROP_MODE_REPLACE,
397 window: m_window,
398 property: atom(atom: QXcbAtom::Atom_NET_WM_SYNC_REQUEST_COUNTER),
399 type: XCB_ATOM_CARDINAL,
400 format: 32,
401 data_len: 1,
402 data: &m_syncCounter);
403 }
404
405 // set the PID to let the WM kill the application if unresponsive
406 quint32 pid = getpid();
407 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
408 property: atom(atom: QXcbAtom::Atom_NET_WM_PID), type: XCB_ATOM_CARDINAL, format: 32,
409 data_len: 1, data: &pid);
410
411 const QByteArray clientMachine = QSysInfo::machineHostName().toLocal8Bit();
412 if (!clientMachine.isEmpty()) {
413 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
414 property: atom(atom: QXcbAtom::AtomWM_CLIENT_MACHINE), type: XCB_ATOM_STRING, format: 8,
415 data_len: clientMachine.size(), data: clientMachine.constData());
416 }
417
418 // Create WM_HINTS property on the window, so we can xcb_icccm_get_wm_hints*()
419 // from various setter functions for adjusting the hints.
420 xcb_icccm_wm_hints_t hints;
421 memset(s: &hints, c: 0, n: sizeof(hints));
422 hints.flags = XCB_ICCCM_WM_HINT_WINDOW_GROUP;
423 hints.window_group = connection()->clientLeader();
424 xcb_icccm_set_wm_hints(c: xcb_connection(), window: m_window, hints: &hints);
425
426 xcb_window_t leader = connection()->clientLeader();
427 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
428 property: atom(atom: QXcbAtom::AtomWM_CLIENT_LEADER), type: XCB_ATOM_WINDOW, format: 32,
429 data_len: 1, data: &leader);
430
431 /* Add XEMBED info; this operation doesn't initiate the embedding. */
432 quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED };
433 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
434 property: atom(atom: QXcbAtom::Atom_XEMBED_INFO),
435 type: atom(atom: QXcbAtom::Atom_XEMBED_INFO),
436 format: 32, data_len: 2, data: (void *)data);
437
438 if (connection()->hasXInput2())
439 connection()->xi2SelectDeviceEvents(window: m_window);
440
441 setWindowState(window()->windowStates());
442 setWindowFlags(window()->flags());
443 setWindowTitle(window()->title());
444
445 // force sync to read outstanding requests - see QTBUG-29106
446 connection()->sync();
447
448#if QT_CONFIG(draganddrop)
449 connection()->drag()->dndEnable(win: this, on: true);
450#endif
451
452 const qreal opacity = qt_window_private(window: window())->opacity;
453 if (!qFuzzyCompare(p1: opacity, p2: qreal(1.0)))
454 setOpacity(opacity);
455
456 setMask(QHighDpi::toNativeLocalRegion(pointRegion: window()->mask(), window: window()));
457
458 if (window()->isTopLevel())
459 setWindowIcon(window()->icon());
460
461 if (window()->dynamicPropertyNames().contains(t: wm_window_role_property_id)) {
462 QByteArray wmWindowRole = window()->property(name: wm_window_role_property_id).toByteArray();
463 setWindowRole(QString::fromLatin1(ba: wmWindowRole));
464 }
465
466 if (m_trayIconWindow)
467 m_embedded = requestSystemTrayWindowDock();
468}
469
470QXcbWindow::~QXcbWindow()
471{
472 destroy();
473}
474
475QXcbForeignWindow::QXcbForeignWindow(QWindow *window, WId nativeHandle)
476 : QXcbWindow(window)
477{
478 m_window = nativeHandle;
479
480 // Reflect the foreign window's geometry as our own
481 if (auto geometry = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), m_window)) {
482 QRect nativeGeometry(geometry->x, geometry->y, geometry->width, geometry->height);
483 QPlatformWindow::setGeometry(nativeGeometry);
484 }
485}
486
487QXcbForeignWindow::~QXcbForeignWindow()
488{
489 // Clear window so that destroy() does not affect it
490 m_window = 0;
491
492 if (connection()->mouseGrabber() == this)
493 connection()->setMouseGrabber(nullptr);
494 if (connection()->mousePressWindow() == this)
495 connection()->setMousePressWindow(nullptr);
496}
497
498void QXcbWindow::destroy()
499{
500 if (connection()->focusWindow() == this)
501 doFocusOut();
502 if (connection()->mouseGrabber() == this)
503 connection()->setMouseGrabber(nullptr);
504
505 if (m_syncCounter && connection()->hasXSync())
506 xcb_sync_destroy_counter(c: xcb_connection(), counter: m_syncCounter);
507 if (m_window) {
508 if (m_netWmUserTimeWindow) {
509 xcb_delete_property(c: xcb_connection(), window: m_window, property: atom(atom: QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW));
510 // Some window managers, like metacity, do XSelectInput on the _NET_WM_USER_TIME_WINDOW window,
511 // without trapping BadWindow (which crashes when the user time window is destroyed).
512 connection()->sync();
513 xcb_destroy_window(c: xcb_connection(), window: m_netWmUserTimeWindow);
514 m_netWmUserTimeWindow = XCB_NONE;
515 }
516 connection()->removeWindowEventListener(id: m_window);
517 xcb_destroy_window(c: xcb_connection(), window: m_window);
518 m_window = 0;
519 }
520
521 m_mapped = false;
522 m_recreationReasons = RecreationNotNeeded;
523
524 if (m_pendingSyncRequest)
525 m_pendingSyncRequest->invalidate();
526}
527
528void QXcbWindow::setGeometry(const QRect &rect)
529{
530 setWindowState(Qt::WindowNoState);
531
532 QPlatformWindow::setGeometry(rect);
533
534 propagateSizeHints();
535
536 QXcbScreen *currentScreen = xcbScreen();
537 QXcbScreen *newScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(newGeometry: rect));
538
539 if (!newScreen)
540 newScreen = xcbScreen();
541
542 if (newScreen != currentScreen)
543 QWindowSystemInterface::handleWindowScreenChanged(window: window(), newScreen: newScreen->QPlatformScreen::screen());
544
545 if (qt_window_private(window: window())->positionAutomatic) {
546 const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
547 const qint32 values[] = {
548 qBound<qint32>(min: 1, val: rect.width(), XCOORD_MAX),
549 qBound<qint32>(min: 1, val: rect.height(), XCOORD_MAX),
550 };
551 xcb_configure_window(c: xcb_connection(), window: m_window, value_mask: mask, value_list: reinterpret_cast<const quint32*>(values));
552 } else {
553 const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
554 const qint32 values[] = {
555 qBound<qint32>(min: -XCOORD_MAX, val: rect.x(), XCOORD_MAX),
556 qBound<qint32>(min: -XCOORD_MAX, val: rect.y(), XCOORD_MAX),
557 qBound<qint32>(min: 1, val: rect.width(), XCOORD_MAX),
558 qBound<qint32>(min: 1, val: rect.height(), XCOORD_MAX),
559 };
560 xcb_configure_window(c: xcb_connection(), window: m_window, value_mask: mask, value_list: reinterpret_cast<const quint32*>(values));
561 if (window()->parent() && !window()->transientParent()) {
562 // Wait for server reply for parented windows to ensure that a few window
563 // moves will come as a one event. This is important when native widget is
564 // moved a few times in X and Y directions causing native scroll. Widget
565 // must get single event to not trigger unwanted widget position changes
566 // and then expose events causing backingstore flushes with incorrect
567 // offset causing image crruption.
568 connection()->sync();
569 }
570 }
571
572 xcb_flush(c: xcb_connection());
573}
574
575QMargins QXcbWindow::frameMargins() const
576{
577 if (m_dirtyFrameMargins) {
578 if (connection()->wmSupport()->isSupportedByWM(atom: atom(atom: QXcbAtom::Atom_NET_FRAME_EXTENTS))) {
579 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, m_window,
580 atom(QXcbAtom::Atom_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
581 if (reply && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && reply->value_len == 4) {
582 quint32 *data = (quint32 *)xcb_get_property_value(R: reply.get());
583 // _NET_FRAME_EXTENTS format is left, right, top, bottom
584 m_frameMargins = QMargins(data[0], data[2], data[1], data[3]);
585 m_dirtyFrameMargins = false;
586 return m_frameMargins;
587 }
588 }
589
590 // _NET_FRAME_EXTENTS property is not available, so
591 // walk up the window tree to get the frame parent
592 xcb_window_t window = m_window;
593 xcb_window_t parent = m_window;
594
595 bool foundRoot = false;
596
597 const QList<xcb_window_t> &virtualRoots =
598 connection()->wmSupport()->virtualRoots();
599
600 while (!foundRoot) {
601 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, xcb_connection(), parent);
602 if (reply) {
603 if (reply->root == reply->parent || virtualRoots.indexOf(t: reply->parent) != -1 || reply->parent == XCB_WINDOW_NONE) {
604 foundRoot = true;
605 } else {
606 window = parent;
607 parent = reply->parent;
608 }
609 } else {
610 m_dirtyFrameMargins = false;
611 m_frameMargins = QMargins();
612 return m_frameMargins;
613 }
614 }
615
616 QPoint offset;
617
618 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(), window, parent, 0, 0);
619 if (reply) {
620 offset = QPoint(reply->dst_x, reply->dst_y);
621 }
622
623 auto geom = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), parent);
624 if (geom) {
625 // --
626 // add the border_width for the window managers frame... some window managers
627 // do not use a border_width of zero for their frames, and if we the left and
628 // top strut, we ensure that pos() is absolutely correct. frameGeometry()
629 // will still be incorrect though... perhaps i should have foffset as well, to
630 // indicate the frame offset (equal to the border_width on X).
631 // - Brad
632 // -- copied from qwidget_x11.cpp
633
634 int left = offset.x() + geom->border_width;
635 int top = offset.y() + geom->border_width;
636 int right = geom->width + geom->border_width - geometry().width() - offset.x();
637 int bottom = geom->height + geom->border_width - geometry().height() - offset.y();
638
639 m_frameMargins = QMargins(left, top, right, bottom);
640 }
641
642 m_dirtyFrameMargins = false;
643 }
644
645 return m_frameMargins;
646}
647
648void QXcbWindow::setVisible(bool visible)
649{
650 if (visible)
651 show();
652 else
653 hide();
654}
655
656void QXcbWindow::show()
657{
658 if (window()->isTopLevel()) {
659 if (m_recreationReasons != RecreationNotNeeded) {
660 qCDebug(lcQpaWindow) << "QXcbWindow: need to recreate window" << window() << m_recreationReasons;
661 create();
662 m_recreationReasons = RecreationNotNeeded;
663 }
664
665 // update WM_NORMAL_HINTS
666 propagateSizeHints();
667
668 // update WM_TRANSIENT_FOR
669 xcb_window_t transientXcbParent = 0;
670 if (isTransient(w: window())) {
671 const QWindow *tp = window()->transientParent();
672 if (tp && tp->handle())
673 transientXcbParent = tp->handle()->winId();
674 // Default to client leader if there is no transient parent, else modal dialogs can
675 // be hidden by their parents.
676 if (!transientXcbParent)
677 transientXcbParent = connection()->clientLeader();
678 if (transientXcbParent) { // ICCCM 4.1.2.6
679 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
680 property: XCB_ATOM_WM_TRANSIENT_FOR, type: XCB_ATOM_WINDOW, format: 32,
681 data_len: 1, data: &transientXcbParent);
682 }
683 }
684 if (!transientXcbParent)
685 xcb_delete_property(c: xcb_connection(), window: m_window, property: XCB_ATOM_WM_TRANSIENT_FOR);
686
687 // update _NET_WM_STATE
688 setNetWmStateOnUnmappedWindow();
689 }
690
691 // QWidget-attribute Qt::WA_ShowWithoutActivating.
692 const auto showWithoutActivating = window()->property(name: "_q_showWithoutActivating");
693 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
694 updateNetWmUserTime(timestamp: 0);
695 else if (connection()->time() != XCB_TIME_CURRENT_TIME)
696 updateNetWmUserTime(timestamp: connection()->time());
697
698 if (m_trayIconWindow)
699 return; // defer showing until XEMBED_EMBEDDED_NOTIFY
700
701 xcb_map_window(c: xcb_connection(), window: m_window);
702
703 if (QGuiApplication::modalWindow() == window())
704 requestActivateWindow();
705
706 xcbScreen()->windowShown(window: this);
707
708 connection()->sync();
709}
710
711void QXcbWindow::hide()
712{
713 xcb_unmap_window(c: xcb_connection(), window: m_window);
714
715 // send synthetic UnmapNotify event according to icccm 4.1.4
716 q_padded_xcb_event<xcb_unmap_notify_event_t> event = {};
717 event.response_type = XCB_UNMAP_NOTIFY;
718 event.event = xcbScreen()->root();
719 event.window = m_window;
720 event.from_configure = false;
721 xcb_send_event(c: xcb_connection(), propagate: false, destination: xcbScreen()->root(),
722 event_mask: XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, event: (const char *)&event);
723
724 xcb_flush(c: xcb_connection());
725
726 if (connection()->mouseGrabber() == this)
727 connection()->setMouseGrabber(nullptr);
728 if (QPlatformWindow *w = connection()->mousePressWindow()) {
729 // Unset mousePressWindow when it (or one of its parents) is unmapped
730 while (w) {
731 if (w == this) {
732 connection()->setMousePressWindow(nullptr);
733 break;
734 }
735 w = w->parent();
736 }
737 }
738
739 m_mapped = false;
740
741 // Hiding a modal window doesn't send an enter event to its transient parent when the
742 // mouse is already over the parent window, so the enter event must be emulated.
743 if (window()->isModal()) {
744 // Get the cursor position at modal window screen
745 const QPoint nativePos = xcbScreen()->cursor()->pos();
746 const QPoint cursorPos = QHighDpi::fromNativePixels(value: nativePos, context: xcbScreen()->screenForPosition(point: nativePos)->screen());
747
748 // Find the top level window at cursor position.
749 // Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen
750 QWindow *enterWindow = nullptr;
751 const auto screens = xcbScreen()->virtualSiblings();
752 for (QPlatformScreen *screen : screens) {
753 if (screen->geometry().contains(p: cursorPos)) {
754 const QPoint devicePosition = QHighDpi::toNativePixels(value: cursorPos, context: screen->screen());
755 enterWindow = screen->topLevelAt(point: devicePosition);
756 break;
757 }
758 }
759
760 if (enterWindow && enterWindow != window()) {
761 // Find the child window at cursor position, otherwise use the top level window
762 if (QWindow *childWindow = childWindowAt(win: enterWindow, p: cursorPos))
763 enterWindow = childWindow;
764 const QPoint localPos = enterWindow->mapFromGlobal(pos: cursorPos);
765 QWindowSystemInterface::handleEnterEvent(window: enterWindow,
766 local: localPos * QHighDpiScaling::factor(context: enterWindow),
767 global: nativePos);
768 }
769 }
770}
771
772bool QXcbWindow::relayFocusToModalWindow() const
773{
774 QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(o: window()))->eventReceiver();
775 // get top-level window
776 while (w && w->parent())
777 w = w->parent();
778
779 QWindow *modalWindow = nullptr;
780 const bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window: w, blockingWindow: &modalWindow);
781 if (blocked && modalWindow != w) {
782 modalWindow->requestActivate();
783 connection()->flush();
784 return true;
785 }
786
787 return false;
788}
789
790void QXcbWindow::doFocusIn()
791{
792 if (relayFocusToModalWindow())
793 return;
794 QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(o: window()))->eventReceiver();
795 connection()->setFocusWindow(w);
796 QWindowSystemInterface::handleWindowActivated(window: w, r: Qt::ActiveWindowFocusReason);
797}
798
799void QXcbWindow::doFocusOut()
800{
801 connection()->setFocusWindow(nullptr);
802 relayFocusToModalWindow();
803 // Do not set the active window to nullptr if there is a FocusIn coming.
804 connection()->focusInTimer().start();
805}
806
807struct QtMotifWmHints {
808 quint32 flags, functions, decorations;
809 qint32 input_mode; // unused
810 quint32 status; // unused
811};
812
813enum {
814 MWM_HINTS_FUNCTIONS = (1L << 0),
815
816 MWM_FUNC_ALL = (1L << 0),
817 MWM_FUNC_RESIZE = (1L << 1),
818 MWM_FUNC_MOVE = (1L << 2),
819 MWM_FUNC_MINIMIZE = (1L << 3),
820 MWM_FUNC_MAXIMIZE = (1L << 4),
821 MWM_FUNC_CLOSE = (1L << 5),
822
823 MWM_HINTS_DECORATIONS = (1L << 1),
824
825 MWM_DECOR_ALL = (1L << 0),
826 MWM_DECOR_BORDER = (1L << 1),
827 MWM_DECOR_RESIZEH = (1L << 2),
828 MWM_DECOR_TITLE = (1L << 3),
829 MWM_DECOR_MENU = (1L << 4),
830 MWM_DECOR_MINIMIZE = (1L << 5),
831 MWM_DECOR_MAXIMIZE = (1L << 6),
832};
833
834QXcbWindow::NetWmStates QXcbWindow::netWmStates()
835{
836 NetWmStates result;
837
838 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
839 0, m_window, atom(QXcbAtom::Atom_NET_WM_STATE),
840 XCB_ATOM_ATOM, 0, 1024);
841
842 if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
843 const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(R: reply.get()));
844 const xcb_atom_t *statesEnd = states + reply->length;
845 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_ABOVE)))
846 result |= NetWmStateAbove;
847 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_BELOW)))
848 result |= NetWmStateBelow;
849 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN)))
850 result |= NetWmStateFullScreen;
851 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ)))
852 result |= NetWmStateMaximizedHorz;
853 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)))
854 result |= NetWmStateMaximizedVert;
855 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MODAL)))
856 result |= NetWmStateModal;
857 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP)))
858 result |= NetWmStateStaysOnTop;
859 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION)))
860 result |= NetWmStateDemandsAttention;
861 if (statesEnd != std::find(first: states, last: statesEnd, val: atom(atom: QXcbAtom::Atom_NET_WM_STATE_HIDDEN)))
862 result |= NetWmStateHidden;
863 } else {
864 qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window);
865 }
866
867 return result;
868}
869
870void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
871{
872 Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
873
874 if (type == Qt::ToolTip)
875 flags |= Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint;
876 if (type == Qt::Popup)
877 flags |= Qt::X11BypassWindowManagerHint;
878
879 Qt::WindowFlags oldflags = window()->flags();
880 if ((oldflags & Qt::WindowStaysOnTopHint) != (flags & Qt::WindowStaysOnTopHint))
881 m_recreationReasons |= WindowStaysOnTopHintChanged;
882 if ((oldflags & Qt::WindowStaysOnBottomHint) != (flags & Qt::WindowStaysOnBottomHint))
883 m_recreationReasons |= WindowStaysOnBottomHintChanged;
884
885 const quint32 mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
886 const quint32 values[] = {
887 // XCB_CW_OVERRIDE_REDIRECT
888 (flags & Qt::BypassWindowManagerHint) ? 1u : 0,
889 // XCB_CW_EVENT_MASK
890 (flags & Qt::WindowTransparentForInput) ? transparentForInputEventMask : defaultEventMask
891 };
892
893 xcb_change_window_attributes(c: xcb_connection(), window: xcb_window(), value_mask: mask, value_list: values);
894
895 WindowTypes wmWindowTypes;
896 if (window()->dynamicPropertyNames().contains(t: wm_window_type_property_id)) {
897 wmWindowTypes = static_cast<WindowTypes>(
898 qvariant_cast<int>(v: window()->property(name: wm_window_type_property_id)));
899 }
900
901 setWmWindowType(types: wmWindowTypes, flags);
902 setNetWmState(flags);
903 setMotifWmHints(flags);
904
905 setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
906 updateDoesNotAcceptFocus(doesNotAcceptFocus: flags & Qt::WindowDoesNotAcceptFocus);
907}
908
909void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags)
910{
911 Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
912
913 QtMotifWmHints mwmhints;
914 memset(s: &mwmhints, c: 0, n: sizeof(mwmhints));
915
916 if (type != Qt::SplashScreen) {
917 mwmhints.flags |= MWM_HINTS_DECORATIONS;
918
919 bool customize = flags & Qt::CustomizeWindowHint;
920 if (type == Qt::Window && !customize) {
921 const Qt::WindowFlags defaultFlags = Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
922 if (!(flags & defaultFlags))
923 flags |= defaultFlags;
924 }
925 if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) {
926 mwmhints.decorations |= MWM_DECOR_BORDER;
927 mwmhints.decorations |= MWM_DECOR_RESIZEH;
928 mwmhints.decorations |= MWM_DECOR_TITLE;
929
930 if (flags & Qt::WindowSystemMenuHint)
931 mwmhints.decorations |= MWM_DECOR_MENU;
932
933 if (flags & Qt::WindowMinimizeButtonHint) {
934 mwmhints.decorations |= MWM_DECOR_MINIMIZE;
935 mwmhints.functions |= MWM_FUNC_MINIMIZE;
936 }
937
938 if (flags & Qt::WindowMaximizeButtonHint) {
939 mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
940 mwmhints.functions |= MWM_FUNC_MAXIMIZE;
941 }
942
943 if (flags & Qt::WindowCloseButtonHint)
944 mwmhints.functions |= MWM_FUNC_CLOSE;
945 }
946 } else {
947 // if type == Qt::SplashScreen
948 mwmhints.decorations = MWM_DECOR_ALL;
949 }
950
951 if (mwmhints.functions != 0) {
952 mwmhints.flags |= MWM_HINTS_FUNCTIONS;
953 mwmhints.functions |= MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
954 } else {
955 mwmhints.functions = MWM_FUNC_ALL;
956 }
957
958 if (!(flags & Qt::FramelessWindowHint)
959 && flags & Qt::CustomizeWindowHint
960 && flags & Qt::WindowTitleHint
961 && !(flags &
962 (Qt::WindowMinimizeButtonHint
963 | Qt::WindowMaximizeButtonHint
964 | Qt::WindowCloseButtonHint)))
965 {
966 // a special case - only the titlebar without any button
967 mwmhints.flags = MWM_HINTS_FUNCTIONS;
968 mwmhints.functions = MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
969 mwmhints.decorations = 0;
970 }
971
972 if (mwmhints.flags) {
973 xcb_change_property(c: xcb_connection(),
974 mode: XCB_PROP_MODE_REPLACE,
975 window: m_window,
976 property: atom(atom: QXcbAtom::Atom_MOTIF_WM_HINTS),
977 type: atom(atom: QXcbAtom::Atom_MOTIF_WM_HINTS),
978 format: 32,
979 data_len: 5,
980 data: &mwmhints);
981 } else {
982 xcb_delete_property(c: xcb_connection(), window: m_window, property: atom(atom: QXcbAtom::Atom_MOTIF_WM_HINTS));
983 }
984}
985
986void QXcbWindow::setNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
987{
988 xcb_client_message_event_t event;
989
990 event.response_type = XCB_CLIENT_MESSAGE;
991 event.format = 32;
992 event.sequence = 0;
993 event.window = m_window;
994 event.type = atom(atom: QXcbAtom::Atom_NET_WM_STATE);
995 event.data.data32[0] = set ? 1 : 0;
996 event.data.data32[1] = one;
997 event.data.data32[2] = two;
998 event.data.data32[3] = 0;
999 event.data.data32[4] = 0;
1000
1001 xcb_send_event(c: xcb_connection(), propagate: 0, destination: xcbScreen()->root(),
1002 event_mask: XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1003 event: (const char *)&event);
1004}
1005
1006void QXcbWindow::setNetWmState(Qt::WindowStates state)
1007{
1008 if ((m_windowState ^ state) & Qt::WindowMaximized) {
1009 setNetWmState(set: state & Qt::WindowMaximized,
1010 one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ),
1011 two: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));
1012 }
1013
1014 if ((m_windowState ^ state) & Qt::WindowFullScreen)
1015 setNetWmState(set: state & Qt::WindowFullScreen, one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));
1016}
1017
1018void QXcbWindow::setNetWmState(Qt::WindowFlags flags)
1019{
1020 setNetWmState(set: flags & Qt::WindowStaysOnTopHint,
1021 one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_ABOVE),
1022 two: atom(atom: QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP));
1023 setNetWmState(set: flags & Qt::WindowStaysOnBottomHint, one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_BELOW));
1024}
1025
1026void QXcbWindow::setNetWmStateOnUnmappedWindow()
1027{
1028 if (Q_UNLIKELY(m_mapped))
1029 qCWarning(lcQpaXcb()) << "internal error: " << Q_FUNC_INFO << "called on mapped window";
1030
1031 NetWmStates states;
1032 const Qt::WindowFlags flags = window()->flags();
1033 if (flags & Qt::WindowStaysOnTopHint) {
1034 states |= NetWmStateAbove;
1035 states |= NetWmStateStaysOnTop;
1036 } else if (flags & Qt::WindowStaysOnBottomHint) {
1037 states |= NetWmStateBelow;
1038 }
1039
1040 if (window()->windowStates() & Qt::WindowMinimized)
1041 states |= NetWmStateHidden;
1042
1043 if (window()->windowStates() & Qt::WindowFullScreen)
1044 states |= NetWmStateFullScreen;
1045
1046 if (window()->windowStates() & Qt::WindowMaximized) {
1047 states |= NetWmStateMaximizedHorz;
1048 states |= NetWmStateMaximizedVert;
1049 }
1050
1051 if (window()->modality() != Qt::NonModal)
1052 states |= NetWmStateModal;
1053
1054 // According to EWMH:
1055 // "The Window Manager should remove _NET_WM_STATE whenever a window is withdrawn".
1056 // Which means that we don't have to read this property before changing it on a withdrawn
1057 // window. But there are situations where users want to adjust this property as well
1058 // (e4cea305ed2ba3c9f580bf9d16c59a1048af0e8a), so instead of overwriting the property
1059 // we first read it and then merge our hints with the existing values, allowing a user
1060 // to set custom hints.
1061
1062 QList<xcb_atom_t> atoms;
1063 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
1064 0, m_window, atom(QXcbAtom::Atom_NET_WM_STATE),
1065 XCB_ATOM_ATOM, 0, 1024);
1066 if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) {
1067 const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(R: reply.get()));
1068 atoms.resize(size: reply->value_len);
1069 memcpy(dest: (void *)&atoms.first(), src: (void *)data, n: reply->value_len * sizeof(xcb_atom_t));
1070 }
1071
1072 if (states & NetWmStateAbove && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_ABOVE)))
1073 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_ABOVE));
1074 if (states & NetWmStateBelow && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_BELOW)))
1075 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_BELOW));
1076 if (states & NetWmStateHidden && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_HIDDEN)))
1077 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_HIDDEN));
1078 if (states & NetWmStateFullScreen && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN)))
1079 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));
1080 if (states & NetWmStateMaximizedHorz && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ)))
1081 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ));
1082 if (states & NetWmStateMaximizedVert && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)))
1083 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));
1084 if (states & NetWmStateModal && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MODAL)))
1085 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MODAL));
1086 if (states & NetWmStateStaysOnTop && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP)))
1087 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP));
1088 if (states & NetWmStateDemandsAttention && !atoms.contains(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION)))
1089 atoms.push_back(t: atom(atom: QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION));
1090
1091 if (atoms.isEmpty()) {
1092 xcb_delete_property(c: xcb_connection(), window: m_window, property: atom(atom: QXcbAtom::Atom_NET_WM_STATE));
1093 } else {
1094 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
1095 property: atom(atom: QXcbAtom::Atom_NET_WM_STATE), type: XCB_ATOM_ATOM, format: 32,
1096 data_len: atoms.size(), data: atoms.constData());
1097 }
1098 xcb_flush(c: xcb_connection());
1099}
1100
1101void QXcbWindow::setWindowState(Qt::WindowStates state)
1102{
1103 if (state == m_windowState)
1104 return;
1105
1106 // unset old state
1107 if (m_windowState & Qt::WindowMinimized)
1108 xcb_map_window(c: xcb_connection(), window: m_window);
1109 if (m_windowState & Qt::WindowMaximized)
1110 setNetWmState(set: false,
1111 one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ),
1112 two: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));
1113 if (m_windowState & Qt::WindowFullScreen)
1114 setNetWmState(set: false, one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));
1115
1116 // set new state
1117 if (state & Qt::WindowMinimized) {
1118 {
1119 xcb_client_message_event_t event;
1120
1121 event.response_type = XCB_CLIENT_MESSAGE;
1122 event.format = 32;
1123 event.sequence = 0;
1124 event.window = m_window;
1125 event.type = atom(atom: QXcbAtom::AtomWM_CHANGE_STATE);
1126 event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC;
1127 event.data.data32[1] = 0;
1128 event.data.data32[2] = 0;
1129 event.data.data32[3] = 0;
1130 event.data.data32[4] = 0;
1131
1132 xcb_send_event(c: xcb_connection(), propagate: 0, destination: xcbScreen()->root(),
1133 event_mask: XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1134 event: (const char *)&event);
1135 }
1136 m_minimized = true;
1137 }
1138 if (state & Qt::WindowMaximized)
1139 setNetWmState(set: true,
1140 one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ),
1141 two: atom(atom: QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));
1142 if (state & Qt::WindowFullScreen)
1143 setNetWmState(set: true, one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));
1144
1145 setNetWmState(state);
1146
1147 xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(c: xcb_connection(), window: m_window);
1148 xcb_icccm_wm_hints_t hints;
1149 if (xcb_icccm_get_wm_hints_reply(c: xcb_connection(), cookie, hints: &hints, e: nullptr)) {
1150 if (state & Qt::WindowMinimized)
1151 xcb_icccm_wm_hints_set_iconic(hints: &hints);
1152 else
1153 xcb_icccm_wm_hints_set_normal(hints: &hints);
1154 xcb_icccm_set_wm_hints(c: xcb_connection(), window: m_window, hints: &hints);
1155 }
1156
1157 connection()->sync();
1158 m_windowState = state;
1159}
1160
1161void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
1162{
1163 xcb_window_t wid = m_window;
1164 // If timestamp == 0, then it means that the window should not be
1165 // initially activated. Don't update global user time for this
1166 // special case.
1167 if (timestamp != 0)
1168 connection()->setNetWmUserTime(timestamp);
1169
1170 const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom: atom(atom: QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW));
1171 if (m_netWmUserTimeWindow || isSupportedByWM) {
1172 if (!m_netWmUserTimeWindow) {
1173 m_netWmUserTimeWindow = xcb_generate_id(c: xcb_connection());
1174 xcb_create_window(c: xcb_connection(),
1175 XCB_COPY_FROM_PARENT, // depth -- same as root
1176 wid: m_netWmUserTimeWindow, // window id
1177 parent: m_window, // parent window id
1178 x: -1, y: -1, width: 1, height: 1,
1179 border_width: 0, // border width
1180 class: XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
1181 visual: m_visualId, // visual
1182 value_mask: 0, // value mask
1183 value_list: nullptr); // value list
1184 wid = m_netWmUserTimeWindow;
1185 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window, property: atom(atom: QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW),
1186 type: XCB_ATOM_WINDOW, format: 32, data_len: 1, data: &m_netWmUserTimeWindow);
1187 xcb_delete_property(c: xcb_connection(), window: m_window, property: atom(atom: QXcbAtom::Atom_NET_WM_USER_TIME));
1188
1189 QXcbWindow::setWindowTitle(conn: connection(), window: m_netWmUserTimeWindow,
1190 QStringLiteral("Qt NET_WM User Time Window"));
1191
1192 } else if (!isSupportedByWM) {
1193 // WM no longer supports it, then we should remove the
1194 // _NET_WM_USER_TIME_WINDOW atom.
1195 xcb_delete_property(c: xcb_connection(), window: m_window, property: atom(atom: QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW));
1196 xcb_destroy_window(c: xcb_connection(), window: m_netWmUserTimeWindow);
1197 m_netWmUserTimeWindow = XCB_NONE;
1198 } else {
1199 wid = m_netWmUserTimeWindow;
1200 }
1201 }
1202 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: wid, property: atom(atom: QXcbAtom::Atom_NET_WM_USER_TIME),
1203 type: XCB_ATOM_CARDINAL, format: 32, data_len: 1, data: &timestamp);
1204}
1205
1206void QXcbWindow::setTransparentForMouseEvents(bool transparent)
1207{
1208 if (!connection()->hasXFixes() || transparent == m_transparent)
1209 return;
1210
1211 xcb_rectangle_t rectangle;
1212
1213 xcb_rectangle_t *rect = nullptr;
1214 int nrect = 0;
1215
1216 if (!transparent) {
1217 rectangle.x = 0;
1218 rectangle.y = 0;
1219 rectangle.width = geometry().width();
1220 rectangle.height = geometry().height();
1221 rect = &rectangle;
1222 nrect = 1;
1223 }
1224
1225 xcb_xfixes_region_t region = xcb_generate_id(c: xcb_connection());
1226 xcb_xfixes_create_region(c: xcb_connection(), region, rectangles_len: nrect, rectangles: rect);
1227 xcb_xfixes_set_window_shape_region_checked(c: xcb_connection(), dest: m_window, dest_kind: XCB_SHAPE_SK_INPUT, x_offset: 0, y_offset: 0, region);
1228 xcb_xfixes_destroy_region(c: xcb_connection(), region);
1229
1230 m_transparent = transparent;
1231}
1232
1233void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus)
1234{
1235 xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(c: xcb_connection(), window: m_window);
1236
1237 xcb_icccm_wm_hints_t hints;
1238 if (!xcb_icccm_get_wm_hints_reply(c: xcb_connection(), cookie, hints: &hints, e: nullptr))
1239 return;
1240
1241 xcb_icccm_wm_hints_set_input(hints: &hints, input: !doesNotAcceptFocus);
1242 xcb_icccm_set_wm_hints(c: xcb_connection(), window: m_window, hints: &hints);
1243}
1244
1245WId QXcbWindow::winId() const
1246{
1247 return m_window;
1248}
1249
1250void QXcbWindow::setParent(const QPlatformWindow *parent)
1251{
1252 QPoint topLeft = geometry().topLeft();
1253
1254 xcb_window_t xcb_parent_id;
1255 if (parent) {
1256 const QXcbWindow *qXcbParent = static_cast<const QXcbWindow *>(parent);
1257 xcb_parent_id = qXcbParent->xcb_window();
1258 m_embedded = qXcbParent->isForeignWindow();
1259 } else {
1260 xcb_parent_id = xcbScreen()->root();
1261 m_embedded = false;
1262 }
1263 xcb_reparent_window(c: xcb_connection(), window: xcb_window(), parent: xcb_parent_id, x: topLeft.x(), y: topLeft.y());
1264}
1265
1266void QXcbWindow::setWindowTitle(const QString &title)
1267{
1268 setWindowTitle(conn: connection(), window: m_window, title);
1269}
1270
1271void QXcbWindow::setWindowIconText(const QString &title)
1272{
1273 const QByteArray ba = title.toUtf8();
1274 xcb_change_property(c: xcb_connection(),
1275 mode: XCB_PROP_MODE_REPLACE,
1276 window: m_window,
1277 property: atom(atom: QXcbAtom::Atom_NET_WM_ICON_NAME),
1278 type: atom(atom: QXcbAtom::AtomUTF8_STRING),
1279 format: 8,
1280 data_len: ba.size(),
1281 data: ba.constData());
1282}
1283
1284void QXcbWindow::setWindowIcon(const QIcon &icon)
1285{
1286 QList<quint32> icon_data;
1287 if (!icon.isNull()) {
1288 QList<QSize> availableSizes = icon.availableSizes();
1289 if (availableSizes.isEmpty()) {
1290 // try to use default sizes since the icon can be a scalable image like svg.
1291 availableSizes.push_back(t: QSize(16,16));
1292 availableSizes.push_back(t: QSize(32,32));
1293 availableSizes.push_back(t: QSize(64,64));
1294 availableSizes.push_back(t: QSize(128,128));
1295 }
1296 for (int i = 0; i < availableSizes.size(); ++i) {
1297 QSize size = availableSizes.at(i);
1298 QPixmap pixmap = icon.pixmap(size);
1299 if (!pixmap.isNull()) {
1300 QImage image = pixmap.toImage().convertToFormat(f: QImage::Format_ARGB32);
1301 int pos = icon_data.size();
1302 icon_data.resize(size: pos + 2 + image.width()*image.height());
1303 icon_data[pos++] = image.width();
1304 icon_data[pos++] = image.height();
1305 memcpy(dest: icon_data.data() + pos, src: image.bits(), n: image.width()*image.height()*4);
1306 }
1307 }
1308 }
1309
1310 if (!icon_data.isEmpty()) {
1311 // Ignore icon exceeding maximum xcb request length
1312 if (quint64(icon_data.size()) > quint64(xcb_get_maximum_request_length(c: xcb_connection()))) {
1313 qWarning() << "Ignoring window icon" << icon_data.size()
1314 << "exceeds maximum xcb request length"
1315 << xcb_get_maximum_request_length(c: xcb_connection());
1316 return;
1317 }
1318 xcb_change_property(c: xcb_connection(),
1319 mode: XCB_PROP_MODE_REPLACE,
1320 window: m_window,
1321 property: atom(atom: QXcbAtom::Atom_NET_WM_ICON),
1322 type: atom(atom: QXcbAtom::AtomCARDINAL),
1323 format: 32,
1324 data_len: icon_data.size(),
1325 data: (unsigned char *) icon_data.data());
1326 } else {
1327 xcb_delete_property(c: xcb_connection(),
1328 window: m_window,
1329 property: atom(atom: QXcbAtom::Atom_NET_WM_ICON));
1330 }
1331}
1332
1333void QXcbWindow::raise()
1334{
1335 const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
1336 const quint32 values[] = { XCB_STACK_MODE_ABOVE };
1337 xcb_configure_window(c: xcb_connection(), window: m_window, value_mask: mask, value_list: values);
1338}
1339
1340void QXcbWindow::lower()
1341{
1342 const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
1343 const quint32 values[] = { XCB_STACK_MODE_BELOW };
1344 xcb_configure_window(c: xcb_connection(), window: m_window, value_mask: mask, value_list: values);
1345}
1346
1347void QXcbWindow::propagateSizeHints()
1348{
1349 // update WM_NORMAL_HINTS
1350 xcb_size_hints_t hints;
1351 memset(s: &hints, c: 0, n: sizeof(hints));
1352
1353 const QRect rect = geometry();
1354 QWindowPrivate *win = qt_window_private(window: window());
1355
1356 if (!win->positionAutomatic)
1357 xcb_icccm_size_hints_set_position(hints: &hints, user_specified: true, x: rect.x(), y: rect.y());
1358 if (rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX)
1359 xcb_icccm_size_hints_set_size(hints: &hints, user_specified: true, width: rect.width(), height: rect.height());
1360
1361 /* Gravity describes how to interpret x and y values the next time
1362 window needs to be positioned on a screen.
1363 XCB_GRAVITY_STATIC : the left top corner of the client window
1364 XCB_GRAVITY_NORTH_WEST : the left top corner of the frame window */
1365 auto gravity = win->positionPolicy == QWindowPrivate::WindowFrameInclusive
1366 ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
1367
1368 xcb_icccm_size_hints_set_win_gravity(hints: &hints, win_gravity: gravity);
1369
1370 QSize minimumSize = windowMinimumSize();
1371 QSize maximumSize = windowMaximumSize();
1372 QSize baseSize = windowBaseSize();
1373 QSize sizeIncrement = windowSizeIncrement();
1374
1375 if (minimumSize.width() > 0 || minimumSize.height() > 0)
1376 xcb_icccm_size_hints_set_min_size(hints: &hints,
1377 min_width: qMin(XCOORD_MAX,b: minimumSize.width()),
1378 min_height: qMin(XCOORD_MAX,b: minimumSize.height()));
1379
1380 if (maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX)
1381 xcb_icccm_size_hints_set_max_size(hints: &hints,
1382 max_width: qMin(XCOORD_MAX, b: maximumSize.width()),
1383 max_height: qMin(XCOORD_MAX, b: maximumSize.height()));
1384
1385 if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) {
1386 xcb_icccm_size_hints_set_base_size(hints: &hints, base_width: baseSize.width(), base_height: baseSize.height());
1387 xcb_icccm_size_hints_set_resize_inc(hints: &hints, width_inc: sizeIncrement.width(), height_inc: sizeIncrement.height());
1388 }
1389
1390 xcb_icccm_set_wm_normal_hints(c: xcb_connection(), window: m_window, hints: &hints);
1391
1392 m_sizeHintsScaleFactor = QHighDpiScaling::factor(context: screen());
1393}
1394
1395void QXcbWindow::requestActivateWindow()
1396{
1397 /* Never activate embedded windows; doing that would prevent the container
1398 * to re-gain the keyboard focus later. */
1399 if (m_embedded) {
1400 QPlatformWindow::requestActivateWindow();
1401 return;
1402 }
1403
1404 if (!m_mapped) {
1405 m_deferredActivation = true;
1406 return;
1407 }
1408 m_deferredActivation = false;
1409
1410 updateNetWmUserTime(timestamp: connection()->time());
1411 QWindow *focusWindow = QGuiApplication::focusWindow();
1412
1413 if (window()->isTopLevel()
1414 && !(window()->flags() & Qt::X11BypassWindowManagerHint)
1415 && (!focusWindow || !window()->isAncestorOf(child: focusWindow))
1416 && connection()->wmSupport()->isSupportedByWM(atom: atom(atom: QXcbAtom::Atom_NET_ACTIVE_WINDOW))) {
1417 xcb_client_message_event_t event;
1418
1419 event.response_type = XCB_CLIENT_MESSAGE;
1420 event.format = 32;
1421 event.sequence = 0;
1422 event.window = m_window;
1423 event.type = atom(atom: QXcbAtom::Atom_NET_ACTIVE_WINDOW);
1424 event.data.data32[0] = 1;
1425 event.data.data32[1] = connection()->time();
1426 event.data.data32[2] = focusWindow ? focusWindow->winId() : XCB_NONE;
1427 event.data.data32[3] = 0;
1428 event.data.data32[4] = 0;
1429
1430 xcb_send_event(c: xcb_connection(), propagate: 0, destination: xcbScreen()->root(),
1431 event_mask: XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1432 event: (const char *)&event);
1433 } else {
1434 xcb_set_input_focus(c: xcb_connection(), revert_to: XCB_INPUT_FOCUS_PARENT, focus: m_window, time: connection()->time());
1435 }
1436
1437 connection()->sync();
1438}
1439
1440QSurfaceFormat QXcbWindow::format() const
1441{
1442 return m_format;
1443}
1444
1445QXcbWindow::WindowTypes QXcbWindow::wmWindowTypes() const
1446{
1447 WindowTypes result;
1448
1449 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
1450 0, m_window, atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE),
1451 XCB_ATOM_ATOM, 0, 1024);
1452 if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
1453 const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(R: reply.get()));
1454 const xcb_atom_t *types_end = types + reply->length;
1455 for (; types != types_end; types++) {
1456 QXcbAtom::Atom type = connection()->qatom(atom: *types);
1457 switch (type) {
1458 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL:
1459 result |= WindowType::Normal;
1460 break;
1461 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DESKTOP:
1462 result |= WindowType::Desktop;
1463 break;
1464 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DOCK:
1465 result |= WindowType::Dock;
1466 break;
1467 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLBAR:
1468 result |= WindowType::Toolbar;
1469 break;
1470 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_MENU:
1471 result |= WindowType::Menu;
1472 break;
1473 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY:
1474 result |= WindowType::Utility;
1475 break;
1476 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH:
1477 result |= WindowType::Splash;
1478 break;
1479 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG:
1480 result |= WindowType::Dialog;
1481 break;
1482 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU:
1483 result |= WindowType::DropDownMenu;
1484 break;
1485 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_POPUP_MENU:
1486 result |= WindowType::PopupMenu;
1487 break;
1488 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP:
1489 result |= WindowType::Tooltip;
1490 break;
1491 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NOTIFICATION:
1492 result |= WindowType::Notification;
1493 break;
1494 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_COMBO:
1495 result |= WindowType::Combo;
1496 break;
1497 case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DND:
1498 result |= WindowType::Dnd;
1499 break;
1500 case QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE:
1501 result |= WindowType::KdeOverride;
1502 break;
1503 default:
1504 break;
1505 }
1506 }
1507 }
1508 return result;
1509}
1510
1511void QXcbWindow::setWmWindowType(WindowTypes types, Qt::WindowFlags flags)
1512{
1513 QList<xcb_atom_t> atoms;
1514
1515 // manual selection 1 (these are never set by Qt and take precedence)
1516 if (types & WindowType::Normal)
1517 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL));
1518 if (types & WindowType::Desktop)
1519 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DESKTOP));
1520 if (types & WindowType::Dock)
1521 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DOCK));
1522 if (types & WindowType::Notification)
1523 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NOTIFICATION));
1524
1525 // manual selection 2 (Qt uses these during auto selection);
1526 if (types & WindowType::Utility)
1527 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY));
1528 if (types & WindowType::Splash)
1529 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH));
1530 if (types & WindowType::Dialog)
1531 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG));
1532 if (types & WindowType::Tooltip)
1533 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP));
1534 if (types & WindowType::KdeOverride)
1535 atoms.append(t: atom(atom: QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
1536
1537 // manual selection 3 (these can be set by Qt, but don't have a
1538 // corresponding Qt::WindowType). note that order of the *MENU
1539 // atoms is important
1540 if (types & WindowType::Menu)
1541 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_MENU));
1542 if (types & WindowType::DropDownMenu)
1543 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU));
1544 if (types & WindowType::PopupMenu)
1545 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_POPUP_MENU));
1546 if (types & WindowType::Toolbar)
1547 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLBAR));
1548 if (types & WindowType::Combo)
1549 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_COMBO));
1550 if (types & WindowType::Dnd)
1551 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DND));
1552
1553 // automatic selection
1554 Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
1555 switch (type) {
1556 case Qt::Dialog:
1557 case Qt::Sheet:
1558 if (!(types & WindowType::Dialog))
1559 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG));
1560 break;
1561 case Qt::Tool:
1562 case Qt::Drawer:
1563 if (!(types & WindowType::Utility))
1564 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY));
1565 break;
1566 case Qt::ToolTip:
1567 if (!(types & WindowType::Tooltip))
1568 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP));
1569 break;
1570 case Qt::SplashScreen:
1571 if (!(types & WindowType::Splash))
1572 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH));
1573 break;
1574 default:
1575 break;
1576 }
1577
1578 if ((flags & Qt::FramelessWindowHint) && !(types & WindowType::KdeOverride)) {
1579 // override netwm type - quick and easy for KDE noborder
1580 atoms.append(t: atom(atom: QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
1581 }
1582
1583 if (atoms.size() == 1 && atoms.first() == atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL))
1584 atoms.clear();
1585 else
1586 atoms.append(t: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL));
1587
1588 if (atoms.isEmpty()) {
1589 xcb_delete_property(c: xcb_connection(), window: m_window, property: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE));
1590 } else {
1591 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
1592 property: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_TYPE), type: XCB_ATOM_ATOM, format: 32,
1593 data_len: atoms.size(), data: atoms.constData());
1594 }
1595 xcb_flush(c: xcb_connection());
1596}
1597
1598void QXcbWindow::setWindowRole(const QString &role)
1599{
1600 QByteArray roleData = role.toLatin1();
1601 xcb_change_property(c: xcb_connection(), mode: XCB_PROP_MODE_REPLACE, window: m_window,
1602 property: atom(atom: QXcbAtom::AtomWM_WINDOW_ROLE), type: XCB_ATOM_STRING, format: 8,
1603 data_len: roleData.size(), data: roleData.constData());
1604}
1605
1606void QXcbWindow::setParentRelativeBackPixmap()
1607{
1608 const quint32 mask = XCB_CW_BACK_PIXMAP;
1609 const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE };
1610 xcb_change_window_attributes(c: xcb_connection(), window: m_window, value_mask: mask, value_list: values);
1611}
1612
1613bool QXcbWindow::requestSystemTrayWindowDock()
1614{
1615 if (!connection()->systemTrayTracker())
1616 return false;
1617 connection()->systemTrayTracker()->requestSystemTrayWindowDock(window: m_window);
1618 return true;
1619}
1620
1621bool QXcbWindow::handleNativeEvent(xcb_generic_event_t *event)
1622{
1623 auto eventType = connection()->nativeInterface()->nativeEventType();
1624 qintptr result = 0; // Used only by MS Windows
1625 return QWindowSystemInterface::handleNativeEvent(window: window(), eventType, message: event, result: &result);
1626}
1627
1628void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
1629{
1630 QRect rect(event->x, event->y, event->width, event->height);
1631 m_exposeRegion |= rect;
1632
1633 bool pending = true;
1634
1635 connection()->eventQueue()->peek(option: QXcbEventQueue::PeekConsumeMatchAndContinue,
1636 peeker: [this, &pending](xcb_generic_event_t *event, int type) {
1637 if (type != XCB_EXPOSE)
1638 return false;
1639 auto expose = reinterpret_cast<xcb_expose_event_t *>(event);
1640 if (expose->window != m_window)
1641 return false;
1642 if (expose->count == 0)
1643 pending = false;
1644 m_exposeRegion |= QRect(expose->x, expose->y, expose->width, expose->height);
1645 free(ptr: expose);
1646 return true;
1647 });
1648
1649 // if count is non-zero there are more expose events pending
1650 if (event->count == 0 || !pending) {
1651 QWindowSystemInterface::handleExposeEvent(window: window(), region: m_exposeRegion);
1652 m_exposeRegion = QRegion();
1653 }
1654}
1655
1656void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *event)
1657{
1658 if (event->format != 32)
1659 return;
1660
1661 if (event->type == atom(atom: QXcbAtom::AtomWM_PROTOCOLS)) {
1662 xcb_atom_t protocolAtom = event->data.data32[0];
1663 if (protocolAtom == atom(atom: QXcbAtom::AtomWM_DELETE_WINDOW)) {
1664 QWindowSystemInterface::handleCloseEvent(window: window());
1665 } else if (protocolAtom == atom(atom: QXcbAtom::AtomWM_TAKE_FOCUS)) {
1666 connection()->setTime(event->data.data32[1]);
1667 relayFocusToModalWindow();
1668 return;
1669 } else if (protocolAtom == atom(atom: QXcbAtom::Atom_NET_WM_PING)) {
1670 if (event->window == xcbScreen()->root())
1671 return;
1672
1673 xcb_client_message_event_t reply = *event;
1674
1675 reply.response_type = XCB_CLIENT_MESSAGE;
1676 reply.window = xcbScreen()->root();
1677
1678 xcb_send_event(c: xcb_connection(), propagate: 0, destination: xcbScreen()->root(),
1679 event_mask: XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1680 event: (const char *)&reply);
1681 xcb_flush(c: xcb_connection());
1682 } else if (protocolAtom == atom(atom: QXcbAtom::Atom_NET_WM_SYNC_REQUEST)) {
1683 connection()->setTime(event->data.data32[1]);
1684 m_syncValue.lo = event->data.data32[2];
1685 m_syncValue.hi = event->data.data32[3];
1686 if (connection()->hasXSync())
1687 m_syncState = SyncReceived;
1688#ifndef QT_NO_WHATSTHIS
1689 } else if (protocolAtom == atom(atom: QXcbAtom::Atom_NET_WM_CONTEXT_HELP)) {
1690 QWindowSystemInterface::handleEnterWhatsThisEvent();
1691#endif
1692 } else {
1693 qCWarning(lcQpaXcb, "Unhandled WM_PROTOCOLS (%s)",
1694 connection()->atomName(protocolAtom).constData());
1695 }
1696#if QT_CONFIG(draganddrop)
1697 } else if (event->type == atom(atom: QXcbAtom::AtomXdndEnter)) {
1698 connection()->drag()->handleEnter(window: this, event);
1699 } else if (event->type == atom(atom: QXcbAtom::AtomXdndPosition)) {
1700 connection()->drag()->handlePosition(w: this, event);
1701 } else if (event->type == atom(atom: QXcbAtom::AtomXdndLeave)) {
1702 connection()->drag()->handleLeave(w: this, event);
1703 } else if (event->type == atom(atom: QXcbAtom::AtomXdndDrop)) {
1704 connection()->drag()->handleDrop(this, event);
1705#endif
1706 } else if (event->type == atom(atom: QXcbAtom::Atom_XEMBED)) {
1707 handleXEmbedMessage(event);
1708 } else if (event->type == atom(atom: QXcbAtom::Atom_NET_ACTIVE_WINDOW)) {
1709 doFocusIn();
1710 } else if (event->type == atom(atom: QXcbAtom::AtomMANAGER)
1711 || event->type == atom(atom: QXcbAtom::Atom_NET_WM_STATE)
1712 || event->type == atom(atom: QXcbAtom::AtomWM_CHANGE_STATE)) {
1713 // Ignore _NET_WM_STATE, MANAGER which are relate to tray icons
1714 // and other messages.
1715 } else if (event->type == atom(atom: QXcbAtom::Atom_COMPIZ_DECOR_PENDING)
1716 || event->type == atom(atom: QXcbAtom::Atom_COMPIZ_DECOR_REQUEST)
1717 || event->type == atom(atom: QXcbAtom::Atom_COMPIZ_DECOR_DELETE_PIXMAP)
1718 || event->type == atom(atom: QXcbAtom::Atom_COMPIZ_TOOLKIT_ACTION)
1719 || event->type == atom(atom: QXcbAtom::Atom_GTK_LOAD_ICONTHEMES)) {
1720 //silence the _COMPIZ and _GTK messages for now
1721 } else {
1722 qCWarning(lcQpaXcb) << "Unhandled client message: " << connection()->atomName(atom: event->type);
1723 }
1724}
1725
1726void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event)
1727{
1728 bool fromSendEvent = (event->response_type & 0x80);
1729 QPoint pos(event->x, event->y);
1730 if (!parent() && !fromSendEvent) {
1731 // Do not trust the position, query it instead.
1732 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
1733 xcb_window(), xcbScreen()->root(), 0, 0);
1734 if (reply) {
1735 pos.setX(reply->dst_x);
1736 pos.setY(reply->dst_y);
1737 }
1738 }
1739
1740 const QRect actualGeometry = QRect(pos, QSize(event->width, event->height));
1741 QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(newGeometry: actualGeometry);
1742 if (!newScreen)
1743 return;
1744
1745 QWindowSystemInterface::handleGeometryChange(window: window(), newRect: actualGeometry);
1746
1747 // QPlatformScreen::screen() is updated asynchronously, so we can't compare it
1748 // with the newScreen. Just send the WindowScreenChanged event and QGuiApplication
1749 // will make the comparison later.
1750 QWindowSystemInterface::handleWindowScreenChanged(window: window(), newScreen: newScreen->screen());
1751
1752 if (!qFuzzyCompare(p1: QHighDpiScaling::factor(context: newScreen), p2: m_sizeHintsScaleFactor))
1753 propagateSizeHints();
1754
1755 // Send the synthetic expose event on resize only when the window is shrunk,
1756 // because the "XCB_GRAVITY_NORTH_WEST" flag doesn't send it automatically.
1757 if (!m_oldWindowSize.isEmpty()
1758 && (actualGeometry.width() < m_oldWindowSize.width()
1759 || actualGeometry.height() < m_oldWindowSize.height())) {
1760 QWindowSystemInterface::handleExposeEvent(window: window(), region: QRegion(0, 0, actualGeometry.width(), actualGeometry.height()));
1761 }
1762 m_oldWindowSize = actualGeometry.size();
1763
1764 if (connection()->hasXSync() && m_syncState == SyncReceived)
1765 m_syncState = SyncAndConfigureReceived;
1766
1767 m_dirtyFrameMargins = true;
1768}
1769
1770bool QXcbWindow::isExposed() const
1771{
1772 return m_mapped;
1773}
1774
1775bool QXcbWindow::isEmbedded() const
1776{
1777 return m_embedded;
1778}
1779
1780QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const
1781{
1782 if (!m_embedded)
1783 return QPlatformWindow::mapToGlobal(pos);
1784
1785 QPoint ret;
1786 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
1787 xcb_window(), xcbScreen()->root(),
1788 pos.x(), pos.y());
1789 if (reply) {
1790 ret.setX(reply->dst_x);
1791 ret.setY(reply->dst_y);
1792 }
1793
1794 return ret;
1795}
1796
1797QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const
1798{
1799 if (!m_embedded)
1800 return QPlatformWindow::mapFromGlobal(pos);
1801
1802 QPoint ret;
1803 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
1804 xcbScreen()->root(), xcb_window(),
1805 pos.x(), pos.y());
1806 if (reply) {
1807 ret.setX(reply->dst_x);
1808 ret.setY(reply->dst_y);
1809 }
1810
1811 return ret;
1812}
1813
1814void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event)
1815{
1816 if (event->window == m_window) {
1817 m_mapped = true;
1818 if (m_deferredActivation)
1819 requestActivateWindow();
1820
1821 QWindowSystemInterface::handleExposeEvent(window: window(), region: QRect(QPoint(), geometry().size()));
1822 }
1823}
1824
1825void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
1826{
1827 if (event->window == m_window) {
1828 m_mapped = false;
1829 QWindowSystemInterface::handleExposeEvent(window: window(), region: QRegion());
1830 }
1831}
1832
1833void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
1834 int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
1835 QEvent::Type type, Qt::MouseEventSource source)
1836{
1837 const bool isWheel = detail >= 4 && detail <= 7;
1838 if (!isWheel && window() != QGuiApplication::focusWindow()) {
1839 QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(o: window()))->eventReceiver();
1840 if (!(w->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::BypassWindowManagerHint))
1841 && w->type() != Qt::ToolTip
1842 && w->type() != Qt::Popup) {
1843 w->requestActivate();
1844 }
1845 }
1846
1847 updateNetWmUserTime(timestamp);
1848
1849 if (m_embedded && !m_trayIconWindow) {
1850 if (window() != QGuiApplication::focusWindow()) {
1851 const QXcbWindow *container = static_cast<const QXcbWindow *>(parent());
1852 Q_ASSERT(container != nullptr);
1853
1854 sendXEmbedMessage(window: container->xcb_window(), message: XEMBED_REQUEST_FOCUS);
1855 }
1856 }
1857 QPoint local(event_x, event_y);
1858 QPoint global(root_x, root_y);
1859
1860 if (isWheel) {
1861 if (!connection()->isAtLeastXI21()) {
1862 QPoint angleDelta;
1863 if (detail == 4)
1864 angleDelta.setY(120);
1865 else if (detail == 5)
1866 angleDelta.setY(-120);
1867 else if (detail == 6)
1868 angleDelta.setX(120);
1869 else if (detail == 7)
1870 angleDelta.setX(-120);
1871 if (modifiers & Qt::AltModifier)
1872 angleDelta = angleDelta.transposed();
1873 QWindowSystemInterface::handleWheelEvent(window: window(), timestamp, local, global, pixelDelta: QPoint(), angleDelta, mods: modifiers);
1874 }
1875 return;
1876 }
1877
1878 connection()->setMousePressWindow(this);
1879
1880 handleMouseEvent(time: timestamp, local, global, modifiers, type, source);
1881}
1882
1883void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
1884 int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
1885 QEvent::Type type, Qt::MouseEventSource source)
1886{
1887 QPoint local(event_x, event_y);
1888 QPoint global(root_x, root_y);
1889
1890 if (detail >= 4 && detail <= 7) {
1891 // mouse wheel, handled in handleButtonPressEvent()
1892 return;
1893 }
1894
1895 if (connection()->buttonState() == Qt::NoButton)
1896 connection()->setMousePressWindow(nullptr);
1897
1898 handleMouseEvent(time: timestamp, local, global, modifiers, type, source);
1899}
1900
1901static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
1902{
1903 /* Checking for XCB_NOTIFY_MODE_GRAB and XCB_NOTIFY_DETAIL_ANCESTOR prevents unwanted
1904 * enter/leave events on AwesomeWM on mouse button press. It also ignores duplicated
1905 * enter/leave events on Alt+Tab switching on some WMs with XInput2 events.
1906 * Without XInput2 events the (Un)grabAncestor cannot be checked when mouse button is
1907 * not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events.
1908 */
1909 if (conn) {
1910 const bool mouseButtonsPressed = (conn->buttonState() != Qt::NoButton);
1911 return mouseButtonsPressed || conn->hasXInput2();
1912 }
1913 return true;
1914}
1915
1916static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr)
1917{
1918 return ((doCheckUnGrabAncestor(conn)
1919 && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
1920 || (mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_INFERIOR)
1921 || detail == XCB_NOTIFY_DETAIL_VIRTUAL
1922 || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
1923}
1924
1925static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr)
1926{
1927 return ((doCheckUnGrabAncestor(conn)
1928 && mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
1929 || (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB)
1930 || detail == XCB_NOTIFY_DETAIL_VIRTUAL
1931 || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
1932}
1933
1934void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
1935 quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
1936{
1937 connection()->setTime(timestamp);
1938
1939 const QPoint global = QPoint(root_x, root_y);
1940
1941 if (ignoreEnterEvent(mode, detail, conn: connection()) || connection()->mousePressWindow())
1942 return;
1943
1944 // Updates scroll valuators, as user might have done some scrolling outside our X client.
1945 connection()->xi2UpdateScrollingDevices();
1946
1947 const QPoint local(event_x, event_y);
1948 QWindowSystemInterface::handleEnterEvent(window: window(), local, global);
1949}
1950
1951void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
1952 quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
1953{
1954 connection()->setTime(timestamp);
1955
1956 if (ignoreLeaveEvent(mode, detail, conn: connection()) || connection()->mousePressWindow())
1957 return;
1958
1959 // check if enter event is buffered
1960 auto event = connection()->eventQueue()->peek(peeker: [](xcb_generic_event_t *event, int type) {
1961 if (type != XCB_ENTER_NOTIFY)
1962 return false;
1963 auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
1964 return !ignoreEnterEvent(mode: enter->mode, detail: enter->detail);
1965 });
1966 auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
1967 QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(id: enter->event) : nullptr;
1968
1969 if (enterWindow) {
1970 QPoint local(enter->event_x, enter->event_y);
1971 QPoint global = QPoint(root_x, root_y);
1972 QWindowSystemInterface::handleEnterLeaveEvent(enter: enterWindow->window(), leave: window(), local, global);
1973 } else {
1974 QWindowSystemInterface::handleLeaveEvent(window: window());
1975 }
1976
1977 free(ptr: enter);
1978}
1979
1980void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
1981 Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
1982 QEvent::Type type, Qt::MouseEventSource source)
1983{
1984 QPoint local(event_x, event_y);
1985 QPoint global(root_x, root_y);
1986
1987 // "mousePressWindow" can be NULL i.e. if a window will be grabbed or unmapped, so set it again here.
1988 // Unset "mousePressWindow" when mouse button isn't pressed - in some cases the release event won't arrive.
1989 const bool isMouseButtonPressed = (connection()->buttonState() != Qt::NoButton);
1990 const bool hasMousePressWindow = (connection()->mousePressWindow() != nullptr);
1991 if (isMouseButtonPressed && !hasMousePressWindow)
1992 connection()->setMousePressWindow(this);
1993 else if (hasMousePressWindow && !isMouseButtonPressed)
1994 connection()->setMousePressWindow(nullptr);
1995
1996 handleMouseEvent(time: timestamp, local, global, modifiers, type, source);
1997}
1998
1999void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
2000{
2001 Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(s: event->state);
2002 handleButtonPressEvent(event_x: event->event_x, event_y: event->event_y, root_x: event->root_x, root_y: event->root_y, detail: event->detail,
2003 modifiers, timestamp: event->time, type: QEvent::MouseButtonPress);
2004}
2005
2006void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
2007{
2008 Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(s: event->state);
2009 handleButtonReleaseEvent(event_x: event->event_x, event_y: event->event_y, root_x: event->root_x, root_y: event->root_y, detail: event->detail,
2010 modifiers, timestamp: event->time, type: QEvent::MouseButtonRelease);
2011}
2012
2013void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
2014{
2015 Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(s: event->state);
2016 handleMotionNotifyEvent(event_x: event->event_x, event_y: event->event_y, root_x: event->root_x, root_y: event->root_y, modifiers,
2017 timestamp: event->time, type: QEvent::MouseMove);
2018}
2019
2020static inline int fixed1616ToInt(xcb_input_fp1616_t val)
2021{
2022 return int(qreal(val) / 0x10000);
2023}
2024
2025#define qt_xcb_mask_is_set(ptr, event) (((unsigned char*)(ptr))[(event)>>3] & (1 << ((event) & 7)))
2026
2027void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource source)
2028{
2029 QXcbConnection *conn = connection();
2030 auto *ev = reinterpret_cast<xcb_input_button_press_event_t *>(event);
2031
2032 if (ev->buttons_len > 0) {
2033 unsigned char *buttonMask = (unsigned char *) &ev[1];
2034 // There is a bug in the evdev driver which leads to receiving mouse events without
2035 // XIPointerEmulated being set: https://bugs.freedesktop.org/show_bug.cgi?id=98188
2036 // Filter them out by other attributes: when their source device is a touch screen
2037 // and the LMB is pressed.
2038 if (qt_xcb_mask_is_set(buttonMask, 1) && conn->isTouchScreen(id: ev->sourceid)) {
2039 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2040 qCDebug(lcQpaXInput, "XI2 mouse event from touch device %d was ignored", ev->sourceid);
2041 return;
2042 }
2043 for (int i = 1; i <= 15; ++i)
2044 conn->setButtonState(button: conn->translateMouseButton(s: i), qt_xcb_mask_is_set(buttonMask, i));
2045 }
2046
2047 const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(s: ev->mods.effective);
2048 const int event_x = fixed1616ToInt(val: ev->event_x);
2049 const int event_y = fixed1616ToInt(val: ev->event_y);
2050 const int root_x = fixed1616ToInt(val: ev->root_x);
2051 const int root_y = fixed1616ToInt(val: ev->root_y);
2052
2053 conn->keyboard()->updateXKBStateFromXI(modInfo: &ev->mods, groupInfo: &ev->group);
2054
2055 const Qt::MouseButton button = conn->xiToQtMouseButton(b: ev->detail);
2056
2057 const char *sourceName = nullptr;
2058 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
2059 const QMetaObject *metaObject = qt_getEnumMetaObject(source);
2060 const QMetaEnum me = metaObject->enumerator(index: metaObject->indexOfEnumerator(name: qt_getEnumName(source)));
2061 sourceName = me.valueToKey(value: source);
2062 }
2063
2064 switch (ev->event_type) {
2065 case XCB_INPUT_BUTTON_PRESS:
2066 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2067 qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName);
2068 conn->setButtonState(button, down: true);
2069 handleButtonPressEvent(event_x, event_y, root_x, root_y, detail: ev->detail, modifiers, timestamp: ev->time, type: QEvent::MouseButtonPress, source);
2070 break;
2071 case XCB_INPUT_BUTTON_RELEASE:
2072 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2073 qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName);
2074 conn->setButtonState(button, down: false);
2075 handleButtonReleaseEvent(event_x, event_y, root_x, root_y, detail: ev->detail, modifiers, timestamp: ev->time, type: QEvent::MouseButtonRelease, source);
2076 break;
2077 case XCB_INPUT_MOTION:
2078 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2079 qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName);
2080 handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, timestamp: ev->time, type: QEvent::MouseMove, source);
2081 break;
2082 default:
2083 qWarning() << "Unrecognized XI2 mouse event" << ev->event_type;
2084 break;
2085 }
2086}
2087
2088void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
2089{
2090 auto *ev = reinterpret_cast<xcb_input_enter_event_t *>(event);
2091
2092 // Compare the window with current mouse grabber to prevent deliver events to any other windows.
2093 // If leave event occurs and the window is under mouse - allow to deliver the leave event.
2094 QXcbWindow *mouseGrabber = connection()->mouseGrabber();
2095 if (mouseGrabber && mouseGrabber != this
2096 && (ev->event_type != XCB_INPUT_LEAVE || QGuiApplicationPrivate::currentMouseWindow != window())) {
2097 return;
2098 }
2099
2100 const int root_x = fixed1616ToInt(val: ev->root_x);
2101 const int root_y = fixed1616ToInt(val: ev->root_y);
2102
2103 switch (ev->event_type) {
2104 case XCB_INPUT_ENTER: {
2105 const int event_x = fixed1616ToInt(val: ev->event_x);
2106 const int event_y = fixed1616ToInt(val: ev->event_y);
2107 qCDebug(lcQpaXInputEvents, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d",
2108 event_x, event_y, ev->mode, ev->detail, ev->time);
2109 handleEnterNotifyEvent(event_x, event_y, root_x, root_y, mode: ev->mode, detail: ev->detail, timestamp: ev->time);
2110 break;
2111 }
2112 case XCB_INPUT_LEAVE:
2113 qCDebug(lcQpaXInputEvents, "XI2 mouse leave, mode %d, detail %d, time %d",
2114 ev->mode, ev->detail, ev->time);
2115 connection()->keyboard()->updateXKBStateFromXI(modInfo: &ev->mods, groupInfo: &ev->group);
2116 handleLeaveNotifyEvent(root_x, root_y, mode: ev->mode, detail: ev->detail, timestamp: ev->time);
2117 break;
2118 }
2119}
2120
2121QXcbWindow *QXcbWindow::toWindow() { return this; }
2122
2123void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
2124 Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source)
2125{
2126 m_lastPointerPosition = local;
2127 m_lastPointerGlobalPosition = global;
2128 connection()->setTime(time);
2129 Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button();
2130 QWindowSystemInterface::handleMouseEvent(window: window(), timestamp: time, local, global,
2131 state: connection()->buttonState(), button,
2132 type, mods: modifiers, source);
2133}
2134
2135void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
2136{
2137 handleEnterNotifyEvent(event_x: event->event_x, event_y: event->event_y, root_x: event->root_x, root_y: event->root_y, mode: event->mode, detail: event->detail, timestamp: event->time);
2138}
2139
2140void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
2141{
2142 handleLeaveNotifyEvent(root_x: event->root_x, root_y: event->root_y, mode: event->mode, detail: event->detail, timestamp: event->time);
2143}
2144
2145void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
2146{
2147 connection()->setTime(event->time);
2148
2149 const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
2150
2151 if (event->atom == atom(atom: QXcbAtom::Atom_NET_WM_STATE) || event->atom == atom(atom: QXcbAtom::AtomWM_STATE)) {
2152 if (propertyDeleted)
2153 return;
2154
2155 Qt::WindowStates newState = Qt::WindowNoState;
2156
2157 if (event->atom == atom(atom: QXcbAtom::AtomWM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
2158 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
2159 0, m_window, atom(QXcbAtom::AtomWM_STATE),
2160 XCB_ATOM_ANY, 0, 1024);
2161 if (reply && reply->format == 32 && reply->type == atom(atom: QXcbAtom::AtomWM_STATE)) {
2162 const quint32 *data = (const quint32 *)xcb_get_property_value(R: reply.get());
2163 if (reply->length != 0)
2164 m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC
2165 || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized));
2166 }
2167 }
2168
2169 const NetWmStates states = netWmStates();
2170 // _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would
2171 // not be visible on the screen if its desktop/viewport were active and its coordinates were
2172 // within the screen bounds. The canonical example is that minimized windows should be in
2173 // the _NET_WM_STATE_HIDDEN state.
2174 if (m_minimized && (!connection()->wmSupport()->isSupportedByWM(atom: NetWmStateHidden)
2175 || states.testFlag(flag: NetWmStateHidden)))
2176 newState = Qt::WindowMinimized;
2177
2178 if (states & NetWmStateFullScreen)
2179 newState |= Qt::WindowFullScreen;
2180 if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
2181 newState |= Qt::WindowMaximized;
2182 // Send Window state, compress events in case other flags (modality, etc) are changed.
2183 if (m_lastWindowStateEvent != newState) {
2184 QWindowSystemInterface::handleWindowStateChanged(window: window(), newState);
2185 m_lastWindowStateEvent = newState;
2186 m_windowState = newState;
2187 if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this)
2188 connection()->setMouseGrabber(nullptr);
2189 }
2190 return;
2191 } else if (event->atom == atom(atom: QXcbAtom::Atom_NET_FRAME_EXTENTS)) {
2192 m_dirtyFrameMargins = true;
2193 }
2194}
2195
2196void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *event)
2197{
2198 // Ignore focus events that are being sent only because the pointer is over
2199 // our window, even if the input focus is in a different window.
2200 if (event->detail == XCB_NOTIFY_DETAIL_POINTER)
2201 return;
2202
2203 connection()->focusInTimer().stop();
2204 doFocusIn();
2205}
2206
2207
2208void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *event)
2209{
2210 // Ignore focus events that are being sent only because the pointer is over
2211 // our window, even if the input focus is in a different window.
2212 if (event->detail == XCB_NOTIFY_DETAIL_POINTER)
2213 return;
2214 doFocusOut();
2215}
2216
2217void QXcbWindow::updateSyncRequestCounter()
2218{
2219 if (m_syncState != SyncAndConfigureReceived) {
2220 // window manager does not expect a sync event yet.
2221 return;
2222 }
2223 if (connection()->hasXSync() && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
2224 xcb_sync_set_counter(c: xcb_connection(), counter: m_syncCounter, value: m_syncValue);
2225 xcb_flush(c: xcb_connection());
2226
2227 m_syncValue.lo = 0;
2228 m_syncValue.hi = 0;
2229 m_syncState = NoSyncNeeded;
2230 }
2231}
2232
2233const xcb_visualtype_t *QXcbWindow::createVisual()
2234{
2235 return xcbScreen() ? xcbScreen()->visualForFormat(format: m_format)
2236 : nullptr;
2237}
2238
2239bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
2240{
2241 if (grab && !connection()->canGrab())
2242 return false;
2243
2244 if (!grab) {
2245 xcb_ungrab_keyboard(c: xcb_connection(), time: XCB_TIME_CURRENT_TIME);
2246 return true;
2247 }
2248
2249 auto reply = Q_XCB_REPLY(xcb_grab_keyboard, xcb_connection(), false,
2250 m_window, XCB_TIME_CURRENT_TIME,
2251 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
2252 return reply && reply->status == XCB_GRAB_STATUS_SUCCESS;
2253}
2254
2255bool QXcbWindow::setMouseGrabEnabled(bool grab)
2256{
2257 if (!grab && connection()->mouseGrabber() == this)
2258 connection()->setMouseGrabber(nullptr);
2259
2260 if (grab && !connection()->canGrab())
2261 return false;
2262
2263 if (connection()->hasXInput2()) {
2264 bool result = connection()->xi2SetMouseGrabEnabled(w: m_window, grab);
2265 if (grab && result)
2266 connection()->setMouseGrabber(this);
2267 return result;
2268 }
2269
2270 if (!grab) {
2271 xcb_ungrab_pointer(c: xcb_connection(), time: XCB_TIME_CURRENT_TIME);
2272 return true;
2273 }
2274
2275 auto reply = Q_XCB_REPLY(xcb_grab_pointer, xcb_connection(),
2276 false, m_window,
2277 (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
2278 | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW
2279 | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
2280 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
2281 XCB_WINDOW_NONE, XCB_CURSOR_NONE,
2282 XCB_TIME_CURRENT_TIME);
2283 bool result = reply && reply->status == XCB_GRAB_STATUS_SUCCESS;
2284 if (result)
2285 connection()->setMouseGrabber(this);
2286 return result;
2287}
2288
2289bool QXcbWindow::windowEvent(QEvent *event)
2290{
2291 switch (event->type()) {
2292 case QEvent::FocusIn:
2293 if (m_embedded && !m_trayIconWindow && !event->spontaneous()) {
2294 QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
2295 switch (focusEvent->reason()) {
2296 case Qt::TabFocusReason:
2297 case Qt::BacktabFocusReason:
2298 {
2299 const QXcbWindow *container =
2300 static_cast<const QXcbWindow *>(parent());
2301 sendXEmbedMessage(window: container->xcb_window(),
2302 message: focusEvent->reason() == Qt::TabFocusReason ?
2303 XEMBED_FOCUS_NEXT : XEMBED_FOCUS_PREV);
2304 event->accept();
2305 }
2306 break;
2307 default:
2308 break;
2309 }
2310 }
2311 break;
2312 default:
2313 break;
2314 }
2315 return QPlatformWindow::windowEvent(event);
2316}
2317
2318bool QXcbWindow::startSystemResize(Qt::Edges edges)
2319{
2320 return startSystemMoveResize(pos: m_lastPointerPosition, edges);
2321}
2322
2323bool QXcbWindow::startSystemMove()
2324{
2325 return startSystemMoveResize(pos: m_lastPointerPosition, edges: 16);
2326}
2327
2328bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int edges)
2329{
2330 const xcb_atom_t moveResize = connection()->atom(qatom: QXcbAtom::Atom_NET_WM_MOVERESIZE);
2331 if (!connection()->wmSupport()->isSupportedByWM(atom: moveResize))
2332 return false;
2333
2334 // ### FIXME QTBUG-53389
2335 bool startedByTouch = connection()->startSystemMoveResizeForTouch(window: m_window, edges);
2336 if (startedByTouch) {
2337 const QString wmname = connection()->windowManagerName();
2338 if (wmname != "kwin"_L1 && wmname != "openbox"_L1) {
2339 qCDebug(lcQpaXInputDevices) << "only KDE and OpenBox support startSystemMove/Resize which is triggered from touch events: XDG_CURRENT_DESKTOP="
2340 << qgetenv(varName: "XDG_CURRENT_DESKTOP");
2341 connection()->abortSystemMoveResize(window: m_window);
2342 return false;
2343 }
2344 // KWin, Openbox, AwesomeWM and Gnome have been tested to work with _NET_WM_MOVERESIZE.
2345 } else { // Started by mouse press.
2346 doStartSystemMoveResize(globalPos: mapToGlobal(pos), edges);
2347 }
2348
2349 return true;
2350}
2351
2352static uint qtEdgesToXcbMoveResizeDirection(Qt::Edges edges)
2353{
2354 if (edges == (Qt::TopEdge | Qt::LeftEdge))
2355 return 0;
2356 if (edges == Qt::TopEdge)
2357 return 1;
2358 if (edges == (Qt::TopEdge | Qt::RightEdge))
2359 return 2;
2360 if (edges == Qt::RightEdge)
2361 return 3;
2362 if (edges == (Qt::RightEdge | Qt::BottomEdge))
2363 return 4;
2364 if (edges == Qt::BottomEdge)
2365 return 5;
2366 if (edges == (Qt::BottomEdge | Qt::LeftEdge))
2367 return 6;
2368 if (edges == Qt::LeftEdge)
2369 return 7;
2370
2371 qWarning() << "Cannot convert " << edges << "to _NET_WM_MOVERESIZE direction.";
2372 return 0;
2373}
2374
2375void QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int edges)
2376{
2377 qCDebug(lcQpaXInputDevices) << "triggered system move or resize via sending _NET_WM_MOVERESIZE client message";
2378 const xcb_atom_t moveResize = connection()->atom(qatom: QXcbAtom::Atom_NET_WM_MOVERESIZE);
2379 xcb_client_message_event_t xev;
2380 xev.response_type = XCB_CLIENT_MESSAGE;
2381 xev.type = moveResize;
2382 xev.sequence = 0;
2383 xev.window = xcb_window();
2384 xev.format = 32;
2385 xev.data.data32[0] = globalPos.x();
2386 xev.data.data32[1] = globalPos.y();
2387 if (edges == 16)
2388 xev.data.data32[2] = 8; // move
2389 else
2390 xev.data.data32[2] = qtEdgesToXcbMoveResizeDirection(edges: Qt::Edges(edges));
2391 xev.data.data32[3] = XCB_BUTTON_INDEX_1;
2392 xev.data.data32[4] = 0;
2393 xcb_ungrab_pointer(c: connection()->xcb_connection(), XCB_CURRENT_TIME);
2394 xcb_send_event(c: connection()->xcb_connection(), propagate: false, destination: xcbScreen()->root(),
2395 event_mask: XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
2396 event: (const char *)&xev);
2397
2398 connection()->setDuringSystemMoveResize(true);
2399}
2400
2401// Sends an XEmbed message.
2402void QXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message,
2403 quint32 detail, quint32 data1, quint32 data2)
2404{
2405 xcb_client_message_event_t event;
2406
2407 event.response_type = XCB_CLIENT_MESSAGE;
2408 event.format = 32;
2409 event.sequence = 0;
2410 event.window = window;
2411 event.type = atom(atom: QXcbAtom::Atom_XEMBED);
2412 event.data.data32[0] = connection()->time();
2413 event.data.data32[1] = message;
2414 event.data.data32[2] = detail;
2415 event.data.data32[3] = data1;
2416 event.data.data32[4] = data2;
2417 xcb_send_event(c: xcb_connection(), propagate: false, destination: window, event_mask: XCB_EVENT_MASK_NO_EVENT, event: (const char *)&event);
2418}
2419
2420static bool activeWindowChangeQueued(const QWindow *window)
2421{
2422 /* Check from window system event queue if the next queued activation
2423 * targets a window other than @window.
2424 */
2425 QWindowSystemInterfacePrivate::ActivatedWindowEvent *systemEvent =
2426 static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>
2427 (QWindowSystemInterfacePrivate::peekWindowSystemEvent(t: QWindowSystemInterfacePrivate::ActivatedWindow));
2428 return systemEvent && systemEvent->activated != window;
2429}
2430
2431void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
2432{
2433 connection()->setTime(event->data.data32[0]);
2434 switch (event->data.data32[1]) {
2435 case XEMBED_WINDOW_ACTIVATE:
2436 case XEMBED_WINDOW_DEACTIVATE:
2437 break;
2438 case XEMBED_EMBEDDED_NOTIFY:
2439 xcb_map_window(c: xcb_connection(), window: m_window);
2440 xcbScreen()->windowShown(window: this);
2441 break;
2442 case XEMBED_FOCUS_IN:
2443 connection()->focusInTimer().stop();
2444 Qt::FocusReason reason;
2445 switch (event->data.data32[2]) {
2446 case XEMBED_FOCUS_FIRST:
2447 reason = Qt::TabFocusReason;
2448 break;
2449 case XEMBED_FOCUS_LAST:
2450 reason = Qt::BacktabFocusReason;
2451 break;
2452 case XEMBED_FOCUS_CURRENT:
2453 default:
2454 reason = Qt::OtherFocusReason;
2455 break;
2456 }
2457 connection()->setFocusWindow(window());
2458 QWindowSystemInterface::handleWindowActivated(window: window(), r: reason);
2459 break;
2460 case XEMBED_FOCUS_OUT:
2461 if (window() == QGuiApplication::focusWindow()
2462 && !activeWindowChangeQueued(window: window())) {
2463 connection()->setFocusWindow(nullptr);
2464 QWindowSystemInterface::handleWindowActivated(window: nullptr);
2465 }
2466 break;
2467 }
2468}
2469
2470static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r)
2471{
2472 xcb_rectangle_t result;
2473 result.x = qMax(SHRT_MIN, b: r.x());
2474 result.y = qMax(SHRT_MIN, b: r.y());
2475 result.width = qMin(a: (int)USHRT_MAX, b: r.width());
2476 result.height = qMin(a: (int)USHRT_MAX, b: r.height());
2477 return result;
2478}
2479
2480void QXcbWindow::setOpacity(qreal level)
2481{
2482 if (!m_window)
2483 return;
2484
2485 quint32 value = qRound64(d: qBound(min: qreal(0), val: level, max: qreal(1)) * 0xffffffff);
2486
2487 xcb_change_property(c: xcb_connection(),
2488 mode: XCB_PROP_MODE_REPLACE,
2489 window: m_window,
2490 property: atom(atom: QXcbAtom::Atom_NET_WM_WINDOW_OPACITY),
2491 type: XCB_ATOM_CARDINAL,
2492 format: 32,
2493 data_len: 1,
2494 data: (uchar *)&value);
2495}
2496
2497QList<xcb_rectangle_t> qRegionToXcbRectangleList(const QRegion &region)
2498{
2499 QList<xcb_rectangle_t> rects;
2500 rects.reserve(asize: region.rectCount());
2501 for (const QRect &r : region)
2502 rects.push_back(t: qRectToXCBRectangle(r));
2503 return rects;
2504}
2505
2506void QXcbWindow::setMask(const QRegion &region)
2507{
2508 if (!connection()->hasXShape())
2509 return;
2510 if (region.isEmpty()) {
2511 xcb_shape_mask(c: connection()->xcb_connection(), operation: XCB_SHAPE_SO_SET,
2512 destination_kind: XCB_SHAPE_SK_BOUNDING, destination_window: xcb_window(), x_offset: 0, y_offset: 0, XCB_NONE);
2513 } else {
2514 const auto rects = qRegionToXcbRectangleList(region);
2515 xcb_shape_rectangles(c: connection()->xcb_connection(), operation: XCB_SHAPE_SO_SET,
2516 destination_kind: XCB_SHAPE_SK_BOUNDING, ordering: XCB_CLIP_ORDERING_UNSORTED,
2517 destination_window: xcb_window(), x_offset: 0, y_offset: 0, rectangles_len: rects.size(), rectangles: &rects[0]);
2518 }
2519}
2520
2521void QXcbWindow::setAlertState(bool enabled)
2522{
2523 if (m_alertState == enabled)
2524 return;
2525
2526 m_alertState = enabled;
2527
2528 setNetWmState(set: enabled, one: atom(atom: QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION));
2529}
2530
2531uint QXcbWindow::visualId() const
2532{
2533 return m_visualId;
2534}
2535
2536bool QXcbWindow::needsSync() const
2537{
2538 return m_syncState == SyncAndConfigureReceived;
2539}
2540
2541void QXcbWindow::postSyncWindowRequest()
2542{
2543 if (!m_pendingSyncRequest) {
2544 QXcbSyncWindowRequest *e = new QXcbSyncWindowRequest(this);
2545 m_pendingSyncRequest = e;
2546 QCoreApplication::postEvent(receiver: xcbScreen()->connection(), event: e);
2547 }
2548}
2549
2550QXcbScreen *QXcbWindow::xcbScreen() const
2551{
2552 return static_cast<QXcbScreen *>(screen());
2553}
2554
2555void QXcbWindow::setWindowTitle(const QXcbConnection *conn, xcb_window_t window, const QString &title)
2556{
2557 QString fullTitle = formatWindowTitle(title, separator: QString::fromUtf8(utf8: " \xe2\x80\x94 ")); // unicode character U+2014, EM DASH
2558 const QByteArray ba = std::move(fullTitle).toUtf8();
2559 xcb_change_property(c: conn->xcb_connection(),
2560 mode: XCB_PROP_MODE_REPLACE,
2561 window,
2562 property: conn->atom(qatom: QXcbAtom::Atom_NET_WM_NAME),
2563 type: conn->atom(qatom: QXcbAtom::AtomUTF8_STRING),
2564 format: 8,
2565 data_len: ba.size(),
2566 data: ba.constData());
2567
2568#if QT_CONFIG(xcb_xlib)
2569 Display *dpy = static_cast<Display *>(conn->xlib_display());
2570 XTextProperty *text = qstringToXTP(dpy, s: title);
2571 if (text)
2572 XSetWMName(dpy, window, text);
2573#endif
2574 xcb_flush(c: conn->xcb_connection());
2575}
2576
2577QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window)
2578{
2579 const xcb_atom_t utf8Atom = conn->atom(qatom: QXcbAtom::AtomUTF8_STRING);
2580 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),
2581 false, window, conn->atom(QXcbAtom::Atom_NET_WM_NAME),
2582 utf8Atom, 0, 1024);
2583 if (reply && reply->format == 8 && reply->type == utf8Atom) {
2584 const char *name = reinterpret_cast<const char *>(xcb_get_property_value(R: reply.get()));
2585 return QString::fromUtf8(utf8: name, size: xcb_get_property_value_length(R: reply.get()));
2586 }
2587
2588 reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),
2589 false, window, conn->atom(QXcbAtom::AtomWM_NAME),
2590 XCB_ATOM_STRING, 0, 1024);
2591 if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
2592 const char *name = reinterpret_cast<const char *>(xcb_get_property_value(R: reply.get()));
2593 return QString::fromLatin1(str: name, size: xcb_get_property_value_length(R: reply.get()));
2594 }
2595
2596 return QString();
2597}
2598
2599QT_END_NAMESPACE
2600
2601

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