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
This commit is contained in:
parent
89433eccec
commit
84337d25f9
2 changed files with 51 additions and 2 deletions
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue