Implement xdg-toplevel-drag

This commit is contained in:
David Redondo 2023-02-09 09:40:13 +01:00 committed by David Edmundson
parent 01a1aaf99e
commit b336691b3e
13 changed files with 477 additions and 22 deletions

View file

@ -29,6 +29,7 @@
#include "pointer_input.h"
#include "tablet_input.h"
#include "touch_input.h"
#include "wayland/xdgtopleveldrag_v1_interface.h"
#include "x11window.h"
#if KWIN_BUILD_TABBOX
#include "tabbox/tabbox.h"
@ -72,6 +73,7 @@
#include <xkbcommon/xkbcommon.h>
#include "osd.h"
#include "wayland/xdgshell_interface.h"
#include <cmath>
using namespace std::literals;
@ -2383,6 +2385,15 @@ public:
m_raiseTimer.setSingleShot(true);
m_raiseTimer.setInterval(250);
connect(&m_raiseTimer, &QTimer::timeout, this, &DragAndDropInputFilter::raiseDragTarget);
connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragEnded, this, [this] {
if (!m_currentToplevelDragWindow) {
return;
}
m_currentToplevelDragWindow->setKeepAbove(m_wasKeepAbove);
workspace()->takeActivity(m_currentToplevelDragWindow, Workspace::ActivityFlag::ActivityFocus | Workspace::ActivityFlag::ActivityRaise);
m_currentToplevelDragWindow = nullptr;
});
}
bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
@ -2398,6 +2409,11 @@ public:
switch (event->type()) {
case QEvent::MouseMove: {
const auto pos = input()->globalPointer();
if (seat->xdgTopleveldrag()) {
dragToplevel(pos, seat->xdgTopleveldrag());
}
seat->notifyPointerMotion(pos);
Window *dragTarget = pickDragTarget(pos);
@ -2499,6 +2515,11 @@ public:
if (m_touchId != id) {
return true;
}
if (seat->xdgTopleveldrag()) {
dragToplevel(pos, seat->xdgTopleveldrag());
}
seat->setTimestamp(time);
seat->notifyTouchMotion(id, pos);
@ -2573,6 +2594,9 @@ private:
do {
--it;
Window *window = (*it);
if (auto toplevelDrag = waylandServer()->seat()->xdgTopleveldrag(); toplevelDrag && toplevelDrag->toplevel() && toplevelDrag->toplevel()->surface() == window->surface()) {
continue;
}
if (window->isDeleted()) {
continue;
}
@ -2592,10 +2616,33 @@ private:
return nullptr;
}
void dragToplevel(const QPointF &pos, const KWaylandServer::XdgToplevelDragV1Interface *toplevelDrag)
{
auto window = toplevelDrag->toplevel() ? waylandServer()->findWindow(toplevelDrag->toplevel()->surface()) : nullptr;
if (m_currentToplevelDragWindow != window) {
if (m_currentToplevelDragWindow) {
m_currentToplevelDragWindow->setKeepAbove(m_wasKeepAbove);
}
m_currentToplevelDragWindow = window;
if (window) {
m_wasKeepAbove = window->keepAbove();
window->setKeepAbove(true);
}
}
if (window) {
window->move(pos - toplevelDrag->offset());
}
}
qint32 m_touchId = -1;
QPointF m_lastPos = QPointF(-1, -1);
QPointer<Window> m_dragTarget;
QTimer m_raiseTimer;
QPointer<Window> m_currentToplevelDragWindow = nullptr;
bool m_wasKeepAbove = false;
};
KWIN_SINGLETON_FACTORY(InputRedirection)

View file

@ -217,6 +217,11 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
BASENAME cursor-shape-v1
)
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
PROTOCOL protocols/xdg-toplevel-drag-v1.xml
BASENAME xdg-toplevel-drag-v1
)
target_sources(kwin PRIVATE
abstract_data_source.cpp
abstract_drop_handler.cpp
@ -292,6 +297,7 @@ target_sources(kwin PRIVATE
xdgforeign_v2_interface.cpp
xdgoutput_v1_interface.cpp
xdgshell_interface.cpp
xdgtopleveldrag_v1_interface.cpp
xwaylandkeyboardgrab_v1_interface.cpp
xwaylandshell_v1_interface.cpp
)

View file

@ -5,6 +5,7 @@
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "datadevice_interface.h"
#include "datadevice_interface_p.h"
#include "datadevicemanager_interface.h"
@ -127,6 +128,11 @@ void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, w
return;
}
if (dataSource && dataSource->xdgToplevelDrag()) {
wl_resource_post_error(resource->handle, QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
return;
}
if (selection == dataSource) {
return;
}

View file

@ -202,7 +202,6 @@ void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
}
d->send_action(wlAction);
}
}
#include "moc_dataoffer_interface.cpp"

