From dcadf24e64d5a06dc078941f6ae04b9616e22328 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 16 May 2022 20:03:52 +0300 Subject: [PATCH] wayland: Introduce surface state locking capabilities In certain cases it can be useful to delay applying a surface commit. As an example, the compositor may wait until the committed graphics buffer is ready for reading to avoid stalling its graphics pipeline. This change introduces basic surface state locking capabilities and ports some surface extensions to new helpers to manage associated state. --- src/wayland/contenttype_v1_interface.cpp | 8 +- src/wayland/datadevice_interface.cpp | 23 +- src/wayland/datadevice_interface.h | 2 + src/wayland/inputmethod_v1_interface.cpp | 4 - src/wayland/layershell_v1_interface.cpp | 176 +++++---- .../pointerconstraints_v1_interface.cpp | 56 +-- .../pointerconstraints_v1_interface_p.h | 30 +- src/wayland/subcompositor_interface.cpp | 42 +- src/wayland/subcompositor_interface.h | 4 + src/wayland/subsurface_interface_p.h | 10 +- src/wayland/surface_interface.cpp | 358 +++++++++--------- src/wayland/surface_interface.h | 55 ++- src/wayland/surface_interface_p.h | 20 +- src/wayland/surfacerole_p.h | 2 - src/wayland/tearingcontrol_v1_interface.cpp | 10 +- src/wayland/viewporter_interface.cpp | 24 +- src/wayland/xdgshell_interface.cpp | 71 ++-- src/wayland/xdgshell_interface_p.h | 46 +-- src/wayland/xwaylandshell_v1_interface.cpp | 25 +- 19 files changed, 548 insertions(+), 418 deletions(-) diff --git a/src/wayland/contenttype_v1_interface.cpp b/src/wayland/contenttype_v1_interface.cpp index b0a3028460..6ef7fbff7f 100644 --- a/src/wayland/contenttype_v1_interface.cpp +++ b/src/wayland/contenttype_v1_interface.cpp @@ -62,16 +62,16 @@ void ContentTypeV1Interface::wp_content_type_v1_set_content_type(Resource *, uin return; } SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); - surfacePrivate->pending.contentType = waylandToKwinContentType(content_type); - surfacePrivate->pending.contentTypeIsSet = true; + surfacePrivate->pending->contentType = waylandToKwinContentType(content_type); + surfacePrivate->pending->contentTypeIsSet = true; } void ContentTypeV1Interface::wp_content_type_v1_destroy(Resource *resource) { if (m_surface) { SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); - surfacePrivate->pending.contentType = KWin::ContentType::None; - surfacePrivate->pending.contentTypeIsSet = true; + surfacePrivate->pending->contentType = KWin::ContentType::None; + surfacePrivate->pending->contentTypeIsSet = true; } wl_resource_destroy(resource->handle); } diff --git a/src/wayland/datadevice_interface.cpp b/src/wayland/datadevice_interface.cpp index 5ac9f2a6b1..e38f23b88a 100644 --- a/src/wayland/datadevice_interface.cpp +++ b/src/wayland/datadevice_interface.cpp @@ -22,36 +22,33 @@ namespace KWaylandServer class DragAndDropIconPrivate : public SurfaceRole { public: - explicit DragAndDropIconPrivate(DragAndDropIcon *q, SurfaceInterface *surface); + explicit DragAndDropIconPrivate(SurfaceInterface *surface); - void commit() override; - - DragAndDropIcon *q; QPoint position; }; -DragAndDropIconPrivate::DragAndDropIconPrivate(DragAndDropIcon *q, SurfaceInterface *surface) +DragAndDropIconPrivate::DragAndDropIconPrivate(SurfaceInterface *surface) : SurfaceRole(surface, QByteArrayLiteral("dnd_icon")) - , q(q) { } -void DragAndDropIconPrivate::commit() -{ - position += surface()->offset(); - Q_EMIT q->changed(); -} - DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface) : QObject(surface) - , d(new DragAndDropIconPrivate(this, surface)) + , d(new DragAndDropIconPrivate(surface)) { + connect(surface, &SurfaceInterface::committed, this, &DragAndDropIcon::commit); } DragAndDropIcon::~DragAndDropIcon() { } +void DragAndDropIcon::commit() +{ + d->position += d->surface()->offset(); + Q_EMIT changed(); +} + QPoint DragAndDropIcon::position() const { return d->position; diff --git a/src/wayland/datadevice_interface.h b/src/wayland/datadevice_interface.h index 4d9c973025..5c254e3d2c 100644 --- a/src/wayland/datadevice_interface.h +++ b/src/wayland/datadevice_interface.h @@ -55,6 +55,8 @@ Q_SIGNALS: void changed(); private: + void commit(); + explicit DragAndDropIcon(SurfaceInterface *surface); friend class DataDeviceInterfacePrivate; std::unique_ptr d; diff --git a/src/wayland/inputmethod_v1_interface.cpp b/src/wayland/inputmethod_v1_interface.cpp index 7782771eb8..19b1f57e83 100644 --- a/src/wayland/inputmethod_v1_interface.cpp +++ b/src/wayland/inputmethod_v1_interface.cpp @@ -327,10 +327,6 @@ public: Q_EMIT q->topLevel(OutputInterface::get(output), InputPanelSurfaceV1Interface::Position(position)); } - void commit() override - { - } - void zwp_input_panel_surface_v1_destroy_resource(Resource *) override { delete q; diff --git a/src/wayland/layershell_v1_interface.cpp b/src/wayland/layershell_v1_interface.cpp index 96ae1085e7..4e362e4be9 100644 --- a/src/wayland/layershell_v1_interface.cpp +++ b/src/wayland/layershell_v1_interface.cpp @@ -39,38 +39,45 @@ protected: void zwlr_layer_shell_v1_destroy(Resource *resource) override; }; -class LayerSurfaceV1State +struct LayerSurfaceV1Commit { -public: + std::optional layer; + std::optional anchor; + std::optional margins; + std::optional desiredSize; + std::optional exclusiveZone; + std::optional acknowledgedConfigure; + std::optional acceptsFocus; +}; + +struct LayerSurfaceV1State +{ + QQueue serials; LayerSurfaceV1Interface::Layer layer = LayerSurfaceV1Interface::BottomLayer; Qt::Edges anchor; QMargins margins; QSize desiredSize = QSize(0, 0); int exclusiveZone = 0; - quint32 acknowledgedConfigure; - bool acknowledgedConfigureIsSet = false; bool acceptsFocus = false; + bool configured = false; + bool closed = false; + bool committed = false; + bool firstBufferAttached = false; }; -class LayerSurfaceV1InterfacePrivate : public SurfaceRole, public QtWaylandServer::zwlr_layer_surface_v1 +class LayerSurfaceV1InterfacePrivate : public SurfaceRole, public SurfaceExtension, public QtWaylandServer::zwlr_layer_surface_v1 { public: LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface); - void commit() override; + void apply(LayerSurfaceV1Commit *commit) override; LayerSurfaceV1Interface *q; LayerShellV1Interface *shell; QPointer surface; QPointer output; - LayerSurfaceV1State current; - LayerSurfaceV1State pending; - QQueue serials; QString scope; - bool isClosed = false; - bool isConfigured = false; - bool isCommitted = false; - bool firstBufferAttached = false; + LayerSurfaceV1State state; protected: void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override; @@ -150,6 +157,7 @@ Display *LayerShellV1Interface::display() const LayerSurfaceV1InterfacePrivate::LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface) : SurfaceRole(surface, QByteArrayLiteral("layer_surface_v1")) + , SurfaceExtension(surface) , q(q) , surface(surface) { @@ -177,19 +185,19 @@ void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_anchor(Resource * pending.anchor = Qt::Edges(); if (anchor & anchor_top) { - pending.anchor |= Qt::TopEdge; + *pending.anchor |= Qt::TopEdge; } if (anchor & anchor_right) { - pending.anchor |= Qt::RightEdge; + *pending.anchor |= Qt::RightEdge; } if (anchor & anchor_bottom) { - pending.anchor |= Qt::BottomEdge; + *pending.anchor |= Qt::BottomEdge; } if (anchor & anchor_left) { - pending.anchor |= Qt::LeftEdge; + *pending.anchor |= Qt::LeftEdge; } } @@ -223,19 +231,18 @@ void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_get_popup(Resource *r void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial) { - if (!serials.contains(serial)) { + if (!state.serials.contains(serial)) { wl_resource_post_error(resource->handle, error_invalid_surface_state, "invalid configure serial %d", serial); return; } - while (!serials.isEmpty()) { - const quint32 head = serials.takeFirst(); + while (!state.serials.isEmpty()) { + const quint32 head = state.serials.takeFirst(); if (head == serial) { break; } } - if (!isClosed) { + if (!state.closed) { pending.acknowledgedConfigure = serial; - pending.acknowledgedConfigureIsSet = true; } } @@ -253,19 +260,17 @@ void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_layer(Resource *r pending.layer = LayerSurfaceV1Interface::Layer(layer); } -void LayerSurfaceV1InterfacePrivate::commit() +void LayerSurfaceV1InterfacePrivate::apply(LayerSurfaceV1Commit *commit) { - if (isClosed) { + if (state.closed) { return; } - if (pending.acknowledgedConfigureIsSet) { - current.acknowledgedConfigure = pending.acknowledgedConfigure; - pending.acknowledgedConfigureIsSet = false; - Q_EMIT q->configureAcknowledged(pending.acknowledgedConfigure); + if (commit->acknowledgedConfigure.has_value()) { + Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value()); } - if (Q_UNLIKELY(surface->isMapped() && !isConfigured)) { + if (Q_UNLIKELY(surface->isMapped() && !state.configured)) { wl_resource_post_error(resource()->handle, error_invalid_surface_state, "a buffer has been attached to a layer surface prior " @@ -273,57 +278,79 @@ void LayerSurfaceV1InterfacePrivate::commit() return; } - if (Q_UNLIKELY(pending.desiredSize.width() == 0 && (!(pending.anchor & Qt::LeftEdge) || !(pending.anchor & Qt::RightEdge)))) { - wl_resource_post_error(resource()->handle, - error_invalid_size, - "the layer surface has a width of 0 but its anchor " - "doesn't include the left and the right screen edge"); - return; + if (commit->desiredSize && commit->desiredSize->width() == 0) { + const Qt::Edges anchor = commit->anchor.value_or(state.anchor); + if (!(anchor & Qt::LeftEdge) || !(anchor & Qt::RightEdge)) { + wl_resource_post_error(resource()->handle, + error_invalid_size, + "the layer surface has a width of 0 but its anchor " + "doesn't include the left and the right screen edge"); + return; + } } - if (Q_UNLIKELY(pending.desiredSize.height() == 0 && (!(pending.anchor & Qt::TopEdge) || !(pending.anchor & Qt::BottomEdge)))) { - wl_resource_post_error(resource()->handle, - error_invalid_size, - "the layer surface has a height of 0 but its anchor " - "doesn't include the top and the bottom screen edge"); - return; + if (commit->desiredSize && commit->desiredSize->height() == 0) { + const Qt::Edges anchor = commit->anchor.value_or(state.anchor); + if (!(anchor & Qt::TopEdge) || !(anchor & Qt::BottomEdge)) { + wl_resource_post_error(resource()->handle, + error_invalid_size, + "the layer surface has a height of 0 but its anchor " + "doesn't include the top and the bottom screen edge"); + return; + } } // detect reset - if (!surface->isMapped() && firstBufferAttached) { - isCommitted = false; - firstBufferAttached = false; - isConfigured = false; - - current = LayerSurfaceV1State(); - pending = LayerSurfaceV1State(); + if (!surface->isMapped() && state.firstBufferAttached) { + state = LayerSurfaceV1State(); + pending = LayerSurfaceV1Commit(); + stashed.clear(); return; } - const LayerSurfaceV1State previous = std::exchange(current, pending); + const LayerSurfaceV1State previous = state; - isCommitted = true; // Must set the committed state before emitting any signals. + state.committed = true; // Must set the committed state before emitting any signals. if (surface->isMapped()) { - firstBufferAttached = true; + state.firstBufferAttached = true; } - if (previous.acceptsFocus != current.acceptsFocus) { + if (commit->layer.has_value()) { + state.layer = commit->layer.value(); + } + if (commit->anchor.has_value()) { + state.anchor = commit->anchor.value(); + } + if (commit->margins.has_value()) { + state.margins = commit->margins.value(); + } + if (commit->desiredSize.has_value()) { + state.desiredSize = commit->desiredSize.value(); + } + if (commit->exclusiveZone.has_value()) { + state.exclusiveZone = commit->exclusiveZone.value(); + } + if (commit->acceptsFocus.has_value()) { + state.acceptsFocus = commit->acceptsFocus.value(); + } + + if (previous.acceptsFocus != state.acceptsFocus) { Q_EMIT q->acceptsFocusChanged(); } - if (previous.layer != current.layer) { + if (previous.layer != state.layer) { Q_EMIT q->layerChanged(); } - if (previous.anchor != current.anchor) { + if (previous.anchor != state.anchor) { Q_EMIT q->anchorChanged(); } - if (previous.desiredSize != current.desiredSize) { + if (previous.desiredSize != state.desiredSize) { Q_EMIT q->desiredSizeChanged(); } - if (previous.exclusiveZone != current.exclusiveZone) { + if (previous.exclusiveZone != state.exclusiveZone) { Q_EMIT q->exclusiveZoneChanged(); } - if (previous.margins != current.margins) { + if (previous.margins != state.margins) { Q_EMIT q->marginsChanged(); } } @@ -336,8 +363,7 @@ LayerSurfaceV1Interface::LayerSurfaceV1Interface(LayerShellV1Interface *shell, wl_resource *resource) : d(new LayerSurfaceV1InterfacePrivate(this, surface)) { - d->current.layer = layer; - d->pending.layer = layer; + d->state.layer = layer; d->shell = shell; d->output = output; @@ -352,7 +378,7 @@ LayerSurfaceV1Interface::~LayerSurfaceV1Interface() bool LayerSurfaceV1Interface::isCommitted() const { - return d->isCommitted; + return d->state.committed; } SurfaceInterface *LayerSurfaceV1Interface::surface() const @@ -362,52 +388,52 @@ SurfaceInterface *LayerSurfaceV1Interface::surface() const Qt::Edges LayerSurfaceV1Interface::anchor() const { - return d->current.anchor; + return d->state.anchor; } QSize LayerSurfaceV1Interface::desiredSize() const { - return d->current.desiredSize; + return d->state.desiredSize; } bool LayerSurfaceV1Interface::acceptsFocus() const { - return d->current.acceptsFocus; + return d->state.acceptsFocus; } LayerSurfaceV1Interface::Layer LayerSurfaceV1Interface::layer() const { - return d->current.layer; + return d->state.layer; } QMargins LayerSurfaceV1Interface::margins() const { - return d->current.margins; + return d->state.margins; } int LayerSurfaceV1Interface::leftMargin() const { - return d->current.margins.left(); + return d->state.margins.left(); } int LayerSurfaceV1Interface::topMargin() const { - return d->current.margins.top(); + return d->state.margins.top(); } int LayerSurfaceV1Interface::rightMargin() const { - return d->current.margins.right(); + return d->state.margins.right(); } int LayerSurfaceV1Interface::bottomMargin() const { - return d->current.margins.bottom(); + return d->state.margins.bottom(); } int LayerSurfaceV1Interface::exclusiveZone() const { - return d->current.exclusiveZone; + return d->state.exclusiveZone; } Qt::Edge LayerSurfaceV1Interface::exclusiveEdge() const @@ -442,25 +468,25 @@ QString LayerSurfaceV1Interface::scope() const quint32 LayerSurfaceV1Interface::sendConfigure(const QSize &size) { - if (d->isClosed) { + if (d->state.closed) { qCWarning(KWIN_CORE) << "Cannot configure a closed layer shell surface"; return 0; } const uint32_t serial = d->shell->display()->nextSerial(); - d->serials << serial; + d->state.serials << serial; d->send_configure(serial, size.width(), size.height()); - d->isConfigured = true; + d->state.configured = true; return serial; } void LayerSurfaceV1Interface::sendClosed() { - if (!d->isClosed) { + if (!d->state.closed) { d->send_closed(); - d->isClosed = true; + d->state.closed = true; } } diff --git a/src/wayland/pointerconstraints_v1_interface.cpp b/src/wayland/pointerconstraints_v1_interface.cpp index 54378dfe9d..3909dfbb90 100644 --- a/src/wayland/pointerconstraints_v1_interface.cpp +++ b/src/wayland/pointerconstraints_v1_interface.cpp @@ -138,27 +138,28 @@ LockedPointerV1InterfacePrivate::LockedPointerV1InterfacePrivate(LockedPointerV1 const QRegion ®ion, ::wl_resource *resource) : QtWaylandServer::zwp_locked_pointer_v1(resource) + , SurfaceExtension(surface) , q(q) , surface(surface) , lifeTime(lifeTime) - , pendingRegion(region) - , hasPendingRegion(true) { - commit(); + pending.region = region; + + apply(&pending); + + pending = LockedPointerV1Commit{}; } -void LockedPointerV1InterfacePrivate::commit() +void LockedPointerV1InterfacePrivate::apply(LockedPointerV1Commit *commit) { const QRegion oldRegion = effectiveRegion; const QPointF oldHint = hint; - if (hasPendingRegion) { - region = mapScaleOverride(pendingRegion, surface->scaleOverride()); - hasPendingRegion = false; + if (commit->region.has_value()) { + region = mapScaleOverride(commit->region.value(), surface->scaleOverride()); } - if (hasPendingHint) { - hint = pendingHint / surface->scaleOverride(); - hasPendingHint = false; + if (commit->hint.has_value()) { + hint = commit->hint.value() / surface->scaleOverride(); } effectiveRegion = surface->input(); @@ -187,17 +188,18 @@ void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_destroy(Resource *re void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_set_cursor_position_hint(Resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y) { - pendingHint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); - hasPendingHint = true; + pending.hint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); } void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource) { - pendingRegion = regionFromResource(region_resource); - hasPendingRegion = true; + pending.region = regionFromResource(region_resource); } -LockedPointerV1Interface::LockedPointerV1Interface(SurfaceInterface *surface, LifeTime lifeTime, const QRegion ®ion, ::wl_resource *resource) +LockedPointerV1Interface::LockedPointerV1Interface(SurfaceInterface *surface, + LifeTime lifeTime, + const QRegion ®ion, + ::wl_resource *resource) : d(new LockedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource)) { SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this); @@ -255,22 +257,24 @@ ConfinedPointerV1InterfacePrivate::ConfinedPointerV1InterfacePrivate(ConfinedPoi const QRegion ®ion, ::wl_resource *resource) : QtWaylandServer::zwp_confined_pointer_v1(resource) + , SurfaceExtension(surface) , q(q) , surface(surface) , lifeTime(lifeTime) - , pendingRegion(region) - , hasPendingRegion(true) { - commit(); + pending.region = region; + + apply(&pending); + + pending = ConfinedPointerV1Commit{}; } -void ConfinedPointerV1InterfacePrivate::commit() +void ConfinedPointerV1InterfacePrivate::apply(ConfinedPointerV1Commit *commit) { const QRegion oldRegion = effectiveRegion; - if (hasPendingRegion) { - region = mapScaleOverride(pendingRegion, surface->scaleOverride()); - hasPendingRegion = false; + if (commit->region.has_value()) { + region = mapScaleOverride(commit->region.value(), surface->scaleOverride()); } effectiveRegion = surface->input(); @@ -295,11 +299,13 @@ void ConfinedPointerV1InterfacePrivate::zwp_confined_pointer_v1_destroy(Resource void ConfinedPointerV1InterfacePrivate::zwp_confined_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource) { - pendingRegion = regionFromResource(region_resource); - hasPendingRegion = true; + pending.region = regionFromResource(region_resource); } -ConfinedPointerV1Interface::ConfinedPointerV1Interface(SurfaceInterface *surface, LifeTime lifeTime, const QRegion ®ion, ::wl_resource *resource) +ConfinedPointerV1Interface::ConfinedPointerV1Interface(SurfaceInterface *surface, + LifeTime lifeTime, + const QRegion ®ion, + ::wl_resource *resource) : d(new ConfinedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource)) { SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this); diff --git a/src/wayland/pointerconstraints_v1_interface_p.h b/src/wayland/pointerconstraints_v1_interface_p.h index 67e9394fc2..ad05837d53 100644 --- a/src/wayland/pointerconstraints_v1_interface_p.h +++ b/src/wayland/pointerconstraints_v1_interface_p.h @@ -8,6 +8,8 @@ #pragma once #include "pointerconstraints_v1_interface.h" +#include "surface_interface.h" + #include #include "qwayland-server-pointer-constraints-unstable-v1.h" @@ -35,25 +37,27 @@ protected: void zwp_pointer_constraints_v1_destroy(Resource *resource) override; }; -class LockedPointerV1InterfacePrivate : public QtWaylandServer::zwp_locked_pointer_v1 +struct LockedPointerV1Commit +{ + std::optional region; + std::optional hint; +}; + +class LockedPointerV1InterfacePrivate final : public QtWaylandServer::zwp_locked_pointer_v1, public SurfaceExtension { public: static LockedPointerV1InterfacePrivate *get(LockedPointerV1Interface *pointer); LockedPointerV1InterfacePrivate(LockedPointerV1Interface *q, SurfaceInterface *surface, LockedPointerV1Interface::LifeTime lifeTime, const QRegion ®ion, ::wl_resource *resource); - void commit(); + void apply(LockedPointerV1Commit *commit) override; LockedPointerV1Interface *q; QPointer surface; LockedPointerV1Interface::LifeTime lifeTime; QRegion effectiveRegion; QRegion region; - QRegion pendingRegion; QPointF hint = QPointF(-1, -1); - QPointF pendingHint; - bool hasPendingRegion = false; - bool hasPendingHint = false; bool isLocked = false; protected: @@ -63,25 +67,29 @@ protected: void zwp_locked_pointer_v1_set_region(Resource *resource, struct ::wl_resource *region_resource) override; }; -class ConfinedPointerV1InterfacePrivate : public QtWaylandServer::zwp_confined_pointer_v1 +struct ConfinedPointerV1Commit +{ + std::optional region; +}; + +class ConfinedPointerV1InterfacePrivate final : public QtWaylandServer::zwp_confined_pointer_v1, public SurfaceExtension { public: static ConfinedPointerV1InterfacePrivate *get(ConfinedPointerV1Interface *pointer); - ConfinedPointerV1InterfacePrivate(ConfinedPointerV1Interface *q, SurfaceInterface *surface, + ConfinedPointerV1InterfacePrivate(ConfinedPointerV1Interface *q, + SurfaceInterface *surface, ConfinedPointerV1Interface::LifeTime lifeTime, const QRegion ®ion, ::wl_resource *resource); - void commit(); + void apply(ConfinedPointerV1Commit *commit) override; ConfinedPointerV1Interface *q; QPointer surface; ConfinedPointerV1Interface::LifeTime lifeTime; QRegion effectiveRegion; QRegion region; - QRegion pendingRegion; - bool hasPendingRegion = false; bool isConfined = false; protected: diff --git a/src/wayland/subcompositor_interface.cpp b/src/wayland/subcompositor_interface.cpp index b65016d0de..442ac1bc88 100644 --- a/src/wayland/subcompositor_interface.cpp +++ b/src/wayland/subcompositor_interface.cpp @@ -109,8 +109,8 @@ void SubSurfaceInterfacePrivate::subsurface_set_position(Resource *resource, int SurfaceInterfacePrivate *parentPrivate = SurfaceInterfacePrivate::get(parent); - parentPrivate->pending.subsurface.position[q] = QPoint(x, y); - parentPrivate->pending.subsurfacePositionChanged = true; + parentPrivate->pending->subsurface.position[q] = QPoint(x, y); + parentPrivate->pending->subsurfacePositionChanged = true; } void SubSurfaceInterfacePrivate::subsurface_place_above(Resource *resource, struct ::wl_resource *sibling_resource) @@ -166,20 +166,19 @@ void SubSurfaceInterfacePrivate::subsurface_set_desync(Resource *) mode = SubSurfaceInterface::Mode::Desynchronized; if (!q->isSynchronized()) { auto surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->commitFromCache(); + while (!locks.isEmpty()) { + SubSurfaceStateLock lock = locks.takeFirst(); + surfacePrivate->unlockState(lock.serial); + } } Q_EMIT q->modeChanged(SubSurfaceInterface::Mode::Desynchronized); } -void SubSurfaceInterfacePrivate::commit() -{ -} - -void SubSurfaceInterfacePrivate::parentCommit() +void SubSurfaceInterfacePrivate::parentApplyState(quint32 serial) { auto parentPrivate = SurfaceInterfacePrivate::get(parent); - if (parentPrivate->current.subsurfacePositionChanged) { - const QPoint &pos = parentPrivate->current.subsurface.position[q]; + if (parentPrivate->current->subsurfacePositionChanged) { + const QPoint &pos = parentPrivate->current->subsurface.position[q]; if (position != pos) { position = pos; Q_EMIT q->positionChanged(pos); @@ -188,7 +187,10 @@ void SubSurfaceInterfacePrivate::parentCommit() if (mode == SubSurfaceInterface::Mode::Synchronized) { auto surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->commitFromCache(); + while (!locks.isEmpty() && locks[0].parentSerial == serial) { + SubSurfaceStateLock lock = locks.takeFirst(); + surfacePrivate->unlockState(lock.serial); + } } } @@ -266,6 +268,24 @@ SurfaceInterface *SubSurfaceInterface::mainSurface() const return d->parent; } +void SubSurfaceInterface::commit() +{ + SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(d->surface); + if (isSynchronized()) { + const quint32 serial = surfacePrivate->lockState(surfacePrivate->pending.get()); + const quint32 parentSerial = SurfaceInterfacePrivate::get(d->parent)->pending->serial; + d->locks.append(SubSurfaceStateLock{ + .serial = serial, + .parentSerial = parentSerial, + }); + } else { + while (!d->locks.isEmpty()) { + SubSurfaceStateLock lock = d->locks.takeFirst(); + surfacePrivate->unlockState(lock.serial); + } + } +} + } // namespace KWaylandServer #include "moc_subcompositor_interface.cpp" diff --git a/src/wayland/subcompositor_interface.h b/src/wayland/subcompositor_interface.h index 21c7f9812e..049a45b777 100644 --- a/src/wayland/subcompositor_interface.h +++ b/src/wayland/subcompositor_interface.h @@ -116,7 +116,11 @@ Q_SIGNALS: private: SubSurfaceInterface(SurfaceInterface *surface, SurfaceInterface *parent, wl_resource *resource); + + void commit(); + std::unique_ptr d; + friend class SurfaceInterfacePrivate; friend class SubSurfaceInterfacePrivate; friend class SubCompositorInterfacePrivate; }; diff --git a/src/wayland/subsurface_interface_p.h b/src/wayland/subsurface_interface_p.h index cf04f40a36..8979f8a7dd 100644 --- a/src/wayland/subsurface_interface_p.h +++ b/src/wayland/subsurface_interface_p.h @@ -29,6 +29,12 @@ protected: void subcompositor_get_subsurface(Resource *resource, uint32_t id, struct ::wl_resource *surface_resource, struct ::wl_resource *parent_resource) override; }; +struct SubSurfaceStateLock +{ + const quint32 serial; + const quint32 parentSerial; +}; + class SubSurfaceInterfacePrivate : public SurfaceRole, public QtWaylandServer::wl_subsurface { public: @@ -36,14 +42,14 @@ public: SubSurfaceInterfacePrivate(SubSurfaceInterface *q, SurfaceInterface *surface, SurfaceInterface *parent, ::wl_resource *resource); - void commit() override; - void parentCommit(); + void parentApplyState(quint32 serial); SubSurfaceInterface *q; QPoint position = QPoint(0, 0); SubSurfaceInterface::Mode mode = SubSurfaceInterface::Mode::Synchronized; QPointer surface; QPointer parent; + QList locks; protected: void subsurface_destroy_resource(Resource *resource) override; diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp index 25ccb38770..9c9f03092f 100644 --- a/src/wayland/surface_interface.cpp +++ b/src/wayland/surface_interface.cpp @@ -40,10 +40,9 @@ static QRegion map_helper(const QMatrix4x4 &matrix, const QRegion ®ion) SurfaceInterfacePrivate::SurfaceInterfacePrivate(SurfaceInterface *q) : q(q) + , current(std::make_unique()) + , pending(std::make_unique()) { - wl_list_init(¤t.frameCallbacks); - wl_list_init(&pending.frameCallbacks); - wl_list_init(&cached.frameCallbacks); } SurfaceInterfacePrivate::~SurfaceInterfacePrivate() @@ -51,23 +50,29 @@ SurfaceInterfacePrivate::~SurfaceInterfacePrivate() wl_resource *resource; wl_resource *tmp; - wl_resource_for_each_safe (resource, tmp, ¤t.frameCallbacks) { + wl_resource_for_each_safe (resource, tmp, ¤t->frameCallbacks) { wl_resource_destroy(resource); } - wl_resource_for_each_safe (resource, tmp, &pending.frameCallbacks) { + wl_resource_for_each_safe (resource, tmp, &pending->frameCallbacks) { wl_resource_destroy(resource); } - wl_resource_for_each_safe (resource, tmp, &cached.frameCallbacks) { - wl_resource_destroy(resource); + for (const auto &stash : std::as_const(stashed)) { + wl_resource_for_each_safe (resource, tmp, &stash->frameCallbacks) { + wl_resource_destroy(resource); + } } } void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child) { // protocol is not precise on how to handle the addition of new sub surfaces - pending.subsurface.above.append(child); - cached.subsurface.above.append(child); - current.subsurface.above.append(child); + current->subsurface.above.append(child); + pending->subsurface.above.append(child); + + for (int i = 0; i < stashed.size(); ++i) { + stashed[i]->subsurface.above.append(child); + } + child->surface()->setOutputs(outputs); if (preferredBufferScale.has_value()) { child->surface()->setPreferredBufferScale(preferredBufferScale.value()); @@ -83,15 +88,17 @@ void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child) void SurfaceInterfacePrivate::removeChild(SubSurfaceInterface *child) { // protocol is not precise on how to handle the addition of new sub surfaces - pending.subsurface.below.removeAll(child); - pending.subsurface.above.removeAll(child); - pending.subsurface.position.remove(child); - cached.subsurface.below.removeAll(child); - cached.subsurface.above.removeAll(child); - cached.subsurface.position.remove(child); - current.subsurface.below.removeAll(child); - current.subsurface.above.removeAll(child); - current.subsurface.position.remove(child); + current->subsurface.below.removeAll(child); + current->subsurface.above.removeAll(child); + + pending->subsurface.below.removeAll(child); + pending->subsurface.above.removeAll(child); + + for (int i = 0; i < stashed.size(); ++i) { + stashed[i]->subsurface.below.removeAll(child); + stashed[i]->subsurface.above.removeAll(child); + } + Q_EMIT q->childSubSurfaceRemoved(child); Q_EMIT q->childSubSurfacesChanged(); } @@ -103,23 +110,23 @@ bool SurfaceInterfacePrivate::raiseChild(SubSurfaceInterface *subsurface, Surfac QList *anchorList; int anchorIndex; - pending.subsurface.below.removeOne(subsurface); - pending.subsurface.above.removeOne(subsurface); + pending->subsurface.below.removeOne(subsurface); + pending->subsurface.above.removeOne(subsurface); if (anchor == q) { // Pretend as if the parent surface were before the first child in the above list. - anchorList = &pending.subsurface.above; + anchorList = &pending->subsurface.above; anchorIndex = -1; - } else if (anchorIndex = pending.subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) { - anchorList = &pending.subsurface.above; - } else if (anchorIndex = pending.subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) { - anchorList = &pending.subsurface.below; + } else if (anchorIndex = pending->subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) { + anchorList = &pending->subsurface.above; + } else if (anchorIndex = pending->subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) { + anchorList = &pending->subsurface.below; } else { return false; // The anchor belongs to other sub-surface tree. } anchorList->insert(anchorIndex + 1, subsurface); - pending.subsurfaceOrderChanged = true; + pending->subsurfaceOrderChanged = true; return true; } @@ -130,48 +137,48 @@ bool SurfaceInterfacePrivate::lowerChild(SubSurfaceInterface *subsurface, Surfac QList *anchorList; int anchorIndex; - pending.subsurface.below.removeOne(subsurface); - pending.subsurface.above.removeOne(subsurface); + pending->subsurface.below.removeOne(subsurface); + pending->subsurface.above.removeOne(subsurface); if (anchor == q) { // Pretend as if the parent surface were after the last child in the below list. - anchorList = &pending.subsurface.below; - anchorIndex = pending.subsurface.below.count(); - } else if (anchorIndex = pending.subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) { - anchorList = &pending.subsurface.above; - } else if (anchorIndex = pending.subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) { - anchorList = &pending.subsurface.below; + anchorList = &pending->subsurface.below; + anchorIndex = pending->subsurface.below.count(); + } else if (anchorIndex = pending->subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) { + anchorList = &pending->subsurface.above; + } else if (anchorIndex = pending->subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) { + anchorList = &pending->subsurface.below; } else { return false; // The anchor belongs to other sub-surface tree. } anchorList->insert(anchorIndex, subsurface); - pending.subsurfaceOrderChanged = true; + pending->subsurfaceOrderChanged = true; return true; } void SurfaceInterfacePrivate::setShadow(const QPointer &shadow) { - pending.shadow = shadow; - pending.shadowIsSet = true; + pending->shadow = shadow; + pending->shadowIsSet = true; } void SurfaceInterfacePrivate::setBlur(const QPointer &blur) { - pending.blur = blur; - pending.blurIsSet = true; + pending->blur = blur; + pending->blurIsSet = true; } void SurfaceInterfacePrivate::setSlide(const QPointer &slide) { - pending.slide = slide; - pending.slideIsSet = true; + pending->slide = slide; + pending->slideIsSet = true; } void SurfaceInterfacePrivate::setContrast(const QPointer &contrast) { - pending.contrast = contrast; - pending.contrastIsSet = true; + pending->contrast = contrast; + pending->contrastIsSet = true; } void SurfaceInterfacePrivate::installPointerConstraint(LockedPointerV1Interface *lock) @@ -263,23 +270,23 @@ void SurfaceInterfacePrivate::surface_attach(Resource *resource, struct ::wl_res return; } } else { - pending.offset = QPoint(x, y); + pending->offset = QPoint(x, y); } - pending.bufferIsSet = true; + pending->bufferIsSet = true; if (!buffer) { // got a null buffer, deletes content in next frame - pending.buffer = nullptr; - pending.damage = QRegion(); - pending.bufferDamage = QRegion(); + pending->buffer = nullptr; + pending->damage = QRegion(); + pending->bufferDamage = QRegion(); return; } - pending.buffer = Display::bufferForResource(buffer); + pending->buffer = Display::bufferForResource(buffer); } void SurfaceInterfacePrivate::surface_damage(Resource *, int32_t x, int32_t y, int32_t width, int32_t height) { - pending.damage |= QRect(x, y, width, height); + pending->damage |= QRect(x, y, width, height); } void SurfaceInterfacePrivate::surface_frame(Resource *resource, uint32_t callback) @@ -297,30 +304,43 @@ void SurfaceInterfacePrivate::surface_frame(Resource *resource, uint32_t callbac wl_list_remove(wl_resource_get_link(resource)); }); - wl_list_insert(pending.frameCallbacks.prev, wl_resource_get_link(callbackResource)); + wl_list_insert(pending->frameCallbacks.prev, wl_resource_get_link(callbackResource)); } void SurfaceInterfacePrivate::surface_set_opaque_region(Resource *resource, struct ::wl_resource *region) { RegionInterface *r = RegionInterface::get(region); - pending.opaque = r ? r->region() : QRegion(); - pending.opaqueIsSet = true; + pending->opaque = r ? r->region() : QRegion(); + pending->opaqueIsSet = true; } void SurfaceInterfacePrivate::surface_set_input_region(Resource *resource, struct ::wl_resource *region) { RegionInterface *r = RegionInterface::get(region); - pending.input = r ? r->region() : infiniteRegion(); - pending.inputIsSet = true; + pending->input = r ? r->region() : infiniteRegion(); + pending->inputIsSet = true; } void SurfaceInterfacePrivate::surface_commit(Resource *resource) { if (subSurface) { - commitSubSurface(); - } else { - applyState(&pending); + subSurface->commit(); } + + // If there are already stashed states, this one will be applied when all the previous + // states are applied. + if (pending->locks || !stashed.empty()) { + auto stash = std::make_unique(); + pending->mergeInto(stash.get()); + const quint32 serial = stash->serial; + + stashed.push_back(std::move(stash)); + Q_EMIT q->stateStashed(serial); + } else { + applyState(pending.get()); + } + + pending->serial++; } void SurfaceInterfacePrivate::surface_set_buffer_transform(Resource *resource, int32_t transform) @@ -329,8 +349,8 @@ void SurfaceInterfacePrivate::surface_set_buffer_transform(Resource *resource, i wl_resource_post_error(resource->handle, error_invalid_transform, "buffer transform must be a valid transform (%d specified)", transform); return; } - pending.bufferTransform = KWin::OutputTransform::Kind(transform); - pending.bufferTransformIsSet = true; + pending->bufferTransform = KWin::OutputTransform::Kind(transform); + pending->bufferTransformIsSet = true; } void SurfaceInterfacePrivate::surface_set_buffer_scale(Resource *resource, int32_t scale) @@ -339,18 +359,18 @@ void SurfaceInterfacePrivate::surface_set_buffer_scale(Resource *resource, int32 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; + 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) { - pending.bufferDamage |= QRect(x, y, width, height); + pending->bufferDamage |= QRect(x, y, width, height); } void SurfaceInterfacePrivate::surface_offset(Resource *resource, int32_t x, int32_t y) { - pending.offset = QPoint(x, y); + pending->offset = QPoint(x, y); } SurfaceInterface::SurfaceInterface(CompositorInterface *compositor, wl_resource *resource) @@ -398,22 +418,22 @@ void SurfaceInterface::frameRendered(quint32 msec) wl_resource *resource; wl_resource *tmp; - wl_resource_for_each_safe (resource, tmp, &d->current.frameCallbacks) { + wl_resource_for_each_safe (resource, tmp, &d->current->frameCallbacks) { wl_callback_send_done(resource, msec); wl_resource_destroy(resource); } - for (SubSurfaceInterface *subsurface : std::as_const(d->current.subsurface.below)) { + for (SubSurfaceInterface *subsurface : std::as_const(d->current->subsurface.below)) { subsurface->surface()->frameRendered(msec); } - for (SubSurfaceInterface *subsurface : std::as_const(d->current.subsurface.above)) { + for (SubSurfaceInterface *subsurface : std::as_const(d->current->subsurface.above)) { subsurface->surface()->frameRendered(msec); } } bool SurfaceInterface::hasFrameCallbacks() const { - return !wl_list_empty(&d->current.frameCallbacks); + return !wl_list_empty(&d->current->frameCallbacks); } QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix() @@ -422,56 +442,56 @@ QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix() QMatrix4x4 surfaceToBufferMatrix; - if (!current.buffer) { + if (!current->buffer) { return surfaceToBufferMatrix; } - surfaceToBufferMatrix.scale(current.bufferScale, current.bufferScale); + surfaceToBufferMatrix.scale(current->bufferScale, current->bufferScale); surfaceToBufferMatrix.scale(scaleOverride, scaleOverride); - switch (current.bufferTransform.kind()) { + switch (current->bufferTransform.kind()) { case KWin::OutputTransform::Normal: case KWin::OutputTransform::Flipped: break; case KWin::OutputTransform::Rotated90: case KWin::OutputTransform::Flipped90: - surfaceToBufferMatrix.translate(0, bufferSize.height() / current.bufferScale); + surfaceToBufferMatrix.translate(0, bufferSize.height() / current->bufferScale); surfaceToBufferMatrix.rotate(-90, 0, 0, 1); break; case KWin::OutputTransform::Rotated180: case KWin::OutputTransform::Flipped180: - surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, bufferSize.height() / current.bufferScale); + surfaceToBufferMatrix.translate(bufferSize.width() / current->bufferScale, bufferSize.height() / current->bufferScale); surfaceToBufferMatrix.rotate(-180, 0, 0, 1); break; case KWin::OutputTransform::Rotated270: case KWin::OutputTransform::Flipped270: - surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, 0); + surfaceToBufferMatrix.translate(bufferSize.width() / current->bufferScale, 0); surfaceToBufferMatrix.rotate(-270, 0, 0, 1); break; } - switch (current.bufferTransform.kind()) { + switch (current->bufferTransform.kind()) { case KWin::OutputTransform::Flipped: case KWin::OutputTransform::Flipped180: - surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, 0); + surfaceToBufferMatrix.translate(bufferSize.width() / current->bufferScale, 0); surfaceToBufferMatrix.scale(-1, 1); break; case KWin::OutputTransform::Flipped90: case KWin::OutputTransform::Flipped270: - surfaceToBufferMatrix.translate(bufferSize.height() / current.bufferScale, 0); + surfaceToBufferMatrix.translate(bufferSize.height() / current->bufferScale, 0); surfaceToBufferMatrix.scale(-1, 1); break; default: break; } - if (current.viewport.sourceGeometry.isValid()) { - surfaceToBufferMatrix.translate(current.viewport.sourceGeometry.x(), current.viewport.sourceGeometry.y()); + if (current->viewport.sourceGeometry.isValid()) { + surfaceToBufferMatrix.translate(current->viewport.sourceGeometry.x(), current->viewport.sourceGeometry.y()); } QSizeF sourceSize; - if (current.viewport.sourceGeometry.isValid()) { - sourceSize = current.viewport.sourceGeometry.size(); + if (current->viewport.sourceGeometry.isValid()) { + sourceSize = current->viewport.sourceGeometry.size(); } else { sourceSize = implicitSurfaceSize; } @@ -485,21 +505,29 @@ QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix() QRectF SurfaceInterfacePrivate::computeBufferSourceBox() const { - if (!current.viewport.sourceGeometry.isValid()) { + if (!current->viewport.sourceGeometry.isValid()) { return QRectF(0, 0, bufferSize.width(), bufferSize.height()); } - const QSizeF bounds = current.bufferTransform.map(bufferSize); - const QRectF box(current.viewport.sourceGeometry.x() * current.bufferScale, - current.viewport.sourceGeometry.y() * current.bufferScale, - current.viewport.sourceGeometry.width() * current.bufferScale, - current.viewport.sourceGeometry.height() * current.bufferScale); + const QSizeF bounds = current->bufferTransform.map(bufferSize); + const QRectF box(current->viewport.sourceGeometry.x() * current->bufferScale, + current->viewport.sourceGeometry.y() * current->bufferScale, + current->viewport.sourceGeometry.width() * current->bufferScale, + current->viewport.sourceGeometry.height() * current->bufferScale); - return current.bufferTransform.inverted().map(box, bounds); + return current->bufferTransform.inverted().map(box, bounds); +} + +SurfaceState::SurfaceState() +{ + wl_list_init(&frameCallbacks); } void SurfaceState::mergeInto(SurfaceState *target) { + target->serial = serial; + target->locks = locks; + target->bufferIsSet = bufferIsSet; if (target->bufferIsSet) { target->buffer = buffer; @@ -580,6 +608,7 @@ void SurfaceState::mergeInto(SurfaceState *target) } *this = SurfaceState{}; + serial = target->serial; subsurface = target->subsurface; wl_list_init(&frameCallbacks); } @@ -588,13 +617,13 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) { const bool bufferChanged = next->bufferIsSet; const bool opaqueRegionChanged = next->opaqueIsSet; - const bool transformChanged = next->bufferTransformIsSet && (current.bufferTransform != next->bufferTransform); + const bool transformChanged = next->bufferTransformIsSet && (current->bufferTransform != next->bufferTransform); const bool shadowChanged = next->shadowIsSet; const bool blurChanged = next->blurIsSet; const bool contrastChanged = next->contrastIsSet; const bool slideChanged = next->slideIsSet; const bool subsurfaceOrderChanged = next->subsurfaceOrderChanged; - const bool visibilityChanged = bufferChanged && bool(current.buffer) != bool(next->buffer); + const bool visibilityChanged = bufferChanged && bool(current->buffer) != bool(next->buffer); const QSizeF oldSurfaceSize = surfaceSize; const QSize oldBufferSize = bufferSize; @@ -602,17 +631,17 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) const QMatrix4x4 oldSurfaceToBufferMatrix = surfaceToBufferMatrix; const QRegion oldInputRegion = inputRegion; - next->mergeInto(¤t); - bufferRef = current.buffer; + next->mergeInto(current.get()); + bufferRef = current->buffer; scaleOverride = pendingScaleOverride; // TODO: Refactor the state management code because it gets more clumsy. - if (current.buffer) { - bufferSize = current.buffer->size(); + if (current->buffer) { + bufferSize = current->buffer->size(); bufferSourceBox = computeBufferSourceBox(); - implicitSurfaceSize = current.buffer->size() / current.bufferScale; - switch (current.bufferTransform.kind()) { + implicitSurfaceSize = current->buffer->size() / current->bufferScale; + switch (current->bufferTransform.kind()) { case KWin::OutputTransform::Rotated90: case KWin::OutputTransform::Rotated270: case KWin::OutputTransform::Flipped90: @@ -626,21 +655,21 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) break; } - if (current.viewport.destinationSize.isValid()) { - surfaceSize = current.viewport.destinationSize; - } else if (current.viewport.sourceGeometry.isValid()) { - surfaceSize = current.viewport.sourceGeometry.size(); + if (current->viewport.destinationSize.isValid()) { + surfaceSize = current->viewport.destinationSize; + } else if (current->viewport.sourceGeometry.isValid()) { + surfaceSize = current->viewport.sourceGeometry.size(); } else { surfaceSize = implicitSurfaceSize; } const QRectF surfaceRect(QPoint(0, 0), surfaceSize); - inputRegion = current.input & surfaceRect.toAlignedRect(); + inputRegion = current->input & surfaceRect.toAlignedRect(); - if (!current.buffer->hasAlphaChannel()) { + if (!current->buffer->hasAlphaChannel()) { opaqueRegion = surfaceRect.toAlignedRect(); } else { - opaqueRegion = current.opaque & surfaceRect.toAlignedRect(); + opaqueRegion = current->opaque & surfaceRect.toAlignedRect(); } QMatrix4x4 scaleOverrideMatrix; @@ -663,15 +692,6 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) surfaceToBufferMatrix = buildSurfaceToBufferMatrix(); - if (lockedPointer) { - auto lockedPointerPrivate = LockedPointerV1InterfacePrivate::get(lockedPointer); - lockedPointerPrivate->commit(); - } - if (confinedPointer) { - auto confinedPointerPrivate = ConfinedPointerV1InterfacePrivate::get(confinedPointer); - confinedPointerPrivate->commit(); - } - if (opaqueRegionChanged) { Q_EMIT q->opaqueChanged(opaqueRegion); } @@ -679,7 +699,7 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) Q_EMIT q->inputChanged(inputRegion); } if (transformChanged) { - Q_EMIT q->bufferTransformChanged(current.bufferTransform); + Q_EMIT q->bufferTransformChanged(current->bufferTransform); } if (visibilityChanged) { updateEffectiveMapped(); @@ -713,56 +733,56 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) } if (bufferChanged) { - if (current.buffer && (!current.damage.isEmpty() || !current.bufferDamage.isEmpty())) { - const QRect bufferRect = QRect(QPoint(0, 0), current.buffer->size()); - bufferDamage = current.bufferDamage - .united(q->mapToBuffer(current.damage)) + if (current->buffer && (!current->damage.isEmpty() || !current->bufferDamage.isEmpty())) { + const QRect bufferRect = QRect(QPoint(0, 0), current->buffer->size()); + bufferDamage = current->bufferDamage + .united(q->mapToBuffer(current->damage)) .intersected(bufferRect); Q_EMIT q->damaged(bufferDamage); } } // The position of a sub-surface is applied when its parent is committed. - for (SubSurfaceInterface *subsurface : std::as_const(current.subsurface.below)) { + for (SubSurfaceInterface *subsurface : std::as_const(current->subsurface.below)) { auto subsurfacePrivate = SubSurfaceInterfacePrivate::get(subsurface); - subsurfacePrivate->parentCommit(); + subsurfacePrivate->parentApplyState(next->serial); } - for (SubSurfaceInterface *subsurface : std::as_const(current.subsurface.above)) { + for (SubSurfaceInterface *subsurface : std::as_const(current->subsurface.above)) { auto subsurfacePrivate = SubSurfaceInterfacePrivate::get(subsurface); - subsurfacePrivate->parentCommit(); - } - if (role) { - role->commit(); + subsurfacePrivate->parentApplyState(next->serial); } + + Q_EMIT q->stateApplied(next->serial); Q_EMIT q->committed(); } -void SurfaceInterfacePrivate::commitSubSurface() +quint32 SurfaceInterfacePrivate::lockState(SurfaceState *state) { - if (subSurface->isSynchronized()) { - commitToCache(); + state->locks++; + return state->serial; +} + +void SurfaceInterfacePrivate::unlockState(quint32 serial) +{ + if (pending->serial == serial) { + Q_ASSERT(pending->locks > 0); + pending->locks--; } else { - if (hasCacheState) { - commitToCache(); - commitFromCache(); - } else { - applyState(&pending); + for (const auto &state : stashed) { + if (state->serial == serial) { + Q_ASSERT(state->locks > 0); + state->locks--; + break; + } + } + while (!stashed.empty() && !stashed[0]->locks) { + auto stash = std::move(stashed.front()); + stashed.pop_front(); + applyState(stash.get()); } } } -void SurfaceInterfacePrivate::commitToCache() -{ - pending.mergeInto(&cached); - hasCacheState = true; -} - -void SurfaceInterfacePrivate::commitFromCache() -{ - applyState(&cached); - hasCacheState = false; -} - bool SurfaceInterfacePrivate::computeEffectiveMapped() const { if (!bufferRef) { @@ -789,11 +809,11 @@ void SurfaceInterfacePrivate::updateEffectiveMapped() Q_EMIT q->unmapped(); } - for (SubSurfaceInterface *subsurface : std::as_const(current.subsurface.below)) { + for (SubSurfaceInterface *subsurface : std::as_const(current->subsurface.below)) { auto surfacePrivate = SurfaceInterfacePrivate::get(subsurface->surface()); surfacePrivate->updateEffectiveMapped(); } - for (SubSurfaceInterface *subsurface : std::as_const(current.subsurface.above)) { + for (SubSurfaceInterface *subsurface : std::as_const(current->subsurface.above)) { auto surfacePrivate = SurfaceInterfacePrivate::get(subsurface->surface()); surfacePrivate->updateEffectiveMapped(); } @@ -835,7 +855,7 @@ QRectF SurfaceInterface::bufferSourceBox() const KWin::OutputTransform SurfaceInterface::bufferTransform() const { - return d->current.bufferTransform; + return d->current->bufferTransform; } KWin::GraphicsBuffer *SurfaceInterface::buffer() const @@ -845,7 +865,7 @@ KWin::GraphicsBuffer *SurfaceInterface::buffer() const QPoint SurfaceInterface::offset() const { - return d->current.offset / d->scaleOverride; + return d->current->offset / d->scaleOverride; } SurfaceInterface *SurfaceInterface::get(wl_resource *native) @@ -866,12 +886,12 @@ SurfaceInterface *SurfaceInterface::get(quint32 id, const ClientConnection *clie QList SurfaceInterface::below() const { - return d->current.subsurface.below; + return d->current->subsurface.below; } QList SurfaceInterface::above() const { - return d->current.subsurface.above; + return d->current->subsurface.above; } SubSurfaceInterface *SurfaceInterface::subSurface() const @@ -888,11 +908,11 @@ QRectF SurfaceInterface::boundingRect() const { QRectF rect(QPoint(0, 0), size()); - for (const SubSurfaceInterface *subSurface : std::as_const(d->current.subsurface.below)) { + for (const SubSurfaceInterface *subSurface : std::as_const(d->current->subsurface.below)) { const SurfaceInterface *childSurface = subSurface->surface(); rect |= childSurface->boundingRect().translated(subSurface->position()); } - for (const SubSurfaceInterface *subSurface : std::as_const(d->current.subsurface.above)) { + for (const SubSurfaceInterface *subSurface : std::as_const(d->current->subsurface.above)) { const SurfaceInterface *childSurface = subSurface->surface(); rect |= childSurface->boundingRect().translated(subSurface->position()); } @@ -902,22 +922,22 @@ QRectF SurfaceInterface::boundingRect() const QPointer SurfaceInterface::shadow() const { - return d->current.shadow; + return d->current->shadow; } QPointer SurfaceInterface::blur() const { - return d->current.blur; + return d->current->blur; } QPointer SurfaceInterface::contrast() const { - return d->current.contrast; + return d->current->contrast; } QPointer SurfaceInterface::slideOnShowHide() const { - return d->current.slide; + return d->current->slide; } bool SurfaceInterface::isMapped() const @@ -973,10 +993,10 @@ void SurfaceInterface::setOutputs(const QVector &outputs) } d->outputs = outputs; - for (auto child : std::as_const(d->current.subsurface.below)) { + for (auto child : std::as_const(d->current->subsurface.below)) { child->surface()->setOutputs(outputs); } - for (auto child : std::as_const(d->current.subsurface.above)) { + for (auto child : std::as_const(d->current->subsurface.above)) { child->surface()->setOutputs(outputs); } } @@ -987,7 +1007,7 @@ SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position) return nullptr; } - for (auto it = d->current.subsurface.above.crbegin(); it != d->current.subsurface.above.crend(); ++it) { + for (auto it = d->current->subsurface.above.crbegin(); it != d->current->subsurface.above.crend(); ++it) { const SubSurfaceInterface *current = *it; SurfaceInterface *surface = current->surface(); if (auto s = surface->surfaceAt(position - current->position())) { @@ -1000,7 +1020,7 @@ SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position) return this; } - for (auto it = d->current.subsurface.below.crbegin(); it != d->current.subsurface.below.crend(); ++it) { + for (auto it = d->current->subsurface.below.crbegin(); it != d->current->subsurface.below.crend(); ++it) { const SubSurfaceInterface *current = *it; SurfaceInterface *surface = current->surface(); if (auto s = surface->surfaceAt(position - current->position())) { @@ -1018,7 +1038,7 @@ SurfaceInterface *SurfaceInterface::inputSurfaceAt(const QPointF &position) return nullptr; } - for (auto it = d->current.subsurface.above.crbegin(); it != d->current.subsurface.above.crend(); ++it) { + for (auto it = d->current->subsurface.above.crbegin(); it != d->current->subsurface.above.crend(); ++it) { const SubSurfaceInterface *current = *it; auto surface = current->surface(); if (auto s = surface->inputSurfaceAt(position - current->position())) { @@ -1031,7 +1051,7 @@ SurfaceInterface *SurfaceInterface::inputSurfaceAt(const QPointF &position) return this; } - for (auto it = d->current.subsurface.below.crbegin(); it != d->current.subsurface.below.crend(); ++it) { + for (auto it = d->current->subsurface.below.crbegin(); it != d->current->subsurface.below.crend(); ++it) { const SubSurfaceInterface *current = *it; auto surface = current->surface(); if (auto s = surface->inputSurfaceAt(position - current->position())) { @@ -1064,7 +1084,7 @@ LinuxDmaBufV1Feedback *SurfaceInterface::dmabufFeedbackV1() const KWin::ContentType SurfaceInterface::contentType() const { - return d->current.contentType; + return d->current->contentType; } QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const @@ -1126,7 +1146,7 @@ QPointF SurfaceInterface::toSurfaceLocal(const QPointF &point) const PresentationHint SurfaceInterface::presentationHint() const { - return d->current.presentationHint; + return d->current->presentationHint; } void SurfaceInterface::setPreferredBufferScale(qreal scale) @@ -1143,10 +1163,10 @@ void SurfaceInterface::setPreferredBufferScale(qreal scale) d->send_preferred_buffer_scale(std::ceil(scale)); } - for (auto child : qAsConst(d->current.subsurface.below)) { + for (auto child : qAsConst(d->current->subsurface.below)) { child->surface()->setPreferredBufferScale(scale); } - for (auto child : qAsConst(d->current.subsurface.above)) { + for (auto child : qAsConst(d->current->subsurface.above)) { child->surface()->setPreferredBufferScale(scale); } } @@ -1162,10 +1182,10 @@ void SurfaceInterface::setPreferredBufferTransform(KWin::OutputTransform transfo d->send_preferred_buffer_transform(uint32_t(transform.kind())); } - for (auto child : qAsConst(d->current.subsurface.below)) { + for (auto child : qAsConst(d->current->subsurface.below)) { child->surface()->setPreferredBufferTransform(transform); } - for (auto child : qAsConst(d->current.subsurface.above)) { + for (auto child : qAsConst(d->current->subsurface.above)) { child->surface()->setPreferredBufferTransform(transform); } } diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h index e36f80e240..6a56a4be3f 100644 --- a/src/wayland/surface_interface.h +++ b/src/wayland/surface_interface.h @@ -430,11 +430,64 @@ Q_SIGNALS: */ void committed(); + /** + * This signal is emitted when a surface commit with the specified \a serial has been cached + * to be applied later. + */ + void stateStashed(quint32 serial); + + /** + * This signal is emitted when the state in a surface commit with the specified \a serial + * has been applied. + */ + void stateApplied(quint32 serial); + private: std::unique_ptr d; friend class SurfaceInterfacePrivate; }; -} +/** + * The SurfaceExtension class is the base class for wl_surface extensions. The SurfaceExtension + * helps with managing extension state and keeping it in sync with the surface state. + */ +template +class SurfaceExtension : public QObject +{ +public: + explicit SurfaceExtension(SurfaceInterface *surface) + { + connect(surface, &SurfaceInterface::stateStashed, this, &SurfaceExtension::stashState); + connect(surface, &SurfaceInterface::stateApplied, this, &SurfaceExtension::applyState); + } + + virtual void apply(Commit *commit) = 0; + + Commit pending; + QMap stashed; + +private: + void stashState(quint32 serial) + { + Commit stash = std::exchange(pending, Commit{}); + stashed.insert(serial, stash); + } + + void applyState(quint32 serial) + { + if (!stashed.isEmpty()) { + if (stashed.firstKey() == serial) { + Commit stash = stashed.take(serial); + apply(&stash); + } + return; + } + + apply(&pending); + pending = Commit{}; + } +}; + +} // namespace KWaylandServer Q_DECLARE_METATYPE(KWaylandServer::SurfaceInterface *) diff --git a/src/wayland/surface_interface_p.h b/src/wayland/surface_interface_p.h index 5b58066e6b..f7a547b93d 100644 --- a/src/wayland/surface_interface_p.h +++ b/src/wayland/surface_interface_p.h @@ -15,6 +15,7 @@ // Wayland #include "qwayland-server-wayland.h" // C++ +#include #include namespace KWaylandServer @@ -28,8 +29,13 @@ class FractionalScaleV1Interface; struct SurfaceState { + SurfaceState(); + void mergeInto(SurfaceState *target); + quint32 serial = 0; + quint32 locks = 0; + QRegion damage = QRegion(); QRegion bufferDamage = QRegion(); QRegion opaque = QRegion(); @@ -103,14 +109,13 @@ public: void installPointerConstraint(ConfinedPointerV1Interface *confinement); void installIdleInhibitor(IdleInhibitorV1Interface *inhibitor); - void commitToCache(); - void commitFromCache(); - - void commitSubSurface(); QMatrix4x4 buildSurfaceToBufferMatrix(); QRectF computeBufferSourceBox() const; void applyState(SurfaceState *next); + quint32 lockState(SurfaceState *state); + void unlockState(quint32 serial); + bool computeEffectiveMapped() const; void updateEffectiveMapped(); @@ -124,9 +129,9 @@ public: CompositorInterface *compositor; SurfaceInterface *q; SurfaceRole *role = nullptr; - SurfaceState current; - SurfaceState pending; - SurfaceState cached; + std::unique_ptr current; + std::unique_ptr pending; + std::deque> stashed; SubSurfaceInterface *subSurface = nullptr; QMatrix4x4 surfaceToBufferMatrix; QSize bufferSize = QSize(0, 0); @@ -139,7 +144,6 @@ public: KWin::GraphicsBufferRef bufferRef; QRegion bufferDamage; bool mapped = false; - bool hasCacheState = false; qreal scaleOverride = 1.; qreal pendingScaleOverride = 1.; diff --git a/src/wayland/surfacerole_p.h b/src/wayland/surfacerole_p.h index 8dbbbfe6ba..0b0d9ec21c 100644 --- a/src/wayland/surfacerole_p.h +++ b/src/wayland/surfacerole_p.h @@ -24,8 +24,6 @@ public: return m_surface; } - virtual void commit() = 0; - static SurfaceRole *get(SurfaceInterface *surface); private: diff --git a/src/wayland/tearingcontrol_v1_interface.cpp b/src/wayland/tearingcontrol_v1_interface.cpp index 9ddaefa100..09eb3c5e3e 100644 --- a/src/wayland/tearingcontrol_v1_interface.cpp +++ b/src/wayland/tearingcontrol_v1_interface.cpp @@ -74,8 +74,8 @@ TearingControlV1Interface::~TearingControlV1Interface() { if (m_surface) { SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); - surfacePrivate->pending.presentationHint = PresentationHint::VSync; - surfacePrivate->pending.tearingIsSet = true; + surfacePrivate->pending->presentationHint = PresentationHint::VSync; + surfacePrivate->pending->tearingIsSet = true; surfacePrivate->tearing = nullptr; } } @@ -85,11 +85,11 @@ void TearingControlV1Interface::wp_tearing_control_v1_set_presentation_hint(Reso if (m_surface) { SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); if (hint == presentation_hint::presentation_hint_async) { - surfacePrivate->pending.presentationHint = PresentationHint::Async; + surfacePrivate->pending->presentationHint = PresentationHint::Async; } else { - surfacePrivate->pending.presentationHint = PresentationHint::VSync; + surfacePrivate->pending->presentationHint = PresentationHint::VSync; } - surfacePrivate->pending.tearingIsSet = true; + surfacePrivate->pending->tearingIsSet = true; } } diff --git a/src/wayland/viewporter_interface.cpp b/src/wayland/viewporter_interface.cpp index 573897af69..dde62a32b8 100644 --- a/src/wayland/viewporter_interface.cpp +++ b/src/wayland/viewporter_interface.cpp @@ -70,10 +70,10 @@ void ViewportInterface::wp_viewport_destroy(Resource *resource) { if (surface) { SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->pending.viewport.sourceGeometry = QRectF(); - surfacePrivate->pending.viewport.sourceGeometryIsSet = true; - surfacePrivate->pending.viewport.destinationSize = QSize(); - surfacePrivate->pending.viewport.destinationSizeIsSet = true; + surfacePrivate->pending->viewport.sourceGeometry = QRectF(); + surfacePrivate->pending->viewport.sourceGeometryIsSet = true; + surfacePrivate->pending->viewport.destinationSize = QSize(); + surfacePrivate->pending->viewport.destinationSizeIsSet = true; } wl_resource_destroy(resource->handle); @@ -93,8 +93,8 @@ void ViewportInterface::wp_viewport_set_source(Resource *resource, wl_fixed_t x_ if (x == -1 && y == -1 && width == -1 && height == -1) { SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->pending.viewport.sourceGeometry = QRectF(); - surfacePrivate->pending.viewport.sourceGeometryIsSet = true; + surfacePrivate->pending->viewport.sourceGeometry = QRectF(); + surfacePrivate->pending->viewport.sourceGeometryIsSet = true; return; } @@ -104,8 +104,8 @@ void ViewportInterface::wp_viewport_set_source(Resource *resource, wl_fixed_t x_ } SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->pending.viewport.sourceGeometry = QRectF(x, y, width, height); - surfacePrivate->pending.viewport.sourceGeometryIsSet = true; + surfacePrivate->pending->viewport.sourceGeometry = QRectF(x, y, width, height); + surfacePrivate->pending->viewport.sourceGeometryIsSet = true; } void ViewportInterface::wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height) @@ -117,8 +117,8 @@ void ViewportInterface::wp_viewport_set_destination(Resource *resource, int32_t if (width == -1 && height == -1) { SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->pending.viewport.destinationSize = QSize(); - surfacePrivate->pending.viewport.destinationSizeIsSet = true; + surfacePrivate->pending->viewport.destinationSize = QSize(); + surfacePrivate->pending->viewport.destinationSizeIsSet = true; return; } @@ -128,8 +128,8 @@ void ViewportInterface::wp_viewport_set_destination(Resource *resource, int32_t } SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); - surfacePrivate->pending.viewport.destinationSize = QSize(width, height); - surfacePrivate->pending.viewport.destinationSizeIsSet = true; + surfacePrivate->pending->viewport.destinationSize = QSize(width, height); + surfacePrivate->pending->viewport.destinationSizeIsSet = true; } ViewporterInterface::ViewporterInterface(Display *display, QObject *parent) diff --git a/src/wayland/xdgshell_interface.cpp b/src/wayland/xdgshell_interface.cpp index d19c730bfd..83296f5d93 100644 --- a/src/wayland/xdgshell_interface.cpp +++ b/src/wayland/xdgshell_interface.cpp @@ -142,7 +142,6 @@ void XdgSurfaceInterfacePrivate::unassignRole() { toplevel = nullptr; popup = nullptr; - current = nullptr; pending = nullptr; } @@ -150,46 +149,40 @@ void XdgSurfaceInterfacePrivate::assignRole(XdgToplevelInterface *toplevel) { this->toplevel = toplevel; - XdgSurfaceRole *role = XdgToplevelInterfacePrivate::get(toplevel); - current = &role->current.base; - pending = &role->pending.base; + XdgSurfaceRole *role = XdgToplevelInterfacePrivate::get(toplevel); + pending = &role->pending; } void XdgSurfaceInterfacePrivate::assignRole(XdgPopupInterface *popup) { this->popup = popup; - XdgSurfaceRole *role = XdgPopupInterfacePrivate::get(popup); - current = &role->current.base; - pending = &role->pending.base; + XdgSurfaceRole *role = XdgPopupInterfacePrivate::get(popup); + pending = &role->pending; } -void XdgSurfaceInterfacePrivate::applyState(XdgSurfaceState *next) +void XdgSurfaceInterfacePrivate::apply(XdgSurfaceCommit *commit) { if (surface->buffer()) { firstBufferAttached = true; } - if (next->acknowledgedConfigureIsSet) { - current->acknowledgedConfigure = next->acknowledgedConfigure; - next->acknowledgedConfigureIsSet = false; - Q_EMIT q->configureAcknowledged(current->acknowledgedConfigure); + if (commit->acknowledgedConfigure.has_value()) { + Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value()); } - if (next->windowGeometryIsSet) { - current->windowGeometry = next->windowGeometry; - next->windowGeometryIsSet = false; - Q_EMIT q->windowGeometryChanged(current->windowGeometry); + if (commit->windowGeometry.has_value()) { + windowGeometry = commit->windowGeometry.value(); + Q_EMIT q->windowGeometryChanged(windowGeometry); } } -void XdgSurfaceInterfacePrivate::resetState() +void XdgSurfaceInterfacePrivate::reset() { firstBufferAttached = false; isConfigured = false; isInitialized = false; - *current = XdgSurfaceState{}; - *pending = XdgSurfaceState{}; + windowGeometry = QRect(); Q_EMIT q->resetOccurred(); } @@ -275,7 +268,6 @@ void XdgSurfaceInterfacePrivate::xdg_surface_set_window_geometry(Resource *resou } pending->windowGeometry = QRect(x, y, width, height); - pending->windowGeometryIsSet = true; } void XdgSurfaceInterfacePrivate::xdg_surface_ack_configure(Resource *resource, uint32_t serial) @@ -286,7 +278,6 @@ void XdgSurfaceInterfacePrivate::xdg_surface_ack_configure(Resource *resource, u } pending->acknowledgedConfigure = serial; - pending->acknowledgedConfigureIsSet = true; } XdgSurfaceInterface::XdgSurfaceInterface(XdgShellInterface *shell, SurfaceInterface *surface, ::wl_resource *resource) @@ -333,7 +324,7 @@ bool XdgSurfaceInterface::isConfigured() const QRect XdgSurfaceInterface::windowGeometry() const { - return d->current->windowGeometry; + return d->windowGeometry; } XdgSurfaceInterface *XdgSurfaceInterface::get(::wl_resource *resource) @@ -351,7 +342,7 @@ XdgToplevelInterfacePrivate::XdgToplevelInterfacePrivate(XdgToplevelInterface *t { } -void XdgToplevelInterfacePrivate::commit() +void XdgToplevelInterfacePrivate::apply(XdgToplevelCommit *commit) { auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) { @@ -359,15 +350,15 @@ void XdgToplevelInterfacePrivate::commit() return; } - xdgSurfacePrivate->applyState(&pending.base); + xdgSurfacePrivate->apply(commit); - if (current.minimumSize != pending.minimumSize) { - current.minimumSize = pending.minimumSize; - Q_EMIT q->minimumSizeChanged(current.minimumSize); + if (commit->minimumSize && commit->minimumSize != minimumSize) { + minimumSize = commit->minimumSize.value(); + Q_EMIT q->minimumSizeChanged(minimumSize); } - if (current.maximumSize != pending.maximumSize) { - current.maximumSize = pending.maximumSize; - Q_EMIT q->maximumSizeChanged(current.maximumSize); + if (commit->maximumSize && commit->maximumSize != maximumSize) { + maximumSize = commit->maximumSize.value(); + Q_EMIT q->maximumSizeChanged(maximumSize); } if (!xdgSurfacePrivate->isInitialized) { @@ -379,12 +370,14 @@ void XdgToplevelInterfacePrivate::commit() void XdgToplevelInterfacePrivate::reset() { auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); - xdgSurfacePrivate->resetState(); + xdgSurfacePrivate->reset(); windowTitle = QString(); windowClass = QString(); - current = XdgToplevelState{}; - pending = XdgToplevelState{}; + minimumSize = QSize(); + maximumSize = QSize(); + pending = XdgToplevelCommit{}; + stashed.clear(); Q_EMIT q->resetOccurred(); } @@ -574,12 +567,12 @@ QString XdgToplevelInterface::windowClass() const QSize XdgToplevelInterface::minimumSize() const { - return d->current.minimumSize.isEmpty() ? QSize(0, 0) : d->current.minimumSize; + return d->minimumSize.isEmpty() ? QSize(0, 0) : d->minimumSize; } QSize XdgToplevelInterface::maximumSize() const { - return d->current.maximumSize.isEmpty() ? QSize(INT_MAX, INT_MAX) : d->current.maximumSize; + return d->maximumSize.isEmpty() ? QSize(INT_MAX, INT_MAX) : d->maximumSize; } quint32 XdgToplevelInterface::sendConfigure(const QSize &size, const States &states) @@ -693,7 +686,7 @@ XdgPopupInterfacePrivate::XdgPopupInterfacePrivate(XdgPopupInterface *popup, Xdg { } -void XdgPopupInterfacePrivate::commit() +void XdgPopupInterfacePrivate::apply(XdgPopupCommit *commit) { if (!parentSurface) { auto shellPrivate = XdgShellInterfacePrivate::get(xdgSurface->shell()); @@ -709,7 +702,7 @@ void XdgPopupInterfacePrivate::commit() return; } - xdgSurfacePrivate->applyState(&pending.base); + xdgSurfacePrivate->apply(commit); if (!xdgSurfacePrivate->isInitialized) { Q_EMIT q->initializeRequested(); @@ -720,7 +713,9 @@ void XdgPopupInterfacePrivate::commit() void XdgPopupInterfacePrivate::reset() { auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); - xdgSurfacePrivate->resetState(); + pending = XdgPopupCommit{}; + stashed.clear(); + xdgSurfacePrivate->reset(); } void XdgPopupInterfacePrivate::xdg_popup_destroy_resource(Resource *resource) diff --git a/src/wayland/xdgshell_interface_p.h b/src/wayland/xdgshell_interface_p.h index 25527698e0..4831a5611d 100644 --- a/src/wayland/xdgshell_interface_p.h +++ b/src/wayland/xdgshell_interface_p.h @@ -9,7 +9,7 @@ #include "qwayland-server-xdg-shell.h" #include "xdgshell_interface.h" -#include "surface_interface.h" +#include "surface_interface_p.h" #include "surfacerole_p.h" namespace KWaylandServer @@ -83,37 +83,31 @@ protected: void xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial) override; }; -struct XdgSurfaceState +struct XdgSurfaceCommit { - QRect windowGeometry; - quint32 acknowledgedConfigure; - bool acknowledgedConfigureIsSet = false; - bool windowGeometryIsSet = false; + std::optional windowGeometry; + std::optional acknowledgedConfigure; }; -struct XdgToplevelState +struct XdgToplevelCommit : XdgSurfaceCommit { - XdgSurfaceState base; - QSize minimumSize; - QSize maximumSize; + std::optional minimumSize; + std::optional maximumSize; }; -struct XdgPopupState +struct XdgPopupCommit : XdgSurfaceCommit { - XdgSurfaceState base; }; -template -class XdgSurfaceRole : public SurfaceRole +template +class XdgSurfaceRole : public SurfaceRole, public SurfaceExtension { public: XdgSurfaceRole(SurfaceInterface *surface, const QByteArray &name) : SurfaceRole(surface, name) + , SurfaceExtension(surface) { } - - State pending; - State current; }; class XdgSurfaceInterfacePrivate : public QtWaylandServer::xdg_surface @@ -121,22 +115,22 @@ class XdgSurfaceInterfacePrivate : public QtWaylandServer::xdg_surface public: XdgSurfaceInterfacePrivate(XdgSurfaceInterface *xdgSurface); - void applyState(XdgSurfaceState *next); - void resetState(); + void apply(XdgSurfaceCommit *commit); + void reset(); void unassignRole(); void assignRole(XdgToplevelInterface *toplevel); void assignRole(XdgPopupInterface *popup); // These two point into XdgSurfaceRole's state and are valid as long as a role is assigned. - XdgSurfaceState *current = nullptr; - XdgSurfaceState *pending = nullptr; + XdgSurfaceCommit *pending = nullptr; XdgSurfaceInterface *q; XdgShellInterface *shell = nullptr; QPointer toplevel; QPointer popup; QPointer surface; + QRect windowGeometry; bool firstBufferAttached = false; bool isConfigured = false; bool isInitialized = false; @@ -152,12 +146,12 @@ protected: void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override; }; -class XdgToplevelInterfacePrivate : public XdgSurfaceRole, public QtWaylandServer::xdg_toplevel +class XdgToplevelInterfacePrivate : public XdgSurfaceRole, public QtWaylandServer::xdg_toplevel { public: XdgToplevelInterfacePrivate(XdgToplevelInterface *toplevel, XdgSurfaceInterface *surface); - void commit() override; + void apply(XdgToplevelCommit *commit) override; void reset(); static XdgToplevelInterfacePrivate *get(XdgToplevelInterface *toplevel); @@ -169,6 +163,8 @@ public: XdgSurfaceInterface *xdgSurface; QString windowTitle; QString windowClass; + QSize minimumSize; + QSize maximumSize; protected: void xdg_toplevel_destroy_resource(Resource *resource) override; @@ -188,14 +184,14 @@ protected: void xdg_toplevel_set_minimized(Resource *resource) override; }; -class XdgPopupInterfacePrivate : public XdgSurfaceRole, public QtWaylandServer::xdg_popup +class XdgPopupInterfacePrivate : public XdgSurfaceRole, public QtWaylandServer::xdg_popup { public: static XdgPopupInterfacePrivate *get(XdgPopupInterface *popup); XdgPopupInterfacePrivate(XdgPopupInterface *popup, XdgSurfaceInterface *surface); - void commit() override; + void apply(XdgPopupCommit *commit) override; void reset(); XdgPopupInterface *q; diff --git a/src/wayland/xwaylandshell_v1_interface.cpp b/src/wayland/xwaylandshell_v1_interface.cpp index 6a023a1348..454b5091cb 100644 --- a/src/wayland/xwaylandshell_v1_interface.cpp +++ b/src/wayland/xwaylandshell_v1_interface.cpp @@ -7,8 +7,8 @@ #include "xwaylandshell_v1_interface.h" #include "display.h" +#include "surface_interface_p.h" #include "surfacerole_p.h" -#include "surface_interface.h" #include "qwayland-server-xwayland-shell-v1.h" @@ -30,23 +30,21 @@ protected: void xwayland_shell_v1_get_xwayland_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface) override; }; -struct XwaylandSurfaceV1State +struct XwaylandSurfaceV1Commit { std::optional serial; }; -class XwaylandSurfaceV1InterfacePrivate : public SurfaceRole, public QtWaylandServer::xwayland_surface_v1 +class XwaylandSurfaceV1InterfacePrivate : public SurfaceRole, public SurfaceExtension, public QtWaylandServer::xwayland_surface_v1 { public: XwaylandSurfaceV1InterfacePrivate(XwaylandShellV1Interface *shell, SurfaceInterface *surface, wl_client *client, uint32_t id, int version, XwaylandSurfaceV1Interface *q); - void commit() override; + void apply(XwaylandSurfaceV1Commit *commit) override; XwaylandSurfaceV1Interface *q; XwaylandShellV1Interface *shell; - - XwaylandSurfaceV1State current; - XwaylandSurfaceV1State pending; + std::optional serial; protected: void xwayland_surface_v1_destroy_resource(Resource *resource) override; @@ -82,16 +80,17 @@ void XwaylandShellV1InterfacePrivate::xwayland_shell_v1_get_xwayland_surface(Res XwaylandSurfaceV1InterfacePrivate::XwaylandSurfaceV1InterfacePrivate(XwaylandShellV1Interface *shell, SurfaceInterface *surface, wl_client *client, uint32_t id, int version, XwaylandSurfaceV1Interface *q) : SurfaceRole(surface, QByteArrayLiteral("xwayland_surface_v1")) + , SurfaceExtension(surface) , QtWaylandServer::xwayland_surface_v1(client, id, version) , q(q) , shell(shell) { } -void XwaylandSurfaceV1InterfacePrivate::commit() +void XwaylandSurfaceV1InterfacePrivate::apply(XwaylandSurfaceV1Commit *commit) { - if (pending.serial.has_value()) { - current.serial = std::exchange(pending.serial, std::nullopt); + if (commit->serial.has_value()) { + serial = commit->serial; Q_EMIT shell->surfaceAssociated(q); } } @@ -109,9 +108,9 @@ void XwaylandSurfaceV1InterfacePrivate::xwayland_surface_v1_set_serial(Resource return; } - if (current.serial.has_value()) { + if (this->serial.has_value()) { wl_resource_post_error(resource->handle, error_already_associated, - "xwayland_surface_v1 already has a serial assigned to it: %" PRIu64, current.serial.value()); + "xwayland_surface_v1 already has a serial assigned to it: %" PRIu64, this->serial.value()); return; } @@ -159,7 +158,7 @@ SurfaceInterface *XwaylandSurfaceV1Interface::surface() const std::optional XwaylandSurfaceV1Interface::serial() const { - return d->current.serial; + return d->serial; } } // namespace KWaylandServer