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 ®ion)
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