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 }