diff --git a/autotests/integration/helper/copy.cpp b/autotests/integration/helper/copy.cpp index 5b0bb6c2ae..dae218f2be 100644 --- a/autotests/integration/helper/copy.cpp +++ b/autotests/integration/helper/copy.cpp @@ -21,7 +21,7 @@ public: protected: void paintEvent(QPaintEvent *event) override; - void focusInEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *) override; }; Window::Window() @@ -37,13 +37,9 @@ void Window::paintEvent(QPaintEvent *event) p.fillRect(0, 0, width(), height(), Qt::red); } -void Window::focusInEvent(QFocusEvent *event) +void Window::keyPressEvent(QKeyEvent *event) { - QRasterWindow::focusInEvent(event); - // TODO: make it work without singleshot - QTimer::singleShot(100, [] { - qApp->clipboard()->setText(QStringLiteral("test")); - }); + qApp->clipboard()->setText(QStringLiteral("test")); } int main(int argc, char *argv[]) diff --git a/autotests/integration/xwayland_selections_test.cpp b/autotests/integration/xwayland_selections_test.cpp index e49ae08666..6db94f657e 100644 --- a/autotests/integration/xwayland_selections_test.cpp +++ b/autotests/integration/xwayland_selections_test.cpp @@ -10,6 +10,7 @@ #include "kwin_wayland_test.h" #include "core/output.h" +#include "wayland/display.h" #include "wayland/seat.h" #include "wayland_server.h" #include "window.h" @@ -20,6 +21,8 @@ #include #include +#include + using namespace KWin; static const QString s_socketName = QStringLiteral("wayland_test_kwin_xwayland_selections-0"); @@ -111,6 +114,10 @@ void XwaylandSelectionsTest::testSync() workspace()->activateWindow(copyWindow); } QCOMPARE(workspace()->activeWindow(), copyWindow); + // Wait until the window has a surface so we can pass input to it + QTRY_VERIFY(copyWindow->surface()); + Test::keyboardKeyPressed(KEY_C, waylandServer()->display()->nextSerial()); + Test::keyboardKeyReleased(KEY_C, waylandServer()->display()->nextSerial()); clipboardChangedSpy.wait(); // start the paste process diff --git a/autotests/wayland/client/test_datadevice.cpp b/autotests/wayland/client/test_datadevice.cpp index 0e6c69e4f4..5fe2c7e3c5 100644 --- a/autotests/wayland/client/test_datadevice.cpp +++ b/autotests/wayland/client/test_datadevice.cpp @@ -173,7 +173,7 @@ void TestDataDevice::testCreate() // this will probably fail, we need to make a selection client side QVERIFY(!m_seatInterface->selection()); - m_seatInterface->setSelection(deviceInterface->selection()); + m_seatInterface->setSelection(deviceInterface->selection(), m_display->nextSerial()); QCOMPARE(m_seatInterface->selection(), deviceInterface->selection()); // and destroy @@ -402,13 +402,13 @@ void TestDataDevice::testSetSelection() QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html")); // now clear the selection - dataDevice->clearSelection(1); + dataDevice->clearSelection(2); QVERIFY(selectionChangedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 2); QVERIFY(!deviceInterface->selection()); // set another selection - dataDevice->setSelection(2, dataSource.get()); + dataDevice->setSelection(3, dataSource.get()); QVERIFY(selectionChangedSpy.wait()); // now unbind the dataDevice QSignalSpy unboundSpy(deviceInterface, &QObject::destroyed); @@ -509,14 +509,14 @@ void TestDataDevice::testReplaceSource() QVERIFY(dataSource2->isValid()); dataSource2->offer(QStringLiteral("text/plain")); QSignalSpy sourceCancelled2Spy(dataSource2.get(), &KWayland::Client::DataSource::cancelled); - dataDevice->setSelection(1, dataSource2.get()); + dataDevice->setSelection(2, dataSource2.get()); QCOMPARE(selectionOfferedSpy.count(), 1); QVERIFY(sourceCancelledSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 2); QVERIFY(sourceCancelled2Spy.isEmpty()); // replace the data source with itself, ensure that it did not get cancelled - dataDevice->setSelection(1, dataSource2.get()); + dataDevice->setSelection(3, dataSource2.get()); QVERIFY(!sourceCancelled2Spy.wait(500)); QCOMPARE(selectionOfferedSpy.count(), 2); QVERIFY(sourceCancelled2Spy.isEmpty()); @@ -527,7 +527,7 @@ void TestDataDevice::testReplaceSource() std::unique_ptr dataSource3(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource3->isValid()); dataSource3->offer(QStringLiteral("text/plain")); - dataDevice2->setSelection(1, dataSource3.get()); + dataDevice2->setSelection(4, dataSource3.get()); QVERIFY(sourceCancelled2Spy.wait()); // try to crash by first destroying dataSource3 and setting a new DataSource @@ -535,7 +535,7 @@ void TestDataDevice::testReplaceSource() QVERIFY(dataSource4->isValid()); dataSource4->offer(QStringLiteral("text/plain")); dataSource3.reset(); - dataDevice2->setSelection(1, dataSource4.get()); + dataDevice2->setSelection(5, dataSource4.get()); QVERIFY(selectionOfferedSpy.wait()); auto dataOffer = selectionOfferedSpy.last()[0].value(); diff --git a/autotests/wayland/client/test_selection.cpp b/autotests/wayland/client/test_selection.cpp index 403b880914..77c77f30b0 100644 --- a/autotests/wayland/client/test_selection.cpp +++ b/autotests/wayland/client/test_selection.cpp @@ -234,7 +234,7 @@ void SelectionTest::testClearOnEnter() m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value(), dataSource2.get()); QVERIFY(selectionOfferedClient2Spy.wait()); // and clear - m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value()); + m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value() + 1); QVERIFY(selectionClearedClient2Spy.wait()); // now pass focus to first surface diff --git a/autotests/wayland/client/test_wayland_seat.cpp b/autotests/wayland/client/test_wayland_seat.cpp index 7e7ab71bf2..172ff9a576 100644 --- a/autotests/wayland/client/test_wayland_seat.cpp +++ b/autotests/wayland/client/test_wayland_seat.cpp @@ -1571,7 +1571,7 @@ void TestWaylandSeat::testSelection() std::unique_ptr ds(ddm->createDataSource()); QVERIFY(ds->isValid()); ds->offer(QStringLiteral("text/plain")); - dd1->setSelection(0, ds.get()); + dd1->setSelection(m_display->nextSerial(), ds.get()); QVERIFY(selectionSpy.wait()); QCOMPARE(selectionSpy.count(), 1); auto ddi = m_seatInterface->selection(); @@ -1581,7 +1581,7 @@ void TestWaylandSeat::testSelection() QCOMPARE(df->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); // try to clear - dd1->setSelection(0); + dd1->setSelection(m_display->nextSerial()); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionClearedSpy.count(), 1); QCOMPARE(selectionSpy.count(), 1); @@ -1594,30 +1594,30 @@ void TestWaylandSeat::testSelection() QCoreApplication::processEvents(); // try to set Selection - dd1->setSelection(0, ds.get()); + dd1->setSelection(m_display->nextSerial(), ds.get()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QCOMPARE(selectionSpy.count(), 1); // let's unset the selection on the seat - m_seatInterface->setSelection(nullptr); + m_seatInterface->setSelection(nullptr, m_display->nextSerial()); // and pass focus back on our surface m_seatInterface->setFocusedKeyboardSurface(serverSurface); // we don't have a selection, so it should not send a selection QVERIFY(sync()); QCOMPARE(selectionSpy.count(), 1); // now let's set it manually - m_seatInterface->setSelection(ddi); + m_seatInterface->setSelection(ddi, m_display->nextSerial()); QCOMPARE(m_seatInterface->selection(), ddi); QVERIFY(selectionSpy.wait()); QCOMPARE(selectionSpy.count(), 2); // setting the same again should not change - m_seatInterface->setSelection(ddi); + m_seatInterface->setSelection(ddi, m_display->nextSerial()); QVERIFY(sync()); QCOMPARE(selectionSpy.count(), 2); // now clear it manually - m_seatInterface->setSelection(nullptr); + m_seatInterface->setSelection(nullptr, m_display->nextSerial()); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionSpy.count(), 2); @@ -1627,10 +1627,10 @@ void TestWaylandSeat::testSelection() std::unique_ptr ds2(ddm->createDataSource()); QVERIFY(ds2->isValid()); ds2->offer(QStringLiteral("text/plain")); - dd2->setSelection(0, ds2.get()); + dd2->setSelection(m_display->nextSerial(), ds2.get()); QVERIFY(selectionSpy.wait()); QSignalSpy cancelledSpy(ds2.get(), &KWayland::Client::DataSource::cancelled); - m_seatInterface->setSelection(ddi); + m_seatInterface->setSelection(ddi, m_display->nextSerial()); QVERIFY(cancelledSpy.wait()); } @@ -1680,7 +1680,7 @@ void TestWaylandSeat::testDataDeviceForKeyboardSurface() QVERIFY(ddiCreatedSpy.wait()); auto ddi = ddiCreatedSpy.first().first().value(); QVERIFY(ddi); - m_seatInterface->setSelection(ddi->selection()); + m_seatInterface->setSelection(ddi->selection(), m_display->nextSerial()); // switch to other client // create a surface and pass it keyboard focus diff --git a/autotests/wayland/server/test_datacontrol_interface.cpp b/autotests/wayland/server/test_datacontrol_interface.cpp index 00a1028060..d9090bde7f 100644 --- a/autotests/wayland/server/test_datacontrol_interface.cpp +++ b/autotests/wayland/server/test_datacontrol_interface.cpp @@ -257,7 +257,7 @@ void DataControlInterfaceTest::testCopyToControl() QSignalSpy selectionSpy(dataControlDevice.get(), &DataControlDevice::selection); std::unique_ptr testSelection(new TestDataSource); - m_seat->setSelection(testSelection.get()); + m_seat->setSelection(testSelection.get(), m_display->nextSerial()); // selection will be sent after we've been sent a new offer object and the mimes have been sent to that object selectionSpy.wait(); @@ -283,7 +283,7 @@ void DataControlInterfaceTest::testCopyToControlPrimarySelection() QSignalSpy selectionSpy(dataControlDevice.get(), &DataControlDevice::primary_selection); std::unique_ptr testSelection(new TestDataSource); - m_seat->setPrimarySelection(testSelection.get()); + m_seat->setPrimarySelection(testSelection.get(), m_display->nextSerial()); // selection will be sent after we've been sent a new offer object and the mimes have been sent to that object selectionSpy.wait(); @@ -353,7 +353,7 @@ void DataControlInterfaceTest::testKlipperCase() // Client A has a data source std::unique_ptr testSelection(new TestDataSource); - m_seat->setSelection(testSelection.get()); + m_seat->setSelection(testSelection.get(), m_display->nextSerial()); // klipper gets it selectionSpy.wait(); @@ -366,7 +366,7 @@ void DataControlInterfaceTest::testKlipperCase() // Client A sets something else std::unique_ptr testSelection2(new TestDataSource); - m_seat->setSelection(testSelection2.get()); + m_seat->setSelection(testSelection2.get(), m_display->nextSerial()); // Meanwhile klipper updates with the old content std::unique_ptr source(new DataControlSource); diff --git a/src/wayland/datadevice.cpp b/src/wayland/datadevice.cpp index 0edff59e09..b7102fa315 100644 --- a/src/wayland/datadevice.cpp +++ b/src/wayland/datadevice.cpp @@ -140,7 +140,7 @@ void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, w selection->cancel(); } selection = dataSource; - Q_EMIT q->selectionChanged(selection); + Q_EMIT q->selectionChanged(selection, serial); } void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource) diff --git a/src/wayland/datadevice.h b/src/wayland/datadevice.h index 687c471700..8c6ffa1d7f 100644 --- a/src/wayland/datadevice.h +++ b/src/wayland/datadevice.h @@ -109,7 +109,7 @@ public: Q_SIGNALS: void aboutToBeDestroyed(); void dragStarted(AbstractDataSource *source, SurfaceInterface *originSurface, quint32 serial, DragAndDropIcon *dragIcon); - void selectionChanged(KWin::DataSourceInterface *); + void selectionChanged(KWin::DataSourceInterface *, quint32 serial); private: friend class DataDeviceManagerInterfacePrivate; diff --git a/src/wayland/primaryselectiondevice_v1.cpp b/src/wayland/primaryselectiondevice_v1.cpp index 2f0940c496..9e51c12782 100644 --- a/src/wayland/primaryselectiondevice_v1.cpp +++ b/src/wayland/primaryselectiondevice_v1.cpp @@ -62,7 +62,7 @@ void PrimarySelectionDeviceV1InterfacePrivate::zwp_primary_selection_device_v1_s } selection = dataSource; if (selection) { - Q_EMIT q->selectionChanged(selection); + Q_EMIT q->selectionChanged(selection, serial); } } diff --git a/src/wayland/primaryselectiondevice_v1.h b/src/wayland/primaryselectiondevice_v1.h index 5c18fde0a3..b5e5dbb83c 100644 --- a/src/wayland/primaryselectiondevice_v1.h +++ b/src/wayland/primaryselectiondevice_v1.h @@ -45,7 +45,7 @@ public: wl_client *client() const; Q_SIGNALS: - void selectionChanged(KWin::PrimarySelectionSourceV1Interface *); + void selectionChanged(KWin::PrimarySelectionSourceV1Interface *, quint32 serial); private: friend class PrimarySelectionDeviceManagerV1InterfacePrivate; diff --git a/src/wayland/seat.cpp b/src/wayland/seat.cpp index 3b1728d985..d4780d2140 100644 --- a/src/wayland/seat.cpp +++ b/src/wayland/seat.cpp @@ -150,8 +150,8 @@ void SeatInterfacePrivate::registerDataDevice(DataDeviceInterface *dataDevice) globalKeyboard.focus.selections.removeOne(dataDevice); }; QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup); - QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q, [this, dataDevice] { - updateSelection(dataDevice); + QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q, [this](DataSourceInterface *source, quint32 serial) { + updateSelection(source, serial); }); QObject::connect(dataDevice, &DataDeviceInterface::dragStarted, @@ -208,7 +208,7 @@ void SeatInterfacePrivate::registerDataControlDevice(DataControlDeviceV1Interfac dataDevice->sendSelection(currentSelection); return; } - q->setSelection(dataDevice->selection()); + q->setSelection(dataDevice->selection(), display->nextSerial()); }); QObject::connect(dataDevice, &DataControlDeviceV1Interface::primarySelectionChanged, q, [this, dataDevice] { @@ -222,7 +222,7 @@ void SeatInterfacePrivate::registerDataControlDevice(DataControlDeviceV1Interfac dataDevice->sendPrimarySelection(currentPrimarySelection); return; } - q->setPrimarySelection(dataDevice->primarySelection()); + q->setPrimarySelection(dataDevice->primarySelection(), display->nextSerial()); }); dataDevice->sendSelection(currentSelection); @@ -239,8 +239,8 @@ void SeatInterfacePrivate::registerPrimarySelectionDevice(PrimarySelectionDevice globalKeyboard.focus.primarySelections.removeOne(primarySelectionDevice); }; QObject::connect(primarySelectionDevice, &QObject::destroyed, q, dataDeviceCleanup); - QObject::connect(primarySelectionDevice, &PrimarySelectionDeviceV1Interface::selectionChanged, q, [this, primarySelectionDevice] { - updatePrimarySelection(primarySelectionDevice); + QObject::connect(primarySelectionDevice, &PrimarySelectionDeviceV1Interface::selectionChanged, q, [this](PrimarySelectionSourceV1Interface *source, quint32 serial) { + updatePrimarySelection(source, serial); }); // is the new DataDevice for the current keyoard focus? if (globalKeyboard.focus.surface) { @@ -306,30 +306,26 @@ void SeatInterfacePrivate::endDrag() Q_EMIT q->dragEnded(); } -void SeatInterfacePrivate::updateSelection(DataDeviceInterface *dataDevice) +void SeatInterfacePrivate::updateSelection(DataSourceInterface *dataSource, quint32 serial) { - DataSourceInterface *selection = dataDevice->selection(); - // if the update is from the focussed window we should inform the active client - if (!(globalKeyboard.focus.surface && (*globalKeyboard.focus.surface->client() == dataDevice->client()))) { - if (selection) { - selection->cancel(); + if (currentSelectionSerial - serial < UINT32_MAX / 2) { + if (dataSource) { + dataSource->cancel(); } return; } - q->setSelection(selection); + q->setSelection(dataSource, serial); } -void SeatInterfacePrivate::updatePrimarySelection(PrimarySelectionDeviceV1Interface *primarySelectionDevice) +void SeatInterfacePrivate::updatePrimarySelection(PrimarySelectionSourceV1Interface *dataSource, quint32 serial) { - PrimarySelectionSourceV1Interface *selection = primarySelectionDevice->selection(); - // if the update is from the focussed window we should inform the active client - if (!(globalKeyboard.focus.surface && (*globalKeyboard.focus.surface->client() == primarySelectionDevice->client()))) { - if (selection) { - selection->cancel(); + if (currentPrimarySelectionSerial - serial < UINT32_MAX / 2) { + if (dataSource) { + dataSource->cancel(); } return; } - q->setPrimarySelection(selection); + q->setPrimarySelection(dataSource, serial); } void SeatInterfacePrivate::sendCapabilities() @@ -1286,7 +1282,7 @@ AbstractDataSource *SeatInterface::selection() const return d->currentSelection; } -void SeatInterface::setSelection(AbstractDataSource *selection) +void SeatInterface::setSelection(AbstractDataSource *selection, quint32 serial) { if (d->currentSelection == selection) { return; @@ -1298,13 +1294,14 @@ void SeatInterface::setSelection(AbstractDataSource *selection) } if (selection) { - auto cleanup = [this]() { - setSelection(nullptr); + auto cleanup = [this, serial]() { + setSelection(nullptr, serial); }; connect(selection, &AbstractDataSource::aboutToBeDestroyed, this, cleanup); } d->currentSelection = selection; + d->currentSelectionSerial = serial; for (auto focussedSelection : std::as_const(d->globalKeyboard.focus.selections)) { focussedSelection->sendSelection(selection); @@ -1322,7 +1319,7 @@ AbstractDataSource *SeatInterface::primarySelection() const return d->currentPrimarySelection; } -void SeatInterface::setPrimarySelection(AbstractDataSource *selection) +void SeatInterface::setPrimarySelection(AbstractDataSource *selection, quint32 serial) { if (d->currentPrimarySelection == selection) { return; @@ -1333,13 +1330,14 @@ void SeatInterface::setPrimarySelection(AbstractDataSource *selection) } if (selection) { - auto cleanup = [this]() { - setPrimarySelection(nullptr); + auto cleanup = [this, serial]() { + setPrimarySelection(nullptr, serial); }; connect(selection, &AbstractDataSource::aboutToBeDestroyed, this, cleanup); } d->currentPrimarySelection = selection; + d->currentPrimarySelectionSerial = serial; for (auto focussedSelection : std::as_const(d->globalKeyboard.focus.primarySelections)) { focussedSelection->sendSelection(selection); diff --git a/src/wayland/seat.h b/src/wayland/seat.h index c04eaae9ad..650149aab1 100644 --- a/src/wayland/seat.h +++ b/src/wayland/seat.h @@ -645,10 +645,10 @@ public: * @see selection * @see selectionChanged */ - void setSelection(AbstractDataSource *selection); + void setSelection(AbstractDataSource *selection, quint32 serial); AbstractDataSource *primarySelection() const; - void setPrimarySelection(AbstractDataSource *selection); + void setPrimarySelection(AbstractDataSource *selection, quint32 serial); void startDrag(AbstractDataSource *source, SurfaceInterface *sourceSurface, int dragSerial = -1, DragAndDropIcon *dragIcon = nullptr); diff --git a/src/wayland/seat_p.h b/src/wayland/seat_p.h index 55f5f327c6..bdfea7bb05 100644 --- a/src/wayland/seat_p.h +++ b/src/wayland/seat_p.h @@ -28,6 +28,7 @@ class TextInputV1Interface; class TextInputV2Interface; class TextInputV3Interface; class PrimarySelectionDeviceV1Interface; +class PrimarySelectionSourceV1Interface; class DragAndDropIcon; class SeatInterfacePrivate : public QtWaylandServer::wl_seat @@ -68,7 +69,9 @@ public: // the last thing copied into the clipboard content AbstractDataSource *currentSelection = nullptr; + quint32 currentSelectionSerial = 0; AbstractDataSource *currentPrimarySelection = nullptr; + quint32 currentPrimarySelectionSerial = 0; // Pointer related members struct Pointer @@ -151,8 +154,8 @@ protected: void seat_release(Resource *resource) override; private: - void updateSelection(DataDeviceInterface *dataDevice); - void updatePrimarySelection(PrimarySelectionDeviceV1Interface *primarySelectionDevice); + void updateSelection(DataSourceInterface *dataSource, quint32 serial); + void updatePrimarySelection(PrimarySelectionSourceV1Interface *dataSource, quint32); }; } // namespace KWin diff --git a/src/xwayland/clipboard.cpp b/src/xwayland/clipboard.cpp index f654a2df82..919fb7625b 100644 --- a/src/xwayland/clipboard.cpp +++ b/src/xwayland/clipboard.cpp @@ -11,6 +11,7 @@ #include "datasource.h" #include "selection_source.h" +#include "wayland/display.h" #include "wayland/seat.h" #include "wayland_server.h" #include "workspace.h" @@ -160,11 +161,11 @@ void Clipboard::x11OffersChanged(const QStringList &added, const QStringList &re connect(newSelection.get(), &XwlDataSource::dataRequested, source, &X11Source::startTransfer); // we keep the old selection around because setSelection needs it to be still alive std::swap(m_selectionSource, newSelection); - waylandServer()->seat()->setSelection(m_selectionSource.get()); + waylandServer()->seat()->setSelection(m_selectionSource.get(), waylandServer()->display()->nextSerial()); } else { AbstractDataSource *currentSelection = waylandServer()->seat()->selection(); if (!ownsSelection(currentSelection)) { - waylandServer()->seat()->setSelection(nullptr); + waylandServer()->seat()->setSelection(nullptr, waylandServer()->display()->nextSerial()); m_selectionSource.reset(); } } diff --git a/src/xwayland/primary.cpp b/src/xwayland/primary.cpp index ab73c24258..415799a3cb 100644 --- a/src/xwayland/primary.cpp +++ b/src/xwayland/primary.cpp @@ -12,6 +12,7 @@ #include "datasource.h" #include "selection_source.h" +#include "wayland/display.h" #include "wayland/seat.h" #include "wayland_server.h" #include "workspace.h" @@ -171,11 +172,11 @@ void Primary::x11OffersChanged(const QStringList &added, const QStringList &remo connect(newSelection.get(), &XwlDataSource::dataRequested, source, &X11Source::startTransfer); // we keep the old selection around because setPrimarySelection needs it to be still alive std::swap(m_primarySelectionSource, newSelection); - waylandServer()->seat()->setPrimarySelection(m_primarySelectionSource.get()); + waylandServer()->seat()->setPrimarySelection(m_primarySelectionSource.get(), waylandServer()->display()->nextSerial()); } else { AbstractDataSource *currentSelection = waylandServer()->seat()->primarySelection(); if (!ownsSelection(currentSelection)) { - waylandServer()->seat()->setPrimarySelection(nullptr); + waylandServer()->seat()->setPrimarySelection(nullptr, waylandServer()->display()->nextSerial()); m_primarySelectionSource.reset(); } }