Commit 2776f829 authored by Roman Gilg's avatar Roman Gilg

Remove X clipboard sync helper and rename its autotest

Summary:
With the gneric X selections infrastructure and clipboard support the X
clipboard sync helper utility can be removed. Also rename its autotest as it
tests the inner workings of the new mechanism since this mechanism was
introduced.

Test Plan: Autotest still passes under new name.

Reviewers: #kwin, davidedmundson

Reviewed By: #kwin, davidedmundson

Subscribers: graesslin, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D15063
parent 6e08fb2f
......@@ -37,7 +37,7 @@ integrationTest(WAYLAND_ONLY NAME testPlasmaSurface SRCS plasma_surface_test.cpp
integrationTest(WAYLAND_ONLY NAME testMaximized SRCS maximize_test.cpp)
integrationTest(WAYLAND_ONLY NAME testShellClient SRCS shell_client_test.cpp)
integrationTest(WAYLAND_ONLY NAME testDontCrashNoBorder SRCS dont_crash_no_border.cpp)
integrationTest(NAME testXClipboardSync SRCS xclipboardsync_test.cpp)
integrationTest(NAME testXwaylandSelections SRCS xwayland_selections_test.cpp)
integrationTest(WAYLAND_ONLY NAME testSceneOpenGL SRCS scene_opengl_test.cpp generic_scene_opengl_test.cpp)
integrationTest(WAYLAND_ONLY NAME testSceneOpenGLShadow SRCS scene_opengl_shadow_test.cpp)
integrationTest(WAYLAND_ONLY NAME testSceneOpenGLES SRCS scene_opengl_es_test.cpp generic_scene_opengl_test.cpp)
......
......@@ -2,7 +2,8 @@
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
Copyright 2019 Roman Gilg <subdiff@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -32,9 +33,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_xclipboard_sync-0");
static const QString s_socketName = QStringLiteral("wayland_test_kwin_xwayland_selections-0");
class XClipboardSyncTest : public QObject
class XwaylandSelectionsTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
......@@ -48,7 +49,7 @@ private:
QProcess *m_pasteProcess = nullptr;
};
void XClipboardSyncTest::initTestCase()
void XwaylandSelectionsTest::initTestCase()
{
QSKIP("Skipped as it fails for unknown reasons on build.kde.org");
qRegisterMetaType<KWin::ShellClient*>();
......@@ -58,8 +59,8 @@ void XClipboardSyncTest::initTestCase()
QVERIFY(workspaceCreatedSpy.isValid());
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
QSignalSpy clipboardSyncDevicedCreated{waylandServer(), &WaylandServer::xclipboardSyncDataDeviceCreated};
QVERIFY(clipboardSyncDevicedCreated.isValid());
// QSignalSpy clipboardSyncDevicedCreated{waylandServer(), &WaylandServer::xclipboardSyncDataDeviceCreated};
// QVERIFY(clipboardSyncDevicedCreated.isValid());
QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit()));
kwinApp()->start();
......@@ -79,7 +80,7 @@ void XClipboardSyncTest::initTestCase()
QVERIFY(Xwl::DataBridge::self()->dataDeviceIface() != nullptr);
}
void XClipboardSyncTest::cleanup()
void XwaylandSelectionsTest::cleanup()
{
if (m_copyProcess) {
m_copyProcess->terminate();
......@@ -93,7 +94,7 @@ void XClipboardSyncTest::cleanup()
}
}
void XClipboardSyncTest::testSync_data()
void XwaylandSelectionsTest::testSync_data()
{
QTest::addColumn<QString>("copyPlatform");
QTest::addColumn<QString>("pastePlatform");
......@@ -102,7 +103,7 @@ void XClipboardSyncTest::testSync_data()
QTest::newRow("wayland->x11") << QStringLiteral("wayland") << QStringLiteral("xcb");
}
void XClipboardSyncTest::testSync()
void XwaylandSelectionsTest::testSync()
{
// this test verifies the syncing of X11 to Wayland clipboard
const QString copy = QFINDTESTDATA(QStringLiteral("copy"));
......@@ -192,5 +193,5 @@ void XClipboardSyncTest::testSync()
m_copyProcess = nullptr;
}
WAYLANDTEST_MAIN(XClipboardSyncTest)
#include "xclipboardsync_test.moc"
WAYLANDTEST_MAIN(XwaylandSelectionsTest)
#include "xwayland_selections_test.moc"
add_subdirectory(killer)
add_subdirectory(xclipboardsync)
set(xclipboard_SRCS main.cpp waylandclipboard.cpp)
add_executable(org_kde_kwin_xclipboard_syncer ${xclipboard_SRCS})
target_link_libraries(org_kde_kwin_xclipboard_syncer Qt5::Gui KF5::WaylandClient KF5::Crash)
install(TARGETS org_kde_kwin_xclipboard_syncer DESTINATION ${LIBEXEC_INSTALL_DIR} )
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "waylandclipboard.h"
#include <KCrash>
#include <QGuiApplication>
#include <config-kwin.h>
#if HAVE_PR_SET_PDEATHSIG
#include <sys/prctl.h>
#include <signal.h>
#endif
int main(int argc, char *argv[])
{
#if HAVE_PR_SET_PDEATHSIG
prctl(PR_SET_PDEATHSIG, SIGTERM);
#endif
qputenv("QT_QPA_PLATFORM", "xcb");
QGuiApplication app(argc, argv);
// perform sanity checks
if (app.platformName().toLower() != QStringLiteral("xcb")) {
fprintf(stderr, "%s: FATAL ERROR expecting platform xcb but got platform %s\n",
argv[0], qPrintable(app.platformName()));
return 1;
}
KCrash::initialize();
new WaylandClipboard(&app);
return app.exec();
}
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "waylandclipboard.h"
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/datadevicemanager.h>
#include <KWayland/Client/datadevice.h>
#include <KWayland/Client/datasource.h>
#include <KWayland/Client/event_queue.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/registry.h>
#include <QClipboard>
#include <QFile>
#include <QGuiApplication>
#include <QThread>
#include <QMimeData>
#include <QMimeType>
#include <qplatformdefs.h>
#include <errno.h>
#include <unistd.h>
using namespace KWayland::Client;
WaylandClipboard::WaylandClipboard(QObject *parent)
: QObject(parent)
, m_thread(new QThread)
, m_connectionThread(new ConnectionThread)
{
m_connectionThread->setSocketFd(qEnvironmentVariableIntValue("WAYLAND_SOCKET"));
m_connectionThread->moveToThread(m_thread);
m_thread->start();
connect(m_connectionThread, &ConnectionThread::connected, this, &WaylandClipboard::setup, Qt::QueuedConnection);
m_connectionThread->initConnection();
connect(qApp->clipboard(), &QClipboard::changed, this,
[this] (QClipboard::Mode mode) {
if (mode != QClipboard::Clipboard) {
return;
}
// TODO: do we need to take a copy of the clipboard in order to keep it after the X application quit?
if (!m_dataDeviceManager || !m_dataDevice) {
return;
}
auto source = m_dataDeviceManager->createDataSource(this);
auto mimeData = qApp->clipboard()->mimeData();
const auto formats = mimeData->formats();
for (const auto &format : formats) {
source->offer(format);
}
connect(source, &DataSource::sendDataRequested, this,
[] (const QString &type, qint32 fd) {
auto mimeData = qApp->clipboard()->mimeData();
if (!mimeData->hasFormat(type)) {
close(fd);
return;
}
const auto data = mimeData->data(type);
QFile writePipe;
if (writePipe.open(fd, QIODevice::WriteOnly, QFile::AutoCloseHandle)) {
writePipe.write(data);
writePipe.close();
} else {
close(fd);
}
}
);
m_dataDevice->setSelection(0, source);
delete m_dataSource;
m_dataSource = source;
m_connectionThread->flush();
}
);
}
WaylandClipboard::~WaylandClipboard()
{
m_connectionThread->deleteLater();
m_thread->quit();
m_thread->wait();
}
static int readData(int fd, QByteArray &data)
{
// implementation based on QtWayland file qwaylanddataoffer.cpp
char buf[4096];
int retryCount = 0;
int n;
while (true) {
n = QT_READ(fd, buf, sizeof buf);
if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && ++retryCount < 1000) {
usleep(1000);
} else {
break;
}
}
if (n > 0) {
data.append(buf, n);
n = readData(fd, data);
}
return n;
}
void WaylandClipboard::setup()
{
EventQueue *queue = new EventQueue(this);
queue->setup(m_connectionThread);
Registry *registry = new Registry(this);
registry->setEventQueue(queue);
registry->create(m_connectionThread);
connect(registry, &Registry::interfacesAnnounced, this,
[this, registry] {
const auto seatInterface = registry->interface(Registry::Interface::Seat);
if (seatInterface.name != 0) {
m_seat = registry->createSeat(seatInterface.name, seatInterface.version, this);
}
const auto ddmInterface = registry->interface(Registry::Interface::DataDeviceManager);
if (ddmInterface.name != 0) {
m_dataDeviceManager = registry->createDataDeviceManager(ddmInterface.name, ddmInterface.version, this);
}
if (m_seat && m_dataDeviceManager) {
m_dataDevice = m_dataDeviceManager->getDataDevice(m_seat, this);
connect(m_dataDevice, &DataDevice::selectionOffered, this,
[this] (DataOffer *offer) {
if (offer->offeredMimeTypes().isEmpty()) {
return;
}
int pipeFds[2];
if (pipe(pipeFds) != 0) {
return;
}
const auto mimeType = offer->offeredMimeTypes().first();
offer->receive(mimeType, pipeFds[1]);
m_connectionThread->flush();
close(pipeFds[1]);
QByteArray content;
if (readData(pipeFds[0], content) != 0) {
content = QByteArray();
}
close(pipeFds[0]);
QMimeData *mimeData = new QMimeData();
mimeData->setData(mimeType.name(), content);
qApp->clipboard()->setMimeData(mimeData);
}
);
connect(m_dataDevice, &DataDevice::selectionCleared, this,
[this] {
qApp->clipboard()->clear();
}
);
}
}
);
registry->setup();
}
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef WAYLANDCLIPBOARD_H
#define WAYLANDCLIPBOARD_H
#include <QObject>
class QThread;
namespace KWayland
{
namespace Client
{
class ConnectionThread;
class Seat;
class DataDeviceManager;
class DataDevice;
class DataSource;
}
}
class WaylandClipboard : public QObject
{
Q_OBJECT
public:
explicit WaylandClipboard(QObject *parent);
~WaylandClipboard();
private:
void setup();
QThread *m_thread;
KWayland::Client::ConnectionThread *m_connectionThread;
KWayland::Client::Seat *m_seat = nullptr;
KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr;
KWayland::Client::DataDevice *m_dataDevice = nullptr;
KWayland::Client::DataSource *m_dataSource = nullptr;
};
#endif
......@@ -184,19 +184,6 @@ void KeyboardInputRedirection::update()
if (found && found->surface()) {
if (found->surface() != seat->focusedKeyboardSurface()) {
seat->setFocusedKeyboardSurface(found->surface());
auto newKeyboard = seat->focusedKeyboard();
if (newKeyboard && newKeyboard->client() == waylandServer()->xWaylandConnection()) {
// focus passed to an XWayland surface
const auto selection = seat->selection();
auto xclipboard = waylandServer()->xclipboardSyncDataDevice();
if (xclipboard && selection != xclipboard.data()) {
if (selection) {
xclipboard->sendSelection(selection);
} else {
xclipboard->sendClearSelection();
}
}
}
}
} else {
seat->setFocusedKeyboardSurface(nullptr);
......
......@@ -92,7 +92,6 @@ WaylandServer::WaylandServer(QObject *parent)
qRegisterMetaType<KWayland::Server::OutputInterface::DpmsMode>();
connect(kwinApp(), &Application::screensCreated, this, &WaylandServer::initOutputs);
// connect(kwinApp(), &Application::x11ConnectionChanged, this, &WaylandServer::setupX11ClipboardSync);
}
WaylandServer::~WaylandServer()
......@@ -260,24 +259,6 @@ bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags)
m_display->createPointerConstraints(PointerConstraintsInterfaceVersion::UnstableV1, m_display)->create();
m_dataDeviceManager = m_display->createDataDeviceManager(m_display);
m_dataDeviceManager->create();
connect(m_dataDeviceManager, &DataDeviceManagerInterface::dataDeviceCreated, this,
[this] (DataDeviceInterface *ddi) {
if (ddi->client() == m_xclipbaordSync.client && m_xclipbaordSync.client != nullptr) {
m_xclipbaordSync.ddi = QPointer<DataDeviceInterface>(ddi);
emit xclipboardSyncDataDeviceCreated();
connect(m_xclipbaordSync.ddi.data(), &DataDeviceInterface::selectionChanged, this,
[this] {
// testing whether the active client inherits Client
// it would be better to test for the keyboard focus, but we might get a clipboard update
// when the Client is already active, but no Surface is created yet.
if (workspace()->activeClient() && workspace()->activeClient()->inherits("KWin::Client")) {
m_seat->setSelection(m_xclipbaordSync.ddi.data());
}
}
);
}
}
);
m_idle = m_display->createIdle(m_display);
m_idle->create();
auto idleInhibition = new IdleInhibition(m_idle);
......@@ -531,10 +512,6 @@ void WaylandServer::destroyXWaylandConnection()
if (!m_xwayland.client) {
return;
}
// first terminate the clipboard sync
if (m_xclipbaordSync.process) {
m_xclipbaordSync.process->terminate();
}
disconnect(m_xwayland.destroyConnection);
m_xwayland.client->destroy();
m_xwayland.client = nullptr;
......@@ -559,62 +536,6 @@ void WaylandServer::destroyInputMethodConnection()
m_inputMethodServerConnection = nullptr;
}
int WaylandServer::createXclipboardSyncConnection()
{
const auto socket = createConnection();
if (!socket.connection) {
return -1;
}
m_xclipbaordSync.client = socket.connection;
return socket.fd;
}
void WaylandServer::setupX11ClipboardSync()
{
if (m_xclipbaordSync.process) {
qCWarning(KWIN_CORE) << "Tried to start x clipboard syncer although process already started";
return;
}
int socket = dup(createXclipboardSyncConnection());
if (socket == -1) {
delete m_xclipbaordSync.client;
m_xclipbaordSync.client = nullptr;
qCWarning(KWIN_CORE) << "Could not create wayland socket for x clipboard syncer";
return;
}
if (socket >= 0) {
QProcessEnvironment environment = kwinApp()->processStartupEnvironment();
environment.insert(QStringLiteral("WAYLAND_SOCKET"), QByteArray::number(socket));
environment.insert(QStringLiteral("DISPLAY"), QString::fromUtf8(qgetenv("DISPLAY")));
environment.remove("WAYLAND_DISPLAY");
m_xclipbaordSync.process = new Process(this);
m_xclipbaordSync.process->setProcessChannelMode(QProcess::ForwardedChannels);
auto finishedSignal = static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished);
connect(m_xclipbaordSync.process, finishedSignal, this,
[this] {
qCDebug(KWIN_CORE) << "X clipboard syncer process finished";
m_xclipbaordSync.process->deleteLater();
m_xclipbaordSync.process = nullptr;
m_xclipbaordSync.ddi.clear();
m_xclipbaordSync.client->destroy();
m_xclipbaordSync.client = nullptr;
// TODO: restart
}
);
m_xclipbaordSync.process->setProcessEnvironment(environment);
// start from build directory if executable is available there (e.g. autotests), otherwise start libexec executable
const QFileInfo clipboardSync{QDir{QCoreApplication::applicationDirPath()}, QStringLiteral("org_kde_kwin_xclipboard_syncer")};
if (clipboardSync.exists()) {
qCDebug(KWIN_CORE) << "Starting" << clipboardSync.absoluteFilePath();
m_xclipbaordSync.process->start(clipboardSync.absoluteFilePath());
} else {
qCDebug(KWIN_CORE) << "Starting" << KWIN_XCLIPBOARD_SYNC_BIN;
m_xclipbaordSync.process->start(QStringLiteral(KWIN_XCLIPBOARD_SYNC_BIN));
}
}
}
void WaylandServer::createInternalConnection()
{
const auto socket = createConnection();
......
......@@ -23,7 +23,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kwinglobals.h>
#include <QObject>
#include <QPointer>
class QThread;
class QProcess;
......@@ -150,8 +149,6 @@ public:
int createInputMethodConnection();
void destroyInputMethodConnection();
int createXclipboardSyncConnection();
/**
* @returns true if screen is locked.
**/
......@@ -181,9 +178,6 @@ public:
KWayland::Server::ClientConnection *screenLockerClientConnection() const {
return m_screenLockerClientConnection;
}
QPointer<KWayland::Server::DataDeviceInterface> xclipboardSyncDataDevice() const {
return m_xclipbaordSync.ddi;
}
KWayland::Client::Seat *internalSeat() {
return m_internalConnection.seat;
}
......@@ -229,10 +223,8 @@ Q_SIGNALS:
void terminatingInternalClientConnection();
void initialized();
void foreignTransientChanged(KWayland::Server::SurfaceInterface *child);
void xclipboardSyncDataDeviceCreated();
private:
void setupX11ClipboardSync();
void shellClientShown(Toplevel *t);
void initOutputs();
void syncOutputsToWayland();
......@@ -277,11 +269,6 @@ private:
bool interfacesAnnounced = false;
} m_internalConnection;
struct {
QProcess *process = nullptr;
KWayland::Server::ClientConnection *client = nullptr;
QPointer<KWayland::Server::DataDeviceInterface> ddi;
} m_xclipbaordSync;
KWayland::Server::XdgForeignInterface *m_XdgForeign = nullptr;
QList<ShellClient*> m_clients;
QList<ShellClient*> m_internalClients;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment