Index: autotests/client/test_datadevice.cpp =================================================================== --- autotests/client/test_datadevice.cpp +++ autotests/client/test_datadevice.cpp @@ -184,9 +184,11 @@ QVERIFY(!deviceInterface->selection()); QVERIFY(deviceInterface->parentResource()); + + // this will probably fail, we need to make a selection client side QVERIFY(!m_seatInterface->selection()); - m_seatInterface->setSelection(deviceInterface); - QCOMPARE(m_seatInterface->selection(), deviceInterface); + m_seatInterface->setSelection(deviceInterface->selection()); + QCOMPARE(m_seatInterface->selection(), deviceInterface->selection()); // and destroy QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed); @@ -404,7 +406,7 @@ // send selection to datadevice QSignalSpy selectionOfferedSpy(dataDevice.data(), SIGNAL(selectionOffered(KWayland::Client::DataOffer*))); QVERIFY(selectionOfferedSpy.isValid()); - deviceInterface->sendSelection(deviceInterface); + deviceInterface->sendSelection(deviceInterface->selection()); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); auto dataOffer = selectionOfferedSpy.first().first().value(); @@ -439,7 +441,7 @@ dataDevice.reset(); QVERIFY(unboundSpy.wait()); // send a selection to the unbound data device - deviceInterface->sendSelection(deviceInterface); + deviceInterface->sendSelection(deviceInterface->selection()); } void TestDataDevice::testSendSelectionOnSeat() Index: autotests/client/test_wayland_seat.cpp =================================================================== --- autotests/client/test_wayland_seat.cpp +++ autotests/client/test_wayland_seat.cpp @@ -26,6 +26,7 @@ #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" +#include "../../src/server/datasource_interface.h" #include "../../src/server/display.h" #include "../../src/server/keyboard_interface.h" #include "../../src/server/pointer_interface.h" @@ -72,7 +73,6 @@ void testCast(); void testDestroy(); void testSelection(); - void testSelectionNoDataSource(); void testDataDeviceForKeyboardSurface(); void testTouch(); void testDisconnect(); @@ -1921,54 +1921,6 @@ QVERIFY(cancelledSpy.isValid()); m_seatInterface->setSelection(ddi); QVERIFY(cancelledSpy.wait()); - - // Copy already cleared selection, BUG 383054 - ddi->sendSelection(ddi); -} - -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 KWaylandServer; - // 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::testDataDeviceForKeyboardSurface() @@ -2021,7 +1973,7 @@ QVERIFY(ddiCreatedSpy.wait()); auto ddi = ddiCreatedSpy.first().first().value(); QVERIFY(ddi); - m_seatInterface->setSelection(ddi); + m_seatInterface->setSelection(ddi->selection()); // switch to other client // create a surface and pass it keyboard focus Index: src/server/datadevice_interface.h =================================================================== --- src/server/datadevice_interface.h +++ src/server/datadevice_interface.h @@ -51,7 +51,7 @@ DataSourceInterface *selection() const; - void sendSelection(DataDeviceInterface *other); + void sendSelection(DataSourceInterface *other); void sendClearSelection(); /** * The event is sent when a drag-and-drop operation is ended because the implicit grab is removed. Index: src/server/datadevice_interface.cpp =================================================================== --- src/server/datadevice_interface.cpp +++ src/server/datadevice_interface.cpp @@ -209,15 +209,10 @@ return d->selection; } -void DataDeviceInterface::sendSelection(DataDeviceInterface *other) +void DataDeviceInterface::sendSelection(DataSourceInterface *other) { Q_D(); - auto otherSelection = other->selection(); - if (!otherSelection) { - sendClearSelection(); - return; - } - auto r = d->createDataOffer(otherSelection); + auto r = d->createDataOffer(other); if (!r) { return; } Index: src/server/seat_interface.h =================================================================== --- src/server/seat_interface.h +++ src/server/seat_interface.h @@ -23,6 +23,7 @@ { class DataDeviceInterface; +class DataSourceInterface; class Display; class SurfaceInterface; class TextInputInterface; @@ -692,8 +693,10 @@ * @since 5.24 * @see selectionChanged * @see setSelection + * This may be null **/ - DataDeviceInterface *selection() const; + DataSourceInterface *selection() const; + /** * This method allows to manually set the @p dataDevice for the current clipboard selection. * The clipboard selection is handled automatically in SeatInterface. @@ -707,7 +710,7 @@ * @see selectionChanged * @since 5.24 **/ - void setSelection(DataDeviceInterface *dataDevice); + void setSelection(DataSourceInterface *selection); static SeatInterface *get(wl_resource *native); @@ -736,7 +739,7 @@ * @see selection * @see setSelection **/ - void selectionChanged(DataDeviceInterface*); + void selectionChanged(DataSourceInterface*); /** * Emitted when a drag'n'drop operation is started Index: src/server/seat_interface.cpp =================================================================== --- src/server/seat_interface.cpp +++ src/server/seat_interface.cpp @@ -255,25 +255,17 @@ if (keys.focus.selection == dataDevice) { keys.focus.selection = nullptr; } - if (currentSelection == dataDevice) { - // current selection is cleared - currentSelection = nullptr; - emit q->selectionChanged(nullptr); - if (keys.focus.selection) { - keys.focus.selection->sendClearSelection(); - } - } }; QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup); QObject::connect(dataDevice, &Resource::unbound, q, dataDeviceCleanup); QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q, [this, dataDevice] { - updateSelection(dataDevice, true); + updateSelection(dataDevice); } ); QObject::connect(dataDevice, &DataDeviceInterface::selectionCleared, q, [this, dataDevice] { - updateSelection(dataDevice, false); + updateSelection(dataDevice); } ); QObject::connect(dataDevice, &DataDeviceInterface::dragStarted, q, @@ -332,14 +324,13 @@ // same client? if (keys.focus.surface->client() == dataDevice->client()) { keys.focus.selection = dataDevice; - if (currentSelection && currentSelection->selection()) { + if (currentSelection) { dataDevice->sendSelection(currentSelection); } } } } - void SeatInterface::Private::registerTextInput(TextInputInterface *ti) { // text input version 0 might call this multiple times @@ -383,44 +374,13 @@ emit q->dragEnded(); } -void SeatInterface::Private::cancelPreviousSelection(DataDeviceInterface *dataDevice) +void SeatInterface::Private::updateSelection(DataDeviceInterface *dataDevice) { - if (!currentSelection) { + // if the update is from the focussed window we should inform the active client + if (!(keys.focus.surface && (keys.focus.surface->client() == dataDevice->client()))) { return; } - if (auto s = currentSelection->selection()) { - if (currentSelection != dataDevice) { - // only if current selection is not on the same device - // that would cancel the newly set source - s->cancel(); - } - } -} - -void SeatInterface::Private::updateSelection(DataDeviceInterface *dataDevice, bool set) -{ - bool selChanged = currentSelection != dataDevice; - if (keys.focus.surface && (keys.focus.surface->client() == dataDevice->client())) { - // cancel the previous selection - cancelPreviousSelection(dataDevice); - // new selection on a data device belonging to current keyboard focus - currentSelection = dataDevice; - } - if (dataDevice == currentSelection) { - // need to send out the selection - if (keys.focus.selection) { - if (set) { - keys.focus.selection->sendSelection(dataDevice); - } else { - keys.focus.selection->sendClearSelection(); - currentSelection = nullptr; - selChanged = true; - } - } - } - if (selChanged) { - emit q->selectionChanged(currentSelection); - } + q->setSelection(dataDevice->selection()); } void SeatInterface::setHasKeyboard(bool has) @@ -1130,7 +1090,7 @@ // selection? d->keys.focus.selection = d->dataDeviceForSurface(surface); if (d->keys.focus.selection) { - if (d->currentSelection && d->currentSelection->selection()) { + if (d->currentSelection) { d->keys.focus.selection->sendSelection(d->currentSelection); } else { d->keys.focus.selection->sendClearSelection(); @@ -1587,29 +1547,43 @@ return d->textInput.focus.textInput; } -DataDeviceInterface *SeatInterface::selection() const +DataSourceInterface *SeatInterface::selection() const { Q_D(); return d->currentSelection; } -void SeatInterface::setSelection(DataDeviceInterface *dataDevice) +void SeatInterface::setSelection(DataSourceInterface *selection) { Q_D(); - if (d->currentSelection == dataDevice) { + if (d->currentSelection == selection) { return; } - // cancel the previous selection - d->cancelPreviousSelection(dataDevice); - d->currentSelection = dataDevice; + + if (d->currentSelection) { + d->currentSelection->cancel(); + disconnect(d->currentSelection, nullptr, this, nullptr); + } + + if (selection) { + auto cleanup = [this]() { + setSelection(nullptr); + }; + connect(selection, &DataSourceInterface::unbound, this, cleanup); + connect(selection, &QObject::destroyed, this, cleanup); + } + + d->currentSelection = selection; + if (d->keys.focus.selection) { - if (dataDevice && dataDevice->selection()) { - d->keys.focus.selection->sendSelection(dataDevice); + if (selection) { + d->keys.focus.selection->sendSelection(selection); } else { d->keys.focus.selection->sendClearSelection(); } } - emit selectionChanged(dataDevice); + + emit selectionChanged(selection); } } Index: src/server/seat_interface_p.h =================================================================== --- src/server/seat_interface_p.h +++ src/server/seat_interface_p.h @@ -37,7 +37,6 @@ void registerDataDevice(DataDeviceInterface *dataDevice); void registerTextInput(TextInputInterface *textInput); void endDrag(quint32 serial); - void cancelPreviousSelection(DataDeviceInterface *newlySelectedDataDevice); QString name; bool pointer = false; @@ -49,8 +48,11 @@ QVector keyboards; QVector touchs; QVector dataDevices; + QVector textInputs; - DataDeviceInterface *currentSelection = nullptr; + + // the last thing copied into the clipboard content + DataSourceInterface *currentSelection = nullptr; // Pointer related members struct Pointer { @@ -165,7 +167,7 @@ void getPointer(wl_client *client, wl_resource *resource, uint32_t id); void getKeyboard(wl_client *client, wl_resource *resource, uint32_t id); void getTouch(wl_client *client, wl_resource *resource, uint32_t id); - void updateSelection(DataDeviceInterface *dataDevice, bool set); + void updateSelection(DataDeviceInterface *dataDevice); static Private *cast(wl_resource *r); static void unbind(wl_resource *r);