Client: Remove QWaylandWindow wl_surface inheritance

...and introduce a client-side QWaylandSurface.

The goal of this is to remove the assumption that a QWaylandWindow maps 1:1
with a wl_surface and also to be able to share implementation with the cursor
implementation and other parts of the plugin that needs to use surfaces, but
are not QWaylandWindows.

QWaylandWindow and QWaylandSurface are still tightly coupled and a lot of the
implementation remains as part of QWaylandWindow, but I'm hoping to fix this in
later patch sets.

Task-number: QTBUG-74373
Change-Id: Ia5a192c8d133847336d266f63ec216fb3639b8c5
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2019-04-05 12:50:19 +02:00 committed by Johan Helsing
parent dfa4796eae
commit 24be607d50
7 changed files with 267 additions and 92 deletions

View File

@ -45,6 +45,7 @@ SOURCES += qwaylandintegration.cpp \
qwaylandshellsurface.cpp \
qwaylandextendedsurface.cpp \
qwaylandsubsurface.cpp \
qwaylandsurface.cpp \
qwaylandtouch.cpp \
qwaylandqtkey.cpp \
../shared/qwaylandmimehelper.cpp \
@ -70,6 +71,7 @@ HEADERS += qwaylandintegration_p.h \
qwaylandshellsurface_p.h \
qwaylandextendedsurface_p.h \
qwaylandsubsurface_p.h \
qwaylandsurface_p.h \
qwaylandtouch_p.h \
qwaylandqtkey_p.h \
qwaylandabstractdecoration_p.h \

View File

@ -41,6 +41,7 @@
#include "qwaylandintegration_p.h"
#include "qwaylandwindow_p.h"
#include "qwaylandsurface_p.h"
#include "qwaylandabstractdecoration_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"

View File

@ -93,6 +93,7 @@ class QWaylandWindow;
class QWaylandIntegration;
class QWaylandHardwareIntegration;
class QWaylandShellSurface;
class QWaylandSurface;
class QWaylandCursor;
class QWaylandCursorTheme;

View File

@ -0,0 +1,113 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the config.tests of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwaylandsurface_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandscreen_p.h"
#include <QtGui/QGuiApplication>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandSurface::QWaylandSurface(QWaylandDisplay *display)
: wl_surface(display->createSurface(this))
{
connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandSurface::handleScreenRemoved);
}
QWaylandSurface::~QWaylandSurface()
{
destroy();
}
QWaylandScreen *QWaylandSurface::oldestEnteredScreen()
{
return m_screens.value(0, nullptr);
}
QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
{
return static_cast<QWaylandSurface *>(static_cast<QtWayland::wl_surface *>(wl_surface_get_user_data(surface)));
}
void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
{
auto *screen = static_cast<QWaylandScreen *>(qScreen->handle());
if (m_screens.removeOne(screen))
emit screensChanged();
}
void QWaylandSurface::surface_enter(wl_output *output)
{
auto addedScreen = QWaylandScreen::fromWlOutput(output);
if (m_screens.contains(addedScreen)) {
qCWarning(lcQpaWayland)
<< "Ignoring unexpected wl_surface.enter received for output with id:"
<< wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
<< "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model()
<< "This is most likely a bug in the compositor.";
return;
}
m_screens.append(addedScreen);
emit screensChanged();
}
void QWaylandSurface::surface_leave(wl_output *output)
{
auto *removedScreen = QWaylandScreen::fromWlOutput(output);
bool wasRemoved = m_screens.removeOne(removedScreen);
if (!wasRemoved) {
qCWarning(lcQpaWayland)
<< "Ignoring unexpected wl_surface.leave received for output with id:"
<< wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
<< "screen name:" << removedScreen->name()
<< "screen model:" << removedScreen->model()
<< "This is most likely a bug in the compositor.";
return;
}
emit screensChanged();
}
} // namespace QtWaylandClient
QT_END_NAMESPACE

View File

@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the config.tests of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QWAYLANDSURFACE_P_H
#define QWAYLANDSURFACE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtGui/QScreen>
#include <QtWaylandClient/private/qwayland-wayland.h>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
class QWaylandScreen;
class QWaylandWindow;
class QWaylandDisplay;
class QWaylandSurface : public QObject, public QtWayland::wl_surface
{
Q_OBJECT
public:
explicit QWaylandSurface(QWaylandDisplay *display);
~QWaylandSurface() override;
QWaylandScreen *oldestEnteredScreen();
static QWaylandSurface *fromWlSurface(::wl_surface *surface);
signals:
void screensChanged();
private slots:
void handleScreenRemoved(QScreen *qScreen);
protected:
void surface_enter(struct ::wl_output *output) override;
void surface_leave(struct ::wl_output *output) override;
QVector<QWaylandScreen *> m_screens; //As seen by wl_surface.enter/leave events. Chronological order.
QWaylandWindow *m_window = nullptr;
friend class QWaylandWindow; // TODO: shouldn't need to be friends
};
} // namespace QtWaylandClient
QT_END_NAMESPACE
#endif // QWAYLANDSURFACE_P_H

