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.
This commit is contained in:
Vlad Zahorodnii 2022-05-16 20:03:52 +03:00
parent 60251bc1e4
commit dcadf24e64
19 changed files with 548 additions and 418 deletions

View file

@ -62,16 +62,16 @@ void ContentTypeV1Interface::wp_content_type_v1_set_content_type(Resource *, uin
return; return;
} }
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
surfacePrivate->pending.contentType = waylandToKwinContentType(content_type); surfacePrivate->pending->contentType = waylandToKwinContentType(content_type);
surfacePrivate->pending.contentTypeIsSet = true; surfacePrivate->pending->contentTypeIsSet = true;
} }
void ContentTypeV1Interface::wp_content_type_v1_destroy(Resource *resource) void ContentTypeV1Interface::wp_content_type_v1_destroy(Resource *resource)
{ {
if (m_surface) { if (m_surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
surfacePrivate->pending.contentType = KWin::ContentType::None; surfacePrivate->pending->contentType = KWin::ContentType::None;
surfacePrivate->pending.contentTypeIsSet = true; surfacePrivate->pending->contentTypeIsSet = true;
} }
wl_resource_destroy(resource->handle); wl_resource_destroy(resource->handle);
} }

View file

@ -22,36 +22,33 @@ namespace KWaylandServer
class DragAndDropIconPrivate : public SurfaceRole class DragAndDropIconPrivate : public SurfaceRole
{ {
public: public:
explicit DragAndDropIconPrivate(DragAndDropIcon *q, SurfaceInterface *surface); explicit DragAndDropIconPrivate(SurfaceInterface *surface);
void commit() override;
DragAndDropIcon *q;
QPoint position; QPoint position;
}; };
DragAndDropIconPrivate::DragAndDropIconPrivate(DragAndDropIcon *q, SurfaceInterface *surface) DragAndDropIconPrivate::DragAndDropIconPrivate(SurfaceInterface *surface)
: SurfaceRole(surface, QByteArrayLiteral("dnd_icon")) : SurfaceRole(surface, QByteArrayLiteral("dnd_icon"))
, q(q)
{ {
} }
void DragAndDropIconPrivate::commit()
{
position += surface()->offset();
Q_EMIT q->changed();
}
DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface) DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface)
: QObject(surface) : QObject(surface)
, d(new DragAndDropIconPrivate(this, surface)) , d(new DragAndDropIconPrivate(surface))
{ {
connect(surface, &SurfaceInterface::committed, this, &DragAndDropIcon::commit);
} }
DragAndDropIcon::~DragAndDropIcon() DragAndDropIcon::~DragAndDropIcon()
{ {
} }
void DragAndDropIcon::commit()
{
d->position += d->surface()->offset();
Q_EMIT changed();
}
QPoint DragAndDropIcon::position() const QPoint DragAndDropIcon::position() const
{ {
return d->position; return d->position;

View file

@ -55,6 +55,8 @@ Q_SIGNALS:
void changed(); void changed();
private: private:
void commit();
explicit DragAndDropIcon(SurfaceInterface *surface); explicit DragAndDropIcon(SurfaceInterface *surface);
friend class DataDeviceInterfacePrivate; friend class DataDeviceInterfacePrivate;
std::unique_ptr<DragAndDropIconPrivate> d; std::unique_ptr<DragAndDropIconPrivate> d;

View file

@ -327,10 +327,6 @@ public:
Q_EMIT q->topLevel(OutputInterface::get(output), InputPanelSurfaceV1Interface::Position(position)); Q_EMIT q->topLevel(OutputInterface::get(output), InputPanelSurfaceV1Interface::Position(position));
} }
void commit() override
{
}
void zwp_input_panel_surface_v1_destroy_resource(Resource *) override void zwp_input_panel_surface_v1_destroy_resource(Resource *) override
{ {
delete q; delete q;

View file

@ -39,38 +39,45 @@ protected:
void zwlr_layer_shell_v1_destroy(Resource *resource) override; void zwlr_layer_shell_v1_destroy(Resource *resource) override;
}; };
class LayerSurfaceV1State struct LayerSurfaceV1Commit
{ {
public: std::optional<LayerSurfaceV1Interface::Layer> layer;
std::optional<Qt::Edges> anchor;
std::optional<QMargins> margins;
std::optional<QSize> desiredSize;
std::optional<int> exclusiveZone;
std::optional<quint32> acknowledgedConfigure;
std::optional<bool> acceptsFocus;
};
struct LayerSurfaceV1State
{
QQueue<quint32> serials;
LayerSurfaceV1Interface::Layer layer = LayerSurfaceV1Interface::BottomLayer; LayerSurfaceV1Interface::Layer layer = LayerSurfaceV1Interface::BottomLayer;
Qt::Edges anchor; Qt::Edges anchor;
QMargins margins; QMargins margins;
QSize desiredSize = QSize(0, 0); QSize desiredSize = QSize(0, 0);
int exclusiveZone = 0; int exclusiveZone = 0;
quint32 acknowledgedConfigure;
bool acknowledgedConfigureIsSet = false;
bool acceptsFocus = 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<LayerSurfaceV1Commit>, public QtWaylandServer::zwlr_layer_surface_v1
{ {
public: public:
LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface); LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface);
void commit() override; void apply(LayerSurfaceV1Commit *commit) override;
LayerSurfaceV1Interface *q; LayerSurfaceV1Interface *q;
LayerShellV1Interface *shell; LayerShellV1Interface *shell;
QPointer<SurfaceInterface> surface; QPointer<SurfaceInterface> surface;
QPointer<OutputInterface> output; QPointer<OutputInterface> output;
LayerSurfaceV1State current;
LayerSurfaceV1State pending;
QQueue<quint32> serials;
QString scope; QString scope;
bool isClosed = false; LayerSurfaceV1State state;
bool isConfigured = false;
bool isCommitted = false;
bool firstBufferAttached = false;
protected: protected:
void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override; void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override;
@ -150,6 +157,7 @@ Display *LayerShellV1Interface::display() const
LayerSurfaceV1InterfacePrivate::LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface) LayerSurfaceV1InterfacePrivate::LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface)
: SurfaceRole(surface, QByteArrayLiteral("layer_surface_v1")) : SurfaceRole(surface, QByteArrayLiteral("layer_surface_v1"))
, SurfaceExtension(surface)
, q(q) , q(q)
, surface(surface) , surface(surface)
{ {
@ -177,19 +185,19 @@ void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_anchor(Resource *
pending.anchor = Qt::Edges(); pending.anchor = Qt::Edges();
if (anchor & anchor_top) { if (anchor & anchor_top) {
pending.anchor |= Qt::TopEdge; *pending.anchor |= Qt::TopEdge;
} }
if (anchor & anchor_right) { if (anchor & anchor_right) {
pending.anchor |= Qt::RightEdge; *pending.anchor |= Qt::RightEdge;
} }
if (anchor & anchor_bottom) { if (anchor & anchor_bottom) {
pending.anchor |= Qt::BottomEdge; *pending.anchor |= Qt::BottomEdge;
} }
if (anchor & anchor_left) { 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) 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); wl_resource_post_error(resource->handle, error_invalid_surface_state, "invalid configure serial %d", serial);
return; return;
} }
while (!serials.isEmpty()) { while (!state.serials.isEmpty()) {
const quint32 head = serials.takeFirst(); const quint32 head = state.serials.takeFirst();
if (head == serial) { if (head == serial) {
break; break;
} }
} }
if (!isClosed) { if (!state.closed) {
pending.acknowledgedConfigure = serial; 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); pending.layer = LayerSurfaceV1Interface::Layer(layer);
} }
void LayerSurfaceV1InterfacePrivate::commit() void LayerSurfaceV1InterfacePrivate::apply(LayerSurfaceV1Commit *commit)
{ {
if (isClosed) { if (state.closed) {
return; return;
} }
if (pending.acknowledgedConfigureIsSet) { if (commit->acknowledgedConfigure.has_value()) {
current.acknowledgedConfigure = pending.acknowledgedConfigure; Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value());
pending.acknowledgedConfigureIsSet = false;
Q_EMIT q->configureAcknowledged(pending.acknowledgedConfigure);
} }
if (Q_UNLIKELY(surface->isMapped() && !isConfigured)) { if (Q_UNLIKELY(surface->isMapped() && !state.configured)) {
wl_resource_post_error(resource()->handle, wl_resource_post_error(resource()->handle,
error_invalid_surface_state, error_invalid_surface_state,
"a buffer has been attached to a layer surface prior " "a buffer has been attached to a layer surface prior "
@ -273,57 +278,79 @@ void LayerSurfaceV1InterfacePrivate::commit()
return; return;
} }
if (Q_UNLIKELY(pending.desiredSize.width() == 0 && (!(pending.anchor & Qt::LeftEdge) || !(pending.anchor & Qt::RightEdge)))) { if (commit->desiredSize && commit->desiredSize->width() == 0) {
wl_resource_post_error(resource()->handle, const Qt::Edges anchor = commit->anchor.value_or(state.anchor);
error_invalid_size, if (!(anchor & Qt::LeftEdge) || !(anchor & Qt::RightEdge)) {
"the layer surface has a width of 0 but its anchor " wl_resource_post_error(resource()->handle,
"doesn't include the left and the right screen edge"); error_invalid_size,
return; "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)))) { if (commit->desiredSize && commit->desiredSize->height() == 0) {
wl_resource_post_error(resource()->handle, const Qt::Edges anchor = commit->anchor.value_or(state.anchor);
error_invalid_size, if (!(anchor & Qt::TopEdge) || !(anchor & Qt::BottomEdge)) {
"the layer surface has a height of 0 but its anchor " wl_resource_post_error(resource()->handle,
"doesn't include the top and the bottom screen edge"); error_invalid_size,
return; "the layer surface has a height of 0 but its anchor "
"doesn't include the top and the bottom screen edge");
return;
}
} }
// detect reset // detect reset
if (!surface->isMapped() && firstBufferAttached) { if (!surface->isMapped() && state.firstBufferAttached) {
isCommitted = false; state = LayerSurfaceV1State();
firstBufferAttached = false; pending = LayerSurfaceV1Commit();
isConfigured = false; stashed.clear();
current = LayerSurfaceV1State();
pending = LayerSurfaceV1State();
return; 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()) { 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(); Q_EMIT q->acceptsFocusChanged();
} }
if (previous.layer != current.layer) { if (previous.layer != state.layer) {
Q_EMIT q->layerChanged(); Q_EMIT q->layerChanged();
} }
if (previous.anchor != current.anchor) { if (previous.anchor != state.anchor) {
Q_EMIT q->anchorChanged(); Q_EMIT q->anchorChanged();
} }
if (previous.desiredSize != current.desiredSize) { if (previous.desiredSize != state.desiredSize) {
Q_EMIT q->desiredSizeChanged(); Q_EMIT q->desiredSizeChanged();
} }
if (previous.exclusiveZone != current.exclusiveZone) { if (previous.exclusiveZone != state.exclusiveZone) {
Q_EMIT q->exclusiveZoneChanged(); Q_EMIT q->exclusiveZoneChanged();
} }
if (previous.margins != current.margins) { if (previous.margins != state.margins) {
Q_EMIT q->marginsChanged(); Q_EMIT q->marginsChanged();
} }
} }
@ -336,8 +363,7 @@ LayerSurfaceV1Interface::LayerSurfaceV1Interface(LayerShellV1Interface *shell,
wl_resource *resource) wl_resource *resource)
: d(new LayerSurfaceV1InterfacePrivate(this, surface)) : d(new LayerSurfaceV1InterfacePrivate(this, surface))
{ {
d->current.layer = layer; d->state.layer = layer;
d->pending.layer = layer;
d->shell = shell; d->shell = shell;
d->output = output; d->output = output;
@ -352,7 +378,7 @@ LayerSurfaceV1Interface::~LayerSurfaceV1Interface()
bool LayerSurfaceV1Interface::isCommitted() const bool LayerSurfaceV1Interface::isCommitted() const
{ {
return d->isCommitted; return d->state.committed;
} }
SurfaceInterface *LayerSurfaceV1Interface::surface() const SurfaceInterface *LayerSurfaceV1Interface::surface() const
@ -362,52 +388,52 @@ SurfaceInterface *LayerSurfaceV1Interface::surface() const
Qt::Edges LayerSurfaceV1Interface::anchor() const Qt::Edges LayerSurfaceV1Interface::anchor() const
{ {
return d->current.anchor; return d->state.anchor;
} }
QSize LayerSurfaceV1Interface::desiredSize() const QSize LayerSurfaceV1Interface::desiredSize() const
{ {
return d->current.desiredSize; return d->state.desiredSize;
} }
bool LayerSurfaceV1Interface::acceptsFocus() const bool LayerSurfaceV1Interface::acceptsFocus() const
{ {
return d->current.acceptsFocus; return d->state.acceptsFocus;
} }
LayerSurfaceV1Interface::Layer LayerSurfaceV1Interface::layer() const LayerSurfaceV1Interface::Layer LayerSurfaceV1Interface::layer() const
{ {
return d->current.layer; return d->state.layer;
} }
QMargins LayerSurfaceV1Interface::margins() const QMargins LayerSurfaceV1Interface::margins() const
{ {
return d->current.margins; return d->state.margins;
} }
int LayerSurfaceV1Interface::leftMargin() const int LayerSurfaceV1Interface::leftMargin() const
{ {
return d->current.margins.left(); return d->state.margins.left();
} }
int LayerSurfaceV1Interface::topMargin() const int LayerSurfaceV1Interface::topMargin() const
{ {
return d->current.margins.top(); return d->state.margins.top();
} }
int LayerSurfaceV1Interface::rightMargin() const int LayerSurfaceV1Interface::rightMargin() const
{ {
return d->current.margins.right(); return d->state.margins.right();
} }
int LayerSurfaceV1Interface::bottomMargin() const int LayerSurfaceV1Interface::bottomMargin() const
{ {
return d->current.margins.bottom(); return d->state.margins.bottom();
} }
int LayerSurfaceV1Interface::exclusiveZone() const int LayerSurfaceV1Interface::exclusiveZone() const
{ {
return d->current.exclusiveZone; return d->state.exclusiveZone;
} }
Qt::Edge LayerSurfaceV1Interface::exclusiveEdge() const Qt::Edge LayerSurfaceV1Interface::exclusiveEdge() const
@ -442,25 +468,25 @@ QString LayerSurfaceV1Interface::scope() const
quint32 LayerSurfaceV1Interface::sendConfigure(const QSize &size) quint32 LayerSurfaceV1Interface::sendConfigure(const QSize &size)
{ {
if (d->isClosed) { if (d->state.closed) {
qCWarning(KWIN_CORE) << "Cannot configure a closed layer shell surface"; qCWarning(KWIN_CORE) << "Cannot configure a closed layer shell surface";
return 0; return 0;
} }
const uint32_t serial = d->shell->display()->nextSerial(); const uint32_t serial = d->shell->display()->nextSerial();
d->serials << serial; d->state.serials << serial;
d->send_configure(serial, size.width(), size.height()); d->send_configure(serial, size.width(), size.height());
d->isConfigured = true; d->state.configured = true;
return serial; return serial;
} }
void LayerSurfaceV1Interface::sendClosed() void LayerSurfaceV1Interface::sendClosed()
{ {
if (!d->isClosed) { if (!d->state.closed) {
d->send_closed(); d->send_closed();
d->isClosed = true; d->state.closed = true;
} }
} }

View file

@ -138,27 +138,28 @@ LockedPointerV1InterfacePrivate::LockedPointerV1InterfacePrivate(LockedPointerV1
const QRegion &region, const QRegion &region,
::wl_resource *resource) ::wl_resource *resource)
: QtWaylandServer::zwp_locked_pointer_v1(resource) : QtWaylandServer::zwp_locked_pointer_v1(resource)
, SurfaceExtension(surface)
, q(q) , q(q)
, surface(surface) , surface(surface)
, lifeTime(lifeTime) , 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 QRegion oldRegion = effectiveRegion;
const QPointF oldHint = hint; const QPointF oldHint = hint;
if (hasPendingRegion) { if (commit->region.has_value()) {
region = mapScaleOverride(pendingRegion, surface->scaleOverride()); region = mapScaleOverride(commit->region.value(), surface->scaleOverride());
hasPendingRegion = false;
} }
if (hasPendingHint) { if (commit->hint.has_value()) {
hint = pendingHint / surface->scaleOverride(); hint = commit->hint.value() / surface->scaleOverride();
hasPendingHint = false;
} }
effectiveRegion = surface->input(); 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) 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)); pending.hint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
hasPendingHint = true;
} }
void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource) void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource)
{ {
pendingRegion = regionFromResource(region_resource); pending.region = regionFromResource(region_resource);
hasPendingRegion = true;
} }
LockedPointerV1Interface::LockedPointerV1Interface(SurfaceInterface *surface, LifeTime lifeTime, const QRegion &region, ::wl_resource *resource) LockedPointerV1Interface::LockedPointerV1Interface(SurfaceInterface *surface,
LifeTime lifeTime,
const QRegion &region,
::wl_resource *resource)
: d(new LockedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource)) : d(new LockedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource))
{ {
SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this); SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this);
@ -255,22 +257,24 @@ ConfinedPointerV1InterfacePrivate::ConfinedPointerV1InterfacePrivate(ConfinedPoi
const QRegion &region, const QRegion &region,
::wl_resource *resource) ::wl_resource *resource)
: QtWaylandServer::zwp_confined_pointer_v1(resource) : QtWaylandServer::zwp_confined_pointer_v1(resource)
, SurfaceExtension(surface)
, q(q) , q(q)
, surface(surface) , surface(surface)
, lifeTime(lifeTime) , 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; const QRegion oldRegion = effectiveRegion;
if (hasPendingRegion) { if (commit->region.has_value()) {
region = mapScaleOverride(pendingRegion, surface->scaleOverride()); region = mapScaleOverride(commit->region.value(), surface->scaleOverride());
hasPendingRegion = false;
} }
effectiveRegion = surface->input(); 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) void ConfinedPointerV1InterfacePrivate::zwp_confined_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource)
{ {
pendingRegion = regionFromResource(region_resource); pending.region = regionFromResource(region_resource);
hasPendingRegion = true;
} }
ConfinedPointerV1Interface::ConfinedPointerV1Interface(SurfaceInterface *surface, LifeTime lifeTime, const QRegion &region, ::wl_resource *resource) ConfinedPointerV1Interface::ConfinedPointerV1Interface(SurfaceInterface *surface,
LifeTime lifeTime,
const QRegion &region,
::wl_resource *resource)
: d(new ConfinedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource)) : d(new ConfinedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource))
{ {
SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this); SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this);

