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