diff --git a/autotests/client/test_wayland_seat.cpp b/autotests/client/test_wayland_seat.cpp --- a/autotests/client/test_wayland_seat.cpp +++ b/autotests/client/test_wayland_seat.cpp @@ -79,6 +79,7 @@ void testCast(); void testDestroy(); void testSelection(); + void testSelectionNoDataSource(); void testTouch(); void testDisconnect(); void testPointerEnterOnUnboundSurface(); @@ -1552,6 +1553,51 @@ QVERIFY(cancelledSpy.wait()); } +void TestWaylandSeat::testSelectionNoDataSource() +{ + // this test verifies that the server doesn't crash when using setSelection with + // a DataDevice which doesn't have a DataSource yet + using namespace KWayland::Client; + using namespace KWayland::Server; + // first create the DataDevice + QScopedPointer ddmi(m_display->createDataDeviceManager()); + ddmi->create(); + QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated); + QVERIFY(ddiCreatedSpy.isValid()); + Registry registry; + QSignalSpy dataDeviceManagerSpy(®istry, &Registry::dataDeviceManagerAnnounced); + QVERIFY(dataDeviceManagerSpy.isValid()); + registry.setEventQueue(m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + QVERIFY(dataDeviceManagerSpy.wait()); + QScopedPointer ddm(registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), + dataDeviceManagerSpy.first().last().value())); + QVERIFY(ddm->isValid()); + + QScopedPointer dd(ddm->getDataDevice(m_seat)); + QVERIFY(dd->isValid()); + QVERIFY(ddiCreatedSpy.wait()); + auto ddi = ddiCreatedSpy.first().first().value(); + QVERIFY(ddi); + + // now create a surface and pass it keyboard focus + QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(surfaceCreatedSpy.isValid()); + QScopedPointer surface(m_compositor->createSurface()); + QVERIFY(surface->isValid()); + QVERIFY(surfaceCreatedSpy.wait()); + auto serverSurface = surfaceCreatedSpy.first().first().value(); + QVERIFY(!m_seatInterface->selection()); + m_seatInterface->setFocusedKeyboardSurface(serverSurface); + QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); + + // now let's set the selection + m_seatInterface->setSelection(ddi); +} + void TestWaylandSeat::testTouch() { using namespace KWayland::Client; diff --git a/src/server/dataoffer_interface.cpp b/src/server/dataoffer_interface.cpp --- a/src/server/dataoffer_interface.cpp +++ b/src/server/dataoffer_interface.cpp @@ -93,6 +93,7 @@ DataOfferInterface::DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource) : Resource(new Private(source, parentInterface, this, parentResource)) { + Q_ASSERT(source); connect(source, &DataSourceInterface::mimeTypeOffered, this, [this](const QString &mimeType) { Q_D(); diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -1344,7 +1344,7 @@ d->cancelPreviousSelection(dataDevice); d->currentSelection = dataDevice; if (d->keys.focus.selection) { - if (dataDevice) { + if (dataDevice && dataDevice->selection()) { d->keys.focus.selection->sendSelection(dataDevice); } else { d->keys.focus.selection->sendClearSelection();