File indexing completed on 2024-11-10 04:57:25
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"