kwin/wayland_server/surface_interface.cpp
Martin Gräßlin 35c4786820 [kwin_wayland] Add support for shm buffers in server module
The Display provides a method to create the shm pool and a
BufferInterface class is added to the server module. It is created
from the SurfaceInterface when a buffer gets attached to the surface.
The BufferInterface can be referenced and once its unreferenced it
sends a buffer release to the client and destroys itself.

For the case that the buffer is a shm buffer the BufferInterface
provides a convenience method to turn it into a QImage.

The auto test for Surface is extended by attaching buffers to the
surface and verifying that the content is correct.
2014-09-02 09:52:16 +02:00

238 lines
6.8 KiB
C++

/********************************************************************
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_interface.h"
#include "buffer_interface.h"
#include "compositor_interface.h"
namespace KWin
{
namespace WaylandServer
{
const struct wl_surface_interface SurfaceInterface::s_interface = {
SurfaceInterface::destroyCallback,
SurfaceInterface::attachCallback,
SurfaceInterface::damageCallback,
SurfaceInterface::frameCallaback,
SurfaceInterface::opaqueRegionCallback,
SurfaceInterface::inputRegionCallback,
SurfaceInterface::commitCallback,
SurfaceInterface::bufferTransformCallback,
SurfaceInterface::bufferScaleCallback
};
SurfaceInterface::SurfaceInterface(CompositorInterface *parent)
: QObject(parent)
, m_compositor(parent)
, m_surface(nullptr)
, m_client(nullptr)
{
}
SurfaceInterface::~SurfaceInterface()
{
destroy();
}
void SurfaceInterface::create(wl_client *client, quint32 version, quint32 id)
{
Q_ASSERT(!m_surface);
Q_ASSERT(!m_client);
m_client = client;
m_surface = wl_resource_create(client, &wl_surface_interface, version, id);
if (!m_surface) {
return;
}
wl_resource_set_implementation(m_surface, &SurfaceInterface::s_interface, this, SurfaceInterface::unbind);
}
void SurfaceInterface::frameRendered(quint32 msec)
{
// notify all callbacks
while (!m_current.callbacks.isEmpty()) {
wl_resource *r = m_current.callbacks.takeFirst();
wl_callback_send_done(r, msec);
wl_resource_destroy(r);
}
}
void SurfaceInterface::unbind(wl_resource *r)
{
SurfaceInterface *s = SurfaceInterface::cast(r);
s->m_surface = nullptr;
s->deleteLater();
}
void SurfaceInterface::destroy()
{
for (wl_resource *c : m_current.callbacks) {
wl_resource_destroy(c);
}
for (wl_resource *c : m_pending.callbacks) {
wl_resource_destroy(c);
}
if (m_current.buffer) {
m_current.buffer->unref();
}
if (m_surface) {
wl_resource_destroy(m_surface);
m_surface = nullptr;
}
}
void SurfaceInterface::commit()
{
for (wl_resource *c : m_current.callbacks) {
wl_resource_destroy(c);
}
const bool opaqueRegionChanged = m_current.opaque != m_pending.opaque;
const bool inputRegionChanged = m_current.input != m_pending.input;
const bool scaleFactorChanged = m_current.scale != m_pending.scale;
const bool transformFactorChanged = m_current.transform != m_pending.transform;
if (m_current.buffer) {
m_current.buffer->unref();
}
if (m_pending.buffer) {
m_pending.buffer->ref();
}
// copy values
m_current = m_pending;
m_pending = State{};
if (opaqueRegionChanged) {
emit opaqueChanged(m_current.opaque);
}
if (inputRegionChanged) {
emit inputChanged(m_current.input);
}
if (scaleFactorChanged) {
emit scaleChanged(m_current.scale);
}
if (transformFactorChanged) {
emit transformChanged(m_current.transform);
}
if (!m_current.damage.isEmpty()) {
emit damaged(m_current.damage);
}
}
void SurfaceInterface::damage(const QRect &rect)
{
// TODO: documentation says we need to remove the parts outside of the surface
m_pending.damage = m_pending.damage.united(rect);
}
void SurfaceInterface::setScale(qint32 scale)
{
m_pending.scale = scale;
}
void SurfaceInterface::setTransform(OutputInterface::Transform transform)
{
m_pending.transform = transform;
}
void SurfaceInterface::addFrameCallback(uint32_t callback)
{
wl_resource *r = wl_resource_create(m_client, &wl_callback_interface, 1, callback);
if (!r) {
wl_resource_post_no_memory(m_surface);
return;
}
wl_resource_set_implementation(r, nullptr, this, destroyFrameCallback);
m_pending.callbacks << r;
}
void SurfaceInterface::attachBuffer(wl_resource *buffer, const QPoint &offset)
{
m_pending.offset = offset;
if (m_pending.buffer) {
delete m_pending.buffer;
}
m_pending.buffer = new BufferInterface(buffer, this);
}
void SurfaceInterface::destroyFrameCallback(wl_resource *r)
{
SurfaceInterface *s = SurfaceInterface::cast(r);
s->m_current.callbacks.removeAll(r);
s->m_pending.callbacks.removeAll(r);
}
void SurfaceInterface::destroyCallback(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client)
SurfaceInterface::cast(resource)->deleteLater();
}
void SurfaceInterface::attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy)
{
Q_UNUSED(client)
SurfaceInterface::cast(resource)->attachBuffer(buffer, QPoint(sx, sy));
}
void SurfaceInterface::damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
{
Q_UNUSED(client)
SurfaceInterface::cast(resource)->damage(QRect(x, y, width, height));
}
void SurfaceInterface::frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback)
{
SurfaceInterface *s = SurfaceInterface::cast(resource);
Q_ASSERT(client == s->m_client);
s->addFrameCallback(callback);
}
void SurfaceInterface::opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region)
{
Q_UNUSED(client)
Q_UNUSED(resource)
Q_UNUSED(region)
// TODO: implement me
}
void SurfaceInterface::inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region)
{
Q_UNUSED(client)
Q_UNUSED(resource)
Q_UNUSED(region)
// TODO: implement me
}
void SurfaceInterface::commitCallback(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client)
SurfaceInterface::cast(resource)->commit();
}
void SurfaceInterface::bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform)
{
Q_UNUSED(client)
SurfaceInterface::cast(resource)->setTransform(OutputInterface::Transform(transform));
}
void SurfaceInterface::bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale)
{
Q_UNUSED(client)
SurfaceInterface::cast(resource)->setScale(scale);
}
}
}