[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.
This commit is contained in:
Martin Gräßlin 2014-08-20 14:06:58 +02:00
parent bd8ed3cd70
commit ce8c4240f7
14 changed files with 263 additions and 79 deletions

View file

@ -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)

View file

@ -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)

View file

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// KWin
#include "../../wayland_client/connection_thread.h"
#include "../../wayland_client/shell.h"
#include "../../wayland_client/surface.h"
#include "../../wayland_client/registry.h"
// Wayland
#include <wayland-client-protocol.h>
@ -151,7 +152,9 @@ void TestWaylandShell::testShell()
KWin::Wayland::Shell shell;
shell.setup(registry.bindShell(announced.first().first().value<quint32>(), announced.first().last().value<quint32>()));
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);
}

View file

@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "composite.h"
#include "options.h"
#include "wayland_backend.h"
#include "wayland_client/surface.h"
#include "xcbutils.h"
// kwin libs
#include <kwinglplatform.h>
@ -33,21 +34,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin
{
static void handleFrameCallback(void *data, wl_callback *callback, uint32_t time)
{
Q_UNUSED(data)
Q_UNUSED(time)
reinterpret_cast<EglWaylandBackend*>(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);

View file

@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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<WaylandQPainterBackend*>(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()

View file

@ -37,6 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "kwinxrenderutils.h"
#if HAVE_WAYLAND
#include "wayland_backend.h"
#include "wayland_client/surface.h"
#endif
#include <xcb/xfixes.h>
@ -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<WaylandXRenderBackend*>(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

View file

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "wayland_client/output.h"
#include "wayland_client/registry.h"
#include "wayland_client/shell.h"
#include "wayland_client/surface.h"
// Qt
#include <QAbstractEventDispatcher>
#include <QCoreApplication>
@ -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());
}

View file

@ -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<WaylandSeat> m_seat;
QScopedPointer<ShmPool> m_shm;
@ -285,7 +286,7 @@ ShmPool* WaylandBackend::shmPool()
}
inline
wl_surface *WaylandBackend::surface() const
Surface *WaylandBackend::surface() const
{
return m_surface;
}

View file

@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "fullscreen_shell.h"
#include "surface.h"
#include "output.h"
#include <QDebug>
@ -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);
}
}
}

View file

@ -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);

View file

@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#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)

View file

@ -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;

125
wayland_client/surface.cpp Normal file
View file

@ -0,0 +1,125 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "surface.h"
#include <QRegion>
#include <QVector>
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<Surface*>(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 &region)
{
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());
}
}
}

78
wayland_client/surface.h Normal file
View file

@ -0,0 +1,78 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_WAYLAND_SURFACE_H
#define KWIN_WAYLAND_SURFACE_H
#include <QObject>
#include <QPoint>
#include <wayland-client-protocol.h>
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 &region);
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