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

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