/* SPDX-FileCopyrightText: 2020 Vlad Zahorodnii 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 "logging.h" #include "surface_interface.h" #include "surfacerole_p.h" #include "xdgshell_interface_p.h" #include #include #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 surface; QPointer output; LayerSurfaceV1State current; LayerSurfaceV1State pending; QQueue serials; QString scope; bool isClosed = false; bool isConfigured = false; bool isCommitted = 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; } if (!surface->isMapped() && isCommitted) { isCommitted = 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 (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(KWAYLAND_SERVER) << "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