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 cleanup();
|
||||||
void testCopyToControl();
|
void testCopyToControl();
|
||||||
void testCopyFromControl();
|
void testCopyFromControl();
|
||||||
|
void testKlipperCase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KWayland::Client::ConnectionThread *m_connection;
|
KWayland::Client::ConnectionThread *m_connection;
|
||||||
|
@ -302,6 +303,46 @@ void DataControlInterfaceTest::testCopyFromControl()
|
||||||
QVERIFY(m_seat->selection());
|
QVERIFY(m_seat->selection());
|
||||||
QCOMPARE(m_seat->selection()->mimeTypes(), QStringList({"cheese/test1", "cheese/test2"}));
|
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, &QObject::destroyed, q, dataDeviceCleanup);
|
||||||
|
|
||||||
QObject::connect(dataDevice, &DataControlDeviceV1Interface::selectionChanged, q,
|
QObject::connect(dataDevice, &DataControlDeviceV1Interface::selectionChanged, q,
|
||||||
[this, dataDevice] {
|
[this, dataDevice] {
|
||||||
q->setSelection(dataDevice->selection());
|
// 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,
|
QObject::connect(dataDevice, &DataControlDeviceV1Interface::selectionCleared, q,
|
||||||
|
|
Loading…
Reference in a new issue