kwin/src/wayland/surface_interface.cpp
Vlad Zahorodnii 16db81b5cc Make RegionInterface private
If a Wayland protocol deals with regions, they will be exposed as
QRegion objects in public API. Therefore, it makes sense to make
RegionInterface private as it's an implementation detail and it's
not intended to be used in public api.

The corresponding test was dropped because the CompositorInterface
no longer emits a signal to indicate that a wl_region has been created.
It should be also noted that wl_region stuff is already tested via
other means, e.g. surface damage, etc.
2021-03-15 16:28:30 +00:00

1030 lines
32 KiB
C++

/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "surface_interface.h"
#include "surface_interface_p.h"
#include "buffer_interface.h"
#include "clientconnection.h"
#include "compositor_interface.h"
#include "display.h"
#include "idleinhibit_v1_interface_p.h"
#include "pointerconstraints_v1_interface_p.h"
#include "region_interface_p.h"
#include "subcompositor_interface.h"
#include "subsurface_interface_p.h"
#include "surfacerole_p.h"
#include "utils.h"
// std
#include <algorithm>
namespace KWaylandServer
{
QList<SurfaceInterface *> SurfaceInterfacePrivate::surfaces;
KWaylandFrameCallback::KWaylandFrameCallback(wl_resource *resource, SurfaceInterface *surface)
: QtWaylandServer::wl_callback(resource)
, surface(surface)
{
}
void KWaylandFrameCallback::destroy()
{
wl_resource_destroy(resource()->handle);
}
void KWaylandFrameCallback::callback_destroy_resource(Resource *)
{
if (surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->current.frameCallbacks.removeOne(this);
surfacePrivate->pending.frameCallbacks.removeOne(this);
surfacePrivate->cached.frameCallbacks.removeOne(this);
}
delete this;
}
SurfaceInterfacePrivate::SurfaceInterfacePrivate(SurfaceInterface *q)
: q(q)
{
surfaces.append(q);
}
SurfaceInterfacePrivate::~SurfaceInterfacePrivate()
{
// Need a copy to avoid hitting invalidated iterators in the for loop.
const QList<KWaylandFrameCallback *> currentFrameCallbacks = current.frameCallbacks;
for (KWaylandFrameCallback *frameCallback : currentFrameCallbacks) {
frameCallback->destroy();
}
const QList<KWaylandFrameCallback *> pendingFrameCallbacks = pending.frameCallbacks;
for (KWaylandFrameCallback *frameCallback : pendingFrameCallbacks) {
frameCallback->destroy();
}
const QList<KWaylandFrameCallback *> cachedFrameCallbacks = cached.frameCallbacks;
for (KWaylandFrameCallback *frameCallback : cachedFrameCallbacks) {
frameCallback->destroy();
}
if (current.buffer) {
current.buffer->unref();
}
surfaces.removeOne(q);
}
void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child)
{
// protocol is not precise on how to handle the addition of new sub surfaces
pending.children.append(child);
cached.children.append(child);
current.children.append(child);
child->surface()->setOutputs(outputs);
emit q->childSubSurfaceAdded(child);
emit q->subSurfaceTreeChanged();
QObject::connect(child, &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged);
QObject::connect(child->surface(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged);
QObject::connect(child->surface(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged);
QObject::connect(child->surface(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged);
}
void SurfaceInterfacePrivate::removeChild(SubSurfaceInterface *child)
{
// protocol is not precise on how to handle the addition of new sub surfaces
pending.children.removeAll(child);
cached.children.removeAll(child);
current.children.removeAll(child);
emit q->childSubSurfaceRemoved(child);
emit q->subSurfaceTreeChanged();
QObject::disconnect(child, &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged);
if (child->surface()) {
QObject::disconnect(child->surface(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged);
QObject::disconnect(child->surface(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged);
QObject::disconnect(child->surface(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged);
}
}
bool SurfaceInterfacePrivate::raiseChild(SubSurfaceInterface *subsurface, SurfaceInterface *sibling)
{
auto it = std::find(pending.children.begin(), pending.children.end(), subsurface);
if (it == pending.children.end()) {
return false;
}
if (pending.children.count() == 1) {
// nothing to do
return true;
}
if (sibling == q) {
// it's to the parent, so needs to become last item
pending.children.append(*it);
pending.children.erase(it);
pending.childrenChanged = true;
return true;
}
if (!sibling->subSurface()) {
// not a sub surface
return false;
}
auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
if (siblingIt == pending.children.end() || siblingIt == it) {
// not a sibling
return false;
}
auto value = (*it);
pending.children.erase(it);
// find the iterator again
siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
pending.children.insert(++siblingIt, value);
pending.childrenChanged = true;
return true;
}
bool SurfaceInterfacePrivate::lowerChild(SubSurfaceInterface *subsurface, SurfaceInterface *sibling)
{
auto it = std::find(pending.children.begin(), pending.children.end(), subsurface);
if (it == pending.children.end()) {
return false;
}
if (pending.children.count() == 1) {
// nothing to do
return true;
}
if (sibling == q) {
// it's to the parent, so needs to become first item
auto value = *it;
pending.children.erase(it);
pending.children.prepend(value);
pending.childrenChanged = true;
return true;
}
if (!sibling->subSurface()) {
// not a sub surface
return false;
}
auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
if (siblingIt == pending.children.end() || siblingIt == it) {
// not a sibling
return false;
}
auto value = (*it);
pending.children.erase(it);
// find the iterator again
siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
pending.children.insert(siblingIt, value);
pending.childrenChanged = true;
return true;
}
void SurfaceInterfacePrivate::setShadow(const QPointer<ShadowInterface> &shadow)
{
pending.shadow = shadow;
pending.shadowIsSet = true;
}
void SurfaceInterfacePrivate::setBlur(const QPointer<BlurInterface> &blur)
{
pending.blur = blur;
pending.blurIsSet = true;
}
void SurfaceInterfacePrivate::setSlide(const QPointer<SlideInterface> &slide)
{
pending.slide = slide;
pending.slideIsSet = true;
}
void SurfaceInterfacePrivate::setContrast(const QPointer<ContrastInterface> &contrast)
{
pending.contrast = contrast;
pending.contrastIsSet = true;
}
void SurfaceInterfacePrivate::installPointerConstraint(LockedPointerV1Interface *lock)
{
Q_ASSERT(!lockedPointer);
Q_ASSERT(!confinedPointer);
lockedPointer = lock;
auto cleanUp = [this]() {
lockedPointer = nullptr;
QObject::disconnect(constrainsOneShotConnection);
constrainsOneShotConnection = QMetaObject::Connection();
QObject::disconnect(constrainsUnboundConnection);
constrainsUnboundConnection = QMetaObject::Connection();
emit q->pointerConstraintsChanged();
};
if (lock->lifeTime() == LockedPointerV1Interface::LifeTime::OneShot) {
constrainsOneShotConnection = QObject::connect(lock, &LockedPointerV1Interface::lockedChanged, q,
[this, cleanUp] {
if (lockedPointer->isLocked()) {
return;
}
cleanUp();
}
);
}
constrainsUnboundConnection = QObject::connect(lock, &LockedPointerV1Interface::destroyed, q, cleanUp);
emit q->pointerConstraintsChanged();
}
void SurfaceInterfacePrivate::installPointerConstraint(ConfinedPointerV1Interface *confinement)
{
Q_ASSERT(!lockedPointer);
Q_ASSERT(!confinedPointer);
confinedPointer = confinement;
auto cleanUp = [this]() {
confinedPointer = nullptr;
QObject::disconnect(constrainsOneShotConnection);
constrainsOneShotConnection = QMetaObject::Connection();
QObject::disconnect(constrainsUnboundConnection);
constrainsUnboundConnection = QMetaObject::Connection();
emit q->pointerConstraintsChanged();
};
if (confinement->lifeTime() == ConfinedPointerV1Interface::LifeTime::OneShot) {
constrainsOneShotConnection = QObject::connect(confinement, &ConfinedPointerV1Interface::confinedChanged, q,
[this, cleanUp] {
if (confinedPointer->isConfined()) {
return;
}
cleanUp();
}
);
}
constrainsUnboundConnection = QObject::connect(confinement, &ConfinedPointerV1Interface::destroyed, q, cleanUp);
emit q->pointerConstraintsChanged();
}
void SurfaceInterfacePrivate::installIdleInhibitor(IdleInhibitorV1Interface *inhibitor)
{
idleInhibitors << inhibitor;
QObject::connect(inhibitor, &IdleInhibitorV1Interface::destroyed, q,
[this, inhibitor] {
idleInhibitors.removeOne(inhibitor);
if (idleInhibitors.isEmpty()) {
emit q->inhibitsIdleChanged();
}
}
);
if (idleInhibitors.count() == 1) {
emit q->inhibitsIdleChanged();
}
}
void SurfaceInterfacePrivate::surface_destroy_resource(Resource *)
{
emit q->aboutToBeDestroyed();
delete q;
}
void SurfaceInterfacePrivate::surface_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void SurfaceInterfacePrivate::surface_attach(Resource *resource, struct ::wl_resource *buffer, int32_t x, int32_t y)
{
Q_UNUSED(resource)
pending.bufferIsSet = true;
pending.offset = QPoint(x, y);
if (!buffer) {
// got a null buffer, deletes content in next frame
pending.buffer = nullptr;
pending.damage = QRegion();
pending.bufferDamage = QRegion();
return;
}
pending.buffer = BufferInterface::get(compositor->display(), buffer);
QObject::connect(pending.buffer, &BufferInterface::aboutToBeDestroyed, q, &SurfaceInterface::handleBufferRemoved, Qt::UniqueConnection);
}
void SurfaceInterfacePrivate::surface_damage(Resource *, int32_t x, int32_t y, int32_t width, int32_t height)
{
pending.damage |= QRect(x, y, width, height);
}
void SurfaceInterfacePrivate::surface_frame(Resource *resource, uint32_t callback)
{
wl_resource *callbackResource = wl_resource_create(resource->client(), &wl_callback_interface,
/* version */ 1, callback);
if (!callbackResource) {
wl_resource_post_no_memory(resource->handle);
return;
}
pending.frameCallbacks.append(new KWaylandFrameCallback(callbackResource, q));
}
void SurfaceInterfacePrivate::surface_set_opaque_region(Resource *resource, struct ::wl_resource *region)
{
Q_UNUSED(resource)
RegionInterface *r = RegionInterface::get(region);
pending.opaque = r ? r->region() : QRegion();
pending.opaqueIsSet = true;
}
void SurfaceInterfacePrivate::surface_set_input_region(Resource *resource, struct ::wl_resource *region)
{
Q_UNUSED(resource)
RegionInterface *r = RegionInterface::get(region);
pending.input = r ? r->region() : infiniteRegion();
pending.inputIsSet = true;
}
void SurfaceInterfacePrivate::surface_commit(Resource *resource)
{
Q_UNUSED(resource)
commit();
}
void SurfaceInterfacePrivate::surface_set_buffer_transform(Resource *resource, int32_t transform)
{
if (transform < 0 || transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) {
wl_resource_post_error(resource->handle, error_invalid_transform,
"buffer transform must be a valid transform (%d specified)", transform);
return;
}
pending.bufferTransform = OutputInterface::Transform(transform);
pending.bufferTransformIsSet = true;
}
void SurfaceInterfacePrivate::surface_set_buffer_scale(Resource *resource, int32_t scale)
{
if (scale < 1) {
wl_resource_post_error(resource->handle, error_invalid_scale,
"buffer scale must be at least one (%d specified)", scale);
return;
}
pending.bufferScale = scale;
pending.bufferScaleIsSet = true;
}
void SurfaceInterfacePrivate::surface_damage_buffer(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
{
Q_UNUSED(resource)
pending.bufferDamage |= QRect(x, y, width, height);
}
SurfaceInterface::SurfaceInterface(CompositorInterface *compositor, wl_resource *resource)
: QObject(compositor)
, d(new SurfaceInterfacePrivate(this))
{
d->compositor = compositor;
d->init(resource);
d->client = compositor->display()->getConnection(d->resource()->client());
}
SurfaceInterface::~SurfaceInterface()
{
}
uint32_t SurfaceInterface::id() const
{
return wl_resource_get_id(resource());
}
ClientConnection *SurfaceInterface::client() const
{
return d->client;
}
wl_resource *SurfaceInterface::resource() const
{
return d->resource()->handle;
}
CompositorInterface *SurfaceInterface::compositor() const
{
return d->compositor;
}
QList<SurfaceInterface *> SurfaceInterface::surfaces()
{
return SurfaceInterfacePrivate::surfaces;
}
void SurfaceInterface::frameRendered(quint32 msec)
{
// notify all callbacks
const bool needsFlush = !d->current.frameCallbacks.isEmpty();
while (!d->current.frameCallbacks.isEmpty()) {
KWaylandFrameCallback *frameCallback = d->current.frameCallbacks.takeFirst();
frameCallback->send_done(msec);
frameCallback->destroy();
}
for (auto it = d->current.children.constBegin(); it != d->current.children.constEnd(); ++it) {
(*it)->surface()->frameRendered(msec);
}
if (needsFlush) {
client()->flush();
}
}
bool SurfaceInterface::hasFrameCallbacks() const
{
return !d->current.frameCallbacks.isEmpty();
}
QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix(const State *state)
{
// The order of transforms is reversed, i.e. the viewport transform is the first one.
QMatrix4x4 surfaceToBufferMatrix;
if (!state->buffer) {
return surfaceToBufferMatrix;
}
surfaceToBufferMatrix.scale(state->bufferScale, state->bufferScale);
switch (state->bufferTransform) {
case OutputInterface::Transform::Normal:
case OutputInterface::Transform::Flipped:
break;
case OutputInterface::Transform::Rotated90:
case OutputInterface::Transform::Flipped90:
surfaceToBufferMatrix.translate(0, state->buffer->height() / state->bufferScale);
surfaceToBufferMatrix.rotate(-90, 0, 0, 1);
break;
case OutputInterface::Transform::Rotated180:
case OutputInterface::Transform::Flipped180:
surfaceToBufferMatrix.translate(state->buffer->width() / state->bufferScale,
state->buffer->height() / state->bufferScale);
surfaceToBufferMatrix.rotate(-180, 0, 0, 1);
break;
case OutputInterface::Transform::Rotated270:
case OutputInterface::Transform::Flipped270:
surfaceToBufferMatrix.translate(state->buffer->width() / state->bufferScale, 0);
surfaceToBufferMatrix.rotate(-270, 0, 0, 1);
break;
}
switch (state->bufferTransform) {
case OutputInterface::Transform::Flipped:
case OutputInterface::Transform::Flipped180:
surfaceToBufferMatrix.translate(state->buffer->width() / state->bufferScale, 0);
surfaceToBufferMatrix.scale(-1, 1);
break;
case OutputInterface::Transform::Flipped90:
case OutputInterface::Transform::Flipped270:
surfaceToBufferMatrix.translate(state->buffer->height() / state->bufferScale, 0);
surfaceToBufferMatrix.scale(-1, 1);
break;
default:
break;
}
if (state->sourceGeometry.isValid()) {
surfaceToBufferMatrix.translate(state->sourceGeometry.x(), state->sourceGeometry.y());
surfaceToBufferMatrix.scale(state->sourceGeometry.width() / state->size.width(),
state->sourceGeometry.height() / state->size.height());
}
return surfaceToBufferMatrix;
}
void SurfaceInterfacePrivate::swapStates(State *source, State *target, bool emitChanged)
{
const bool bufferChanged = source->bufferIsSet;
const bool opaqueRegionChanged = source->opaqueIsSet;
const bool inputRegionChanged = source->inputIsSet;
const bool scaleFactorChanged = source->bufferScaleIsSet && (target->bufferScale != source->bufferScale);
const bool transformChanged = source->bufferTransformIsSet && (target->bufferTransform != source->bufferTransform);
const bool shadowChanged = source->shadowIsSet;
const bool blurChanged = source->blurIsSet;
const bool contrastChanged = source->contrastIsSet;
const bool slideChanged = source->slideIsSet;
const bool childrenChanged = source->childrenChanged;
const bool visibilityChanged = bufferChanged && (bool(source->buffer) != bool(target->buffer));
const QSize oldSize = target->size;
const QSize oldBufferSize = bufferSize;
const QMatrix4x4 oldSurfaceToBufferMatrix = surfaceToBufferMatrix;
const QRegion oldInputRegion = inputRegion;
if (bufferChanged) {
// TODO: is the reffing correct for subsurfaces?
if (target->buffer) {
if (emitChanged) {
target->buffer->unref();
} else {
target->buffer = nullptr;
}
}
if (source->buffer) {
if (emitChanged) {
source->buffer->ref();
}
}
target->buffer = source->buffer;
target->offset = source->offset;
target->damage = source->damage;
target->bufferDamage = source->bufferDamage;
target->bufferIsSet = source->bufferIsSet;
}
if (source->sourceGeometryIsSet) {
target->sourceGeometry = source->sourceGeometry;
target->sourceGeometryIsSet = true;
}
if (source->destinationSizeIsSet) {
target->destinationSize = source->destinationSize;
target->destinationSizeIsSet = true;
}
if (childrenChanged) {
target->childrenChanged = source->childrenChanged;
target->children = source->children;
}
target->frameCallbacks.append(source->frameCallbacks);
if (shadowChanged) {
target->shadow = source->shadow;
target->shadowIsSet = true;
}
if (blurChanged) {
target->blur = source->blur;
target->blurIsSet = true;
}
if (contrastChanged) {
target->contrast = source->contrast;
target->contrastIsSet = true;
}
if (slideChanged) {
target->slide = source->slide;
target->slideIsSet = true;
}
if (inputRegionChanged) {
target->input = source->input;
target->inputIsSet = true;
}
if (opaqueRegionChanged) {
target->opaque = source->opaque;
target->opaqueIsSet = true;
}
if (scaleFactorChanged) {
target->bufferScale = source->bufferScale;
target->bufferScaleIsSet = true;
}
if (transformChanged) {
target->bufferTransform = source->bufferTransform;
target->bufferTransformIsSet = true;
}
if (lockedPointer) {
auto lockedPointerPrivate = LockedPointerV1InterfacePrivate::get(lockedPointer);
lockedPointerPrivate->commit();
}
if (confinedPointer) {
auto confinedPointerPrivate = ConfinedPointerV1InterfacePrivate::get(confinedPointer);
confinedPointerPrivate->commit();
}
*source = State{};
source->children = target->children;
if (!emitChanged) {
return;
}
// TODO: Refactor the state management code because it gets more clumsy.
if (target->buffer) {
bufferSize = target->buffer->size();
if (target->destinationSize.isValid()) {
target->size = target->destinationSize;
} else if (target->sourceGeometry.isValid()) {
target->size = target->sourceGeometry.size().toSize();
} else {
target->size = target->buffer->size() / target->bufferScale;
switch (target->bufferTransform) {
case OutputInterface::Transform::Rotated90:
case OutputInterface::Transform::Rotated270:
case OutputInterface::Transform::Flipped90:
case OutputInterface::Transform::Flipped270:
target->size.transpose();
break;
case OutputInterface::Transform::Normal:
case OutputInterface::Transform::Rotated180:
case OutputInterface::Transform::Flipped:
case OutputInterface::Transform::Flipped180:
break;
}
}
} else {
target->size = QSize();
bufferSize = QSize();
}
surfaceToBufferMatrix = buildSurfaceToBufferMatrix(target);
bufferToSurfaceMatrix = surfaceToBufferMatrix.inverted();
inputRegion = target->input & QRect(QPoint(0, 0), target->size);
if (opaqueRegionChanged) {
emit q->opaqueChanged(target->opaque);
}
if (oldInputRegion != inputRegion) {
emit q->inputChanged(inputRegion);
}
if (scaleFactorChanged) {
emit q->bufferScaleChanged(target->bufferScale);
}
if (transformChanged) {
emit q->bufferTransformChanged(target->bufferTransform);
}
if (visibilityChanged) {
if (target->buffer) {
subSurfaceIsMapped = true;
emit q->mapped();
} else {
subSurfaceIsMapped = false;
emit q->unmapped();
}
}
if (bufferChanged) {
if (target->buffer && (!target->damage.isEmpty() || !target->bufferDamage.isEmpty())) {
const QRegion windowRegion = QRegion(0, 0, q->size().width(), q->size().height());
const QRegion bufferDamage = q->mapFromBuffer(target->bufferDamage);
target->damage = windowRegion.intersected(target->damage.united(bufferDamage));
trackedDamage |= target->damage;
emit q->damaged(target->damage);
// workaround for https://bugreports.qt.io/browse/QTBUG-52092
// if the surface is a sub-surface, but the main surface is not yet mapped, fake frame rendered
if (subSurface) {
const auto mainSurface = subSurface->mainSurface();
if (!mainSurface || !mainSurface->buffer()) {
q->frameRendered(0);
}
}
}
}
if (surfaceToBufferMatrix != oldSurfaceToBufferMatrix) {
emit q->surfaceToBufferMatrixChanged();
}
if (bufferSize != oldBufferSize) {
emit q->bufferSizeChanged();
}
if (target->size != oldSize) {
emit q->sizeChanged();
}
if (shadowChanged) {
emit q->shadowChanged();
}
if (blurChanged) {
emit q->blurChanged();
}
if (contrastChanged) {
emit q->contrastChanged();
}
if (slideChanged) {
emit q->slideOnShowHideChanged();
}
if (childrenChanged) {
emit q->subSurfaceTreeChanged();
}
}
void SurfaceInterfacePrivate::commit()
{
if (!subSurface) {
swapStates(&pending, &current, true);
// The position of a sub-surface is applied when its parent is committed.
const QList<SubSurfaceInterface *> children = current.children;
for (SubSurfaceInterface *subsurface : children) {
auto subsurfacePrivate = SubSurfaceInterfacePrivate::get(subsurface);
subsurfacePrivate->parentCommit();
}
}
if (role) {
role->commit();
}
emit q->committed();
}
QRegion SurfaceInterface::damage() const
{
return d->current.damage;
}
QRegion SurfaceInterface::opaque() const
{
return d->current.opaque;
}
QRegion SurfaceInterface::input() const
{
return d->inputRegion;
}
qint32 SurfaceInterface::bufferScale() const
{
return d->current.bufferScale;
}
OutputInterface::Transform SurfaceInterface::bufferTransform() const
{
return d->current.bufferTransform;
}
BufferInterface *SurfaceInterface::buffer()
{
return d->current.buffer;
}
QPoint SurfaceInterface::offset() const
{
return d->current.offset;
}
SurfaceInterface *SurfaceInterface::get(wl_resource *native)
{
if (auto surfacePrivate = resource_cast<SurfaceInterfacePrivate *>(native)) {
return surfacePrivate->q;
}
return nullptr;
}
SurfaceInterface *SurfaceInterface::get(quint32 id, const ClientConnection *client)
{
const QList<SurfaceInterface *> candidates = surfaces();
for (SurfaceInterface *surface : candidates) {
if (surface->client() == client && surface->id() == id) {
return surface;
}
}
return nullptr;
}
QList<SubSurfaceInterface *> SurfaceInterface::childSubSurfaces() const
{
return d->current.children;
}
SubSurfaceInterface *SurfaceInterface::subSurface() const
{
return d->subSurface;
}
QSize SurfaceInterface::size() const
{
return d->current.size;
}
QRect SurfaceInterface::boundingRect() const
{
QRect rect(QPoint(0, 0), size());
const QList<SubSurfaceInterface *> subSurfaces = childSubSurfaces();
for (const SubSurfaceInterface *subSurface : subSurfaces) {
const SurfaceInterface *childSurface = subSurface->surface();
rect |= childSurface->boundingRect().translated(subSurface->position());
}
return rect;
}
QPointer< ShadowInterface > SurfaceInterface::shadow() const
{
return d->current.shadow;
}
QPointer< BlurInterface > SurfaceInterface::blur() const
{
return d->current.blur;
}
QPointer< ContrastInterface > SurfaceInterface::contrast() const
{
return d->current.contrast;
}
QPointer< SlideInterface > SurfaceInterface::slideOnShowHide() const
{
return d->current.slide;
}
bool SurfaceInterface::isMapped() const
{
if (d->subSurface) {
// from spec:
// "A sub-surface becomes mapped, when a non-NULL wl_buffer is applied and the parent surface is mapped."
return d->subSurfaceIsMapped && d->subSurface->parentSurface() && d->subSurface->parentSurface()->isMapped();
}
return d->current.buffer != nullptr;
}
QRegion SurfaceInterface::trackedDamage() const
{
return d->trackedDamage;
}
void SurfaceInterface::resetTrackedDamage()
{
d->trackedDamage = QRegion();
}
QVector<OutputInterface *> SurfaceInterface::outputs() const
{
return d->outputs;
}
void SurfaceInterface::setOutputs(const QVector<OutputInterface *> &outputs)
{
QVector<OutputInterface *> removedOutputs = d->outputs;
for (auto it = outputs.constBegin(), end = outputs.constEnd(); it != end; ++it) {
const auto o = *it;
removedOutputs.removeOne(o);
}
for (auto it = removedOutputs.constBegin(), end = removedOutputs.constEnd(); it != end; ++it) {
const auto resources = (*it)->clientResources(client());
for (wl_resource *outputResource : resources) {
d->send_leave(outputResource);
}
disconnect(d->outputDestroyedConnections.take(*it));
disconnect(d->outputBoundConnections.take(*it));
}
QVector<OutputInterface *> addedOutputsOutputs = outputs;
for (auto it = d->outputs.constBegin(), end = d->outputs.constEnd(); it != end; ++it) {
const auto o = *it;
addedOutputsOutputs.removeOne(o);
}
for (auto it = addedOutputsOutputs.constBegin(), end = addedOutputsOutputs.constEnd(); it != end; ++it) {
const auto o = *it;
const auto resources = o->clientResources(client());
for (wl_resource *outputResource : resources) {
d->send_enter(outputResource);
}
d->outputDestroyedConnections[o] = connect(o, &Global::aboutToDestroyGlobal, this, [this, o] {
auto outputs = d->outputs;
if (outputs.removeOne(o)) {
setOutputs(outputs);
}});
Q_ASSERT(!d->outputBoundConnections.contains(o));
d->outputBoundConnections[o] = connect(o, &OutputInterface::bound, this, [this](ClientConnection *c, wl_resource *outputResource) {
if (c != client()) {
return;
}
d->send_enter(outputResource);
});
}
d->outputs = outputs;
for (auto child : d->current.children) {
child->surface()->setOutputs(outputs);
}
}
SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position)
{
if (!isMapped()) {
return nullptr;
}
// go from top to bottom. Top most child is last in list
QListIterator<SubSurfaceInterface *> it(d->current.children);
it.toBack();
while (it.hasPrevious()) {
const auto &current = it.previous();
auto surface = current->surface();
if (auto s = surface->surfaceAt(position - current->position())) {
return s;
}
}
// check whether the geometry contains the pos
if (!size().isEmpty() && QRectF(QPoint(0, 0), size()).contains(position)) {
return this;
}
return nullptr;
}
SurfaceInterface *SurfaceInterface::inputSurfaceAt(const QPointF &position)
{
// TODO: Most of this is very similar to SurfaceInterface::surfaceAt
// Is there a way to reduce the code duplication?
if (!isMapped()) {
return nullptr;
}
// go from top to bottom. Top most child is last in list
QListIterator<SubSurfaceInterface *> it(d->current.children);
it.toBack();
while (it.hasPrevious()) {
const auto &current = it.previous();
auto surface = current->surface();
if (auto s = surface->inputSurfaceAt(position - current->position())) {
return s;
}
}
// check whether the geometry and input region contain the pos
if (!size().isEmpty() && QRectF(QPoint(0, 0), size()).contains(position) &&
input().contains(position.toPoint())) {
return this;
}
return nullptr;
}
LockedPointerV1Interface *SurfaceInterface::lockedPointer() const
{
return d->lockedPointer;
}
ConfinedPointerV1Interface *SurfaceInterface::confinedPointer() const
{
return d->confinedPointer;
}
bool SurfaceInterface::inhibitsIdle() const
{
return !d->idleInhibitors.isEmpty();
}
void SurfaceInterface::setDataProxy(SurfaceInterface *surface)
{
d->dataProxy = surface;
}
SurfaceInterface* SurfaceInterface::dataProxy() const
{
return d->dataProxy;
}
QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const
{
return d->surfaceToBufferMatrix.map(point);
}
QPointF SurfaceInterface::mapFromBuffer(const QPointF &point) const
{
return d->bufferToSurfaceMatrix.map(point);
}
static QRegion map_helper(const QMatrix4x4 &matrix, const QRegion &region)
{
QRegion result;
for (const QRect &rect : region) {
result += matrix.mapRect(rect);
}
return result;
}
QRegion SurfaceInterface::mapToBuffer(const QRegion &region) const
{
return map_helper(d->surfaceToBufferMatrix, region);
}
QRegion SurfaceInterface::mapFromBuffer(const QRegion &region) const
{
return map_helper(d->bufferToSurfaceMatrix, region);
}
QMatrix4x4 SurfaceInterface::surfaceToBufferMatrix() const
{
return d->surfaceToBufferMatrix;
}
void SurfaceInterface::handleBufferRemoved(BufferInterface *buffer)
{
if (d->pending.buffer == buffer) {
d->pending.buffer = nullptr;
}
if (d->cached.buffer == buffer) {
d->cached.buffer = nullptr;
}
if (d->current.buffer == buffer) {
d->current.buffer->unref();
d->current.buffer = nullptr;
}
}
QPointF SurfaceInterface::mapToChild(SurfaceInterface *child, const QPointF &point) const
{
QPointF local = point;
SurfaceInterface *surface = child;
while (true) {
if (surface == this) {
return local;
}
SubSurfaceInterface *subsurface = surface->subSurface();
if (Q_UNLIKELY(!subsurface)) {
return QPointF();
}
local -= subsurface->position();
surface = subsurface->parentSurface();
}
return QPointF();
}
QSize SurfaceInterface::bufferSize() const
{
return d->bufferSize;
}
} // namespace KWaylandServer