Commit edbde063 authored by David Edmundson's avatar David Edmundson Committed by David Edmundson
Browse files

Add a workaround to prevent klipper racing with clipboard updates

We have a situation where some clients drop their old offer before
creating a new one. This means klipper tries to fill in the empty
clipboard at the same time the client posts its new real contents.

This adds in a flag (via a hidden mimetype) that klipper is trying to
replace a null clipboard. If this flag is set and our clipboard is not
null because the client has updated it in the meantime we ignore the
klipper update.

It's a workaround, rather than an ideal fix at a data level, but it
solves the problem in the interim.

CCBUG: 424855
parent e8a43b6b
......@@ -139,6 +139,7 @@ private Q_SLOTS:
void cleanup();
void testCopyToControl();
void testCopyFromControl();
void testKlipperCase();
private:
KWayland::Client::ConnectionThread *m_connection;
......@@ -302,6 +303,46 @@ void DataControlInterfaceTest::testCopyFromControl()
QVERIFY(m_seat->selection());
QCOMPARE(m_seat->selection()->mimeTypes(), QStringList({"cheese/test1", "cheese/test2"}));
}
void DataControlInterfaceTest::testKlipperCase()
{
// This tests the setup of klipper's real world operation and a race with a common pattern seen between clients and klipper
// The client's behaviour is faked with direct access to the seat
QScopedPointer<DataControlDevice> dataControlDevice(new DataControlDevice);
dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat));
QSignalSpy newOfferSpy(dataControlDevice.data(), &DataControlDevice::dataControlOffer);
QSignalSpy selectionSpy(dataControlDevice.data(), &DataControlDevice::selection);
QSignalSpy serverSelectionChangedSpy(m_seat, &SeatInterface::selectionChanged);
// Client A has a data source
QScopedPointer<TestDataSource> testSelection(new TestDataSource);
m_seat->setSelection(testSelection.data());
// klipper gets it
selectionSpy.wait();
// Client A deletes it
testSelection.reset();
//klipper gets told
selectionSpy.wait();
// Client A sets something else
QScopedPointer<TestDataSource> testSelection2(new TestDataSource);
m_seat->setSelection(testSelection2.data());
// Meanwhile klipper updates with the old content
QScopedPointer<DataControlSource> source(new DataControlSource);
source->init(m_dataControlDeviceManager->create_data_source());
source->offer("fromKlipper/test1");
source->offer("application/x-kde-onlyReplaceEmpty");
dataControlDevice->set_selection(source->object());
QVERIFY(!serverSelectionChangedSpy.wait(10));
QCOMPARE(m_seat->selection(), testSelection2.data());
}
......
......@@ -345,9 +345,17 @@ void SeatInterface::Private::registerDataControlDevice(DataControlDeviceV1Interf
QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup);
QObject::connect(dataDevice, &DataControlDeviceV1Interface::selectionChanged, q,
[this, dataDevice] {
q->setSelection(dataDevice->selection());
[this, dataDevice] {
// Special klipper workaround to avoid a race
// If the mimetype x-kde-onlyReplaceEmpty is set, and we've had another update in the meantime, do nothing
// See https://github.com/swaywm/wlr-protocols/issues/92
if (dataDevice->selection() && dataDevice->selection()->mimeTypes().contains(QLatin1String("application/x-kde-onlyReplaceEmpty")) &&
currentSelection) {
dataDevice->selection()->cancel();
return;
}
q->setSelection(dataDevice->selection());
}
);
QObject::connect(dataDevice, &DataControlDeviceV1Interface::selectionCleared, q,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment