Skip to content

Commit

Permalink
Wayland: Update code from kguiaddons
Browse files Browse the repository at this point in the history
  • Loading branch information
hluk committed Jan 3, 2024
1 parent 53509f2 commit 6ac1384
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 10 deletions.
5 changes: 5 additions & 0 deletions src/platform/x11/systemclipboard/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ if(WITH_QT6)
qt_generate_wayland_protocol_client_sources(systemclipboard
FILES
"${CMAKE_CURRENT_SOURCE_DIR}/wlr-data-control-unstable-v1.xml"
"${Wayland_DATADIR}/wayland.xml"
)
else()
find_package(QtWaylandScanner REQUIRED)
ecm_add_qtwayland_client_protocol(systemclipboard_SRCS
PROTOCOL wlr-data-control-unstable-v1.xml
BASENAME wlr-data-control-unstable-v1
)
ecm_add_qtwayland_client_protocol(systemclipboard_SRCS
PROTOCOL ${Wayland_DATADIR}/wayland.xml
BASENAME wayland
)
add_library(systemclipboard STATIC ${systemclipboard_SRCS})
endif()

Expand Down
127 changes: 117 additions & 10 deletions src/platform/x11/systemclipboard/waylandclipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
#include <QImageWriter>
#include <QMimeData>
#include <QThread>
#include <QtWaylandClient/QWaylandClientExtension>
#include <QWaylandClientExtension>
#include <QWindow>
#include <QtWaylandClientVersion>
#include <qpa/qplatformnativeinterface.h>
#include <qtwaylandclientversion.h>

#include <errno.h>
#include <fcntl.h>
Expand All @@ -27,6 +28,7 @@
#include <string.h>
#include <unistd.h>

#include "qwayland-wayland.h"
#include "qwayland-wlr-data-control-unstable-v1.h"

static inline QString applicationQtXImageLiteral()
Expand Down Expand Up @@ -337,20 +339,20 @@ class DataControlSource : public QObject, public QtWayland::zwlr_data_control_so
Q_OBJECT
public:
DataControlSource(struct ::zwlr_data_control_source_v1 *id, QMimeData *mimeData);

DataControlSource() = default;
~DataControlSource()
{
if (m_mimeData) {
m_mimeData->deleteLater();
m_mimeData = nullptr;
}
if ( isInitialized() )
destroy();
}

QMimeData *mimeData()
{
return m_mimeData;
return m_mimeData.get();
}
std::unique_ptr<QMimeData> releaseMimeData()
{
return std::move(m_mimeData);
}

bool isCancelled() const { return m_cancelled; }
Expand All @@ -363,7 +365,7 @@ class DataControlSource : public QObject, public QtWayland::zwlr_data_control_so
void zwlr_data_control_source_v1_cancelled() override;

private:
QMimeData *m_mimeData = nullptr;
std::unique_ptr<QMimeData> m_mimeData;
bool m_cancelled = false;
};

Expand Down Expand Up @@ -520,6 +522,7 @@ class DataControlDevice : public QObject, public QtWayland::zwlr_data_control_de

std::unique_ptr<DataControlSource> m_primarySelection; // selection set locally
std::unique_ptr<DataControlOffer> m_receivedPrimarySelection; // latest selection set from externally to here
friend WaylandClipboard;
};

void DataControlDevice::setSelection(std::unique_ptr<DataControlSource> selection)
Expand All @@ -546,9 +549,82 @@ void DataControlDevice::setPrimarySelection(std::unique_ptr<DataControlSource> s
Q_EMIT primarySelectionChanged();
}
}
class Keyboard;
// We are binding to Seat/Keyboard manually because we want to react to gaining focus but inside Qt the events are Qt and arrive to late
class KeyboardFocusWatcher : public QWaylandClientExtensionTemplate<KeyboardFocusWatcher>, public QtWayland::wl_seat
{
Q_OBJECT
public:
KeyboardFocusWatcher()
: QWaylandClientExtensionTemplate(5)
{
#if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
initialize();
#else
QMetaObject::invokeMethod(this, "addRegistryListener");
#endif
auto native = qGuiApp->platformNativeInterface();
auto display = static_cast<struct ::wl_display *>(native->nativeResourceForIntegration("wl_display"));
// so we get capabilities
wl_display_roundtrip(display);
}
~KeyboardFocusWatcher() override
{
if (isActive()) {
release();
}
}
void seat_capabilities(uint32_t capabilities) override
{
const bool hasKeyboard = capabilities & capability_keyboard;
if (hasKeyboard && !m_keyboard) {
m_keyboard = std::make_unique<Keyboard>(get_keyboard(), *this);
} else if (!hasKeyboard && m_keyboard) {
m_keyboard.reset();
}
}
bool hasFocus() const
{
return m_focus;
}
Q_SIGNALS:
void keyboardEntered();

private:
friend Keyboard;
bool m_focus = false;
std::unique_ptr<Keyboard> m_keyboard;
};