View file

@ -8,6 +8,8 @@
#pragma once #pragma once
#include "pointerconstraints_v1_interface.h" #include "pointerconstraints_v1_interface.h"
#include "surface_interface.h"
#include <QPointer> #include <QPointer>
#include "qwayland-server-pointer-constraints-unstable-v1.h" #include "qwayland-server-pointer-constraints-unstable-v1.h"
@ -35,25 +37,27 @@ protected:
void zwp_pointer_constraints_v1_destroy(Resource *resource) override; void zwp_pointer_constraints_v1_destroy(Resource *resource) override;
}; };
class LockedPointerV1InterfacePrivate : public QtWaylandServer::zwp_locked_pointer_v1 struct LockedPointerV1Commit
{
std::optional<QRegion> region;
std::optional<QPointF> hint;
};
class LockedPointerV1InterfacePrivate final : public QtWaylandServer::zwp_locked_pointer_v1, public SurfaceExtension<LockedPointerV1Commit>
{ {
public: public:
static LockedPointerV1InterfacePrivate *get(LockedPointerV1Interface *pointer); static LockedPointerV1InterfacePrivate *get(LockedPointerV1Interface *pointer);
LockedPointerV1InterfacePrivate(LockedPointerV1Interface *q, SurfaceInterface *surface, LockedPointerV1Interface::LifeTime lifeTime, const QRegion &region, ::wl_resource *resource); LockedPointerV1InterfacePrivate(LockedPointerV1Interface *q, SurfaceInterface *surface, LockedPointerV1Interface::LifeTime lifeTime, const QRegion &region, ::wl_resource *resource);
void commit(); void apply(LockedPointerV1Commit *commit) override;
LockedPointerV1Interface *q; LockedPointerV1Interface *q;
QPointer<SurfaceInterface> surface; QPointer<SurfaceInterface> surface;
LockedPointerV1Interface::LifeTime lifeTime; LockedPointerV1Interface::LifeTime lifeTime;
QRegion effectiveRegion; QRegion effectiveRegion;
QRegion region; QRegion region;
QRegion pendingRegion;
QPointF hint = QPointF(-1, -1); QPointF hint = QPointF(-1, -1);
QPointF pendingHint;
bool hasPendingRegion = false;
bool hasPendingHint = false;
bool isLocked = false; bool isLocked = false;
protected: protected:
@ -63,25 +67,29 @@ protected:
void zwp_locked_pointer_v1_set_region(Resource *resource, struct ::wl_resource *region_resource) override; 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<QRegion> region;
};
class ConfinedPointerV1InterfacePrivate final : public QtWaylandServer::zwp_confined_pointer_v1, public SurfaceExtension<ConfinedPointerV1Commit>
{ {
public: public:
static ConfinedPointerV1InterfacePrivate *get(ConfinedPointerV1Interface *pointer); static ConfinedPointerV1InterfacePrivate *get(ConfinedPointerV1Interface *pointer);
ConfinedPointerV1InterfacePrivate(ConfinedPointerV1Interface *q, SurfaceInterface *surface, ConfinedPointerV1InterfacePrivate(ConfinedPointerV1Interface *q,
SurfaceInterface *surface,
ConfinedPointerV1Interface::LifeTime lifeTime, ConfinedPointerV1Interface::LifeTime lifeTime,
const QRegion &region, const QRegion &region,
::wl_resource *resource); ::wl_resource *resource);
void commit(); void apply(ConfinedPointerV1Commit *commit) override;
ConfinedPointerV1Interface *q; ConfinedPointerV1Interface *q;
QPointer<SurfaceInterface> surface; QPointer<SurfaceInterface> surface;
ConfinedPointerV1Interface::LifeTime lifeTime; ConfinedPointerV1Interface::LifeTime lifeTime;
QRegion effectiveRegion; QRegion effectiveRegion;
QRegion region; QRegion region;
QRegion pendingRegion;
bool hasPendingRegion = false;
bool isConfined = false; bool isConfined = false;
protected: protected:

View file

@ -109,8 +109,8 @@ void SubSurfaceInterfacePrivate::subsurface_set_position(Resource *resource, int
SurfaceInterfacePrivate *parentPrivate = SurfaceInterfacePrivate::get(parent); SurfaceInterfacePrivate *parentPrivate = SurfaceInterfacePrivate::get(parent);
parentPrivate->pending.subsurface.position[q] = QPoint(x, y); parentPrivate->pending->subsurface.position[q] = QPoint(x, y);
parentPrivate->pending.subsurfacePositionChanged = true; parentPrivate->pending->subsurfacePositionChanged = true;
} }
void SubSurfaceInterfacePrivate::subsurface_place_above(Resource *resource, struct ::wl_resource *sibling_resource) 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; mode = SubSurfaceInterface::Mode::Desynchronized;
if (!q->isSynchronized()) { if (!q->isSynchronized()) {
auto surfacePrivate = SurfaceInterfacePrivate::get(surface); 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); Q_EMIT q->modeChanged(SubSurfaceInterface::Mode::Desynchronized);
} }
void SubSurfaceInterfacePrivate::commit() void SubSurfaceInterfacePrivate::parentApplyState(quint32 serial)
{
}
void SubSurfaceInterfacePrivate::parentCommit()
{ {
auto parentPrivate = SurfaceInterfacePrivate::get(parent); auto parentPrivate = SurfaceInterfacePrivate::get(parent);
if (parentPrivate->current.subsurfacePositionChanged) { if (parentPrivate->current->subsurfacePositionChanged) {
const QPoint &pos = parentPrivate->current.subsurface.position[q]; const QPoint &pos = parentPrivate->current->subsurface.position[q];
if (position != pos) { if (position != pos) {
position = pos; position = pos;
Q_EMIT q->positionChanged(pos); Q_EMIT q->positionChanged(pos);
@ -188,7 +187,10 @@ void SubSurfaceInterfacePrivate::parentCommit()
if (mode == SubSurfaceInterface::Mode::Synchronized) { if (mode == SubSurfaceInterface::Mode::Synchronized) {
auto surfacePrivate = SurfaceInterfacePrivate::get(surface); 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; 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 } // namespace KWaylandServer
#include "moc_subcompositor_interface.cpp" #include "moc_subcompositor_interface.cpp"

View file

@ -116,7 +116,11 @@ Q_SIGNALS:
private: private:
SubSurfaceInterface(SurfaceInterface *surface, SurfaceInterface *parent, wl_resource *resource); SubSurfaceInterface(SurfaceInterface *surface, SurfaceInterface *parent, wl_resource *resource);
void commit();
std::unique_ptr<SubSurfaceInterfacePrivate> d; std::unique_ptr<SubSurfaceInterfacePrivate> d;
friend class SurfaceInterfacePrivate;
friend class SubSurfaceInterfacePrivate; friend class SubSurfaceInterfacePrivate;
friend class SubCompositorInterfacePrivate; friend class SubCompositorInterfacePrivate;
}; };

View file

@ -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; 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 class SubSurfaceInterfacePrivate : public SurfaceRole, public QtWaylandServer::wl_subsurface
{ {
public: public:
@ -36,14 +42,14 @@ public:
SubSurfaceInterfacePrivate(SubSurfaceInterface *q, SurfaceInterface *surface, SurfaceInterface *parent, ::wl_resource *resource); SubSurfaceInterfacePrivate(SubSurfaceInterface *q, SurfaceInterface *surface, SurfaceInterface *parent, ::wl_resource *resource);
void commit() override; void parentApplyState(quint32 serial);
void parentCommit();
SubSurfaceInterface *q; SubSurfaceInterface *q;
QPoint position = QPoint(0, 0); QPoint position = QPoint(0, 0);
SubSurfaceInterface::Mode mode = SubSurfaceInterface::Mode::Synchronized; SubSurfaceInterface::Mode mode = SubSurfaceInterface::Mode::Synchronized;
QPointer<SurfaceInterface> surface; QPointer<SurfaceInterface> surface;
QPointer<SurfaceInterface> parent; QPointer<SurfaceInterface> parent;
QList<SubSurfaceStateLock> locks;
protected: protected:
void subsurface_destroy_resource(Resource *resource) override; void subsurface_destroy_resource(Resource *resource) override;

View file

@ -40,10 +40,9 @@ static QRegion map_helper(const QMatrix4x4 &matrix, const QRegion &region)
SurfaceInterfacePrivate::SurfaceInterfacePrivate(SurfaceInterface *q) SurfaceInterfacePrivate::SurfaceInterfacePrivate(SurfaceInterface *q)
: q(q) : q(q)
, current(std::make_unique<SurfaceState>())
, pending(std::make_unique<SurfaceState>())
{ {
wl_list_init(&current.frameCallbacks);
wl_list_init(&pending.frameCallbacks);
wl_list_init(&cached.frameCallbacks);
} }
SurfaceInterfacePrivate::~SurfaceInterfacePrivate() SurfaceInterfacePrivate::~SurfaceInterfacePrivate()
@ -51,23 +50,29 @@ SurfaceInterfacePrivate::~SurfaceInterfacePrivate()
wl_resource *resource; wl_resource *resource;
wl_resource *tmp; wl_resource *tmp;
wl_resource_for_each_safe (resource, tmp, &current.frameCallbacks) { wl_resource_for_each_safe (resource, tmp, &current->frameCallbacks) {
wl_resource_destroy(resource); 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_destroy(resource);
} }
wl_resource_for_each_safe (resource, tmp, &cached.frameCallbacks) { for (const auto &stash : std::as_const(stashed)) {
wl_resource_destroy(resource); wl_resource_for_each_safe (resource, tmp, &stash->frameCallbacks) {
wl_resource_destroy(resource);
}
} }
} }
void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child) void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child)
{ {
// protocol is not precise on how to handle the addition of new sub surfaces // protocol is not precise on how to handle the addition of new sub surfaces
pending.subsurface.above.append(child); current->subsurface.above.append(child);
cached.subsurface.above.append(child); pending->subsurface.above.append(child);
current.subsurface.above.append(child);
for (int i = 0; i < stashed.size(); ++i) {
stashed[i]->subsurface.above.append(child);
}
child->surface()->setOutputs(outputs); child->surface()->setOutputs(outputs);
if (preferredBufferScale.has_value()) { if (preferredBufferScale.has_value()) {
child->surface()->setPreferredBufferScale(preferredBufferScale.value()); child->surface()->setPreferredBufferScale(preferredBufferScale.value());
@ -83,15 +88,17 @@ void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child)
void SurfaceInterfacePrivate::removeChild(SubSurfaceInterface *child) void SurfaceInterfacePrivate::removeChild(SubSurfaceInterface *child)
{ {
// protocol is not precise on how to handle the addition of new sub surfaces // protocol is not precise on how to handle the addition of new sub surfaces
pending.subsurface.below.removeAll(child); current->subsurface.below.removeAll(child);
pending.subsurface.above.removeAll(child); current->subsurface.above.removeAll(child);
pending.subsurface.position.remove(child);
cached.subsurface.below.removeAll(child); pending->subsurface.below.removeAll(child);
cached.subsurface.above.removeAll(child); pending->subsurface.above.removeAll(child);
cached.subsurface.position.remove(child);
current.subsurface.below.removeAll(child); for (int i = 0; i < stashed.size(); ++i) {
current.subsurface.above.removeAll(child); stashed[i]->subsurface.below.removeAll(child);
current.subsurface.position.remove(child); stashed[i]->subsurface.above.removeAll(child);
}
Q_EMIT q->childSubSurfaceRemoved(child); Q_EMIT q->childSubSurfaceRemoved(child);
Q_EMIT q->childSubSurfacesChanged(); Q_EMIT q->childSubSurfacesChanged();
} }
@ -103,23 +110,23 @@ bool SurfaceInterfacePrivate::raiseChild(SubSurfaceInterface *subsurface, Surfac
QList<SubSurfaceInterface *> *anchorList; QList<SubSurfaceInterface *> *anchorList;
int anchorIndex; int anchorIndex;
pending.subsurface.below.removeOne(subsurface); pending->subsurface.below.removeOne(subsurface);
pending.subsurface.above.removeOne(subsurface); pending->subsurface.above.removeOne(subsurface);
if (anchor == q) { if (anchor == q) {
// Pretend as if the parent surface were before the first child in the above list. // 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; anchorIndex = -1;
} else if (anchorIndex = pending.subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) { } else if (anchorIndex = pending->subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) {
anchorList = &pending.subsurface.above; anchorList = &pending->subsurface.above;
} else if (anchorIndex = pending.subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) { } else if (anchorIndex = pending->subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) {
anchorList = &pending.subsurface.below; anchorList = &pending->subsurface.below;
} else { } else {
return false; // The anchor belongs to other sub-surface tree. return false; // The anchor belongs to other sub-surface tree.
} }
anchorList->insert(anchorIndex + 1, subsurface); anchorList->insert(anchorIndex + 1, subsurface);
pending.subsurfaceOrderChanged = true; pending->subsurfaceOrderChanged = true;
return true; return true;
} }
@ -130,48 +137,48 @@ bool SurfaceInterfacePrivate::lowerChild(SubSurfaceInterface *subsurface, Surfac
QList<SubSurfaceInterface *> *anchorList; QList<SubSurfaceInterface *> *anchorList;
int anchorIndex; int anchorIndex;
pending.subsurface.below.removeOne(subsurface); pending->subsurface.below.removeOne(subsurface);
pending.subsurface.above.removeOne(subsurface); pending->subsurface.above.removeOne(subsurface);
if (anchor == q) { if (anchor == q) {
// Pretend as if the parent surface were after the last child in the below list. // Pretend as if the parent surface were after the last child in the below list.
anchorList = &pending.subsurface.below; anchorList = &pending->subsurface.below;
anchorIndex = pending.subsurface.below.count(); anchorIndex = pending->subsurface.below.count();
} else if (anchorIndex = pending.subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) { } else if (anchorIndex = pending->subsurface.above.indexOf(anchor->subSurface()); anchorIndex != -1) {
anchorList = &pending.subsurface.above; anchorList = &pending->subsurface.above;
} else if (anchorIndex = pending.subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) { } else if (anchorIndex = pending->subsurface.below.indexOf(anchor->subSurface()); anchorIndex != -1) {
anchorList = &pending.subsurface.below; anchorList = &pending->subsurface.below;
} else { } else {
return false; // The anchor belongs to other sub-surface tree. return false; // The anchor belongs to other sub-surface tree.
} }
anchorList->insert(anchorIndex, subsurface); anchorList->insert(anchorIndex, subsurface);
pending.subsurfaceOrderChanged = true; pending->subsurfaceOrderChanged = true;
return true; return true;
} }
void SurfaceInterfacePrivate::setShadow(const QPointer<ShadowInterface> &shadow) void SurfaceInterfacePrivate::setShadow(const QPointer<ShadowInterface> &shadow)
{ {
pending.shadow = shadow; pending->shadow = shadow;
pending.shadowIsSet = true; pending->shadowIsSet = true;
} }
void SurfaceInterfacePrivate::setBlur(const QPointer<BlurInterface> &blur) void SurfaceInterfacePrivate::setBlur(const QPointer<BlurInterface> &blur)
{ {
pending.blur = blur; pending->blur = blur;
pending.blurIsSet = true; pending->blurIsSet = true;
} }
void SurfaceInterfacePrivate::setSlide(const QPointer<SlideInterface> &slide) void SurfaceInterfacePrivate::setSlide(const QPointer<SlideInterface> &slide)
{ {
pending.slide = slide; pending->slide = slide;
pending.slideIsSet = true; pending->slideIsSet = true;
} }
void SurfaceInterfacePrivate::setContrast(const QPointer<ContrastInterface> &contrast) void SurfaceInterfacePrivate::setContrast(const QPointer<ContrastInterface> &contrast)
{ {
pending.contrast = contrast; pending->contrast = contrast;
pending.contrastIsSet = true; pending->contrastIsSet = true;
} }
void SurfaceInterfacePrivate::installPointerConstraint(LockedPointerV1Interface *lock) void SurfaceInterfacePrivate::installPointerConstraint(LockedPointerV1Interface *lock)
@ -263,23 +270,23 @@ void SurfaceInterfacePrivate::surface_attach(Resource *resource, struct ::wl_res
return; return;
} }
} else { } else {
pending.offset = QPoint(x, y); pending->offset = QPoint(x, y);
} }
pending.bufferIsSet = true; pending->bufferIsSet = true;
if (!buffer) { if (!buffer) {
// got a null buffer, deletes content in next frame // got a null buffer, deletes content in next frame
pending.buffer = nullptr; pending->buffer = nullptr;
pending.damage = QRegion(); pending->damage = QRegion();
pending.bufferDamage = QRegion(); pending->bufferDamage = QRegion();
return; 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) 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) 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_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) void SurfaceInterfacePrivate::surface_set_opaque_region(Resource *resource, struct ::wl_resource *region)
{ {
RegionInterface *r = RegionInterface::get(region); RegionInterface *r = RegionInterface::get(region);
pending.opaque = r ? r->region() : QRegion(); pending->opaque = r ? r->region() : QRegion();
pending.opaqueIsSet = true; pending->opaqueIsSet = true;
} }
void SurfaceInterfacePrivate::surface_set_input_region(Resource *resource, struct ::wl_resource *region) void SurfaceInterfacePrivate::surface_set_input_region(Resource *resource, struct ::wl_resource *region)
{ {
RegionInterface *r = RegionInterface::get(region); RegionInterface *r = RegionInterface::get(region);
pending.input = r ? r->region() : infiniteRegion(); pending->input = r ? r->region() : infiniteRegion();
pending.inputIsSet = true; pending->inputIsSet = true;
} }
void SurfaceInterfacePrivate::surface_commit(Resource *resource) void SurfaceInterfacePrivate::surface_commit(Resource *resource)
{ {
if (subSurface) { if (subSurface) {
commitSubSurface(); subSurface->commit();
} else {
applyState(&pending);
} }
// 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<SurfaceState>();
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) 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); wl_resource_post_error(resource->handle, error_invalid_transform, "buffer transform must be a valid transform (%d specified)", transform);
return; return;
} }
pending.bufferTransform = KWin::OutputTransform::Kind(transform); pending->bufferTransform = KWin::OutputTransform::Kind(transform);
pending.bufferTransformIsSet = true; pending->bufferTransformIsSet = true;
} }
void SurfaceInterfacePrivate::surface_set_buffer_scale(Resource *resource, int32_t scale) 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); wl_resource_post_error(resource->handle, error_invalid_scale, "buffer scale must be at least one (%d specified)", scale);
return; return;
} }
pending.bufferScale = scale; pending->bufferScale = scale;
pending.bufferScaleIsSet = true; pending->bufferScaleIsSet = true;
} }
void SurfaceInterfacePrivate::surface_damage_buffer(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) 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) 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) SurfaceInterface::SurfaceInterface(CompositorInterface *compositor, wl_resource *resource)
@ -398,22 +418,22 @@ void SurfaceInterface::frameRendered(quint32 msec)
wl_resource *resource; wl_resource *resource;
wl_resource *tmp; 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_callback_send_done(resource, msec);
wl_resource_destroy(resource); 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); 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); subsurface->surface()->frameRendered(msec);
} }
} }
bool SurfaceInterface::hasFrameCallbacks() const bool SurfaceInterface::hasFrameCallbacks() const
{ {
return !wl_list_empty(&d->current.frameCallbacks); return !wl_list_empty(&d->current->frameCallbacks);
} }
QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix() QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix()
@ -422,56 +442,56 @@ QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix()
QMatrix4x4 surfaceToBufferMatrix; QMatrix4x4 surfaceToBufferMatrix;
if (!current.buffer) { if (!current->buffer) {
return surfaceToBufferMatrix; return surfaceToBufferMatrix;
} }
surfaceToBufferMatrix.scale(current.bufferScale, current.bufferScale); surfaceToBufferMatrix.scale(current->bufferScale, current->bufferScale);
surfaceToBufferMatrix.scale(scaleOverride, scaleOverride); surfaceToBufferMatrix.scale(scaleOverride, scaleOverride);
switch (current.bufferTransform.kind()) { switch (current->bufferTransform.kind()) {
case KWin::OutputTransform::Normal: case KWin::OutputTransform::Normal:
case KWin::OutputTransform::Flipped: case KWin::OutputTransform::Flipped:
break; break;
case KWin::OutputTransform::Rotated90: case KWin::OutputTransform::Rotated90:
case KWin::OutputTransform::Flipped90: case KWin::OutputTransform::Flipped90:
surfaceToBufferMatrix.translate(0, bufferSize.height() / current.bufferScale); surfaceToBufferMatrix.translate(0, bufferSize.height() / current->bufferScale);
surfaceToBufferMatrix.rotate(-90, 0, 0, 1); surfaceToBufferMatrix.rotate(-90, 0, 0, 1);
break; break;
case KWin::OutputTransform::Rotated180: case KWin::OutputTransform::Rotated180:
case KWin::OutputTransform::Flipped180: 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); surfaceToBufferMatrix.rotate(-180, 0, 0, 1);
break; break;
case KWin::OutputTransform::Rotated270: case KWin::OutputTransform::Rotated270:
case KWin::OutputTransform::Flipped270: case KWin::OutputTransform::Flipped270:
surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, 0); surfaceToBufferMatrix.translate(bufferSize.width() / current->bufferScale, 0);
surfaceToBufferMatrix.rotate(-270, 0, 0, 1); surfaceToBufferMatrix.rotate(-270, 0, 0, 1);
break; break;
} }
switch (current.bufferTransform.kind()) { switch (current->bufferTransform.kind()) {
case KWin::OutputTransform::Flipped: case KWin::OutputTransform::Flipped:
case KWin::OutputTransform::Flipped180: case KWin::OutputTransform::Flipped180:
surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, 0); surfaceToBufferMatrix.translate(bufferSize.width() / current->bufferScale, 0);
surfaceToBufferMatrix.scale(-1, 1); surfaceToBufferMatrix.scale(-1, 1);
break; break;
case KWin::OutputTransform::Flipped90: case KWin::OutputTransform::Flipped90:
case KWin::OutputTransform::Flipped270: case KWin::OutputTransform::Flipped270:
surfaceToBufferMatrix.translate(bufferSize.height() / current.bufferScale, 0); surfaceToBufferMatrix.translate(bufferSize.height() / current->bufferScale, 0);
surfaceToBufferMatrix.scale(-1, 1); surfaceToBufferMatrix.scale(-1, 1);
break; break;
default: default:
break; break;
} }
if (current.viewport.sourceGeometry.isValid()) { if (current->viewport.sourceGeometry.isValid()) {
surfaceToBufferMatrix.translate(current.viewport.sourceGeometry.x(), current.viewport.sourceGeometry.y()); surfaceToBufferMatrix.translate(current->viewport.sourceGeometry.x(), current->viewport.sourceGeometry.y());
} }
QSizeF sourceSize; QSizeF sourceSize;
if (current.viewport.sourceGeometry.isValid()) { if (current->viewport.sourceGeometry.isValid()) {
sourceSize = current.viewport.sourceGeometry.size(); sourceSize = current->viewport.sourceGeometry.size();
} else { } else {
sourceSize = implicitSurfaceSize; sourceSize = implicitSurfaceSize;
} }
@ -485,21 +505,29 @@ QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix()
QRectF SurfaceInterfacePrivate::computeBufferSourceBox() const QRectF SurfaceInterfacePrivate::computeBufferSourceBox() const
{ {
if (!current.viewport.sourceGeometry.isValid()) { if (!current->viewport.sourceGeometry.isValid()) {
return QRectF(0, 0, bufferSize.width(), bufferSize.height()); return QRectF(0, 0, bufferSize.width(), bufferSize.height());
} }
const QSizeF bounds = current.bufferTransform.map(bufferSize); const QSizeF bounds = current->bufferTransform.map(bufferSize);
const QRectF box(current.viewport.sourceGeometry.x() * current.bufferScale, const QRectF box(current->viewport.sourceGeometry.x() * current->bufferScale,
current.viewport.sourceGeometry.y() * current.bufferScale, current->viewport.sourceGeometry.y() * current->bufferScale,
current.viewport.sourceGeometry.width() * current.bufferScale, current->viewport.sourceGeometry.width() * current->bufferScale,
current.viewport.sourceGeometry.height() * 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) void SurfaceState::mergeInto(SurfaceState *target)
{ {
target->serial = serial;
target->locks = locks;
target->bufferIsSet = bufferIsSet; target->bufferIsSet = bufferIsSet;
if (target->bufferIsSet) { if (target->bufferIsSet) {
target->buffer = buffer; target->buffer = buffer;
@ -580,6 +608,7 @@ void SurfaceState::mergeInto(SurfaceState *target)
} }
*this = SurfaceState{}; *this = SurfaceState{};
serial = target->serial;
subsurface = target->subsurface; subsurface = target->subsurface;
wl_list_init(&frameCallbacks); wl_list_init(&frameCallbacks);
} }
@ -588,13 +617,13 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
{ {
const bool bufferChanged = next->bufferIsSet; const bool bufferChanged = next->bufferIsSet;
const bool opaqueRegionChanged = next->opaqueIsSet; 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 shadowChanged = next->shadowIsSet;
const bool blurChanged = next->blurIsSet; const bool blurChanged = next->blurIsSet;
const bool contrastChanged = next->contrastIsSet; const bool contrastChanged = next->contrastIsSet;
const bool slideChanged = next->slideIsSet; const bool slideChanged = next->slideIsSet;
const bool subsurfaceOrderChanged = next->subsurfaceOrderChanged; 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 QSizeF oldSurfaceSize = surfaceSize;
const QSize oldBufferSize = bufferSize; const QSize oldBufferSize = bufferSize;
@ -602,17 +631,17 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
const QMatrix4x4 oldSurfaceToBufferMatrix = surfaceToBufferMatrix; const QMatrix4x4 oldSurfaceToBufferMatrix = surfaceToBufferMatrix;
const QRegion oldInputRegion = inputRegion; const QRegion oldInputRegion = inputRegion;
next->mergeInto(&current); next->mergeInto(current.get());
bufferRef = current.buffer; bufferRef = current->buffer;
scaleOverride = pendingScaleOverride; scaleOverride = pendingScaleOverride;
// TODO: Refactor the state management code because it gets more clumsy. // TODO: Refactor the state management code because it gets more clumsy.
if (current.buffer) { if (current->buffer) {
bufferSize = current.buffer->size(); bufferSize = current->buffer->size();
bufferSourceBox = computeBufferSourceBox(); bufferSourceBox = computeBufferSourceBox();
implicitSurfaceSize = current.buffer->size() / current.bufferScale; implicitSurfaceSize = current->buffer->size() / current->bufferScale;
switch (current.bufferTransform.kind()) { switch (current->bufferTransform.kind()) {
case KWin::OutputTransform::Rotated90: case KWin::OutputTransform::Rotated90:
case KWin::OutputTransform::Rotated270: case KWin::OutputTransform::Rotated270:
case KWin::OutputTransform::Flipped90: case KWin::OutputTransform::Flipped90:
@ -626,21 +655,21 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
break; break;
} }
if (current.viewport.destinationSize.isValid()) { if (current->viewport.destinationSize.isValid()) {
surfaceSize = current.viewport.destinationSize; surfaceSize = current->viewport.destinationSize;
} else if (current.viewport.sourceGeometry.isValid()) { } else if (current->viewport.sourceGeometry.isValid()) {
surfaceSize = current.viewport.sourceGeometry.size(); surfaceSize = current->viewport.sourceGeometry.size();
} else { } else {
surfaceSize = implicitSurfaceSize; surfaceSize = implicitSurfaceSize;
} }
const QRectF surfaceRect(QPoint(0, 0), surfaceSize); 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(); opaqueRegion = surfaceRect.toAlignedRect();
} else { } else {
opaqueRegion = current.opaque & surfaceRect.toAlignedRect(); opaqueRegion = current->opaque & surfaceRect.toAlignedRect();
} }
QMatrix4x4 scaleOverrideMatrix; QMatrix4x4 scaleOverrideMatrix;
@ -663,15 +692,6 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
surfaceToBufferMatrix = buildSurfaceToBufferMatrix(); surfaceToBufferMatrix = buildSurfaceToBufferMatrix();
if (lockedPointer) {
auto lockedPointerPrivate = LockedPointerV1InterfacePrivate::get(lockedPointer);
lockedPointerPrivate->commit();
}
if (confinedPointer) {
auto confinedPointerPrivate = ConfinedPointerV1InterfacePrivate::get(confinedPointer);
confinedPointerPrivate->commit();
}
if (opaqueRegionChanged) { if (opaqueRegionChanged) {
Q_EMIT q->opaqueChanged(opaqueRegion); Q_EMIT q->opaqueChanged(opaqueRegion);
} }
@ -679,7 +699,7 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
Q_EMIT q->inputChanged(inputRegion); Q_EMIT q->inputChanged(inputRegion);
} }
if (transformChanged) { if (transformChanged) {
Q_EMIT q->bufferTransformChanged(current.bufferTransform); Q_EMIT q->bufferTransformChanged(current->bufferTransform);
} }
if (visibilityChanged) { if (visibilityChanged) {
updateEffectiveMapped(); updateEffectiveMapped();
@ -713,56 +733,56 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
} }
if (bufferChanged) { if (bufferChanged) {
if (current.buffer && (!current.damage.isEmpty() || !current.bufferDamage.isEmpty())) { if (current->buffer && (!current->damage.isEmpty() || !current->bufferDamage.isEmpty())) {
const QRect bufferRect = QRect(QPoint(0, 0), current.buffer->size()); const QRect bufferRect = QRect(QPoint(0, 0), current->buffer->size());
bufferDamage = current.bufferDamage bufferDamage = current->bufferDamage
.united(q->mapToBuffer(current.damage)) .united(q->mapToBuffer(current->damage))
.intersected(bufferRect); .intersected(bufferRect);
Q_EMIT q->damaged(bufferDamage); Q_EMIT q->damaged(bufferDamage);
} }
} }
// The position of a sub-surface is applied when its parent is committed. // 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); 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); auto subsurfacePrivate = SubSurfaceInterfacePrivate::get(subsurface);
subsurfacePrivate->parentCommit(); subsurfacePrivate->parentApplyState(next->serial);
}
if (role) {
role->commit();
} }
Q_EMIT q->stateApplied(next->serial);
Q_EMIT q->committed(); Q_EMIT q->committed();
} }
void SurfaceInterfacePrivate::commitSubSurface() quint32 SurfaceInterfacePrivate::lockState(SurfaceState *state)
{ {
if (subSurface->isSynchronized()) { state->locks++;
commitToCache(); return state->serial;
}
void SurfaceInterfacePrivate::unlockState(quint32 serial)
{
if (pending->serial == serial) {
Q_ASSERT(pending->locks > 0);
pending->locks--;
} else { } else {
if (hasCacheState) { for (const auto &state : stashed) {
commitToCache(); if (state->serial == serial) {
commitFromCache(); Q_ASSERT(state->locks > 0);
} else { state->locks--;
applyState(&pending); 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 bool SurfaceInterfacePrivate::computeEffectiveMapped() const
{ {
if (!bufferRef) { if (!bufferRef) {
@ -789,11 +809,11 @@ void SurfaceInterfacePrivate::updateEffectiveMapped()
Q_EMIT q->unmapped(); 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()); auto surfacePrivate = SurfaceInterfacePrivate::get(subsurface->surface());
surfacePrivate->updateEffectiveMapped(); 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()); auto surfacePrivate = SurfaceInterfacePrivate::get(subsurface->surface());
surfacePrivate->updateEffectiveMapped(); surfacePrivate->updateEffectiveMapped();
} }
@ -835,7 +855,7 @@ QRectF SurfaceInterface::bufferSourceBox() const
KWin::OutputTransform SurfaceInterface::bufferTransform() const KWin::OutputTransform SurfaceInterface::bufferTransform() const
{ {
return d->current.bufferTransform; return d->current->bufferTransform;
} }
KWin::GraphicsBuffer *SurfaceInterface::buffer() const KWin::GraphicsBuffer *SurfaceInterface::buffer() const
@ -845,7 +865,7 @@ KWin::GraphicsBuffer *SurfaceInterface::buffer() const
QPoint SurfaceInterface::offset() const QPoint SurfaceInterface::offset() const
{ {
return d->current.offset / d->scaleOverride; return d->current->offset / d->scaleOverride;
} }
SurfaceInterface *SurfaceInterface::get(wl_resource *native) SurfaceInterface *SurfaceInterface::get(wl_resource *native)
@ -866,12 +886,12 @@ SurfaceInterface *SurfaceInterface::get(quint32 id, const ClientConnection *clie
QList<SubSurfaceInterface *> SurfaceInterface::below() const QList<SubSurfaceInterface *> SurfaceInterface::below() const
{ {
return d->current.subsurface.below; return d->current->subsurface.below;
} }
QList<SubSurfaceInterface *> SurfaceInterface::above() const QList<SubSurfaceInterface *> SurfaceInterface::above() const
{ {
return d->current.subsurface.above; return d->current->subsurface.above;
} }
SubSurfaceInterface *SurfaceInterface::subSurface() const SubSurfaceInterface *SurfaceInterface::subSurface() const
@ -888,11 +908,11 @@ QRectF SurfaceInterface::boundingRect() const
{ {
QRectF rect(QPoint(0, 0), size()); 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(); const SurfaceInterface *childSurface = subSurface->surface();
rect |= childSurface->boundingRect().translated(subSurface->position()); 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(); const SurfaceInterface *childSurface = subSurface->surface();
rect |= childSurface->boundingRect().translated(subSurface->position()); rect |= childSurface->boundingRect().translated(subSurface->position());
} }
@ -902,22 +922,22 @@ QRectF SurfaceInterface::boundingRect() const
QPointer<ShadowInterface> SurfaceInterface::shadow() const QPointer<ShadowInterface> SurfaceInterface::shadow() const
{ {
return d->current.shadow; return d->current->shadow;
} }
QPointer<BlurInterface> SurfaceInterface::blur() const QPointer<BlurInterface> SurfaceInterface::blur() const
{ {
return d->current.blur; return d->current->blur;
} }
QPointer<ContrastInterface> SurfaceInterface::contrast() const QPointer<ContrastInterface> SurfaceInterface::contrast() const
{ {
return d->current.contrast; return d->current->contrast;
} }
QPointer<SlideInterface> SurfaceInterface::slideOnShowHide() const QPointer<SlideInterface> SurfaceInterface::slideOnShowHide() const
{ {
return d->current.slide; return d->current->slide;
} }
bool SurfaceInterface::isMapped() const bool SurfaceInterface::isMapped() const
@ -973,10 +993,10 @@ void SurfaceInterface::setOutputs(const QVector<OutputInterface *> &outputs)
} }
d->outputs = 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); 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); child->surface()->setOutputs(outputs);
} }
} }
@ -987,7 +1007,7 @@ SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position)
return nullptr; 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; const SubSurfaceInterface *current = *it;
SurfaceInterface *surface = current->surface(); SurfaceInterface *surface = current->surface();
if (auto s = surface->surfaceAt(position - current->position())) { if (auto s = surface->surfaceAt(position - current->position())) {
@ -1000,7 +1020,7 @@ SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position)
return this; 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; const SubSurfaceInterface *current = *it;
SurfaceInterface *surface = current->surface(); SurfaceInterface *surface = current->surface();
if (auto s = surface->surfaceAt(position - current->position())) { if (auto s = surface->surfaceAt(position - current->position())) {
@ -1018,7 +1038,7 @@ SurfaceInterface *SurfaceInterface::inputSurfaceAt(const QPointF &position)
return nullptr; 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; const SubSurfaceInterface *current = *it;
auto surface = current->surface(); auto surface = current->surface();
if (auto s = surface->inputSurfaceAt(position - current->position())) { if (auto s = surface->inputSurfaceAt(position - current->position())) {
@ -1031,7 +1051,7 @@ SurfaceInterface *SurfaceInterface::inputSurfaceAt(const QPointF &position)
return this; 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; const SubSurfaceInterface *current = *it;
auto surface = current->surface(); auto surface = current->surface();
if (auto s = surface->inputSurfaceAt(position - current->position())) { if (auto s = surface->inputSurfaceAt(position - current->position())) {
@ -1064,7 +1084,7 @@ LinuxDmaBufV1Feedback *SurfaceInterface::dmabufFeedbackV1() const
KWin::ContentType SurfaceInterface::contentType() const KWin::ContentType SurfaceInterface::contentType() const
{ {
return d->current.contentType; return d->current->contentType;
} }
QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const
@ -1126,7 +1146,7 @@ QPointF SurfaceInterface::toSurfaceLocal(const QPointF &point) const
PresentationHint SurfaceInterface::presentationHint() const PresentationHint SurfaceInterface::presentationHint() const
{ {
return d->current.presentationHint; return d->current->presentationHint;
} }
void SurfaceInterface::setPreferredBufferScale(qreal scale) void SurfaceInterface::setPreferredBufferScale(qreal scale)
@ -1143,10 +1163,10 @@ void SurfaceInterface::setPreferredBufferScale(qreal scale)
d->send_preferred_buffer_scale(std::ceil(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); child->surface()->setPreferredBufferScale(scale);
} }
for (auto child : qAsConst(d->current.subsurface.above)) { for (auto child : qAsConst(d->current->subsurface.above)) {
child->surface()->setPreferredBufferScale(scale); child->surface()->setPreferredBufferScale(scale);
} }
} }
@ -1162,10 +1182,10 @@ void SurfaceInterface::setPreferredBufferTransform(KWin::OutputTransform transfo
d->send_preferred_buffer_transform(uint32_t(transform.kind())); 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); child->surface()->setPreferredBufferTransform(transform);
} }
for (auto child : qAsConst(d->current.subsurface.above)) { for (auto child : qAsConst(d->current->subsurface.above)) {
child->surface()->setPreferredBufferTransform(transform); child->surface()->setPreferredBufferTransform(transform);
} }
} }

