[xwl] text/x-uri converter for selected X url list format targets
Summary: On X several target atoms are established to provide data similar to the text/uri-list target format (the respective MIME on Wayland is called text/x-uri). Firefox can send link data in the NETSCAPE_URL format ones. Chromium on the other side sends link data in the text/x-moz-url format, which transports UTF-16 text. For both these peculiarities this patch provides converter functions, that translate these formats into the Wayland native text/x-uri format. In the other direction no translation is necessary. Both browsers supports the text/uri-list format when receiving link lists. Test Plan: Manually with Firefox and Chromium. Reviewers: #kwin Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D15629
This commit is contained in:
parent
548978bfe1
commit
522d2935e6
7 changed files with 153 additions and 18 deletions
|
@ -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"))
|
||||
|
|
2
atoms.h
2
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;
|
||||
|
|
|
@ -42,6 +42,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
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_atom_t*>(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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<const char16_t*>(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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue