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:
parent
dd1a7a6379
commit
542ba35f2f
@ -96,7 +96,6 @@ public:
|
|||||||
QWaylandSelection(QWaylandDisplay *display, QMimeData *data);
|
QWaylandSelection(QWaylandDisplay *display, QMimeData *data);
|
||||||
~QWaylandSelection();
|
~QWaylandSelection();
|
||||||
|
|
||||||
private:
|
|
||||||
static uint32_t getTime();
|
static uint32_t getTime();
|
||||||
static void send(void *data, struct wl_selection *selection, const char *mime_type, int fd);
|
static void send(void *data, struct wl_selection *selection, const char *mime_type, int fd);
|
||||||
static void cancelled(void *data, struct wl_selection *selection);
|
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)
|
QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display)
|
||||||
: mDisplay(display), mSelection(0), mMimeDataIn(0), mOffer(0)
|
: mDisplay(display), mMimeDataIn(0), mOffer(0)
|
||||||
{
|
{
|
||||||
clipboard = this;
|
clipboard = this;
|
||||||
}
|
}
|
||||||
@ -222,6 +221,8 @@ QVariant QWaylandClipboard::retrieveData(const QString &mimeType, QVariant::Type
|
|||||||
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
|
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
|
||||||
{
|
{
|
||||||
Q_ASSERT(mode == QClipboard::Clipboard);
|
Q_ASSERT(mode == QClipboard::Clipboard);
|
||||||
|
if (!mSelections.isEmpty())
|
||||||
|
return mSelections.last()->mMimeData;
|
||||||
if (!mMimeDataIn)
|
if (!mMimeDataIn)
|
||||||
mMimeDataIn = new QWaylandMimeData;
|
mMimeDataIn = new QWaylandMimeData;
|
||||||
mMimeDataIn->clearAll();
|
mMimeDataIn->clearAll();
|
||||||
@ -236,7 +237,7 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
|
|||||||
if (!mDisplay->inputDevices().isEmpty()) {
|
if (!mDisplay->inputDevices().isEmpty()) {
|
||||||
if (!data)
|
if (!data)
|
||||||
data = new QMimeData;
|
data = new QMimeData;
|
||||||
mSelection = new QWaylandSelection(mDisplay, data);
|
mSelections.append(new QWaylandSelection(mDisplay, data));
|
||||||
} else {
|
} else {
|
||||||
qWarning("QWaylandClipboard::setMimeData: No input devices");
|
qWarning("QWaylandClipboard::setMimeData: No input devices");
|
||||||
}
|
}
|
||||||
@ -266,21 +267,27 @@ void QWaylandClipboard::offer(void *data,
|
|||||||
struct wl_selection_offer *selection_offer,
|
struct wl_selection_offer *selection_offer,
|
||||||
const char *type)
|
const char *type)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(data);
|
||||||
Q_UNUSED(selection_offer);
|
Q_UNUSED(selection_offer);
|
||||||
QWaylandClipboard *self = static_cast<QWaylandClipboard *>(data);
|
clipboard->mOfferedMimeTypes.append(QString::fromLatin1(type));
|
||||||
self->mOfferedMimeTypes.append(QString::fromLatin1(type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandClipboard::keyboardFocus(void *data,
|
void QWaylandClipboard::keyboardFocus(void *data,
|
||||||
struct wl_selection_offer *selection_offer,
|
struct wl_selection_offer *selection_offer,
|
||||||
wl_input_device *input_device)
|
wl_input_device *input_device)
|
||||||
{
|
{
|
||||||
QWaylandClipboard *self = static_cast<QWaylandClipboard *>(data);
|
Q_UNUSED(data);
|
||||||
if (!input_device) {
|
if (!input_device) {
|
||||||
wl_selection_offer_destroy(selection_offer);
|
wl_selection_offer_destroy(selection_offer);
|
||||||
self->mOffer = 0;
|
clipboard->mOffer = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self->mOffer = selection_offer;
|
clipboard->mOffer = selection_offer;
|
||||||
self->emitChanged(QClipboard::Clipboard);
|
if (clipboard->mSelections.isEmpty())
|
||||||
|
QMetaObject::invokeMethod(&clipboard->mEmitter, "emitChanged", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandClipboardSignalEmitter::emitChanged()
|
||||||
|
{
|
||||||
|
clipboard->emitChanged(QClipboard::Clipboard);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,13 @@ class QWaylandSelection;
|
|||||||
class QWaylandMimeData;
|
class QWaylandMimeData;
|
||||||
struct wl_selection_offer;
|
struct wl_selection_offer;
|
||||||
|
|
||||||
|
class QWaylandClipboardSignalEmitter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public slots:
|
||||||
|
void emitChanged();
|
||||||
|
};
|
||||||
|
|
||||||
class QWaylandClipboard : public QPlatformClipboard
|
class QWaylandClipboard : public QPlatformClipboard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -80,11 +87,11 @@ private:
|
|||||||
static void forceRoundtrip(struct wl_display *display);
|
static void forceRoundtrip(struct wl_display *display);
|
||||||
|
|
||||||
QWaylandDisplay *mDisplay;
|
QWaylandDisplay *mDisplay;
|
||||||
QWaylandSelection *mSelection;
|
|
||||||
QWaylandMimeData *mMimeDataIn;
|
QWaylandMimeData *mMimeDataIn;
|
||||||
QList<QWaylandSelection *> mSelections;
|
QList<QWaylandSelection *> mSelections;
|
||||||
QStringList mOfferedMimeTypes;
|
QStringList mOfferedMimeTypes;
|
||||||
struct wl_selection_offer *mOffer;
|
struct wl_selection_offer *mOffer;
|
||||||
|
QWaylandClipboardSignalEmitter mEmitter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QWAYLANDCLIPBOARD_H
|
#endif // QWAYLANDCLIPBOARD_H
|
||||||
|
Loading…
Reference in New Issue
Block a user