1 /*
2     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7 #include "datasource_interface.h"
8 #include "clientconnection.h"
9 #include "datadevicemanager_interface.h"
10 #include "utils.h"
11 // Qt
12 #include <QStringList>
13 // Wayland
14 #include <qwayland-server-wayland.h>
15 // system
16 #include <unistd.h>
17 
18 namespace KWaylandServer
19 {
20 class DataSourceInterfacePrivate : public QtWaylandServer::wl_data_source
21 {
22 public:
23     DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource);
24 
25     DataSourceInterface *q;
26     QStringList mimeTypes;
27     DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None;
28     bool isAccepted = false;
29 
30 protected:
31     void data_source_destroy_resource(Resource *resource) override;
32     void data_source_offer(Resource *resource, const QString &mime_type) override;
33     void data_source_destroy(Resource *resource) override;
34     void data_source_set_actions(Resource *resource, uint32_t dnd_actions) override;
35 
36 private:
37     void offer(const QString &mimeType);
38 };
39 
DataSourceInterfacePrivate(DataSourceInterface * _q,::wl_resource * resource)40 DataSourceInterfacePrivate::DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource)
41     : QtWaylandServer::wl_data_source(resource)
42     , q(_q)
43 {
44 }
45 
data_source_destroy_resource(Resource * resource)46 void DataSourceInterfacePrivate::data_source_destroy_resource(Resource *resource)
47 {
48     Q_UNUSED(resource)
49     Q_EMIT q->aboutToBeDestroyed();
50     delete q;
51 }
52 
data_source_offer(QtWaylandServer::wl_data_source::Resource * resource,const QString & mime_type)53 void DataSourceInterfacePrivate::data_source_offer(QtWaylandServer::wl_data_source::Resource *resource, const QString &mime_type)
54 {
55     Q_UNUSED(resource)
56     mimeTypes << mime_type;
57     Q_EMIT q->mimeTypeOffered(mime_type);
58 }
59 
data_source_destroy(QtWaylandServer::wl_data_source::Resource * resource)60 void DataSourceInterfacePrivate::data_source_destroy(QtWaylandServer::wl_data_source::Resource *resource)
61 {
62     wl_resource_destroy(resource->handle);
63 }
64 
offer(const QString & mimeType)65 void DataSourceInterfacePrivate::offer(const QString &mimeType)
66 {
67     mimeTypes << mimeType;
68     Q_EMIT q->mimeTypeOffered(mimeType);
69 }
70 
data_source_set_actions(Resource * resource,uint32_t dnd_actions)71 void DataSourceInterfacePrivate::data_source_set_actions(Resource *resource, uint32_t dnd_actions)
72 {
73     // verify that the no other actions are sent
74     if (dnd_actions
75         & ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move
76             | QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
77         wl_resource_post_error(resource->handle, error_invalid_action_mask, "Invalid action mask");
78         return;
79     }
80     DataDeviceManagerInterface::DnDActions supportedActions;
81     if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
82         supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
83     }
84     if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
85         supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
86     }
87     if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
88         supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
89     }
90     if (supportedDnDActions != supportedActions) {
91         supportedDnDActions = supportedActions;
92         Q_EMIT q->supportedDragAndDropActionsChanged();
93     }
94 }
95 
DataSourceInterface(DataDeviceManagerInterface * parent,wl_resource * resource)96 DataSourceInterface::DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *resource)
97     : AbstractDataSource(parent)
98     , d(new DataSourceInterfacePrivate(this, resource))
99 {
100     if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
101         d->supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy;
102     }
103 }
104 
105 DataSourceInterface::~DataSourceInterface() = default;
106 
accept(const QString & mimeType)107 void DataSourceInterface::accept(const QString &mimeType)
108 {
109     d->send_target(mimeType);
110     d->isAccepted = !mimeType.isNull();
111 }
112 
requestData(const QString & mimeType,qint32 fd)113 void DataSourceInterface::requestData(const QString &mimeType, qint32 fd)
114 {
115     d->send_send(mimeType, int32_t(fd));
116     close(fd);
117 }
118 
cancel()119 void DataSourceInterface::cancel()
120 {
121     d->send_cancelled();
122 }
123 
mimeTypes() const124 QStringList DataSourceInterface::mimeTypes() const
125 {
126     return d->mimeTypes;
127 }
128 
get(wl_resource * native)129 DataSourceInterface *DataSourceInterface::get(wl_resource *native)
130 {
131     if (auto sourcePrivate = resource_cast<DataSourceInterfacePrivate *>(native)) {
132         return sourcePrivate->q;
133     }
134     return nullptr;
135 }
136 
supportedDragAndDropActions() const137 DataDeviceManagerInterface::DnDActions DataSourceInterface::supportedDragAndDropActions() const
138 {
139     return d->supportedDnDActions;
140 }
141 
dropPerformed()142 void DataSourceInterface::dropPerformed()
143 {
144     if (d->resource()->version() < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
145         return;
146     }
147     d->send_dnd_drop_performed();
148 }
149 
dndFinished()150 void DataSourceInterface::dndFinished()
151 {
152     if (d->resource()->version() < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
153         return;
154     }
155     d->send_dnd_finished();
156 }
157 
dndAction(DataDeviceManagerInterface::DnDAction action)158 void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
159 {
160     if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
161         return;
162     }
163     uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
164     if (action == DataDeviceManagerInterface::DnDAction::Copy) {
165         wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
166     } else if (action == DataDeviceManagerInterface::DnDAction::Move) {
167         wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
168     } else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
169         wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
170     }
171     d->send_action(wlAction);
172 }
173 
dndCancelled()174 void DataSourceInterface::dndCancelled()
175 {
176     // for v3 or less, cancel should not be called after a failed drag operation
177     if (wl_resource_get_version(resource()) < 3) {
178         return;
179     }
180     d->send_cancelled();
181 }
182 
resource() const183 wl_resource *DataSourceInterface::resource() const
184 {
185     return d->resource()->handle;
186 }
187 
client() const188 wl_client *DataSourceInterface::client() const
189 {
190     return d->resource()->client();
191 }
192 
isAccepted() const193 bool DataSourceInterface::isAccepted() const
194 {
195     return d->isAccepted;
196 }
197 
setAccepted(bool accepted)198 void DataSourceInterface::setAccepted(bool accepted)
199 {
200     d->isAccepted = accepted;
201 }
202 
203 }
204