b64f95b703
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.
469 lines
14 KiB
C++
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
|