View file

@ -430,11 +430,64 @@ Q_SIGNALS:
*/ */
void committed(); 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: private:
std::unique_ptr<SurfaceInterfacePrivate> d; std::unique_ptr<SurfaceInterfacePrivate> d;
friend class SurfaceInterfacePrivate; 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<typename Commit>
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<quint32, Commit> 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 *) Q_DECLARE_METATYPE(KWaylandServer::SurfaceInterface *)

View file

@ -15,6 +15,7 @@
// Wayland // Wayland
#include "qwayland-server-wayland.h" #include "qwayland-server-wayland.h"
// C++ // C++
#include <deque>
#include <optional> #include <optional>
namespace KWaylandServer namespace KWaylandServer
@ -28,8 +29,13 @@ class FractionalScaleV1Interface;
struct SurfaceState struct SurfaceState
{ {
SurfaceState();
void mergeInto(SurfaceState *target); void mergeInto(SurfaceState *target);
quint32 serial = 0;
quint32 locks = 0;
QRegion damage = QRegion(); QRegion damage = QRegion();
QRegion bufferDamage = QRegion(); QRegion bufferDamage = QRegion();
QRegion opaque = QRegion(); QRegion opaque = QRegion();
@ -103,14 +109,13 @@ public:
void installPointerConstraint(ConfinedPointerV1Interface *confinement); void installPointerConstraint(ConfinedPointerV1Interface *confinement);
void installIdleInhibitor(IdleInhibitorV1Interface *inhibitor); void installIdleInhibitor(IdleInhibitorV1Interface *inhibitor);
void commitToCache();
void commitFromCache();
void commitSubSurface();
QMatrix4x4 buildSurfaceToBufferMatrix(); QMatrix4x4 buildSurfaceToBufferMatrix();
QRectF computeBufferSourceBox() const; QRectF computeBufferSourceBox() const;
void applyState(SurfaceState *next); void applyState(SurfaceState *next);
quint32 lockState(SurfaceState *state);
void unlockState(quint32 serial);
bool computeEffectiveMapped() const; bool computeEffectiveMapped() const;
void updateEffectiveMapped(); void updateEffectiveMapped();
@ -124,9 +129,9 @@ public:
CompositorInterface *compositor; CompositorInterface *compositor;
SurfaceInterface *q; SurfaceInterface *q;
SurfaceRole *role = nullptr; SurfaceRole *role = nullptr;
SurfaceState current; std::unique_ptr<SurfaceState> current;
SurfaceState pending; std::unique_ptr<SurfaceState> pending;
SurfaceState cached; std::deque<std::unique_ptr<SurfaceState>> stashed;
SubSurfaceInterface *subSurface = nullptr; SubSurfaceInterface *subSurface = nullptr;
QMatrix4x4 surfaceToBufferMatrix; QMatrix4x4 surfaceToBufferMatrix;
QSize bufferSize = QSize(0, 0); QSize bufferSize = QSize(0, 0);
@ -139,7 +144,6 @@ public:
KWin::GraphicsBufferRef bufferRef; KWin::GraphicsBufferRef bufferRef;
QRegion bufferDamage; QRegion bufferDamage;
bool mapped = false; bool mapped = false;
bool hasCacheState = false;
qreal scaleOverride = 1.; qreal scaleOverride = 1.;
qreal pendingScaleOverride = 1.; qreal pendingScaleOverride = 1.;

View file

@ -24,8 +24,6 @@ public:
return m_surface; return m_surface;
} }
virtual void commit() = 0;
static SurfaceRole *get(SurfaceInterface *surface); static SurfaceRole *get(SurfaceInterface *surface);
private: private:

