1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qxcbwindow.h"
41
42#include <QtDebug>
43#include <QMetaEnum>
44#include <QScreen>
45#include <QtGui/QIcon>
46#include <QtGui/QRegion>
47#include <QtGui/private/qhighdpiscaling_p.h>
48
49#include "qxcbintegration.h"
50#include "qxcbconnection.h"
51#include "qxcbscreen.h"
52#if QT_CONFIG(draganddrop)
53#include "qxcbdrag.h"
54#endif
55#include "qxcbkeyboard.h"
56#include "qxcbimage.h"
57#include "qxcbwmsupport.h"
58#include "qxcbimage.h"
59#include "qxcbnativeinterface.h"
60#include "qxcbsystemtraytracker.h"
61
62#include <qpa/qplatformintegration.h>
63#include <qpa/qplatformcursor.h>
64
65#include <algorithm>
66
67#include <xcb/xcb_icccm.h>
68#include <xcb/xfixes.h>
69#include <xcb/shape.h>
70#if QT_CONFIG(xcb_xinput)
71#include <xcb/xinput.h>
72#endif
73
74#include <private/qguiapplication_p.h>
75#include <private/qwindow_p.h>
76
77#include <qpa/qplatformbackingstore.h>
78#include <qpa/qwindowsysteminterface.h>
79
80#include <QTextCodec>
81#include <stdio.h>
82
83#if QT_CONFIG(xcb_xlib)
84#define register /* C++17 deprecated register */
85#include <X11/Xlib.h>
86#include <X11/Xutil.h>
87#undef register
88#endif
89
90#define XCOORD_MAX 16383
91enum {
92 defaultWindowWidth = 160,
93 defaultWindowHeight = 160
94};
95
96QT_BEGIN_NAMESPACE
97
98Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);
99
100#undef FocusIn
101
102enum QX11EmbedFocusInDetail {
103 XEMBED_FOCUS_CURRENT = 0,
104 XEMBED_FOCUS_FIRST = 1,
105 XEMBED_FOCUS_LAST = 2
106};
107
108enum QX11EmbedInfoFlags {
109 XEMBED_MAPPED = (1 << 0),
110};
111
112enum QX11EmbedMessageType {
113 XEMBED_EMBEDDED_NOTIFY = 0,
114 XEMBED_WINDOW_ACTIVATE = 1,
115 XEMBED_WINDOW_DEACTIVATE = 2,
116 XEMBED_REQUEST_FOCUS = 3,
117 XEMBED_FOCUS_IN = 4,
118 XEMBED_FOCUS_OUT = 5,
119 XEMBED_FOCUS_NEXT = 6,
120 XEMBED_FOCUS_PREV = 7,
121 XEMBED_MODALITY_ON = 10,
122 XEMBED_MODALITY_OFF = 11,
123 XEMBED_REGISTER_ACCELERATOR = 12,
124 XEMBED_UNREGISTER_ACCELERATOR = 13,
125 XEMBED_ACTIVATE_ACCELERATOR = 14
126};
127
128const quint32 XEMBED_VERSION = 0;
129
130QXcbScreen *QXcbWindow::parentScreen()
131{
132 return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : xcbScreen();
133}
134
135//QPlatformWindow::screenForGeometry version that uses deviceIndependentGeometry
136QXcbScreen *QXcbWindow::initialScreen() const
137{
138 QWindowPrivate *windowPrivate = qt_window_private(window());
139 QScreen *screen = windowPrivate->screenForGeometry(window()->geometry());
140 return static_cast<QXcbScreen*>(screen->handle());
141}
142
143// Returns \c true if we should set WM_TRANSIENT_FOR on \a w
144static inline bool isTransient(const QWindow *w)
145{
146 return w->type() == Qt::Dialog
147 || w->type() == Qt::Sheet
148 || w->type() == Qt::Tool
149 || w->type() == Qt::SplashScreen
150 || w->type() == Qt::ToolTip
151 || w->type() == Qt::Drawer
152 || w->type() == Qt::Popup;
153}
154
155void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual)
156{
157 if (qt_xcb_imageFormatForVisual(connection(), m_depth, visual, &m_imageFormat, &m_imageRgbSwap))
158 return;
159
160 switch (m_depth) {
161 case 32:
162 case 24:
163 qWarning("Using RGB32 fallback, if this works your X11 server is reporting a bad screen format.");
164 m_imageFormat = QImage::Format_RGB32;
165 break;
166 case 16:
167 qWarning("Using RGB16 fallback, if this works your X11 server is reporting a bad screen format.");
168 m_imageFormat = QImage::Format_RGB16;
169 default:
170 break;
171 }
172}
173
174#if QT_CONFIG(xcb_xlib)
175static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s)
176{
177 #include <X11/Xatom.h>
178
179 static XTextProperty tp = { 0, 0, 0, 0 };
180 static bool free_prop = true; // we can't free tp.value in case it references
181 // the data of the static QByteArray below.
182 if (tp.value) {
183 if (free_prop)
184 XFree(tp.value);
185 tp.value = 0;
186 free_prop = true;
187 }
188
189#if QT_CONFIG(textcodec)
190 static const QTextCodec* mapper = QTextCodec::codecForLocale();
191 int errCode = 0;
192 if (mapper) {
193 QByteArray mapped = mapper->fromUnicode(s);
194 char* tl[2];
195 tl[0] = mapped.data();
196 tl[1] = 0;
197 errCode = XmbTextListToTextProperty(dpy, tl, 1, XStdICCTextStyle, &tp);
198 if (errCode < 0)
199 qCDebug(lcQpaXcb, "XmbTextListToTextProperty result code %d", errCode);
200 }
201 if (!mapper || errCode < 0) {
202 mapper = QTextCodec::codecForName("latin1");
203 if (!mapper || !mapper->canEncode(s))
204 return nullptr;
205#endif
206 static QByteArray qcs;
207 qcs = s.toLatin1();
208 tp.value = (uchar*)qcs.data();
209 tp.encoding = XA_STRING;
210 tp.format = 8;
211 tp.nitems = qcs.length();
212 free_prop = false;
213#if QT_CONFIG(textcodec)
214 }
215#else
216 Q_UNUSED(dpy);
217#endif
218 return &tp;
219}
220#endif // QT_CONFIG(xcb_xlib)
221
222// TODO move this into a utility function in QWindow or QGuiApplication
223static QWindow *childWindowAt(QWindow *win, const QPoint &p)
224{
225 for (QObject *obj : win->children()) {
226 if (obj->isWindowType()) {
227 QWindow *childWin = static_cast<QWindow *>(obj);
228 if (childWin->isVisible()) {
229 if (QWindow *recurse = childWindowAt(childWin, p))
230 return recurse;
231 }
232 }
233 }
234 if (!win->isTopLevel()
235 && !(win->flags() & Qt::WindowTransparentForInput)
236 && win->geometry().contains(win->parent()->mapFromGlobal(p))) {
237 return win;
238 }
239 return nullptr;
240}
241
242static const char *wm_window_type_property_id = "_q_xcb_wm_window_type";
243static const char *wm_window_role_property_id = "_q_xcb_wm_window_role";
244
245QXcbWindow::QXcbWindow(QWindow *window)
246 : QPlatformWindow(window)
247{
248 setConnection(xcbScreen()->connection());
249}
250
251enum : quint32 {
252 baseEventMask
253 = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
254 | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE,
255
256 defaultEventMask = baseEventMask
257 | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE
258 | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
259 | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW
260 | XCB_EVENT_MASK_POINTER_MOTION,
261
262 transparentForInputEventMask = baseEventMask
263 | XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_RESIZE_REDIRECT
264 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
265 | XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_OWNER_GRAB_BUTTON
266};
267
268void QXcbWindow::create()
269{
270 destroy();
271
272 m_windowState = Qt::WindowNoState;
273 m_trayIconWindow = isTrayIconWindow(window());
274
275 Qt::WindowType type = window()->type();
276
277 QXcbScreen *currentScreen = xcbScreen();
278 QXcbScreen *platformScreen = parent() ? parentScreen() : initialScreen();
279 QRect rect = QHighDpi::toNativePixels(window()->geometry(), platformScreen);
280
281 if (type == Qt::Desktop) {
282 m_window = platformScreen->root();
283 m_depth = platformScreen->screen()->root_depth;
284 m_visualId = platformScreen->screen()->root_visual;
285 const xcb_visualtype_t *visual = 0;
286 if (connection()->hasDefaultVisualId()) {
287 visual = platformScreen->visualForId(connection()->defaultVisualId());
288 if (visual)
289 m_visualId = connection()->defaultVisualId();
290 if (!visual)
291 qWarning("Could not use default visual id. Falling back to root_visual for screen.");
292 }
293 if (!visual)
294 visual = platformScreen->visualForId(m_visualId);
295 setImageFormatForVisual(visual);
296 connection()->addWindowEventListener(m_window, this);
297 return;
298 }
299
300 QPlatformWindow::setGeometry(rect);
301
302 if (platformScreen != currentScreen)
303 QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen());
304
305 const QSize minimumSize = windowMinimumSize();
306 if (rect.width() > 0 || rect.height() > 0) {
307 rect.setWidth(qBound(1, rect.width(), XCOORD_MAX));
308 rect.setHeight(qBound(1, rect.height(), XCOORD_MAX));
309 } else if (minimumSize.width() > 0 || minimumSize.height() > 0) {
310 rect.setSize(minimumSize);
311 } else {
312 rect.setWidth(QHighDpi::toNativePixels(int(defaultWindowWidth), platformScreen->QPlatformScreen::screen()));
313 rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen()));
314 }
315
316 xcb_window_t xcb_parent_id = platformScreen->root();
317 if (parent()) {
318 xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window();
319 m_embedded = parent()->isForeignWindow();
320
321 QSurfaceFormat parentFormat = parent()->window()->requestedFormat();
322 if (window()->surfaceType() != QSurface::OpenGLSurface && parentFormat.hasAlpha()) {
323 window()->setFormat(parentFormat);
324 }
325 }
326
327 resolveFormat(platformScreen->surfaceFormatFor(window()->requestedFormat()));
328
329 const xcb_visualtype_t *visual = nullptr;
330
331 if (m_trayIconWindow && connection()->systemTrayTracker()) {
332 visual = platformScreen->visualForId(connection()->systemTrayTracker()->visualId());
333 } else if (connection()->hasDefaultVisualId()) {
334 visual = platformScreen->visualForId(connection()->defaultVisualId());
335 if (!visual)
336 qWarning() << "Failed to use requested visual id.";
337 }
338
339 if (parent()) {
340 // When using a Vulkan QWindow via QWidget::createWindowContainer() we
341 // must make sure the visuals are compatible. Now, the parent will be
342 // of RasterGLSurface which typically chooses a GLX/EGL compatible
343 // visual which may not be what the Vulkan window would choose.
344 // Therefore, take the parent's visual.
345 if (window()->surfaceType() == QSurface::VulkanSurface
346 && parent()->window()->surfaceType() != QSurface::VulkanSurface)
347 {
348 visual = platformScreen->visualForId(static_cast<QXcbWindow *>(parent())->visualId());
349 }
350 }
351
352 if (!visual)
353 visual = createVisual();
354
355 if (!visual) {
356 qWarning() << "Falling back to using screens root_visual.";
357 visual = platformScreen->visualForId(platformScreen->screen()->root_visual);
358 }
359
360 Q_ASSERT(visual);
361
362 m_visualId = visual->visual_id;
363 m_depth = platformScreen->depthOfVisual(m_visualId);
364 setImageFormatForVisual(visual);
365
366 quint32 mask = XCB_CW_BACK_PIXMAP
367 | XCB_CW_BORDER_PIXEL
368 | XCB_CW_BIT_GRAVITY
369 | XCB_CW_OVERRIDE_REDIRECT
370 | XCB_CW_SAVE_UNDER
371 | XCB_CW_EVENT_MASK;
372
373 static auto haveOpenGL = []() {
374 static const bool result = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL);
375 return result;
376 };
377
378 if ((window()->supportsOpenGL() && haveOpenGL()) || m_format.hasAlpha()) {
379 m_cmap = xcb_generate_id(xcb_connection());
380 xcb_create_colormap(xcb_connection(),
381 XCB_COLORMAP_ALLOC_NONE,
382 m_cmap,
383 xcb_parent_id,
384 m_visualId);
385
386 mask |= XCB_CW_COLORMAP;
387 }
388
389 quint32 values[] = {
390 XCB_BACK_PIXMAP_NONE,
391 platformScreen->screen()->black_pixel,
392 XCB_GRAVITY_NORTH_WEST,
393 type == Qt::Popup || type == Qt::ToolTip || (window()->flags() & Qt::BypassWindowManagerHint),
394 type == Qt::Popup || type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip || type == Qt::Drawer,
395 defaultEventMask,
396 m_cmap
397 };
398
399 m_window = xcb_generate_id(xcb_connection());
400 xcb_create_window(xcb_connection(),
401 m_depth,
402 m_window, // window id
403 xcb_parent_id, // parent window id
404 rect.x(),
405 rect.y(),
406 rect.width(),
407 rect.height(),
408 0, // border width
409 XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
410 m_visualId, // visual
411 mask,
412 values);
413
414 connection()->addWindowEventListener(m_window, this);
415
416 propagateSizeHints();
417
418 xcb_atom_t properties[5];
419 int propertyCount = 0;
420 properties[propertyCount++] = atom(QXcbAtom::WM_DELETE_WINDOW);
421 properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS);
422 properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING);
423
424 if (connection()->hasXSync())
425 properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST);
426
427 if (window()->flags() & Qt::WindowContextHelpButtonHint)
428 properties[propertyCount++] = atom(QXcbAtom::_NET_WM_CONTEXT_HELP);
429
430 xcb_change_property(xcb_connection(),
431 XCB_PROP_MODE_REPLACE,
432 m_window,
433 atom(QXcbAtom::WM_PROTOCOLS),
434 XCB_ATOM_ATOM,
435 32,
436 propertyCount,
437 properties);
438 m_syncValue.hi = 0;
439 m_syncValue.lo = 0;
440
441 const QByteArray wmClass = QXcbIntegration::instance()->wmClass();
442 if (!wmClass.isEmpty()) {
443 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE,
444 m_window, atom(QXcbAtom::WM_CLASS),
445 XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData());
446 }
447
448 if (connection()->hasXSync()) {
449 m_syncCounter = xcb_generate_id(xcb_connection());
450 xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue);
451
452 xcb_change_property(xcb_connection(),
453 XCB_PROP_MODE_REPLACE,
454 m_window,
455 atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER),
456 XCB_ATOM_CARDINAL,
457 32,
458 1,
459 &m_syncCounter);
460 }
461
462 // set the PID to let the WM kill the application if unresponsive
463 quint32 pid = getpid();
464 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
465 atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
466 1, &pid);
467
468 const QByteArray clientMachine = QSysInfo::machineHostName().toLocal8Bit();
469 if (!clientMachine.isEmpty()) {
470 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
471 atom(QXcbAtom::WM_CLIENT_MACHINE), XCB_ATOM_STRING, 8,
472 clientMachine.size(), clientMachine.constData());
473 }
474
475 // Create WM_HINTS property on the window, so we can xcb_icccm_get_wm_hints*()
476 // from various setter functions for adjusting the hints.
477 xcb_icccm_wm_hints_t hints;
478 memset(&hints, 0, sizeof(hints));
479 hints.flags = XCB_ICCCM_WM_HINT_WINDOW_GROUP;
480 hints.window_group = connection()->clientLeader();
481 xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);
482
483 xcb_window_t leader = connection()->clientLeader();
484 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
485 atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
486 1, &leader);
487
488 /* Add XEMBED info; this operation doesn't initiate the embedding. */
489 quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED };
490 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
491 atom(QXcbAtom::_XEMBED_INFO),
492 atom(QXcbAtom::_XEMBED_INFO),
493 32, 2, (void *)data);
494
495#if QT_CONFIG(xcb_xinput)
496 if (connection()->hasXInput2()) {
497 if (connection()->xi2MouseEventsDisabled())
498 connection()->xi2SelectDeviceEventsCompatibility(m_window);
499 else
500 connection()->xi2SelectDeviceEvents(m_window);
501 }
502#endif
503
504 setWindowState(window()->windowStates());
505 setWindowFlags(window()->flags());
506 setWindowTitle(window()->title());
507
508#if QT_CONFIG(xcb_xlib)
509 // force sync to read outstanding requests - see QTBUG-29106
510 XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false);
511#endif
512
513#if QT_CONFIG(draganddrop)
514 connection()->drag()->dndEnable(this, true);
515#endif
516
517 const qreal opacity = qt_window_private(window())->opacity;
518 if (!qFuzzyCompare(opacity, qreal(1.0)))
519 setOpacity(opacity);
520
521 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
522
523 if (window()->isTopLevel())
524 setWindowIcon(window()->icon());
525
526 if (window()->dynamicPropertyNames().contains(wm_window_role_property_id)) {
527 QByteArray wmWindowRole = window()->property(wm_window_role_property_id).toByteArray();
528 setWmWindowRole(wmWindowRole);
529 }
530
531 if (m_trayIconWindow)
532 m_embedded = requestSystemTrayWindowDock();
533}
534
535QXcbWindow::~QXcbWindow()
536{
537 destroy();
538}
539
540QXcbForeignWindow::~QXcbForeignWindow()
541{
542 // Clear window so that destroy() does not affect it
543 m_window = 0;
544
545 if (connection()->mouseGrabber() == this)
546 connection()->setMouseGrabber(nullptr);
547 if (connection()->mousePressWindow() == this)
548 connection()->setMousePressWindow(nullptr);
549}
550
551void QXcbWindow::destroy()
552{
553 if (connection()->focusWindow() == this)
554 doFocusOut();
555 if (connection()->mouseGrabber() == this)
556 connection()->setMouseGrabber(nullptr);
557
558 if (m_syncCounter && connection()->hasXSync())
559 xcb_sync_destroy_counter(xcb_connection(), m_syncCounter);
560 if (m_window) {
561 if (m_netWmUserTimeWindow) {
562 xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
563 // Some window managers, like metacity, do XSelectInput on the _NET_WM_USER_TIME_WINDOW window,
564 // without trapping BadWindow (which crashes when the user time window is destroyed).
565 connection()->sync();
566 xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow);
567 m_netWmUserTimeWindow = XCB_NONE;
568 }
569 connection()->removeWindowEventListener(m_window);
570 xcb_destroy_window(xcb_connection(), m_window);
571 m_window = 0;
572 }
573 if (m_cmap) {
574 xcb_free_colormap(xcb_connection(), m_cmap);
575 }
576 m_mapped = false;
577
578 if (m_pendingSyncRequest)
579 m_pendingSyncRequest->invalidate();
580}
581
582void QXcbWindow::setGeometry(const QRect &rect)
583{
584 QPlatformWindow::setGeometry(rect);
585
586 propagateSizeHints();
587
588 QXcbScreen *currentScreen = xcbScreen();
589 QXcbScreen *newScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect));
590
591 if (!newScreen)
592 newScreen = xcbScreen();
593
594 if (newScreen != currentScreen)
595 QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
596
597 if (qt_window_private(window())->positionAutomatic) {
598 const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
599 const qint32 values[] = {
600 qBound<qint32>(1, rect.width(), XCOORD_MAX),
601 qBound<qint32>(1, rect.height(), XCOORD_MAX),
602 };
603 xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
604 } else {
605 const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
606 const qint32 values[] = {
607 qBound<qint32>(-XCOORD_MAX, rect.x(), XCOORD_MAX),
608 qBound<qint32>(-XCOORD_MAX, rect.y(), XCOORD_MAX),
609 qBound<qint32>(1, rect.width(), XCOORD_MAX),
610 qBound<qint32>(1, rect.height(), XCOORD_MAX),
611 };
612 xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
613 if (window()->parent() && !window()->transientParent()) {
614 // Wait for server reply for parented windows to ensure that a few window
615 // moves will come as a one event. This is important when native widget is
616 // moved a few times in X and Y directions causing native scroll. Widget
617 // must get single event to not trigger unwanted widget position changes
618 // and then expose events causing backingstore flushes with incorrect
619 // offset causing image crruption.
620 connection()->sync();
621 }
622 }
623
624 xcb_flush(xcb_connection());
625}
626
627QMargins QXcbWindow::frameMargins() const
628{
629 if (m_dirtyFrameMargins) {
630 if (connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_FRAME_EXTENTS))) {
631 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, m_window,
632 atom(QXcbAtom::_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
633 if (reply && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && reply->value_len == 4) {
634 quint32 *data = (quint32 *)xcb_get_property_value(reply.get());
635 // _NET_FRAME_EXTENTS format is left, right, top, bottom
636 m_frameMargins = QMargins(data[0], data[2], data[1], data[3]);
637 m_dirtyFrameMargins = false;
638 return m_frameMargins;
639 }
640 }
641
642 // _NET_FRAME_EXTENTS property is not available, so
643 // walk up the window tree to get the frame parent
644 xcb_window_t window = m_window;
645 xcb_window_t parent = m_window;
646
647 bool foundRoot = false;
648
649 const QVector<xcb_window_t> &virtualRoots =
650 connection()->wmSupport()->virtualRoots();
651
652 while (!foundRoot) {
653 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, xcb_connection(), parent);
654 if (reply) {
655 if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1 || reply->parent == XCB_WINDOW_NONE) {
656 foundRoot = true;
657 } else {
658 window = parent;
659 parent = reply->parent;
660 }
661 } else {
662 m_dirtyFrameMargins = false;
663 m_frameMargins = QMargins();
664 return m_frameMargins;
665 }
666 }
667
668 QPoint offset;
669
670 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(), window, parent, 0, 0);
671 if (reply) {
672 offset = QPoint(reply->dst_x, reply->dst_y);
673 }
674
675 auto geom = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), parent);
676 if (geom) {
677 // --
678 // add the border_width for the window managers frame... some window managers
679 // do not use a border_width of zero for their frames, and if we the left and
680 // top strut, we ensure that pos() is absolutely correct. frameGeometry()
681 // will still be incorrect though... perhaps i should have foffset as well, to
682 // indicate the frame offset (equal to the border_width on X).
683 // - Brad
684 // -- copied from qwidget_x11.cpp
685
686 int left = offset.x() + geom->border_width;
687 int top = offset.y() + geom->border_width;
688 int right = geom->width + geom->border_width - geometry().width() - offset.x();
689 int bottom = geom->height + geom->border_width - geometry().height() - offset.y();
690
691 m_frameMargins = QMargins(left, top, right, bottom);
692 }
693
694 m_dirtyFrameMargins = false;
695 }
696
697 return m_frameMargins;
698}
699
700void QXcbWindow::setVisible(bool visible)
701{
702 if (visible)
703 show();
704 else
705 hide();
706}
707
708void QXcbWindow::show()
709{
710 if (window()->isTopLevel()) {
711
712 // update WM_NORMAL_HINTS
713 propagateSizeHints();
714
715 // update WM_TRANSIENT_FOR
716 xcb_window_t transientXcbParent = 0;
717 if (isTransient(window())) {
718 const QWindow *tp = window()->transientParent();
719 if (tp && tp->handle())
720 transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId();
721 // Default to client leader if there is no transient parent, else modal dialogs can
722 // be hidden by their parents.
723 if (!transientXcbParent)
724 transientXcbParent = connection()->clientLeader();
725 if (transientXcbParent) { // ICCCM 4.1.2.6
726 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
727 XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
728 1, &transientXcbParent);
729 }
730 }
731 if (!transientXcbParent)
732 xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR);
733
734 // update _NET_WM_STATE
735 setNetWmStateOnUnmappedWindow();
736 }
737
738 // QWidget-attribute Qt::WA_ShowWithoutActivating.
739 const auto showWithoutActivating = window()->property("_q_showWithoutActivating");
740 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
741 updateNetWmUserTime(0);
742 else if (connection()->time() != XCB_TIME_CURRENT_TIME)
743 updateNetWmUserTime(connection()->time());
744
745 if (m_trayIconWindow)
746 return; // defer showing until XEMBED_EMBEDDED_NOTIFY
747
748 xcb_map_window(xcb_connection(), m_window);
749
750 if (QGuiApplication::modalWindow() == window())
751 requestActivateWindow();
752
753 xcbScreen()->windowShown(this);
754
755 connection()->sync();
756}
757
758void QXcbWindow::hide()
759{
760 xcb_unmap_window(xcb_connection(), m_window);
761
762 // send synthetic UnmapNotify event according to icccm 4.1.4
763 q_padded_xcb_event<xcb_unmap_notify_event_t> event = {};
764 event.response_type = XCB_UNMAP_NOTIFY;
765 event.event = xcbScreen()->root();
766 event.window = m_window;
767 event.from_configure = false;
768 xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
769 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event);
770
771 xcb_flush(xcb_connection());
772
773 if (connection()->mouseGrabber() == this)
774 connection()->setMouseGrabber(nullptr);
775 if (QPlatformWindow *w = connection()->mousePressWindow()) {
776 // Unset mousePressWindow when it (or one of its parents) is unmapped
777 while (w) {
778 if (w == this) {
779 connection()->setMousePressWindow(nullptr);
780 break;
781 }
782 w = w->parent();
783 }
784 }
785
786 m_mapped = false;
787
788 // Hiding a modal window doesn't send an enter event to its transient parent when the
789 // mouse is already over the parent window, so the enter event must be emulated.
790 if (window()->isModal()) {
791 // Get the cursor position at modal window screen
792 const QPoint nativePos = xcbScreen()->cursor()->pos();
793 const QPoint cursorPos = QHighDpi::fromNativePixels(nativePos, xcbScreen()->screenForPosition(nativePos)->screen());
794
795 // Find the top level window at cursor position.
796 // Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen
797 QWindow *enterWindow = nullptr;
798 const auto screens = xcbScreen()->virtualSiblings();
799 for (QPlatformScreen *screen : screens) {
800 if (screen->geometry().contains(cursorPos)) {
801 const QPoint devicePosition = QHighDpi::toNativePixels(cursorPos, screen->screen());
802 enterWindow = screen->topLevelAt(devicePosition);
803 break;
804 }
805 }
806
807 if (enterWindow && enterWindow != window()) {
808 // Find the child window at cursor position, otherwise use the top level window
809 if (QWindow *childWindow = childWindowAt(enterWindow, cursorPos))
810 enterWindow = childWindow;
811 const QPoint localPos = enterWindow->mapFromGlobal(cursorPos);
812 QWindowSystemInterface::handleEnterEvent(enterWindow,
813 localPos * QHighDpiScaling::factor(enterWindow),
814 nativePos);
815 }
816 }
817}
818
819bool QXcbWindow::relayFocusToModalWindow() const
820{
821 QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
822 // get top-level window
823 while (w && w->parent())
824 w = w->parent();
825
826 QWindow *modalWindow = 0;
827 const bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(w, &modalWindow);
828 if (blocked && modalWindow != w) {
829 modalWindow->requestActivate();
830 connection()->flush();
831 return true;
832 }
833
834 return false;
835}
836
837void QXcbWindow::doFocusIn()
838{
839 if (relayFocusToModalWindow())
840 return;
841 QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
842 connection()->setFocusWindow(w);
843 QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
844}
845
846void QXcbWindow::doFocusOut()
847{
848 connection()->setFocusWindow(nullptr);
849 relayFocusToModalWindow();
850 // Do not set the active window to nullptr if there is a FocusIn coming.
851 connection()->focusInTimer().start();
852}
853
854struct QtMotifWmHints {
855 quint32 flags, functions, decorations;
856 qint32 input_mode; // unused
857 quint32 status; // unused
858};
859
860enum {
861 MWM_HINTS_FUNCTIONS = (1L << 0),
862
863 MWM_FUNC_ALL = (1L << 0),
864 MWM_FUNC_RESIZE = (1L << 1),
865 MWM_FUNC_MOVE = (1L << 2),
866 MWM_FUNC_MINIMIZE = (1L << 3),
867 MWM_FUNC_MAXIMIZE = (1L << 4),
868 MWM_FUNC_CLOSE = (1L << 5),
869
870 MWM_HINTS_DECORATIONS = (1L << 1),
871
872 MWM_DECOR_ALL = (1L << 0),
873 MWM_DECOR_BORDER = (1L << 1),
874 MWM_DECOR_RESIZEH = (1L << 2),
875 MWM_DECOR_TITLE = (1L << 3),
876 MWM_DECOR_MENU = (1L << 4),
877 MWM_DECOR_MINIMIZE = (1L << 5),
878 MWM_DECOR_MAXIMIZE = (1L << 6),
879};
880
881QXcbWindow::NetWmStates QXcbWindow::netWmStates()
882{
883 NetWmStates result(0);
884
885 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
886 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
887 XCB_ATOM_ATOM, 0, 1024);
888
889 if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
890 const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
891 const xcb_atom_t *statesEnd = states + reply->length;
892 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
893 result |= NetWmStateAbove;
894 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_BELOW)))
895 result |= NetWmStateBelow;
896 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
897 result |= NetWmStateFullScreen;
898 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
899 result |= NetWmStateMaximizedHorz;
900 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
901 result |= NetWmStateMaximizedVert;
902 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MODAL)))
903 result |= NetWmStateModal;
904 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
905 result |= NetWmStateStaysOnTop;
906 if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
907 result |= NetWmStateDemandsAttention;
908 } else {
909 qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window);
910 }
911
912 return result;
913}
914
915void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
916{
917 Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
918
919 if (type == Qt::ToolTip)
920 flags |= Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint;
921 if (type == Qt::Popup)
922 flags |= Qt::X11BypassWindowManagerHint;
923
924 const quint32 mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
925 const quint32 values[] = {
926 // XCB_CW_OVERRIDE_REDIRECT
927 (flags & Qt::BypassWindowManagerHint) ? 1u : 0,
928 // XCB_CW_EVENT_MASK
929 (flags & Qt::WindowTransparentForInput) ? transparentForInputEventMask : defaultEventMask
930 };
931
932 xcb_change_window_attributes(xcb_connection(), xcb_window(), mask, values);
933
934 QXcbWindowFunctions::WmWindowTypes wmWindowTypes = 0;
935 if (window()->dynamicPropertyNames().contains(wm_window_type_property_id)) {
936 wmWindowTypes = static_cast<QXcbWindowFunctions::WmWindowTypes>(
937 window()->property(wm_window_type_property_id).value<int>());
938 }
939
940 setWmWindowType(wmWindowTypes, flags);
941 setNetWmState(flags);
942 setMotifWmHints(flags);
943
944 setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
945 updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus);
946}
947
948void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags)
949{
950 Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
951
952 QtMotifWmHints mwmhints;
953 memset(&mwmhints, 0, sizeof(mwmhints));
954
955 if (type != Qt::SplashScreen) {
956 mwmhints.flags |= MWM_HINTS_DECORATIONS;
957
958 bool customize = flags & Qt::CustomizeWindowHint;
959 if (type == Qt::Window && !customize) {
960 const Qt::WindowFlags defaultFlags = Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
961 if (!(flags & defaultFlags))
962 flags |= defaultFlags;
963 }
964 if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) {
965 mwmhints.decorations |= MWM_DECOR_BORDER;
966 mwmhints.decorations |= MWM_DECOR_RESIZEH;
967 mwmhints.decorations |= MWM_DECOR_TITLE;
968
969 if (flags & Qt::WindowSystemMenuHint)
970 mwmhints.decorations |= MWM_DECOR_MENU;
971
972 if (flags & Qt::WindowMinimizeButtonHint) {
973 mwmhints.decorations |= MWM_DECOR_MINIMIZE;
974 mwmhints.functions |= MWM_FUNC_MINIMIZE;
975 }
976
977 if (flags & Qt::WindowMaximizeButtonHint) {
978 mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
979 mwmhints.functions |= MWM_FUNC_MAXIMIZE;
980 }
981
982 if (flags & Qt::WindowCloseButtonHint)
983 mwmhints.functions |= MWM_FUNC_CLOSE;
984 }
985 } else {
986 // if type == Qt::SplashScreen
987 mwmhints.decorations = MWM_DECOR_ALL;
988 }
989
990 if (mwmhints.functions != 0) {
991 mwmhints.flags |= MWM_HINTS_FUNCTIONS;
992 mwmhints.functions |= MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
993 } else {
994 mwmhints.functions = MWM_FUNC_ALL;
995 }
996
997 if (!(flags & Qt::FramelessWindowHint)
998 && flags & Qt::CustomizeWindowHint
999 && flags & Qt::WindowTitleHint
1000 && !(flags &
1001 (Qt::WindowMinimizeButtonHint
1002 | Qt::WindowMaximizeButtonHint
1003 | Qt::WindowCloseButtonHint)))
1004 {
1005 // a special case - only the titlebar without any button
1006 mwmhints.flags = MWM_HINTS_FUNCTIONS;
1007 mwmhints.functions = MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
1008 mwmhints.decorations = 0;
1009 }
1010
1011 if (mwmhints.flags) {
1012 xcb_change_property(xcb_connection(),
1013 XCB_PROP_MODE_REPLACE,
1014 m_window,
1015 atom(QXcbAtom::_MOTIF_WM_HINTS),
1016 atom(QXcbAtom::_MOTIF_WM_HINTS),
1017 32,
1018 5,
1019 &mwmhints);
1020 } else {
1021 xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_MOTIF_WM_HINTS));
1022 }
1023}
1024
1025void QXcbWindow::setNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
1026{
1027 xcb_client_message_event_t event;
1028
1029 event.response_type = XCB_CLIENT_MESSAGE;
1030 event.format = 32;
1031 event.sequence = 0;
1032 event.window = m_window;
1033 event.type = atom(QXcbAtom::_NET_WM_STATE);
1034 event.data.data32[0] = set ? 1 : 0;
1035 event.data.data32[1] = one;
1036 event.data.data32[2] = two;
1037 event.data.data32[3] = 0;
1038 event.data.data32[4] = 0;
1039
1040 xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
1041 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1042 (const char *)&event);
1043}
1044
1045void QXcbWindow::setNetWmState(Qt::WindowStates state)
1046{
1047 if ((m_windowState ^ state) & Qt::WindowMaximized) {
1048 setNetWmState(state & Qt::WindowMaximized,
1049 atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
1050 atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
1051 }
1052
1053 if ((m_windowState ^ state) & Qt::WindowFullScreen)
1054 setNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
1055}
1056
1057void QXcbWindow::setNetWmState(Qt::WindowFlags flags)
1058{
1059 setNetWmState(flags & Qt::WindowStaysOnTopHint,
1060 atom(QXcbAtom::_NET_WM_STATE_ABOVE),
1061 atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
1062 setNetWmState(flags & Qt::WindowStaysOnBottomHint, atom(QXcbAtom::_NET_WM_STATE_BELOW));
1063}
1064
1065void QXcbWindow::setNetWmStateOnUnmappedWindow()
1066{
1067 if (Q_UNLIKELY(m_mapped))
1068 qCWarning(lcQpaXcb()) << "internal error: " << Q_FUNC_INFO << "called on mapped window";
1069
1070 NetWmStates states(0);
1071 const Qt::WindowFlags flags = window()->flags();
1072 if (flags & Qt::WindowStaysOnTopHint) {
1073 states |= NetWmStateAbove;
1074 states |= NetWmStateStaysOnTop;
1075 } else if (flags & Qt::WindowStaysOnBottomHint) {
1076 states |= NetWmStateBelow;
1077 }
1078
1079 if (window()->windowStates() & Qt::WindowFullScreen)
1080 states |= NetWmStateFullScreen;
1081
1082 if (window()->windowStates() & Qt::WindowMaximized) {
1083 states |= NetWmStateMaximizedHorz;
1084 states |= NetWmStateMaximizedVert;
1085 }
1086
1087 if (window()->modality() != Qt::NonModal)
1088 states |= NetWmStateModal;
1089
1090 // According to EWMH:
1091 // "The Window Manager should remove _NET_WM_STATE whenever a window is withdrawn".
1092 // Which means that we don't have to read this property before changing it on a withdrawn
1093 // window. But there are situations where users want to adjust this property as well
1094 // (e4cea305ed2ba3c9f580bf9d16c59a1048af0e8a), so instead of overwriting the property
1095 // we first read it and then merge our hints with the existing values, allowing a user
1096 // to set custom hints.
1097
1098 QVector<xcb_atom_t> atoms;
1099 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
1100 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
1101 XCB_ATOM_ATOM, 0, 1024);
1102 if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) {
1103 const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
1104 atoms.resize(reply->value_len);
1105 memcpy((void *)&atoms.first(), (void *)data, reply->value_len * sizeof(xcb_atom_t));
1106 }
1107
1108 if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
1109 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
1110 if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW)))
1111 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW));
1112 if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
1113 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
1114 if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
1115 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ));
1116 if (states & NetWmStateMaximizedVert && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
1117 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
1118 if (states & NetWmStateModal && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MODAL)))
1119 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MODAL));
1120 if (states & NetWmStateStaysOnTop && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
1121 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
1122 if (states & NetWmStateDemandsAttention && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
1123 atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
1124
1125 if (atoms.isEmpty()) {
1126 xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE));
1127 } else {
1128 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
1129 atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
1130 atoms.count(), atoms.constData());
1131 }
1132 xcb_flush(xcb_connection());
1133}
1134
1135void QXcbWindow::setWindowState(Qt::WindowStates state)
1136{
1137 if (state == m_windowState)
1138 return;
1139
1140 if ((m_windowState & Qt::WindowMinimized) && !(state & Qt::WindowMinimized)) {
1141 xcb_map_window(xcb_connection(), m_window);
1142 } else if (!(m_windowState & Qt::WindowMinimized) && (state & Qt::WindowMinimized)) {
1143 xcb_client_message_event_t event;
1144
1145 event.response_type = XCB_CLIENT_MESSAGE;
1146 event.format = 32;
1147 event.sequence = 0;
1148 event.window = m_window;
1149 event.type = atom(QXcbAtom::WM_CHANGE_STATE);
1150 event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC;
1151 event.data.data32[1] = 0;
1152 event.data.data32[2] = 0;
1153 event.data.data32[3] = 0;
1154 event.data.data32[4] = 0;
1155
1156 xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
1157 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1158 (const char *)&event);
1159 m_minimized = true;
1160 }
1161
1162 setNetWmState(state);
1163
1164 xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window);
1165 xcb_icccm_wm_hints_t hints;
1166 if (xcb_icccm_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr)) {
1167 if (state & Qt::WindowMinimized)
1168 xcb_icccm_wm_hints_set_iconic(&hints);
1169 else
1170 xcb_icccm_wm_hints_set_normal(&hints);
1171 xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);
1172 }
1173
1174 connection()->sync();
1175 m_windowState = state;
1176}
1177
1178void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
1179{
1180 xcb_window_t wid = m_window;
1181 // If timestamp == 0, then it means that the window should not be
1182 // initially activated. Don't update global user time for this
1183 // special case.
1184 if (timestamp != 0)
1185 connection()->setNetWmUserTime(timestamp);
1186
1187 const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
1188 if (m_netWmUserTimeWindow || isSupportedByWM) {
1189 if (!m_netWmUserTimeWindow) {
1190 m_netWmUserTimeWindow = xcb_generate_id(xcb_connection());
1191 xcb_create_window(xcb_connection(),
1192 XCB_COPY_FROM_PARENT, // depth -- same as root
1193 m_netWmUserTimeWindow, // window id
1194 m_window, // parent window id
1195 -1, -1, 1, 1,
1196 0, // border width
1197 XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
1198 m_visualId, // visual
1199 0, // value mask
1200 0); // value list
1201 wid = m_netWmUserTimeWindow;
1202 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW),
1203 XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow);
1204 xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME));
1205
1206 QXcbWindow::setWindowTitle(connection(), m_netWmUserTimeWindow,
1207 QStringLiteral("Qt NET_WM User Time Window"));
1208
1209 } else if (!isSupportedByWM) {
1210 // WM no longer supports it, then we should remove the
1211 // _NET_WM_USER_TIME_WINDOW atom.
1212 xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
1213 xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow);
1214 m_netWmUserTimeWindow = XCB_NONE;
1215 } else {
1216 wid = m_netWmUserTimeWindow;
1217 }
1218 }
1219 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid, atom(QXcbAtom::_NET_WM_USER_TIME),
1220 XCB_ATOM_CARDINAL, 32, 1, &timestamp);
1221}
1222
1223void QXcbWindow::setTransparentForMouseEvents(bool transparent)
1224{
1225 if (!connection()->hasXFixes() || transparent == m_transparent)
1226 return;
1227
1228 xcb_rectangle_t rectangle;
1229
1230 xcb_rectangle_t *rect = 0;
1231 int nrect = 0;
1232
1233 if (!transparent) {
1234 rectangle.x = 0;
1235 rectangle.y = 0;
1236 rectangle.width = geometry().width();
1237 rectangle.height = geometry().height();
1238 rect = &rectangle;
1239 nrect = 1;
1240 }
1241
1242 xcb_xfixes_region_t region = xcb_generate_id(xcb_connection());
1243 xcb_xfixes_create_region(xcb_connection(), region, nrect, rect);
1244 xcb_xfixes_set_window_shape_region_checked(xcb_connection(), m_window, XCB_SHAPE_SK_INPUT, 0, 0, region);
1245 xcb_xfixes_destroy_region(xcb_connection(), region);
1246
1247 m_transparent = transparent;
1248}
1249
1250void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus)
1251{
1252 xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window);
1253
1254 xcb_icccm_wm_hints_t hints;
1255 if (!xcb_icccm_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr))
1256 return;
1257
1258 xcb_icccm_wm_hints_set_input(&hints, !doesNotAcceptFocus);
1259 xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);
1260}
1261
1262WId QXcbWindow::winId() const
1263{
1264 return m_window;
1265}
1266
1267void QXcbWindow::setParent(const QPlatformWindow *parent)
1268{
1269 QPoint topLeft = geometry().topLeft();
1270
1271 xcb_window_t xcb_parent_id;
1272 if (parent) {
1273 const QXcbWindow *qXcbParent = static_cast<const QXcbWindow *>(parent);
1274 xcb_parent_id = qXcbParent->xcb_window();
1275 m_embedded = qXcbParent->isForeignWindow();
1276 } else {
1277 xcb_parent_id = xcbScreen()->root();
1278 m_embedded = false;
1279 }
1280 xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y());
1281}
1282
1283void QXcbWindow::setWindowTitle(const QString &title)
1284{
1285 setWindowTitle(connection(), m_window, title);
1286}
1287
1288void QXcbWindow::setWindowIconText(const QString &title)
1289{
1290 const QByteArray ba = title.toUtf8();
1291 xcb_change_property(xcb_connection(),
1292 XCB_PROP_MODE_REPLACE,
1293 m_window,
1294 atom(QXcbAtom::_NET_WM_ICON_NAME),
1295 atom(QXcbAtom::UTF8_STRING),
1296 8,
1297 ba.length(),
1298 ba.constData());
1299}
1300
1301void QXcbWindow::setWindowIcon(const QIcon &icon)
1302{
1303 QVector<quint32> icon_data;
1304 if (!icon.isNull()) {
1305 QList<QSize> availableSizes = icon.availableSizes();
1306 if (availableSizes.isEmpty()) {
1307 // try to use default sizes since the icon can be a scalable image like svg.
1308 availableSizes.push_back(QSize(16,16));
1309 availableSizes.push_back(QSize(32,32));
1310 availableSizes.push_back(QSize(64,64));
1311 availableSizes.push_back(QSize(128,128));
1312 }
1313 for (int i = 0; i < availableSizes.size(); ++i) {
1314 QSize size = availableSizes.at(i);
1315 QPixmap pixmap = icon.pixmap(size);
1316 if (!pixmap.isNull()) {
1317 QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
1318 int pos = icon_data.size();
1319 icon_data.resize(pos + 2 + image.width()*image.height());
1320 icon_data[pos++] = image.width();
1321 icon_data[pos++] = image.height();
1322 memcpy(icon_data.data() + pos, image.bits(), image.width()*image.height()*4);
1323 }
1324 }
1325 }
1326
1327 if (!icon_data.isEmpty()) {
1328 xcb_change_property(xcb_connection(),
1329 XCB_PROP_MODE_REPLACE,
1330 m_window,
1331 atom(QXcbAtom::_NET_WM_ICON),
1332 atom(QXcbAtom::CARDINAL),
1333 32,
1334 icon_data.size(),
1335 (unsigned char *) icon_data.data());
1336 } else {
1337 xcb_delete_property(xcb_connection(),
1338 m_window,
1339 atom(QXcbAtom::_NET_WM_ICON));
1340 }
1341}
1342
1343void QXcbWindow::raise()
1344{
1345 const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
1346 const quint32 values[] = { XCB_STACK_MODE_ABOVE };
1347 xcb_configure_window(xcb_connection(), m_window, mask, values);
1348}
1349
1350void QXcbWindow::lower()
1351{
1352 const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
1353 const quint32 values[] = { XCB_STACK_MODE_BELOW };
1354 xcb_configure_window(xcb_connection(), m_window, mask, values);
1355}
1356
1357void QXcbWindow::propagateSizeHints()
1358{
1359 // update WM_NORMAL_HINTS
1360 xcb_size_hints_t hints;
1361 memset(&hints, 0, sizeof(hints));
1362
1363 const QRect rect = geometry();
1364 QWindowPrivate *win = qt_window_private(window());
1365
1366 if (!win->positionAutomatic)
1367 xcb_icccm_size_hints_set_position(&hints, true, rect.x(), rect.y());
1368 if (rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX)
1369 xcb_icccm_size_hints_set_size(&hints, true, rect.width(), rect.height());
1370
1371 /* Gravity describes how to interpret x and y values the next time
1372 window needs to be positioned on a screen.
1373 XCB_GRAVITY_STATIC : the left top corner of the client window
1374 XCB_GRAVITY_NORTH_WEST : the left top corner of the frame window */
1375 auto gravity = win->positionPolicy == QWindowPrivate::WindowFrameInclusive
1376 ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
1377
1378 xcb_icccm_size_hints_set_win_gravity(&hints, gravity);
1379
1380 QSize minimumSize = windowMinimumSize();
1381 QSize maximumSize = windowMaximumSize();
1382 QSize baseSize = windowBaseSize();
1383 QSize sizeIncrement = windowSizeIncrement();
1384
1385 if (minimumSize.width() > 0 || minimumSize.height() > 0)
1386 xcb_icccm_size_hints_set_min_size(&hints,
1387 qMin(XCOORD_MAX,minimumSize.width()),
1388 qMin(XCOORD_MAX,minimumSize.height()));
1389
1390 if (maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX)
1391 xcb_icccm_size_hints_set_max_size(&hints,
1392 qMin(XCOORD_MAX, maximumSize.width()),
1393 qMin(XCOORD_MAX, maximumSize.height()));
1394
1395 if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) {
1396 xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height());
1397 xcb_icccm_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height());
1398 }
1399
1400 xcb_icccm_set_wm_normal_hints(xcb_connection(), m_window, &hints);
1401}
1402
1403void QXcbWindow::requestActivateWindow()
1404{
1405 /* Never activate embedded windows; doing that would prevent the container
1406 * to re-gain the keyboard focus later. */
1407 if (m_embedded) {
1408 QPlatformWindow::requestActivateWindow();
1409 return;
1410 }
1411
1412 if (!m_mapped) {
1413 m_deferredActivation = true;
1414 return;
1415 }
1416 m_deferredActivation = false;
1417
1418 updateNetWmUserTime(connection()->time());
1419 QWindow *focusWindow = QGuiApplication::focusWindow();
1420
1421 if (window()->isTopLevel()
1422 && !(window()->flags() & Qt::X11BypassWindowManagerHint)
1423 && (!focusWindow || !window()->isAncestorOf(focusWindow))
1424 && connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_ACTIVE_WINDOW))) {
1425 xcb_client_message_event_t event;
1426
1427 event.response_type = XCB_CLIENT_MESSAGE;
1428 event.format = 32;
1429 event.sequence = 0;
1430 event.window = m_window;
1431 event.type = atom(QXcbAtom::_NET_ACTIVE_WINDOW);
1432 event.data.data32[0] = 1;
1433 event.data.data32[1] = connection()->time();
1434 event.data.data32[2] = focusWindow ? focusWindow->winId() : XCB_NONE;
1435 event.data.data32[3] = 0;
1436 event.data.data32[4] = 0;
1437
1438 xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
1439 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1440 (const char *)&event);
1441 } else {
1442 xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time());
1443 }
1444
1445 connection()->sync();
1446}
1447
1448QSurfaceFormat QXcbWindow::format() const
1449{
1450 return m_format;
1451}
1452
1453void QXcbWindow::setWmWindowTypeStatic(QWindow *window, QXcbWindowFunctions::WmWindowTypes windowTypes)
1454{
1455 window->setProperty(wm_window_type_property_id, QVariant::fromValue(static_cast<int>(windowTypes)));
1456
1457 if (window->handle())
1458 static_cast<QXcbWindow *>(window->handle())->setWmWindowType(windowTypes, window->flags());
1459}
1460
1461void QXcbWindow::setWindowIconTextStatic(QWindow *window, const QString &text)
1462{
1463 if (window->handle())
1464 static_cast<QXcbWindow *>(window->handle())->setWindowIconText(text);
1465}
1466
1467void QXcbWindow::setWmWindowRoleStatic(QWindow *window, const QByteArray &role)
1468{
1469 if (window->handle())
1470 static_cast<QXcbWindow *>(window->handle())->setWmWindowRole(role);
1471 else
1472 window->setProperty(wm_window_role_property_id, role);
1473}
1474
1475uint QXcbWindow::visualIdStatic(QWindow *window)
1476{
1477 if (window && window->handle())
1478 return static_cast<QXcbWindow *>(window->handle())->visualId();
1479 return UINT_MAX;
1480}
1481
1482QXcbWindowFunctions::WmWindowTypes QXcbWindow::wmWindowTypes() const
1483{
1484 QXcbWindowFunctions::WmWindowTypes result(0);
1485
1486 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
1487 0, m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE),
1488 XCB_ATOM_ATOM, 0, 1024);
1489 if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
1490 const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
1491 const xcb_atom_t *types_end = types + reply->length;
1492 for (; types != types_end; types++) {
1493 QXcbAtom::Atom type = connection()->qatom(*types);
1494 switch (type) {
1495 case QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL:
1496 result |= QXcbWindowFunctions::Normal;
1497 break;
1498 case QXcbAtom::_NET_WM_WINDOW_TYPE_DESKTOP:
1499 result |= QXcbWindowFunctions::Desktop;
1500 break;
1501 case QXcbAtom::_NET_WM_WINDOW_TYPE_DOCK:
1502 result |= QXcbWindowFunctions::Dock;
1503 break;
1504 case QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLBAR:
1505 result |= QXcbWindowFunctions::Toolbar;
1506 break;
1507 case QXcbAtom::_NET_WM_WINDOW_TYPE_MENU:
1508 result |= QXcbWindowFunctions::Menu;
1509 break;
1510 case QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY:
1511 result |= QXcbWindowFunctions::Utility;
1512 break;
1513 case QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH:
1514 result |= QXcbWindowFunctions::Splash;
1515 break;
1516 case QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG:
1517 result |= QXcbWindowFunctions::Dialog;
1518 break;
1519 case QXcbAtom::_NET_WM_WINDOW_TYPE_DROPDOWN_MENU:
1520 result |= QXcbWindowFunctions::DropDownMenu;
1521 break;
1522 case QXcbAtom::_NET_WM_WINDOW_TYPE_POPUP_MENU:
1523 result |= QXcbWindowFunctions::PopupMenu;
1524 break;
1525 case QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP:
1526 result |= QXcbWindowFunctions::Tooltip;
1527 break;
1528 case QXcbAtom::_NET_WM_WINDOW_TYPE_NOTIFICATION:
1529 result |= QXcbWindowFunctions::Notification;
1530 break;
1531 case QXcbAtom::_NET_WM_WINDOW_TYPE_COMBO:
1532 result |= QXcbWindowFunctions::Combo;
1533 break;
1534 case QXcbAtom::_NET_WM_WINDOW_TYPE_DND:
1535 result |= QXcbWindowFunctions::Dnd;
1536 break;
1537 case QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE:
1538 result |= QXcbWindowFunctions::KdeOverride;
1539 break;
1540 default:
1541 break;
1542 }
1543 }
1544 }
1545 return result;
1546}
1547
1548void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::WindowFlags flags)
1549{
1550 QVector<xcb_atom_t> atoms;
1551
1552 // manual selection 1 (these are never set by Qt and take precedence)
1553 if (types & QXcbWindowFunctions::Normal)
1554 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL));
1555 if (types & QXcbWindowFunctions::Desktop)
1556 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DESKTOP));
1557 if (types & QXcbWindowFunctions::Dock)
1558 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DOCK));
1559 if (types & QXcbWindowFunctions::Notification)
1560 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NOTIFICATION));
1561
1562 // manual selection 2 (Qt uses these during auto selection);
1563 if (types & QXcbWindowFunctions::Utility)
1564 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY));
1565 if (types & QXcbWindowFunctions::Splash)
1566 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH));
1567 if (types & QXcbWindowFunctions::Dialog)
1568 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG));
1569 if (types & QXcbWindowFunctions::Tooltip)
1570 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP));
1571 if (types & QXcbWindowFunctions::KdeOverride)
1572 atoms.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
1573
1574 // manual selection 3 (these can be set by Qt, but don't have a
1575 // corresponding Qt::WindowType). note that order of the *MENU
1576 // atoms is important
1577 if (types & QXcbWindowFunctions::Menu)
1578 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_MENU));
1579 if (types & QXcbWindowFunctions::DropDownMenu)
1580 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DROPDOWN_MENU));
1581 if (types & QXcbWindowFunctions::PopupMenu)
1582 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_POPUP_MENU));
1583 if (types & QXcbWindowFunctions::Toolbar)
1584 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLBAR));
1585 if (types & QXcbWindowFunctions::Combo)
1586 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_COMBO));
1587 if (types & QXcbWindowFunctions::Dnd)
1588 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DND));
1589
1590 // automatic selection
1591 Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
1592 switch (type) {
1593 case Qt::Dialog:
1594 case Qt::Sheet:
1595 if (!(types & QXcbWindowFunctions::Dialog))
1596 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG));
1597 break;
1598 case Qt::Tool:
1599 case Qt::Drawer:
1600 if (!(types & QXcbWindowFunctions::Utility))
1601 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY));
1602 break;
1603 case Qt::ToolTip:
1604 if (!(types & QXcbWindowFunctions::Tooltip))
1605 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP));
1606 break;
1607 case Qt::SplashScreen:
1608 if (!(types & QXcbWindowFunctions::Splash))
1609 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH));
1610 break;
1611 default:
1612 break;
1613 }
1614
1615 if ((flags & Qt::FramelessWindowHint) && !(type & QXcbWindowFunctions::KdeOverride)) {
1616 // override netwm type - quick and easy for KDE noborder
1617 atoms.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
1618 }
1619
1620 if (atoms.size() == 1 && atoms.first() == atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL))
1621 atoms.clear();
1622 else
1623 atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL));
1624
1625 if (atoms.isEmpty()) {
1626 xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE));
1627 } else {
1628 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
1629 atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32,
1630 atoms.count(), atoms.constData());
1631 }
1632 xcb_flush(xcb_connection());
1633}
1634
1635void QXcbWindow::setWmWindowRole(const QByteArray &role)
1636{
1637 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
1638 atom(QXcbAtom::WM_WINDOW_ROLE), XCB_ATOM_STRING, 8,
1639 role.size(), role.constData());
1640}
1641
1642void QXcbWindow::setParentRelativeBackPixmap()
1643{
1644 const quint32 mask = XCB_CW_BACK_PIXMAP;
1645 const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE };
1646 xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
1647}
1648
1649bool QXcbWindow::requestSystemTrayWindowDock()
1650{
1651 if (!connection()->systemTrayTracker())
1652 return false;
1653 connection()->systemTrayTracker()->requestSystemTrayWindowDock(m_window);
1654 return true;
1655}
1656
1657bool QXcbWindow::handleNativeEvent(xcb_generic_event_t *event)
1658{
1659 auto eventType = connection()->nativeInterface()->nativeEventType();
1660#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1661 qintptr result = 0; // Used only by MS Windows
1662#else
1663 long result = 0; // Used only by MS Windows
1664#endif
1665 return QWindowSystemInterface::handleNativeEvent(window(), eventType, event, &result);
1666}
1667
1668void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
1669{
1670 QRect rect(event->x, event->y, event->width, event->height);
1671 m_exposeRegion |= rect;
1672
1673 bool pending = true;
1674
1675 connection()->eventQueue()->peek(QXcbEventQueue::PeekRemoveMatchContinue,
1676 [this, &pending](xcb_generic_event_t *event, int type) {
1677 if (type != XCB_EXPOSE)
1678 return false;
1679 auto expose = reinterpret_cast<xcb_expose_event_t *>(event);
1680 if (expose->window != m_window)
1681 return false;
1682 if (expose->count == 0)
1683 pending = false;
1684 m_exposeRegion |= QRect(expose->x, expose->y, expose->width, expose->height);
1685 free(expose);
1686 return true;
1687 });
1688
1689 // if count is non-zero there are more expose events pending
1690 if (event->count == 0 || !pending) {
1691 QWindowSystemInterface::handleExposeEvent(window(), m_exposeRegion);
1692 m_exposeRegion = QRegion();
1693 }
1694}
1695
1696void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *event)
1697{
1698 if (event->format != 32)
1699 return;
1700
1701 if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
1702 xcb_atom_t protocolAtom = event->data.data32[0];
1703 if (protocolAtom == atom(QXcbAtom::WM_DELETE_WINDOW)) {
1704 QWindowSystemInterface::handleCloseEvent(window());
1705 } else if (protocolAtom == atom(QXcbAtom::WM_TAKE_FOCUS)) {
1706 connection()->setTime(event->data.data32[1]);
1707 relayFocusToModalWindow();
1708 return;
1709 } else if (protocolAtom == atom(QXcbAtom::_NET_WM_PING)) {
1710 if (event->window == xcbScreen()->root())
1711 return;
1712
1713 xcb_client_message_event_t reply = *event;
1714
1715 reply.response_type = XCB_CLIENT_MESSAGE;
1716 reply.window = xcbScreen()->root();
1717
1718 xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
1719 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1720 (const char *)&reply);
1721 xcb_flush(xcb_connection());
1722 } else if (protocolAtom == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) {
1723 connection()->setTime(event->data.data32[1]);
1724 m_syncValue.lo = event->data.data32[2];
1725 m_syncValue.hi = event->data.data32[3];
1726 if (connection()->hasXSync())
1727 m_syncState = SyncReceived;
1728#ifndef QT_NO_WHATSTHIS
1729 } else if (protocolAtom == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) {
1730 QWindowSystemInterface::handleEnterWhatsThisEvent();
1731#endif
1732 } else {
1733 qCWarning(lcQpaXcb, "Unhandled WM_PROTOCOLS (%s)",
1734 connection()->atomName(protocolAtom).constData());
1735 }
1736#if QT_CONFIG(draganddrop)
1737 } else if (event->type == atom(QXcbAtom::XdndEnter)) {
1738 connection()->drag()->handleEnter(this, event);
1739 } else if (event->type == atom(QXcbAtom::XdndPosition)) {
1740 connection()->drag()->handlePosition(this, event);
1741 } else if (event->type == atom(QXcbAtom::XdndLeave)) {
1742 connection()->drag()->handleLeave(this, event);
1743 } else if (event->type == atom(QXcbAtom::XdndDrop)) {
1744 connection()->drag()->handleDrop(this, event);
1745#endif
1746 } else if (event->type == atom(QXcbAtom::_XEMBED)) {
1747 handleXEmbedMessage(event);
1748 } else if (event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)) {
1749 doFocusIn();
1750 } else if (event->type == atom(QXcbAtom::MANAGER)
1751 || event->type == atom(QXcbAtom::_NET_WM_STATE)
1752 || event->type == atom(QXcbAtom::WM_CHANGE_STATE)) {
1753 // Ignore _NET_WM_STATE, MANAGER which are relate to tray icons
1754 // and other messages.
1755 } else if (event->type == atom(QXcbAtom::_COMPIZ_DECOR_PENDING)
1756 || event->type == atom(QXcbAtom::_COMPIZ_DECOR_REQUEST)
1757 || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP)
1758 || event->type == atom(QXcbAtom::_COMPIZ_TOOLKIT_ACTION)
1759 || event->type == atom(QXcbAtom::_GTK_LOAD_ICONTHEMES)) {
1760 //silence the _COMPIZ and _GTK messages for now
1761 } else {
1762 qCWarning(lcQpaXcb) << "Unhandled client message: " << connection()->atomName(event->type);
1763 }
1764}
1765
1766void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event)
1767{
1768 bool fromSendEvent = (event->response_type & 0x80);
1769 QPoint pos(event->x, event->y);
1770 if (!parent() && !fromSendEvent) {
1771 // Do not trust the position, query it instead.
1772 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
1773 xcb_window(), xcbScreen()->root(), 0, 0);
1774 if (reply) {
1775 pos.setX(reply->dst_x);
1776 pos.setY(reply->dst_y);
1777 }
1778 }
1779
1780 const QRect actualGeometry = QRect(pos, QSize(event->width, event->height));
1781 QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(actualGeometry);
1782 if (!newScreen)
1783 return;
1784
1785 QWindowSystemInterface::handleGeometryChange(window(), actualGeometry);
1786
1787 // QPlatformScreen::screen() is updated asynchronously, so we can't compare it
1788 // with the newScreen. Just send the WindowScreenChanged event and QGuiApplication
1789 // will make the comparison later.
1790 QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen());
1791
1792 // Send the synthetic expose event on resize only when the window is shrinked,
1793 // because the "XCB_GRAVITY_NORTH_WEST" flag doesn't send it automatically.
1794 if (!m_oldWindowSize.isEmpty()
1795 && (actualGeometry.width() < m_oldWindowSize.width()
1796 || actualGeometry.height() < m_oldWindowSize.height())) {
1797 QWindowSystemInterface::handleExposeEvent(window(), QRegion(0, 0, actualGeometry.width(), actualGeometry.height()));
1798 }
1799 m_oldWindowSize = actualGeometry.size();
1800
1801 if (connection()->hasXSync() && m_syncState == SyncReceived)
1802 m_syncState = SyncAndConfigureReceived;
1803
1804 m_dirtyFrameMargins = true;
1805}
1806
1807bool QXcbWindow::isExposed() const
1808{
1809 return m_mapped;
1810}
1811
1812bool QXcbWindow::isEmbedded() const
1813{
1814 return m_embedded;
1815}
1816
1817QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const
1818{
1819 if (!m_embedded)
1820 return QPlatformWindow::mapToGlobal(pos);
1821
1822 QPoint ret;
1823 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
1824 xcb_window(), xcbScreen()->root(),
1825 pos.x(), pos.y());
1826 if (reply) {
1827 ret.setX(reply->dst_x);
1828 ret.setY(reply->dst_y);
1829 }
1830
1831 return ret;
1832}
1833
1834QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const
1835{
1836 if (!m_embedded)
1837 return QPlatformWindow::mapFromGlobal(pos);
1838
1839 QPoint ret;
1840 auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
1841 xcbScreen()->root(), xcb_window(),
1842 pos.x(), pos.y());
1843 if (reply) {
1844 ret.setX(reply->dst_x);
1845 ret.setY(reply->dst_y);
1846 }
1847
1848 return ret;
1849}
1850
1851void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event)
1852{
1853 if (event->window == m_window) {
1854 m_mapped = true;
1855 if (m_deferredActivation)
1856 requestActivateWindow();
1857
1858 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
1859 }
1860}
1861
1862void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
1863{
1864 if (event->window == m_window) {
1865 m_mapped = false;
1866 QWindowSystemInterface::handleExposeEvent(window(), QRegion());
1867 }
1868}
1869
1870void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
1871 int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
1872 QEvent::Type type, Qt::MouseEventSource source)
1873{
1874 const bool isWheel = detail >= 4 && detail <= 7;
1875 if (!isWheel && window() != QGuiApplication::focusWindow()) {
1876 QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
1877 if (!(w->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::BypassWindowManagerHint))
1878 && w->type() != Qt::ToolTip
1879 && w->type() != Qt::Popup) {
1880 w->requestActivate();
1881 }
1882 }
1883
1884 updateNetWmUserTime(timestamp);
1885
1886 if (m_embedded && !m_trayIconWindow) {
1887 if (window() != QGuiApplication::focusWindow()) {
1888 const QXcbWindow *container = static_cast<const QXcbWindow *>(parent());
1889 Q_ASSERT(container != 0);
1890
1891 sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS);
1892 }
1893 }
1894 QPoint local(event_x, event_y);
1895 QPoint global(root_x, root_y);
1896
1897 if (isWheel) {
1898#if QT_CONFIG(xcb_xinput)
1899 if (!connection()->isAtLeastXI21()) {
1900#endif
1901 QPoint angleDelta;
1902 if (detail == 4)
1903 angleDelta.setY(120);
1904 else if (detail == 5)
1905 angleDelta.setY(-120);
1906 else if (detail == 6)
1907 angleDelta.setX(120);
1908 else if (detail == 7)
1909 angleDelta.setX(-120);
1910 if (modifiers & Qt::AltModifier)
1911 std::swap(angleDelta.rx(), angleDelta.ry());
1912 QWindowSystemInterface::handleWheelEvent(window(), timestamp, local, global, QPoint(), angleDelta, modifiers);
1913#if QT_CONFIG(xcb_xinput)
1914 }
1915#endif
1916 return;
1917 }
1918
1919 connection()->setMousePressWindow(this);
1920
1921 handleMouseEvent(timestamp, local, global, modifiers, type, source);
1922}
1923
1924void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
1925 int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
1926 QEvent::Type type, Qt::MouseEventSource source)
1927{
1928 QPoint local(event_x, event_y);
1929 QPoint global(root_x, root_y);
1930
1931 if (detail >= 4 && detail <= 7) {
1932 // mouse wheel, handled in handleButtonPressEvent()
1933 return;
1934 }
1935
1936 if (connection()->buttonState() == Qt::NoButton)
1937 connection()->setMousePressWindow(nullptr);
1938
1939 handleMouseEvent(timestamp, local, global, modifiers, type, source);
1940}
1941
1942static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
1943{
1944 /* Checking for XCB_NOTIFY_MODE_GRAB and XCB_NOTIFY_DETAIL_ANCESTOR prevents unwanted
1945 * enter/leave events on AwesomeWM on mouse button press. It also ignores duplicated
1946 * enter/leave events on Alt+Tab switching on some WMs with XInput2 events.
1947 * Without XInput2 events the (Un)grabAncestor cannot be checked when mouse button is
1948 * not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events.
1949 */
1950 if (conn) {
1951
1952 const bool mouseButtonsPressed = (conn->buttonState() != Qt::NoButton);
1953#if QT_CONFIG(xcb_xinput)
1954 return mouseButtonsPressed || (conn->hasXInput2() && !conn->xi2MouseEventsDisabled());
1955#else
1956 return mouseButtonsPressed;
1957#endif
1958 }
1959 return true;
1960}
1961
1962static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr)
1963{
1964 return ((doCheckUnGrabAncestor(conn)
1965 && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
1966 || (mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_INFERIOR)
1967 || detail == XCB_NOTIFY_DETAIL_VIRTUAL
1968 || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
1969}
1970
1971static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr)
1972{
1973 return ((doCheckUnGrabAncestor(conn)
1974 && mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
1975 || (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB)
1976 || detail == XCB_NOTIFY_DETAIL_VIRTUAL
1977 || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
1978}
1979
1980void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
1981 quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
1982{
1983 connection()->setTime(timestamp);
1984
1985 const QPoint global = QPoint(root_x, root_y);
1986
1987 if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow())
1988 return;
1989#if QT_CONFIG(xcb_xinput)
1990 // Updates scroll valuators, as user might have done some scrolling outside our X client.
1991 connection()->xi2UpdateScrollingDevices();
1992#endif
1993
1994 const QPoint local(event_x, event_y);
1995 QWindowSystemInterface::handleEnterEvent(window(), local, global);
1996}
1997
1998void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
1999 quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
2000{
2001 connection()->setTime(timestamp);
2002
2003 if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow())
2004 return;
2005
2006 // check if enter event is buffered
2007 auto event = connection()->eventQueue()->peek([](xcb_generic_event_t *event, int type) {
2008 if (type != XCB_ENTER_NOTIFY)
2009 return false;
2010 auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
2011 return !ignoreEnterEvent(enter->mode, enter->detail);
2012 });
2013 auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
2014 QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : nullptr;
2015
2016 if (enterWindow) {
2017 QPoint local(enter->event_x, enter->event_y);
2018 QPoint global = QPoint(root_x, root_y);
2019 QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global);
2020 } else {
2021 QWindowSystemInterface::handleLeaveEvent(window());
2022 }
2023
2024 free(enter);
2025}
2026
2027void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
2028 Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
2029 QEvent::Type type, Qt::MouseEventSource source)
2030{
2031 QPoint local(event_x, event_y);
2032 QPoint global(root_x, root_y);
2033
2034 // "mousePressWindow" can be NULL i.e. if a window will be grabbed or unmapped, so set it again here.
2035 // Unset "mousePressWindow" when mouse button isn't pressed - in some cases the release event won't arrive.
2036 const bool isMouseButtonPressed = (connection()->buttonState() != Qt::NoButton);
2037 const bool hasMousePressWindow = (connection()->mousePressWindow() != nullptr);
2038 if (isMouseButtonPressed && !hasMousePressWindow)
2039 connection()->setMousePressWindow(this);
2040 else if (hasMousePressWindow && !isMouseButtonPressed)
2041 connection()->setMousePressWindow(nullptr);
2042
2043 handleMouseEvent(timestamp, local, global, modifiers, type, source);
2044}
2045
2046void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
2047{
2048 Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
2049 handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
2050 modifiers, event->time, QEvent::MouseButtonPress);
2051}
2052
2053void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
2054{
2055 Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
2056 handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
2057 modifiers, event->time, QEvent::MouseButtonRelease);
2058}
2059
2060void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
2061{
2062 Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
2063 handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers,
2064 event->time, QEvent::MouseMove);
2065}
2066
2067#if QT_CONFIG(xcb_xinput)
2068static inline int fixed1616ToInt(xcb_input_fp1616_t val)
2069{
2070 return int(qreal(val) / 0x10000);
2071}
2072
2073#define qt_xcb_mask_is_set(ptr, event) (((unsigned char*)(ptr))[(event)>>3] & (1 << ((event) & 7)))
2074
2075void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource source)
2076{
2077 QXcbConnection *conn = connection();
2078 auto *ev = reinterpret_cast<xcb_input_button_press_event_t *>(event);
2079
2080 if (ev->buttons_len > 0) {
2081 unsigned char *buttonMask = (unsigned char *) &ev[1];
2082 // There is a bug in the evdev driver which leads to receiving mouse events without
2083 // XIPointerEmulated being set: https://bugs.freedesktop.org/show_bug.cgi?id=98188
2084 // Filter them out by other attributes: when their source device is a touch screen
2085 // and the LMB is pressed.
2086 if (qt_xcb_mask_is_set(buttonMask, 1) && conn->isTouchScreen(ev->sourceid)) {
2087 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2088 qCDebug(lcQpaXInput, "XI2 mouse event from touch device %d was ignored", ev->sourceid);
2089 return;
2090 }
2091 for (int i = 1; i <= 15; ++i)
2092 conn->setButtonState(conn->translateMouseButton(i), qt_xcb_mask_is_set(buttonMask, i));
2093 }
2094
2095 const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective);
2096 const int event_x = fixed1616ToInt(ev->event_x);
2097 const int event_y = fixed1616ToInt(ev->event_y);
2098 const int root_x = fixed1616ToInt(ev->root_x);
2099 const int root_y = fixed1616ToInt(ev->root_y);
2100
2101 conn->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
2102
2103 const Qt::MouseButton button = conn->xiToQtMouseButton(ev->detail);
2104
2105 const char *sourceName = 0;
2106 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
2107 const QMetaObject *metaObject = qt_getEnumMetaObject(source);
2108 const QMetaEnum me = metaObject->enumerator(metaObject->indexOfEnumerator(qt_getEnumName(source)));
2109 sourceName = me.valueToKey(source);
2110 }
2111
2112 switch (ev->event_type) {
2113 case XCB_INPUT_BUTTON_PRESS:
2114 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2115 qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName);
2116 conn->setButtonState(button, true);
2117 handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonPress, source);
2118 break;
2119 case XCB_INPUT_BUTTON_RELEASE:
2120 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2121 qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName);
2122 conn->setButtonState(button, false);
2123 handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonRelease, source);
2124 break;
2125 case XCB_INPUT_MOTION:
2126 if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
2127 qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName);
2128 handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, QEvent::MouseMove, source);
2129 break;
2130 default:
2131 qWarning() << "Unrecognized XI2 mouse event" << ev->event_type;
2132 break;
2133 }
2134}
2135
2136void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
2137{
2138 auto *ev = reinterpret_cast<xcb_input_enter_event_t *>(event);
2139
2140 // Compare the window with current mouse grabber to prevent deliver events to any other windows.
2141 // If leave event occurs and the window is under mouse - allow to deliver the leave event.
2142 QXcbWindow *mouseGrabber = connection()->mouseGrabber();
2143 if (mouseGrabber && mouseGrabber != this
2144 && (ev->event_type != XCB_INPUT_LEAVE || QGuiApplicationPrivate::currentMouseWindow != window())) {
2145 return;
2146 }
2147
2148 const int root_x = fixed1616ToInt(ev->root_x);
2149 const int root_y = fixed1616ToInt(ev->root_y);
2150
2151 switch (ev->event_type) {
2152 case XCB_INPUT_ENTER: {
2153 const int event_x = fixed1616ToInt(ev->event_x);
2154 const int event_y = fixed1616ToInt(ev->event_y);
2155 qCDebug(lcQpaXInputEvents, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d",
2156 event_x, event_y, ev->mode, ev->detail, ev->time);
2157 handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time);
2158 break;
2159 }
2160 case XCB_INPUT_LEAVE:
2161 qCDebug(lcQpaXInputEvents, "XI2 mouse leave, mode %d, detail %d, time %d",
2162 ev->mode, ev->detail, ev->time);
2163 connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
2164 handleLeaveNotifyEvent(root_x, root_y, ev->mode, ev->detail, ev->time);
2165 break;
2166 }
2167}
2168#endif
2169
2170QXcbWindow *QXcbWindow::toWindow() { return this; }
2171
2172void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
2173 Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source)
2174{
2175 connection()->setTime(time);
2176 Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button();
2177 QWindowSystemInterface::handleMouseEvent(window(), time, local, global,
2178 connection()->buttonState(), button,
2179 type, modifiers, source);
2180}
2181
2182void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
2183{
2184 handleEnterNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->mode, event->detail, event->time);
2185}
2186
2187void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
2188{
2189 handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time);
2190}
2191
2192void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
2193{
2194 connection()->setTime(event->time);
2195
2196 const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
2197
2198 if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) {
2199 if (propertyDeleted)
2200 return;
2201
2202 Qt::WindowStates newState = Qt::WindowNoState;
2203
2204 if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
2205 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
2206 0, m_window, atom(QXcbAtom::WM_STATE),
2207 XCB_ATOM_ANY, 0, 1024);
2208 if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
2209 const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());
2210 if (reply->length != 0)
2211 m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC
2212 || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized));
2213 }
2214 }
2215 if (m_minimized)
2216 newState = Qt::WindowMinimized;
2217
2218 const NetWmStates states = netWmStates();
2219 if (states & NetWmStateFullScreen)
2220 newState |= Qt::WindowFullScreen;
2221 if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
2222 newState |= Qt::WindowMaximized;
2223 // Send Window state, compress events in case other flags (modality, etc) are changed.
2224 if (m_lastWindowStateEvent !=