File indexing completed on 2024-05-19 05:32:33

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