View file

@ -74,8 +74,8 @@ TearingControlV1Interface::~TearingControlV1Interface()
{ {
if (m_surface) { if (m_surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
surfacePrivate->pending.presentationHint = PresentationHint::VSync; surfacePrivate->pending->presentationHint = PresentationHint::VSync;
surfacePrivate->pending.tearingIsSet = true; surfacePrivate->pending->tearingIsSet = true;
surfacePrivate->tearing = nullptr; surfacePrivate->tearing = nullptr;
} }
} }
@ -85,11 +85,11 @@ void TearingControlV1Interface::wp_tearing_control_v1_set_presentation_hint(Reso
if (m_surface) { if (m_surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
if (hint == presentation_hint::presentation_hint_async) { if (hint == presentation_hint::presentation_hint_async) {
surfacePrivate->pending.presentationHint = PresentationHint::Async; surfacePrivate->pending->presentationHint = PresentationHint::Async;
} else { } else {
surfacePrivate->pending.presentationHint = PresentationHint::VSync; surfacePrivate->pending->presentationHint = PresentationHint::VSync;
} }
surfacePrivate->pending.tearingIsSet = true; surfacePrivate->pending->tearingIsSet = true;
} }
} }

View file

@ -70,10 +70,10 @@ void ViewportInterface::wp_viewport_destroy(Resource *resource)
{ {
if (surface) { if (surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->pending.viewport.sourceGeometry = QRectF(); surfacePrivate->pending->viewport.sourceGeometry = QRectF();
surfacePrivate->pending.viewport.sourceGeometryIsSet = true; surfacePrivate->pending->viewport.sourceGeometryIsSet = true;
surfacePrivate->pending.viewport.destinationSize = QSize(); surfacePrivate->pending->viewport.destinationSize = QSize();
surfacePrivate->pending.viewport.destinationSizeIsSet = true; surfacePrivate->pending->viewport.destinationSizeIsSet = true;
} }
wl_resource_destroy(resource->handle); 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) { if (x == -1 && y == -1 && width == -1 && height == -1) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->pending.viewport.sourceGeometry = QRectF(); surfacePrivate->pending->viewport.sourceGeometry = QRectF();
surfacePrivate->pending.viewport.sourceGeometryIsSet = true; surfacePrivate->pending->viewport.sourceGeometryIsSet = true;
return; return;
} }
@ -104,8 +104,8 @@ void ViewportInterface::wp_viewport_set_source(Resource *resource, wl_fixed_t x_
} }
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->pending.viewport.sourceGeometry = QRectF(x, y, width, height); surfacePrivate->pending->viewport.sourceGeometry = QRectF(x, y, width, height);
surfacePrivate->pending.viewport.sourceGeometryIsSet = true; surfacePrivate->pending->viewport.sourceGeometryIsSet = true;
} }
void ViewportInterface::wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height) 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) { if (width == -1 && height == -1) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->pending.viewport.destinationSize = QSize(); surfacePrivate->pending->viewport.destinationSize = QSize();
surfacePrivate->pending.viewport.destinationSizeIsSet = true; surfacePrivate->pending->viewport.destinationSizeIsSet = true;
return; return;
} }
@ -128,8 +128,8 @@ void ViewportInterface::wp_viewport_set_destination(Resource *resource, int32_t
} }
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface); SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->pending.viewport.destinationSize = QSize(width, height); surfacePrivate->pending->viewport.destinationSize = QSize(width, height);
surfacePrivate->pending.viewport.destinationSizeIsSet = true; surfacePrivate->pending->viewport.destinationSizeIsSet = true;
} }
ViewporterInterface::ViewporterInterface(Display *display, QObject *parent) ViewporterInterface::ViewporterInterface(Display *display, QObject *parent)

