Add xcb based mime type handling
authorLars Knoll <lars.knoll@nokia.com>
Sun, 22 May 2011 20:23:48 +0000 (22:23 +0200)
committerLars Knoll <lars.knoll@nokia.com>
Mon, 30 May 2011 12:44:12 +0000 (14:44 +0200)
Reworked the code from the xlib lighthouse
plugin to remove most of the Xlib dependencies.

The code originally comdes from qdnd_x11.cpp

src/plugins/platforms/xcb/qxcbconnection.cpp
src/plugins/platforms/xcb/qxcbconnection.h
src/plugins/platforms/xcb/qxcbmime.cpp [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbmime.h [new file with mode: 0644]
src/plugins/platforms/xcb/xcb.pro

index da6e69e..bdd1850 100644 (file)
@@ -583,9 +583,12 @@ static const char * xcb_atomnames = {
     "_NET_ACTIVE_WINDOW\0"
 
     // Property formats
+    "XA_STRING\0"
     "COMPOUND_TEXT\0"
     "TEXT\0"
     "UTF8_STRING\0"
+    "XA_PIXMAP\0"
+    "XA_BITMAP\0"
 
     // xdnd
     "XdndEnter\0"
@@ -663,8 +666,32 @@ void QXcbConnection::initializeAllAtoms() {
     for (i = 0; i < QXcbAtom::NAtoms; ++i)
         cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]);
 
-    for (i = 0; i < QXcbAtom::NAtoms; ++i)
-        m_allAtoms[i] = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0)->atom;
+    for (i = 0; i < QXcbAtom::NAtoms; ++i) {
+        xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0);
+        m_allAtoms[i] = reply->atom;
+        free(reply);
+    }
+}
+
+xcb_atom_t QXcbConnection::internAtom(const char *name)
+{
+    if (!name || *name == 0)
+        return XCB_NONE;
+
+    xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xcb_connection(), false, strlen(name), name);
+    xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookie, 0);
+    int atom = reply->atom;
+    free(reply);
+    return atom;
+}
+
+QByteArray QXcbConnection::atomName(xcb_atom_t atom)
+{
+    xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name_unchecked(xcb_connection(), atom);
+    xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, 0);
+    QByteArray result(xcb_get_atom_name_name(reply));
+    free(reply);
+    return result;
 }
 
 void QXcbConnection::sync()
