diff --git a/src/3rdparty/wayland/protocols/wlr-data-control/qt_attribution.json b/src/3rdparty/wayland/protocols/wlr-data-control/qt_attribution.json
new file mode 100644
index 00000000000..eea2dde6ef6
--- /dev/null
+++ b/src/3rdparty/wayland/protocols/wlr-data-control/qt_attribution.json
@@ -0,0 +1,18 @@
+[
+ {
+ "Id": "wlr-data-control-unstable-v1-protocol",
+ "Name": "Wlr Data Control Unstable V1 Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland platform plugin",
+ "Files": "wlr-data-control-unstable-v1.xml",
+
+ "Description": "This protocol allows a privileged client to control data devices.",
+ "Homepage": "https://gitlab.freedesktop.org/wlroots/wlr-protocols/",
+ "Version": "2",
+ "DownloadLocation": "https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/raw/master/unstable/wlr-data-control-unstable-v1.xml",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "../MIT_LICENSE.txt",
+ "Copyright": "Copyright © 2018 Simon Ser\nCopyright © 2019 Ivan Molodetskikht"
+ }
+]
\ No newline at end of file
diff --git a/src/3rdparty/wayland/protocols/wlr-data-control/wlr-data-control-unstable-v1.xml b/src/3rdparty/wayland/protocols/wlr-data-control/wlr-data-control-unstable-v1.xml
new file mode 100644
index 00000000000..75e8671b0de
--- /dev/null
+++ b/src/3rdparty/wayland/protocols/wlr-data-control/wlr-data-control-unstable-v1.xml
@@ -0,0 +1,278 @@
+
+
+
+ Copyright © 2018 Simon Ser
+ Copyright © 2019 Ivan Molodetskikh
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+ This protocol allows a privileged client to control data devices. In
+ particular, the client will be able to manage the current selection and take
+ the role of a clipboard manager.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ This interface is a manager that allows creating per-seat data device
+ controls.
+
+
+
+
+ Create a new data source.
+
+
+
+
+
+
+ Create a data device that can be used to manage a seat's selection.
+
+
+
+
+
+
+
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+
+
+
+
+
+
+ This interface allows a client to manage a seat's selection.
+
+ When the seat is destroyed, this object becomes inert.
+
+
+
+
+ This request asks the compositor to set the selection to the data from
+ the source on behalf of the client.
+
+ The given source may not be used in any further set_selection or
+ set_primary_selection requests. Attempting to use a previously used
+ source is a protocol error.
+
+ To unset the selection, set the source to NULL.
+
+
+
+
+
+
+ Destroys the data device object.
+
+
+
+
+
+ The data_offer event introduces a new wlr_data_control_offer object,
+ which will subsequently be used in either the
+ wlr_data_control_device.selection event (for the regular clipboard
+ selections) or the wlr_data_control_device.primary_selection event (for
+ the primary clipboard selections). Immediately following the
+ wlr_data_control_device.data_offer event, the new data_offer object
+ will send out wlr_data_control_offer.offer events to describe the MIME
+ types it offers.
+
+
+
+
+
+
+ The selection event is sent out to notify the client of a new
+ wlr_data_control_offer for the selection for this device. The
+ wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
+ events are sent out immediately before this event to introduce the data
+ offer object. The selection event is sent to a client when a new
+ selection is set. The wlr_data_control_offer is valid until a new
+ wlr_data_control_offer or NULL is received. The client must destroy the
+ previous selection wlr_data_control_offer, if any, upon receiving this
+ event.
+
+ The first selection event is sent upon binding the
+ wlr_data_control_device object.
+
+
+
+
+
+
+ This data control object is no longer valid and should be destroyed by
+ the client.
+
+
+
+
+
+
+
+ The primary_selection event is sent out to notify the client of a new
+ wlr_data_control_offer for the primary selection for this device. The
+ wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
+ events are sent out immediately before this event to introduce the data
+ offer object. The primary_selection event is sent to a client when a
+ new primary selection is set. The wlr_data_control_offer is valid until
+ a new wlr_data_control_offer or NULL is received. The client must
+ destroy the previous primary selection wlr_data_control_offer, if any,
+ upon receiving this event.
+
+ If the compositor supports primary selection, the first
+ primary_selection event is sent upon binding the
+ wlr_data_control_device object.
+
+
+
+
+
+
+ This request asks the compositor to set the primary selection to the
+ data from the source on behalf of the client.
+
+ The given source may not be used in any further set_selection or
+ set_primary_selection requests. Attempting to use a previously used
+ source is a protocol error.
+
+ To unset the primary selection, set the source to NULL.
+
+ The compositor will ignore this request if it does not support primary
+ selection.
+
+
+
+
+
+
+
+
+
+
+
+ The wlr_data_control_source object is the source side of a
+ wlr_data_control_offer. It is created by the source client in a data
+ transfer and provides a way to describe the offered data and a way to
+ respond to requests to transfer the data.
+
+
+
+
+
+
+
+
+ This request adds a MIME type to the set of MIME types advertised to
+ targets. Can be called several times to offer multiple types.
+
+ Calling this after wlr_data_control_device.set_selection is a protocol
+ error.
+
+
+
+
+
+
+ Destroys the data source object.
+
+
+
+
+
+ Request for data from the client. Send the data as the specified MIME
+ type over the passed file descriptor, then close it.
+
+
+
+
+
+
+
+ This data source is no longer valid. The data source has been replaced
+ by another data source.
+
+ The client should clean up and destroy this data source.
+
+
+
+
+
+
+ A wlr_data_control_offer represents a piece of data offered for transfer
+ by another client (the source client). The offer describes the different
+ MIME types that the data can be converted to and provides the mechanism
+ for transferring the data directly from the source client.
+
+
+
+
+ To transfer the offered data, the client issues this request and
+ indicates the MIME type it wants to receive. The transfer happens
+ through the passed file descriptor (typically created with the pipe
+ system call). The source client writes the data in the MIME type
+ representation requested and then closes the file descriptor.
+
+ The receiving client reads from the read end of the pipe until EOF and
+ then closes its end, at which point the transfer is complete.
+
+ This request may happen multiple times for different MIME types.
+
+
+
+
+
+
+
+ Destroys the data offer object.
+
+
+
+
+
+ Sent immediately after creating the wlr_data_control_offer object.
+ One event per offered MIME type.
+
+
+
+
+
diff --git a/src/plugins/platforms/wayland/CMakeLists.txt b/src/plugins/platforms/wayland/CMakeLists.txt
index 4dfc54bfcb5..90cc673cb3e 100644
--- a/src/plugins/platforms/wayland/CMakeLists.txt
+++ b/src/plugins/platforms/wayland/CMakeLists.txt
@@ -28,6 +28,7 @@ qt_internal_add_module(WaylandClient
qwaylandabstractdecoration.cpp qwaylandabstractdecoration_p.h
qwaylandappmenu.cpp qwaylandappmenu_p.h
qwaylandbuffer.cpp qwaylandbuffer_p.h
+ qwaylanddatacontrolv1.cpp qwaylanddatacontrolv1_p.h
qwaylanddecorationfactory.cpp qwaylanddecorationfactory_p.h
qwaylanddecorationplugin.cpp qwaylanddecorationplugin_p.h
qwaylanddisplay.cpp qwaylanddisplay_p.h
@@ -92,6 +93,7 @@ qt_internal_add_module(WaylandClient
../3rdparty/protocol/fractional-scale
../3rdparty/protocol/viewporter
../3rdparty/protocol/xdg-shell
+ ../3rdparty/protocol/wlr-data-control
)
qt6_generate_wayland_protocol_client_sources(WaylandClient
@@ -109,6 +111,7 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output/xdg-output-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/fractional-scale/fractional-scale-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/viewporter/viewporter.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wlr-data-control/wlr-data-control-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-shell/xdg-shell.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-system-bell/xdg-system-bell-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml
diff --git a/src/plugins/platforms/wayland/qwaylandclipboard.cpp b/src/plugins/platforms/wayland/qwaylandclipboard.cpp
index a1730c77fd7..a1737ef69eb 100644
--- a/src/plugins/platforms/wayland/qwaylandclipboard.cpp
+++ b/src/plugins/platforms/wayland/qwaylandclipboard.cpp
@@ -1,9 +1,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 Jie Liu
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwaylandclipboard_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandinputdevice_p.h"
+#include "qwaylanddatacontrolv1_p.h"
#include "qwaylanddataoffer_p.h"
#include "qwaylanddatasource_p.h"
#include "qwaylanddatadevice_p.h"
@@ -37,6 +39,12 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
switch (mode) {
case QClipboard::Clipboard:
+ if (auto *dataControlDevice = seat->dataControlDevice()) {
+ if (dataControlDevice->selectionSource())
+ return m_clientClipboard[QClipboard::Clipboard];
+ if (auto *offer = dataControlDevice->selectionOffer())
+ return offer->mimeData();
+ }
if (auto *dataDevice = seat->dataDevice()) {
if (dataDevice->selectionSource())
return m_clientClipboard[QClipboard::Clipboard];
@@ -45,6 +53,12 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
}
return &m_emptyData;
case QClipboard::Selection:
+ if (auto *dataControlDevice = seat->dataControlDevice()) {
+ if (dataControlDevice->primarySelectionSource())
+ return m_clientClipboard[QClipboard::Selection];
+ if (auto *offer = dataControlDevice->primarySelectionOffer())
+ return offer->mimeData();
+ }
#if QT_CONFIG(wayland_client_primary_selection)
if (auto *selectionDevice = seat->primarySelectionDevice()) {
if (selectionDevice->selectionSource())
@@ -84,20 +98,28 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
switch (mode) {
case QClipboard::Clipboard:
- if (auto *dataDevice = seat->dataDevice()) {
+ if (auto *dataControlDevice = seat->dataControlDevice()) {
+ dataControlDevice->setSelectionSource(data ? new QWaylandDataControlSourceV1(mDisplay->dataControlManager(),
+ m_clientClipboard[QClipboard::Clipboard]) : nullptr);
+ emitChanged(mode);
+ } else if (auto *dataDevice = seat->dataDevice()) {
dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(),
m_clientClipboard[QClipboard::Clipboard]) : nullptr);
emitChanged(mode);
}
break;
case QClipboard::Selection:
+ if (auto *dataControlDevice = seat->dataControlDevice()) {
+ dataControlDevice->setPrimarySelectionSource(data ? new QWaylandDataControlSourceV1(mDisplay->dataControlManager(),
+ m_clientClipboard[QClipboard::Selection]) : nullptr);
+ emitChanged(mode);
#if QT_CONFIG(wayland_client_primary_selection)
- if (auto *selectionDevice = seat->primarySelectionDevice()) {
+ } else if (auto *selectionDevice = seat->primarySelectionDevice()) {
selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(),
m_clientClipboard[QClipboard::Selection]) : nullptr);
emitChanged(mode);
- }
#endif
+ }
break;
default:
break;
@@ -106,12 +128,18 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
bool QWaylandClipboard::supportsMode(QClipboard::Mode mode) const
{
-#if QT_CONFIG(wayland_client_primary_selection)
if (mode == QClipboard::Selection) {
auto *seat = mDisplay->currentInputDevice();
- return seat && seat->primarySelectionDevice();
- }
+ if (!seat)
+ return false;
+ if (seat->dataControlDevice())
+ return true;
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (seat->primarySelectionDevice())
+ return true;
#endif
+ return false;
+ }
return mode == QClipboard::Clipboard;
}
@@ -124,8 +152,10 @@ bool QWaylandClipboard::ownsMode(QClipboard::Mode mode) const
switch (mode) {
case QClipboard::Clipboard:
return seat->dataDevice() && seat->dataDevice()->selectionSource() != nullptr;
-#if QT_CONFIG(wayland_client_primary_selection)
case QClipboard::Selection:
+ if (seat->dataControlDevice() && seat->dataControlDevice()->primarySelectionSource() != nullptr)
+ return true;
+#if QT_CONFIG(wayland_client_primary_selection)
return seat->primarySelectionDevice() && seat->primarySelectionDevice()->selectionSource() != nullptr;
#endif
default:
diff --git a/src/plugins/platforms/wayland/qwaylanddatacontrolv1.cpp b/src/plugins/platforms/wayland/qwaylanddatacontrolv1.cpp
new file mode 100644
index 00000000000..6d2b60b2db8
--- /dev/null
+++ b/src/plugins/platforms/wayland/qwaylanddatacontrolv1.cpp
@@ -0,0 +1,161 @@
+// Copyright (C) 2024 Jie Liu
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwaylanddatacontrolv1_p.h"
+#include "qwaylandinputdevice_p.h"
+#include "qwaylanddisplay_p.h"
+#include "qwaylandmimehelper_p.h"
+
+#include
+
+#include
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandDataControlManagerV1::QWaylandDataControlManagerV1(QWaylandDisplay *display, uint id, uint version)
+ : zwlr_data_control_manager_v1(display->wl_registry(), id, qMin(version, uint(2)))
+ , m_display(display)
+{
+}
+
+QWaylandDataControlDeviceV1 *QWaylandDataControlManagerV1::createDevice(QWaylandInputDevice *seat)
+{
+ return new QWaylandDataControlDeviceV1(this, seat);
+}
+
+QWaylandDataControlOfferV1::QWaylandDataControlOfferV1(QWaylandDisplay *display, ::zwlr_data_control_offer_v1 *offer)
+ : zwlr_data_control_offer_v1(offer)
+ , m_display(display)
+ , m_mimeData(new QWaylandMimeData(this))
+{}
+
+void QWaylandDataControlOfferV1::startReceiving(const QString &mimeType, int fd)
+{
+ receive(mimeType, fd);
+ wl_display_flush(m_display->wl_display());
+}
+
+void QWaylandDataControlOfferV1::zwlr_data_control_offer_v1_offer(const QString &mime_type)
+{
+ m_mimeData->appendFormat(mime_type);
+}
+
+QWaylandDataControlDeviceV1::QWaylandDataControlDeviceV1(
+ QWaylandDataControlManagerV1 *manager, QWaylandInputDevice *seat)
+ : QtWayland::zwlr_data_control_device_v1(manager->get_data_device(seat->wl_seat()))
+ , m_display(manager->display())
+ , m_seat(seat)
+{
+}
+
+QWaylandDataControlDeviceV1::~QWaylandDataControlDeviceV1()
+{
+ destroy();
+}
+
+void QWaylandDataControlDeviceV1::invalidateSelectionOffer()
+{
+ if (!m_selectionOffer)
+ return;
+
+ m_selectionOffer.reset();
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+}
+
+void QWaylandDataControlDeviceV1::setSelectionSource(QWaylandDataControlSourceV1 *source)
+{
+ if (source) {
+ connect(source, &QWaylandDataControlSourceV1::cancelled, this, [this]() {
+ m_selectionSource.reset();
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Clipboard);
+ });
+ }
+ set_selection(source ? source->object() : nullptr);
+ m_selectionSource.reset(source);
+}
+
+void QWaylandDataControlDeviceV1::setPrimarySelectionSource(QWaylandDataControlSourceV1 *source)
+{
+ if (source) {
+ connect(source, &QWaylandDataControlSourceV1::cancelled, this, [this]() {
+ m_primarySelectionSource.reset();
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+ });
+ }
+ set_primary_selection(source ? source->object() : nullptr);
+ m_primarySelectionSource.reset(source);
+}
+
+void QWaylandDataControlDeviceV1::zwlr_data_control_device_v1_data_offer(zwlr_data_control_offer_v1 *offer)
+{
+ new QWaylandDataControlOfferV1(m_display, offer);
+}
+
+void QWaylandDataControlDeviceV1::zwlr_data_control_device_v1_selection(zwlr_data_control_offer_v1 *id)
+{
+ if (!id)
+ return;
+ m_selectionOffer.reset(static_cast(zwlr_data_control_offer_v1_get_user_data(id)));
+
+ // The selection event may be sent before platfrmIntegration is set.
+ if (auto* integration = QGuiApplicationPrivate::platformIntegration())
+ integration->clipboard()->emitChanged(QClipboard::Selection);
+}
+
+void QWaylandDataControlDeviceV1::zwlr_data_control_device_v1_finished()
+{
+ m_selectionOffer.reset();
+ m_primarySelectionOffer.reset();
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+}
+
+void QWaylandDataControlDeviceV1::zwlr_data_control_device_v1_primary_selection(struct ::zwlr_data_control_offer_v1 *id)
+{
+ if (!id)
+ return;
+ m_primarySelectionOffer.reset(static_cast(zwlr_data_control_offer_v1_get_user_data(id)));
+
+ // The selection event may be sent before platfrmIntegration is set.
+ if (auto* integration = QGuiApplicationPrivate::platformIntegration())
+ integration->clipboard()->emitChanged(QClipboard::Selection);
+}
+
+QWaylandDataControlSourceV1::QWaylandDataControlSourceV1(QWaylandDataControlManagerV1 *manager, QMimeData *mimeData)
+ : QtWayland::zwlr_data_control_source_v1(manager->create_data_source())
+ , m_mimeData(mimeData)
+{
+ if (!mimeData)
+ return;
+ for (auto &format : mimeData->formats())
+ offer(format);
+}
+
+QWaylandDataControlSourceV1::~QWaylandDataControlSourceV1()
+{
+ destroy();
+}
+
+void QWaylandDataControlSourceV1::zwlr_data_control_source_v1_send(const QString &mime_type, int32_t fd)
+{
+ QByteArray content = QWaylandMimeHelper::getByteArray(m_mimeData, mime_type);
+ if (!content.isEmpty()) {
+ // Create a sigpipe handler that does nothing, or clients may be forced to terminate
+ // if the pipe is closed in the other end.
+ struct sigaction action, oldAction;
+ action.sa_handler = SIG_IGN;
+ sigemptyset (&action.sa_mask);
+ action.sa_flags = 0;
+
+ sigaction(SIGPIPE, &action, &oldAction);
+ ssize_t unused = write(fd, content.constData(), size_t(content.size()));
+ Q_UNUSED(unused);
+ sigaction(SIGPIPE, &oldAction, nullptr);
+ }
+ close(fd);
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wayland/qwaylanddatacontrolv1_p.h b/src/plugins/platforms/wayland/qwaylanddatacontrolv1_p.h
new file mode 100644
index 00000000000..79f6378b3f9
--- /dev/null
+++ b/src/plugins/platforms/wayland/qwaylanddatacontrolv1_p.h
@@ -0,0 +1,119 @@
+// Copyright (C) 2024 Jie Liu
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWAYLANDDATACONTROLV1_H
+#define QWAYLANDDATACONTROLV1_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include
+
+#include
+#include
+
+#include
+
+QT_REQUIRE_CONFIG(clipboard);
+
+QT_BEGIN_NAMESPACE
+
+class QMimeData;
+
+namespace QtWaylandClient {
+
+class QWaylandInputDevice;
+class QWaylandDataControlDeviceV1;
+
+class QWaylandDataControlManagerV1 : public QtWayland::zwlr_data_control_manager_v1
+{
+public:
+ explicit QWaylandDataControlManagerV1(QWaylandDisplay *display, uint id, uint version);
+ QWaylandDataControlDeviceV1 *createDevice(QWaylandInputDevice *seat);
+ QWaylandDisplay *display() const { return m_display; }
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+};
+
+class QWaylandDataControlOfferV1 : public QtWayland::zwlr_data_control_offer_v1, public QWaylandAbstractDataOffer
+{
+public:
+ explicit QWaylandDataControlOfferV1(QWaylandDisplay *display, ::zwlr_data_control_offer_v1 *offer);
+ ~QWaylandDataControlOfferV1() override { destroy(); }
+ void startReceiving(const QString &mimeType, int fd) override;
+ QMimeData *mimeData() override { return m_mimeData.data(); }
+
+protected:
+ void zwlr_data_control_offer_v1_offer(const QString &mime_type) override;
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QScopedPointer m_mimeData;
+};
+
+class Q_WAYLANDCLIENT_EXPORT QWaylandDataControlSourceV1 : public QObject, public QtWayland::zwlr_data_control_source_v1
+{
+ Q_OBJECT
+public:
+ explicit QWaylandDataControlSourceV1(QWaylandDataControlManagerV1 *manager, QMimeData *mimeData);
+ ~QWaylandDataControlSourceV1() override;
+
+ QMimeData *mimeData() const { return m_mimeData; }
+
+Q_SIGNALS:
+ void cancelled();
+
+protected:
+ void zwlr_data_control_source_v1_send(const QString &mime_type, int32_t fd) override;
+ void zwlr_data_control_source_v1_cancelled() override { Q_EMIT cancelled(); }
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QMimeData *m_mimeData = nullptr;
+};
+
+class QWaylandDataControlDeviceV1 : public QObject, public QtWayland::zwlr_data_control_device_v1
+{
+ Q_OBJECT
+ QWaylandDataControlDeviceV1(QWaylandDataControlManagerV1 *manager, QWaylandInputDevice *seat);
+
+public:
+ ~QWaylandDataControlDeviceV1() override;
+ QWaylandDataControlOfferV1 *primarySelectionOffer() const { return m_primarySelectionOffer.data(); }
+ QWaylandDataControlOfferV1 *selectionOffer() const { return m_selectionOffer.data(); }
+ void invalidateSelectionOffer();
+ QWaylandDataControlSourceV1 *selectionSource() const { return m_selectionSource.data(); }
+ QWaylandDataControlSourceV1 *primarySelectionSource() const { return m_primarySelectionSource.data(); }
+ void setSelectionSource(QWaylandDataControlSourceV1 *source);
+ void setPrimarySelectionSource(QWaylandDataControlSourceV1 *source);
+
+protected:
+ void zwlr_data_control_device_v1_data_offer(struct ::zwlr_data_control_offer_v1 *id) override;
+ void zwlr_data_control_device_v1_selection(struct ::zwlr_data_control_offer_v1 *id) override;
+ void zwlr_data_control_device_v1_finished() override;
+ void zwlr_data_control_device_v1_primary_selection(struct ::zwlr_data_control_offer_v1 *id) override;
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QWaylandInputDevice *m_seat = nullptr;
+ QScopedPointer m_selectionOffer;
+ QScopedPointer m_primarySelectionOffer;
+ QScopedPointer m_selectionSource;
+ QScopedPointer m_primarySelectionSource;
+ friend class QWaylandDataControlManagerV1;
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDDATACONTROLV1_H
diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp
index 572275057d8..dfdf88c0b4a 100644
--- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp
+++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp
@@ -1,4 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 Jie Liu
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwaylanddisplay_p.h"
@@ -13,6 +14,7 @@
#include "qwaylandinputdevice_p.h"
#if QT_CONFIG(clipboard)
#include "qwaylandclipboard_p.h"
+#include "qwaylanddatacontrolv1_p.h"
#endif
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevicemanager_p.h"
@@ -53,6 +55,7 @@
#include
#include
#include
+#include
#include
@@ -329,6 +332,7 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
}
mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
+ mPreferWlrDataControl = qEnvironmentVariableIntValue("QT_WAYLAND_USE_DATA_CONTROL") > 0;
}
void QWaylandDisplay::setupConnection()
@@ -787,6 +791,13 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
} else if (
interface == QLatin1String(QtWayland::org_kde_kwin_appmenu_manager::interface()->name)) {
mGlobals.appMenuManager.reset(new QWaylandAppMenuManager(registry, id, 1));
+#if QT_CONFIG(clipboard)
+ } else if (mPreferWlrDataControl && interface == QLatin1String(QWaylandDataControlManagerV1::interface()->name)) {
+ mGlobals.dataControlManager.reset(new QWaylandDataControlManagerV1(this, id, 2));
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) {
+ inputDevice->setDataControlDevice(mGlobals.dataControlManager->createDevice(inputDevice));
+ }
+#endif
}
mRegistryGlobals.append(RegistryGlobal(id, interface, version, registry));
@@ -851,6 +862,13 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
inputDevice->setPrimarySelectionDevice(nullptr);
}
+#endif
+#if QT_CONFIG(clipboard)
+ if (global.interface == QLatin1String(QtWayland::zwlr_data_control_manager_v1::interface()->name)) {
+ mGlobals.dataControlManager.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setDataControlDevice(nullptr);
+ }
#endif
emit globalRemoved(mRegistryGlobals.takeAt(i));
break;
diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h
index 074117a38f2..1a6a5ba8e91 100644
--- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h
+++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h
@@ -1,4 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 Jie Liu
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWAYLANDDISPLAY_H
@@ -71,6 +72,9 @@ class QWaylandXdgOutputManagerV1;
class QWaylandClientBufferIntegration;
class QWaylandWindowManagerIntegration;
class QWaylandDataDeviceManager;
+#if QT_CONFIG(clipboard)
+class QWaylandDataControlManagerV1;
+#endif
#if QT_CONFIG(wayland_client_primary_selection)
class QWaylandPrimarySelectionDeviceManagerV1;
#endif
@@ -148,6 +152,12 @@ public:
return mGlobals.dndSelectionHandler.get();
}
#endif
+#if QT_CONFIG(clipboard)
+ QWaylandDataControlManagerV1 *dataControlManager() const
+ {
+ return mGlobals.dataControlManager.get();
+ }
+#endif
#if QT_CONFIG(wayland_client_primary_selection)
QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const
{
@@ -334,6 +344,9 @@ private:
std::unique_ptr tabletManager;
#endif
std::unique_ptr pointerGestures;
+#if QT_CONFIG(clipboard)
+ std::unique_ptr dataControlManager;
+#endif
#if QT_CONFIG(wayland_client_primary_selection)
std::unique_ptr primarySelectionManager;
#endif
@@ -363,6 +376,7 @@ private:
struct wl_callback *mSyncCallback = nullptr;
static const wl_callback_listener syncCallbackListener;
bool mWaylandTryReconnect = false;
+ bool mPreferWlrDataControl = false;
bool mWaylandInputContextRequested = [] () {
const auto requested = QPlatformInputContextFactory::requested();
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
index afbb3879b6d..0c4da6fef4a 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
@@ -1,4 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 Jie Liu
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwaylandinputdevice_p.h"
@@ -12,6 +13,9 @@
#include "qwaylanddatadevice_p.h"
#include "qwaylanddatadevicemanager_p.h"
#endif
+#if QT_CONFIG(clipboard)
+#include "qwaylanddatacontrolv1_p.h"
+#endif
#if QT_CONFIG(wayland_client_primary_selection)
#include "qwaylandprimaryselectionv1_p.h"
#endif
@@ -316,6 +320,13 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
, mDisplay(display->wl_display())
, mId(id)
{
+
+#if QT_CONFIG(clipboard)
+ if (auto *dataControlManager = mQDisplay->dataControlManager()) {
+ setDataControlDevice(dataControlManager->createDevice(this));
+ }
+#endif
+
#if QT_CONFIG(wayland_datadevice)
if (mQDisplay->dndSelectionHandler()) {
mDataDevice = mQDisplay->dndSelectionHandler()->getDataDevice(this);
@@ -485,6 +496,18 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
}
#endif
+#if QT_CONFIG(clipboard)
+void QWaylandInputDevice::setDataControlDevice(QWaylandDataControlDeviceV1 *dataControlDevice)
+{
+ mDataControlDevice.reset(dataControlDevice);
+}
+
+QWaylandDataControlDeviceV1 *QWaylandInputDevice::dataControlDevice() const
+{
+ return mDataControlDevice.data();
+}
+#endif
+
#if QT_CONFIG(wayland_client_primary_selection)
void QWaylandInputDevice::setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice)
{
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
index 0d79ef93376..bcaf025840d 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
@@ -1,4 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 Jie Liu
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWAYLANDINPUTDEVICE_H
@@ -51,6 +52,9 @@ namespace QtWaylandClient {
class QWaylandDataDevice;
class QWaylandDisplay;
+#if QT_CONFIG(clipboard)
+class QWaylandDataControlDeviceV1;
+#endif
#if QT_CONFIG(wayland_client_primary_selection)
class QWaylandPrimarySelectionDeviceV1;
#endif
@@ -102,6 +106,11 @@ public:
QWaylandDataDevice *dataDevice() const;
#endif
+#if QT_CONFIG(clipboard)
+ void setDataControlDevice(QWaylandDataControlDeviceV1 *dataControlDevice);
+ QWaylandDataControlDeviceV1 *dataControlDevice() const;
+#endif
+
#if QT_CONFIG(wayland_client_primary_selection)
void setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice);
QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const;
@@ -166,6 +175,10 @@ protected:
QWaylandDataDevice *mDataDevice = nullptr;
#endif
+#if QT_CONFIG(clipboard)
+ QScopedPointer mDataControlDevice;
+#endif
+
#if QT_CONFIG(wayland_client_primary_selection)
QScopedPointer mPrimarySelectionDevice;
#endif