File indexing completed on 2024-05-19 16:35:17

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
0004     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 #include "datadevice_interface.h"
0009 #include "datadevice_interface_p.h"
0010 #include "datadevicemanager_interface.h"
0011 #include "dataoffer_interface.h"
0012 #include "datasource_interface.h"
0013 #include "display.h"
0014 #include "pointer_interface.h"
0015 #include "seat_interface.h"
0016 #include "seat_interface_p.h"
0017 #include "surface_interface.h"
0018 #include "surfacerole_p.h"
0019 
0020 namespace KWaylandServer
0021 {
0022 class DragAndDropIconPrivate : public SurfaceRole
0023 {
0024 public:
0025     explicit DragAndDropIconPrivate(DragAndDropIcon *q, SurfaceInterface *surface);
0026 
0027     void commit() override;
0028 
0029     DragAndDropIcon *q;
0030     QPoint position;
0031 };
0032 
0033 DragAndDropIconPrivate::DragAndDropIconPrivate(DragAndDropIcon *q, SurfaceInterface *surface)
0034     : SurfaceRole(surface, QByteArrayLiteral("dnd_icon"))
0035     , q(q)
0036 {
0037 }
0038 
0039 void DragAndDropIconPrivate::commit()
0040 {
0041     position += surface()->offset();
0042     Q_EMIT q->changed();
0043 }
0044 
0045 DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface)
0046     : QObject(surface)
0047     , d(new DragAndDropIconPrivate(this, surface))
0048 {
0049 }
0050 
0051 DragAndDropIcon::~DragAndDropIcon()
0052 {
0053 }
0054 
0055 QPoint DragAndDropIcon::position() const
0056 {
0057     return d->position;
0058 }
0059 
0060 SurfaceInterface *DragAndDropIcon::surface() const
0061 {
0062     return d->surface();
0063 }
0064 
0065 DataDeviceInterfacePrivate *DataDeviceInterfacePrivate::get(DataDeviceInterface *device)
0066 {
0067     return device->d.get();
0068 }
0069 
0070 DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
0071     : QtWaylandServer::wl_data_device(resource)
0072     , seat(seat)
0073     , q(_q)
0074 {
0075 }
0076 
0077 void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
0078                                                         wl_resource *sourceResource,
0079                                                         wl_resource *originResource,
0080                                                         wl_resource *iconResource,
0081                                                         uint32_t serial)
0082 {
0083     SurfaceInterface *iconSurface = SurfaceInterface::get(iconResource);
0084 
0085     const SurfaceRole *surfaceRole = SurfaceRole::get(iconSurface);
0086     if (surfaceRole) {
0087         wl_resource_post_error(resource->handle, error_role, "the icon surface already has a role assigned %s", surfaceRole->name().constData());
0088         return;
0089     }
0090 
0091     SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
0092     DataSourceInterface *dataSource = nullptr;
0093     if (sourceResource) {
0094         dataSource = DataSourceInterface::get(sourceResource);
0095     }
0096 
0097     const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
0098     if (!pointerGrab) {
0099         // Client doesn't have pointer grab.
0100         const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface;
0101         if (!touchGrab) {
0102             // Client neither has pointer nor touch grab. No drag start allowed.
0103             return;
0104         }
0105     }
0106 
0107     DragAndDropIcon *dragIcon = nullptr;
0108     if (iconSurface) {
0109         // drag icon lifespan is mapped to surface lifespan
0110         dragIcon = new DragAndDropIcon(iconSurface);
0111     }
0112     drag.serial = serial;
0113     Q_EMIT q->dragStarted(dataSource, focusSurface, serial, dragIcon);
0114 }
0115 
0116 void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
0117 {
0118     DataSourceInterface *dataSource = DataSourceInterface::get(source);
0119 
0120     if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
0121         wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
0122         return;
0123     }
0124 
0125     if (selection == dataSource) {
0126         return;
0127     }
0128     if (selection) {
0129         selection->cancel();
0130     }
0131     selection = dataSource;
0132     Q_EMIT q->selectionChanged(selection);
0133 }
0134 
0135 void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
0136 {
0137     wl_resource_destroy(resource->handle);
0138 }
0139 
0140 DataOfferInterface *DataDeviceInterfacePrivate::createDataOffer(AbstractDataSource *source)
0141 {
0142     if (!source) {
0143         // a data offer can only exist together with a source
0144         return nullptr;
0145     }
0146 
0147     wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &wl_data_offer_interface, resource()->version(), 0);
0148     if (!data_offer_resource) {
0149         wl_resource_post_no_memory(resource()->handle);
0150         return nullptr;
0151     }
0152 
0153     DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
0154     send_data_offer(offer->resource());
0155     offer->sendAllOffers();
0156     offer->sendSourceActions();
0157     return offer;
0158 }
0159 
0160 void DataDeviceInterfacePrivate::data_device_destroy_resource(QtWaylandServer::wl_data_device::Resource *resource)
0161 {
0162     Q_EMIT q->aboutToBeDestroyed();
0163     delete q;
0164 }
0165 
0166 DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, wl_resource *resource)
0167     : AbstractDropHandler(nullptr)
0168     , d(new DataDeviceInterfacePrivate(seat, this, resource))
0169 {
0170     SeatInterfacePrivate *seatPrivate = SeatInterfacePrivate::get(seat);
0171     seatPrivate->registerDataDevice(this);
0172 }
0173 
0174 DataDeviceInterface::~DataDeviceInterface() = default;
0175 
0176 SeatInterface *DataDeviceInterface::seat() const
0177 {
0178     return d->seat;
0179 }
0180 
0181 DataSourceInterface *DataDeviceInterface::selection() const
0182 {
0183     return d->selection;
0184 }
0185 
0186 void DataDeviceInterface::sendSelection(AbstractDataSource *other)
0187 {
0188     auto r = other ? d->createDataOffer(other) : nullptr;
0189     d->send_selection(r ? r->resource() : nullptr);
0190 }
0191 
0192 void DataDeviceInterface::drop()
0193 {
0194     d->send_drop();
0195     d->drag.surface = nullptr; // prevent sending wl_data_device.leave event
0196 
0197     disconnect(d->drag.posConnection);
0198     d->drag.posConnection = QMetaObject::Connection();
0199     disconnect(d->drag.destroyConnection);
0200     d->drag.destroyConnection = QMetaObject::Connection();
0201 
0202     if (d->seat->dragSource()->selectedDndAction() != DataDeviceManagerInterface::DnDAction::Ask) {
0203         disconnect(d->drag.sourceActionConnection);
0204         d->drag.sourceActionConnection = QMetaObject::Connection();
0205         disconnect(d->drag.targetActionConnection);
0206         d->drag.targetActionConnection = QMetaObject::Connection();
0207     }
0208 }
0209 
0210 static DataDeviceManagerInterface::DnDAction chooseDndAction(AbstractDataSource *source, DataOfferInterface *offer)
0211 {
0212     if (offer->preferredDragAndDropAction().has_value()) {
0213         if (source->supportedDragAndDropActions().testFlag(*offer->preferredDragAndDropAction())) {
0214             return *offer->preferredDragAndDropAction();
0215         }
0216     }
0217 
0218     if (offer->supportedDragAndDropActions().has_value()) {
0219         for (const auto &action : {DataDeviceManagerInterface::DnDAction::Copy, DataDeviceManagerInterface::DnDAction::Move, DataDeviceManagerInterface::DnDAction::Ask}) {
0220             if (source->supportedDragAndDropActions().testFlag(action) && offer->supportedDragAndDropActions()->testFlag(action)) {
0221                 return action;
0222             }
0223         }
0224     }
0225 
0226     return DataDeviceManagerInterface::DnDAction::None;
0227 }
0228 
0229 void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial)
0230 {
0231     if (d->drag.surface == surface) {
0232         return;
0233     }
0234 
0235     if (d->drag.surface) {
0236         if (d->drag.surface->resource()) {
0237             d->send_leave();
0238         }
0239         if (d->drag.posConnection) {
0240             disconnect(d->drag.posConnection);
0241             d->drag.posConnection = QMetaObject::Connection();
0242         }
0243         disconnect(d->drag.destroyConnection);
0244         d->drag.destroyConnection = QMetaObject::Connection();
0245         d->drag.surface = nullptr;
0246         if (d->drag.sourceActionConnection) {
0247             disconnect(d->drag.sourceActionConnection);
0248             d->drag.sourceActionConnection = QMetaObject::Connection();
0249         }
0250         if (d->drag.targetActionConnection) {
0251             disconnect(d->drag.targetActionConnection);
0252             d->drag.targetActionConnection = QMetaObject::Connection();
0253         }
0254         // don't update serial, we need it
0255     }
0256     auto dragSource = d->seat->dragSource();
0257     if (!surface || !dragSource) {
0258         if (auto s = dragSource) {
0259             s->dndAction(DataDeviceManagerInterface::DnDAction::None);
0260         }
0261         return;
0262     }
0263 
0264     if (dragSource) {
0265         dragSource->accept(QString());
0266     }
0267     DataOfferInterface *offer = d->createDataOffer(dragSource);
0268     d->drag.surface = surface;
0269     if (d->seat->isDragPointer()) {
0270         d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] {
0271             const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
0272             d->send_motion(d->seat->timestamp().count(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
0273         });
0274     } else if (d->seat->isDragTouch()) {
0275         // When dragging from one window to another, we may end up in a data_device
0276         // that didn't get "data_device_start_drag". In that case, the internal
0277         // touch point serial will be incorrect and we need to update it to the
0278         // serial from the seat.
0279         SeatInterfacePrivate *seatPrivate = SeatInterfacePrivate::get(seat());
0280         if (seatPrivate->drag.dragImplicitGrabSerial != d->drag.serial) {
0281             d->drag.serial = seatPrivate->drag.dragImplicitGrabSerial.value();
0282         }
0283 
0284         d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this, [this](qint32 id, quint32 serial, const QPointF &globalPosition) {
0285             if (serial != d->drag.serial) {
0286                 // different touch down has been moved
0287                 return;
0288             }
0289             const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
0290             d->send_motion(d->seat->timestamp().count(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
0291         });
0292     }
0293     d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this, [this] {
0294         d->send_leave();
0295         if (d->drag.posConnection) {
0296             disconnect(d->drag.posConnection);
0297         }
0298         d->drag = DataDeviceInterfacePrivate::Drag();
0299     });
0300 
0301     QPointF pos;
0302     if (d->seat->isDragPointer()) {
0303         pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
0304     } else if (d->seat->isDragTouch()) {
0305         pos = d->seat->dragSurfaceTransformation().map(d->seat->firstTouchPointPosition());
0306     }
0307     d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
0308     if (offer) {
0309         auto matchOffers = [dragSource, offer] {
0310             const DataDeviceManagerInterface::DnDAction action = chooseDndAction(dragSource, offer);
0311             offer->dndAction(action);
0312             dragSource->dndAction(action);
0313         };
0314         d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, dragSource, matchOffers);
0315         d->drag.sourceActionConnection = connect(dragSource, &AbstractDataSource::supportedDragAndDropActionsChanged, dragSource, matchOffers);
0316     }
0317 }
0318 
0319 wl_client *DataDeviceInterface::client()
0320 {
0321     return d->resource()->client();
0322 }
0323 
0324 }