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 config.tests 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 "qwaylandwindow_p.h"
41 
42 #include "qwaylandbuffer_p.h"
43 #include "qwaylanddisplay_p.h"
44 #include "qwaylandsurface_p.h"
45 #include "qwaylandinputdevice_p.h"
46 #include "qwaylandscreen_p.h"
47 #include "qwaylandshellsurface_p.h"
48 #include "qwaylandsubsurface_p.h"
49 #include "qwaylandabstractdecoration_p.h"
50 #include "qwaylandwindowmanagerintegration_p.h"
51 #include "qwaylandnativeinterface_p.h"
52 #include "qwaylanddecorationfactory_p.h"
53 #include "qwaylandshmbackingstore_p.h"
54 #include "qwaylandshellintegration_p.h"
55 
56 #include <QtCore/QFileInfo>
57 #include <QtCore/QPointer>
58 #include <QtCore/QRegularExpression>
59 #include <QtGui/QWindow>
60 
61 #include <QGuiApplication>
62 #include <qpa/qwindowsysteminterface.h>
63 #include <QtGui/private/qwindow_p.h>
64 
65 #include <QtCore/QDebug>
66 #include <QtCore/QThread>
67 
68 QT_BEGIN_NAMESPACE
69 
70 namespace QtWaylandClient {
71 
72 Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore")
73 
74 QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
75 
QWaylandWindow(QWindow * window,QWaylandDisplay * display)76 QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
77     : QPlatformWindow(window)
78     , mDisplay(display)
79     , mFrameQueue(mDisplay->createFrameQueue())
80     , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
81 {
82     {
83         bool ok;
84         int frameCallbackTimeout = qEnvironmentVariableIntValue("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", &ok);
85         if (ok)
86             mFrameCallbackTimeout = frameCallbackTimeout;
87     }
88 
89     mScale = waylandScreen() ? waylandScreen()->scale() : 1; // fallback to 1 if we don't have a real screen
90 
91     static WId id = 1;
92     mWindowId = id++;
93     initializeWlSurface();
94 }
95 
~QWaylandWindow()96 QWaylandWindow::~QWaylandWindow()
97 {
98     mDisplay->destroyFrameQueue(mFrameQueue);
99 
100     delete mWindowDecoration;
101 
102     if (mSurface)
103         reset();
104 
105     const QWindow *parent = window();
106     const auto tlw = QGuiApplication::topLevelWindows();
107     for (QWindow *w : tlw) {
108         if (w->transientParent() == parent)
109             QWindowSystemInterface::handleCloseEvent(w);
110     }
111 
112     if (mMouseGrab == this) {
113         mMouseGrab = nullptr;
114     }
115 }
116 
ensureSize()117 void QWaylandWindow::ensureSize()
118 {
119     if (mBackingStore)
120         mBackingStore->ensureSize();
121 }
122 
initWindow()123 void QWaylandWindow::initWindow()
124 {
125     if (window()->type() == Qt::Desktop)
126         return;
127 
128     if (!mSurface) {
129         initializeWlSurface();
130     }
131 
132     if (shouldCreateSubSurface()) {
133         Q_ASSERT(!mSubSurfaceWindow);
134 
135         auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
136         if (parent->wlSurface()) {
137             if (::wl_subsurface *subsurface = mDisplay->createSubSurface(this, parent))
138                 mSubSurfaceWindow = new QWaylandSubSurface(this, parent, subsurface);
139         }
140     } else if (shouldCreateShellSurface()) {
141         Q_ASSERT(!mShellSurface);
142         Q_ASSERT(mDisplay->shellIntegration());
143 
144         mShellSurface = mDisplay->shellIntegration()->createShellSurface(this);
145         if (mShellSurface) {
146             // Set initial surface title
147             setWindowTitle(window()->title());
148 
149             // The appId is the desktop entry identifier that should follow the
150             // reverse DNS convention (see http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html).
151             // According to xdg-shell the appId is only the name, without
152             // the .desktop suffix.
153             //
154             // If the application specifies the desktop file name use that
155             // removing the ".desktop" suffix, otherwise fall back to the
156             // executable name and prepend the reversed organization domain
157             // when available.
158             if (!QGuiApplication::desktopFileName().isEmpty()) {
159                 QString name = QGuiApplication::desktopFileName();
160                 if (name.endsWith(QLatin1String(".desktop")))
161                     name.chop(8);
162                 mShellSurface->setAppId(name);
163             } else {
164                 QFileInfo fi = QCoreApplication::instance()->applicationFilePath();
165                 QStringList domainName =
166                         QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'),
167                                                                                  Qt::SkipEmptyParts);
168 
169                 if (domainName.isEmpty()) {
170                     mShellSurface->setAppId(fi.baseName());
171                 } else {
172                     QString appId;
173                     for (int i = 0; i < domainName.count(); ++i)
174                         appId.prepend(QLatin1Char('.')).prepend(domainName.at(i));
175                     appId.append(fi.baseName());
176                     mShellSurface->setAppId(appId);
177                 }
178             }
179             // the user may have already set some window properties, so make sure to send them out
180             for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it)
181                 mShellSurface->sendProperty(it.key(), it.value());
182         } else {
183             qWarning("Could not create a shell surface object.");
184         }
185     }
186 
187     // Enable high-dpi rendering. Scale() returns the screen scale factor and will
188     // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
189     // to inform the compositor that high-resolution buffers will be provided.
190     if (mDisplay->compositorVersion() >= 3)
191         mSurface->set_buffer_scale(scale());
192 
193     if (QScreen *s = window()->screen())
194         setOrientationMask(s->orientationUpdateMask());
195     setWindowFlags(window()->flags());
196     QRect geometry = windowGeometry();
197     if (geometry.isEmpty())
198         setGeometry_helper(QRect(QPoint(), QSize(500,500)));
199     else
200         setGeometry_helper(geometry);
201     setMask(window()->mask());
202     if (mShellSurface)
203         mShellSurface->requestWindowStates(window()->windowStates());
204     handleContentOrientationChange(window()->contentOrientation());
205     mFlags = window()->flags();
206 }
207 
initializeWlSurface()208 void QWaylandWindow::initializeWlSurface()
209 {
210     Q_ASSERT(!mSurface);
211     {
212         QWriteLocker lock(&mSurfaceLock);
213         mSurface.reset(new QWaylandSurface(mDisplay));
214         connect(mSurface.data(), &QWaylandSurface::screensChanged,
215                 this, &QWaylandWindow::handleScreensChanged);
216         mSurface->m_window = this;
217     }
218     emit wlSurfaceCreated();
219 }
220 
shouldCreateShellSurface() const221 bool QWaylandWindow::shouldCreateShellSurface() const
222 {
223     if (!mDisplay->shellIntegration())
224         return false;
225 
226     if (shouldCreateSubSurface())
227         return false;
228 
229     if (window()->inherits("QShapedPixmapWindow"))
230         return false;
231 
232     if (qEnvironmentVariableIsSet("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"))
233         return !(window()->flags() & Qt::BypassWindowManagerHint);
234 
235     return true;
236 }
237 
shouldCreateSubSurface() const238 bool QWaylandWindow::shouldCreateSubSurface() const
239 {
240     return QPlatformWindow::parent() != nullptr;
241 }
242 
reset()243 void QWaylandWindow::reset()
244 {
245     delete mShellSurface;
246     mShellSurface = nullptr;
247     delete mSubSurfaceWindow;
248     mSubSurfaceWindow = nullptr;
249 
250     invalidateSurface();
251     if (mSurface) {
252         emit wlSurfaceDestroyed();
253         QWriteLocker lock(&mSurfaceLock);
254         mSurface.reset();
255     }
256 
257     if (mFrameCallback) {
258         wl_callback_destroy(mFrameCallback);
259         mFrameCallback = nullptr;
260     }
261 
262     mFrameCallbackElapsedTimer.invalidate();
263     mWaitingForFrameCallback = false;
264     mFrameCallbackTimedOut = false;
265 
266     mMask = QRegion();
267     mQueuedBuffer = nullptr;
268 
269     mDisplay->handleWindowDestroyed(this);
270 }
271 
fromWlSurface(::wl_surface * surface)272 QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
273 {
274     if (auto *s = QWaylandSurface::fromWlSurface(surface))
275         return s->m_window;
276     return nullptr;
277 }
278 
winId() const279 WId QWaylandWindow::winId() const
280 {
281     return mWindowId;
282 }
283 
setParent(const QPlatformWindow * parent)284 void QWaylandWindow::setParent(const QPlatformWindow *parent)
285 {
286     if (!window()->isVisible())
287         return;
288 
289     QWaylandWindow *oldparent = mSubSurfaceWindow ? mSubSurfaceWindow->parent() : nullptr;
290     if (oldparent == parent)
291         return;
292 
293     if (mSubSurfaceWindow && parent) { // new parent, but we were a subsurface already
294         delete mSubSurfaceWindow;
295         QWaylandWindow *p = const_cast<QWaylandWindow *>(static_cast<const QWaylandWindow *>(parent));
296         mSubSurfaceWindow = new QWaylandSubSurface(this, p, mDisplay->createSubSurface(this, p));
297     } else { // we're changing role, need to make a new wl_surface
298         reset();
299         initWindow();
300     }
301 }
302 
setWindowTitle(const QString & title)303 void QWaylandWindow::setWindowTitle(const QString &title)
304 {
305     if (mShellSurface) {
306         const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH
307         const QString formatted = formatWindowTitle(title, separator);
308 
309         const int libwaylandMaxBufferSize = 4096;
310         // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
311         // Also, QString is in utf-16, which means that in the worst case each character will be
312         // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
313         const int maxLength = libwaylandMaxBufferSize / 3 - 100;
314 
315         auto truncated = QStringRef(&formatted).left(maxLength);
316         if (truncated.length() < formatted.length()) {
317             qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported."
318                                     << "Truncating window title (from" << formatted.length() << "chars)";
319         }
320         mShellSurface->setTitle(truncated.toString());
321     }
322 
323     if (mWindowDecoration && window()->isVisible())
324         mWindowDecoration->update();
325 }
326 
setWindowIcon(const QIcon & icon)327 void QWaylandWindow::setWindowIcon(const QIcon &icon)
328 {
329     mWindowIcon = icon;
330 
331     if (mWindowDecoration && window()->isVisible())
332         mWindowDecoration->update();
333 }
334 
setGeometry_helper(const QRect & rect)335 void QWaylandWindow::setGeometry_helper(const QRect &rect)
336 {
337     QSize minimum = windowMinimumSize();
338     QSize maximum = windowMaximumSize();
339     QPlatformWindow::setGeometry(QRect(rect.x(), rect.y(),
340                 qBound(minimum.width(), rect.width(), maximum.width()),
341                 qBound(minimum.height(), rect.height(), maximum.height())));
342 
343     if (mSubSurfaceWindow) {
344         QMargins m = QPlatformWindow::parent()->frameMargins();
345         mSubSurfaceWindow->set_position(rect.x() + m.left(), rect.y() + m.top());
346 
347         QWaylandWindow *parentWindow = mSubSurfaceWindow->parent();
348         if (parentWindow && parentWindow->isExposed()) {
349             QRect parentExposeGeometry(QPoint(), parentWindow->geometry().size());
350             parentWindow->sendExposeEvent(parentExposeGeometry);
351         }
352     }
353 }
354 
setGeometry(const QRect & rect)355 void QWaylandWindow::setGeometry(const QRect &rect)
356 {
357     setGeometry_helper(rect);
358 
359     if (window()->isVisible() && rect.isValid()) {
360         if (mWindowDecoration)
361             mWindowDecoration->update();
362 
363         if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize)
364             mResizeDirty = true;
365         else
366             QWindowSystemInterface::handleGeometryChange(window(), geometry());
367 
368         mSentInitialResize = true;
369     }
370     QRect exposeGeometry(QPoint(), geometry().size());
371     if (isExposed() && !mInResizeFromApplyConfigure && exposeGeometry != mLastExposeGeometry)
372         sendExposeEvent(exposeGeometry);
373 
374     if (mShellSurface && isExposed())
375         mShellSurface->setWindowGeometry(windowContentGeometry());
376 
377     if (isOpaque() && mMask.isEmpty())
378         setOpaqueArea(rect);
379 }
380 
resizeFromApplyConfigure(const QSize & sizeWithMargins,const QPoint & offset)381 void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
382 {
383     QMargins margins = frameMargins();
384     int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1);
385     int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1);
386     QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins));
387 
388     mOffset += offset;
389     mInResizeFromApplyConfigure = true;
390     setGeometry(geometry);
391     mInResizeFromApplyConfigure = false;
392 }
393 
sendExposeEvent(const QRect & rect)394 void QWaylandWindow::sendExposeEvent(const QRect &rect)
395 {
396     if (!(mShellSurface && mShellSurface->handleExpose(rect)))
397         QWindowSystemInterface::handleExposeEvent(window(), rect);
398     else
399         qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending";
400     mLastExposeGeometry = rect;
401 }
402 
403 
404 static QVector<QPointer<QWaylandWindow>> activePopups;
405 
closePopups(QWaylandWindow * parent)406 void QWaylandWindow::closePopups(QWaylandWindow *parent)
407 {
408     while (!activePopups.isEmpty()) {
409         auto popup = activePopups.takeLast();
410         if (popup.isNull())
411             continue;
412         if (popup.data() == parent)
413             return;
414         popup->reset();
415     }
416 }
417 
calculateScreenFromSurfaceEvents() const418 QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
419 {
420     if (mSurface) {
421         if (auto *screen = mSurface->oldestEnteredScreen())
422             return screen;
423     }
424 
425     return QPlatformWindow::screen();
426 }
427 
setVisible(bool visible)428 void QWaylandWindow::setVisible(bool visible)
429 {
430     // Workaround for issue where setVisible may be called with the same value twice
431     if (lastVisible == visible)
432         return;
433     lastVisible = visible;
434 
435     if (visible) {
436         if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip)
437             activePopups << this;
438         initWindow();
439         mDisplay->flushRequests();
440 
441         setGeometry(windowGeometry());
442         // Don't flush the events here, or else the newly visible window may start drawing, but since
443         // there was no frame before it will be stuck at the waitForFrameSync() in
444         // QWaylandShmBackingStore::beginPaint().
445     } else {
446         sendExposeEvent(QRect());
447         closePopups(this);
448         reset();
449     }
450 }
451 
452 
raise()453 void QWaylandWindow::raise()
454 {
455     if (mShellSurface)
456         mShellSurface->raise();
457 }
458 
459 
lower()460 void QWaylandWindow::lower()
461 {
462     if (mShellSurface)
463         mShellSurface->lower();
464 }
465 
setMask(const QRegion & mask)466 void QWaylandWindow::setMask(const QRegion &mask)
467 {
468     QReadLocker locker(&mSurfaceLock);
469     if (!mSurface)
470         return;
471 
472     if (mMask == mask)
473         return;
474 
475     mMask = mask;
476 
477     if (mMask.isEmpty()) {
478         mSurface->set_input_region(nullptr);
479 
480         if (isOpaque())
481             setOpaqueArea(QRect(QPoint(0, 0), geometry().size()));
482     } else {
483         struct ::wl_region *region = mDisplay->createRegion(mMask);
484         mSurface->set_input_region(region);
485         wl_region_destroy(region);
486 
487         if (isOpaque())
488             setOpaqueArea(mMask);
489     }
490 
491     mSurface->commit();
492 }
493 
applyConfigureWhenPossible()494 void QWaylandWindow::applyConfigureWhenPossible()
495 {
496     QMutexLocker resizeLocker(&mResizeLock);
497     if (!mWaitingToApplyConfigure) {
498         mWaitingToApplyConfigure = true;
499         QMetaObject::invokeMethod(this, "applyConfigure", Qt::QueuedConnection);
500     }
501 }
502 
doApplyConfigure()503 void QWaylandWindow::doApplyConfigure()
504 {
505     if (!mWaitingToApplyConfigure)
506         return;
507 
508     if (mShellSurface)
509         mShellSurface->applyConfigure();
510 
511     mWaitingToApplyConfigure = false;
512 }
513 
setCanResize(bool canResize)514 void QWaylandWindow::setCanResize(bool canResize)
515 {
516     QMutexLocker lock(&mResizeLock);
517     mCanResize = canResize;
518 
519     if (canResize) {
520         if (mResizeDirty) {
521             QWindowSystemInterface::handleGeometryChange(window(), geometry());
522         }
523         if (mWaitingToApplyConfigure) {
524             doApplyConfigure();
525             sendExposeEvent(QRect(QPoint(), geometry().size()));
526         } else if (mResizeDirty) {
527             mResizeDirty = false;
528             sendExposeEvent(QRect(QPoint(), geometry().size()));
529         }
530     }
531 }
532 
applyConfigure()533 void QWaylandWindow::applyConfigure()
534 {
535     QMutexLocker lock(&mResizeLock);
536 
537     if (mCanResize || !mSentInitialResize)
538         doApplyConfigure();
539 
540     lock.unlock();
541     sendRecursiveExposeEvent();
542     QWindowSystemInterface::flushWindowSystemEvents();
543 }
544 
sendRecursiveExposeEvent()545 void QWaylandWindow::sendRecursiveExposeEvent()
546 {
547     if (!window()->isVisible())
548         return;
549     sendExposeEvent(QRect(QPoint(), geometry().size()));
550 
551     for (QWaylandSubSurface *subSurface : qAsConst(mChildren)) {
552         auto subWindow = subSurface->window();
553         subWindow->sendRecursiveExposeEvent();
554     }
555 }
556 
attach(QWaylandBuffer * buffer,int x,int y)557 void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
558 {
559     Q_ASSERT(!buffer->committed());
560     if (buffer) {
561         handleUpdate();
562         buffer->setBusy();
563 
564         mSurface->attach(buffer->buffer(), x, y);
565     } else {
566         mSurface->attach(nullptr, 0, 0);
567     }
568 }
569 
attachOffset(QWaylandBuffer * buffer)570 void QWaylandWindow::attachOffset(QWaylandBuffer *buffer)
571 {
572     attach(buffer, mOffset.x(), mOffset.y());
573     mOffset = QPoint();
574 }
575 
damage(const QRect & rect)576 void QWaylandWindow::damage(const QRect &rect)
577 {
578     mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
579 }
580 
safeCommit(QWaylandBuffer * buffer,const QRegion & damage)581 void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
582 {
583     if (isExposed()) {
584         commit(buffer, damage);
585     } else {
586         mQueuedBuffer = buffer;
587         mQueuedBufferDamage = damage;
588     }
589 }
590 
handleExpose(const QRegion & region)591 void QWaylandWindow::handleExpose(const QRegion &region)
592 {
593     QWindowSystemInterface::handleExposeEvent(window(), region);
594     if (mQueuedBuffer) {
595         commit(mQueuedBuffer, mQueuedBufferDamage);
596         mQueuedBuffer = nullptr;
597         mQueuedBufferDamage = QRegion();
598     }
599 }
600 
commit(QWaylandBuffer * buffer,const QRegion & damage)601 void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
602 {
603     Q_ASSERT(isExposed());
604     if (buffer->committed()) {
605         qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring.";
606         return;
607     }
608     if (!mSurface)
609         return;
610 
611     attachOffset(buffer);
612     for (const QRect &rect: damage)
613         mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
614     Q_ASSERT(!buffer->committed());
615     buffer->setCommitted();
616     mSurface->commit();
617 }
618 
commit()619 void QWaylandWindow::commit()
620 {
621     mSurface->commit();
622 }
623 
624 const wl_callback_listener QWaylandWindow::callbackListener = {
__anon368dfff40102() 625     [](void *data, wl_callback *callback, uint32_t time) {
626         Q_UNUSED(time);
627         auto *window = static_cast<QWaylandWindow*>(data);
628 
629         Q_ASSERT(callback == window->mFrameCallback);
630         wl_callback_destroy(callback);
631         window->mFrameCallback = nullptr;
632 
633         window->handleFrameCallback();
634     }
635 };
636 
handleFrameCallback()637 void QWaylandWindow::handleFrameCallback()
638 {
639     mWaitingForFrameCallback = false;
640     mFrameCallbackElapsedTimer.invalidate();
641 
642     // The rest can wait until we can run it on the correct thread
643     if (!mWaitingForUpdateDelivery) {
644         auto doHandleExpose = [this]() {
645             bool wasExposed = isExposed();
646             mFrameCallbackTimedOut = false;
647             if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed?
648                 sendExposeEvent(QRect(QPoint(), geometry().size()));
649             if (wasExposed && hasPendingUpdateRequest())
650                 deliverUpdateRequest();
651 
652             mWaitingForUpdateDelivery = false;
653         };
654 
655         // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
656         // in the single-threaded case.
657         mWaitingForUpdateDelivery = true;
658         QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection);
659     }
660 }
661 
waitForFrameSync(int timeout)662 bool QWaylandWindow::waitForFrameSync(int timeout)
663 {
664     QMutexLocker locker(mFrameQueue.mutex);
665     mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout);
666 
667     if (mWaitingForFrameCallback) {
668         qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
669         mFrameCallbackTimedOut = true;
670         mWaitingForUpdate = false;
671         sendExposeEvent(QRect());
672     }
673 
674     return !mWaitingForFrameCallback;
675 }
676 
frameMargins() const677 QMargins QWaylandWindow::frameMargins() const
678 {
679     if (mWindowDecoration)
680         return mWindowDecoration->margins();
681     return QPlatformWindow::frameMargins();
682 }
683 
684 /*!
685  * Size, with decorations (including including eventual shadows) in wl_surface coordinates
686  */
surfaceSize() const687 QSize QWaylandWindow::surfaceSize() const
688 {
689     return geometry().marginsAdded(frameMargins()).size();
690 }
691 
692 /*!
693  * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
694  * topLeft is where the shadow stops and the decorations border start.
695  */
windowContentGeometry() const696 QRect QWaylandWindow::windowContentGeometry() const
697 {
698     return QRect(QPoint(), surfaceSize());
699 }
700 
701 /*!
702  * Converts from wl_surface coordinates to Qt window coordinates. Qt window
703  * coordinates start inside (not including) the window decorations, while
704  * wl_surface coordinates start at the first pixel of the buffer. Potentially,
705  * this should be in the window shadow, although we don't have those. So for
706  * now, it's the first pixel of the decorations.
707  */
mapFromWlSurface(const QPointF & surfacePosition) const708 QPointF QWaylandWindow::mapFromWlSurface(const QPointF &surfacePosition) const
709 {
710     const QMargins margins = frameMargins();
711     return QPointF(surfacePosition.x() - margins.left(), surfacePosition.y() - margins.top());
712 }
713 
wlSurface()714 wl_surface *QWaylandWindow::wlSurface()
715 {
716     return mSurface ? mSurface->object() : nullptr;
717 }
718 
shellSurface() const719 QWaylandShellSurface *QWaylandWindow::shellSurface() const
720 {
721     return mShellSurface;
722 }
723 
subSurfaceWindow() const724 QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const
725 {
726     return mSubSurfaceWindow;
727 }
728 
waylandScreen() const729 QWaylandScreen *QWaylandWindow::waylandScreen() const
730 {
731     auto *platformScreen = QPlatformWindow::screen();
732     Q_ASSERT(platformScreen);
733     if (platformScreen->isPlaceholder())
734         return nullptr;
735     return static_cast<QWaylandScreen *>(platformScreen);
736 }
737 
handleContentOrientationChange(Qt::ScreenOrientation orientation)738 void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
739 {
740     if (mDisplay->compositorVersion() < 2)
741         return;
742 
743     wl_output_transform transform;
744     bool isPortrait = window()->screen() && window()->screen()->primaryOrientation() == Qt::PortraitOrientation;
745     switch (orientation) {
746         case Qt::PrimaryOrientation:
747             transform = WL_OUTPUT_TRANSFORM_NORMAL;
748             break;
749         case Qt::LandscapeOrientation:
750             transform = isPortrait ? WL_OUTPUT_TRANSFORM_270 : WL_OUTPUT_TRANSFORM_NORMAL;
751             break;
752         case Qt::PortraitOrientation:
753             transform = isPortrait ? WL_OUTPUT_TRANSFORM_NORMAL : WL_OUTPUT_TRANSFORM_90;
754             break;
755         case Qt::InvertedLandscapeOrientation:
756             transform = isPortrait ? WL_OUTPUT_TRANSFORM_90 : WL_OUTPUT_TRANSFORM_180;
757             break;
758         case Qt::InvertedPortraitOrientation:
759             transform = isPortrait ? WL_OUTPUT_TRANSFORM_180 : WL_OUTPUT_TRANSFORM_270;
760             break;
761         default:
762             Q_UNREACHABLE();
763     }
764     mSurface->set_buffer_transform(transform);
765     // set_buffer_transform is double buffered, we need to commit.
766     mSurface->commit();
767 }
768 
setOrientationMask(Qt::ScreenOrientations mask)769 void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask)
770 {
771     if (mShellSurface)
772         mShellSurface->setContentOrientationMask(mask);
773 }
774 
setWindowState(Qt::WindowStates states)775 void QWaylandWindow::setWindowState(Qt::WindowStates states)
776 {
777     if (mShellSurface)
778         mShellSurface->requestWindowStates(states);
779 }
780 
setWindowFlags(Qt::WindowFlags flags)781 void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags)
782 {
783     if (mShellSurface)
784         mShellSurface->setWindowFlags(flags);
785 
786     mFlags = flags;
787     createDecoration();
788 }
789 
createDecoration()790 bool QWaylandWindow::createDecoration()
791 {
792     if (!mDisplay->supportsWindowDecoration())
793         return false;
794 
795     static bool decorationPluginFailed = false;
796     bool decoration = false;
797     switch (window()->type()) {
798         case Qt::Window:
799         case Qt::Widget:
800         case Qt::Dialog:
801         case Qt::Tool:
802         case Qt::Drawer:
803             decoration = true;
804             break;
805         default:
806             break;
807     }
808     if (mFlags & Qt::FramelessWindowHint)
809         decoration = false;
810     if (mFlags & Qt::BypassWindowManagerHint)
811         decoration = false;
812     if (mSubSurfaceWindow)
813         decoration = false;
814     if (mShellSurface && !mShellSurface->wantsDecorations())
815         decoration = false;
816 
817     bool hadDecoration = mWindowDecoration;
818     if (decoration && !decorationPluginFailed) {
819         if (!mWindowDecoration) {
820             QStringList decorations = QWaylandDecorationFactory::keys();
821             if (decorations.empty()) {
822                 qWarning() << "No decoration plugins available. Running with no decorations.";
823                 decorationPluginFailed = true;
824                 return false;
825             }
826 
827             QString targetKey;
828             QByteArray decorationPluginName = qgetenv("QT_WAYLAND_DECORATION");
829             if (!decorationPluginName.isEmpty()) {
830                 targetKey = QString::fromLocal8Bit(decorationPluginName);
831                 if (!decorations.contains(targetKey)) {
832                     qWarning() << "Requested decoration " << targetKey << " not found, falling back to default";
833                     targetKey = QString(); // fallthrough
834                 }
835             }
836 
837             if (targetKey.isEmpty())
838                 targetKey = decorations.first(); // first come, first served.
839 
840 
841             mWindowDecoration = QWaylandDecorationFactory::create(targetKey, QStringList());
842             if (!mWindowDecoration) {
843                 qWarning() << "Could not create decoration from factory! Running with no decorations.";
844                 decorationPluginFailed = true;
845                 return false;
846             }
847             mWindowDecoration->setWaylandWindow(this);
848         }
849     } else {
850         delete mWindowDecoration;
851         mWindowDecoration = nullptr;
852     }
853 
854     if (hadDecoration != (bool)mWindowDecoration) {
855         for (QWaylandSubSurface *subsurf : qAsConst(mChildren)) {
856             QPoint pos = subsurf->window()->geometry().topLeft();
857             QMargins m = frameMargins();
858             subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
859         }
860         sendExposeEvent(QRect(QPoint(), geometry().size()));
861     }
862 
863     return mWindowDecoration;
864 }
865 
decoration() const866 QWaylandAbstractDecoration *QWaylandWindow::decoration() const
867 {
868     return mWindowDecoration;
869 }
870 
closestShellSurfaceWindow(QWindow * window)871 static QWaylandWindow *closestShellSurfaceWindow(QWindow *window)
872 {
873     while (window) {
874         auto w = static_cast<QWaylandWindow *>(window->handle());
875         if (w && w->shellSurface())
876             return w;
877         window = window->transientParent() ? window->transientParent() : window->parent();
878     }
879     return nullptr;
880 }
881 
transientParent() const882 QWaylandWindow *QWaylandWindow::transientParent() const
883 {
884     // Take the closest window with a shell surface, since the transient parent may be a
885     // QWidgetWindow or some other window without a shell surface, which is then not able to
886     // get mouse events.
887     if (auto transientParent = closestShellSurfaceWindow(window()->transientParent()))
888         return transientParent;
889 
890     if (QGuiApplication::focusWindow() && (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup))
891         return closestShellSurfaceWindow(QGuiApplication::focusWindow());
892 
893     return nullptr;
894 }
895 
handleMouse(QWaylandInputDevice * inputDevice,const QWaylandPointerEvent & e)896 void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
897 {
898     if (e.type == QEvent::Leave) {
899         if (mWindowDecoration) {
900             if (mMouseEventsInContentArea)
901                 QWindowSystemInterface::handleLeaveEvent(window());
902         } else {
903             QWindowSystemInterface::handleLeaveEvent(window());
904         }
905 #if QT_CONFIG(cursor)
906         restoreMouseCursor(inputDevice);
907 #endif
908         return;
909     }
910 
911     if (mWindowDecoration) {
912         handleMouseEventWithDecoration(inputDevice, e);
913     } else {
914         switch (e.type) {
915             case QEvent::Enter:
916                 QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global);
917                 break;
918             case QEvent::MouseButtonPress:
919             case QEvent::MouseButtonRelease:
920             case QEvent::MouseMove:
921                 QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.button, e.type, e.modifiers);
922                 break;
923             case QEvent::Wheel:
924                 QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,
925                                                          e.pixelDelta, e.angleDelta, e.modifiers,
926                                                          e.phase, e.source, false);
927                 break;
928         default:
929             Q_UNREACHABLE();
930         }
931     }
932 
933 #if QT_CONFIG(cursor)
934     if (e.type == QEvent::Enter) {
935         QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());
936         if (contentGeometry.contains(e.local.toPoint()))
937             restoreMouseCursor(inputDevice);
938     }
939 #endif
940 }
941 
touchDragDecoration(QWaylandInputDevice * inputDevice,const QPointF & local,const QPointF & global,Qt::TouchPointState state,Qt::KeyboardModifiers mods)942 bool QWaylandWindow::touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods)
943 {
944     if (!mWindowDecoration)
945         return false;
946     return mWindowDecoration->handleTouch(inputDevice, local, global, state, mods);
947 }
948 
handleMouseEventWithDecoration(QWaylandInputDevice * inputDevice,const QWaylandPointerEvent & e)949 void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
950 {
951     if (mMousePressedInContentArea == Qt::NoButton &&
952         mWindowDecoration->handleMouse(inputDevice, e.local, e.global, e.buttons, e.modifiers)) {
953         if (mMouseEventsInContentArea) {
954             QWindowSystemInterface::handleLeaveEvent(window());
955             mMouseEventsInContentArea = false;
956         }
957         return;
958     }
959 
960     QMargins marg = frameMargins();
961     QRect windowRect(0 + marg.left(),
962                      0 + marg.top(),
963                      geometry().size().width() - marg.right(),
964                      geometry().size().height() - marg.bottom());
965     if (windowRect.contains(e.local.toPoint()) || mMousePressedInContentArea != Qt::NoButton) {
966         const QPointF localTranslated = mapFromWlSurface(e.local);
967         QPointF globalTranslated = e.global;
968         globalTranslated.setX(globalTranslated.x() - marg.left());
969         globalTranslated.setY(globalTranslated.y() - marg.top());
970         if (!mMouseEventsInContentArea) {
971 #if QT_CONFIG(cursor)
972             restoreMouseCursor(inputDevice);
973 #endif
974             QWindowSystemInterface::handleEnterEvent(window());
975         }
976 
977         switch (e.type) {
978             case QEvent::Enter:
979                 QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated);
980                 break;
981             case QEvent::MouseButtonPress:
982             case QEvent::MouseButtonRelease:
983             case QEvent::MouseMove:
984                 QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.button, e.type, e.modifiers);
985                 break;
986             case QEvent::Wheel: {
987                 QWindowSystemInterface::handleWheelEvent(window(), e.timestamp,
988                                                          localTranslated, globalTranslated,
989                                                          e.pixelDelta, e.angleDelta, e.modifiers,
990                                                          e.phase, e.source, false);
991                 break;
992             }
993             default:
994                 Q_UNREACHABLE();
995         }
996 
997         mMouseEventsInContentArea = true;
998         mMousePressedInContentArea = e.buttons;
999     } else {
1000         if (mMouseEventsInContentArea) {
1001             QWindowSystemInterface::handleLeaveEvent(window());
1002             mMouseEventsInContentArea = false;
1003         }
1004     }
1005 }
1006 
handleScreensChanged()1007 void QWaylandWindow::handleScreensChanged()
1008 {
1009     QPlatformScreen *newScreen = calculateScreenFromSurfaceEvents();
1010 
1011     if (newScreen == mLastReportedScreen)
1012         return;
1013 
1014     QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
1015     mLastReportedScreen = newScreen;
1016 
1017     int scale = newScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(newScreen)->scale();
1018     if (scale != mScale) {
1019         mScale = scale;
1020         if (mSurface && mDisplay->compositorVersion() >= 3)
1021             mSurface->set_buffer_scale(mScale);
1022         ensureSize();
1023     }
1024 }
1025 
1026 #if QT_CONFIG(cursor)
setMouseCursor(QWaylandInputDevice * device,const QCursor & cursor)1027 void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor)
1028 {
1029     int fallbackBufferScale = int(devicePixelRatio());
1030     device->setCursor(&cursor, {}, fallbackBufferScale);
1031 }
1032 
restoreMouseCursor(QWaylandInputDevice * device)1033 void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device)
1034 {
1035     setMouseCursor(device, window()->cursor());
1036 }
1037 #endif
1038 
requestActivateWindow()1039 void QWaylandWindow::requestActivateWindow()
1040 {
1041     qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()";
1042 }
1043 
isExposed() const1044 bool QWaylandWindow::isExposed() const
1045 {
1046     if (!window()->isVisible())
1047         return false;
1048 
1049     if (mFrameCallbackTimedOut)
1050         return false;
1051 
1052     if (mShellSurface)
1053         return mShellSurface->isExposed();
1054 
1055     if (mSubSurfaceWindow)
1056         return mSubSurfaceWindow->parent()->isExposed();
1057 
1058     return !(shouldCreateShellSurface() || shouldCreateSubSurface());
1059 }
1060 
isActive() const1061 bool QWaylandWindow::isActive() const
1062 {
1063     return mDisplay->isWindowActivated(this);
1064 }
1065 
scale() const1066 int QWaylandWindow::scale() const
1067 {
1068     return mScale;
1069 }
1070 
devicePixelRatio() const1071 qreal QWaylandWindow::devicePixelRatio() const
1072 {
1073     return mScale;
1074 }
1075 
setMouseGrabEnabled(bool grab)1076 bool QWaylandWindow::setMouseGrabEnabled(bool grab)
1077 {
1078     if (window()->type() != Qt::Popup) {
1079         qWarning("This plugin supports grabbing the mouse only for popup windows");
1080         return false;
1081     }
1082 
1083     mMouseGrab = grab ? this : nullptr;
1084     return true;
1085 }
1086 
windowStates() const1087 Qt::WindowStates QWaylandWindow::windowStates() const
1088 {
1089     return mLastReportedWindowStates;
1090 }
1091 
handleWindowStatesChanged(Qt::WindowStates states)1092 void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states)
1093 {
1094     createDecoration();
1095     Qt::WindowStates statesWithoutActive = states & ~Qt::WindowActive;
1096     Qt::WindowStates lastStatesWithoutActive = mLastReportedWindowStates & ~Qt::WindowActive;
1097     QWindowSystemInterface::handleWindowStateChanged(window(), statesWithoutActive,
1098                                                      lastStatesWithoutActive);
1099     mLastReportedWindowStates = states;
1100 }
1101 
sendProperty(const QString & name,const QVariant & value)1102 void QWaylandWindow::sendProperty(const QString &name, const QVariant &value)
1103 {
1104     m_properties.insert(name, value);
1105     QWaylandNativeInterface *nativeInterface = static_cast<QWaylandNativeInterface *>(
1106                 QGuiApplication::platformNativeInterface());
1107     nativeInterface->emitWindowPropertyChanged(this, name);
1108     if (mShellSurface)
1109         mShellSurface->sendProperty(name, value);
1110 }
1111 
setProperty(const QString & name,const QVariant & value)1112 void QWaylandWindow::setProperty(const QString &name, const QVariant &value)
1113 {
1114     m_properties.insert(name, value);
1115     QWaylandNativeInterface *nativeInterface = static_cast<QWaylandNativeInterface *>(
1116                 QGuiApplication::platformNativeInterface());
1117     nativeInterface->emitWindowPropertyChanged(this, name);
1118 }
1119 
properties() const1120 QVariantMap QWaylandWindow::properties() const
1121 {
1122     return m_properties;
1123 }
1124 
property(const QString & name)1125 QVariant QWaylandWindow::property(const QString &name)
1126 {
1127     return m_properties.value(name);
1128 }
1129 
property(const QString & name,const QVariant & defaultValue)1130 QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultValue)
1131 {
1132     return m_properties.value(name, defaultValue);
1133 }
1134 
timerEvent(QTimerEvent * event)1135 void QWaylandWindow::timerEvent(QTimerEvent *event)
1136 {
1137     if (event->timerId() != mFrameCallbackCheckIntervalTimerId)
1138         return;
1139 
1140     bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout);
1141     if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) {
1142         killTimer(mFrameCallbackCheckIntervalTimerId);
1143         mFrameCallbackCheckIntervalTimerId = -1;
1144     }
1145     if (mFrameCallbackElapsedTimer.isValid() && callbackTimerExpired) {
1146         mFrameCallbackElapsedTimer.invalidate();
1147 
1148         qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
1149         mFrameCallbackTimedOut = true;
1150         mWaitingForUpdate = false;
1151         sendExposeEvent(QRect());
1152     }
1153 }
1154 
requestUpdate()1155 void QWaylandWindow::requestUpdate()
1156 {
1157     qCDebug(lcWaylandBackingstore) << "requestUpdate";
1158     Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
1159 
1160     // If we have a frame callback all is good and will be taken care of there
1161     if (mWaitingForFrameCallback)
1162         return;
1163 
1164     // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
1165     // This is a somewhat redundant behavior and might indicate a bug in the calling code, so log
1166     // here so we can get this information when debugging update/frame callback issues.
1167     // Continue as nothing happened, though.
1168     if (mWaitingForUpdate)
1169         qCDebug(lcWaylandBackingstore) << "requestUpdate called twice without committing anything";
1170 
1171     // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1172     // so use invokeMethod to delay the delivery a bit.
1173     QMetaObject::invokeMethod(this, [this] {
1174         // Things might have changed in the meantime
1175         if (hasPendingUpdateRequest() && !mWaitingForFrameCallback)
1176             deliverUpdateRequest();
1177     }, Qt::QueuedConnection);
1178 }
1179 
1180 // Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1181 // with eglSwapBuffers) to know when it's time to commit the next one.
1182 // Can be called from the render thread (without locking anything) so make sure to not make races in this method.
handleUpdate()1183 void QWaylandWindow::handleUpdate()
1184 {
1185     qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
1186 
1187     if (mWaitingForFrameCallback)
1188         return;
1189 
1190     // TODO: Should sync subsurfaces avoid requesting frame callbacks?
1191     QReadLocker lock(&mSurfaceLock);
1192     if (!mSurface)
1193         return;
1194 
1195     QMutexLocker locker(mFrameQueue.mutex);
1196     struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1197     wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mFrameQueue.queue);
1198     mFrameCallback = wl_surface_frame(wrappedSurface);
1199     wl_proxy_wrapper_destroy(wrappedSurface);
1200     wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
1201     mWaitingForFrameCallback = true;
1202     mWaitingForUpdate = false;
1203 
1204     // Start a timer for handling the case when the compositor stops sending frame callbacks.
1205     if (mFrameCallbackTimeout > 0) {
1206         QMetaObject::invokeMethod(this, [this] {
1207             if (mWaitingForFrameCallback) {
1208                 if (mFrameCallbackCheckIntervalTimerId < 0)
1209                     mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout);
1210                 mFrameCallbackElapsedTimer.start();
1211             }
1212         }, Qt::QueuedConnection);
1213     }
1214 }
1215 
deliverUpdateRequest()1216 void QWaylandWindow::deliverUpdateRequest()
1217 {
1218     qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1219     mWaitingForUpdate = true;
1220     QPlatformWindow::deliverUpdateRequest();
1221 }
1222 
addAttachOffset(const QPoint point)1223 void QWaylandWindow::addAttachOffset(const QPoint point)
1224 {
1225     mOffset += point;
1226 }
1227 
propagateSizeHints()1228 void QWaylandWindow::propagateSizeHints()
1229 {
1230     if (mShellSurface)
1231         mShellSurface->propagateSizeHints();
1232 }
1233 
startSystemResize(Qt::Edges edges)1234 bool QWaylandWindow::startSystemResize(Qt::Edges edges)
1235 {
1236     if (auto *seat = display()->lastInputDevice())
1237         return mShellSurface && mShellSurface->resize(seat, edges);
1238     return false;
1239 }
1240 
startSystemMove()1241 bool QtWaylandClient::QWaylandWindow::startSystemMove()
1242 {
1243     if (auto seat = display()->lastInputDevice())
1244         return mShellSurface && mShellSurface->move(seat);
1245     return false;
1246 }
1247 
isOpaque() const1248 bool QWaylandWindow::isOpaque() const
1249 {
1250     return window()->requestedFormat().alphaBufferSize() <= 0;
1251 }
1252 
setOpaqueArea(const QRegion & opaqueArea)1253 void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea)
1254 {
1255     const QRegion translatedOpaqueArea = opaqueArea.translated(frameMargins().left(), frameMargins().top());
1256 
1257     if (translatedOpaqueArea == mOpaqueArea || !mSurface)
1258         return;
1259 
1260     mOpaqueArea = translatedOpaqueArea;
1261 
1262     struct ::wl_region *region = mDisplay->createRegion(translatedOpaqueArea);
1263     mSurface->set_opaque_region(region);
1264     wl_region_destroy(region);
1265 }
1266 
1267 }
1268 
1269 QT_END_NAMESPACE
1270