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