From 735902e897235b61166d9b9dc14c7737298bdb2e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 16 May 2011 10:03:21 +0200 Subject: [PATCH] Add an optional retained selection mode to the compositor. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Enabling this feature will cause the server to retrieve all data for all mime types as soon as a selection is activated. This data is then used as a backup, to keep the selection and the offer working even after the app holding the current selection exits. Reviewed-by: Samuel Rødal --- examples/qwidget-compositor/main.cpp | 1 + .../compositor_api/waylandcompositor.cpp | 18 +++ .../compositor_api/waylandcompositor.h | 7 ++ src/qt-compositor/wayland_wrapper/wlselection.cpp | 128 ++++++++++++++++++--- src/qt-compositor/wayland_wrapper/wlselection.h | 21 ++++ 5 files changed, 159 insertions(+), 16 deletions(-) diff --git a/examples/qwidget-compositor/main.cpp b/examples/qwidget-compositor/main.cpp index b9d5d8f..861139b 100644 --- a/examples/qwidget-compositor/main.cpp +++ b/examples/qwidget-compositor/main.cpp @@ -65,6 +65,7 @@ class QWidgetCompositor : public QWidget, public WaylandCompositor public: QWidgetCompositor() : WaylandCompositor(this), m_dragSurface(0) { setMouseTracking(true); + setRetainedSelectionEnabled(true); m_background = QImage(QLatin1String("background.jpg")); //make sure we get the window id and create the glcontext //so that clients can successfully initialize egl diff --git a/src/qt-compositor/compositor_api/waylandcompositor.cpp b/src/qt-compositor/compositor_api/waylandcompositor.cpp index cc70809..e992c18 100644 --- a/src/qt-compositor/compositor_api/waylandcompositor.cpp +++ b/src/qt-compositor/compositor_api/waylandcompositor.cpp @@ -42,6 +42,7 @@ #include "wayland_wrapper/wlcompositor.h" #include "wayland_wrapper/wlsurface.h" +#include "wayland_wrapper/wlselection.h" #ifdef QT_COMPOSITOR_DECLARATIVE #include "waylandsurfaceitem.h" @@ -101,3 +102,20 @@ Wayland::Compositor * WaylandCompositor::handle() const return m_compositor; } +void WaylandCompositor::setRetainedSelectionEnabled(bool enable) +{ + Wayland::Selection *sel = Wayland::Selection::instance(); + sel->setRetainedSelection(enable); + sel->setRetainedSelectionWatcher(retainedSelectionChanged, this); +} + +void WaylandCompositor::retainedSelectionChanged(QMimeData *mimeData, void *param) +{ + WaylandCompositor *self = static_cast(param); + self->retainedSelectionReceived(mimeData); +} + +void WaylandCompositor::retainedSelectionReceived(QMimeData *) +{ +} + diff --git a/src/qt-compositor/compositor_api/waylandcompositor.h b/src/qt-compositor/compositor_api/waylandcompositor.h index 805bb33..058f1fe 100644 --- a/src/qt-compositor/compositor_api/waylandcompositor.h +++ b/src/qt-compositor/compositor_api/waylandcompositor.h @@ -50,6 +50,7 @@ #endif class QWidget; +class QMimeData; class WaylandSurface; namespace Wayland @@ -76,7 +77,13 @@ public: virtual void surfaceCreated(WaylandSurface *surface) = 0; Wayland::Compositor *handle() const; + + void setRetainedSelectionEnabled(bool enable); + virtual void retainedSelectionReceived(QMimeData *mimeData); + private: + static void retainedSelectionChanged(QMimeData *mimeData, void *param); + Wayland::Compositor *m_compositor; QWidget *m_toplevel_widget; diff --git a/src/qt-compositor/wayland_wrapper/wlselection.cpp b/src/qt-compositor/wayland_wrapper/wlselection.cpp index c3e5f2b..2a54562 100644 --- a/src/qt-compositor/wayland_wrapper/wlselection.cpp +++ b/src/qt-compositor/wayland_wrapper/wlselection.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include namespace Wayland { @@ -51,10 +53,20 @@ void Selection::send(struct wl_client *client, const char *mime_type, int fd) { Q_UNUSED(client); - struct wl_selection *selection = container_of(offer, struct wl_selection, selection_offer); - wl_client_post_event(selection->client, - &selection->resource.object, - WL_SELECTION_SEND, mime_type, fd); + Selection *self = instance(); + if (self->m_retainedSelection) { + QByteArray data = self->m_retainedData.data(QString::fromLatin1(mime_type)); + if (!data.isEmpty()) { + QFile f; + if (f.open(fd, QIODevice::WriteOnly)) + f.write(data); + } + } else { + struct wl_selection *selection = container_of(offer, struct wl_selection, selection_offer); + wl_client_post_event(selection->client, + &selection->resource.object, + WL_SELECTION_SEND, mime_type, fd); + } close(fd); } @@ -117,6 +129,60 @@ void Selection::selActivate(struct wl_client *client, wl_client_post_event(client, &selection->selection_offer.object, WL_SELECTION_OFFER_KEYBOARD_FOCUS, selection->input_device); } + + if (self->m_retainedSelectionEnabled) { + self->m_retainedData.clear(); + self->m_retainedReadIndex = 0; + self->retain(); + } +} + +void Selection::retain() +{ + finishReadFromClient(); + if (m_retainedReadIndex >= m_offerList.count()) { + if (m_watchFunc) + m_watchFunc(&m_retainedData, m_watchFuncParam); + return; + } + QString mimeType = m_offerList.at(m_retainedReadIndex); + m_retainedReadBuf.clear(); + QByteArray mimeTypeBa = mimeType.toLatin1(); + int fd[2]; + if (pipe(fd) == -1) { + qWarning("Clipboard: Failed to create pipe"); + return; + } + wl_client_post_event(m_currentSelection->client, &m_currentSelection->resource.object, + WL_SELECTION_SEND, mimeTypeBa.constData(), fd[1]); + close(fd[1]); + m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this); + connect(m_retainedReadNotifier, SIGNAL(activated(int)), SLOT(readFromClient(int))); +} + +void Selection::finishReadFromClient() +{ + if (m_retainedReadNotifier) { + int fd = m_retainedReadNotifier->socket(); + delete m_retainedReadNotifier; + m_retainedReadNotifier = 0; + close(fd); + } +} + +void Selection::readFromClient(int fd) +{ + char buf[256]; + int n = read(fd, buf, sizeof buf); + if (n <= 0) { + finishReadFromClient(); + QString mimeType = m_offerList.at(m_retainedReadIndex); + m_retainedData.setData(mimeType, m_retainedReadBuf); + ++m_retainedReadIndex; + retain(); + } else { + m_retainedReadBuf.append(buf, n); + } } void Selection::selDestroy(struct wl_client *client, struct wl_selection *selection) @@ -138,21 +204,26 @@ void Selection::destroySelection(struct wl_resource *resource, struct wl_client if (self->m_currentSelection == selection) self->m_currentSelection = 0; if (self->m_currentOffer == &selection->selection_offer) { - QList clients = Compositor::instance()->clients(); - foreach (struct wl_client *client, clients) { - wl_client_post_event(client, &selection->selection_offer.object, - WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0); - } self->m_currentOffer = 0; + if (self->m_retainedSelectionEnabled) { + delete self->m_retainedSelection; + self->m_retainedSelection = selection; + return; + } self->m_offerList.clear(); + foreach (struct wl_client *client, Compositor::instance()->clients()) + wl_client_post_event(client, &selection->selection_offer.object, + WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0); } delete selection; } void Selection::create(struct wl_client *client, uint32_t id) { - struct wl_selection *selection = new struct wl_selection; + delete m_retainedSelection; + m_retainedSelection = 0; m_offerList.clear(); + struct wl_selection *selection = new struct wl_selection; memset(selection, 0, sizeof *selection); selection->resource.object.id = id; selection->resource.object.interface = &wl_selection_interface; @@ -163,17 +234,34 @@ void Selection::create(struct wl_client *client, uint32_t id) wl_client_add_resource(client, &selection->resource); } +void Selection::setRetainedSelection(bool enable) +{ + m_retainedSelectionEnabled = enable; +} + +void Selection::setRetainedSelectionWatcher(Watcher func, void *param) +{ + m_watchFunc = func; + m_watchFuncParam = param; +} + void Selection::onClientAdded(wl_client *client) { - if (m_currentSelection && m_currentOffer) { - wl_client_post_global(client, &m_currentOffer->object); + struct wl_selection *selection = m_currentSelection; + struct wl_selection_offer *offer = m_currentOffer; + if (m_retainedSelection) { + selection = m_retainedSelection; + offer = &m_retainedSelection->selection_offer; + } + if (selection && offer) { + wl_client_post_global(client, &offer->object); foreach (const QString &mimeType, m_offerList) { QByteArray mimeTypeBa = mimeType.toLatin1(); - wl_client_post_event(client, &m_currentOffer->object, + wl_client_post_event(client, &offer->object, WL_SELECTION_OFFER_OFFER, mimeTypeBa.constData()); } - wl_client_post_event(client, &m_currentOffer->object, - WL_SELECTION_OFFER_KEYBOARD_FOCUS, m_currentSelection->input_device); + wl_client_post_event(client, &offer->object, + WL_SELECTION_OFFER_KEYBOARD_FOCUS, selection->input_device); } } @@ -185,9 +273,17 @@ Selection *Selection::instance() } Selection::Selection() - : m_currentSelection(0), m_currentOffer(0) + : m_currentSelection(0), m_currentOffer(0), + m_retainedReadNotifier(0), m_retainedSelection(0), + m_retainedSelectionEnabled(false), + m_watchFunc(0), m_watchFuncParam(0) { connect(Compositor::instance(), SIGNAL(clientAdded(wl_client*)), SLOT(onClientAdded(wl_client*))); } +Selection::~Selection() +{ + finishReadFromClient(); +} + } diff --git a/src/qt-compositor/wayland_wrapper/wlselection.h b/src/qt-compositor/wayland_wrapper/wlselection.h index 761444c..0346842 100644 --- a/src/qt-compositor/wayland_wrapper/wlselection.h +++ b/src/qt-compositor/wayland_wrapper/wlselection.h @@ -43,8 +43,13 @@ #include #include +#include #include +QT_BEGIN_NAMESPACE +class QSocketNotifier; +QT_END_NAMESPACE + namespace Wayland { class Selection : public QObject @@ -54,10 +59,15 @@ class Selection : public QObject public: static Selection *instance(); Selection(); + ~Selection(); void create(struct wl_client *client, uint32_t id); + void setRetainedSelection(bool enable); + typedef void (*Watcher)(QMimeData*, void*); + void setRetainedSelectionWatcher(Watcher func, void *param); private slots: void onClientAdded(wl_client *client); + void readFromClient(int fd); private: static void destroySelection(struct wl_resource *resource, struct wl_client *client); @@ -75,9 +85,20 @@ private: const char *mime_type, int fd); static const struct wl_selection_offer_interface selectionOfferInterface; + void retain(); + void finishReadFromClient(); + QStringList m_offerList; struct wl_selection *m_currentSelection; struct wl_selection_offer *m_currentOffer; + QMimeData m_retainedData; + QSocketNotifier *m_retainedReadNotifier; + int m_retainedReadIndex; + QByteArray m_retainedReadBuf; + struct wl_selection *m_retainedSelection; + bool m_retainedSelectionEnabled; + Watcher m_watchFunc; + void *m_watchFuncParam; }; } -- 2.7.4