View file

@ -142,7 +142,6 @@ void XdgSurfaceInterfacePrivate::unassignRole()
{ {
toplevel = nullptr; toplevel = nullptr;
popup = nullptr; popup = nullptr;
current = nullptr;
pending = nullptr; pending = nullptr;
} }
@ -150,46 +149,40 @@ void XdgSurfaceInterfacePrivate::assignRole(XdgToplevelInterface *toplevel)
{ {
this->toplevel = toplevel; this->toplevel = toplevel;
XdgSurfaceRole<XdgToplevelState> *role = XdgToplevelInterfacePrivate::get(toplevel); XdgSurfaceRole<XdgToplevelCommit> *role = XdgToplevelInterfacePrivate::get(toplevel);
current = &role->current.base; pending = &role->pending;
pending = &role->pending.base;
} }
void XdgSurfaceInterfacePrivate::assignRole(XdgPopupInterface *popup) void XdgSurfaceInterfacePrivate::assignRole(XdgPopupInterface *popup)
{ {
this->popup = popup; this->popup = popup;
XdgSurfaceRole<XdgPopupState> *role = XdgPopupInterfacePrivate::get(popup); XdgSurfaceRole<XdgPopupCommit> *role = XdgPopupInterfacePrivate::get(popup);
current = &role->current.base; pending = &role->pending;
pending = &role->pending.base;
} }
void XdgSurfaceInterfacePrivate::applyState(XdgSurfaceState *next) void XdgSurfaceInterfacePrivate::apply(XdgSurfaceCommit *commit)
{ {
if (surface->buffer()) { if (surface->buffer()) {
firstBufferAttached = true; firstBufferAttached = true;
} }
if (next->acknowledgedConfigureIsSet) { if (commit->acknowledgedConfigure.has_value()) {
current->acknowledgedConfigure = next->acknowledgedConfigure; Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value());
next->acknowledgedConfigureIsSet = false;
Q_EMIT q->configureAcknowledged(current->acknowledgedConfigure);
} }
if (next->windowGeometryIsSet) { if (commit->windowGeometry.has_value()) {
current->windowGeometry = next->windowGeometry; windowGeometry = commit->windowGeometry.value();
next->windowGeometryIsSet = false; Q_EMIT q->windowGeometryChanged(windowGeometry);
Q_EMIT q->windowGeometryChanged(current->windowGeometry);
} }
} }
void XdgSurfaceInterfacePrivate::resetState() void XdgSurfaceInterfacePrivate::reset()
{ {
firstBufferAttached = false; firstBufferAttached = false;
isConfigured = false; isConfigured = false;
isInitialized = false; isInitialized = false;
*current = XdgSurfaceState{}; windowGeometry = QRect();
*pending = XdgSurfaceState{};
Q_EMIT q->resetOccurred(); 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->windowGeometry = QRect(x, y, width, height);
pending->windowGeometryIsSet = true;
} }
void XdgSurfaceInterfacePrivate::xdg_surface_ack_configure(Resource *resource, uint32_t serial) 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->acknowledgedConfigure = serial;
pending->acknowledgedConfigureIsSet = true;
} }
XdgSurfaceInterface::XdgSurfaceInterface(XdgShellInterface *shell, SurfaceInterface *surface, ::wl_resource *resource) XdgSurfaceInterface::XdgSurfaceInterface(XdgShellInterface *shell, SurfaceInterface *surface, ::wl_resource *resource)
@ -333,7 +324,7 @@ bool XdgSurfaceInterface::isConfigured() const
QRect XdgSurfaceInterface::windowGeometry() const QRect XdgSurfaceInterface::windowGeometry() const
{ {
return d->current->windowGeometry; return d->windowGeometry;
} }
XdgSurfaceInterface *XdgSurfaceInterface::get(::wl_resource *resource) 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); auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) { if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) {
@ -359,15 +350,15 @@ void XdgToplevelInterfacePrivate::commit()
return; return;
} }
xdgSurfacePrivate->applyState(&pending.base); xdgSurfacePrivate->apply(commit);
if (current.minimumSize != pending.minimumSize) { if (commit->minimumSize && commit->minimumSize != minimumSize) {
current.minimumSize = pending.minimumSize; minimumSize = commit->minimumSize.value();
Q_EMIT q->minimumSizeChanged(current.minimumSize); Q_EMIT q->minimumSizeChanged(minimumSize);
} }
if (current.maximumSize != pending.maximumSize) { if (commit->maximumSize && commit->maximumSize != maximumSize) {
current.maximumSize = pending.maximumSize; maximumSize = commit->maximumSize.value();
Q_EMIT q->maximumSizeChanged(current.maximumSize); Q_EMIT q->maximumSizeChanged(maximumSize);
} }
if (!xdgSurfacePrivate->isInitialized) { if (!xdgSurfacePrivate->isInitialized) {
@ -379,12 +370,14 @@ void XdgToplevelInterfacePrivate::commit()
void XdgToplevelInterfacePrivate::reset() void XdgToplevelInterfacePrivate::reset()
{ {
auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
xdgSurfacePrivate->resetState(); xdgSurfacePrivate->reset();
windowTitle = QString(); windowTitle = QString();
windowClass = QString(); windowClass = QString();
current = XdgToplevelState{}; minimumSize = QSize();
pending = XdgToplevelState{}; maximumSize = QSize();
pending = XdgToplevelCommit{};
stashed.clear();
Q_EMIT q->resetOccurred(); Q_EMIT q->resetOccurred();
} }
@ -574,12 +567,12 @@ QString XdgToplevelInterface::windowClass() const
QSize XdgToplevelInterface::minimumSize() 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 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) 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) { if (!parentSurface) {
auto shellPrivate = XdgShellInterfacePrivate::get(xdgSurface->shell()); auto shellPrivate = XdgShellInterfacePrivate::get(xdgSurface->shell());
@ -709,7 +702,7 @@ void XdgPopupInterfacePrivate::commit()
return; return;
} }
xdgSurfacePrivate->applyState(&pending.base); xdgSurfacePrivate->apply(commit);
if (!xdgSurfacePrivate->isInitialized) { if (!xdgSurfacePrivate->isInitialized) {
Q_EMIT q->initializeRequested(); Q_EMIT q->initializeRequested();
@ -720,7 +713,9 @@ void XdgPopupInterfacePrivate::commit()
void XdgPopupInterfacePrivate::reset() void XdgPopupInterfacePrivate::reset()
{ {
auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
xdgSurfacePrivate->resetState(); pending = XdgPopupCommit{};
stashed.clear();
xdgSurfacePrivate->reset();
} }
void XdgPopupInterfacePrivate::xdg_popup_destroy_resource(Resource *resource) void XdgPopupInterfacePrivate::xdg_popup_destroy_resource(Resource *resource)

View file

@ -9,7 +9,7 @@
#include "qwayland-server-xdg-shell.h" #include "qwayland-server-xdg-shell.h"
#include "xdgshell_interface.h" #include "xdgshell_interface.h"
#include "surface_interface.h" #include "surface_interface_p.h"
#include "surfacerole_p.h" #include "surfacerole_p.h"
namespace KWaylandServer namespace KWaylandServer
@ -83,37 +83,31 @@ protected:
void xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial) override; void xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial) override;
}; };
struct XdgSurfaceState struct XdgSurfaceCommit
{ {
QRect windowGeometry; std::optional<QRect> windowGeometry;
quint32 acknowledgedConfigure; std::optional<quint32> acknowledgedConfigure;
bool acknowledgedConfigureIsSet = false;
bool windowGeometryIsSet = false;
}; };
struct XdgToplevelState struct XdgToplevelCommit : XdgSurfaceCommit
{ {
XdgSurfaceState base; std::optional<QSize> minimumSize;
QSize minimumSize; std::optional<QSize> maximumSize;
QSize maximumSize;
}; };
struct XdgPopupState struct XdgPopupCommit : XdgSurfaceCommit
{ {
XdgSurfaceState base;
}; };
template<typename State> template<typename Commit>
class XdgSurfaceRole : public SurfaceRole class XdgSurfaceRole : public SurfaceRole, public SurfaceExtension<Commit>
{ {
public: public:
XdgSurfaceRole(SurfaceInterface *surface, const QByteArray &name) XdgSurfaceRole(SurfaceInterface *surface, const QByteArray &name)
: SurfaceRole(surface, name) : SurfaceRole(surface, name)
, SurfaceExtension<Commit>(surface)
{ {
} }
State pending;
State current;
}; };
class XdgSurfaceInterfacePrivate : public QtWaylandServer::xdg_surface class XdgSurfaceInterfacePrivate : public QtWaylandServer::xdg_surface
@ -121,22 +115,22 @@ class XdgSurfaceInterfacePrivate : public QtWaylandServer::xdg_surface
public: public:
XdgSurfaceInterfacePrivate(XdgSurfaceInterface *xdgSurface); XdgSurfaceInterfacePrivate(XdgSurfaceInterface *xdgSurface);
void applyState(XdgSurfaceState *next); void apply(XdgSurfaceCommit *commit);
void resetState(); void reset();
void unassignRole(); void unassignRole();
void assignRole(XdgToplevelInterface *toplevel); void assignRole(XdgToplevelInterface *toplevel);
void assignRole(XdgPopupInterface *popup); void assignRole(XdgPopupInterface *popup);
// These two point into XdgSurfaceRole's state and are valid as long as a role is assigned. // These two point into XdgSurfaceRole's state and are valid as long as a role is assigned.
XdgSurfaceState *current = nullptr; XdgSurfaceCommit *pending = nullptr;
XdgSurfaceState *pending = nullptr;
XdgSurfaceInterface *q; XdgSurfaceInterface *q;
XdgShellInterface *shell = nullptr; XdgShellInterface *shell = nullptr;
QPointer<XdgToplevelInterface> toplevel; QPointer<XdgToplevelInterface> toplevel;
QPointer<XdgPopupInterface> popup; QPointer<XdgPopupInterface> popup;
QPointer<SurfaceInterface> surface; QPointer<SurfaceInterface> surface;
QRect windowGeometry;
bool firstBufferAttached = false; bool firstBufferAttached = false;
bool isConfigured = false; bool isConfigured = false;
bool isInitialized = false; bool isInitialized = false;
@ -152,12 +146,12 @@ protected:
void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override; void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override;
}; };
class XdgToplevelInterfacePrivate : public XdgSurfaceRole<XdgToplevelState>, public QtWaylandServer::xdg_toplevel class XdgToplevelInterfacePrivate : public XdgSurfaceRole<XdgToplevelCommit>, public QtWaylandServer::xdg_toplevel
{ {
public: public:
XdgToplevelInterfacePrivate(XdgToplevelInterface *toplevel, XdgSurfaceInterface *surface); XdgToplevelInterfacePrivate(XdgToplevelInterface *toplevel, XdgSurfaceInterface *surface);
void commit() override; void apply(XdgToplevelCommit *commit) override;
void reset(); void reset();
static XdgToplevelInterfacePrivate *get(XdgToplevelInterface *toplevel); static XdgToplevelInterfacePrivate *get(XdgToplevelInterface *toplevel);
@ -169,6 +163,8 @@ public:
XdgSurfaceInterface *xdgSurface; XdgSurfaceInterface *xdgSurface;
QString windowTitle; QString windowTitle;
QString windowClass; QString windowClass;
QSize minimumSize;
QSize maximumSize;
protected: protected:
void xdg_toplevel_destroy_resource(Resource *resource) override; void xdg_toplevel_destroy_resource(Resource *resource) override;
@ -188,14 +184,14 @@ protected:
void xdg_toplevel_set_minimized(Resource *resource) override; void xdg_toplevel_set_minimized(Resource *resource) override;
}; };
class XdgPopupInterfacePrivate : public XdgSurfaceRole<XdgPopupState>, public QtWaylandServer::xdg_popup class XdgPopupInterfacePrivate : public XdgSurfaceRole<XdgPopupCommit>, public QtWaylandServer::xdg_popup
{ {
public: public:
static XdgPopupInterfacePrivate *get(XdgPopupInterface *popup); static XdgPopupInterfacePrivate *get(XdgPopupInterface *popup);
XdgPopupInterfacePrivate(XdgPopupInterface *popup, XdgSurfaceInterface *surface); XdgPopupInterfacePrivate(XdgPopupInterface *popup, XdgSurfaceInterface *surface);
void commit() override; void apply(XdgPopupCommit *commit) override;
void reset(); void reset();
XdgPopupInterface *q; XdgPopupInterface *q;

View file

@ -7,8 +7,8 @@
#include "xwaylandshell_v1_interface.h" #include "xwaylandshell_v1_interface.h"
#include "display.h" #include "display.h"
#include "surface_interface_p.h"
#include "surfacerole_p.h" #include "surfacerole_p.h"
#include "surface_interface.h"
#include "qwayland-server-xwayland-shell-v1.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; void xwayland_shell_v1_get_xwayland_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface) override;
}; };
struct XwaylandSurfaceV1State struct XwaylandSurfaceV1Commit
{ {
std::optional<uint64_t> serial; std::optional<uint64_t> serial;
}; };
class XwaylandSurfaceV1InterfacePrivate : public SurfaceRole, public QtWaylandServer::xwayland_surface_v1 class XwaylandSurfaceV1InterfacePrivate : public SurfaceRole, public SurfaceExtension<XwaylandSurfaceV1Commit>, public QtWaylandServer::xwayland_surface_v1
{ {
public: public:
XwaylandSurfaceV1InterfacePrivate(XwaylandShellV1Interface *shell, SurfaceInterface *surface, wl_client *client, uint32_t id, int version, XwaylandSurfaceV1Interface *q); 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; XwaylandSurfaceV1Interface *q;
XwaylandShellV1Interface *shell; XwaylandShellV1Interface *shell;
std::optional<uint64_t> serial;
XwaylandSurfaceV1State current;
XwaylandSurfaceV1State pending;
protected: protected:
void xwayland_surface_v1_destroy_resource(Resource *resource) override; 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) XwaylandSurfaceV1InterfacePrivate::XwaylandSurfaceV1InterfacePrivate(XwaylandShellV1Interface *shell, SurfaceInterface *surface, wl_client *client, uint32_t id, int version, XwaylandSurfaceV1Interface *q)
: SurfaceRole(surface, QByteArrayLiteral("xwayland_surface_v1")) : SurfaceRole(surface, QByteArrayLiteral("xwayland_surface_v1"))
, SurfaceExtension(surface)
, QtWaylandServer::xwayland_surface_v1(client, id, version) , QtWaylandServer::xwayland_surface_v1(client, id, version)
, q(q) , q(q)
, shell(shell) , shell(shell)
{ {
} }
void XwaylandSurfaceV1InterfacePrivate::commit() void XwaylandSurfaceV1InterfacePrivate::apply(XwaylandSurfaceV1Commit *commit)
{ {
if (pending.serial.has_value()) { if (commit->serial.has_value()) {
current.serial = std::exchange(pending.serial, std::nullopt); serial = commit->serial;
Q_EMIT shell->surfaceAssociated(q); Q_EMIT shell->surfaceAssociated(q);
} }
} }
@ -109,9 +108,9 @@ void XwaylandSurfaceV1InterfacePrivate::xwayland_surface_v1_set_serial(Resource
return; return;
} }
if (current.serial.has_value()) { if (this->serial.has_value()) {
wl_resource_post_error(resource->handle, error_already_associated, 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; return;
} }
@ -159,7 +158,7 @@ SurfaceInterface *XwaylandSurfaceV1Interface::surface() const
std::optional<uint64_t> XwaylandSurfaceV1Interface::serial() const std::optional<uint64_t> XwaylandSurfaceV1Interface::serial() const
{ {
return d->current.serial; return d->serial;
} }
} // namespace KWaylandServer } // namespace KWaylandServer