Client: Implement primary-selection-unstable-v1
[ChangeLog][QPA plugin] Added support for middle mouse pasting through the primary-selection-unstable-v1 protocol. Fixes: QTBUG-66008 Change-Id: I7c8fb9aa2c856f5b6794aeab1ee75d80cad05dcd Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
parent
50d2c36a57
commit
72cae9c58c
@ -55,6 +55,23 @@ Copyright © 2012-2013 Collabora, Ltd."
|
|||||||
Copyright (c) 2013 BMW Car IT GmbH"
|
Copyright (c) 2013 BMW Car IT GmbH"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"Id": "wayland-primary-selection-protocol",
|
||||||
|
"Name": "Wayland Primary Selection Protocol",
|
||||||
|
"QDocModule": "qtwaylandcompositor",
|
||||||
|
"QtUsage": "Used in the Qt Wayland platform plugin",
|
||||||
|
"Files": "wp-primary-selection-unstable-v1.xml",
|
||||||
|
|
||||||
|
"Description": "The primary selection extension allows copying text by selecting it and pasting it with the middle mouse button.",
|
||||||
|
"Homepage": "https://wayland.freedesktop.org",
|
||||||
|
"Version": "1",
|
||||||
|
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/primary-selection/primary-selection-unstable-v1.xml",
|
||||||
|
"LicenseId": "MIT",
|
||||||
|
"License": "MIT License",
|
||||||
|
"LicenseFile": "MIT_LICENSE.txt",
|
||||||
|
"Copyright": "Copyright © 2015 2016 Red Hat"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"Id": "wayland-scaler-protocol",
|
"Id": "wayland-scaler-protocol",
|
||||||
"Name": "Wayland Scaler Protocol",
|
"Name": "Wayland Scaler Protocol",
|
||||||
|
225
src/3rdparty/wayland/protocols/wp-primary-selection-unstable-v1.xml
vendored
Normal file
225
src/3rdparty/wayland/protocols/wp-primary-selection-unstable-v1.xml
vendored
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wp_primary_selection_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2015, 2016 Red Hat
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<description summary="Primary selection protocol">
|
||||||
|
This protocol provides the ability to have a primary selection device to
|
||||||
|
match that of the X server. This primary selection is a shortcut to the
|
||||||
|
common clipboard selection, where text just needs to be selected in order
|
||||||
|
to allow copying it elsewhere. The de facto way to perform this action
|
||||||
|
is the middle mouse button, although it is not limited to this one.
|
||||||
|
|
||||||
|
Clients wishing to honor primary selection should create a primary
|
||||||
|
selection source and set it as the selection through
|
||||||
|
wp_primary_selection_device.set_selection whenever the text selection
|
||||||
|
changes. In order to minimize calls in pointer-driven text selection,
|
||||||
|
it should happen only once after the operation finished. Similarly,
|
||||||
|
a NULL source should be set when text is unselected.
|
||||||
|
|
||||||
|
wp_primary_selection_offer objects are first announced through the
|
||||||
|
wp_primary_selection_device.data_offer event. Immediately after this event,
|
||||||
|
the primary data offer will emit wp_primary_selection_offer.offer events
|
||||||
|
to let know of the mime types being offered.
|
||||||
|
|
||||||
|
When the primary selection changes, the client with the keyboard focus
|
||||||
|
will receive wp_primary_selection_device.selection events. Only the client
|
||||||
|
with the keyboard focus will receive such events with a non-NULL
|
||||||
|
wp_primary_selection_offer. Across keyboard focus changes, previously
|
||||||
|
focused clients will receive wp_primary_selection_device.events with a
|
||||||
|
NULL wp_primary_selection_offer.
|
||||||
|
|
||||||
|
In order to request the primary selection data, the client must pass
|
||||||
|
a recent serial pertaining to the press event that is triggering the
|
||||||
|
operation, if the compositor deems the serial valid and recent, the
|
||||||
|
wp_primary_selection_source.send event will happen in the other end
|
||||||
|
to let the transfer begin. The client owning the primary selection
|
||||||
|
should write the requested data, and close the file descriptor
|
||||||
|
immediately.
|
||||||
|
|
||||||
|
If the primary selection owner client disappeared during the transfer,
|
||||||
|
the client reading the data will receive a
|
||||||
|
wp_primary_selection_device.selection event with a NULL
|
||||||
|
wp_primary_selection_offer, the client should take this as a hint
|
||||||
|
to finish the reads related to the no longer existing offer.
|
||||||
|
|
||||||
|
The primary selection owner should be checking for errors during
|
||||||
|
writes, merely cancelling the ongoing transfer if any happened.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<interface name="zwp_primary_selection_device_manager_v1" version="1">
|
||||||
|
<description summary="X primary selection emulation">
|
||||||
|
The primary selection device manager is a singleton global object that
|
||||||
|
provides access to the primary selection. It allows to create
|
||||||
|
wp_primary_selection_source objects, as well as retrieving the per-seat
|
||||||
|
wp_primary_selection_device objects.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="create_source">
|
||||||
|
<description summary="create a new primary selection source">
|
||||||
|
Create a new primary selection source.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwp_primary_selection_source_v1"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_device">
|
||||||
|
<description summary="create a new primary selection device">
|
||||||
|
Create a new data device for a given seat.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwp_primary_selection_device_v1"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the primary selection device manager">
|
||||||
|
Destroy the primary selection device manager.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_primary_selection_device_v1" version="1">
|
||||||
|
<request name="set_selection">
|
||||||
|
<description summary="set the primary selection">
|
||||||
|
Replaces the current selection. The previous owner of the primary
|
||||||
|
selection will receive a wp_primary_selection_source.cancelled event.
|
||||||
|
|
||||||
|
To unset the selection, set the source to NULL.
|
||||||
|
</description>
|
||||||
|
<arg name="source" type="object" interface="zwp_primary_selection_source_v1" allow-null="true"/>
|
||||||
|
<arg name="serial" type="uint" summary="serial of the event that triggered this request"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="data_offer">
|
||||||
|
<description summary="introduce a new wp_primary_selection_offer">
|
||||||
|
Introduces a new wp_primary_selection_offer object that may be used
|
||||||
|
to receive the current primary selection. Immediately following this
|
||||||
|
event, the new wp_primary_selection_offer object will send
|
||||||
|
wp_primary_selection_offer.offer events to describe the offered mime
|
||||||
|
types.
|
||||||
|
</description>
|
||||||
|
<arg name="offer" type="new_id" interface="zwp_primary_selection_offer_v1"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="selection">
|
||||||
|
<description summary="advertise a new primary selection">
|
||||||
|
The wp_primary_selection_device.selection event is sent to notify the
|
||||||
|
client of a new primary selection. This event is sent after the
|
||||||
|
wp_primary_selection.data_offer event introducing this object, and after
|
||||||
|
the offer has announced its mimetypes through
|
||||||
|
wp_primary_selection_offer.offer.
|
||||||
|
|
||||||
|
The data_offer is valid until a new offer or NULL is received
|
||||||
|
or until the client loses keyboard focus. The client must destroy the
|
||||||
|
previous selection data_offer, if any, upon receiving this event.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="object" interface="zwp_primary_selection_offer_v1" allow-null="true"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the primary selection device">
|
||||||
|
Destroy the primary selection device.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_primary_selection_offer_v1" version="1">
|
||||||
|
<description summary="offer to transfer primary selection contents">
|
||||||
|
A wp_primary_selection_offer represents an offer to transfer the contents
|
||||||
|
of the primary selection clipboard to the client. Similar to
|
||||||
|
wl_data_offer, the offer also describes the mime types that the data can
|
||||||
|
be converted to and provides the mechanisms for transferring the data
|
||||||
|
directly to the client.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="receive">
|
||||||
|
<description summary="request that the data is transferred">
|
||||||
|
To transfer the contents of the primary selection clipboard, the client
|
||||||
|
issues this request and indicates the mime type that it wants to
|
||||||
|
receive. The transfer happens through the passed file descriptor
|
||||||
|
(typically created with the pipe system call). The source client writes
|
||||||
|
the data in the mime type representation requested and then closes the
|
||||||
|
file descriptor.
|
||||||
|
|
||||||
|
The receiving client reads from the read end of the pipe until EOF and
|
||||||
|
closes its end, at which point the transfer is complete.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string"/>
|
||||||
|
<arg name="fd" type="fd"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the primary selection offer">
|
||||||
|
Destroy the primary selection offer.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="offer">
|
||||||
|
<description summary="advertise offered mime type">
|
||||||
|
Sent immediately after creating announcing the
|
||||||
|
wp_primary_selection_offer through
|
||||||
|
wp_primary_selection_device.data_offer. One event is sent per offered
|
||||||
|
mime type.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_primary_selection_source_v1" version="1">
|
||||||
|
<description summary="offer to replace the contents of the primary selection">
|
||||||
|
The source side of a wp_primary_selection_offer, it provides a way to
|
||||||
|
describe the offered data and respond to requests to transfer the
|
||||||
|
requested contents of the primary selection clipboard.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="offer">
|
||||||
|
<description summary="add an offered mime type">
|
||||||
|
This request adds a mime type to the set of mime types advertised to
|
||||||
|
targets. Can be called several times to offer multiple types.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the primary selection source">
|
||||||
|
Destroy the primary selection source.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="send">
|
||||||
|
<description summary="send the primary selection contents">
|
||||||
|
Request for the current primary selection contents from the client.
|
||||||
|
Send the specified mime type over the passed file descriptor, then
|
||||||
|
close it.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string"/>
|
||||||
|
<arg name="fd" type="fd"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="cancelled">
|
||||||
|
<description summary="request for primary selection contents was canceled">
|
||||||
|
This primary selection source is no longer valid. The client should
|
||||||
|
clean up and destroy this primary selection source.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
@ -31,6 +31,7 @@ WAYLANDCLIENTSOURCES += \
|
|||||||
../extensions/touch-extension.xml \
|
../extensions/touch-extension.xml \
|
||||||
../extensions/qt-key-unstable-v1.xml \
|
../extensions/qt-key-unstable-v1.xml \
|
||||||
../extensions/qt-windowmanager.xml \
|
../extensions/qt-windowmanager.xml \
|
||||||
|
../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \
|
||||||
../3rdparty/protocol/text-input-unstable-v2.xml \
|
../3rdparty/protocol/text-input-unstable-v2.xml \
|
||||||
../3rdparty/protocol/xdg-output-unstable-v1.xml \
|
../3rdparty/protocol/xdg-output-unstable-v1.xml \
|
||||||
../3rdparty/protocol/wayland.xml
|
../3rdparty/protocol/wayland.xml
|
||||||
@ -118,6 +119,11 @@ qtConfig(wayland-datadevice) {
|
|||||||
qwaylanddatasource.cpp
|
qwaylanddatasource.cpp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qtConfig(wayland-client-primary-selection) {
|
||||||
|
HEADERS += qwaylandprimaryselectionv1_p.h
|
||||||
|
SOURCES += qwaylandprimaryselectionv1.cpp
|
||||||
|
}
|
||||||
|
|
||||||
qtConfig(draganddrop) {
|
qtConfig(draganddrop) {
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
qwaylanddnd_p.h
|
qwaylanddnd_p.h
|
||||||
|
@ -93,6 +93,11 @@
|
|||||||
"condition": "features.draganddrop || features.clipboard",
|
"condition": "features.draganddrop || features.clipboard",
|
||||||
"output": [ "privateFeature" ]
|
"output": [ "privateFeature" ]
|
||||||
},
|
},
|
||||||
|
"wayland-client-primary-selection": {
|
||||||
|
"label": "primary-selection clipboard",
|
||||||
|
"condition": "features.clipboard",
|
||||||
|
"output": [ "privateFeature" ]
|
||||||
|
},
|
||||||
"wayland-client-fullscreen-shell-v1": {
|
"wayland-client-fullscreen-shell-v1": {
|
||||||
"label": "fullscreen-shell-v1",
|
"label": "fullscreen-shell-v1",
|
||||||
"condition": "features.wayland-client",
|
"condition": "features.wayland-client",
|
||||||
|
@ -43,6 +43,9 @@
|
|||||||
#include "qwaylanddataoffer_p.h"
|
#include "qwaylanddataoffer_p.h"
|
||||||
#include "qwaylanddatasource_p.h"
|
#include "qwaylanddatasource_p.h"
|
||||||
#include "qwaylanddatadevice_p.h"
|
#include "qwaylanddatadevice_p.h"
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
#include "qwaylandprimaryselectionv1_p.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -59,44 +62,74 @@ QWaylandClipboard::~QWaylandClipboard()
|
|||||||
|
|
||||||
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
|
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
|
||||||
{
|
{
|
||||||
if (mode != QClipboard::Clipboard)
|
auto *seat = mDisplay->currentInputDevice();
|
||||||
|
if (!seat)
|
||||||
return &m_emptyData;
|
return &m_emptyData;
|
||||||
|
|
||||||
QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
|
switch (mode) {
|
||||||
if (!inputDevice || !inputDevice->dataDevice())
|
case QClipboard::Clipboard:
|
||||||
return &m_emptyData;
|
if (auto *dataDevice = seat->dataDevice()) {
|
||||||
|
if (auto *source = dataDevice->selectionSource())
|
||||||
QWaylandDataSource *source = inputDevice->dataDevice()->selectionSource();
|
|
||||||
if (source) {
|
|
||||||
return source->mimeData();
|
return source->mimeData();
|
||||||
|
if (auto *offer = dataDevice->selectionOffer())
|
||||||
|
return offer->mimeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputDevice->dataDevice()->selectionOffer())
|
|
||||||
return inputDevice->dataDevice()->selectionOffer()->mimeData();
|
|
||||||
|
|
||||||
return &m_emptyData;
|
return &m_emptyData;
|
||||||
|
case QClipboard::Selection:
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
if (auto *selectionDevice = seat->primarySelectionDevice()) {
|
||||||
|
if (auto *source = selectionDevice->selectionSource())
|
||||||
|
return source->mimeData();
|
||||||
|
if (auto *offer = selectionDevice->selectionOffer())
|
||||||
|
return offer->mimeData();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return &m_emptyData;
|
||||||
|
default:
|
||||||
|
return &m_emptyData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
|
void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
|
||||||
{
|
{
|
||||||
if (mode != QClipboard::Clipboard)
|
auto *seat = mDisplay->currentInputDevice();
|
||||||
return;
|
if (!seat)
|
||||||
|
|
||||||
QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
|
|
||||||
if (!inputDevice || !inputDevice->dataDevice())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static const QString plain = QStringLiteral("text/plain");
|
static const QString plain = QStringLiteral("text/plain");
|
||||||
static const QString utf8 = QStringLiteral("text/plain;charset=utf-8");
|
static const QString utf8 = QStringLiteral("text/plain;charset=utf-8");
|
||||||
|
|
||||||
if (data && data->hasFormat(plain) && !data->hasFormat(utf8))
|
if (data && data->hasFormat(plain) && !data->hasFormat(utf8))
|
||||||
data->setData(utf8, data->data(plain));
|
data->setData(utf8, data->data(plain));
|
||||||
inputDevice->dataDevice()->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr);
|
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case QClipboard::Clipboard:
|
||||||
|
if (auto *dataDevice = seat->dataDevice()) {
|
||||||
|
dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr);
|
||||||
emitChanged(mode);
|
emitChanged(mode);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case QClipboard::Selection:
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
if (auto *selectionDevice = seat->primarySelectionDevice()) {
|
||||||
|
selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr);
|
||||||
|
emitChanged(mode);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool QWaylandClipboard::supportsMode(QClipboard::Mode mode) const
|
bool QWaylandClipboard::supportsMode(QClipboard::Mode mode) const
|
||||||
{
|
{
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
if (mode == QClipboard::Selection) {
|
||||||
|
auto *seat = mDisplay->currentInputDevice();
|
||||||
|
return seat && seat->primarySelectionDevice();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return mode == QClipboard::Clipboard;
|
return mode == QClipboard::Clipboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,8 @@ static QString utf8Text()
|
|||||||
|
|
||||||
QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
|
QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
|
||||||
: QtWayland::wl_data_offer(offer)
|
: QtWayland::wl_data_offer(offer)
|
||||||
, m_mimeData(new QWaylandMimeData(this, display))
|
, m_display(display)
|
||||||
|
, m_mimeData(new QWaylandMimeData(this))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,14 +82,19 @@ QMimeData *QWaylandDataOffer::mimeData()
|
|||||||
return m_mimeData.data();
|
return m_mimeData.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd)
|
||||||
|
{
|
||||||
|
receive(mimeType, fd);
|
||||||
|
wl_display_flush(m_display->wl_display());
|
||||||
|
}
|
||||||
|
|
||||||
void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
|
void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
|
||||||
{
|
{
|
||||||
m_mimeData->appendFormat(mime_type);
|
m_mimeData->appendFormat(mime_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
QWaylandMimeData::QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay *display)
|
QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
|
||||||
: m_dataOffer(dataOffer)
|
: m_dataOffer(dataOffer)
|
||||||
, m_display(display)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,8 +146,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dataOffer->receive(mime, pipefd[1]);
|
m_dataOffer->startReceiving(mime, pipefd[1]);
|
||||||
wl_display_flush(m_display->wl_display());
|
|
||||||
|
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
|
|
||||||
|
@ -65,27 +65,40 @@ namespace QtWaylandClient {
|
|||||||
class QWaylandDisplay;
|
class QWaylandDisplay;
|
||||||
class QWaylandMimeData;
|
class QWaylandMimeData;
|
||||||
|
|
||||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer : public QtWayland::wl_data_offer
|
class QWaylandAbstractDataOffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void startReceiving(const QString &mimeType, int fd) = 0;
|
||||||
|
virtual QMimeData *mimeData() = 0;
|
||||||
|
|
||||||
|
virtual ~QWaylandAbstractDataOffer() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer
|
||||||
|
: public QtWayland::wl_data_offer // needs to be the first because we do static casts from the user pointer to the wrapper
|
||||||
|
, public QWaylandAbstractDataOffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer);
|
explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer);
|
||||||
~QWaylandDataOffer() override;
|
~QWaylandDataOffer() override;
|
||||||
|
QMimeData *mimeData() override;
|
||||||
|
|
||||||
QString firstFormat() const;
|
QString firstFormat() const;
|
||||||
|
|
||||||
QMimeData *mimeData();
|
void startReceiving(const QString &mimeType, int fd) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void data_offer_offer(const QString &mime_type) override;
|
void data_offer_offer(const QString &mime_type) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QWaylandDisplay *m_display = nullptr;
|
||||||
QScopedPointer<QWaylandMimeData> m_mimeData;
|
QScopedPointer<QWaylandMimeData> m_mimeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class QWaylandMimeData : public QInternalMimeData {
|
class QWaylandMimeData : public QInternalMimeData {
|
||||||
public:
|
public:
|
||||||
explicit QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay *display);
|
explicit QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer);
|
||||||
~QWaylandMimeData() override;
|
~QWaylandMimeData() override;
|
||||||
|
|
||||||
void appendFormat(const QString &mimeType);
|
void appendFormat(const QString &mimeType);
|
||||||
@ -98,13 +111,12 @@ protected:
|
|||||||
private:
|
private:
|
||||||
int readData(int fd, QByteArray &data) const;
|
int readData(int fd, QByteArray &data) const;
|
||||||
|
|
||||||
mutable QWaylandDataOffer *m_dataOffer = nullptr;
|
QWaylandAbstractDataOffer *m_dataOffer = nullptr;
|
||||||
QWaylandDisplay *m_display = nullptr;
|
|
||||||
mutable QStringList m_types;
|
mutable QStringList m_types;
|
||||||
mutable QHash<QString, QByteArray> m_data;
|
mutable QHash<QString, QByteArray> m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace QtWaylandClient
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
#endif
|
#endif
|
||||||
|
@ -52,7 +52,10 @@
|
|||||||
#if QT_CONFIG(wayland_datadevice)
|
#if QT_CONFIG(wayland_datadevice)
|
||||||
#include "qwaylanddatadevicemanager_p.h"
|
#include "qwaylanddatadevicemanager_p.h"
|
||||||
#include "qwaylanddatadevice_p.h"
|
#include "qwaylanddatadevice_p.h"
|
||||||
#endif
|
#endif // QT_CONFIG(wayland_datadevice)
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
#include "qwaylandprimaryselectionv1_p.h"
|
||||||
|
#endif // QT_CONFIG(wayland_client_primary_selection)
|
||||||
#if QT_CONFIG(cursor)
|
#if QT_CONFIG(cursor)
|
||||||
#include <wayland-cursor.h>
|
#include <wayland-cursor.h>
|
||||||
#endif
|
#endif
|
||||||
@ -69,6 +72,7 @@
|
|||||||
#include "qwaylandqtkey_p.h"
|
#include "qwaylandqtkey_p.h"
|
||||||
|
|
||||||
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
|
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
|
||||||
|
#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
|
||||||
|
|
||||||
#include <QtCore/private/qcore_unix_p.h>
|
#include <QtCore/private/qcore_unix_p.h>
|
||||||
|
|
||||||
@ -318,6 +322,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
|
|||||||
mTouchExtension.reset(new QWaylandTouchExtension(this, id));
|
mTouchExtension.reset(new QWaylandTouchExtension(this, id));
|
||||||
} else if (interface == QStringLiteral("zqt_key_v1")) {
|
} else if (interface == QStringLiteral("zqt_key_v1")) {
|
||||||
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
|
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
} else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
|
||||||
|
mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
|
||||||
|
#endif
|
||||||
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
|
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
|
||||||
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
|
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
|
||||||
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
|
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
|
||||||
|
@ -93,6 +93,9 @@ class QWaylandScreen;
|
|||||||
class QWaylandClientBufferIntegration;
|
class QWaylandClientBufferIntegration;
|
||||||
class QWaylandWindowManagerIntegration;
|
class QWaylandWindowManagerIntegration;
|
||||||
class QWaylandDataDeviceManager;
|
class QWaylandDataDeviceManager;
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
class QWaylandPrimarySelectionDeviceManagerV1;
|
||||||
|
#endif
|
||||||
class QWaylandTouchExtension;
|
class QWaylandTouchExtension;
|
||||||
class QWaylandQtKeyExtension;
|
class QWaylandQtKeyExtension;
|
||||||
class QWaylandWindow;
|
class QWaylandWindow;
|
||||||
@ -149,6 +152,9 @@ public:
|
|||||||
QWaylandInputDevice *currentInputDevice() const { return defaultInputDevice(); }
|
QWaylandInputDevice *currentInputDevice() const { return defaultInputDevice(); }
|
||||||
#if QT_CONFIG(wayland_datadevice)
|
#if QT_CONFIG(wayland_datadevice)
|
||||||
QWaylandDataDeviceManager *dndSelectionHandler() const { return mDndSelectionHandler.data(); }
|
QWaylandDataDeviceManager *dndSelectionHandler() const { return mDndSelectionHandler.data(); }
|
||||||
|
#endif
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const { return mPrimarySelectionManager.data(); }
|
||||||
#endif
|
#endif
|
||||||
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
|
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
|
||||||
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
|
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
|
||||||
@ -238,6 +244,9 @@ private:
|
|||||||
QScopedPointer<QWaylandTouchExtension> mTouchExtension;
|
QScopedPointer<QWaylandTouchExtension> mTouchExtension;
|
||||||
QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension;
|
QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension;
|
||||||
QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration;
|
QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration;
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
|
||||||
|
#endif
|
||||||
QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
|
QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
|
||||||
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
|
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
|
||||||
QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager;
|
QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager;
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
#include "qwaylanddatadevice_p.h"
|
#include "qwaylanddatadevice_p.h"
|
||||||
#include "qwaylanddatadevicemanager_p.h"
|
#include "qwaylanddatadevicemanager_p.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
#include "qwaylandprimaryselectionv1_p.h"
|
||||||
|
#endif
|
||||||
#include "qwaylandtouch_p.h"
|
#include "qwaylandtouch_p.h"
|
||||||
#include "qwaylandscreen_p.h"
|
#include "qwaylandscreen_p.h"
|
||||||
#include "qwaylandcursor_p.h"
|
#include "qwaylandcursor_p.h"
|
||||||
@ -363,6 +366,12 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
// TODO: Could probably decouple this more if there was a signal for new seat added
|
||||||
|
if (auto *psm = mQDisplay->primarySelectionManager())
|
||||||
|
setPrimarySelectionDevice(psm->createDevice(this));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mQDisplay->textInputManager())
|
if (mQDisplay->textInputManager())
|
||||||
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
|
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
|
||||||
|
|
||||||
@ -446,6 +455,18 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
void QWaylandInputDevice::setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice)
|
||||||
|
{
|
||||||
|
mPrimarySelectionDevice.reset(primarySelectionDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionDeviceV1 *QWaylandInputDevice::primarySelectionDevice() const
|
||||||
|
{
|
||||||
|
return mPrimarySelectionDevice.data();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
|
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
|
||||||
{
|
{
|
||||||
mTextInput.reset(textInput);
|
mTextInput.reset(textInput);
|
||||||
@ -1189,6 +1210,10 @@ void QWaylandInputDevice::Keyboard::handleFocusLost()
|
|||||||
#if QT_CONFIG(clipboard)
|
#if QT_CONFIG(clipboard)
|
||||||
if (auto *dataDevice = mParent->dataDevice())
|
if (auto *dataDevice = mParent->dataDevice())
|
||||||
dataDevice->invalidateSelectionOffer();
|
dataDevice->invalidateSelectionOffer();
|
||||||
|
#endif
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
if (auto *device = mParent->primarySelectionDevice())
|
||||||
|
device->invalidateSelectionOffer();
|
||||||
#endif
|
#endif
|
||||||
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
|
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
|
||||||
mRepeatTimer.stop();
|
mRepeatTimer.stop();
|
||||||
|
@ -78,11 +78,17 @@ struct wl_cursor_image;
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace QtWayland {
|
||||||
|
class zwp_primary_selection_device_v1;
|
||||||
|
} //namespace QtWayland
|
||||||
|
|
||||||
namespace QtWaylandClient {
|
namespace QtWaylandClient {
|
||||||
|
|
||||||
class QWaylandWindow;
|
|
||||||
class QWaylandDisplay;
|
|
||||||
class QWaylandDataDevice;
|
class QWaylandDataDevice;
|
||||||
|
class QWaylandDisplay;
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
class QWaylandPrimarySelectionDeviceV1;
|
||||||
|
#endif
|
||||||
class QWaylandTextInput;
|
class QWaylandTextInput;
|
||||||
#if QT_CONFIG(cursor)
|
#if QT_CONFIG(cursor)
|
||||||
class QWaylandCursorTheme;
|
class QWaylandCursorTheme;
|
||||||
@ -116,6 +122,11 @@ public:
|
|||||||
QWaylandDataDevice *dataDevice() const;
|
QWaylandDataDevice *dataDevice() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
void setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice);
|
||||||
|
QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
void setTextInput(QWaylandTextInput *textInput);
|
void setTextInput(QWaylandTextInput *textInput);
|
||||||
QWaylandTextInput *textInput() const;
|
QWaylandTextInput *textInput() const;
|
||||||
|
|
||||||
@ -159,6 +170,10 @@ private:
|
|||||||
QWaylandDataDevice *mDataDevice = nullptr;
|
QWaylandDataDevice *mDataDevice = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if QT_CONFIG(wayland_client_primary_selection)
|
||||||
|
QScopedPointer<QWaylandPrimarySelectionDeviceV1> mPrimarySelectionDevice;
|
||||||
|
#endif
|
||||||
|
|
||||||
Keyboard *mKeyboard = nullptr;
|
Keyboard *mKeyboard = nullptr;
|
||||||
Pointer *mPointer = nullptr;
|
Pointer *mPointer = nullptr;
|
||||||
Touch *mTouch = nullptr;
|
Touch *mTouch = nullptr;
|
||||||
|
162
src/plugins/platforms/wayland/qwaylandprimaryselectionv1.cpp
Normal file
162
src/plugins/platforms/wayland/qwaylandprimaryselectionv1.cpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the plugins of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 2.0 or (at your option) the GNU General
|
||||||
|
** Public license version 3 or any later version approved by the KDE Free
|
||||||
|
** Qt Foundation. The licenses are as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||||
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "qwaylandprimaryselectionv1_p.h"
|
||||||
|
#include "qwaylandinputdevice_p.h"
|
||||||
|
#include "qwaylanddisplay_p.h"
|
||||||
|
#include "qwaylandmimehelper_p.h"
|
||||||
|
|
||||||
|
#include <QtGui/private/qguiapplication_p.h>
|
||||||
|
|
||||||
|
#include <qpa/qplatformclipboard.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace QtWaylandClient {
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version)
|
||||||
|
: zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1)))
|
||||||
|
, m_display(display)
|
||||||
|
{
|
||||||
|
// Create devices for all seats.
|
||||||
|
// This only works if we get the global before all devices
|
||||||
|
const auto seats = m_display->inputDevices();
|
||||||
|
for (auto *seat : seats)
|
||||||
|
seat->setPrimarySelectionDevice(createDevice(seat));
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat)
|
||||||
|
{
|
||||||
|
return new QWaylandPrimarySelectionDeviceV1(this, seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionOfferV1::QWaylandPrimarySelectionOfferV1(QWaylandDisplay *display, ::zwp_primary_selection_offer_v1 *offer)
|
||||||
|
: zwp_primary_selection_offer_v1(offer)
|
||||||
|
, m_display(display)
|
||||||
|
, m_mimeData(new QWaylandMimeData(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void QWaylandPrimarySelectionOfferV1::startReceiving(const QString &mimeType, int fd)
|
||||||
|
{
|
||||||
|
receive(mimeType, fd);
|
||||||
|
wl_display_flush(m_display->wl_display());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandPrimarySelectionOfferV1::zwp_primary_selection_offer_v1_offer(const QString &mime_type)
|
||||||
|
{
|
||||||
|
m_mimeData->appendFormat(mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionDeviceV1::QWaylandPrimarySelectionDeviceV1(
|
||||||
|
QWaylandPrimarySelectionDeviceManagerV1 *manager, QWaylandInputDevice *seat)
|
||||||
|
: QtWayland::zwp_primary_selection_device_v1(manager->get_device(seat->wl_seat()))
|
||||||
|
, m_display(manager->display())
|
||||||
|
, m_seat(seat)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionDeviceV1::~QWaylandPrimarySelectionDeviceV1()
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandPrimarySelectionDeviceV1::setSelectionSource(QWaylandPrimarySelectionSourceV1 *source)
|
||||||
|
{
|
||||||
|
if (source) {
|
||||||
|
connect(source, &QWaylandPrimarySelectionSourceV1::cancelled, this, [this]() {
|
||||||
|
m_selectionSource.reset();
|
||||||
|
QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
set_selection(source ? source->object() : nullptr, m_seat->serial());
|
||||||
|
m_selectionSource.reset(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_data_offer(zwp_primary_selection_offer_v1 *offer)
|
||||||
|
{
|
||||||
|
new QWaylandPrimarySelectionOfferV1(m_display, offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_selection(zwp_primary_selection_offer_v1 *id)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (id)
|
||||||
|
m_selectionOffer.reset(static_cast<QWaylandPrimarySelectionOfferV1 *>(zwp_primary_selection_offer_v1_get_user_data(id)));
|
||||||
|
else
|
||||||
|
m_selectionOffer.reset();
|
||||||
|
|
||||||
|
QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionSourceV1::QWaylandPrimarySelectionSourceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QMimeData *mimeData)
|
||||||
|
: QtWayland::zwp_primary_selection_source_v1(manager->create_source())
|
||||||
|
, m_mimeData(mimeData)
|
||||||
|
{
|
||||||
|
if (!mimeData)
|
||||||
|
return;
|
||||||
|
for (auto &format : mimeData->formats())
|
||||||
|
offer(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWaylandPrimarySelectionSourceV1::~QWaylandPrimarySelectionSourceV1()
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandPrimarySelectionSourceV1::zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd)
|
||||||
|
{
|
||||||
|
QByteArray content = QWaylandMimeHelper::getByteArray(m_mimeData, mime_type);
|
||||||
|
if (!content.isEmpty()) {
|
||||||
|
// Create a sigpipe handler that does nothing, or clients may be forced to terminate
|
||||||
|
// if the pipe is closed in the other end.
|
||||||
|
struct sigaction action, oldAction;
|
||||||
|
action.sa_handler = SIG_IGN;
|
||||||
|
sigemptyset (&action.sa_mask);
|
||||||
|
action.sa_flags = 0;
|
||||||
|
|
||||||
|
sigaction(SIGPIPE, &action, &oldAction);
|
||||||
|
write(fd, content.constData(), size_t(content.size()));
|
||||||
|
sigaction(SIGPIPE, &oldAction, nullptr);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtWaylandClient
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
148
src/plugins/platforms/wayland/qwaylandprimaryselectionv1_p.h
Normal file
148
src/plugins/platforms/wayland/qwaylandprimaryselectionv1_p.h
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the plugins of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 2.0 or (at your option) the GNU General
|
||||||
|
** Public license version 3 or any later version approved by the KDE Free
|
||||||
|
** Qt Foundation. The licenses are as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||||
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QWAYLANDPRIMARYSELECTIONV1_P_H
|
||||||
|
#define QWAYLANDPRIMARYSELECTIONV1_P_H
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
|
||||||
|
|
||||||
|
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
|
||||||
|
#include <QtWaylandClient/private/qwaylanddataoffer_p.h>
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
QT_REQUIRE_CONFIG(wayland_client_primary_selection);
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QMimeData;
|
||||||
|
|
||||||
|
namespace QtWaylandClient {
|
||||||
|
|
||||||
|
class QWaylandInputDevice;
|
||||||
|
class QWaylandPrimarySelectionDeviceV1;
|
||||||
|
|
||||||
|
class QWaylandPrimarySelectionDeviceManagerV1 : public QtWayland::zwp_primary_selection_device_manager_v1
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version);
|
||||||
|
QWaylandPrimarySelectionDeviceV1 *createDevice(QWaylandInputDevice *seat);
|
||||||
|
QWaylandDisplay *display() const { return m_display; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWaylandDisplay *m_display = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QWaylandPrimarySelectionOfferV1 : public QtWayland::zwp_primary_selection_offer_v1, public QWaylandAbstractDataOffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit QWaylandPrimarySelectionOfferV1(QWaylandDisplay *display, ::zwp_primary_selection_offer_v1 *offer);
|
||||||
|
~QWaylandPrimarySelectionOfferV1() override { destroy(); }
|
||||||
|
void startReceiving(const QString &mimeType, int fd) override;
|
||||||
|
QMimeData *mimeData() override { return m_mimeData.data(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_offer_v1_offer(const QString &mime_type) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWaylandDisplay *m_display = nullptr;
|
||||||
|
QScopedPointer<QWaylandMimeData> m_mimeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Q_WAYLAND_CLIENT_EXPORT QWaylandPrimarySelectionSourceV1 : public QObject, public QtWayland::zwp_primary_selection_source_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit QWaylandPrimarySelectionSourceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QMimeData *mimeData);
|
||||||
|
~QWaylandPrimarySelectionSourceV1() override;
|
||||||
|
|
||||||
|
QMimeData *mimeData() const { return m_mimeData; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void cancelled();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd) override;
|
||||||
|
void zwp_primary_selection_source_v1_cancelled() override { emit cancelled(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWaylandDisplay *m_display = nullptr;
|
||||||
|
QMimeData *m_mimeData = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QWaylandPrimarySelectionDeviceV1 : public QObject, public QtWayland::zwp_primary_selection_device_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QWaylandPrimarySelectionDeviceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QWaylandInputDevice *seat);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~QWaylandPrimarySelectionDeviceV1() override;
|
||||||
|
QWaylandPrimarySelectionOfferV1 *selectionOffer() const { return m_selectionOffer.data(); }
|
||||||
|
void invalidateSelectionOffer() { m_selectionOffer.reset(); }
|
||||||
|
QWaylandPrimarySelectionSourceV1 *selectionSource() const { return m_selectionSource.data(); }
|
||||||
|
void setSelectionSource(QWaylandPrimarySelectionSourceV1 *source);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_device_v1_data_offer(struct ::zwp_primary_selection_offer_v1 *offer) override;
|
||||||
|
void zwp_primary_selection_device_v1_selection(struct ::zwp_primary_selection_offer_v1 *id) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWaylandDisplay *m_display = nullptr;
|
||||||
|
QWaylandInputDevice *m_seat = nullptr;
|
||||||
|
QScopedPointer<QWaylandPrimarySelectionOfferV1> m_selectionOffer;
|
||||||
|
QScopedPointer<QWaylandPrimarySelectionSourceV1> m_selectionSource;
|
||||||
|
friend class QWaylandPrimarySelectionDeviceManagerV1;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QtWaylandClient
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QWAYLANDPRIMARYSELECTIONV1_P_H
|
@ -6,6 +6,7 @@ SUBDIRS += \
|
|||||||
fullscreenshellv1 \
|
fullscreenshellv1 \
|
||||||
iviapplication \
|
iviapplication \
|
||||||
output \
|
output \
|
||||||
|
primaryselectionv1 \
|
||||||
seatv4 \
|
seatv4 \
|
||||||
seatv5 \
|
seatv5 \
|
||||||
surface \
|
surface \
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
include (../shared/shared.pri)
|
||||||
|
|
||||||
|
WAYLANDSERVERSOURCES += \
|
||||||
|
$$PWD/../../../../src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml
|
||||||
|
|
||||||
|
TARGET = tst_primaryselectionv1
|
||||||
|
SOURCES += tst_primaryselectionv1.cpp
|
466
tests/auto/wayland/primaryselectionv1/tst_primaryselectionv1.cpp
Normal file
466
tests/auto/wayland/primaryselectionv1/tst_primaryselectionv1.cpp
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the test suite of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "mockcompositor.h"
|
||||||
|
|
||||||
|
#include <qwayland-server-wp-primary-selection-unstable-v1.h>
|
||||||
|
|
||||||
|
#include <QtGui/QRasterWindow>
|
||||||
|
#include <QtGui/QOpenGLWindow>
|
||||||
|
#include <QtGui/QClipboard>
|
||||||
|
#include <QtCore/private/qcore_unix_p.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
using namespace MockCompositor;
|
||||||
|
|
||||||
|
constexpr int primarySelectionVersion = 1; // protocol VERSION, not the name suffix (_v1)
|
||||||
|
|
||||||
|
class PrimarySelectionDeviceV1;
|
||||||
|
class PrimarySelectionDeviceManagerV1;
|
||||||
|
|
||||||
|
class PrimarySelectionOfferV1 : public QObject, public QtWaylandServer::zwp_primary_selection_offer_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PrimarySelectionOfferV1(PrimarySelectionDeviceV1 *device, wl_client *client, int version)
|
||||||
|
: zwp_primary_selection_offer_v1(client, 0, version)
|
||||||
|
, m_device(device)
|
||||||
|
{}
|
||||||
|
void send_offer() = delete;
|
||||||
|
void sendOffer(const QString &offer)
|
||||||
|
{
|
||||||
|
zwp_primary_selection_offer_v1::send_offer(offer);
|
||||||
|
m_mimeTypes << offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimarySelectionDeviceV1 *m_device = nullptr;
|
||||||
|
QStringList m_mimeTypes;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void receive(QString mimeType, int fd);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_offer_v1_destroy_resource(Resource *resource) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void zwp_primary_selection_offer_v1_receive(Resource *resource, const QString &mime_type, int32_t fd) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
QTRY_VERIFY(m_mimeTypes.contains(mime_type));
|
||||||
|
emit receive(mime_type, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zwp_primary_selection_offer_v1_destroy(Resource *resource) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrimarySelectionSourceV1 : public QObject, public QtWaylandServer::zwp_primary_selection_source_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PrimarySelectionSourceV1(wl_client *client, int id, int version)
|
||||||
|
: zwp_primary_selection_source_v1(client, id, version)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
QStringList m_offers;
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_source_v1_destroy_resource(Resource *resource) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
void zwp_primary_selection_source_v1_offer(Resource *resource, const QString &mime_type) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
m_offers << mime_type;
|
||||||
|
}
|
||||||
|
void zwp_primary_selection_source_v1_destroy(Resource *resource) override
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrimarySelectionDeviceV1 : public QObject, public QtWaylandServer::zwp_primary_selection_device_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PrimarySelectionDeviceV1(PrimarySelectionDeviceManagerV1 *manager, Seat *seat)
|
||||||
|
: m_manager(manager)
|
||||||
|
, m_seat(seat)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void send_data_offer(::wl_resource *resource) = delete;
|
||||||
|
|
||||||
|
PrimarySelectionOfferV1 *sendDataOffer(::wl_client *client, const QStringList &mimeTypes = {});
|
||||||
|
|
||||||
|
PrimarySelectionOfferV1 *sendDataOffer(const QStringList &mimeTypes = {}) // creates a new offer for the focused surface and sends it
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_seat->m_capabilities & Seat::capability_keyboard);
|
||||||
|
Q_ASSERT(m_seat->m_keyboard->m_enteredSurface);
|
||||||
|
auto *client = m_seat->m_keyboard->m_enteredSurface->resource()->client();
|
||||||
|
return sendDataOffer(client, mimeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_selection(::wl_resource *resource) = delete;
|
||||||
|
void sendSelection(PrimarySelectionOfferV1 *offer)
|
||||||
|
{
|
||||||
|
auto *client = offer->resource()->client();
|
||||||
|
for (auto *resource : resourceMap().values(client))
|
||||||
|
zwp_primary_selection_device_v1::send_selection(resource->handle, offer->resource()->handle);
|
||||||
|
m_sentSelectionOffers << offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimarySelectionDeviceManagerV1 *m_manager = nullptr;
|
||||||
|
Seat *m_seat = nullptr;
|
||||||
|
QVector<PrimarySelectionOfferV1 *> m_sentSelectionOffers;
|
||||||
|
PrimarySelectionSourceV1 *m_selectionSource = nullptr;
|
||||||
|
uint m_serial = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_device_v1_set_selection(Resource *resource, ::wl_resource *source, uint32_t serial) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
m_selectionSource = fromResource<PrimarySelectionSourceV1>(source);
|
||||||
|
m_serial = serial;
|
||||||
|
}
|
||||||
|
void zwp_primary_selection_device_v1_destroy(Resource *resource) override
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
void zwp_primary_selection_device_v1_destroy_resource(Resource *resource) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrimarySelectionDeviceManagerV1 : public Global, public QtWaylandServer::zwp_primary_selection_device_manager_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PrimarySelectionDeviceManagerV1(CoreCompositor *compositor, int version = 1)
|
||||||
|
: QtWaylandServer::zwp_primary_selection_device_manager_v1(compositor->m_display, version)
|
||||||
|
, m_version(version)
|
||||||
|
{}
|
||||||
|
bool isClean() override
|
||||||
|
{
|
||||||
|
for (auto *device : qAsConst(m_devices)) {
|
||||||
|
// The client should not leak selection offers, i.e. if this fails, there is a missing
|
||||||
|
// zwp_primary_selection_offer_v1.destroy request
|
||||||
|
if (!device->m_sentSelectionOffers.empty())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimarySelectionDeviceV1 *deviceFor(Seat *seat)
|
||||||
|
{
|
||||||
|
Q_ASSERT(seat);
|
||||||
|
if (auto *device = m_devices.value(seat, nullptr))
|
||||||
|
return device;
|
||||||
|
|
||||||
|
auto *device = new PrimarySelectionDeviceV1(this, seat);
|
||||||
|
m_devices[seat] = device;
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_version = 1; // TODO: Remove on libwayland upgrade
|
||||||
|
QMap<Seat *, PrimarySelectionDeviceV1 *> m_devices;
|
||||||
|
QVector<PrimarySelectionSourceV1 *> m_sources;
|
||||||
|
protected:
|
||||||
|
void zwp_primary_selection_device_manager_v1_destroy(Resource *resource) override
|
||||||
|
{
|
||||||
|
// The protocol doesn't say whether managed objects should be destroyed as well,
|
||||||
|
// so leave them alone, they'll be cleaned up in the destructor anyway
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zwp_primary_selection_device_manager_v1_create_source(Resource *resource, uint32_t id) override
|
||||||
|
{
|
||||||
|
int version = m_version;
|
||||||
|
m_sources << new PrimarySelectionSourceV1(resource->client(), id, version);
|
||||||
|
}
|
||||||
|
void zwp_primary_selection_device_manager_v1_get_device(Resource *resource, uint32_t id, ::wl_resource *seatResource) override
|
||||||
|
{
|
||||||
|
auto *seat = fromResource<Seat>(seatResource);
|
||||||
|
QVERIFY(seat);
|
||||||
|
auto *device = deviceFor(seat);
|
||||||
|
device->add(resource->client(), id, resource->version());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PrimarySelectionOfferV1 *PrimarySelectionDeviceV1::sendDataOffer(wl_client *client, const QStringList &mimeTypes)
|
||||||
|
{
|
||||||
|
Q_ASSERT(client);
|
||||||
|
auto *offer = new PrimarySelectionOfferV1(this, client, m_manager->m_version);
|
||||||
|
for (auto *resource : resourceMap().values(client))
|
||||||
|
zwp_primary_selection_device_v1::send_data_offer(resource->handle, offer->resource()->handle);
|
||||||
|
for (const auto &mimeType : mimeTypes)
|
||||||
|
offer->sendOffer(mimeType);
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrimarySelectionOfferV1::zwp_primary_selection_offer_v1_destroy(QtWaylandServer::zwp_primary_selection_offer_v1::Resource *resource)
|
||||||
|
{
|
||||||
|
bool removed = m_device->m_sentSelectionOffers.removeOne(this);
|
||||||
|
QVERIFY(removed);
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrimarySelectionCompositor : public DefaultCompositor {
|
||||||
|
public:
|
||||||
|
explicit PrimarySelectionCompositor()
|
||||||
|
{
|
||||||
|
exec([this] {
|
||||||
|
m_config.autoConfigure = true;
|
||||||
|
add<PrimarySelectionDeviceManagerV1>(primarySelectionVersion);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PrimarySelectionDeviceV1 *primarySelectionDevice(int i = 0) {
|
||||||
|
return get<PrimarySelectionDeviceManagerV1>()->deviceFor(get<Seat>(i));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tst_primaryselectionv1 : public QObject, private PrimarySelectionCompositor
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
|
||||||
|
void initTestCase();
|
||||||
|
void bindsToManager();
|
||||||
|
void createsPrimaryDevice();
|
||||||
|
void createsPrimaryDeviceForNewSeats();
|
||||||
|
void pasteAscii();
|
||||||
|
void pasteUtf8();
|
||||||
|
void destroysPreviousSelection();
|
||||||
|
void copy();
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::initTestCase()
|
||||||
|
{
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(pointer());
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(!pointer()->resourceMap().empty());
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4);
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(keyboard());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::bindsToManager()
|
||||||
|
{
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(get<PrimarySelectionDeviceManagerV1>()->resourceMap().size(), 1);
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(get<PrimarySelectionDeviceManagerV1>()->resourceMap().first()->version(), primarySelectionVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::createsPrimaryDevice()
|
||||||
|
{
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice());
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice()->resourceMap().contains(client()));
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(primarySelectionDevice()->resourceMap().value(client())->version(), primarySelectionVersion);
|
||||||
|
QTRY_VERIFY(QGuiApplication::clipboard()->supportsSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::createsPrimaryDeviceForNewSeats()
|
||||||
|
{
|
||||||
|
exec([=] { add<Seat>(); });
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::pasteAscii()
|
||||||
|
{
|
||||||
|
class Window : public QRasterWindow {
|
||||||
|
public:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
auto *mimeData = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
|
||||||
|
m_formats = mimeData->formats();
|
||||||
|
m_text = QGuiApplication::clipboard()->text(QClipboard::Selection);
|
||||||
|
}
|
||||||
|
QStringList m_formats;
|
||||||
|
QString m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
Window window;
|
||||||
|
window.resize(64, 64);
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
exec([&] {
|
||||||
|
auto *surface = xdgSurface()->m_surface;
|
||||||
|
keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
|
||||||
|
|
||||||
|
auto *device = primarySelectionDevice();
|
||||||
|
auto *offer = device->sendDataOffer({"text/plain"});
|
||||||
|
connect(offer, &PrimarySelectionOfferV1::receive, [](QString mimeType, int fd) {
|
||||||
|
QFile file;
|
||||||
|
file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
|
||||||
|
QCOMPARE(mimeType, "text/plain");
|
||||||
|
file.write(QByteArray("normal ascii"));
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
device->sendSelection(offer);
|
||||||
|
|
||||||
|
pointer()->sendEnter(surface, {32, 32});
|
||||||
|
pointer()->sendButton(client(), BTN_MIDDLE, 1);
|
||||||
|
pointer()->sendButton(client(), BTN_MIDDLE, 0);
|
||||||
|
});
|
||||||
|
QTRY_COMPARE(window.m_formats, QStringList{"text/plain"});
|
||||||
|
QTRY_COMPARE(window.m_text, "normal ascii");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::pasteUtf8()
|
||||||
|
{
|
||||||
|
class Window : public QRasterWindow {
|
||||||
|
public:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
auto *mimeData = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
|
||||||
|
m_formats = mimeData->formats();
|
||||||
|
m_text = QGuiApplication::clipboard()->text(QClipboard::Selection);
|
||||||
|
}
|
||||||
|
QStringList m_formats;
|
||||||
|
QString m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
Window window;
|
||||||
|
window.resize(64, 64);
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
exec([&] {
|
||||||
|
auto *surface = xdgSurface()->m_surface;
|
||||||
|
keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
|
||||||
|
|
||||||
|
auto *device = primarySelectionDevice();
|
||||||
|
auto *offer = device->sendDataOffer({"text/plain", "text/plain;charset=utf-8"});
|
||||||
|
connect(offer, &PrimarySelectionOfferV1::receive, [](QString mimeType, int fd) {
|
||||||
|
QFile file;
|
||||||
|
file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
|
||||||
|
QCOMPARE(mimeType, "text/plain;charset=utf-8");
|
||||||
|
file.write(QByteArray("face with tears of joy: 😂"));
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
device->sendSelection(offer);
|
||||||
|
|
||||||
|
pointer()->sendEnter(surface, {32, 32});
|
||||||
|
pointer()->sendButton(client(), BTN_MIDDLE, 1);
|
||||||
|
pointer()->sendButton(client(), BTN_MIDDLE, 0);
|
||||||
|
});
|
||||||
|
QTRY_COMPARE(window.m_formats, QStringList({"text/plain", "text/plain;charset=utf-8"}));
|
||||||
|
QTRY_COMPARE(window.m_text, "face with tears of joy: 😂");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::destroysPreviousSelection()
|
||||||
|
{
|
||||||
|
QRasterWindow window;
|
||||||
|
window.resize(64, 64);
|
||||||
|
window.show();
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
|
||||||
|
// When the client receives a selection event, it is required to destroy the previous offer
|
||||||
|
exec([&] {
|
||||||
|
auto *surface = xdgSurface()->m_surface;
|
||||||
|
keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
|
||||||
|
|
||||||
|
auto *offer = primarySelectionDevice()->sendDataOffer({"text/plain"});
|
||||||
|
primarySelectionDevice()->sendSelection(offer);
|
||||||
|
});
|
||||||
|
|
||||||
|
exec([&] {
|
||||||
|
auto *offer = primarySelectionDevice()->sendDataOffer({"text/plain"});
|
||||||
|
primarySelectionDevice()->sendSelection(offer);
|
||||||
|
QCOMPARE(primarySelectionDevice()->m_sentSelectionOffers.size(), 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify the first offer gets destroyed
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(primarySelectionDevice()->m_sentSelectionOffers.size(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_primaryselectionv1::copy()
|
||||||
|
{
|
||||||
|
class Window : public QRasterWindow {
|
||||||
|
public:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
QGuiApplication::clipboard()->setText("face with tears of joy: 😂", QClipboard::Selection);
|
||||||
|
}
|
||||||
|
QStringList m_formats;
|
||||||
|
QString m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
Window window;
|
||||||
|
window.resize(64, 64);
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
QVector<uint> mouseSerials;
|
||||||
|
exec([&] {
|
||||||
|
auto *surface = xdgSurface()->m_surface;
|
||||||
|
keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
|
||||||
|
pointer()->sendEnter(surface, {32, 32});
|
||||||
|
mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 1);
|
||||||
|
mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 0);
|
||||||
|
});
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice()->m_selectionSource);
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(mouseSerials.contains(primarySelectionDevice()->m_serial));
|
||||||
|
QByteArray pastedBuf;
|
||||||
|
exec([&](){
|
||||||
|
auto *source = primarySelectionDevice()->m_selectionSource;
|
||||||
|
QCOMPARE(source->m_offers, QStringList({"text/plain", "text/plain;charset=utf-8"}));
|
||||||
|
int fd[2];
|
||||||
|
if (pipe(fd) == -1)
|
||||||
|
QSKIP("Failed to create pipe");
|
||||||
|
fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
source->send_send("text/plain;charset=utf-8", fd[1]);
|
||||||
|
auto *notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
|
||||||
|
connect(notifier, &QSocketNotifier::activated, this, [&](int fd) {
|
||||||
|
exec([&]{
|
||||||
|
static char buf[1024];
|
||||||
|
int n = QT_READ(fd, buf, sizeof buf);
|
||||||
|
if (n <= 0) {
|
||||||
|
delete notifier;
|
||||||
|
close(fd);
|
||||||
|
} else {
|
||||||
|
pastedBuf.append(buf, n);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(pastedBuf.size()); // this assumes we got everything in one read
|
||||||
|
auto pasted = QString::fromUtf8(pastedBuf);
|
||||||
|
QCOMPARE(pasted, "face with tears of joy: 😂");
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPOSITOR_TEST_MAIN(tst_primaryselectionv1)
|
||||||
|
#include "tst_primaryselectionv1.moc"
|
@ -124,6 +124,23 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the nth global with the given type, if any
|
||||||
|
*/
|
||||||
|
template<typename global_type>
|
||||||
|
global_type *get(int index)
|
||||||
|
{
|
||||||
|
warnIfNotLockedByThread(Q_FUNC_INFO);
|
||||||
|
for (auto *global : qAsConst(m_globals)) {
|
||||||
|
if (auto *casted = qobject_cast<global_type *>(global)) {
|
||||||
|
if (index--)
|
||||||
|
continue;
|
||||||
|
return casted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns all globals with the given type, if any
|
* \brief Returns all globals with the given type, if any
|
||||||
*/
|
*/
|
||||||
|
@ -378,6 +378,7 @@ uint Keyboard::sendEnter(Surface *surface)
|
|||||||
const auto pointerResources = resourceMap().values(client);
|
const auto pointerResources = resourceMap().values(client);
|
||||||
for (auto *r : pointerResources)
|
for (auto *r : pointerResources)
|
||||||
send_enter(r->handle, serial, surface->resource()->handle, QByteArray());
|
send_enter(r->handle, serial, surface->resource()->handle, QByteArray());
|
||||||
|
m_enteredSurface = surface;
|
||||||
return serial;
|
return serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,6 +389,7 @@ uint Keyboard::sendLeave(Surface *surface)
|
|||||||
const auto pointerResources = resourceMap().values(client);
|
const auto pointerResources = resourceMap().values(client);
|
||||||
for (auto *r : pointerResources)
|
for (auto *r : pointerResources)
|
||||||
send_leave(r->handle, serial, surface->resource()->handle);
|
send_leave(r->handle, serial, surface->resource()->handle);
|
||||||
|
m_enteredSurface = nullptr;
|
||||||
return serial;
|
return serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ class Seat : public Global, public QtWaylandServer::wl_seat
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit Seat(CoreCompositor *compositor, uint capabilities, int version = 4);
|
explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard, int version = 4);
|
||||||
~Seat() override;
|
~Seat() override;
|
||||||
void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead
|
void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead
|
||||||
void send_capabilities(uint capabilities) = delete; // Use wrapper instead
|
void send_capabilities(uint capabilities) = delete; // Use wrapper instead
|
||||||
@ -317,6 +317,7 @@ public:
|
|||||||
uint sendLeave(Surface *surface);
|
uint sendLeave(Surface *surface);
|
||||||
uint sendKey(wl_client *client, uint key, uint state);
|
uint sendKey(wl_client *client, uint key, uint state);
|
||||||
Seat *m_seat = nullptr;
|
Seat *m_seat = nullptr;
|
||||||
|
Surface *m_enteredSurface = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Shm : public Global, public QtWaylandServer::wl_shm
|
class Shm : public Global, public QtWaylandServer::wl_shm
|
||||||
|
@ -36,10 +36,16 @@
|
|||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
#ifndef BTN_LEFT
|
|
||||||
// As defined in linux/input-event-codes.h
|
// As defined in linux/input-event-codes.h
|
||||||
|
#ifndef BTN_LEFT
|
||||||
#define BTN_LEFT 0x110
|
#define BTN_LEFT 0x110
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef BTN_RIGHT
|
||||||
|
#define BTN_RIGHT 0x111
|
||||||
|
#endif
|
||||||
|
#ifndef BTN_MIDDLE
|
||||||
|
#define BTN_MIDDLE 0x112
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MockCompositor {
|
namespace MockCompositor {
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user