Added Wayland selection support.
authorLaszlo Agocs <laszlo.p.agocs@nokia.com>
Fri, 6 May 2011 16:36:43 +0000 (18:36 +0200)
committerLaszlo Agocs <laszlo.p.agocs@nokia.com>
Mon, 9 May 2011 09:11:14 +0000 (11:11 +0200)
13 files changed:
src/gui/kernel/qclipboard.h
src/gui/kernel/qplatformclipboard_qpa.cpp
src/gui/kernel/qplatformclipboard_qpa.h
src/plugins/platforms/wayland/qwaylandclipboard.cpp [new file with mode: 0644]
src/plugins/platforms/wayland/qwaylandclipboard.h [new file with mode: 0644]
src/plugins/platforms/wayland/qwaylanddisplay.cpp
src/plugins/platforms/wayland/qwaylanddisplay.h
src/plugins/platforms/wayland/qwaylandinputdevice.h
src/plugins/platforms/wayland/qwaylandintegration.cpp
src/plugins/platforms/wayland/qwaylandintegration.h
src/plugins/platforms/wayland/wayland.pro
src/plugins/platforms/xcb/xcb.pro
src/plugins/platforms/xlib/xlib.pro

index b55bdc6..019917e 100644 (file)
@@ -112,6 +112,7 @@ protected:
     friend class QBaseApplication;
     friend class QDragManager;
     friend class QMimeSource;
+    friend class QPlatformClipboard;
 
 private:
     Q_DISABLE_COPY(QClipboard)
index 957a4df..33d2afc 100644 (file)
@@ -42,6 +42,8 @@
 
 #ifndef QT_NO_CLIPBOARD
 
+#include <QtGui/private/qapplication_p.h>
+
 QT_BEGIN_NAMESPACE
 
 class QClipboardData
@@ -100,6 +102,11 @@ bool QPlatformClipboard::supportsMode(QClipboard::Mode mode) const
     return mode == QClipboard::Clipboard;
 }
 
+void QPlatformClipboard::emitChanged(QClipboard::Mode mode)
+{
+    QApplication::clipboard()->emitChanged(mode);
+}
+
 QT_END_NAMESPACE
 
 #endif //QT_NO_CLIPBOARD
index 3381c06..5444a1f 100644 (file)
@@ -62,6 +62,7 @@ public:
     virtual const QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard ) const;
     virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard);
     virtual bool supportsMode(QClipboard::Mode mode) const;
+    void emitChanged(QClipboard::Mode mode);
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wayland/qwaylandclipboard.cpp b/src/plugins/platforms/wayland/qwaylandclipboard.cpp
new file mode 100644 (file)
index 0000000..9c533aa
--- /dev/null
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandclipboard.h"
+#include "qwaylanddisplay.h"
+#include "qwaylandinputdevice.h"
+#include <QtGui/QPlatformNativeInterface>
+#include <QtGui/QApplication>
+#include <QtCore/QMimeData>
+#include <QtCore/QStringList>
+#include <QtCore/QFile>
+#include <QtCore/QtDebug>
+
+static QWaylandClipboard *clipboard;
+
+class QWaylandSelection
+{
+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);
+    static const struct wl_selection_listener selectionListener;
+
+    QMimeData *mMimeData;
+    struct wl_selection *mSelection;
+};
+
+const struct wl_selection_listener QWaylandSelection::selectionListener = {
+    QWaylandSelection::send,
+    QWaylandSelection::cancelled
+};
+
+uint32_t QWaylandSelection::getTime()
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+QWaylandSelection::QWaylandSelection(QWaylandDisplay *display, QMimeData *data)
+    : mMimeData(data), mSelection(0)
+{
+    struct wl_shell *shell = display->wl_shell();
+    mSelection = wl_shell_create_selection(shell);
+    wl_selection_add_listener(mSelection, &selectionListener, this);
+    foreach (const QString &format, data->formats())
+        wl_selection_offer(mSelection, format.toLatin1().constData());
+    wl_selection_activate(mSelection,
+                          display->inputDevices().at(0)->wl_input_device(),
+                          getTime());
+}
+
+QWaylandSelection::~QWaylandSelection()
+{
+    if (mSelection) {
+        clipboard->unregisterSelection(this);
+        wl_selection_destroy(mSelection);
+    }
+    delete mMimeData;
+}
+
+void QWaylandSelection::send(void *data,
+                             struct wl_selection *selection,
+                             const char *mime_type,
+                             int fd)
+{
+    Q_UNUSED(selection);
+    QWaylandSelection *self = static_cast<QWaylandSelection *>(data);
+    QString mimeType = QString::fromLatin1(mime_type);
+    QByteArray content = self->mMimeData->data(mimeType);
+    if (!content.isEmpty()) {
+        QFile f;
+        if (f.open(fd, QIODevice::WriteOnly))
+            f.write(content);
+    }
+    close(fd);
+}
+
+void QWaylandSelection::cancelled(void *data, struct wl_selection *selection)
+{
+    Q_UNUSED(selection);
+    delete static_cast<QWaylandSelection *>(data);
+}
+
+QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display)
+    : mDisplay(display), mSelection(0), mMimeDataIn(0), mOffer(0)
+{
+    clipboard = this;
+}
+
+QWaylandClipboard::~QWaylandClipboard()
+{
+    if (mOffer)
+        wl_selection_offer_destroy(mOffer);
+    delete mMimeDataIn;
+    qDeleteAll(mSelections);
+}
+
+void QWaylandClipboard::unregisterSelection(QWaylandSelection *selection)
+{
+    mSelections.removeOne(selection);
+}
+
+void QWaylandClipboard::syncCallback(void *data)
+{
+    *static_cast<bool *>(data) = true;
+}
+
+void QWaylandClipboard::forceRoundtrip(struct wl_display *display)
+{
+    bool done = false;
+    wl_display_sync_callback(display, syncCallback, &done);
+    wl_display_iterate(display, WL_DISPLAY_WRITABLE);
+    while (!done)
+        wl_display_iterate(display, WL_DISPLAY_READABLE);
+}
+
+const QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) const
+{
+    Q_ASSERT(mode == QClipboard::Clipboard);
+    if (!mMimeDataIn)
+        mMimeDataIn = new QMimeData;
+    mMimeDataIn->clear();
+    if (!mOfferedMimeTypes.isEmpty() && mOffer) {
+        foreach (const QString &mimeType, mOfferedMimeTypes) {
+            int pipefd[2];
+            if (pipe(pipefd) == -1) {
+                qWarning("QWaylandClipboard::mimedata: pipe() failed");
+                break;
+            }
+            QByteArray mimeTypeBa = mimeType.toLatin1();
+            wl_selection_offer_receive(mOffer, mimeTypeBa.constData(), pipefd[1]);
+            QByteArray content;
+            forceRoundtrip(mDisplay->wl_display());
+            char buf[256];
+            int n;
+            close(pipefd[1]);
+            while ((n = read(pipefd[0], &buf, sizeof buf)) > 0)
+                content.append(buf, n);
+            close(pipefd[0]);
+            mMimeDataIn->setData(mimeType, content);
+        }
+    }
+    return mMimeDataIn;
+}
+
+void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
+{
+    Q_ASSERT(mode == QClipboard::Clipboard);
+    if (!mDisplay->inputDevices().isEmpty()) {
+        if (!data)
+            data = new QMimeData;
+        mSelection = new QWaylandSelection(mDisplay, data);
+    } else {
+        qWarning("QWaylandClipboard::setMimeData: No input devices");
+    }
+}
+
+bool QWaylandClipboard::supportsMode(QClipboard::Mode mode) const
+{
+    return mode == QClipboard::Clipboard;
+}
+
+const struct wl_selection_offer_listener QWaylandClipboard::selectionOfferListener = {
+    QWaylandClipboard::offer,
+    QWaylandClipboard::keyboardFocus
+};
+
+void QWaylandClipboard::createSelectionOffer(uint32_t id)
+{
+    mOfferedMimeTypes.clear();
+    if (mOffer)
+        wl_selection_offer_destroy(mOffer);
+    mOffer = 0;
+    struct wl_selection_offer *offer = wl_selection_offer_create(mDisplay->wl_display(), id, 1);
+    wl_selection_offer_add_listener(offer, &selectionOfferListener, this);
+}
+
+void QWaylandClipboard::offer(void *data,
+                              struct wl_selection_offer *selection_offer,
+                              const char *type)
+{
+    Q_UNUSED(selection_offer);
+    QWaylandClipboard *self = static_cast<QWaylandClipboard *>(data);
+    self->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);
+    if (!input_device) {
+        wl_selection_offer_destroy(selection_offer);
+        self->mOffer = 0;
+        return;
+    }
+    self->mOffer = selection_offer;
+    self->emitChanged(QClipboard::Clipboard);
+}
diff --git a/src/plugins/platforms/wayland/qwaylandclipboard.h b/src/plugins/platforms/wayland/qwaylandclipboard.h
new file mode 100644 (file)
index 0000000..606a1f6
--- /dev/null
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDCLIPBOARD_H
+#define QWAYLANDCLIPBOARD_H
+
+#include <QtGui/QPlatformClipboard>
+#include <QtCore/QStringList>
+
+class QWaylandDisplay;
+class QWaylandSelection;
+struct wl_selection_offer;
+
+class QWaylandClipboard : public QPlatformClipboard
+{
+public:
+    QWaylandClipboard(QWaylandDisplay *display);
+    ~QWaylandClipboard();
+
+    const QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) const;
+    void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard);
+    bool supportsMode(QClipboard::Mode mode) const;
+
+    void unregisterSelection(QWaylandSelection *selection);
+
+    void createSelectionOffer(uint32_t id);
+
+private:
+    static void offer(void *data,
+                      struct wl_selection_offer *selection_offer,
+                      const char *type);
+    static void keyboardFocus(void *data,
+                              struct wl_selection_offer *selection_offer,
+                              struct wl_input_device *input_device);
+    static const struct wl_selection_offer_listener selectionOfferListener;
+
+    static void syncCallback(void *data);
+    static void forceRoundtrip(struct wl_display *display);
+
+    QWaylandDisplay *mDisplay;
+    QWaylandSelection *mSelection;
+    mutable QMimeData *mMimeDataIn;
+    QList<QWaylandSelection *> mSelections;
+    QStringList mOfferedMimeTypes;
+    struct wl_selection_offer *mOffer;
+};
+
+#endif // QWAYLANDCLIPBOARD_H
index 876b46a..974453d 100644 (file)
@@ -45,6 +45,7 @@
 #include "qwaylandscreen.h"
 #include "qwaylandcursor.h"
 #include "qwaylandinputdevice.h"
