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;
}
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
surfacePrivate->pending.contentType = waylandToKwinContentType(content_type);
surfacePrivate->pending.contentTypeIsSet = true;
surfacePrivate->pending->contentType = waylandToKwinContentType(content_type);
surfacePrivate->pending->contentTypeIsSet = true;
}
void ContentTypeV1Interface::wp_content_type_v1_destroy(Resource *resource)
{
if (m_surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
surfacePrivate->pending.contentType = KWin::ContentType::None;
surfacePrivate->pending.contentTypeIsSet = true;
surfacePrivate->pending->contentType = KWin::ContentType::None;
surfacePrivate->pending->contentTypeIsSet = true;
}
wl_resource_destroy(resource->handle);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -138,27 +138,28 @@ LockedPointerV1InterfacePrivate::LockedPointerV1InterfacePrivate(LockedPointerV1
const QRegion &region,
::wl_resource *resource)
: QtWaylandServer::zwp_locked_pointer_v1(resource)
, SurfaceExtension(surface)
, q(q)
, surface(surface)
, lifeTime(lifeTime)
, pendingRegion(region)
, hasPendingRegion(true)
{
commit();
pending.region = region;
apply(&pending);
pending = LockedPointerV1Commit{};
}
void LockedPointerV1InterfacePrivate::commit()
void LockedPointerV1InterfacePrivate::apply(LockedPointerV1Commit *commit)
{
const QRegion oldRegion = effectiveRegion;
const QPointF oldHint = hint;
if (hasPendingRegion) {
region = mapScaleOverride(pendingRegion, surface->scaleOverride());
hasPendingRegion = false;
if (commit->region.has_value()) {
region = mapScaleOverride(commit->region.value(), surface->scaleOverride());
}
if (hasPendingHint) {
hint = pendingHint / surface->scaleOverride();
hasPendingHint = false;
if (commit->hint.has_value()) {
hint = commit->hint.value() / surface->scaleOverride();
}
effectiveRegion = surface->input();
@ -187,17 +188,18 @@ void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_destroy(Resource *re
void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_set_cursor_position_hint(Resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
pendingHint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
hasPendingHint = true;
pending.hint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
}
void LockedPointerV1InterfacePrivate::zwp_locked_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource)
{
pendingRegion = regionFromResource(region_resource);
hasPendingRegion = true;
pending.region = regionFromResource(region_resource);
}
LockedPointerV1Interface::LockedPointerV1Interface(SurfaceInterface *surface, LifeTime lifeTime, const QRegion &region, ::wl_resource *resource)
LockedPointerV1Interface::LockedPointerV1Interface(SurfaceInterface *surface,
LifeTime lifeTime,
const QRegion &region,
::wl_resource *resource)
: d(new LockedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource))
{
SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this);
@ -255,22 +257,24 @@ ConfinedPointerV1InterfacePrivate::ConfinedPointerV1InterfacePrivate(ConfinedPoi
const QRegion &region,
::wl_resource *resource)
: QtWaylandServer::zwp_confined_pointer_v1(resource)
, SurfaceExtension(surface)
, q(q)
, surface(surface)
, lifeTime(lifeTime)
, pendingRegion(region)
, hasPendingRegion(true)
{
commit();
pending.region = region;
apply(&pending);
pending = ConfinedPointerV1Commit{};
}
void ConfinedPointerV1InterfacePrivate::commit()
void ConfinedPointerV1InterfacePrivate::apply(ConfinedPointerV1Commit *commit)
{
const QRegion oldRegion = effectiveRegion;
if (hasPendingRegion) {
region = mapScaleOverride(pendingRegion, surface->scaleOverride());
hasPendingRegion = false;
if (commit->region.has_value()) {
region = mapScaleOverride(commit->region.value(), surface->scaleOverride());
}
effectiveRegion = surface->input();
@ -295,11 +299,13 @@ void ConfinedPointerV1InterfacePrivate::zwp_confined_pointer_v1_destroy(Resource
void ConfinedPointerV1InterfacePrivate::zwp_confined_pointer_v1_set_region(Resource *resource, ::wl_resource *region_resource)
{
pendingRegion = regionFromResource(region_resource);
hasPendingRegion = true;
pending.region = regionFromResource(region_resource);
}
ConfinedPointerV1Interface::ConfinedPointerV1Interface(SurfaceInterface *surface, LifeTime lifeTime, const QRegion &region, ::wl_resource *resource)
ConfinedPointerV1Interface::ConfinedPointerV1Interface(SurfaceInterface *surface,
LifeTime lifeTime,
const QRegion &region,
::wl_resource *resource)
: d(new ConfinedPointerV1InterfacePrivate(this, surface, lifeTime, region, resource))
{
SurfaceInterfacePrivate::get(surface)->installPointerConstraint(this);

View file

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

View file

@ -109,8 +109,8 @@ void SubSurfaceInterfacePrivate::subsurface_set_position(Resource *resource, int
SurfaceInterfacePrivate *parentPrivate = SurfaceInterfacePrivate::get(parent);
parentPrivate->pending.subsurface.position[q] = QPoint(x, y);
parentPrivate->pending.subsurfacePositionChanged = true;
parentPrivate->pending->subsurface.position[q] = QPoint(x, y);
parentPrivate->pending->subsurfacePositionChanged = true;
}
void SubSurfaceInterfacePrivate::subsurface_place_above(Resource *resource, struct ::wl_resource *sibling_resource)
@ -166,20 +166,19 @@ void SubSurfaceInterfacePrivate::subsurface_set_desync(Resource *)
mode = SubSurfaceInterface::Mode::Desynchronized;
if (!q->isSynchronized()) {
auto surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->commitFromCache();
while (!locks.isEmpty()) {
SubSurfaceStateLock lock = locks.takeFirst();
surfacePrivate->unlockState(lock.serial);
}
}
Q_EMIT q->modeChanged(SubSurfaceInterface::Mode::Desynchronized);
}
void SubSurfaceInterfacePrivate::commit()
{
}
void SubSurfaceInterfacePrivate::parentCommit()
void SubSurfaceInterfacePrivate::parentApplyState(quint32 serial)
{
auto parentPrivate = SurfaceInterfacePrivate::get(parent);
if (parentPrivate->current.subsurfacePositionChanged) {
const QPoint &pos = parentPrivate->current.subsurface.position[q];
if (parentPrivate->current->subsurfacePositionChanged) {
const QPoint &pos = parentPrivate->current->subsurface.position[q];
if (position != pos) {
position = pos;
Q_EMIT q->positionChanged(pos);
@ -188,7 +187,10 @@ void SubSurfaceInterfacePrivate::parentCommit()
if (mode == SubSurfaceInterface::Mode::Synchronized) {
auto surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->commitFromCache();
while (!locks.isEmpty() && locks[0].parentSerial == serial) {
SubSurfaceStateLock lock = locks.takeFirst();
surfacePrivate->unlockState(lock.serial);
}
}
}
@ -266,6 +268,24 @@ SurfaceInterface *SubSurfaceInterface::mainSurface() const
return d->parent;
}
void SubSurfaceInterface::commit()
{
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(d->surface);
if (isSynchronized()) {
const quint32 serial = surfacePrivate->lockState(surfacePrivate->pending.get());
const quint32 parentSerial = SurfaceInterfacePrivate::get(d->parent)->pending->serial;
d->locks.append(SubSurfaceStateLock{
.serial = serial,
.parentSerial = parentSerial,
});
} else {
while (!d->locks.isEmpty()) {
SubSurfaceStateLock lock = d->locks.takeFirst();
surfacePrivate->unlockState(lock.serial);
}
}
}
} // namespace KWaylandServer
#include "moc_subcompositor_interface.cpp"

View file

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

View file

@ -430,11 +430,64 @@ Q_SIGNALS:
*/
void committed();
/**
* This signal is emitted when a surface commit with the specified \a serial has been cached
* to be applied later.
*/
void stateStashed(quint32 serial);
/**
* This signal is emitted when the state in a surface commit with the specified \a serial
* has been applied.
*/
void stateApplied(quint32 serial);
private:
std::unique_ptr<SurfaceInterfacePrivate> d;
friend class SurfaceInterfacePrivate;
};
}
/**
* The SurfaceExtension class is the base class for wl_surface extensions. The SurfaceExtension
* helps with managing extension state and keeping it in sync with the surface state.
*/
template<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 *)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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