View file

@ -7,6 +7,7 @@
#include "datasource_interface.h"
#include "clientconnection.h"
#include "datadevicemanager_interface.h"
#include "datasource_interface_p.h"
#include "utils.h"
// Qt
#include <QStringList>
@ -17,27 +18,6 @@
namespace KWaylandServer
{
class DataSourceInterfacePrivate : public QtWaylandServer::wl_data_source
{
public:
DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource);
DataSourceInterface *q;
QStringList mimeTypes;
DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None;
DataDeviceManagerInterface::DnDAction selectedDndAction = DataDeviceManagerInterface::DnDAction::None;
bool isAccepted = false;
protected:
void data_source_destroy_resource(Resource *resource) override;
void data_source_offer(Resource *resource, const QString &mime_type) override;
void data_source_destroy(Resource *resource) override;
void data_source_set_actions(Resource *resource, uint32_t dnd_actions) override;
private:
void offer(const QString &mimeType);
};
DataSourceInterfacePrivate::DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource)
: QtWaylandServer::wl_data_source(resource)
, q(_q)
@ -92,6 +72,11 @@ void DataSourceInterfacePrivate::data_source_set_actions(Resource *resource, uin
}
}
DataSourceInterfacePrivate *DataSourceInterfacePrivate::get(DataSourceInterface *dataSource)
{
return dataSource->d.get();
}
DataSourceInterface::DataSourceInterface(wl_resource *resource)
: d(new DataSourceInterfacePrivate(this, resource))
{
@ -144,6 +129,7 @@ DataDeviceManagerInterface::DnDAction DataSourceInterface::selectedDndAction() c
void DataSourceInterface::dropPerformed()
{
d->dropPerformed = true;
if (d->resource()->version() < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
return;
}
@ -158,6 +144,11 @@ void DataSourceInterface::dndFinished()
d->send_dnd_finished();
}
bool DataSourceInterface::isDropPerformed() const
{
return d->dropPerformed;
}
void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
{
d->selectedDndAction = action;
@ -178,6 +169,7 @@ void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action
void DataSourceInterface::dndCancelled()
{
d->isCanceled = true;
// for v3 or less, cancel should not be called after a failed drag operation
if (wl_resource_get_version(resource()) < 3) {
return;
@ -185,6 +177,11 @@ void DataSourceInterface::dndCancelled()
d->send_cancelled();
}
bool DataSourceInterface::isDndCancelled() const
{
return d->isCanceled;
}
wl_resource *DataSourceInterface::resource() const
{
return d->resource()->handle;
@ -205,6 +202,11 @@ void DataSourceInterface::setAccepted(bool accepted)
d->isAccepted = accepted;
}
XdgToplevelDragV1Interface *DataSourceInterface::xdgToplevelDrag() const
{
return d->xdgToplevelDrag;
}
}
#include "moc_datasource_interface.cpp"

View file

@ -14,6 +14,7 @@
namespace KWaylandServer
{
class DataSourceInterfacePrivate;
class XdgToplevelDragV1Interface;
/**
* @brief Represents the Resource for the wl_data_source interface.
@ -42,6 +43,9 @@ public:
void dndAction(DataDeviceManagerInterface::DnDAction action) override;
void dndCancelled() override;
bool isDndCancelled() const;
bool isDropPerformed() const;
wl_resource *resource() const;
wl_client *client() const override;
@ -49,8 +53,11 @@ public:
bool isAccepted() const override;
void setAccepted(bool accepted);
XdgToplevelDragV1Interface *xdgToplevelDrag() const;
private:
friend class DataDeviceManagerInterfacePrivate;
friend class DataSourceInterfacePrivate;
explicit DataSourceInterface(wl_resource *resource);
std::unique_ptr<DataSourceInterfacePrivate> d;

View file

@ -0,0 +1,44 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <qwayland-server-wayland.h>
#include "datadevicemanager_interface.h"
namespace KWaylandServer
{
class DataSourceInterface;
class XdgToplevelDragV1Interface;
class DataSourceInterfacePrivate : public QtWaylandServer::wl_data_source
{
public:
DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource);
static DataSourceInterfacePrivate *get(DataSourceInterface *dataSource);
DataSourceInterface *q;
QStringList mimeTypes;
DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None;
DataDeviceManagerInterface::DnDAction selectedDndAction = DataDeviceManagerInterface::DnDAction::None;
bool isAccepted = false;
bool dropPerformed = false;
bool isCanceled = false;
XdgToplevelDragV1Interface *xdgToplevelDrag = nullptr;
protected:
void data_source_destroy_resource(Resource *resource) override;
void data_source_offer(Resource *resource, const QString &mime_type) override;
void data_source_destroy(Resource *resource) override;
void data_source_set_actions(Resource *resource, uint32_t dnd_actions) override;
private:
void offer(const QString &mimeType);
};
}

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_toplevel_drag_v1">
<copyright>
Copyright 2023 David Redondo &lt;kde@david-redondo.de&gt;
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="xdg_toplevel_drag_manager_v1" version="1">
<description summary="Move a window during a drag">
This protocol enhances normal drag and drop with the ability to move a
window at the same time. This allows having detachable parts of a window
that when dragged out of it become a new window and can be dragged over
an existing window to be reattached.
A typical workflow would be when the user starts dragging on top of a
detachable part of a window, the client would create a wl_data_source and
a xdg_toplevel_drag_v1 object and start the drag as normal via
wl_data_device.start_drag. Once the client determines that the detachable
window contents should be detached from the originating window, it creates
a new xdg_toplevel with these contents and issues a
xdg_toplevel_drag_v1.attach request. From now on the new window is moved
by the compositor during the drag as if the client called
xdg_toplevel.move.
Dragging an existing window is similar. The client creates a
xdg_toplevel_drag_v1 object and attaches the existing toplevel before
starting the drag.
Clients use the existing drag and drop mechanism to detect when a window
can be docked or undocked. If the client wants to snap a window into a
parent window it should delete or unmap the dragged top-level. If the
contents should be detached again it attaches a new toplevel as described
above. If a drag operation is cancelled, clients should revert to the
previous state, deleting any newly created windows as appropriate.
When a drag operation ends the dragged toplevel window's final position is
determined as if a xdg_toplevel_move operation ended.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<request name="get_xdg_toplevel_drag">
<description summary="get an xdg_toplevel_drag for a wl_data_source">
Create an xdg_toplevel_drag for a drag and drop operation that is going
to be started with data_source.
This request can only be made on sources used in drag-and-drop, so it
must be performed before wl_data_device.start_drag. Attempting to use
the source other than for drag-and-drop such as in
wl_data_device.set_selection will raise an invalid_source error.
Destroying data_source while a toplevel is attached to the
xdg_toplevel_drag is undefined.
</description>
<arg name="id" type="new_id" interface="xdg_toplevel_drag_v1"/>
<arg name="data_source" type="object" interface="wl_data_source"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_toplevel_drag_manager_v1 object">
Destroy this xdg_toplevel_drag_manager_v1 object. Other objects,
including xdg_toplevel_drag_v1 objects created by this factory, are not
affected by this request.
</description>
</request>
<enum name="error">
<entry name="invalid_source" value="0"
summary="data_source already used for toplevel drag"/>
</enum>
</interface>
<interface name="xdg_toplevel_drag_v1" version="1">
<description summary="Object representing a toplevel move during a drag">
</description>
<request name="attach">
<description summary="Move a toplevel with the drag operation">
Request that the window will be moved with the cursor during the drag
operation. The offset describes how the toplevel will be positioned
relative to the cursor hotspot in surface local coordinates. The
attached window does not participate in the selection of the drag
target.
If the toplevel is unmapped while it is attached, it is automatically
detached from the drag. In this case this request has to be called again
if the window should be attached after it is remapped.
This request can be called multiple times but issuing it while a
toplevel with an active role is attached raises a toplevel_attached
error.
</description>
<arg name="toplevel" type="object" interface="xdg_toplevel"/>
<arg name="x_offset" type="int" summary="dragged surface x offset"/>
<arg name="y_offset" type="int" summary="dragged surface y offset"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy an xdg_toplevel_drag_v1 object">
Destroy this xdg_toplevel_drag_v1 object. This request must only be
called after the underlying wl_data_source drag has ended, as indicated
by the dnd_drop_performed or cancelled events. In any other case an
ongoing_drag error is raised.
</description>
</request>
<enum name="error">
<entry name="toplevel_attached" value="0"
summary="valid toplevel already attached"/>
<entry name="ongoing_drag" value="1"
summary="drag has not ended" />
</enum>
</interface>
</protocol>

View file

@ -31,6 +31,7 @@
#include "touch_interface_p.h"
#include "utils.h"
#include "utils/common.h"
#include "xdgtopleveldrag_v1_interface.h"
#include <linux/input.h>
@ -273,6 +274,7 @@ void SeatInterfacePrivate::endDrag()
AbstractDropHandler *dragTargetDevice = drag.target.data();
AbstractDataSource *dragSource = drag.source;
if (dragSource) {
// TODO: Also check the current drag-and-drop action.
if (dragTargetDevice && dragSource->isAccepted()) {
@ -1214,6 +1216,14 @@ AbstractDataSource *SeatInterface::dragSource() const
return d->drag.source;
}
XdgToplevelDragV1Interface *SeatInterface::xdgTopleveldrag() const
{
if (auto source = qobject_cast<DataSourceInterface *>(d->drag.source)) {
return source->xdgToplevelDrag();
}
return nullptr;
}
void SeatInterface::setFocusedTextInputSurface(SurfaceInterface *surface)
{
const quint32 serial = d->display->nextSerial();

View file

@ -30,6 +30,7 @@ class TextInputV1Interface;
class TextInputV2Interface;
class TextInputV3Interface;
class TouchInterface;
class XdgToplevelDragV1Interface;
/**
* Describes the source types for axis events. This indicates to the
@ -182,6 +183,11 @@ public:
* @see isDrag
*/
KWaylandServer::AbstractDataSource *dragSource() const;
/**
* @returns the toplevel drag if the current drag is a toplevel drag
*/
KWaylandServer::XdgToplevelDragV1Interface *xdgTopleveldrag() const;
/**
* Sets the current drag target to @p surface.
*

View file

@ -0,0 +1,131 @@
/*
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "xdgtopleveldrag_v1_interface.h"
#include <qwayland-server-xdg-toplevel-drag-v1.h>
#include "dataoffer_interface.h"
#include "datasource_interface.h"
#include "datasource_interface_p.h"
#include "xdgshell_interface.h"
#include "display.h"
#include "surface_interface.h"
#include "utils.h"
#include <QPointer>
#include <memory>
namespace KWaylandServer
{
constexpr int version = 1;
class XdgToplevelDragV1InterfacePrivate : public QtWaylandServer::xdg_toplevel_drag_v1
{
public:
XdgToplevelDragV1InterfacePrivate(wl_resource *resource, XdgToplevelDragV1Interface *q)
: xdg_toplevel_drag_v1(resource)
, q(q)
{
}
XdgToplevelDragV1Interface *q;
QPointer<DataSourceInterface> dataSource;
QPointer<XdgToplevelInterface> toplevel;
QPoint pos;
private:
void xdg_toplevel_drag_v1_attach(Resource *resource, wl_resource *toplevelResource, int32_t x_offset, int32_t y_offset) override
{
if (toplevel) {
wl_resource_post_error(resource->handle, error_toplevel_attached, "Valid toplevel already attached");
return;
}
toplevel = XdgToplevelInterface::get(toplevelResource);
QObject::connect(toplevel, &XdgToplevelInterface::resetOccurred, q, [this] {
toplevel = nullptr;
});
pos = QPoint(x_offset, y_offset);
Q_EMIT q->toplevelChanged();
}
void xdg_toplevel_drag_v1_destroy_resource(Resource *resource) override
{
delete q;
}
void xdg_toplevel_drag_v1_destroy(Resource *resource) override
{
if (!dataSource || dataSource->isDndCancelled() || dataSource->isDropPerformed()) {
wl_resource_destroy(resource->handle);
return;
}
wl_resource_post_error(resource->handle, error_ongoing_drag, "Drag has not ended");
}
};
XdgToplevelDragV1Interface::XdgToplevelDragV1Interface(wl_resource *resource, DataSourceInterface *dataSource)
: d(std::make_unique<XdgToplevelDragV1InterfacePrivate>(resource, this))
{
d->dataSource = dataSource;
DataSourceInterfacePrivate::get(dataSource)->xdgToplevelDrag = this;
}
XdgToplevelDragV1Interface::~XdgToplevelDragV1Interface()
{
if (d->dataSource) {
DataSourceInterfacePrivate::get(d->dataSource)->xdgToplevelDrag = nullptr;
}
}
XdgToplevelInterface *XdgToplevelDragV1Interface::toplevel() const
{
return d->toplevel;
}
QPoint XdgToplevelDragV1Interface::offset() const
{
return d->pos;
}
class XdgToplevelDragManagerV1InterfacePrivate : public QtWaylandServer::xdg_toplevel_drag_manager_v1
{
public:
XdgToplevelDragManagerV1InterfacePrivate(XdgToplevelDragManagerV1Interface *q, Display *display)
: xdg_toplevel_drag_manager_v1(*display, version)
, q(q)
{
}
protected:
void xdg_toplevel_drag_manager_v1_destroy(Resource *resource) override
{
wl_resource_destroy(resource->handle);
}
void xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(Resource *resource, uint32_t id, wl_resource *data_source) override
{
auto dataSource = DataSourceInterface::get(data_source);
wl_resource *xdg_toplevel_drag = wl_resource_create(resource->client(), &xdg_toplevel_drag_v1_interface, resource->version(), id);
if (!xdg_toplevel_drag) {
wl_resource_post_no_memory(resource->handle);
return;
}
new XdgToplevelDragV1Interface(xdg_toplevel_drag, dataSource);
}
XdgToplevelDragManagerV1Interface *q;
};
XdgToplevelDragManagerV1Interface::XdgToplevelDragManagerV1Interface(KWaylandServer::Display *display, QObject *parent)
: QObject(parent)
, d(std::make_unique<XdgToplevelDragManagerV1InterfacePrivate>(this, display))
{
}
XdgToplevelDragManagerV1Interface::~XdgToplevelDragManagerV1Interface() = default;
}

View file

@ -0,0 +1,56 @@
/*
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QObject>
#include <memory>
#include "datadevice_interface.h"
struct wl_resource;
namespace KWaylandServer
{
class DataOfferInterface;
class DataSourceInterface;
class Display;
class XdgToplevelDragV1InterfacePrivate;
class XdgToplevelDragManagerV1InterfacePrivate;
class XdgToplevelInterface;
class XdgToplevelDragV1Interface : public QObject
{
Q_OBJECT
public:
~XdgToplevelDragV1Interface() override;
DataSourceInterface *dataSource();
XdgToplevelInterface *toplevel() const;
QPoint offset() const;
Q_SIGNALS:
void toplevelChanged();
private:
XdgToplevelDragV1Interface(wl_resource *resource, DataSourceInterface *dataSource);
std::unique_ptr<XdgToplevelDragV1InterfacePrivate> d;
friend class XdgToplevelDragManagerV1InterfacePrivate;
};
class XdgToplevelDragManagerV1Interface : public QObject
{
Q_OBJECT
public:
XdgToplevelDragManagerV1Interface(Display *display, QObject *parent = nullptr);
~XdgToplevelDragManagerV1Interface() override;
private:
std::unique_ptr<XdgToplevelDragManagerV1InterfacePrivate> d;
};
}

View file

@ -65,6 +65,7 @@
#include "wayland/xdgforeign_v2_interface.h"
#include "wayland/xdgoutput_v1_interface.h"
#include "wayland/xdgshell_interface.h"
#include "wayland/xdgtopleveldrag_v1_interface.h"
#include "wayland/xwaylandkeyboardgrab_v1_interface.h"
#include "wayland/xwaylandshell_v1_interface.h"
#include "workspace.h"
@ -513,6 +514,7 @@ bool WaylandServer::init(InitializationFlags flags)
m_contentTypeManager = new KWaylandServer::ContentTypeManagerV1Interface(m_display, m_display);
m_tearingControlInterface = new KWaylandServer::TearingControlManagerV1Interface(m_display, m_display);
new XdgToplevelDragManagerV1Interface(m_display, this);
auto screenEdgeManager = new KWaylandServer::ScreenEdgeManagerV1Interface(m_display, m_display);
connect(screenEdgeManager, &KWaylandServer::ScreenEdgeManagerV1Interface::edgeRequested, this, [this](KWaylandServer::AutoHideScreenEdgeV1Interface *edge) {