kwin/src/wayland/layershell_v1_interface.cpp
Vlad Zahorodnii b64f95b703 Integrate kwaylandserver
This makes KWin switch to in-tree copy of KWaylandServer codebase.

KWaylandServer namespace has been left as is. It will be addressed later
by renaming classes in order to fit in the KWin namespace.
2022-04-22 12:27:33 +03:00

469 lines
14 KiB
C++

/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "layershell_v1_interface.h"
#include "display.h"
#include "surface_interface.h"
#include "surfacerole_p.h"
#include "utils/common.h"
#include "xdgshell_interface_p.h"
#include <QPointer>
#include <QQueue>
#include "qwayland-server-wlr-layer-shell-unstable-v1.h"
namespace KWaylandServer
{
static const int s_version = 3;
class LayerShellV1InterfacePrivate : public QtWaylandServer::zwlr_layer_shell_v1
{
public:
LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display);
LayerShellV1Interface *q;
Display *display;
protected:
void zwlr_layer_shell_v1_get_layer_surface(Resource *resource,
uint32_t id,
struct ::wl_resource *surface_resource,
struct ::wl_resource *output_resource,
uint32_t layer,
const QString &scope) override;
void zwlr_layer_shell_v1_destroy(Resource *resource) override;
};
class LayerSurfaceV1State
{
public:
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;
};
class LayerSurfaceV1InterfacePrivate : public SurfaceRole, public QtWaylandServer::zwlr_layer_surface_v1
{
public:
LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface);
void 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;
protected:
void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override;
void zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height) override;
void zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor) override;
void zwlr_layer_surface_v1_set_exclusive_zone(Resource *resource, int32_t zone) override;
void zwlr_layer_surface_v1_set_margin(Resource *resource, int32_t top, int32_t right, int32_t bottom, int32_t left) override;
void zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity) override;
void zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup) override;
void zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial) override;
void zwlr_layer_surface_v1_destroy(Resource *resource) override;
void zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer) override;
};
LayerShellV1InterfacePrivate::LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display)
: QtWaylandServer::zwlr_layer_shell_v1(*display, s_version)
, q(q)
, display(display)
{
}
void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_get_layer_surface(Resource *resource,
uint32_t id,
wl_resource *surface_resource,
wl_resource *output_resource,
uint32_t layer,
const QString &scope)
{
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
OutputInterface *output = OutputInterface::get(output_resource);
if (surface->buffer()) {
wl_resource_post_error(resource->handle, error_already_constructed, "the wl_surface already has a buffer attached");
return;
}
if (layer > layer_overlay) {
wl_resource_post_error(resource->handle, error_invalid_layer, "invalid layer %d", layer);
return;
}
SurfaceRole *surfaceRole = SurfaceRole::get(surface);
if (surfaceRole) {
wl_resource_post_error(resource->handle, error_role, "the wl_surface already has a role assigned %s", surfaceRole->name().constData());
return;
}
wl_resource *layerSurfaceResource = wl_resource_create(resource->client(), &zwlr_layer_surface_v1_interface, resource->version(), id);
if (!layerSurfaceResource) {
wl_resource_post_no_memory(resource->handle);
return;
}
auto layerSurface = new LayerSurfaceV1Interface(q, surface, output, LayerSurfaceV1Interface::Layer(layer), scope, layerSurfaceResource);
Q_EMIT q->surfaceCreated(layerSurface);
}
void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
LayerShellV1Interface::LayerShellV1Interface(Display *display, QObject *parent)
: QObject(parent)
, d(new LayerShellV1InterfacePrivate(this, display))
{
}
LayerShellV1Interface::~LayerShellV1Interface()
{
}
Display *LayerShellV1Interface::display() const
{
return d->display;
}
LayerSurfaceV1InterfacePrivate::LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface)
: SurfaceRole(surface, QByteArrayLiteral("layer_surface_v1"))
, q(q)
, surface(surface)
{
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_destroy_resource(Resource *resource)
{
Q_UNUSED(resource)
Q_EMIT q->aboutToBeDestroyed();
delete q;
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height)
{
Q_UNUSED(resource)
pending.desiredSize = QSize(width, height);
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor)
{
const uint32_t anchorMask = anchor_top | anchor_left | anchor_right | anchor_bottom;
if (anchor > anchorMask) {
wl_resource_post_error(resource->handle, error_invalid_anchor, "invalid anchor %d", anchor);
return;
}
pending.anchor = Qt::Edges();
if (anchor & anchor_top) {
pending.anchor |= Qt::TopEdge;
}
if (anchor & anchor_right) {
pending.anchor |= Qt::RightEdge;
}
if (anchor & anchor_bottom) {
pending.anchor |= Qt::BottomEdge;
}
if (anchor & anchor_left) {
pending.anchor |= Qt::LeftEdge;
}
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_exclusive_zone(Resource *, int32_t zone)
{
pending.exclusiveZone = zone;
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_margin(Resource *, int32_t top, int32_t right, int32_t bottom, int32_t left)
{
pending.margins = QMargins(left, top, right, bottom);
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity)
{
Q_UNUSED(resource)
pending.acceptsFocus = keyboard_interactivity;
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup_resource)
{
XdgPopupInterface *popup = XdgPopupInterface::get(popup_resource);
XdgPopupInterfacePrivate *popupPrivate = XdgPopupInterfacePrivate::get(popup);
if (popup->isConfigured()) {
wl_resource_post_error(resource->handle, error_invalid_surface_state, "xdg_popup surface is already configured");
return;
}
popupPrivate->parentSurface = surface;
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial)
{
if (!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();
if (head == serial) {
break;
}
}
if (!isClosed) {
pending.acknowledgedConfigure = serial;
pending.acknowledgedConfigureIsSet = true;
}
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer)
{
if (Q_UNLIKELY(layer > LayerShellV1InterfacePrivate::layer_overlay)) {
wl_resource_post_error(resource->handle, LayerShellV1InterfacePrivate::error_invalid_layer, "invalid layer %d", layer);
return;
}
pending.layer = LayerSurfaceV1Interface::Layer(layer);
}
void LayerSurfaceV1InterfacePrivate::commit()
{
if (isClosed) {
return;
}
if (pending.acknowledgedConfigureIsSet) {
current.acknowledgedConfigure = pending.acknowledgedConfigure;
pending.acknowledgedConfigureIsSet = false;
Q_EMIT q->configureAcknowledged(pending.acknowledgedConfigure);
}
if (Q_UNLIKELY(surface->isMapped() && !isConfigured)) {
wl_resource_post_error(resource()->handle,
error_invalid_surface_state,
"a buffer has been attached to a layer surface prior "
"to the first layer_surface.configure event");
return;
}
if (Q_UNLIKELY(pending.desiredSize.width() == 0 && (!(pending.anchor & Qt::LeftEdge) || !(pending.anchor & Qt::RightEdge)))) {
wl_resource_post_error(resource()->handle,
error_invalid_size,
"the layer surface has a width of 0 but its anchor "
"doesn't include the left and the right screen edge");
return;
}
if (Q_UNLIKELY(pending.desiredSize.height() == 0 && (!(pending.anchor & Qt::TopEdge) || !(pending.anchor & Qt::BottomEdge)))) {
wl_resource_post_error(resource()->handle,
error_invalid_size,
"the layer surface has a height of 0 but its anchor "
"doesn't include the top and the bottom screen edge");
return;
}
// detect reset
if (!surface->isMapped() && firstBufferAttached) {
isCommitted = false;
firstBufferAttached = false;
isConfigured = false;
current = LayerSurfaceV1State();
pending = LayerSurfaceV1State();
return;
}
const LayerSurfaceV1State previous = std::exchange(current, pending);
isCommitted = true; // Must set the committed state before emitting any signals.
if (surface->isMapped()) {
firstBufferAttached = true;
}
if (previous.acceptsFocus != current.acceptsFocus) {
Q_EMIT q->acceptsFocusChanged();
}
if (previous.layer != current.layer) {
Q_EMIT q->layerChanged();
}
if (previous.anchor != current.anchor) {
Q_EMIT q->anchorChanged();
}
if (previous.desiredSize != current.desiredSize) {
Q_EMIT q->desiredSizeChanged();
}
if (previous.exclusiveZone != current.exclusiveZone) {
Q_EMIT q->exclusiveZoneChanged();
}
if (previous.margins != current.margins) {
Q_EMIT q->marginsChanged();
}
}
LayerSurfaceV1Interface::LayerSurfaceV1Interface(LayerShellV1Interface *shell,
SurfaceInterface *surface,
OutputInterface *output,
Layer layer,
const QString &scope,
wl_resource *resource)
: d(new LayerSurfaceV1InterfacePrivate(this, surface))
{
d->current.layer = layer;
d->pending.layer = layer;
d->shell = shell;
d->output = output;
d->scope = scope;
d->init(resource);
}
LayerSurfaceV1Interface::~LayerSurfaceV1Interface()
{
}
bool LayerSurfaceV1Interface::isCommitted() const
{
return d->isCommitted;
}
SurfaceInterface *LayerSurfaceV1Interface::surface() const
{
return d->surface;
}
Qt::Edges LayerSurfaceV1Interface::anchor() const
{
return d->current.anchor;
}
QSize LayerSurfaceV1Interface::desiredSize() const
{
return d->current.desiredSize;
}
bool LayerSurfaceV1Interface::acceptsFocus() const
{
return d->current.acceptsFocus;
}
LayerSurfaceV1Interface::Layer LayerSurfaceV1Interface::layer() const
{
return d->current.layer;
}
QMargins LayerSurfaceV1Interface::margins() const
{
return d->current.margins;
}
int LayerSurfaceV1Interface::leftMargin() const
{
return d->current.margins.left();
}
int LayerSurfaceV1Interface::topMargin() const
{
return d->current.margins.top();
}
int LayerSurfaceV1Interface::rightMargin() const
{
return d->current.margins.right();
}
int LayerSurfaceV1Interface::bottomMargin() const
{
return d->current.margins.bottom();
}
int LayerSurfaceV1Interface::exclusiveZone() const
{
return d->current.exclusiveZone;
}
Qt::Edge LayerSurfaceV1Interface::exclusiveEdge() const
{
if (exclusiveZone() <= 0) {
return Qt::Edge();
}
if (anchor() == (Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge) || anchor() == Qt::TopEdge) {
return Qt::TopEdge;
}
if (anchor() == (Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge) || anchor() == Qt::RightEdge) {
return Qt::RightEdge;
}
if (anchor() == (Qt::RightEdge | Qt::BottomEdge | Qt::LeftEdge) || anchor() == Qt::BottomEdge) {
return Qt::BottomEdge;
}
if (anchor() == (Qt::BottomEdge | Qt::LeftEdge | Qt::TopEdge) || anchor() == Qt::LeftEdge) {
return Qt::LeftEdge;
}
return Qt::Edge();
}
OutputInterface *LayerSurfaceV1Interface::output() const
{
return d->output;
}
QString LayerSurfaceV1Interface::scope() const
{
return d->scope;
}
quint32 LayerSurfaceV1Interface::sendConfigure(const QSize &size)
{
if (d->isClosed) {
qCWarning(KWIN_CORE) << "Cannot configure a closed layer shell surface";
return 0;
}
const uint32_t serial = d->shell->display()->nextSerial();
d->serials << serial;
d->send_configure(serial, size.width(), size.height());
d->isConfigured = true;
return serial;
}
void LayerSurfaceV1Interface::sendClosed()
{
if (!d->isClosed) {
d->send_closed();
d->isClosed = true;
}
}
} // namespace KWaylandServer