+#include "qwaylandclipboard.h"
 
 #ifdef QT_WAYLAND_GL_SUPPORT
 #include "gl_integration/qwaylandglintegration.h"
@@ -52,6 +53,7 @@
 
 #include <QtCore/QAbstractEventDispatcher>
 #include <QtGui/QApplication>
+#include <QtGui/private/qapplication_p.h>
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -249,7 +251,6 @@ void QWaylandDisplay::displayHandleGlobal(uint32_t id,
                                           uint32_t version)
 {
     Q_UNUSED(version);
-
     if (interface == "wl_output") {
         struct wl_output *output = wl_output_create(mDisplay, id, 1);
         wl_output_add_listener(output, &outputListener, this);
@@ -264,5 +265,9 @@ void QWaylandDisplay::displayHandleGlobal(uint32_t id,
         QWaylandInputDevice *inputDevice =
             new QWaylandInputDevice(mDisplay, id);
         mInputDevices.append(inputDevice);
+    } else if (interface == "wl_selection_offer") {
+        QPlatformIntegration *plat = QApplicationPrivate::platformIntegration();
+        QWaylandClipboard *clipboard = static_cast<QWaylandClipboard *>(plat->clipboard());
+        clipboard->createSelectionOffer(id);
     }
 }
index a2cb1b2..0658956 100644 (file)
@@ -80,6 +80,7 @@ public:
     void frameCallback(wl_display_frame_func_t func, struct wl_surface *surface, void *data);
 
     struct wl_display *wl_display() const { return mDisplay; }
+    struct wl_shell *wl_shell() const { return mShell; }
 
     QList<QWaylandInputDevice *> inputDevices() const { return mInputDevices; }
 
index 3c83252..251259b 100644 (file)
@@ -60,6 +60,7 @@ public:
     QWaylandInputDevice(struct wl_display *display, uint32_t id);
     void attach(QWaylandBuffer *buffer, int x, int y);
     void handleWindowDestroyed(QWaylandWindow *window);
+    struct wl_input_device *wl_input_device() const { return mInputDevice; }
 
 private:
     struct wl_display *mDisplay;
index b6401f6..6166c14 100644 (file)
@@ -45,6 +45,7 @@
 #include "qwaylandshmsurface.h"
 #include "qwaylandshmwindow.h"
 #include "qwaylandnativeinterface.h"
+#include "qwaylandclipboard.h"
 
 #include "qgenericunixfontdatabase.h"
 
@@ -64,6 +65,7 @@ QWaylandIntegration::QWaylandIntegration(bool useOpenGL)
     , mDisplay(new QWaylandDisplay())
     , mUseOpenGL(useOpenGL)
     , mNativeInterface(new QWaylandNativeInterface)
+    , mClipboard(0)
 {
 }
 
@@ -132,3 +134,10 @@ bool QWaylandIntegration::hasOpenGL() const
     return false;
 #endif
 }
