Fix deadlocks in wayland clipboard that can occur in special scenarios.

setMimeData() emits the changed signal always so to prevent duplicated
signals keyboardFocus() must only emit when the change came from
another wayland client. However direct connection may cause issues
when invoking the slot from a wayland callback, so use a metacall
to make sure we return from the callback. Unnecessary data transfer
and potential deadlock is now also avoided when a client is requesting
the mime data from itself.

Reviewed-by: Jørgen Lind
This commit is contained in:
Laszlo Agocs 2011-05-12 16:20:03 +02:00
parent dd1a7a6379
commit 542ba35f2f
2 changed files with 24 additions and 10 deletions

View File

@ -96,7 +96,6 @@ public:
QWaylandSelection(QWaylandDisplay *display, QMimeData *data);
~QWaylandSelection();
private:
static uint32_t getTime();
static void send(void *data, struct wl_selection *selection, const char *mime_type, int fd);
static void cancelled(void *data, struct wl_selection *selection);
@ -164,7 +163,7 @@ void QWaylandSelection::cancelled(void *data, struct wl_selection *selection)
}
QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display)
: mDisplay(display), mSelection(0), mMimeDataIn(0), mOffer(0)
: mDisplay(display), mMimeDataIn(0), mOffer(0)
{
clipboard = this;
}
@ -222,6 +221,8 @@ QVariant QWaylandClipboard::retrieveData(const QString &mimeType, QVariant::Type
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
{
Q_ASSERT(mode == QClipboard::Clipboard);
if (!mSelections.isEmpty())
return mSelections.last()->mMimeData;
if (!mMimeDataIn)
mMimeDataIn = new QWaylandMimeData;
mMimeDataIn->clearAll();
@ -236,7 +237,7 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
if (!mDisplay->inputDevices().isEmpty()) {
if (!data)
data = new QMimeData;
mSelection = new QWaylandSelection(mDisplay, data);
mSelections.append(new QWaylandSelection(mDisplay, data));
} else {
qWarning("QWaylandClipboard::setMimeData: No input devices");
}
@ -266,21 +267,27 @@ void QWaylandClipboard::offer(void *data,
struct wl_selection_offer *selection_offer,
const char *type)
{
Q_UNUSED(data);
Q_UNUSED(selection_offer);
QWaylandClipboard *self = static_cast<QWaylandClipboard *>(data);
self->mOfferedMimeTypes.append(QString::fromLatin1(type));
clipboard->mOfferedMimeTypes.append(QString::fromLatin1(type));
}
void QWaylandClipboard::keyboardFocus(void *data,
struct wl_selection_offer *selection_offer,
wl_input_device *input_device)
{
QWaylandClipboard *self = static_cast<QWaylandClipboard *>(data);
Q_UNUSED(data);
if (!input_device) {
wl_selection_offer_destroy(selection_offer);
self->mOffer = 0;
clipboard->mOffer = 0;
return;
}
self->mOffer = selection_offer;
self->emitChanged(QClipboard::Clipboard);
clipboard->mOffer = selection_offer;
if (clipboard->mSelections.isEmpty())
QMetaObject::invokeMethod(&clipboard->mEmitter, "emitChanged", Qt::QueuedConnection);
}
void QWaylandClipboardSignalEmitter::emitChanged()
{
clipboard->emitChanged(QClipboard::Clipboard);
}

View File

@ -51,6 +51,13 @@ class QWaylandSelection;
class QWaylandMimeData;
struct wl_selection_offer;
class QWaylandClipboardSignalEmitter : public QObject
{
Q_OBJECT
public slots:
void emitChanged();
};
class QWaylandClipboard : public QPlatformClipboard
{
public:
@ -80,11 +87,11 @@ private:
static void forceRoundtrip(struct wl_display *display);
QWaylandDisplay *mDisplay;
QWaylandSelection *mSelection;
QWaylandMimeData *mMimeDataIn;
QList<QWaylandSelection *> mSelections;
QStringList mOfferedMimeTypes;
struct wl_selection_offer *mOffer;
QWaylandClipboardSignalEmitter mEmitter;
};
#endif // QWAYLANDCLIPBOARD_H