File indexing completed on 2024-11-10 04:57:26

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 #include "dataoffer.h"
0008 #include "datadevice.h"
0009 #include "datasource.h"
0010 
0011 // Qt
0012 #include <QPointer>
0013 #include <QStringList>
0014 // Wayland
0015 #include <qwayland-server-wayland.h>
0016 // system
0017 #include <unistd.h>
0018 
0019 namespace KWin
0020 {
0021 class DataOfferInterfacePrivate : public QtWaylandServer::wl_data_offer
0022 {
0023 public:
0024     DataOfferInterfacePrivate(AbstractDataSource *source, DataOfferInterface *q, wl_resource *resource);
0025     DataOfferInterface *q;
0026     QPointer<AbstractDataSource> source;
0027 
0028     std::optional<DataDeviceManagerInterface::DnDActions> supportedDnDActions = std::nullopt;
0029     std::optional<DataDeviceManagerInterface::DnDAction> preferredDnDAction = std::nullopt;
0030 
0031 protected:
0032     void data_offer_destroy_resource(Resource *resource) override;
0033     void data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type) override;
0034     void data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd) override;
0035     void data_offer_destroy(Resource *resource) override;
0036     void data_offer_finish(Resource *resource) override;
0037     void data_offer_set_actions(Resource *resource, uint32_t dnd_actions, uint32_t preferred_action) override;
0038 };
0039 
0040 DataOfferInterfacePrivate::DataOfferInterfacePrivate(AbstractDataSource *_source, DataOfferInterface *_q, wl_resource *resource)
0041     : QtWaylandServer::wl_data_offer(resource)
0042     , q(_q)
0043     , source(_source)
0044 {
0045     // defaults are set to sensible values for < version 3 interfaces
0046     if (wl_resource_get_version(resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) {
0047         supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy | DataDeviceManagerInterface::DnDAction::Move;
0048         preferredDnDAction = DataDeviceManagerInterface::DnDAction::Copy;
0049     }
0050 }
0051 
0052 void DataOfferInterfacePrivate::data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type)
0053 {
0054     if (!source) {
0055         return;
0056     }
0057     source->accept(mime_type);
0058 }
0059 
0060 void DataOfferInterfacePrivate::data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd)
0061 {
0062     if (!source) {
0063         close(fd);
0064         return;
0065     }
0066     source->requestData(mime_type, fd);
0067 }
0068 
0069 void DataOfferInterfacePrivate::data_offer_destroy(QtWaylandServer::wl_data_offer::Resource *resource)
0070 {
0071     wl_resource_destroy(resource->handle);
0072 }
0073 
0074 void DataOfferInterfacePrivate::data_offer_finish(Resource *resource)
0075 {
0076     if (!source) {
0077         return;
0078     }
0079     source->dndFinished();
0080     // TODO: It is a client error to perform other requests than wl_data_offer.destroy after this one
0081 }
0082 
0083 void DataOfferInterfacePrivate::data_offer_set_actions(Resource *resource, uint32_t dnd_actions, uint32_t preferred_action)
0084 {
0085     // TODO: check it's drag and drop, otherwise send error
0086     // verify that the no other actions are sent
0087     if (dnd_actions
0088         & ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move
0089             | QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
0090         wl_resource_post_error(resource->handle, error_invalid_action_mask, "Invalid action mask");
0091         return;
0092     }
0093 
0094     if (preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_copy
0095         && preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_move
0096         && preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_ask
0097         && preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_none) {
0098         wl_resource_post_error(resource->handle, error_invalid_action, "Invalid preferred action");
0099         return;
0100     }
0101 
0102     DataDeviceManagerInterface::DnDActions supportedActions;
0103     if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
0104         supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
0105     }
0106     if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
0107         supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
0108     }
0109     if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
0110         supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
0111     }
0112 
0113     DataDeviceManagerInterface::DnDAction preferredAction = DataDeviceManagerInterface::DnDAction::None;
0114     if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
0115         preferredAction = DataDeviceManagerInterface::DnDAction::Copy;
0116     } else if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_move) {
0117         preferredAction = DataDeviceManagerInterface::DnDAction::Move;
0118     } else if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
0119         preferredAction = DataDeviceManagerInterface::DnDAction::Ask;
0120     }
0121 
0122     if (supportedDnDActions != supportedActions || preferredDnDAction != preferredAction) {
0123         supportedDnDActions = supportedActions;
0124         preferredDnDAction = preferredAction;
0125         Q_EMIT q->dragAndDropActionsChanged();
0126     }
0127 }
0128 
0129 void DataOfferInterface::sendSourceActions()
0130 {
0131     if (!d->source) {
0132         return;
0133     }
0134     if (d->resource()->version() < WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
0135         return;
0136     }
0137     uint32_t wlActions = QtWaylandServer::wl_data_device_manager::dnd_action_none;
0138     const auto actions = d->source->supportedDragAndDropActions();
0139     if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
0140         wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_copy;
0141     }
0142     if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
0143         wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_move;
0144     }
0145     if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
0146         wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_ask;
0147     }
0148     d->send_source_actions(wlActions);
0149 }
0150 
0151 void DataOfferInterfacePrivate::data_offer_destroy_resource(QtWaylandServer::wl_data_offer::Resource *resource)
0152 {
0153     delete q;
0154 }
0155 
0156 DataOfferInterface::DataOfferInterface(AbstractDataSource *source, wl_resource *resource)
0157     : QObject(nullptr)
0158     , d(new DataOfferInterfacePrivate(source, this, resource))
0159 {
0160     Q_ASSERT(source);
0161     connect(source, &DataSourceInterface::mimeTypeOffered, this, [this](const QString &mimeType) {
0162         d->send_offer(mimeType);
0163     });
0164 }
0165 
0166 DataOfferInterface::~DataOfferInterface() = default;
0167 
0168 void DataOfferInterface::sendAllOffers()
0169 {
0170     for (const QString &mimeType : d->source->mimeTypes()) {
0171         d->send_offer(mimeType);
0172     }
0173 }
0174 
0175 wl_resource *DataOfferInterface::resource() const
0176 {
0177     return d->resource()->handle;
0178 }
0179 
0180 std::optional<DataDeviceManagerInterface::DnDActions> DataOfferInterface::supportedDragAndDropActions() const
0181 {
0182     return d->supportedDnDActions;
0183 }
0184 
0185 std::optional<DataDeviceManagerInterface::DnDAction> DataOfferInterface::preferredDragAndDropAction() const
0186 {
0187     return d->preferredDnDAction;
0188 }
0189 
0190 void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
0191 {
0192     if (d->resource()->version() < WL_DATA_OFFER_ACTION_SINCE_VERSION) {
0193         return;
0194     }
0195     uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
0196     if (action == DataDeviceManagerInterface::DnDAction::Copy) {
0197         wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
0198     } else if (action == DataDeviceManagerInterface::DnDAction::Move) {
0199         wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
0200     } else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
0201         wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
0202     }
0203     d->send_action(wlAction);
0204 }
0205 }
0206 
0207 #include "moc_dataoffer.cpp"