class Keyboard : public QtWayland::wl_keyboard
{
public:
Keyboard(::wl_keyboard *keyboard, KeyboardFocusWatcher &seat)
: wl_keyboard(keyboard)
, m_seat(seat)
{
}
~Keyboard()
{
release();
}

private:
void keyboard_enter([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface, [[maybe_unused]] wl_array *keys) override
{
m_seat.m_focus = true;
Q_EMIT m_seat.keyboardEntered();
}
void keyboard_leave([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface) override
{
m_seat.m_focus = false;
}
KeyboardFocusWatcher &m_seat;
};

WaylandClipboard::WaylandClipboard(QObject *parent)
: QObject(parent)
, m_keyboardFocusWatcher(new KeyboardFocusWatcher)
, m_manager(new DataControlDeviceManager)
{
connect(m_manager.get(), &DataControlDeviceManager::activeChanged, this, [this]() {
Expand All @@ -561,7 +637,6 @@ WaylandClipboard::WaylandClipboard(QObject *parent)
if (!seat) {
return;
}

m_device.reset(new DataControlDevice(m_manager->get_data_device(seat)));

connect(m_device.get(), &DataControlDevice::receivedSelectionChanged, this, [this]() {
Expand Down Expand Up @@ -616,6 +691,20 @@ void WaylandClipboard::setMimeData(QMimeData *mime, QClipboard::Mode mode)
if (!m_device) {
return;
}

// roundtrip to have accurate focus state when losing focus but setting mime data before processing wayland events.
auto native = qGuiApp->platformNativeInterface();
auto display = static_cast<struct ::wl_display *>(native->nativeResourceForIntegration("wl_display"));
wl_display_roundtrip(display);

// If the application is focused, use the normal mechanism so a future paste will not deadlock itselfs
if (m_keyboardFocusWatcher->hasFocus()) {
QGuiApplication::clipboard()->setMimeData(mime, mode);
return;
}
// If not, set the clipboard once the app receives focus to avoid the deadlock
connect(m_keyboardFocusWatcher.get(), &KeyboardFocusWatcher::keyboardEntered, this, &WaylandClipboard::gainedFocus, Qt::UniqueConnection);

std::unique_ptr<DataControlSource> source(new DataControlSource(m_manager->create_data_source(), mime));
if (mode == QClipboard::Clipboard) {
m_device->setSelection(std::move(source));
Expand All @@ -624,16 +713,34 @@ void WaylandClipboard::setMimeData(QMimeData *mime, QClipboard::Mode mode)
}
}

void WaylandClipboard::gainedFocus()
{
disconnect(m_keyboardFocusWatcher.get(), &KeyboardFocusWatcher::keyboardEntered, this, nullptr);
// QClipboard takes ownership of the QMimeData so we need to transfer and unset our selections
if (auto &selection = m_device->m_selection) {
std::unique_ptr<QMimeData> data = selection->releaseMimeData();
WaylandClipboard::clear(QClipboard::Clipboard);
QGuiApplication::clipboard()->setMimeData(data.release(), QClipboard::Clipboard);
}
if (auto &primarySelection = m_device->m_primarySelection) {
std::unique_ptr<QMimeData> data = primarySelection->releaseMimeData();
WaylandClipboard::clear(QClipboard::Selection);
QGuiApplication::clipboard()->setMimeData(data.release(), QClipboard::Selection);
}
}

void WaylandClipboard::clear(QClipboard::Mode mode)
{
if (!m_device) {
return;
}
if (mode == QClipboard::Clipboard) {
m_device->set_selection(nullptr);
m_device->m_selection.reset();
} else if (mode == QClipboard::Selection) {
if (zwlr_data_control_device_v1_get_version(m_device->object()) >= ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION) {
m_device->set_primary_selection(nullptr);
m_device->m_primarySelection.reset();
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/platform/x11/systemclipboard/waylandclipboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

class DataControlDevice;
class DataControlDeviceManager;
class KeyboardFocusWatcher;
class QMimeData;

class WaylandClipboard final : public QObject
Expand All @@ -35,6 +36,8 @@ class WaylandClipboard final : public QObject
explicit WaylandClipboard(QObject *parent);
static WaylandClipboard *createInstance();

void gainedFocus();
std::unique_ptr<KeyboardFocusWatcher> m_keyboardFocusWatcher;
std::unique_ptr<DataControlDeviceManager> m_manager;
std::unique_ptr<DataControlDevice> m_device;
};

0 comments on commit 6ac1384

Please sign in to comment.