diff --git a/atoms.cpp b/atoms.cpp
index 540ade8bae..86540ab47e 100644
--- a/atoms.cpp
+++ b/atoms.cpp
@@ -70,6 +70,8 @@ Atoms::Atoms()
, utf8_string(QByteArrayLiteral("UTF8_STRING"))
, text(QByteArrayLiteral("TEXT"))
, uri_list(QByteArrayLiteral("text/uri-list"))
+ , netscape_url(QByteArrayLiteral("_NETSCAPE_URL"))
+ , moz_url(QByteArrayLiteral("text/x-moz-url"))
, wl_surface_id(QByteArrayLiteral("WL_SURFACE_ID"))
, kde_net_wm_appmenu_service_name(QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"))
, kde_net_wm_appmenu_object_path(QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"))
diff --git a/atoms.h b/atoms.h
index 0a6bfd414f..74e0fdc618 100644
--- a/atoms.h
+++ b/atoms.h
@@ -79,6 +79,8 @@ public:
Xcb::Atom utf8_string;
Xcb::Atom text;
Xcb::Atom uri_list;
+ Xcb::Atom netscape_url;
+ Xcb::Atom moz_url;
Xcb::Atom wl_surface_id;
Xcb::Atom kde_net_wm_appmenu_service_name;
Xcb::Atom kde_net_wm_appmenu_object_path;
diff --git a/xwl/drag_x.cpp b/xwl/drag_x.cpp
index 2e73ed1213..57b2cd178b 100644
--- a/xwl/drag_x.cpp
+++ b/xwl/drag_x.cpp
@@ -42,6 +42,24 @@ along with this program. If not, see .
namespace KWin {
namespace Xwl {
+static QStringList atomToMimeTypes(xcb_atom_t atom)
+{
+ QStringList mimeTypes;
+
+ if (atom == atoms->utf8_string) {
+ mimeTypes << QString::fromLatin1("text/plain;charset=utf-8");
+ } else if (atom == atoms->text) {
+ mimeTypes << QString::fromLatin1("text/plain");
+ } else if (atom == atoms->uri_list || atom == atoms->netscape_url || atom == atoms->moz_url) {
+ // We identify netscape and moz format as less detailed formats text/uri-list,
+ // text/x-uri and accept the information loss.
+ mimeTypes << QString::fromLatin1("text/uri-list") << QString::fromLatin1("text/x-uri");
+ } else {
+ mimeTypes << Selection::atomName(atom);
+ }
+ return mimeTypes;
+}
+
XToWlDrag::XToWlDrag(X11Source *source)
: m_src(source)
{
@@ -365,7 +383,7 @@ bool WlVisit::handleEnter(xcb_client_message_event_t *ev)
// message has only max 3 types (which are directly in data)
for (size_t i = 0; i < 3; i++) {
xcb_atom_t mimeAtom = data->data32[2 + i];
- const auto mimeStrings = Selection::atomToMimeTypes(mimeAtom);
+ const auto mimeStrings = atomToMimeTypes(mimeAtom);
for (const auto mime : mimeStrings ) {
if (!hasMimeName(offers, mime)) {
offers << Mime(mime, mimeAtom);
@@ -403,7 +421,7 @@ void WlVisit::getMimesFromWinProperty(Mimes &offers)
xcb_atom_t *mimeAtoms = static_cast(xcb_get_property_value(reply));
for (size_t i = 0; i < reply->value_len; ++i) {
- const auto mimeStrings = Selection::atomToMimeTypes(mimeAtoms[i]);
+ const auto mimeStrings = atomToMimeTypes(mimeAtoms[i]);
for (const auto mime : mimeStrings ) {
if (!hasMimeName(offers, mime)) {
offers << Mime(mime, mimeAtoms[i]);
diff --git a/xwl/selection.cpp b/xwl/selection.cpp
index 944b0b7eed..a50205cfe5 100644
--- a/xwl/selection.cpp
+++ b/xwl/selection.cpp
@@ -51,6 +51,21 @@ xcb_atom_t Selection::mimeTypeToAtomLiteral(const QString &mimeType)
return Xcb::Atom(mimeType.toLatin1(), false, kwinApp()->x11Connection());
}
+QString Selection::atomName(xcb_atom_t atom)
+{
+ auto *xcbConn = kwinApp()->x11Connection();
+ xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom);
+ xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, NULL);
+ if (nameReply == NULL) {
+ return QString();
+ }
+
+ size_t len = xcb_get_atom_name_name_length(nameReply);
+ QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), len);
+ free(nameReply);
+ return name;
+}
+
QStringList Selection::atomToMimeTypes(xcb_atom_t atom)
{
QStringList mimeTypes;
@@ -62,17 +77,7 @@ QStringList Selection::atomToMimeTypes(xcb_atom_t atom)
} else if (atom == atoms->uri_list) {
mimeTypes << "text/uri-list" << "text/x-uri";
} else {
- auto *xcbConn = kwinApp()->x11Connection();
- xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom);
- xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, NULL);
- if (nameReply == NULL) {
- return QStringList();
- }
-
- size_t len = xcb_get_atom_name_name_length(nameReply);
- char *name = xcb_get_atom_name_name(nameReply);
- mimeTypes << QString::fromLatin1(name, len);
- free(nameReply);
+ mimeTypes << atomName(atom);
}
return mimeTypes;
}
diff --git a/xwl/selection.h b/xwl/selection.h
index 79d2febaf9..c55ad3c526 100644
--- a/xwl/selection.h
+++ b/xwl/selection.h
@@ -60,6 +60,7 @@ public:
static xcb_atom_t mimeTypeToAtom(const QString &mimeType);
static xcb_atom_t mimeTypeToAtomLiteral(const QString &mimeType);
static QStringList atomToMimeTypes(xcb_atom_t atom);
+ static QString atomName(xcb_atom_t atom);
static void sendSelNotify(xcb_selection_request_event_t *event, bool success);
// on selection owner changes by X clients (Xwl -> Wl)
diff --git a/xwl/transfer.cpp b/xwl/transfer.cpp
index 65198aa8ea..71e1761ab8 100644
--- a/xwl/transfer.cpp
+++ b/xwl/transfer.cpp
@@ -351,7 +351,13 @@ bool TransferXtoWl::handleSelNotify(xcb_selection_notify_event_t *event)
return True;
}
- m_receiver = new DataReceiver;
+ if (event->target == atoms->netscape_url) {
+ m_receiver = new NetscapeUrlReceiver;
+ } else if (event->target == atoms->moz_url) {
+ m_receiver = new MozUrlReceiver;
+ } else {
+ m_receiver = new DataReceiver;
+ }
startTransfer();
return true;
}
@@ -463,6 +469,87 @@ void DataReceiver::partRead(int length)
}
}
+void NetscapeUrlReceiver::setData(char *value, int length)
+{
+ auto origData = QByteArray::fromRawData(value, length);
+
+ if (origData.indexOf('\n') == -1) {
+ // there are no line breaks, not in Netscape url format or empty,
+ // but try anyway
+ setDataInternal(origData);
+ return;
+ }
+ // remove every second line
+ QByteArray data;
+ int start = 0;
+ bool remLine = false;
+ while (start < length) {
+ auto part = QByteArray::fromRawData(value + start, length - start);
+ const int linebreak = part.indexOf('\n');
+ if (linebreak == -1) {
+ // no more linebreaks, end of work
+ if (!remLine) {
+ // append the rest
+ data.append(part);
+ }
+ break;
+ }
+ if (remLine) {
+ // no data to add, but add a linebreak for the next line
+ data.append('\n');
+ } else {
+ // add data till before linebreak
+ data.append(part.data(), linebreak);
+ }
+ remLine = !remLine;
+ start = linebreak + 1;
+ }
+ setDataInternal(data);
+}
+
+void MozUrlReceiver::setData(char *value, int length)
+{
+ // represent as QByteArray (guaranteed '\0'-terminated)
+ const auto origData = QByteArray::fromRawData(value, length);
+
+ // text/x-moz-url data is sent in utf-16 - copies the content
+ // and converts it into 8 byte representation
+ const auto byteData = QString::fromUtf16(reinterpret_cast(origData.data())).toLatin1();
+
+ if (byteData.indexOf('\n') == -1) {
+ // there are no line breaks, not in text/x-moz-url format or empty,
+ // but try anyway
+ setDataInternal(byteData);
+ return;
+ }
+ // remove every second line
+ QByteArray data;
+ int start = 0;
+ bool remLine = false;
+ while (start < length) {
+ auto part = QByteArray::fromRawData(byteData.data() + start, byteData.size() - start);
+ const int linebreak = part.indexOf('\n');
+ if (linebreak == -1) {
+ // no more linebreaks, end of work
+ if (!remLine) {
+ // append the rest
+ data.append(part);
+ }
+ break;
+ }
+ if (remLine) {
+ // no data to add, but add a linebreak for the next line
+ data.append('\n');
+ } else {
+ // add data till before linebreak
+ data.append(part.data(), linebreak);
+ }
+ remLine = !remLine;
+ start = linebreak + 1;
+ }
+ setDataInternal(data);
+}
+
void TransferXtoWl::dataSourceWrite()
{
QByteArray property = m_receiver->data();
diff --git a/xwl/transfer.h b/xwl/transfer.h
index 02cc2c0204..e3352f73e3 100644
--- a/xwl/transfer.h
+++ b/xwl/transfer.h
@@ -41,7 +41,7 @@ namespace KWin
namespace Xwl
{
-/*
+/**
* Represents for an arbitrary selection a data transfer between
* sender and receiver.
*
@@ -103,7 +103,7 @@ private:
bool m_timeout = false;
};
-/*
+/**
* Represents a transfer from a Wayland native source to an X window.
*/
class TransferWltoX : public Transfer
@@ -139,7 +139,7 @@ private:
bool flushPropOnDelete = false;
};
-/*
+/**
* Helper class for X to Wl transfers
*/
class DataReceiver
@@ -166,7 +166,27 @@ private:
QByteArray m_data;
};
-/*
+/**
+ * Compatibility receiver for clients only
+ * supporting the NETSCAPE_URL scheme (Firefox)
+ */
+class NetscapeUrlReceiver : public DataReceiver
+{
+public:
+ void setData(char *value, int length) override;
+};
+
+/**
+ * Compatibility receiver for clients only
+ * supporting the text/x-moz-url scheme (Chromium on own drags)
+ */
+class MozUrlReceiver : public DataReceiver
+{
+public:
+ void setData(char *value, int length) override;
+};
+
+/**
* Represents a transfer from an X window to a Wayland native client.
*/
class TransferXtoWl : public Transfer