diff --git a/src/input.cpp b/src/input.cpp index 8a0a3b3ba8..fcd291f7eb 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -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 #include "osd.h" +#include "wayland/xdgshell_interface.h" #include 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 m_dragTarget; QTimer m_raiseTimer; + QPointer m_currentToplevelDragWindow = nullptr; + bool m_wasKeepAbove = false; }; KWIN_SINGLETON_FACTORY(InputRedirection) diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index dcbf3e13e1..c9ee292fab 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -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 ) diff --git a/src/wayland/datadevice_interface.cpp b/src/wayland/datadevice_interface.cpp index 6748d87902..40bae801f3 100644 --- a/src/wayland/datadevice_interface.cpp +++ b/src/wayland/datadevice_interface.cpp @@ -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; } diff --git a/src/wayland/dataoffer_interface.cpp b/src/wayland/dataoffer_interface.cpp index df80aed685..df37a40d0f 100644 --- a/src/wayland/dataoffer_interface.cpp +++ b/src/wayland/dataoffer_interface.cpp @@ -202,7 +202,6 @@ void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action) } d->send_action(wlAction); } - } #include "moc_dataoffer_interface.cpp" diff --git a/src/wayland/datasource_interface.cpp b/src/wayland/datasource_interface.cpp index 1cfcb20368..2c7355b7ca 100644 --- a/src/wayland/datasource_interface.cpp +++ b/src/wayland/datasource_interface.cpp @@ -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 @@ -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" diff --git a/src/wayland/datasource_interface.h b/src/wayland/datasource_interface.h index 784beecc61..823ebc5854 100644 --- a/src/wayland/datasource_interface.h +++ b/src/wayland/datasource_interface.h @@ -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 d; diff --git a/src/wayland/datasource_interface_p.h b/src/wayland/datasource_interface_p.h new file mode 100644 index 0000000000..e4fa701a07 --- /dev/null +++ b/src/wayland/datasource_interface_p.h @@ -0,0 +1,44 @@ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#pragma once + +#include + +#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); +}; +} diff --git a/src/wayland/protocols/xdg-toplevel-drag-v1.xml b/src/wayland/protocols/xdg-toplevel-drag-v1.xml new file mode 100644 index 0000000000..b29d802f18 --- /dev/null +++ b/src/wayland/protocols/xdg-toplevel-drag-v1.xml @@ -0,0 +1,139 @@ + + + + + Copyright 2023 David Redondo <kde@david-redondo.de> + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + 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. + + + + + + + + + + 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. + + + + + + + + + + + diff --git a/src/wayland/seat_interface.cpp b/src/wayland/seat_interface.cpp index 23b1a7c639..66618d7a15 100644 --- a/src/wayland/seat_interface.cpp +++ b/src/wayland/seat_interface.cpp @@ -31,6 +31,7 @@ #include "touch_interface_p.h" #include "utils.h" #include "utils/common.h" +#include "xdgtopleveldrag_v1_interface.h" #include @@ -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(d->drag.source)) { + return source->xdgToplevelDrag(); + } + return nullptr; +} + void SeatInterface::setFocusedTextInputSurface(SurfaceInterface *surface) { const quint32 serial = d->display->nextSerial(); diff --git a/src/wayland/seat_interface.h b/src/wayland/seat_interface.h index 517e135ee1..2cd6b7a708 100644 --- a/src/wayland/seat_interface.h +++ b/src/wayland/seat_interface.h @@ -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. * diff --git a/src/wayland/xdgtopleveldrag_v1_interface.cpp b/src/wayland/xdgtopleveldrag_v1_interface.cpp new file mode 100644 index 0000000000..0f5b019953 --- /dev/null +++ b/src/wayland/xdgtopleveldrag_v1_interface.cpp @@ -0,0 +1,131 @@ +/* + SPDX-FileCopyrightText: 2023 David Redondo + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "xdgtopleveldrag_v1_interface.h" + +#include + +#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 + +#include + +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 dataSource; + QPointer 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(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(this, display)) +{ +} + +XdgToplevelDragManagerV1Interface::~XdgToplevelDragManagerV1Interface() = default; +} diff --git a/src/wayland/xdgtopleveldrag_v1_interface.h b/src/wayland/xdgtopleveldrag_v1_interface.h new file mode 100644 index 0000000000..febd30d052 --- /dev/null +++ b/src/wayland/xdgtopleveldrag_v1_interface.h @@ -0,0 +1,56 @@ +/* + SPDX-FileCopyrightText: 2023 David Redondo + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#pragma once + +#include + +#include + +#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 d; + friend class XdgToplevelDragManagerV1InterfacePrivate; +}; + +class XdgToplevelDragManagerV1Interface : public QObject +{ + Q_OBJECT +public: + XdgToplevelDragManagerV1Interface(Display *display, QObject *parent = nullptr); + ~XdgToplevelDragManagerV1Interface() override; + +private: + std::unique_ptr d; +}; +} diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp index 1c722ecad0..fbf0aa3468 100644 --- a/src/wayland_server.cpp +++ b/src/wayland_server.cpp @@ -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) {