From ce8c4240f7d45a0adae7bf9929344e0befa9c223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 20 Aug 2014 14:06:58 +0200 Subject: [PATCH] [kwin_wayland] Create a dedicated Surface class A Surface class is split out which holds a wl_surface and supports attaching a buffer, setting the damage and emitting a signal when the frame callback got called. It doesn't come with a unit test yet as it first needs the ShmPool and Buffer properly split out to easily set it up. --- CMakeLists.txt | 1 + autotests/wayland_client/CMakeLists.txt | 3 +- .../wayland_client/test_wayland_shell.cpp | 6 +- egl_wayland_backend.cpp | 23 +--- scene_qpainter.cpp | 30 +---- scene_xrender.cpp | 29 +--- wayland_backend.cpp | 18 +-- wayland_backend.h | 7 +- wayland_client/fullscreen_shell.cpp | 9 ++ wayland_client/fullscreen_shell.h | 4 + wayland_client/shell.cpp | 7 + wayland_client/shell.h | 2 + wayland_client/surface.cpp | 125 ++++++++++++++++++ wayland_client/surface.h | 78 +++++++++++ 14 files changed, 263 insertions(+), 79 deletions(-) create mode 100644 wayland_client/surface.cpp create mode 100644 wayland_client/surface.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c58268d271..1bcd2c9768 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,6 +431,7 @@ if(Wayland_Client_FOUND AND XKB_FOUND) wayland_client/fullscreen_shell.cpp wayland_client/output.cpp wayland_client/shell.cpp + wayland_client/surface.cpp ${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client-fullscreen-shell.c ) if(KWIN_HAVE_EGL AND Wayland_Egl_FOUND) diff --git a/autotests/wayland_client/CMakeLists.txt b/autotests/wayland_client/CMakeLists.txt index 7271a6bf68..98938f3cff 100644 --- a/autotests/wayland_client/CMakeLists.txt +++ b/autotests/wayland_client/CMakeLists.txt @@ -66,10 +66,11 @@ set( testWaylandShell_SRCS ${KWIN_SOURCE_DIR}/wayland_client/registry.cpp ${KWIN_SOURCE_DIR}/wayland_client/fullscreen_shell.cpp ${KWIN_SOURCE_DIR}/wayland_client/shell.cpp + ${KWIN_SOURCE_DIR}/wayland_client/surface.cpp ${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client-fullscreen-shell.c ) add_executable(testWaylandShell ${testWaylandShell_SRCS}) add_dependencies(testWaylandShell wayland-client-fullscreen-shell) -target_link_libraries( testWaylandShell Qt5::Test Wayland::Client) +target_link_libraries( testWaylandShell Qt5::Test Qt5::Gui Wayland::Client) add_test(kwin-testWaylandShell testWaylandShell) ecm_mark_as_test(testWaylandShell) diff --git a/autotests/wayland_client/test_wayland_shell.cpp b/autotests/wayland_client/test_wayland_shell.cpp index 5be894b7c6..09ff052403 100644 --- a/autotests/wayland_client/test_wayland_shell.cpp +++ b/autotests/wayland_client/test_wayland_shell.cpp @@ -22,6 +22,7 @@ along with this program. If not, see . // KWin #include "../../wayland_client/connection_thread.h" #include "../../wayland_client/shell.h" +#include "../../wayland_client/surface.h" #include "../../wayland_client/registry.h" // Wayland #include @@ -151,7 +152,9 @@ void TestWaylandShell::testShell() KWin::Wayland::Shell shell; shell.setup(registry.bindShell(announced.first().first().value(), announced.first().last().value())); wl_display_flush(connection.display()); - KWin::Wayland::ShellSurface *surface = shell.createSurface(wl_compositor_create_surface(compositor), &shell); + KWin::Wayland::Surface s; + s.setup(wl_compositor_create_surface(compositor)); + KWin::Wayland::ShellSurface *surface = shell.createSurface(&s, &shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); @@ -167,6 +170,7 @@ void TestWaylandShell::testShell() shell.release(); QVERIFY(!surface->isValid()); + s.release(); wl_compositor_destroy(compositor); } diff --git a/egl_wayland_backend.cpp b/egl_wayland_backend.cpp index b7dc0e624d..ce2d4f634e 100644 --- a/egl_wayland_backend.cpp +++ b/egl_wayland_backend.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . #include "composite.h" #include "options.h" #include "wayland_backend.h" +#include "wayland_client/surface.h" #include "xcbutils.h" // kwin libs #include @@ -33,21 +34,6 @@ along with this program. If not, see . namespace KWin { -static void handleFrameCallback(void *data, wl_callback *callback, uint32_t time) -{ - Q_UNUSED(data) - Q_UNUSED(time) - reinterpret_cast(data)->lastFrameRendered(); - - if (callback) { - wl_callback_destroy(callback); - } -} - -static const struct wl_callback_listener s_surfaceFrameListener = { - handleFrameCallback -}; - EglWaylandBackend::EglWaylandBackend() : QObject(NULL) , OpenGLBackend() @@ -186,7 +172,9 @@ bool EglWaylandBackend::initRenderingContext() } const QSize &size = m_wayland->shellSurfaceSize(); - m_overlay = wl_egl_window_create(m_wayland->surface(), size.width(), size.height()); + Wayland::Surface *s = m_wayland->surface(); + connect(s, &Wayland::Surface::frameRendered, this, &EglWaylandBackend::lastFrameRendered); + m_overlay = wl_egl_window_create(*s, size.width(), size.height()); if (!m_overlay) { qCritical() << "Creating Wayland Egl window failed"; return false; @@ -251,8 +239,7 @@ bool EglWaylandBackend::initBufferConfigs() void EglWaylandBackend::present() { m_lastFrameRendered = false; - wl_callback *callback = wl_surface_frame(m_wayland->surface()); - wl_callback_add_listener(callback, &s_surfaceFrameListener, this); + m_wayland->surface()->setupFrameCallback(); if (supportsBufferAge()) { eglSwapBuffers(m_display, m_surface); eglQuerySurface(m_display, m_surface, EGL_BUFFER_AGE_EXT, &m_bufferAge); diff --git a/scene_qpainter.cpp b/scene_qpainter.cpp index 5fe91ef9d9..f3e7f36c43 100644 --- a/scene_qpainter.cpp +++ b/scene_qpainter.cpp @@ -28,6 +28,7 @@ along with this program. If not, see . #include "toplevel.h" #if HAVE_WAYLAND #include "wayland_backend.h" +#include "wayland_client/surface.h" #endif #include "workspace.h" #include "xcbutils.h" @@ -79,20 +80,6 @@ void QPainterBackend::setFailed(const QString &reason) //**************************************** // WaylandQPainterBackend //**************************************** -static void handleFrameCallback(void *data, wl_callback *callback, uint32_t time) -{ - Q_UNUSED(data) - Q_UNUSED(time) - reinterpret_cast(data)->lastFrameRendered(); - - if (callback) { - wl_callback_destroy(callback); - } -} - -static const struct wl_callback_listener s_surfaceFrameListener = { - handleFrameCallback -}; WaylandQPainterBackend::WaylandQPainterBackend() : QPainterBackend() @@ -104,6 +91,8 @@ WaylandQPainterBackend::WaylandQPainterBackend() connect(Wayland::WaylandBackend::self()->shmPool(), SIGNAL(poolResized()), SLOT(remapBuffer())); connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::shellSurfaceSizeChanged, this, &WaylandQPainterBackend::screenGeometryChanged); + connect(Wayland::WaylandBackend::self()->surface(), &Wayland::Surface::frameRendered, + this, &WaylandQPainterBackend::lastFrameRendered); } WaylandQPainterBackend::~WaylandQPainterBackend() @@ -126,20 +115,15 @@ bool WaylandQPainterBackend::usesOverlayWindow() const void WaylandQPainterBackend::present(int mask, const QRegion &damage) { Q_UNUSED(mask) - Wayland::WaylandBackend *wl = Wayland::WaylandBackend::self(); if (m_backBuffer.isNull()) { return; } m_lastFrameRendered = false; m_needsFullRepaint = false; - wl_surface *surface = wl->surface(); - wl_callback *callback = wl_surface_frame(surface); - wl_callback_add_listener(callback, &s_surfaceFrameListener, this); - wl_surface_attach(surface, m_buffer->buffer(), 0, 0); - Q_FOREACH (const QRect &rect, damage.rects()) { - wl_surface_damage(surface, rect.x(), rect.y(), rect.width(), rect.height()); - } - wl_surface_commit(surface); + Wayland::Surface *s = Wayland::WaylandBackend::self()->surface(); + s->attachBuffer(m_buffer->buffer()); + s->damage(damage); + s->commit(); } void WaylandQPainterBackend::lastFrameRendered() diff --git a/scene_xrender.cpp b/scene_xrender.cpp index a62e904219..b373e4c44b 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -37,6 +37,7 @@ along with this program. If not, see . #include "kwinxrenderutils.h" #if HAVE_WAYLAND #include "wayland_backend.h" +#include "wayland_client/surface.h" #endif #include @@ -257,20 +258,6 @@ bool X11XRenderBackend::usesOverlayWindow() const // WaylandXRenderBackend //**************************************** #if HAVE_WAYLAND -static void handleFrameCallback(void *data, wl_callback *callback, uint32_t time) -{ - Q_UNUSED(data) - Q_UNUSED(time) - reinterpret_cast(data)->lastFrameRendered(); - - if (callback) { - wl_callback_destroy(callback); - } -} - -static const struct wl_callback_listener s_surfaceFrameListener = { - handleFrameCallback -}; WaylandXRenderBackend::WaylandXRenderBackend() : m_shm(new Xcb::Shm) @@ -286,6 +273,8 @@ WaylandXRenderBackend::WaylandXRenderBackend() init(); connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::shellSurfaceSizeChanged, this, &WaylandXRenderBackend::createBuffer); + connect(Wayland::WaylandBackend::self()->surface(), &Wayland::Surface::frameRendered, + this, &WaylandXRenderBackend::lastFrameRendered); } WaylandXRenderBackend::~WaylandXRenderBackend() @@ -341,14 +330,10 @@ void WaylandXRenderBackend::present(int mask, const QRegion &damage) return; } m_lastFrameRendered = false; - wl_surface *surface = wl->surface(); - wl_callback *callback = wl_surface_frame(surface); - wl_callback_add_listener(callback, &s_surfaceFrameListener, this); - wl_surface_attach(surface, buffer, 0, 0); - Q_FOREACH (const QRect &rect, damage.rects()) { - wl_surface_damage(surface, rect.x(), rect.y(), rect.width(), rect.height()); - } - wl_surface_commit(surface); + Wayland::Surface *s = wl->surface(); + s->attachBuffer(buffer); + s->damage(damage); + s->commit(); } bool WaylandXRenderBackend::isLastFrameRendered() const diff --git a/wayland_backend.cpp b/wayland_backend.cpp index a89d532605..73d6674aaf 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -27,6 +27,7 @@ along with this program. If not, see . #include "wayland_client/output.h" #include "wayland_client/registry.h" #include "wayland_client/shell.h" +#include "wayland_client/surface.h" // Qt #include #include @@ -565,7 +566,7 @@ WaylandBackend::WaylandBackend(QObject *parent) , m_registry(new Registry(this)) , m_compositor(NULL) , m_shell(new Shell(this)) - , m_surface(NULL) + , m_surface(new Surface(this)) , m_shellSurface(NULL) , m_seat() , m_shm() @@ -617,9 +618,7 @@ WaylandBackend::~WaylandBackend() m_shellSurface->release(); } m_fullscreenShell->release(); - if (m_surface) { - wl_surface_destroy(m_surface); - } + m_surface->release(); m_shell->release(); if (m_compositor) { wl_compositor_destroy(m_compositor); @@ -677,10 +676,7 @@ void WaylandBackend::initConnection() m_shellSurface = nullptr; } m_fullscreenShell->destroy(); - if (m_surface) { - free(m_surface); - m_surface = nullptr; - } + m_surface->destroy(); if (m_shell) { m_shell->destroy(); } @@ -718,14 +714,14 @@ void WaylandBackend::installCursorImage(Qt::CursorShape shape) void WaylandBackend::createSurface() { - m_surface = wl_compositor_create_surface(m_compositor); - if (!m_surface) { + m_surface->setup(wl_compositor_create_surface(m_compositor)); + if (!m_surface->isValid()) { qCritical() << "Creating Wayland Surface failed"; return; } if (m_fullscreenShell->isValid()) { Output *o = m_outputs.first(); - m_fullscreenShell->present(m_surface, o->output()); + m_fullscreenShell->present(m_surface, o); if (o->pixelSize().isValid()) { emit shellSurfaceSizeChanged(o->pixelSize()); } diff --git a/wayland_backend.h b/wayland_backend.h index e01d237864..0f5f7914f1 100644 --- a/wayland_backend.h +++ b/wayland_backend.h @@ -52,6 +52,7 @@ class Output; class Registry; class Shell; class ShellSurface; +class Surface; class CursorData { @@ -187,7 +188,7 @@ public: void createSeat(uint32_t name); void createShm(uint32_t name); - wl_surface *surface() const; + Surface *surface() const; QSize shellSurfaceSize() const; void installCursorImage(Qt::CursorShape shape); Q_SIGNALS: @@ -206,7 +207,7 @@ private: Registry *m_registry; wl_compositor *m_compositor; Shell *m_shell; - wl_surface *m_surface; + Surface *m_surface; ShellSurface *m_shellSurface; QScopedPointer m_seat; QScopedPointer m_shm; @@ -285,7 +286,7 @@ ShmPool* WaylandBackend::shmPool() } inline -wl_surface *WaylandBackend::surface() const +Surface *WaylandBackend::surface() const { return m_surface; } diff --git a/wayland_client/fullscreen_shell.cpp b/wayland_client/fullscreen_shell.cpp index be3c795059..52a52483e8 100644 --- a/wayland_client/fullscreen_shell.cpp +++ b/wayland_client/fullscreen_shell.cpp @@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "fullscreen_shell.h" +#include "surface.h" +#include "output.h" #include @@ -92,5 +94,12 @@ void FullscreenShell::present(wl_surface *surface, wl_output *output) _wl_fullscreen_shell_present_surface(m_shell, surface, _WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT, output); } +void FullscreenShell::present(Surface *surface, Output *output) +{ + Q_ASSERT(surface); + Q_ASSERT(output); + present(*surface, *output); +} + } } diff --git a/wayland_client/fullscreen_shell.h b/wayland_client/fullscreen_shell.h index 11aa91c5d4..30a3f5eaa1 100644 --- a/wayland_client/fullscreen_shell.h +++ b/wayland_client/fullscreen_shell.h @@ -30,6 +30,9 @@ namespace KWin namespace Wayland { +class Surface; +class Output; + class FullscreenShell : public QObject { Q_OBJECT @@ -52,6 +55,7 @@ public: } void setup(_wl_fullscreen_shell *shell); void present(wl_surface *surface, wl_output *output); + void present(Surface *surface, Output *output); static void capabilitiesAnnounce(void *data, struct _wl_fullscreen_shell *shell, uint32_t capability); diff --git a/wayland_client/shell.cpp b/wayland_client/shell.cpp index ffeb47f590..e34acdf7c0 100644 --- a/wayland_client/shell.cpp +++ b/wayland_client/shell.cpp @@ -19,6 +19,7 @@ along with this program. If not, see . *********************************************************************/ #include "shell.h" #include "output.h" +#include "surface.h" namespace KWin { @@ -73,6 +74,12 @@ ShellSurface *Shell::createSurface(wl_surface *surface, QObject *parent) return s; } +ShellSurface *Shell::createSurface(Surface *surface, QObject *parent) +{ + Q_ASSERT(surface); + return createSurface(*surface, parent); +} + ShellSurface::ShellSurface(QObject *parent) : QObject(parent) , m_surface(nullptr) diff --git a/wayland_client/shell.h b/wayland_client/shell.h index a61c081301..73b04fbd50 100644 --- a/wayland_client/shell.h +++ b/wayland_client/shell.h @@ -31,6 +31,7 @@ namespace Wayland { class ShellSurface; class Output; +class Surface; class Shell : public QObject { @@ -47,6 +48,7 @@ public: void setup(wl_shell *shell); ShellSurface *createSurface(wl_surface *surface, QObject *parent = nullptr); + ShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); operator wl_shell*() { return m_shell; diff --git a/wayland_client/surface.cpp b/wayland_client/surface.cpp new file mode 100644 index 0000000000..28c1178ca9 --- /dev/null +++ b/wayland_client/surface.cpp @@ -0,0 +1,125 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "surface.h" + +#include +#include + +namespace KWin +{ +namespace Wayland +{ + +Surface::Surface(QObject *parent) + : QObject(parent) + , m_surface(nullptr) + , m_frameCallbackInstalled(false) +{ +} + +Surface::~Surface() +{ + release(); +} + +void Surface::release() +{ + if (!m_surface) { + return; + } + wl_surface_destroy(m_surface); + m_surface = nullptr; +} + +void Surface::destroy() +{ + if (!m_surface) { + return; + } + free(m_surface); + m_surface = nullptr; +} + +void Surface::setup(wl_surface *surface) +{ + Q_ASSERT(surface); + Q_ASSERT(!m_surface); + m_surface = surface; +} + +void Surface::frameCallback(void *data, wl_callback *callback, uint32_t time) +{ + Q_UNUSED(time) + Surface *s = reinterpret_cast(data); + if (callback) { + wl_callback_destroy(callback); + } + s->handleFrameCallback(); +} + +void Surface::handleFrameCallback() +{ + m_frameCallbackInstalled = false; + frameRendered(); +} + +const struct wl_callback_listener Surface::s_listener = { + Surface::frameCallback +}; + +void Surface::setupFrameCallback() +{ + Q_ASSERT(isValid()); + Q_ASSERT(!m_frameCallbackInstalled); + wl_callback *callback = wl_surface_frame(m_surface); + wl_callback_add_listener(callback, &s_listener, this); + m_frameCallbackInstalled = true; +} + +void Surface::commit(Surface::CommitFlag flag) +{ + Q_ASSERT(isValid()); + if (flag == CommitFlag::FrameCallback) { + setupFrameCallback(); + } + wl_surface_commit(m_surface); +} + +void Surface::damage(const QRegion ®ion) +{ + for (const QRect &r : region.rects()) { + damage(r); + } +} + +void Surface::damage(const QRect &rect) +{ + Q_ASSERT(isValid()); + wl_surface_damage(m_surface, rect.x(), rect.y(), rect.width(), rect.height()); +} + +void Surface::attachBuffer(wl_buffer *buffer, const QPoint &offset) +{ + Q_ASSERT(isValid()); + wl_surface_attach(m_surface, buffer, offset.x(), offset.y()); +} + +} +} diff --git a/wayland_client/surface.h b/wayland_client/surface.h new file mode 100644 index 0000000000..ecd93ad812 --- /dev/null +++ b/wayland_client/surface.h @@ -0,0 +1,78 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_WAYLAND_SURFACE_H +#define KWIN_WAYLAND_SURFACE_H + +#include +#include + +#include + +namespace KWin +{ +namespace Wayland +{ + +class Surface : public QObject +{ + Q_OBJECT +public: + explicit Surface(QObject *parent = nullptr); + virtual ~Surface(); + + void setup(wl_surface *surface); + void release(); + void destroy(); + bool isValid() const { + return m_surface != nullptr; + } + void setupFrameCallback(); + enum class CommitFlag { + None, + FrameCallback + }; + void commit(CommitFlag flag = CommitFlag::FrameCallback); + void damage(const QRect &rect); + void damage(const QRegion ®ion); + void attachBuffer(wl_buffer *buffer, const QPoint &offset = QPoint()); + + operator wl_surface*() { + return m_surface; + } + operator wl_surface*() const { + return m_surface; + } + + static void frameCallback(void *data, wl_callback *callback, uint32_t time); + +Q_SIGNALS: + void frameRendered(); + +private: + void handleFrameCallback(); + static const wl_callback_listener s_listener; + wl_surface *m_surface; + bool m_frameCallbackInstalled; +}; + +} +} + +#endif