View File

@ -41,6 +41,7 @@
#include "qwaylandbuffer_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandsurface_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandshellsurface_p.h"
@ -77,7 +78,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window)
{
static WId id = 1;
mWindowId = id++;
connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandWindow::handleScreenRemoved);
initializeWlSurface();
}
@ -87,7 +87,7 @@ QWaylandWindow::~QWaylandWindow()
delete mWindowDecoration;
if (isInitialized())
if (mSurface)
reset(false);
const QWindow *parent = window();
@ -112,7 +112,7 @@ void QWaylandWindow::initWindow()
if (window()->type() == Qt::Desktop)
return;
if (!isInitialized()) {
if (!mSurface) {
initializeWlSurface();
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(window(), &e);
@ -177,7 +177,7 @@ void QWaylandWindow::initWindow()
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
// to inform the compositor that high-resolution buffers will be provided.
if (mDisplay->compositorVersion() >= 3)
set_buffer_scale(scale());
mSurface->set_buffer_scale(scale());
if (QScreen *s = window()->screen())
setOrientationMask(s->orientationUpdateMask());
@ -195,7 +195,11 @@ void QWaylandWindow::initWindow()
void QWaylandWindow::initializeWlSurface()
{
init(mDisplay->createSurface(static_cast<QtWayland::wl_surface *>(this)));
Q_ASSERT(!mSurface);
mSurface.reset(new QWaylandSurface(mDisplay));
connect(mSurface.data(), &QWaylandSurface::screensChanged,
this, &QWaylandWindow::handleScreensChanged);
mSurface->m_window = this;
}
bool QWaylandWindow::shouldCreateShellSurface() const
@ -219,7 +223,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const
void QWaylandWindow::reset(bool sendDestroyEvent)
{
if (isInitialized() && sendDestroyEvent) {
if (mSurface && sendDestroyEvent) {
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
QGuiApplication::sendEvent(window(), &e);
}
@ -227,11 +231,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
mShellSurface = nullptr;
delete mSubSurfaceWindow;
mSubSurfaceWindow = nullptr;
if (isInitialized()) {
if (mSurface) {
emit wlSurfaceDestroyed();
destroy();
mSurface.reset();
}
mScreens.clear();
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
@ -244,7 +247,9 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
{
return static_cast<QWaylandWindow *>(static_cast<QtWayland::wl_surface *>(wl_surface_get_user_data(surface)));
if (auto *s = QWaylandSurface::fromWlSurface(surface))
return s->m_window;
return nullptr;
}
WId QWaylandWindow::winId() const
@ -372,7 +377,12 @@ void QWaylandWindow::closePopups(QWaylandWindow *parent)
QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
{
return mScreens.isEmpty() ? waylandScreen() : mScreens.first();
if (mSurface) {
if (auto *screen = mSurface->oldestEnteredScreen())
return screen;
}
return waylandScreen();
}
void QWaylandWindow::setVisible(bool visible)
@ -416,18 +426,18 @@ void QWaylandWindow::setMask(const QRegion &mask)
mMask = mask;
if (!isInitialized())
if (!mSurface)
return;
if (mMask.isEmpty()) {
set_input_region(nullptr);
mSurface->set_input_region(nullptr);
} else {
struct ::wl_region *region = mDisplay->createRegion(mMask);
set_input_region(region);
mSurface->set_input_region(region);
wl_region_destroy(region);
}
wl_surface::commit();
mSurface->commit();
}
void QWaylandWindow::applyConfigureWhenPossible()
@ -481,58 +491,6 @@ void QWaylandWindow::applyConfigure()
QWindowSystemInterface::flushWindowSystemEvents();
}
void QWaylandWindow::surface_enter(wl_output *output)
{
QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
auto addedScreen = QWaylandScreen::fromWlOutput(output);
if (mScreens.contains(addedScreen)) {
qCWarning(lcQpaWayland)
<< "Ignoring unexpected wl_surface.enter received for output with id:"
<< wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
<< "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model()
<< "This is most likely a bug in the compositor.";
return;
}
mScreens.append(addedScreen);
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (oldScreen != newScreen) //currently this will only happen if the first wl_surface.enter is for a non-primary screen
handleScreenChanged();
}
void QWaylandWindow::surface_leave(wl_output *output)
{
QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
auto *removedScreen = QWaylandScreen::fromWlOutput(output);
bool wasRemoved = mScreens.removeOne(removedScreen);
if (!wasRemoved) {
qCWarning(lcQpaWayland)
<< "Ignoring unexpected wl_surface.leave received for output with id:"
<< wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
<< "screen name:" << removedScreen->name()
<< "screen model:" << removedScreen->model()
<< "This is most likely a bug in the compositor.";
return;
}
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (oldScreen != newScreen)
handleScreenChanged();
}
void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
{
QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
bool wasRemoved = mScreens.removeOne(static_cast<QWaylandScreen *>(qScreen->handle()));
if (wasRemoved) {
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (oldScreen != newScreen)
handleScreenChanged();
}
}
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
@ -542,14 +500,14 @@ void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
}
if (buffer) {
mFrameCallback = frame();
mFrameCallback = mSurface->frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameSync = true;
buffer->setBusy();
attach(buffer->buffer(), x, y);
mSurface->attach(buffer->buffer(), x, y);
} else {
QtWayland::wl_surface::attach(nullptr, 0, 0);
mSurface->attach(nullptr, 0, 0);
}
}
@ -561,7 +519,7 @@ void QWaylandWindow::attachOffset(QWaylandBuffer *buffer)
void QWaylandWindow::damage(const QRect &rect)
{
damage(rect.x(), rect.y(), rect.width(), rect.height());
mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
}
void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
@ -591,20 +549,20 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring.";
return;
}
if (!isInitialized())
if (!mSurface)
return;
attachOffset(buffer);
for (const QRect &rect: damage)
wl_surface::damage(rect.x(), rect.y(), rect.width(), rect.height());
mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
Q_ASSERT(!buffer->committed());
buffer->setCommitted();
wl_surface::commit();
mSurface->commit();
}
void QWaylandWindow::commit()
{
wl_surface::commit();
mSurface->commit();
}
const wl_callback_listener QWaylandWindow::callbackListener = {
@ -660,6 +618,11 @@ QRect QWaylandWindow::windowGeometry() const
return QRect(QPoint(), surfaceSize());
}
wl_surface *QWaylandWindow::wlSurface()
{
return mSurface ? mSurface->object() : nullptr;
}
QWaylandShellSurface *QWaylandWindow::shellSurface() const
{
return mShellSurface;
@ -701,9 +664,9 @@ void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orient
default:
Q_UNREACHABLE();
}
set_buffer_transform(transform);
mSurface->set_buffer_transform(transform);
// set_buffer_transform is double buffered, we need to commit.
wl_surface::commit();
mSurface->commit();
}
void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask)
@ -942,16 +905,21 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
}
}
void QWaylandWindow::handleScreenChanged()
void QWaylandWindow::handleScreensChanged()
{
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (newScreen == mLastReportedScreen)
return;
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
mLastReportedScreen = newScreen;
int scale = newScreen->scale();
if (scale != mScale) {
mScale = scale;
if (isInitialized() && mDisplay->compositorVersion() >= 3)
set_buffer_scale(mScale);
if (mSurface && mDisplay->compositorVersion() >= 3)
mSurface->set_buffer_scale(mScale);
ensureSize();
}
}