+
+QPlatformClipboard *QWaylandIntegration::clipboard() const
+{
+    if (!mClipboard)
+        mClipboard = new QWaylandClipboard(mDisplay);
+    return mClipboard;
+}
index 71f6d9c..fc748b0 100644 (file)
@@ -65,6 +65,8 @@ public:
 
     QPlatformNativeInterface *nativeInterface() const;
 
+    QPlatformClipboard *clipboard() const;
+
 private:
     bool hasOpenGL() const;
 
@@ -72,6 +74,7 @@ private:
     QWaylandDisplay *mDisplay;
     bool mUseOpenGL;
     QPlatformNativeInterface *mNativeInterface;
+    mutable QPlatformClipboard *mClipboard;
 };
 
 QT_END_NAMESPACE
index 3139232..8fee344 100644 (file)
@@ -6,6 +6,8 @@ DESTDIR = $$QT.gui.plugins/platforms
 DEFINES += Q_PLATFORM_WAYLAND
 DEFINES += $$QMAKE_DEFINES_WAYLAND
 
+QT += core-private gui-private opengl-private
+
 SOURCES =   main.cpp \
             qwaylandintegration.cpp \
             qwaylandnativeinterface.cpp \
@@ -15,7 +17,8 @@ SOURCES =   main.cpp \
             qwaylanddisplay.cpp \
             qwaylandwindow.cpp \
             qwaylandscreen.cpp \
-            qwaylandshmwindow.cpp
+            qwaylandshmwindow.cpp \
+            qwaylandclipboard.cpp
 
 HEADERS =   qwaylandintegration.h \
             qwaylandnativeinterface.h \
@@ -25,7 +28,8 @@ HEADERS =   qwaylandintegration.h \
             qwaylandscreen.h \
             qwaylandshmsurface.h \
             qwaylandbuffer.h \
-            qwaylandshmwindow.h
+            qwaylandshmwindow.h \
+            qwaylandclipboard.h
 
 INCLUDEPATH += $$QMAKE_INCDIR_WAYLAND
 LIBS += $$QMAKE_LIBS_WAYLAND
index 101bdcd..139f5c9 100644 (file)
@@ -3,6 +3,8 @@ TARGET = xcb
 include(../../qpluginbase.pri)
 QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms
 
+QT += core-private gui-private
+
 SOURCES = \
         qxcbconnection.cpp \
         qxcbintegration.cpp \
index ae02077..902d379 100644 (file)
@@ -3,6 +3,8 @@ TARGET = qxlib
 include(../../qpluginbase.pri)
 DESTDIR = $$QT.gui.plugins/platforms
 
+QT += core-private gui-private opengl-private
+
 SOURCES = \
         main.cpp \
         qxlibintegration.cpp \