index 23c704f..124b851 100644 (file)
@@ -164,9 +164,12 @@ namespace QXcbAtom {
         _NET_ACTIVE_WINDOW,
 
         // Property formats
+        XA_STRING,
         COMPOUND_TEXT,
         TEXT,
         UTF8_STRING,
+        XA_PIXMAP,
+        XA_BITMAP,
 
         // Xdnd
         XdndEnter,
@@ -234,6 +237,8 @@ public:
     int primaryScreen() const { return m_primaryScreen; }
 
     xcb_atom_t atom(QXcbAtom::Atom atom);
+    xcb_atom_t internAtom(const char *name);
+    QByteArray atomName(xcb_atom_t atom);
 
     const char *displayName() const { return m_displayName.constData(); }
 
diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp
new file mode 100644 (file)
index 0000000..311a909
--- /dev/null
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** 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 "qxcbmime.h"
+
+#include <QtCore/QTextCodec>
+#include <QtGui/QImageWriter>
+#include <QtCore/QBuffer>
+
+#include <X11/Xutil.h>
+
+#undef XA_STRING
+#undef XA_PIXMAP
+#undef XA_BITMAP
+
+QXcbMime::QXcbMime()
+    : QInternalMimeData()
+{ }
+
+QXcbMime::~QXcbMime()
+{}
+
+
+
+QString QXcbMime::mimeAtomToString(QXcbConnection *connection, xcb_atom_t a)
+{
+    if (a == XCB_NONE)
+        return 0;
+
+    // special cases for string type
+    if (a == connection->atom(QXcbAtom::XA_STRING)
+            || a == connection->atom(QXcbAtom::UTF8_STRING)
+            || a == connection->atom(QXcbAtom::TEXT)
+            || a == connection->atom(QXcbAtom::COMPOUND_TEXT))
+        return QLatin1String("text/plain");
+
+    // special case for images
+    if (a == connection->atom(QXcbAtom::XA_PIXMAP))
+        return QLatin1String("image/ppm");
+
+    QByteArray atomName = connection->atomName(a);
+
+    // special cases for uris
+    if (atomName == "text/x-moz-url")
+        atomName = "text/uri-list";
+
+    return QString::fromLatin1(atomName.constData());
+}
+
+bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data,
+                               xcb_atom_t *atomFormat, int *dataFormat)
+{
+    if (!data)
+        return false;
+
+    bool ret = false;
+    *atomFormat = a;
+    *dataFormat = 8;
+
+    if ((a == connection->atom(QXcbAtom::UTF8_STRING)
+         || a == connection->atom(QXcbAtom::XA_STRING)
+         || a == connection->atom(QXcbAtom::TEXT)
+         || a == connection->atom(QXcbAtom::COMPOUND_TEXT))
+        && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) {
+        if (a == connection->atom(QXcbAtom::UTF8_STRING)){
+            *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData);
+            ret = true;
+        } else if (a == connection->atom(QXcbAtom::XA_STRING)) {
+            *data = QString::fromUtf8(QInternalMimeData::renderDataHelper(
+                        QLatin1String("text/plain"), mimeData)).toLocal8Bit();
+            ret = true;
+        } else if (a == connection->atom(QXcbAtom::TEXT)
+                   || a == connection->atom(QXcbAtom::COMPOUND_TEXT)) {
+            // the ICCCM states that TEXT and COMPOUND_TEXT are in the
+            // encoding of choice, so we choose the encoding of the locale
+            QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper(
+                                 QLatin1String("text/plain"), mimeData)).toLocal8Bit();
+            char *list[] = { strData.data(), NULL };
+
+            XICCEncodingStyle style = (a == connection->atom(QXcbAtom::COMPOUND_TEXT))
+                                    ? XCompoundTextStyle : XStdICCTextStyle;
+            XTextProperty textprop;
+            if (list[0] != NULL
+                && XmbTextListToTextProperty(DISPLAY_FROM_XCB(connection), list, 1, style, &textprop) == Success) {
+                *atomFormat = textprop.encoding;
+                *dataFormat = textprop.format;
+                *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8);
+                ret = true;
+
+                XFree(textprop.value);
+            }
+        }
+        return ret;
+    }
+
+    QString atomName = mimeAtomToString(connection, a);
+    if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) {
+        *data = QInternalMimeData::renderDataHelper(atomName, mimeData);
+        if (atomName == QLatin1String("application/x-color"))
+            *dataFormat = 16;
+        ret = true;
+    } else if (atomName == QLatin1String("text/x-moz-url") &&
+               QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) {
+        QByteArray uri = QInternalMimeData::renderDataHelper(
+                         QLatin1String("text/uri-list"), mimeData).split('\n').first();
+        QString mozUri = QString::fromLatin1(uri, uri.size());
+        mozUri += QLatin1Char('\n');
+        *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2);
+        ret = true;
+    } else if ((a == connection->atom(QXcbAtom::XA_PIXMAP) || a == connection->atom(QXcbAtom::XA_BITMAP)) && mimeData->hasImage()) {
+        ret = true;
+    }
+    return ret;
+}
+
+QList<xcb_atom_t> QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, const QString &format)
+{
+    QList<xcb_atom_t> atoms;
+    atoms.append(connection->internAtom(format.toLatin1()));
+
+    // special cases for strings
+    if (format == QLatin1String("text/plain")) {
+        atoms.append(connection->atom(QXcbAtom::UTF8_STRING));
+        atoms.append(connection->atom(QXcbAtom::XA_STRING));
+        atoms.append(connection->atom(QXcbAtom::TEXT));
+        atoms.append(connection->atom(QXcbAtom::COMPOUND_TEXT));
+    }
+
+    // special cases for uris
+    if (format == QLatin1String("text/uri-list"))
+        atoms.append(connection->internAtom("text/x-moz-url"));
+
+    //special cases for images
+    if (format == QLatin1String("image/ppm"))
+        atoms.append(connection->atom(QXcbAtom::XA_PIXMAP));
+    if (format == QLatin1String("image/pbm"))
+        atoms.append(connection->atom(QXcbAtom::XA_BITMAP));
+
+    return atoms;
+}
+
+QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format,
+                                       QVariant::Type requestedType, const QByteArray &encoding)
+{
+    QString atomName = mimeAtomToString(connection, a);
+    if (atomName == format)
+        return data;
+
+    if (!encoding.isEmpty()
+        && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) {
+
+        if (requestedType == QVariant::String) {
+            QTextCodec *codec = QTextCodec::codecForName(encoding);
+            if (codec)
+                return codec->toUnicode(data);
+        }
+
+        return data;
+    }
+
+    // special cases for string types
+    if (format == QLatin1String("text/plain")) {
+        if (a == connection->atom(QXcbAtom::UTF8_STRING))
+            return QString::fromUtf8(data);
+        if (a == connection->atom(QXcbAtom::XA_STRING))
+            return QString::fromLatin1(data);
+        if (a == connection->atom(QXcbAtom::TEXT)
+                || a == connection->atom(QXcbAtom::COMPOUND_TEXT))
+            // #### might be wrong for COMPOUND_TEXT
+            return QString::fromLocal8Bit(data, data.size());
+    }
+
+    // special case for uri types
+    if (format == QLatin1String("text/uri-list")) {
+        if (atomName == QLatin1String("text/x-moz-url")) {
+            // we expect this as utf16 <url><space><title>
+            // the first part is a url that should only contain ascci char
+            // so it should be safe to check that the second char is 0
+            // to verify that it is utf16
+            if (data.size() > 1 && data.at(1) == 0)
+                return QString::fromRawData((const QChar *)data.constData(),
+                                data.size() / 2).split(QLatin1Char('\n')).first().toLatin1();
+        }
+    }
+
+#if 0 // ###
+    // special case for images
+    if (format == QLatin1String("image/ppm")) {
+        if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) {
+            Pixmap xpm = *((Pixmap*)data.data());
+            if (!xpm)
+                return QByteArray();
+            Window root;
+            int x;
+            int y;
+            uint width;
+            uint height;
+            uint border_width;
+            uint depth;
+
+            XGetGeometry(display, xpm, &root, &x, &y, &width, &height, &border_width, &depth);
+            XImage *ximg = XGetImage(display,xpm,x,y,width,height,AllPlanes,depth==1 ? XYPixmap : ZPixmap);
+            QImage qimg = QXlibStatic::qimageFromXImage(ximg);
+            XDestroyImage(ximg);
+
+            QImageWriter imageWriter;
+            imageWriter.setFormat("PPMRAW");
+            QBuffer buf;
+            buf.open(QIODevice::WriteOnly);
+            imageWriter.setDevice(&buf);
+            imageWriter.write(qimg);
+            return buf.buffer();
+        }
+    }
+#endif
+    return QVariant();
+}
+
+xcb_atom_t QXcbMime::mimeAtomForFormat(QXcbConnection *connection, const QString &format, QVariant::Type requestedType,
+                                 const QList<xcb_atom_t> &atoms, QByteArray *requestedEncoding)
+{
+    requestedEncoding->clear();
+
+    // find matches for string types
+    if (format == QLatin1String("text/plain")) {
+        if (atoms.contains(connection->atom(QXcbAtom::UTF8_STRING)))
+            return connection->atom(QXcbAtom::UTF8_STRING);
+        if (atoms.contains(connection->atom(QXcbAtom::COMPOUND_TEXT)))
+            return connection->atom(QXcbAtom::COMPOUND_TEXT);
+        if (atoms.contains(connection->atom(QXcbAtom::TEXT)))
+            return connection->atom(QXcbAtom::TEXT);
+        if (atoms.contains(connection->atom(QXcbAtom::XA_STRING)))
+            return connection->atom(QXcbAtom::XA_STRING);
+    }
+
+    // find matches for uri types
+    if (format == QLatin1String("text/uri-list")) {
+        xcb_atom_t a = connection->internAtom(format.toLatin1());
+        if (a && atoms.contains(a))
+            return a;
+        a = connection->internAtom("text/x-moz-url");
+        if (a && atoms.contains(a))
+            return a;
+    }
+
+    // find match for image
+    if (format == QLatin1String("image/ppm")) {
+        if (atoms.contains(connection->atom(QXcbAtom::XA_PIXMAP)))
+            return connection->atom(QXcbAtom::XA_PIXMAP);
+    }
+
+    // for string/text requests try to use a format with a well-defined charset
+    // first to avoid encoding problems
+    if (requestedType == QVariant::String
+        && format.startsWith(QLatin1String("text/"))
+        && !format.contains(QLatin1String("charset="))) {
+
+        QString formatWithCharset = format;
+        formatWithCharset.append(QLatin1String(";charset=utf-8"));
+
+        xcb_atom_t a = connection->internAtom(formatWithCharset.toLatin1());
+        if (a && atoms.contains(a)) {
+            *requestedEncoding = "utf-8";
+            return a;
+        }
+    }
+
+    xcb_atom_t a = connection->internAtom(format.toLatin1());
+    if (a && atoms.contains(a))
+        return a;
+
+    return 0;
+}
diff --git a/src/plugins/platforms/xcb/qxcbmime.h b/src/plugins/platforms/xcb/qxcbmime.h
new file mode 100644 (file)
index 0000000..4ab38ed
--- /dev/null
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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 QXCBMIME_H
+#define QXCBMIME_H
+
+#include <private/qdnd_p.h>
+
+#include <QtGui/QClipboard>
+
+#include "qxcbintegration.h"
+#include "qxcbconnection.h"
+
+class QXcbMime : public QInternalMimeData {
+    Q_OBJECT
+public:
+    QXcbMime();
+    ~QXcbMime();
+
+    static QList<xcb_atom_t> mimeAtomsForFormat(QXcbConnection *connection, const QString &format);
+    static QString mimeAtomToString(QXcbConnection *connection, xcb_atom_t a);
+    static bool mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data,
+                                xcb_atom_t *atomFormat, int *dataFormat);
+    static QVariant mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format,
+                                        QVariant::Type requestedType, const QByteArray &encoding);
+    static xcb_atom_t mimeAtomForFormat(QXcbConnection *connection, const QString &format, QVariant::Type requestedType,
+                                        const QList<xcb_atom_t> &atoms, QByteArray *requestedEncoding);
+};
+
+#endif // QXCBMIME_H
index c6bf17f..6dd69c0 100644 (file)
@@ -9,6 +9,7 @@ SOURCES = \
         qxcbconnection.cpp \
         qxcbintegration.cpp \
         qxcbkeyboard.cpp \
+        qxcbmime.cpp \
         qxcbscreen.cpp \
         qxcbwindow.cpp \
         qxcbwindowsurface.cpp \
@@ -19,6 +20,7 @@ HEADERS = \
         qxcbconnection.h \
         qxcbintegration.h \
         qxcbkeyboard.h \
+        qxcbmime.h \
         qxcbobject.h \
         qxcbscreen.h \
         qxcbwindow.h \