View File

@ -79,8 +79,9 @@ class QWaylandInputDevice;
class QWaylandScreen;
class QWaylandShmBackingStore;
class QWaylandPointerEvent;
class QWaylandSurface;
class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow, private QtWayland::wl_surface
class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow
{
Q_OBJECT
public:
@ -108,12 +109,10 @@ public:
void applyConfigureWhenPossible(); //rename to possible?
using QtWayland::wl_surface::attach;
void attach(QWaylandBuffer *buffer, int x, int y);
void attachOffset(QWaylandBuffer *buffer);
QPoint attachOffset() const;
using QtWayland::wl_surface::damage;
void damage(const QRect &rect);
void safeCommit(QWaylandBuffer *buffer, const QRegion &damage);
@ -128,7 +127,7 @@ public:
QSize surfaceSize() const;
QRect windowGeometry() const;
::wl_surface *wlSurface() { return object(); }
::wl_surface *wlSurface();
static QWaylandWindow *fromWlSurface(::wl_surface *surface);
QWaylandDisplay *display() const { return mDisplay; }
@ -203,11 +202,8 @@ signals:
void wlSurfaceDestroyed();
protected:
void surface_enter(struct ::wl_output *output) override;
void surface_leave(struct ::wl_output *output) override;
QVector<QWaylandScreen *> mScreens; //As seen by wl_surface.enter/leave events. Chronological order.
QWaylandDisplay *mDisplay = nullptr;
QScopedPointer<QWaylandSurface> mSurface;
QWaylandShellSurface *mShellSurface = nullptr;
QWaylandSubSurface *mSubSurfaceWindow = nullptr;
QVector<QWaylandSubSurface *> mChildren;
@ -231,6 +227,7 @@ protected:
bool mSentInitialResize = false;
QPoint mOffset;
int mScale = 1;
QWaylandScreen *mLastReportedScreen = nullptr;
QIcon mWindowIcon;
@ -242,9 +239,6 @@ protected:
QWaylandBuffer *mQueuedBuffer = nullptr;
QRegion mQueuedBufferDamage;
private slots:
void handleScreenRemoved(QScreen *qScreen);
private:
void setGeometry_helper(const QRect &rect);
void initWindow();
@ -257,7 +251,7 @@ private:
QWaylandScreen *calculateScreenFromSurfaceEvents() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreenChanged();
void handleScreensChanged();
bool mUpdateRequested = false;
QRect mLastExposeGeometry;