diff --git a/.gitignore b/.gitignore index a4fb4fb1259d0ad25a8cb9e10cda46a64d51d2e0..3975cf26dfd44c6e66f835586a8fdb8a111c2f6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ build/ .cache/ + +# Arch Linux stuff +PKGBUILD +pkg/ diff --git a/CMakeLists.txt b/CMakeLists.txt index aeef691fb15925916a69c1ba36fdd4d59acb43f6..58f849507f24a7d94904bc18e3186d212bbfb1ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -724,7 +724,8 @@ set(kwin_XWAYLAND_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/xwl/drag.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/drag_wl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/drag_x.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/xwl/selection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/xwl/primary_selection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/xwl/selection.h ${CMAKE_CURRENT_SOURCE_DIR}/xwl/selection_source.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/transfer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/xwayland.cpp diff --git a/atoms.cpp b/atoms.cpp index 84e8f2b974dc4727c555b16babc998a63248ba25..02293af38e5c4ff95da7fab9753b4b7c8d023a88 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -79,6 +79,7 @@ Atoms::Atoms() , delete_atom(QByteArrayLiteral("DELETE")) , incr(QByteArrayLiteral("INCR")) , wl_selection(QByteArrayLiteral("WL_SELECTION")) + , primary_selection(QByteArrayLiteral("PRIMARY")) , xwayland_randr_emu_monitor_rects(QByteArrayLiteral("_XWAYLAND_RANDR_EMU_MONITOR_RECTS")) , m_dtSmWindowInfo(QByteArrayLiteral("_DT_SM_WINDOW_INFO")) , m_motifSupport(QByteArrayLiteral("_MOTIF_WM_INFO")) diff --git a/atoms.h b/atoms.h index 693498cd599e5825282c57d0cd172db1defe2cc4..f275292f0b75919814dda1ab2ec8552baf3863aa 100644 --- a/atoms.h +++ b/atoms.h @@ -88,6 +88,7 @@ public: Xcb::Atom delete_atom; Xcb::Atom incr; Xcb::Atom wl_selection; + Xcb::Atom primary_selection; Xcb::Atom xwayland_randr_emu_monitor_rects; /** diff --git a/autotests/integration/helper/copy.cpp b/autotests/integration/helper/copy.cpp index 9fa0af59dcb8d43e4bd010281c4d60631f175393..f98fe1606b0ad19bb0b750638a93676cd96b9101 100644 --- a/autotests/integration/helper/copy.cpp +++ b/autotests/integration/helper/copy.cpp @@ -27,16 +27,20 @@ class Window : public QRasterWindow { Q_OBJECT public: - explicit Window(); + explicit Window(QClipboard::Mode mode); ~Window() override; protected: void paintEvent(QPaintEvent *event) override; void focusInEvent(QFocusEvent *event) override; + +private: + QClipboard::Mode m_mode; }; -Window::Window() +Window::Window(QClipboard::Mode mode) : QRasterWindow() + , m_mode(mode) { } @@ -53,15 +57,20 @@ void Window::focusInEvent(QFocusEvent *event) { QRasterWindow::focusInEvent(event); // TODO: make it work without singleshot - QTimer::singleShot(100,[] { - qApp->clipboard()->setText(QStringLiteral("test")); + QTimer::singleShot(100,[this] { + qApp->clipboard()->setText(QStringLiteral("test"), m_mode); }); } int main(int argc, char *argv[]) { + QClipboard::Mode mode = QClipboard::Clipboard; + if (argv && !strcmp(argv[argc-1], "Selection")) { + mode = QClipboard::Selection; + } + QGuiApplication app(argc, argv); - std::unique_ptr<Window> w(new Window); + std::unique_ptr<Window> w(new Window(mode)); w->setGeometry(QRect(0, 0, 100, 200)); w->show(); diff --git a/autotests/integration/helper/paste.cpp b/autotests/integration/helper/paste.cpp index 1246b1e17e30c02fbfccfe5405cbe98a13e143af..524d283c117d396b913111ca27e563f92d3c34de 100644 --- a/autotests/integration/helper/paste.cpp +++ b/autotests/integration/helper/paste.cpp @@ -50,10 +50,15 @@ void Window::paintEvent(QPaintEvent *event) int main(int argc, char *argv[]) { + QClipboard::Mode mode = QClipboard::Clipboard; + if (argv && !strcmp(argv[argc-1], "Selection")) { + mode = QClipboard::Selection; + } + QGuiApplication app(argc, argv); QObject::connect(app.clipboard(), &QClipboard::changed, &app, - [] { - if (qApp->clipboard()->text() == QLatin1String("test")) { + [mode] { + if (qApp->clipboard()->text(mode) == QLatin1String("test")) { QTimer::singleShot(100, qApp, &QCoreApplication::quit); } } diff --git a/autotests/integration/xwayland_selections_test.cpp b/autotests/integration/xwayland_selections_test.cpp index 2826f3ab2932cc0c2bfc4da13cf92dde36d209da..f448ba4d3d8a379582b505b776ad59f1e091cfc1 100644 --- a/autotests/integration/xwayland_selections_test.cpp +++ b/autotests/integration/xwayland_selections_test.cpp @@ -29,6 +29,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "win/x11/window.h" #include <Wrapland/Server/data_device.h> +#include <Wrapland/Server/data_device_manager.h> +#include <Wrapland/Server/data_source.h> +#include <Wrapland/Server/primary_selection.h> #include <QProcess> #include <QProcessEnvironment> @@ -56,6 +59,8 @@ void XwaylandSelectionsTest::initTestCase() qRegisterMetaType<win::wayland::window*>(); qRegisterMetaType<win::x11::window*>(); qRegisterMetaType<QProcess::ExitStatus>(); + qRegisterMetaType<Wrapland::Server::DataDevice*>(); + qRegisterMetaType<Wrapland::Server::DataSource*>(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); @@ -98,15 +103,34 @@ void XwaylandSelectionsTest::cleanup() void XwaylandSelectionsTest::testSync_data() { + QTest::addColumn<QString>("clipboardMode"); QTest::addColumn<QString>("copyPlatform"); QTest::addColumn<QString>("pastePlatform"); - QTest::newRow("x11->wayland") << QStringLiteral("xcb") << QStringLiteral("wayland"); - QTest::newRow("wayland->x11") << QStringLiteral("wayland") << QStringLiteral("xcb"); + QTest::newRow("Clipboard x11->wayland") << QStringLiteral("Clipboard") + << QStringLiteral("xcb") + << QStringLiteral("wayland"); + QTest::newRow("Clipboard wayland->x11") << QStringLiteral("Clipboard") + << QStringLiteral("wayland") + << QStringLiteral("xcb"); + QTest::newRow("primary_selection x11->wayland") << QStringLiteral("Selection") + << QStringLiteral("xcb") + << QStringLiteral("wayland"); + QTest::newRow("primary_selection wayland->x11") << QStringLiteral("Selection") + << QStringLiteral("wayland") + << QStringLiteral("xcb"); } void XwaylandSelectionsTest::testSync() { + QFETCH(QString, clipboardMode); + if (clipboardMode == "Clipboard") { + QVERIFY(Xwl::DataBridge::self()->dataDeviceIface() != nullptr); + } + if (clipboardMode == "Selection"){ + QVERIFY(Xwl::DataBridge::self()->primarySelectionDeviceIface() != nullptr); + } + // this test verifies the syncing of X11 to Wayland clipboard const QString copy = QFINDTESTDATA(QStringLiteral("copy")); QVERIFY(!copy.isEmpty()); @@ -117,7 +141,18 @@ void XwaylandSelectionsTest::testSync() QVERIFY(clientAddedSpy.isValid()); QSignalSpy shellClientAddedSpy(waylandServer(), &WaylandServer::window_added); QVERIFY(shellClientAddedSpy.isValid()); - QSignalSpy clipboardChangedSpy(Xwl::DataBridge::self()->dataDeviceIface(), &Wrapland::Server::DataDevice::selectionChanged); + + QSignalSpy clipboardChangedSpy = [clipboardMode]() { + if (clipboardMode == "Clipboard") { + return QSignalSpy(Xwl::DataBridge::self()->dataDeviceIface(), + &Wrapland::Server::DataDevice::selectionChanged); + } + if (clipboardMode == "Selection") { + return QSignalSpy(Xwl::DataBridge::self()->primarySelectionDeviceIface(), + &Wrapland::Server::PrimarySelectionDevice::selectionChanged); + } + throw; + }(); QVERIFY(clipboardChangedSpy.isValid()); QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); @@ -130,6 +165,7 @@ void XwaylandSelectionsTest::testSync() m_copyProcess->setProcessEnvironment(environment); m_copyProcess->setProcessChannelMode(QProcess::ForwardedChannels); m_copyProcess->setProgram(copy); + m_copyProcess->setArguments({clipboardMode}); m_copyProcess->start(); QVERIFY(m_copyProcess->waitForStarted()); @@ -165,6 +201,7 @@ void XwaylandSelectionsTest::testSync() m_pasteProcess->setProcessEnvironment(environment); m_pasteProcess->setProcessChannelMode(QProcess::ForwardedChannels); m_pasteProcess->setProgram(paste); + m_pasteProcess->setArguments({clipboardMode}); m_pasteProcess->start(); QVERIFY(m_pasteProcess->waitForStarted()); diff --git a/tooling/analysis/clang-format.sh b/tooling/analysis/clang-format.sh index 85cc484f9a8ecd6e0fb021248f2837cad5e1952d..1d75757e020749abb96a3355f4422ebeda06a995 100755 --- a/tooling/analysis/clang-format.sh +++ b/tooling/analysis/clang-format.sh @@ -14,4 +14,5 @@ python <(curl -s $RUN_SCRIPT_URL) -r \ ${SOURCE_DIR}/render \ ${SOURCE_DIR}/rules \ ${SOURCE_DIR}/seat \ - ${SOURCE_DIR}/cmake + ${SOURCE_DIR}/cmake \ + ${SOURCE_DIR}/xwl diff --git a/wayland_server.cpp b/wayland_server.cpp index 65357e945700d8318eac10d5540280bdb5f8654e..abec85160e2f1cfbea844ca93b2fc7185ef708b3 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -38,6 +38,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <Wrapland/Client/compositor.h> #include <Wrapland/Client/seat.h> #include <Wrapland/Client/datadevicemanager.h> +#include <Wrapland/Client/primary_selection.h> #include <Wrapland/Client/shm_pool.h> #include <Wrapland/Client/surface.h> // Server @@ -59,6 +60,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <Wrapland/Server/pointer_constraints_v1.h> #include <Wrapland/Server/pointer_gestures_v1.h> #include <Wrapland/Server/presentation_time.h> +#include <Wrapland/Server/primary_selection.h> #include <Wrapland/Server/seat.h> #include <Wrapland/Server/server_decoration_palette.h> #include <Wrapland/Server/shadow.h> @@ -214,6 +216,7 @@ void WaylandServer::destroyInternalConnection() delete m_internalConnection.compositor; delete m_internalConnection.seat; delete m_internalConnection.ddm; + delete m_internalConnection.psdm; delete m_internalConnection.shm; dispatch(); delete m_internalConnection.queue; @@ -350,7 +353,7 @@ bool WaylandServer::init(InitializationFlags flags) m_display->createPointerGestures(m_display); m_display->createPointerConstraints(m_display); m_dataDeviceManager = m_display->createDataDeviceManager(m_display); - + m_primarySelectionDeviceManager = m_display->createPrimarySelectionDeviceManager(m_display); m_idle = m_display->createIdle(m_display); auto idleInhibition = new IdleInhibition(m_idle); @@ -746,6 +749,10 @@ void WaylandServer::createInternalConnection() if (ddmInterface.name != 0) { m_internalConnection.ddm = registry->createDataDeviceManager(ddmInterface.name, ddmInterface.version, this); } + const auto psdmInterface = registry->interface(Registry::Interface::PrimarySelectionDeviceManager); + if (psdmInterface.name != 0) { + m_internalConnection.psdm = registry->createPrimarySelectionDeviceManager(psdmInterface.name, psdmInterface.version, this); + } } ); registry->setup(); diff --git a/wayland_server.h b/wayland_server.h index 43b385ba741c5208271aaf356ef297dd2a591932..9726f6c844adc0540516e5cfd14db297e83f767a 100644 --- a/wayland_server.h +++ b/wayland_server.h @@ -39,6 +39,7 @@ class Registry; class Compositor; class Seat; class DataDeviceManager; +class PrimarySelectionDeviceManager; class ShmPool; class Surface; } @@ -60,6 +61,7 @@ class PlasmaShellSurface; class PlasmaVirtualDesktopManager; class PlasmaWindowManager; class PresentationManager; +class PrimarySelectionDeviceManager; class QtSurfaceExtension; class OutputManagementV1; class OutputConfigurationV1; @@ -126,6 +128,9 @@ public: Wrapland::Server::DataDeviceManager *dataDeviceManager() { return m_dataDeviceManager; } + Wrapland::Server::PrimarySelectionDeviceManager *primarySelectionDeviceManager() const { + return m_primarySelectionDeviceManager; + } Wrapland::Server::PlasmaVirtualDesktopManager *virtualDesktopManagement() { return m_virtualDesktopManagement; } @@ -204,6 +209,9 @@ public: Wrapland::Client::DataDeviceManager *internalDataDeviceManager() { return m_internalConnection.ddm; } + Wrapland::Client::PrimarySelectionDeviceManager *internalPrimarySelectionDeviceManager() { + return m_internalConnection.psdm; + } Wrapland::Client::ShmPool *internalShmPool() { return m_internalConnection.shm; } @@ -276,6 +284,7 @@ private: Wrapland::Server::PlasmaWindowManager *m_windowManagement = nullptr; Wrapland::Server::PlasmaVirtualDesktopManager *m_virtualDesktopManagement = nullptr; Wrapland::Server::PresentationManager *m_presentationManager = nullptr; + Wrapland::Server::PrimarySelectionDeviceManager *m_primarySelectionDeviceManager = nullptr; Wrapland::Server::OutputManagementV1 *m_outputManagement = nullptr; Wrapland::Server::AppmenuManager *m_appmenuManager = nullptr; Wrapland::Server::ServerSideDecorationPaletteManager *m_paletteManager = nullptr; @@ -299,6 +308,7 @@ private: Wrapland::Client::EventQueue *queue = nullptr; Wrapland::Client::Seat *seat = nullptr; Wrapland::Client::DataDeviceManager *ddm = nullptr; + Wrapland::Client::PrimarySelectionDeviceManager *psdm = nullptr; Wrapland::Client::ShmPool *shm = nullptr; bool interfacesAnnounced = false; diff --git a/xwl/clipboard.cpp b/xwl/clipboard.cpp index a6412ff403a3bdb4800f75c12248f82dea709508..a61ce26c71b54044200cdb74909b2a81933b5b06 100644 --- a/xwl/clipboard.cpp +++ b/xwl/clipboard.cpp @@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. *********************************************************************/ #include "clipboard.h" -#include "databridge.h" #include "selection_source.h" #include "transfer.h" #include "xwayland.h" @@ -29,7 +28,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "win/x11/window.h" -#include <Wrapland/Client/connection_thread.h> #include <Wrapland/Client/datadevice.h> #include <Wrapland/Client/datasource.h> @@ -47,146 +45,31 @@ namespace KWin namespace Xwl { -Clipboard::Clipboard(xcb_atom_t atom, QObject *parent) - : Selection(atom, parent) +Clipboard::Clipboard(xcb_atom_t atom, srv_data_device* srv_dev, clt_data_device* clt_dev) { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - - const uint32_t clipboardValues[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_PROPERTY_CHANGE }; - xcb_create_window(xcbConn, - XCB_COPY_FROM_PARENT, - window(), - kwinApp()->x11RootWindow(), - 0, 0, - 10, 10, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - Xwayland::self()->xcbScreen()->root_visual, - XCB_CW_EVENT_MASK, - clipboardValues); - registerXfixes(); - xcb_flush(xcbConn); - - connect(waylandServer()->seat(), &Wrapland::Server::Seat::selectionChanged, - this, &Clipboard::wlSelectionChanged); -} + data = create_selection_data(atom, srv_dev, clt_dev); -void Clipboard::wlSelectionChanged(Wrapland::Server::DataDevice *ddi) -{ - if (ddi && ddi != DataBridge::self()->dataDeviceIface()) { - // Wayland native client provides new selection - if (!m_checkConnection) { - m_checkConnection = connect(workspace(), &Workspace::clientActivated, - this, [this](Toplevel* ac) { - Q_UNUSED(ac); - checkWlSource(); - }); - } - // remove previous source so checkWlSource() can create a new one - setWlSource(nullptr); - } - checkWlSource(); + register_x11_selection(this, QSize(10, 10)); + + QObject::connect(waylandServer()->seat(), + &Wrapland::Server::Seat::selectionChanged, + data.qobject.get(), + [this] { handle_wl_selection_change(this); }); } -void Clipboard::checkWlSource() +Clipboard::srv_data_device* Clipboard::get_current_device() const { - auto ddi = waylandServer()->seat()->selection(); - auto removeSource = [this] { - if (wlSource()) { - setWlSource(nullptr); - ownSelection(false); - } - }; - - // Wayland source gets created when: - // - the Wl selection exists, - // - its source is not Xwayland, - // - a client is active, - // - this client is an Xwayland one. - // - // Otherwise the Wayland source gets destroyed to shield - // against snooping X clients. - - if (!ddi || DataBridge::self()->dataDeviceIface() == ddi) { - // Xwayland source or no source - disconnect(m_checkConnection); - m_checkConnection = QMetaObject::Connection(); - removeSource(); - return; - } - if (!workspace()->activeClient() || !workspace()->activeClient()->inherits("KWin::win::x11::window")) { - // no active client or active client is Wayland native - removeSource(); - return; - } - // Xwayland client is active and we need a Wayland source - if (wlSource()) { - // source already exists, nothing more to do - return; - } - auto *wls = new WlSource(this, ddi); - setWlSource(wls); - auto *dsi = ddi->selection(); - if (dsi) { - wls->setDataSourceIface(dsi); - } - connect(ddi, &Wrapland::Server::DataDevice::selectionChanged, - wls, &WlSource::setDataSourceIface); - ownSelection(true); + return waylandServer()->seat()->selection(); } -void Clipboard::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) +Wrapland::Client::DataDeviceManager* Clipboard::get_internal_device_manager() const { - createX11Source(nullptr); - - auto const& client = workspace()->activeClient(); - if (!qobject_cast<win::x11::window const*>(client)) { - // clipboard is only allowed to be acquired when Xwayland has focus - // TODO: can we make this stronger (window id comparison)? - return; - } - - createX11Source(event); - - if (X11Source *source = x11Source()) { - source->getTargets(); - } + return waylandServer()->internalDataDeviceManager(); } -void Clipboard::x11OffersChanged(const QStringList &added, const QStringList &removed) +std::function<void(Clipboard::srv_data_device*)> Clipboard::get_selection_setter() const { - X11Source *source = x11Source(); - if (!source) { - return; - } - - const Mimes offers = source->offers(); - - if (!offers.isEmpty()) { - if (!source->dataSource() || !removed.isEmpty()) { - // create new Wl DataSource if there is none or when types - // were removed (Wl Data Sources can only add types) - Wrapland::Client::DataDeviceManager *dataDeviceManager = - waylandServer()->internalDataDeviceManager(); - Wrapland::Client::DataSource *dataSource = - dataDeviceManager->createDataSource(source); - - // also offers directly the currently available types - source->setDataSource(dataSource); - DataBridge::self()->dataDevice()->setSelection(0, dataSource); - waylandServer()->seat()->setSelection(DataBridge::self()->dataDeviceIface()); - } else if (auto *dataSource = source->dataSource()) { - for (const QString &mime : added) { - dataSource->offer(mime); - } - } - } else { - waylandServer()->seat()->setSelection(nullptr); - } - - waylandServer()->internalClientConection()->flush(); - waylandServer()->dispatch(); + return [](srv_data_device* dev) { waylandServer()->seat()->setSelection(dev); }; } } // namespace Xwl diff --git a/xwl/clipboard.h b/xwl/clipboard.h index 6d4340c18490afa86cf1c80adc1ff59bf288b036..4bc395afcf88ee69a803470b6ad00ab13550b7e6 100644 --- a/xwl/clipboard.h +++ b/xwl/clipboard.h @@ -22,49 +22,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "selection.h" -namespace Wrapland -{ -namespace Server -{ -class DataDevice; -} -} +#include <Wrapland/Client/datadevice.h> +#include <Wrapland/Client/datasource.h> +#include <Wrapland/Server/data_device.h> +#include <Wrapland/Server/data_source.h> -namespace KWin -{ -namespace Xwl +#include <functional> + +namespace KWin::Xwl { +class Clipboard; /** * Represents the X clipboard, which is on Wayland side just called * @e selection. */ -class Clipboard : public Selection +class Clipboard { - Q_OBJECT - public: - Clipboard(xcb_atom_t atom, QObject *parent); + using srv_data_device = Wrapland::Server::DataDevice; + using clt_data_device = Wrapland::Client::DataDevice; + using srv_data_source = srv_data_device::source_t; + using clt_source_t = clt_data_device::source_t; -private: - void doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) override; - void x11OffersChanged(const QStringList &added, const QStringList &removed) override; - /** - * React to Wl selection change. - */ - void wlSelectionChanged(Wrapland::Server::DataDevice *ddi); - /** - * Check the current state of the selection and if a source needs - * to be created or destroyed. - */ - void checkWlSource(); + selection_data<srv_data_device, clt_data_device> data; + QMetaObject::Connection source_check_connection; + + Clipboard(xcb_atom_t atom, srv_data_device* srv_dev, clt_data_device* clt_dev); - QMetaObject::Connection m_checkConnection; + srv_data_device* get_current_device() const; + Wrapland::Client::DataDeviceManager* get_internal_device_manager() const; + std::function<void(srv_data_device*)> get_selection_setter() const; +private: Q_DISABLE_COPY(Clipboard) }; -} // namespace Xwl -} // namespace KWin +} #endif diff --git a/xwl/databridge.cpp b/xwl/databridge.cpp index 1a003f704763a05dcd471c30086081e6c3f77708..8403d065edd4e5992cd72ef57ebaf668ec23f8d5 100644 --- a/xwl/databridge.cpp +++ b/xwl/databridge.cpp @@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "databridge.h" #include "clipboard.h" #include "dnd.h" +#include "primary_selection.h" #include "selection.h" #include "xwayland.h" @@ -29,10 +30,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "workspace.h" #include <Wrapland/Client/datadevicemanager.h> +#include <Wrapland/Client/primary_selection.h> #include <Wrapland/Client/seat.h> -#include <Wrapland/Server/data_device_manager.h> #include <Wrapland/Server/data_device.h> +#include <Wrapland/Server/data_device_manager.h> +#include <Wrapland/Server/primary_selection.h> #include <Wrapland/Server/seat.h> using namespace Wrapland::Client; @@ -42,40 +45,68 @@ namespace KWin namespace Xwl { -static DataBridge *s_self = nullptr; +static DataBridge* s_self = nullptr; -DataBridge *DataBridge::self() +DataBridge* DataBridge::self() { return s_self; } -DataBridge::DataBridge(QObject *parent) +DataBridge::DataBridge(QObject* parent) : QObject(parent) { s_self = this; - auto dataDeviceManager = waylandServer()->internalDataDeviceManager(); - auto seat = waylandServer()->internalSeat(); - m_dataDevice = dataDeviceManager->getDataDevice(seat, this); + m_dataDevice = waylandServer()->internalDataDeviceManager()->getDevice( + waylandServer()->internalSeat(), this); + m_primarySelectionDevice = waylandServer()->internalPrimarySelectionDeviceManager()->getDevice( + waylandServer()->internalSeat(), this); + waylandServer()->dispatch(); - auto dataDeviceManagerInterface = waylandServer()->dataDeviceManager(); - - auto *dc = new QMetaObject::Connection(); - *dc = connect(dataDeviceManagerInterface, &Wrapland::Server::DataDeviceManager::dataDeviceCreated, this, - [this, dc](Wrapland::Server::DataDevice *dataDeviceInterface) { - if (m_dataDeviceInterface) { - return; - } - if (dataDeviceInterface->client() != waylandServer()->internalConnection()) { - return; - } - QObject::disconnect(*dc); - delete dc; - m_dataDeviceInterface = dataDeviceInterface; - init(); - } - ); + auto* dc = new QMetaObject::Connection(); + *dc = connect(waylandServer()->dataDeviceManager(), + &Wrapland::Server::DataDeviceManager::deviceCreated, + this, + [this, dc](auto srv_dev) { + if (srv_dev->client() != waylandServer()->internalConnection()) { + return; + } + + QObject::disconnect(*dc); + delete dc; + + assert(!m_dataDeviceInterface); + m_dataDeviceInterface = srv_dev; + + assert(!m_clipboard); + assert(!m_dnd); + m_clipboard.reset(new Clipboard(atoms->clipboard, srv_dev, m_dataDevice)); + m_dnd.reset(new Dnd(atoms->xdnd_selection, srv_dev, m_dataDevice)); + + waylandServer()->dispatch(); + }); + + auto* pc = new QMetaObject::Connection(); + *pc = connect(waylandServer()->primarySelectionDeviceManager(), + &Wrapland::Server::PrimarySelectionDeviceManager::deviceCreated, + this, + [this, pc](auto srv_dev) { + if (srv_dev->client() != waylandServer()->internalConnection()) { + return; + } + + QObject::disconnect(*pc); + delete pc; + + assert(!m_primarySelectionDeviceInterface); + m_primarySelectionDeviceInterface = srv_dev; + + assert(!m_primarySelection); + m_primarySelection.reset(new primary_selection( + atoms->primary_selection, srv_dev, m_primarySelectionDevice)); + waylandServer()->dispatch(); + }); } DataBridge::~DataBridge() @@ -83,45 +114,39 @@ DataBridge::~DataBridge() s_self = nullptr; } -void DataBridge::init() -{ - m_clipboard = new Clipboard(atoms->clipboard, this); - m_dnd = new Dnd(atoms->xdnd_selection, this); - waylandServer()->dispatch(); -} - -bool DataBridge::filterEvent(xcb_generic_event_t *event) +bool DataBridge::filterEvent(xcb_generic_event_t* event) { - if (m_clipboard->filterEvent(event)) { + if (filter_event(m_clipboard.get(), event)) { return true; } - if (m_dnd->filterEvent(event)) { + if (filter_event(m_dnd.get(), event)) { return true; } - if (event->response_type - Xwayland::self()->xfixes()->first_event == XCB_XFIXES_SELECTION_NOTIFY) { - return handleXfixesNotify((xcb_xfixes_selection_notify_event_t *)event); + if (filter_event(m_primarySelection.get(), event)) { + return true; + } + if (event->response_type - Xwayland::self()->xfixes()->first_event + == XCB_XFIXES_SELECTION_NOTIFY) { + return handleXfixesNotify((xcb_xfixes_selection_notify_event_t*)event); } return false; } -bool DataBridge::handleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) +bool DataBridge::handleXfixesNotify(xcb_xfixes_selection_notify_event_t* event) { - Selection *selection = nullptr; - if (event->selection == atoms->clipboard) { - selection = m_clipboard; - } else if (event->selection == atoms->xdnd_selection) { - selection = m_dnd; + return handle_xfixes_notify(m_clipboard.get(), event); } - - if (!selection) { - return false; + if (event->selection == atoms->primary_selection) { + return handle_xfixes_notify(m_primarySelection.get(), event); } - - return selection->handleXfixesNotify(event); + if (event->selection == atoms->xdnd_selection) { + return handle_xfixes_notify(m_dnd.get(), event); + } + return false; } -DragEventReply DataBridge::dragMoveFilter(Toplevel *target, const QPoint &pos) +DragEventReply DataBridge::dragMoveFilter(Toplevel* target, const QPoint& pos) { if (!m_dnd) { return DragEventReply::Wayland; diff --git a/xwl/databridge.h b/xwl/databridge.h index 6fef522492a0cc8aa570e05179c922e110050d50..d0fc7f7c7b7dbba6daef7360b9ffde092b7f5f30 100644 --- a/xwl/databridge.h +++ b/xwl/databridge.h @@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <QObject> #include <QPoint> +#include <memory> #include <xcb/xcb.h> struct xcb_xfixes_selection_notify_event_t; @@ -34,10 +35,12 @@ namespace Wrapland namespace Client { class DataDevice; +class PrimarySelectionDevice; } namespace Server { class DataDevice; +class PrimarySelectionDevice; class Surface; } } @@ -52,6 +55,7 @@ class Xwayland; class Clipboard; class Dnd; enum class DragEventReply; +class primary_selection; /** * Interface class for all data sharing in the context of X selections @@ -64,38 +68,47 @@ class KWIN_EXPORT DataBridge : public QObject Q_OBJECT public: - static DataBridge *self(); + static DataBridge* self(); - explicit DataBridge(QObject *parent = nullptr); + explicit DataBridge(QObject* parent = nullptr); ~DataBridge() override; - bool filterEvent(xcb_generic_event_t *event); - DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos); + bool filterEvent(xcb_generic_event_t* event); + DragEventReply dragMoveFilter(Toplevel* target, const QPoint& pos); - Wrapland::Client::DataDevice *dataDevice() const + Wrapland::Client::DataDevice* dataDevice() const { return m_dataDevice; } - Wrapland::Server::DataDevice *dataDeviceIface() const + Wrapland::Server::DataDevice* dataDeviceIface() const { return m_dataDeviceInterface; } - Dnd *dnd() const + Dnd* dnd() const { - return m_dnd; + return m_dnd.get(); + } + Wrapland::Client::PrimarySelectionDevice* primarySelectionDevice() const + { + return m_primarySelectionDevice; + } + Wrapland::Server::PrimarySelectionDevice* primarySelectionDeviceIface() const + { + return m_primarySelectionDeviceInterface; } private: - void init(); - - bool handleXfixesNotify(xcb_xfixes_selection_notify_event_t *event); + bool handleXfixesNotify(xcb_xfixes_selection_notify_event_t* event); - Clipboard *m_clipboard = nullptr; - Dnd *m_dnd = nullptr; + std::unique_ptr<Clipboard> m_clipboard; + std::unique_ptr<Dnd> m_dnd; + std::unique_ptr<primary_selection> m_primarySelection; /* Internal data device interface */ - Wrapland::Client::DataDevice *m_dataDevice = nullptr; - Wrapland::Server::DataDevice *m_dataDeviceInterface = nullptr; + Wrapland::Client::DataDevice* m_dataDevice = nullptr; + Wrapland::Server::DataDevice* m_dataDeviceInterface = nullptr; + Wrapland::Client::PrimarySelectionDevice* m_primarySelectionDevice = nullptr; + Wrapland::Server::PrimarySelectionDevice* m_primarySelectionDeviceInterface = nullptr; Q_DISABLE_COPY(DataBridge) }; diff --git a/xwl/dnd.cpp b/xwl/dnd.cpp index 7e4d40e340b44244aa66530fac0d6034ab9ae371..31a565ca8e3faa773ecb116e7c2c698447a287bf 100644 --- a/xwl/dnd.cpp +++ b/xwl/dnd.cpp @@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. *********************************************************************/ #include "dnd.h" -#include "databridge.h" #include "drag_wl.h" #include "drag_x.h" #include "selection_source.h" @@ -47,98 +46,22 @@ namespace KWin namespace Xwl { -// version of DnD support in X -const static uint32_t s_version = 5; -uint32_t Dnd::version() -{ - return s_version; -} - -Dnd::Dnd(xcb_atom_t atom, QObject *parent) - : Selection(atom, parent) +template<> +void do_handle_xfixes_notify(Dnd* sel, xcb_xfixes_selection_notify_event_t* event) { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - - const uint32_t dndValues[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_PROPERTY_CHANGE }; - xcb_create_window(xcbConn, - XCB_COPY_FROM_PARENT, - window(), - kwinApp()->x11RootWindow(), - 0, 0, - 8192, 8192, // TODO: get current screen size and connect to changes - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - Xwayland::self()->xcbScreen()->root_visual, - XCB_CW_EVENT_MASK, - dndValues); - registerXfixes(); - - xcb_change_property(xcbConn, - XCB_PROP_MODE_REPLACE, - window(), - atoms->xdnd_aware, - XCB_ATOM_ATOM, - 32, 1, &s_version); - xcb_flush(xcbConn); - - connect(waylandServer()->seat(), &Wrapland::Server::Seat::dragStarted, this, &Dnd::startDrag); - connect(waylandServer()->seat(), &Wrapland::Server::Seat::dragEnded, this, &Dnd::endDrag); - - const auto *comp = waylandServer()->compositor(); - m_surface = waylandServer()->internalCompositor()->createSurface(this); - m_surface->setInputRegion(nullptr); - m_surface->commit(Wrapland::Client::Surface::CommitFlag::None); - auto *dc = new QMetaObject::Connection(); - *dc = connect(comp, &Wrapland::Server::Compositor::surfaceCreated, this, - [this, dc](Wrapland::Server::Surface *si) { - // TODO: how to make sure that it is the iface of m_surface? - if (m_surfaceIface || si->client() != waylandServer()->internalConnection()) { - return; - } - QObject::disconnect(*dc); - delete dc; - m_surfaceIface = si; - connect(workspace(), &Workspace::clientActivated, this, - [this](Toplevel *ac) { - if (!ac || !ac->inherits("KWin::X11Client")) { - return; - } - auto *surface = ac->surface(); - if (surface) { - surface->setDataProxy(m_surfaceIface); - } else { - auto *dc = new QMetaObject::Connection(); - *dc = connect(ac, &Toplevel::surfaceChanged, this, [this, ac, dc] { - if (auto *surface = ac->surface()) { - surface->setDataProxy(m_surfaceIface); - QObject::disconnect(*dc); - delete dc; - } - } - ); - } - }); - } - ); - waylandServer()->dispatch(); -} - -void Dnd::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) -{ - if (qobject_cast<XToWlDrag *>(m_currentDrag)) { + if (qobject_cast<XToWlDrag*>(sel->m_currentDrag)) { // X drag is in progress, rogue X client took over the selection. return; } - if (m_currentDrag) { + if (sel->m_currentDrag) { // Wl drag is in progress - don't overwrite by rogue X client, // get it back instead! - ownSelection(true); + own_selection(sel, true); return; } - createX11Source(nullptr); - const auto *seat = waylandServer()->seat(); - auto *originSurface = seat->focusedPointerSurface(); + create_x11_source(sel, nullptr); + auto const seat = waylandServer()->seat(); + auto originSurface = seat->focusedPointerSurface(); if (!originSurface) { return; } @@ -152,36 +75,113 @@ void Dnd::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) // pressed for now return; } - createX11Source(event); - X11Source *source = x11Source(); - if (!source) { + create_x11_source(sel, event); + if (!sel->data.x11_source) { return; } - DataBridge::self()->dataDeviceIface()->updateProxy(originSurface); - m_currentDrag = new XToWlDrag(source); -} - -void Dnd::x11OffersChanged(const QStringList &added, const QStringList &removed) -{ - Q_UNUSED(added); - Q_UNUSED(removed); - // TODO: handled internally + sel->data.srv_device->updateProxy(originSurface); + sel->m_currentDrag = new XToWlDrag(sel->data.x11_source, sel); } -bool Dnd::handleClientMessage(xcb_client_message_event_t *event) +template<> +bool handle_client_message(Dnd* sel, xcb_client_message_event_t* event) { - for (Drag *drag : m_oldDrags) { + for (auto& drag : sel->m_oldDrags) { if (drag->handleClientMessage(event)) { return true; } } - if (m_currentDrag && m_currentDrag->handleClientMessage(event)) { + if (sel->m_currentDrag && sel->m_currentDrag->handleClientMessage(event)) { return true; } return false; } -DragEventReply Dnd::dragMoveFilter(Toplevel *target, const QPoint &pos) +template<> +void handle_x11_offer_change([[maybe_unused]] Dnd* sel, + [[maybe_unused]] QStringList const& added, + [[maybe_unused]] QStringList const& removed) +{ + // Handled internally. +} + +// version of DnD support in X +const static uint32_t s_version = 5; +uint32_t Dnd::version() +{ + return s_version; +} + +Dnd::Dnd(xcb_atom_t atom, srv_data_device* srv_dev, clt_data_device* clt_dev) +{ + data = create_selection_data(atom, srv_dev, clt_dev); + + // TODO(romangg): for window size get current screen size and connect to changes. + register_x11_selection(this, QSize(8192, 8192)); + register_xfixes(this); + + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_change_property(xcbConn, + XCB_PROP_MODE_REPLACE, + data.window, + atoms->xdnd_aware, + XCB_ATOM_ATOM, + 32, + 1, + &s_version); + xcb_flush(xcbConn); + + QObject::connect(waylandServer()->seat(), + &Wrapland::Server::Seat::dragStarted, + data.qobject.get(), + [this]() { startDrag(); }); + QObject::connect(waylandServer()->seat(), + &Wrapland::Server::Seat::dragEnded, + data.qobject.get(), + [this]() { endDrag(); }); + + const auto* comp = waylandServer()->compositor(); + m_surface = waylandServer()->internalCompositor()->createSurface(data.qobject.get()); + m_surface->setInputRegion(nullptr); + m_surface->commit(Wrapland::Client::Surface::CommitFlag::None); + auto* dc = new QMetaObject::Connection(); + *dc = QObject::connect( + comp, + &Wrapland::Server::Compositor::surfaceCreated, + data.qobject.get(), + [this, dc](Wrapland::Server::Surface* si) { + // TODO: how to make sure that it is the iface of m_surface? + if (m_surfaceIface || si->client() != waylandServer()->internalConnection()) { + return; + } + QObject::disconnect(*dc); + delete dc; + m_surfaceIface = si; + QObject::connect( + workspace(), &Workspace::clientActivated, data.qobject.get(), [this](Toplevel* ac) { + if (!ac || !ac->inherits("KWin::X11Client")) { + return; + } + auto* surface = ac->surface(); + if (surface) { + surface->setDataProxy(m_surfaceIface); + } else { + auto* dc = new QMetaObject::Connection(); + *dc = QObject::connect( + ac, &Toplevel::surfaceChanged, data.qobject.get(), [this, ac, dc] { + if (auto* surface = ac->surface()) { + surface->setDataProxy(m_surfaceIface); + QObject::disconnect(*dc); + delete dc; + } + }); + } + }); + }); + waylandServer()->dispatch(); +} + +DragEventReply Dnd::dragMoveFilter(Toplevel* target, const QPoint& pos) { // This filter only is used when a drag is in process. Q_ASSERT(m_currentDrag); @@ -190,8 +190,8 @@ DragEventReply Dnd::dragMoveFilter(Toplevel *target, const QPoint &pos) void Dnd::startDrag() { - auto *ddi = waylandServer()->seat()->dragSource(); - if (ddi == DataBridge::self()->dataDeviceIface()) { + auto srv_dev = waylandServer()->seat()->dragSource(); + if (srv_dev == data.srv_device) { // X to Wl drag, started by us, is in progress. Q_ASSERT(m_currentDrag); return; @@ -201,11 +201,11 @@ void Dnd::startDrag() Q_ASSERT(!m_currentDrag); // New Wl to X drag, init drag and Wl source. - m_currentDrag = new WlToXDrag(); - auto source = new WlSource(this, ddi); - source->setDataSourceIface(ddi->dragSource()); - setWlSource(source); - ownSelection(true); + m_currentDrag = new WlToXDrag(this); + auto source = new WlSource<Wrapland::Server::DataDevice, Wrapland::Server::DataSource>(srv_dev); + source->setSourceIface(srv_dev->dragSource()); + set_wl_source(this, source); + own_selection(this, true); } void Dnd::endDrag() @@ -215,13 +215,15 @@ void Dnd::endDrag() if (m_currentDrag->end()) { delete m_currentDrag; } else { - connect(m_currentDrag, &Drag::finish, this, &Dnd::clearOldDrag); + QObject::connect(m_currentDrag, &Drag::finish, data.qobject.get(), [this](auto drag) { + clearOldDrag(drag); + }); m_oldDrags << m_currentDrag; } m_currentDrag = nullptr; } -void Dnd::clearOldDrag(Drag *drag) +void Dnd::clearOldDrag(Drag* drag) { m_oldDrags.removeOne(drag); delete drag; diff --git a/xwl/dnd.h b/xwl/dnd.h index 6c0d21874c4d6436ace78daf66a30ce0c09c81de..05fbd72845640ee9bc571fe925d391939c68aa78 100644 --- a/xwl/dnd.h +++ b/xwl/dnd.h @@ -20,21 +20,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef KWIN_XWL_DND #define KWIN_XWL_DND +#include "drag.h" #include "selection.h" -#include <QPoint> +#include <Wrapland/Client/datadevice.h> +#include <Wrapland/Client/datasource.h> +#include <Wrapland/Server/data_device.h> +#include <Wrapland/Server/data_source.h> -namespace Wrapland -{ -namespace Client -{ -class Surface; -} -namespace Server -{ -class Surface; -} -} +#include <QPoint> namespace KWin { @@ -42,32 +36,44 @@ class Toplevel; namespace Xwl { -class Drag; +class Dnd; enum class DragEventReply; +template<> +void do_handle_xfixes_notify(Dnd* sel, xcb_xfixes_selection_notify_event_t* event); +template<> +bool handle_client_message(Dnd* sel, xcb_client_message_event_t* event); +template<> +void handle_x11_offer_change(Dnd* sel, QStringList const& added, QStringList const& removed); + /** * Represents the drag and drop mechanism, on X side this is the XDND protocol. * For more information on XDND see: https://johnlindal.wixsite.com/xdnd */ -class Dnd : public Selection +class Dnd { - Q_OBJECT + using srv_data_device = Wrapland::Server::DataDevice; + using clt_data_device = Wrapland::Client::DataDevice; public: - explicit Dnd(xcb_atom_t atom, QObject *parent); + selection_data<srv_data_device, clt_data_device> data; - static uint32_t version(); + // active drag or null when no drag active + Drag* m_currentDrag = nullptr; + QVector<Drag*> m_oldDrags; + + explicit Dnd(xcb_atom_t atom, srv_data_device* srv_dev, clt_data_device* clt_dev); - void doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) override; - void x11OffersChanged(const QStringList &added, const QStringList &removed) override; - bool handleClientMessage(xcb_client_message_event_t *event) override; + static uint32_t version(); - DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos); + DragEventReply dragMoveFilter(Toplevel* target, const QPoint& pos); - Wrapland::Server::Surface *surfaceIface() const { + Wrapland::Server::Surface* surfaceIface() const + { return m_surfaceIface; } - Wrapland::Client::Surface *surface() const { + Wrapland::Client::Surface* surface() const + { return m_surface; } @@ -75,14 +81,10 @@ private: // start and end Wl native client drags (Wl -> Xwl) void startDrag(); void endDrag(); - void clearOldDrag(Drag *drag); - - // active drag or null when no drag active - Drag *m_currentDrag = nullptr; - QVector<Drag *> m_oldDrags; + void clearOldDrag(Drag* drag); - Wrapland::Client::Surface *m_surface; - Wrapland::Server::Surface *m_surfaceIface = nullptr; + Wrapland::Client::Surface* m_surface; + Wrapland::Server::Surface* m_surfaceIface = nullptr; Q_DISABLE_COPY(Dnd) }; diff --git a/xwl/drag.cpp b/xwl/drag.cpp index 0877da775ec3c56f3b5e374b741e67f2e180bfec..2487dbcf4b8f16345ad59c9eeb56381024e42e19 100644 --- a/xwl/drag.cpp +++ b/xwl/drag.cpp @@ -26,8 +26,9 @@ namespace KWin namespace Xwl { -Drag::Drag(QObject *parent) +Drag::Drag(Dnd* dnd, QObject* parent) : QObject(parent) + , dnd(dnd) { } @@ -35,23 +36,20 @@ Drag::~Drag() { } -void Drag::sendClientMessage(xcb_window_t target, xcb_atom_t type, xcb_client_message_data_t *data) +void Drag::sendClientMessage(xcb_window_t target, xcb_atom_t type, xcb_client_message_data_t* data) { - xcb_client_message_event_t event { + xcb_client_message_event_t event{ XCB_CLIENT_MESSAGE, // response_type - 32, // format - 0, // sequence - target, // window - type, // type - *data, // data + 32, // format + 0, // sequence + target, // window + type, // type + *data, // data }; - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - xcb_send_event(xcbConn, - 0, - target, - XCB_EVENT_MASK_NO_EVENT, - reinterpret_cast<const char *>(&event)); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_send_event( + xcbConn, 0, target, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char*>(&event)); xcb_flush(xcbConn); } @@ -64,7 +62,7 @@ DnDAction Drag::atomToClientAction(xcb_atom_t atom) } else if (atom == atoms->xdnd_action_ask) { // we currently do not support it - need some test client first return DnDAction::None; -// return DnDAction::Ask; + // return DnDAction::Ask; } return DnDAction::None; } @@ -78,7 +76,7 @@ xcb_atom_t Drag::clientActionToAtom(DnDAction action) } else if (action == DnDAction::Ask) { // we currently do not support it - need some test client first return XCB_ATOM_NONE; -// return atoms->xdnd_action_ask; + // return atoms->xdnd_action_ask; } return XCB_ATOM_NONE; } diff --git a/xwl/drag.h b/xwl/drag.h index 4cef345af7422523e3ff3572e3dd5f605ff95be8..e4e3f71db0525f4acc54bb855c1ce1846f0301a9 100644 --- a/xwl/drag.h +++ b/xwl/drag.h @@ -32,6 +32,7 @@ class Toplevel; namespace Xwl { +class Dnd; enum class DragEventReply; using DnDAction = Wrapland::Client::DataDeviceManager::DnDAction; @@ -44,20 +45,23 @@ class Drag : public QObject Q_OBJECT public: - explicit Drag(QObject *parent = nullptr); + Dnd* dnd; + + explicit Drag(Dnd* dnd, QObject* parent = nullptr); ~Drag() override; - static void sendClientMessage(xcb_window_t target, xcb_atom_t type, xcb_client_message_data_t *data); + static void + sendClientMessage(xcb_window_t target, xcb_atom_t type, xcb_client_message_data_t* data); static DnDAction atomToClientAction(xcb_atom_t atom); static xcb_atom_t clientActionToAtom(DnDAction action); - virtual bool handleClientMessage(xcb_client_message_event_t *event) = 0; - virtual DragEventReply moveFilter(Toplevel *target, const QPoint &pos) = 0; + virtual bool handleClientMessage(xcb_client_message_event_t* event) = 0; + virtual DragEventReply moveFilter(Toplevel* target, const QPoint& pos) = 0; virtual bool end() = 0; Q_SIGNALS: - void finish(Drag *self); + void finish(Drag* self); private: Q_DISABLE_COPY(Drag) diff --git a/xwl/drag_wl.cpp b/xwl/drag_wl.cpp index 1b78547e8b2c4205682d335ac8aa82cd9f29f11b..27b812bb01e262fc0a31ef3f88f0f609b02d7096 100644 --- a/xwl/drag_wl.cpp +++ b/xwl/drag_wl.cpp @@ -19,8 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. *********************************************************************/ #include "drag_wl.h" -#include "databridge.h" #include "dnd.h" +#include "selection.h" #include "xwayland.h" #include "atoms.h" @@ -45,14 +45,15 @@ namespace KWin namespace Xwl { -WlToXDrag::WlToXDrag() +WlToXDrag::WlToXDrag(Dnd* dnd) + : Drag(dnd) { m_dsi = waylandServer()->seat()->dragSource()->dragSource(); } -DragEventReply WlToXDrag::moveFilter(Toplevel *target, const QPoint &pos) +DragEventReply WlToXDrag::moveFilter(Toplevel* target, const QPoint& pos) { - auto *seat = waylandServer()->seat(); + auto* seat = waylandServer()->seat(); if (m_visit && m_visit->target() == target) { // no target change return DragEventReply::Take; @@ -71,12 +72,12 @@ DragEventReply WlToXDrag::moveFilter(Toplevel *target, const QPoint &pos) } // new target workspace()->activateClient(target, false); - seat->setDragTarget(DataBridge::self()->dnd()->surfaceIface(), pos, target->input_transform()); + seat->setDragTarget(dnd->surfaceIface(), pos, target->input_transform()); m_visit = new Xvisit(this, target); return DragEventReply::Take; } -bool WlToXDrag::handleClientMessage(xcb_client_message_event_t *event) +bool WlToXDrag::handleClientMessage(xcb_client_message_event_t* event) { if (m_visit && m_visit->handleClientMessage(event)) { return true; @@ -91,7 +92,7 @@ bool WlToXDrag::end() m_visit = nullptr; return true; } - connect(m_visit, &Xvisit::finish, this, [this](Xvisit *visit) { + connect(m_visit, &Xvisit::finish, this, [this](Xvisit* visit) { Q_ASSERT(m_visit == visit); delete visit; m_visit = nullptr; @@ -101,20 +102,16 @@ bool WlToXDrag::end() return false; } -Xvisit::Xvisit(WlToXDrag *drag, Toplevel* target) - : QObject(drag), - m_drag(drag), - m_target(target) +Xvisit::Xvisit(WlToXDrag* drag, Toplevel* target) + : QObject(drag) + , m_drag(drag) + , m_target(target) { // first check supported DND version - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - xcb_get_property_cookie_t cookie = xcb_get_property(xcbConn, - 0, - m_target->xcb_window(), - atoms->xdnd_aware, - XCB_GET_PROPERTY_TYPE_ANY, - 0, 1); - auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_get_property_cookie_t cookie = xcb_get_property( + xcbConn, 0, m_target->xcb_window(), atoms->xdnd_aware, XCB_GET_PROPERTY_TYPE_ANY, 0, 1); + auto* reply = xcb_get_property_reply(xcbConn, cookie, nullptr); if (!reply) { doFinish(); return; @@ -124,7 +121,7 @@ Xvisit::Xvisit(WlToXDrag *drag, Toplevel* target) free(reply); return; } - xcb_atom_t *value = static_cast<xcb_atom_t *>(xcb_get_property_value(reply)); + xcb_atom_t* value = static_cast<xcb_atom_t*>(xcb_get_property_value(reply)); m_version = qMin(*value, Dnd::version()); if (m_version < 1) { // minimal version we accept is 1 @@ -134,15 +131,14 @@ Xvisit::Xvisit(WlToXDrag *drag, Toplevel* target) } free(reply); - const auto *dd = DataBridge::self()->dataDevice(); + auto const dd = drag->dnd->data.clt_device; // proxy drop - m_enterConnection = connect(dd, &Wrapland::Client::DataDevice::dragEntered, - this, &Xvisit::receiveOffer); - m_dropConnection = connect(dd, &Wrapland::Client::DataDevice::dropped, - this, &Xvisit::drop); + m_enterConnection + = connect(dd, &Wrapland::Client::DataDevice::dragEntered, this, &Xvisit::receiveOffer); + m_dropConnection = connect(dd, &Wrapland::Client::DataDevice::dropped, this, &Xvisit::drop); } -bool Xvisit::handleClientMessage(xcb_client_message_event_t *event) +bool Xvisit::handleClientMessage(xcb_client_message_event_t* event) { if (event->type == atoms->xdnd_status) { return handleStatus(event); @@ -152,9 +148,9 @@ bool Xvisit::handleClientMessage(xcb_client_message_event_t *event) return false; } -bool Xvisit::handleStatus(xcb_client_message_event_t *event) +bool Xvisit::handleStatus(xcb_client_message_event_t* event) { - xcb_client_message_data_t *data = &event->data; + xcb_client_message_data_t* data = &event->data; if (data->data32[0] != m_target->xcb_window()) { // wrong target window return false; @@ -186,9 +182,9 @@ bool Xvisit::handleStatus(xcb_client_message_event_t *event) return true; } -bool Xvisit::handleFinished(xcb_client_message_event_t *event) +bool Xvisit::handleFinished(xcb_client_message_event_t* event) { - xcb_client_message_data_t *data = &event->data; + xcb_client_message_data_t* data = &event->data; if (data->data32[0] != m_target->xcb_window()) { // different target window @@ -202,8 +198,8 @@ bool Xvisit::handleFinished(xcb_client_message_event_t *event) } const bool success = m_version > 4 ? data->data32[1] & 1 : true; - const xcb_atom_t usedActionAtom = m_version > 4 ? data->data32[2] : - static_cast<uint32_t>(XCB_ATOM_NONE); + const xcb_atom_t usedActionAtom + = m_version > 4 ? data->data32[2] : static_cast<uint32_t>(XCB_ATOM_NONE); Q_UNUSED(success); Q_UNUSED(usedActionAtom); @@ -217,7 +213,7 @@ bool Xvisit::handleFinished(xcb_client_message_event_t *event) return true; } -void Xvisit::sendPosition(const QPointF &globalPos) +void Xvisit::sendPosition(const QPointF& globalPos) { const int16_t x = globalPos.x(); const int16_t y = globalPos.y(); @@ -229,8 +225,8 @@ void Xvisit::sendPosition(const QPointF &globalPos) } m_pos.pending = true; - xcb_client_message_data_t data = {0}; - data.data32[0] = DataBridge::self()->dnd()->window(); + xcb_client_message_data_t data = {{0}}; + data.data32[0] = m_drag->dnd->data.window; data.data32[2] = (x << 16) | y; data.data32[3] = XCB_CURRENT_TIME; data.data32[4] = Drag::clientActionToAtom(m_proposedAction); @@ -260,12 +256,14 @@ void Xvisit::receiveOffer() } Q_ASSERT(m_dataOffer.isNull()); - m_dataOffer = DataBridge::self()->dataDevice()->dragOffer(); + m_dataOffer = m_drag->dnd->data.clt_device->dragOffer(); Q_ASSERT(!m_dataOffer.isNull()); retrieveSupportedActions(); - m_actionConnection = connect(m_dataOffer, &Wrapland::Client::DataOffer::sourceDragAndDropActionsChanged, - this, &Xvisit::retrieveSupportedActions); + m_actionConnection = connect(m_dataOffer, + &Wrapland::Client::DataOffer::sourceDragAndDropActionsChanged, + this, + &Xvisit::retrieveSupportedActions); enter(); } @@ -278,14 +276,15 @@ void Xvisit::enter() // proxy future pointer position changes m_motionConnection = connect(waylandServer()->seat(), - &Wrapland::Server::Seat::pointerPosChanged, - this, &Xvisit::sendPosition); + &Wrapland::Server::Seat::pointerPosChanged, + this, + &Xvisit::sendPosition); } void Xvisit::sendEnter() { - xcb_client_message_data_t data = {0}; - data.data32[0] = DataBridge::self()->dnd()->window(); + xcb_client_message_data_t data = {{0}}; + data.data32[0] = m_drag->dnd->data.window; data.data32[1] = m_version << 24; // TODO: replace this with the mime type getter from m_dataOffer, @@ -294,12 +293,12 @@ void Xvisit::sendEnter() const int mimesCount = mimeTypesNames.size(); size_t cnt = 0; size_t totalCnt = 0; - for (const auto mimeName : mimeTypesNames) { + for (const auto& mimeName : mimeTypesNames) { // 3 mimes and less can be sent directly in the XdndEnter message if (totalCnt == 3) { break; } - const auto atom = Selection::mimeTypeToAtom(mimeName.c_str()); + const auto atom = mimeTypeToAtom(mimeName.c_str()); if (atom != XCB_ATOM_NONE) { data.data32[cnt + 2] = atom; @@ -319,8 +318,8 @@ void Xvisit::sendEnter() targets.resize(mimesCount); size_t cnt = 0; - for (const auto mimeName : mimeTypesNames) { - const auto atom = Selection::mimeTypeToAtom(mimeName.c_str()); + for (const auto& mimeName : mimeTypesNames) { + const auto atom = mimeTypeToAtom(mimeName.c_str()); if (atom != XCB_ATOM_NONE) { targets[cnt] = atom; cnt++; @@ -329,18 +328,20 @@ void Xvisit::sendEnter() xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_REPLACE, - DataBridge::self()->dnd()->window(), + m_drag->dnd->data.window, atoms->xdnd_type_list, XCB_ATOM_ATOM, - 32, cnt, targets.data()); + 32, + cnt, + targets.data()); } Drag::sendClientMessage(m_target->xcb_window(), atoms->xdnd_enter, &data); } void Xvisit::sendDrop(uint32_t time) { - xcb_client_message_data_t data = {0}; - data.data32[0] = DataBridge::self()->dnd()->window(); + xcb_client_message_data_t data = {{0}}; + data.data32[0] = m_drag->dnd->data.window; data.data32[2] = time; Drag::sendClientMessage(m_target->xcb_window(), atoms->xdnd_drop, &data); @@ -352,8 +353,8 @@ void Xvisit::sendDrop(uint32_t time) void Xvisit::sendLeave() { - xcb_client_message_data_t data = {0}; - data.data32[0] = DataBridge::self()->dnd()->window(); + xcb_client_message_data_t data = {{0}}; + data.data32[0] = m_drag->dnd->data.window; Drag::sendClientMessage(m_target->xcb_window(), atoms->xdnd_leave, &data); } @@ -385,8 +386,7 @@ void Xvisit::requestDragAndDropAction() if (m_dataOffer.isNull()) { return; } - const auto pref = m_preferredAction != DnDAction::None ? m_preferredAction: - DnDAction::Copy; + const auto pref = m_preferredAction != DnDAction::None ? m_preferredAction : DnDAction::Copy; // we assume the X client supports Move, but this might be wrong - then // the drag just cancels, if the user tries to force it. diff --git a/xwl/drag_wl.h b/xwl/drag_wl.h index b83cd119d4d571d2cd464b93c980378efc87e756..a75451b5cad2cb3a9aa308040459a6827b48cf9c 100644 --- a/xwl/drag_wl.h +++ b/xwl/drag_wl.h @@ -22,8 +22,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "drag.h" -#include <Wrapland/Client/dataoffer.h> - #include <QPoint> #include <QPointer> #include <QVector> @@ -32,6 +30,7 @@ namespace Wrapland { namespace Client { +class DataOffer; class Surface; } namespace Server @@ -48,7 +47,6 @@ class Toplevel; namespace Xwl { -class X11Source; enum class DragEventReply; class Xvisit; @@ -59,20 +57,21 @@ class WlToXDrag : public Drag Q_OBJECT public: - explicit WlToXDrag(); + explicit WlToXDrag(Dnd* dnd); - DragEventReply moveFilter(Toplevel *target, const QPoint &pos) override; - bool handleClientMessage(xcb_client_message_event_t *event) override; + DragEventReply moveFilter(Toplevel* target, const QPoint& pos) override; + bool handleClientMessage(xcb_client_message_event_t* event) override; bool end() override; - Wrapland::Server::DataSource *dataSourceIface() const { + Wrapland::Server::DataSource* dataSourceIface() const + { return m_dsi; } private: - Wrapland::Server::DataSource *m_dsi; - Xvisit *m_visit = nullptr; + Wrapland::Server::DataSource* m_dsi; + Xvisit* m_visit = nullptr; Q_DISABLE_COPY(WlToXDrag) }; @@ -85,24 +84,26 @@ class Xvisit : public QObject public: // TODO: handle ask action - Xvisit(WlToXDrag *drag, Toplevel *target); + Xvisit(WlToXDrag* drag, Toplevel* target); - bool handleClientMessage(xcb_client_message_event_t *event); - bool handleStatus(xcb_client_message_event_t *event); - bool handleFinished(xcb_client_message_event_t *event); + bool handleClientMessage(xcb_client_message_event_t* event); + bool handleStatus(xcb_client_message_event_t* event); + bool handleFinished(xcb_client_message_event_t* event); - void sendPosition(const QPointF &globalPos); + void sendPosition(const QPointF& globalPos); void leave(); - bool finished() const { + bool finished() const + { return m_state.finished; } - Toplevel *target() const { + Toplevel* target() const + { return m_target; } Q_SIGNALS: - void finish(Xvisit *self); + void finish(Xvisit* self); private: void sendEnter(); @@ -122,8 +123,8 @@ private: void doFinish(); void stopConnections(); - WlToXDrag *m_drag; - Toplevel *m_target; + WlToXDrag* m_drag; + Toplevel* m_target; uint32_t m_version = 0; QMetaObject::Connection m_enterConnection; diff --git a/xwl/drag_x.cpp b/xwl/drag_x.cpp index a5f2ac678e7454b0f1781073237f3dd9d0f2dc26..e4a1768a61fbae18a9f2fcfb2aec2475ace4ddd0 100644 --- a/xwl/drag_x.cpp +++ b/xwl/drag_x.cpp @@ -19,8 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. *********************************************************************/ #include "drag_x.h" -#include "databridge.h" #include "dnd.h" +#include "selection.h" #include "selection_source.h" #include "xwayland.h" @@ -45,56 +45,45 @@ namespace KWin namespace Xwl { -static QStringList atomToMimeTypes(xcb_atom_t atom) +XToWlDrag::XToWlDrag(DataX11Source* source, Dnd* dnd) + : Drag(dnd) + , m_source(source) { - QStringList mimeTypes; - - if (atom == atoms->utf8_string) { - mimeTypes << QString::fromLatin1("text/plain;charset=utf-8"); - } else if (atom == atoms->text) { - mimeTypes << QString::fromLatin1("text/plain"); - } else if (atom == atoms->uri_list || atom == atoms->netscape_url || atom == atoms->moz_url) { - // We identify netscape and moz format as less detailed formats text/uri-list, - // text/x-uri and accept the information loss. - mimeTypes << QString::fromLatin1("text/uri-list") << QString::fromLatin1("text/x-uri"); - } else { - mimeTypes << Selection::atomName(atom); - } - return mimeTypes; -} - -XToWlDrag::XToWlDrag(X11Source *source) - : m_source(source) -{ - connect(DataBridge::self()->dnd(), &Dnd::transferFinished, this, [this](xcb_timestamp_t eventTime) { - // we use this mechanism, because the finished call is not - // reliable done by Wayland clients - auto it = std::find_if(m_dataRequests.begin(), m_dataRequests.end(), [eventTime](const QPair<xcb_timestamp_t, bool> &req) { - return req.first == eventTime && req.second == false; + connect(dnd->data.qobject.get(), + &q_selection::transferFinished, + this, + [this](xcb_timestamp_t eventTime) { + // we use this mechanism, because the finished call is not + // reliable done by Wayland clients + auto it = std::find_if(m_dataRequests.begin(), + m_dataRequests.end(), + [eventTime](const QPair<xcb_timestamp_t, bool>& req) { + return req.first == eventTime && req.second == false; + }); + if (it == m_dataRequests.end()) { + // transfer finished for a different drag + return; + } + (*it).second = true; + checkForFinished(); + }); + connect( + source->qobject(), &qX11Source::transferReady, this, [this](xcb_atom_t target, qint32 fd) { + Q_UNUSED(target); + Q_UNUSED(fd); + m_dataRequests << QPair<xcb_timestamp_t, bool>(m_source->timestamp(), false); }); - if (it == m_dataRequests.end()) { - // transfer finished for a different drag - return; - } - (*it).second = true; - checkForFinished(); - }); - connect(source, &X11Source::transferReady, this, [this](xcb_atom_t target, qint32 fd) { - Q_UNUSED(target); - Q_UNUSED(fd); - m_dataRequests << QPair<xcb_timestamp_t, bool>(m_source->timestamp(), false); - }); - auto *ddm = waylandServer()->internalDataDeviceManager(); - m_dataSource = ddm->createDataSource(this); + auto* ddm = waylandServer()->internalDataDeviceManager(); + m_dataSource = ddm->createSource(this); connect(m_dataSource, &Wrapland::Client::DataSource::dragAndDropPerformed, this, [this] { m_performed = true; if (m_visit) { - connect(m_visit, &WlVisit::finish, this, [this](WlVisit *visit) { + connect(m_visit, &WlVisit::finish, this, [this](WlVisit* visit) { Q_UNUSED(visit); checkForFinished(); }); - QTimer::singleShot(2000, this, [this]{ + QTimer::singleShot(2000, this, [this] { if (!m_visit->entered() || !m_visit->dropHandled()) { // X client timed out Q_EMIT finish(this); @@ -113,28 +102,31 @@ XToWlDrag::XToWlDrag(X11Source *source) }); // source does _not_ take ownership of m_dataSource - source->setDataSource(m_dataSource); - - auto *dc = new QMetaObject::Connection(); - *dc = connect(waylandServer()->dataDeviceManager(), &Wrapland::Server::DataDeviceManager::dataSourceCreated, this, - [this, dc](Wrapland::Server::DataSource *dsi) { - Q_ASSERT(dsi); - if (dsi->client() != waylandServer()->internalConnection()) { - return; - } - QObject::disconnect(*dc); - delete dc; - connect(dsi, &Wrapland::Server::DataSource::mimeTypeOffered, this, &XToWlDrag::offerCallback); - } - ); + source->setSource(m_dataSource); + + auto* dc = new QMetaObject::Connection(); + *dc = connect(waylandServer()->dataDeviceManager(), + &Wrapland::Server::DataDeviceManager::sourceCreated, + this, + [this, dc](Wrapland::Server::DataSource* dsi) { + Q_ASSERT(dsi); + if (dsi->client() != waylandServer()->internalConnection()) { + return; + } + QObject::disconnect(*dc); + delete dc; + connect(dsi, + &Wrapland::Server::DataSource::mimeTypeOffered, + this, + &XToWlDrag::offerCallback); + }); // Start drag with serial of last left pointer button press. // This means X to Wl drags can only be executed with the left pointer button being pressed. // For touch and (maybe) other pointer button drags we have to revisit this. // // Until then we accept the restriction for Xwayland clients. - DataBridge::self()->dataDevice()->startDrag(waylandServer()->seat()->pointerButtonSerial(Qt::LeftButton), - m_dataSource, - DataBridge::self()->dnd()->surface()); + dnd->data.clt_device->startDrag( + waylandServer()->seat()->pointerButtonSerial(Qt::LeftButton), m_dataSource, dnd->surface()); waylandServer()->dispatch(); } @@ -144,11 +136,11 @@ XToWlDrag::~XToWlDrag() m_dataSource = nullptr; } -DragEventReply XToWlDrag::moveFilter(Toplevel* target, const QPoint &pos) +DragEventReply XToWlDrag::moveFilter(Toplevel* target, const QPoint& pos) { Q_UNUSED(pos); - auto *seat = waylandServer()->seat(); + auto* seat = waylandServer()->seat(); if (m_visit && m_visit->target() == target) { // still same Wl target, wait for X events @@ -158,7 +150,7 @@ DragEventReply XToWlDrag::moveFilter(Toplevel* target, const QPoint &pos) if (m_visit->leave()) { delete m_visit; } else { - connect(m_visit, &WlVisit::finish, this, [this](WlVisit *visit) { + connect(m_visit, &WlVisit::finish, this, [this](WlVisit* visit) { m_oldVisits.removeOne(visit); delete visit; }); @@ -168,8 +160,8 @@ DragEventReply XToWlDrag::moveFilter(Toplevel* target, const QPoint &pos) const bool hasCurrent = m_visit; m_visit = nullptr; - if (!target || !target->surface() || - target->surface()->client() == waylandServer()->xWaylandConnection()) { + if (!target || !target->surface() + || target->surface()->client() == waylandServer()->xWaylandConnection()) { // currently there is no target or target is an Xwayland window // handled here and by X directly if (target->control) { @@ -190,9 +182,9 @@ DragEventReply XToWlDrag::moveFilter(Toplevel* target, const QPoint &pos) return DragEventReply::Ignore; } -bool XToWlDrag::handleClientMessage(xcb_client_message_event_t *event) +bool XToWlDrag::handleClientMessage(xcb_client_message_event_t* event) { - for (auto *visit : m_oldVisits) { + for (auto* visit : m_oldVisits) { if (visit->handleClientMessage(event)) { return true; } @@ -219,7 +211,7 @@ DnDAction XToWlDrag::selectedDragAndDropAction() return m_lastSelectedDragAndDropAction; } -void XToWlDrag::setOffers(const Mimes &offers) +void XToWlDrag::setOffers(const Mimes& offers) { m_source->setOffers(offers); if (offers.isEmpty()) { @@ -238,17 +230,19 @@ void XToWlDrag::setOffers(const Mimes &offers) // TODO: make sure that offers are not changed in between visits m_offersPending = m_offers = offers; - for (const auto mimePair : offers) { + for (const auto& mimePair : offers) { m_dataSource->offer(mimePair.first); } } using Mime = QPair<QString, xcb_atom_t>; -void XToWlDrag::offerCallback(std::string const &mime) +void XToWlDrag::offerCallback(std::string const& mime) { - m_offersPending.erase(std::remove_if(m_offersPending.begin(), m_offersPending.end(), - [mime](const Mime &m) { return m.first == mime.c_str(); })); + m_offersPending.erase( + std::remove_if(m_offersPending.begin(), m_offersPending.end(), [mime](const Mime& m) { + return m.first == mime.c_str(); + })); if (m_offersPending.isEmpty() && m_visit && m_visit->entered()) { setDragTarget(); } @@ -256,7 +250,7 @@ void XToWlDrag::offerCallback(std::string const &mime) void XToWlDrag::setDragTarget() { - auto *ac = m_visit->target(); + auto* ac = m_visit->target(); workspace()->activateClient(ac); waylandServer()->seat()->setDragTarget(ac->surface(), ac->input_transform()); } @@ -275,8 +269,10 @@ bool XToWlDrag::checkForFinished() // need to wait for first data request return false; } - const bool transfersFinished = std::all_of(m_dataRequests.begin(), m_dataRequests.end(), - [](QPair<xcb_timestamp_t, bool> req) { return req.second; }); + const bool transfersFinished + = std::all_of(m_dataRequests.begin(), + m_dataRequests.end(), + [](QPair<xcb_timestamp_t, bool> req) { return req.second; }); if (transfersFinished) { m_visit->sendFinished(); Q_EMIT finish(this); @@ -284,24 +280,26 @@ bool XToWlDrag::checkForFinished() return transfersFinished; } -WlVisit::WlVisit(Toplevel* target, XToWlDrag *drag) - : QObject(drag), - m_target(target), - m_drag(drag) +WlVisit::WlVisit(Toplevel* target, XToWlDrag* drag) + : QObject(drag) + , m_target(target) + , m_drag(drag) { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); m_window = xcb_generate_id(xcbConn); - DataBridge::self()->dnd()->overwriteRequestorWindow(m_window); + overwrite_requestor_window(drag->dnd, m_window); - const uint32_t dndValues[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_PROPERTY_CHANGE }; + const uint32_t dndValues[] + = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; xcb_create_window(xcbConn, XCB_COPY_FROM_PARENT, m_window, kwinApp()->x11RootWindow(), - 0, 0, - 8192, 8192, // TODO: get current screen size and connect to changes + 0, + 0, + 8192, + 8192, // TODO: get current screen size and connect to changes 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, Xwayland::self()->xcbScreen()->root_visual, @@ -314,7 +312,9 @@ WlVisit::WlVisit(Toplevel* target, XToWlDrag *drag) m_window, atoms->xdnd_aware, XCB_ATOM_ATOM, - 32, 1, &version); + 32, + 1, + &version); xcb_map_window(xcbConn, m_window); workspace()->stacking_order->add_manual_overlay(m_window); @@ -326,19 +326,19 @@ WlVisit::WlVisit(Toplevel* target, XToWlDrag *drag) WlVisit::~WlVisit() { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); xcb_destroy_window(xcbConn, m_window); xcb_flush(xcbConn); } bool WlVisit::leave() { - DataBridge::self()->dnd()->overwriteRequestorWindow(XCB_WINDOW_NONE); + overwrite_requestor_window(m_drag->dnd, XCB_WINDOW_NONE); unmapProxyWindow(); return m_finished; } -bool WlVisit::handleClientMessage(xcb_client_message_event_t *event) +bool WlVisit::handleClientMessage(xcb_client_message_event_t* event) { if (event->window != m_window) { // different window @@ -357,13 +357,13 @@ bool WlVisit::handleClientMessage(xcb_client_message_event_t *event) return false; } -static bool hasMimeName(const Mimes &mimes, const QString &name) +static bool hasMimeName(const Mimes& mimes, const QString& name) { - return std::any_of(mimes.begin(), mimes.end(), - [name](const Mime &m) { return m.first == name; }); + return std::any_of( + mimes.begin(), mimes.end(), [name](const Mime& m) { return m.first == name; }); } -bool WlVisit::handleEnter(xcb_client_message_event_t *event) +bool WlVisit::handleEnter(xcb_client_message_event_t* event) { if (m_entered) { // a drag already entered @@ -371,7 +371,7 @@ bool WlVisit::handleEnter(xcb_client_message_event_t *event) } m_entered = true; - xcb_client_message_data_t *data = &event->data; + xcb_client_message_data_t* data = &event->data; m_srcWindow = data->data32[0]; m_version = data->data32[1] >> 24; @@ -382,7 +382,7 @@ bool WlVisit::handleEnter(xcb_client_message_event_t *event) for (size_t i = 0; i < 3; i++) { xcb_atom_t mimeAtom = data->data32[2 + i]; const auto mimeStrings = atomToMimeTypes(mimeAtom); - for (const auto mime : mimeStrings ) { + for (const auto& mime : mimeStrings) { if (!hasMimeName(offers, mime)) { offers << Mime(mime, mimeAtom); } @@ -397,17 +397,13 @@ bool WlVisit::handleEnter(xcb_client_message_event_t *event) return true; } -void WlVisit::getMimesFromWinProperty(Mimes &offers) +void WlVisit::getMimesFromWinProperty(Mimes& offers) { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - auto cookie = xcb_get_property(xcbConn, - 0, - m_srcWindow, - atoms->xdnd_type_list, - XCB_GET_PROPERTY_TYPE_ANY, - 0, 0x1fffffff); - - auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + auto cookie = xcb_get_property( + xcbConn, 0, m_srcWindow, atoms->xdnd_type_list, XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff); + + auto* reply = xcb_get_property_reply(xcbConn, cookie, nullptr); if (reply == nullptr) { return; } @@ -417,10 +413,10 @@ void WlVisit::getMimesFromWinProperty(Mimes &offers) return; } - xcb_atom_t *mimeAtoms = static_cast<xcb_atom_t *>(xcb_get_property_value(reply)); + xcb_atom_t* mimeAtoms = static_cast<xcb_atom_t*>(xcb_get_property_value(reply)); for (size_t i = 0; i < reply->value_len; ++i) { const auto mimeStrings = atomToMimeTypes(mimeAtoms[i]); - for (const auto mime : mimeStrings) { + for (const auto& mime : mimeStrings) { if (!hasMimeName(offers, mime)) { offers << Mime(mime, mimeAtoms[i]); } @@ -429,9 +425,9 @@ void WlVisit::getMimesFromWinProperty(Mimes &offers) free(reply); } -bool WlVisit::handlePosition(xcb_client_message_event_t *event) +bool WlVisit::handlePosition(xcb_client_message_event_t* event) { - xcb_client_message_data_t *data = &event->data; + xcb_client_message_data_t* data = &event->data; m_srcWindow = data->data32[0]; if (!m_target) { @@ -447,8 +443,7 @@ bool WlVisit::handlePosition(xcb_client_message_event_t *event) const xcb_timestamp_t timestamp = data->data32[3]; m_drag->x11Source()->setTimestamp(timestamp); - xcb_atom_t actionAtom = m_version > 1 ? data->data32[4] : - atoms->xdnd_action_copy; + xcb_atom_t actionAtom = m_version > 1 ? data->data32[4] : atoms->xdnd_action_copy; auto action = Drag::atomToClientAction(actionAtom); if (action == DnDAction::None) { @@ -467,11 +462,11 @@ bool WlVisit::handlePosition(xcb_client_message_event_t *event) return true; } -bool WlVisit::handleDrop(xcb_client_message_event_t *event) +bool WlVisit::handleDrop(xcb_client_message_event_t* event) { m_dropHandled = true; - xcb_client_message_data_t *data = &event->data; + xcb_client_message_data_t* data = &event->data; m_srcWindow = data->data32[0]; const xcb_timestamp_t timestamp = data->data32[2]; m_drag->x11Source()->setTimestamp(timestamp); @@ -489,10 +484,10 @@ void WlVisit::doFinish() Q_EMIT finish(this); } -bool WlVisit::handleLeave(xcb_client_message_event_t *event) +bool WlVisit::handleLeave(xcb_client_message_event_t* event) { m_entered = false; - xcb_client_message_data_t *data = &event->data; + xcb_client_message_data_t* data = &event->data; m_srcWindow = data->data32[0]; doFinish(); return true; @@ -506,7 +501,7 @@ void WlVisit::sendStatus() // accept the drop flags |= (1 << 0); } - xcb_client_message_data_t data = {0}; + xcb_client_message_data_t data = {{0}}; data.data32[0] = m_window; data.data32[1] = flags; data.data32[4] = flags & (1 << 0) ? m_actionAtom : static_cast<uint32_t>(XCB_ATOM_NONE); @@ -516,7 +511,7 @@ void WlVisit::sendStatus() void WlVisit::sendFinished() { const bool accepted = m_entered && m_action != DnDAction::None; - xcb_client_message_data_t data = {0}; + xcb_client_message_data_t data = {{0}}; data.data32[0] = m_window; data.data32[1] = accepted; data.data32[2] = accepted ? m_actionAtom : static_cast<uint32_t>(XCB_ATOM_NONE); @@ -537,7 +532,7 @@ void WlVisit::unmapProxyWindow() if (!m_mapped) { return; } - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); xcb_unmap_window(xcbConn, m_window); workspace()->stacking_order->remove_manual_overlay(m_window); workspace()->stacking_order->update(true); diff --git a/xwl/drag_x.h b/xwl/drag_x.h index bd3c749a83ee3446bfd82d85a5440031984a4278..7c7b5f66e6214628ea46f9cf169c48cf5e3e104b 100644 --- a/xwl/drag_x.h +++ b/xwl/drag_x.h @@ -22,22 +22,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "drag.h" -#include <Wrapland/Client/datadevicemanager.h> -#include <Wrapland/Client/dataoffer.h> - -#include <Wrapland/Server/data_device_manager.h> - #include <QPoint> #include <QPointer> #include <QVector> -namespace Wrapland -{ -namespace Client +namespace Wrapland::Client { class DataSource; } -} namespace KWin { @@ -45,50 +37,54 @@ class Toplevel; namespace Xwl { -class X11Source; enum class DragEventReply; class WlVisit; +template<typename> +class X11Source; -using Mimes = QVector<QPair<QString, xcb_atom_t> >; +using Mimes = QVector<QPair<QString, xcb_atom_t>>; +using DataX11Source = X11Source<Wrapland::Client::DataSource>; class XToWlDrag : public Drag { Q_OBJECT public: - explicit XToWlDrag(X11Source *source); + explicit XToWlDrag(DataX11Source* source, Dnd* dnd); ~XToWlDrag() override; - DragEventReply moveFilter(Toplevel* target, const QPoint &pos) override; - bool handleClientMessage(xcb_client_message_event_t *event) override; + DragEventReply moveFilter(Toplevel* target, const QPoint& pos) override; + bool handleClientMessage(xcb_client_message_event_t* event) override; void setDragAndDropAction(DnDAction action); DnDAction selectedDragAndDropAction(); - bool end() override { + bool end() override + { return false; } - X11Source *x11Source() const { + DataX11Source* x11Source() const + { return m_source; } private: - void setOffers(const Mimes &offers); - void offerCallback(const std::string &mime); + void setOffers(const Mimes& offers); + void offerCallback(const std::string& mime); void setDragTarget(); bool checkForFinished(); - Wrapland::Client::DataSource *m_dataSource; + Wrapland::Client::DataSource* m_dataSource; Mimes m_offers; Mimes m_offersPending; - X11Source *m_source; - QVector<QPair<xcb_timestamp_t, bool> > m_dataRequests; + DataX11Source* m_source; + QVector<QPair<xcb_timestamp_t, bool>> m_dataRequests; - WlVisit *m_visit = nullptr; - QVector<WlVisit *> m_oldVisits; + WlVisit* m_visit = nullptr; + QVector<WlVisit*> m_oldVisits; bool m_performed = false; DnDAction m_lastSelectedDragAndDropAction = DnDAction::None; @@ -101,42 +97,47 @@ class WlVisit : public QObject Q_OBJECT public: - WlVisit(Toplevel* target, XToWlDrag *drag); + WlVisit(Toplevel* target, XToWlDrag* drag); ~WlVisit() override; - bool handleClientMessage(xcb_client_message_event_t *event); + bool handleClientMessage(xcb_client_message_event_t* event); bool leave(); - Toplevel* target() const { + Toplevel* target() const + { return m_target; } - xcb_window_t window() const { + xcb_window_t window() const + { return m_window; } - bool entered() const { + bool entered() const + { return m_entered; } - bool dropHandled() const { + bool dropHandled() const + { return m_dropHandled; } - bool finished() const { + bool finished() const + { return m_finished; } void sendFinished(); Q_SIGNALS: - void offersReceived(const Mimes &offers); - void finish(WlVisit *self); + void offersReceived(const Mimes& offers); + void finish(WlVisit* self); private: - bool handleEnter(xcb_client_message_event_t *event); - bool handlePosition(xcb_client_message_event_t *event); - bool handleDrop(xcb_client_message_event_t *event); - bool handleLeave(xcb_client_message_event_t *event); + bool handleEnter(xcb_client_message_event_t* event); + bool handlePosition(xcb_client_message_event_t* event); + bool handleDrop(xcb_client_message_event_t* event); + bool handleLeave(xcb_client_message_event_t* event); void sendStatus(); - void getMimesFromWinProperty(Mimes &offers); + void getMimesFromWinProperty(Mimes& offers); bool targetAcceptsAction() const; @@ -147,7 +148,7 @@ private: xcb_window_t m_window; xcb_window_t m_srcWindow = XCB_WINDOW_NONE; - XToWlDrag *m_drag; + XToWlDrag* m_drag; uint32_t m_version = 0; diff --git a/xwl/primary_selection.cpp b/xwl/primary_selection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ae0153103472f426fb75651a5be176bc2a6c5f3 --- /dev/null +++ b/xwl/primary_selection.cpp @@ -0,0 +1,44 @@ +/* + SPDX-FileCopyrightText: 2021 Roman Gilg <subdiff@gmail.com> + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#include "primary_selection.h" + +#include <Wrapland/Server/seat.h> + +namespace KWin::Xwl +{ + +primary_selection::primary_selection(xcb_atom_t atom, + srv_data_device* srv_dev, + clt_data_device* clt_dev) +{ + data = create_selection_data(atom, srv_dev, clt_dev); + + register_x11_selection(this, QSize(10, 10)); + + QObject::connect(waylandServer()->seat(), + &Wrapland::Server::Seat::primarySelectionChanged, + data.qobject.get(), + [this] { handle_wl_selection_change(this); }); +} + +primary_selection::srv_data_device* primary_selection::get_current_device() const +{ + return waylandServer()->seat()->primarySelection(); +} + +Wrapland::Client::PrimarySelectionDeviceManager* +primary_selection::get_internal_device_manager() const +{ + return waylandServer()->internalPrimarySelectionDeviceManager(); +} + +std::function<void(primary_selection::srv_data_device*)> +primary_selection::get_selection_setter() const +{ + return [](srv_data_device* dev) { waylandServer()->seat()->setPrimarySelection(dev); }; +} + +} diff --git a/xwl/primary_selection.h b/xwl/primary_selection.h new file mode 100644 index 0000000000000000000000000000000000000000..2ed4ff0dc1b75b250e55b06ad117515518692a34 --- /dev/null +++ b/xwl/primary_selection.h @@ -0,0 +1,39 @@ +/* + SPDX-FileCopyrightText: 2021 Roman Gilg <subdiff@gmail.com> + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once + +#include "selection.h" + +#include <Wrapland/Client/primary_selection.h> +#include <Wrapland/Server/primary_selection.h> + +#include <functional> + +namespace KWin::Xwl +{ + +class primary_selection +{ +public: + using srv_data_device = Wrapland::Server::PrimarySelectionDevice; + using clt_data_device = Wrapland::Client::PrimarySelectionDevice; + using srv_data_source = srv_data_device::source_t; + using clt_source_t = clt_data_device::source_t; + + selection_data<srv_data_device, clt_data_device> data; + QMetaObject::Connection source_check_connection; + + primary_selection(xcb_atom_t atom, srv_data_device* srv_dev, clt_data_device* clt_dev); + + srv_data_device* get_current_device() const; + Wrapland::Client::PrimarySelectionDeviceManager* get_internal_device_manager() const; + std::function<void(srv_data_device*)> get_selection_setter() const; + +private: + Q_DISABLE_COPY(primary_selection) +}; + +} diff --git a/xwl/selection.cpp b/xwl/selection.cpp deleted file mode 100644 index 20b94e77141c06d00e550576142c67db8ed5e22a..0000000000000000000000000000000000000000 --- a/xwl/selection.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -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 -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 "selection.h" -#include "databridge.h" -#include "selection_source.h" -#include "transfer.h" - -#include "atoms.h" -#include "workspace.h" - -#include "win/x11/window.h" - -#include <xcb/xcb_event.h> -#include <xcb/xfixes.h> - -#include <QTimer> - -namespace KWin -{ -namespace Xwl -{ - -xcb_atom_t Selection::mimeTypeToAtom(const QString &mimeType) -{ - if (mimeType == QLatin1String("text/plain;charset=utf-8")) { - return atoms->utf8_string; - } - if (mimeType == QLatin1String("text/plain")) { - return atoms->text; - } - if (mimeType == QLatin1String("text/x-uri")) { - return atoms->uri_list; - } - return mimeTypeToAtomLiteral(mimeType); -} - -xcb_atom_t Selection::mimeTypeToAtomLiteral(const QString &mimeType) -{ - return Xcb::Atom(mimeType.toLatin1(), false, kwinApp()->x11Connection()); -} - -QString Selection::atomName(xcb_atom_t atom) -{ - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom); - xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr); - if (!nameReply) { - return QString(); - } - - const size_t length = xcb_get_atom_name_name_length(nameReply); - QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length); - free(nameReply); - return name; -} - -QStringList Selection::atomToMimeTypes(xcb_atom_t atom) -{ - QStringList mimeTypes; - - if (atom == atoms->utf8_string) { - mimeTypes << QString::fromLatin1("text/plain;charset=utf-8"); - } else if (atom == atoms->text) { - mimeTypes << QString::fromLatin1("text/plain"); - } else if (atom == atoms->uri_list) { - mimeTypes << "text/uri-list" << "text/x-uri"; - } else { - mimeTypes << atomName(atom); - } - return mimeTypes; -} - -Selection::Selection(xcb_atom_t atom, QObject *parent) - : QObject(parent) - , m_atom(atom) -{ - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - m_window = xcb_generate_id(kwinApp()->x11Connection()); - m_requestorWindow = m_window; - xcb_flush(xcbConn); -} - -bool Selection::handleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) -{ - if (event->window != m_window) { - return false; - } - if (event->selection != m_atom) { - return false; - } - if (m_disownPending) { - // notify of our own disown - ignore it - m_disownPending = false; - return true; - } - if (event->owner == m_window && m_waylandSource) { - // When we claim a selection we must use XCB_TIME_CURRENT, - // grab the actual timestamp here to answer TIMESTAMP requests - // correctly - m_waylandSource->setTimestamp(event->timestamp); - m_timestamp = event->timestamp; - return true; - } - - // Being here means some other X window has claimed the selection. - doHandleXfixesNotify(event); - return true; -} - -bool Selection::filterEvent(xcb_generic_event_t *event) -{ - switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { - case XCB_SELECTION_NOTIFY: - return handleSelectionNotify(reinterpret_cast<xcb_selection_notify_event_t *>(event)); - case XCB_PROPERTY_NOTIFY: - return handlePropertyNotify(reinterpret_cast<xcb_property_notify_event_t *>(event)); - case XCB_SELECTION_REQUEST: - return handleSelectionRequest(reinterpret_cast<xcb_selection_request_event_t *>(event)); - case XCB_CLIENT_MESSAGE: - return handleClientMessage(reinterpret_cast<xcb_client_message_event_t *>(event)); - default: - return false; - } -} - -void Selection::sendSelectionNotify(xcb_selection_request_event_t *event, bool success) -{ - xcb_selection_notify_event_t notify; - notify.response_type = XCB_SELECTION_NOTIFY; - notify.sequence = 0; - notify.time = event->time; - notify.requestor = event->requestor; - notify.selection = event->selection; - notify.target = event->target; - notify.property = success ? event->property : xcb_atom_t(XCB_ATOM_NONE); - - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - xcb_send_event(xcbConn, - 0, - event->requestor, - XCB_EVENT_MASK_NO_EVENT, - (const char *)¬ify); - xcb_flush(xcbConn); -} - -void Selection::registerXfixes() -{ - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | - XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | - XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; - xcb_xfixes_select_selection_input(kwinApp()->x11Connection(), - m_window, - m_atom, - mask); - xcb_flush(xcbConn); -} - -void Selection::setWlSource(WlSource *source) -{ - delete m_waylandSource; - delete m_xSource; - m_waylandSource = nullptr; - m_xSource = nullptr; - if (source) { - m_waylandSource = source; - connect(source, &WlSource::transferReady, this, &Selection::startTransferToX); - } -} - -void Selection::createX11Source(xcb_xfixes_selection_notify_event_t *event) -{ - delete m_waylandSource; - delete m_xSource; - m_waylandSource = nullptr; - m_xSource = nullptr; - if (!event || event->owner == XCB_WINDOW_NONE) { - return; - } - m_xSource = new X11Source(this, event); - - connect(m_xSource, &X11Source::offersChanged, this, &Selection::x11OffersChanged); - connect(m_xSource, &X11Source::transferReady, this, &Selection::startTransferToWayland); -} - -void Selection::ownSelection(bool own) -{ - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - if (own) { - xcb_set_selection_owner(xcbConn, - m_window, - m_atom, - XCB_TIME_CURRENT_TIME); - } else { - m_disownPending = true; - xcb_set_selection_owner(xcbConn, - XCB_WINDOW_NONE, - m_atom, - m_timestamp); - } - xcb_flush(xcbConn); -} - -void Selection::overwriteRequestorWindow(xcb_window_t window) -{ - Q_ASSERT(m_xSource); - if (window == XCB_WINDOW_NONE) { - // reset - window = m_window; - } - m_requestorWindow = window; - m_xSource->setRequestor(window); -} - -bool Selection::handleSelectionRequest(xcb_selection_request_event_t *event) -{ - if (event->selection != m_atom) { - return false; - } - - if (qobject_cast<win::x11::window*>(workspace()->activeClient()) == nullptr) { - // Receiving Wayland selection not allowed when no Xwayland surface active - // filter the event, but don't act upon it - sendSelectionNotify(event, false); - return true; - } - - if (m_window != event->owner || !m_waylandSource) { - if (event->time < m_timestamp) { - // cancel earlier attempts at receiving a selection - // TODO: is this for sure without problems? - sendSelectionNotify(event, false); - return true; - } - return false; - } - return m_waylandSource->handleSelectionRequest(event); -} - -bool Selection::handleSelectionNotify(xcb_selection_notify_event_t *event) -{ - if (m_xSource && m_xSource->handleSelectionNotify(event)) { - return true; - } - for (TransferXtoWl *transfer : m_xToWlTransfers) { - if (transfer->handleSelectionNotify(event)) { - return true; - } - } - return false; -} - -bool Selection::handlePropertyNotify(xcb_property_notify_event_t *event) -{ - for (TransferXtoWl *transfer : m_xToWlTransfers) { - if (transfer->handlePropertyNotify(event)) { - return true; - } - } - for (TransferWltoX *transfer : m_wlToXTransfers) { - if (transfer->handlePropertyNotify(event)) { - return true; - } - } - return false; -} - -void Selection::startTransferToWayland(xcb_atom_t target, qint32 fd) -{ - // create new x to wl data transfer object - auto *transfer = new TransferXtoWl(m_atom, target, fd, m_xSource->timestamp(), m_requestorWindow, this); - m_xToWlTransfers << transfer; - - connect(transfer, &TransferXtoWl::finished, this, [this, transfer]() { - Q_EMIT transferFinished(transfer->timestamp()); - delete transfer; - m_xToWlTransfers.removeOne(transfer); - endTimeoutTransfersTimer(); - }); - startTimeoutTransfersTimer(); -} - -void Selection::startTransferToX(xcb_selection_request_event_t *event, qint32 fd) -{ - // create new wl to x data transfer object - auto *transfer = new TransferWltoX(m_atom, event, fd, this); - - connect(transfer, &TransferWltoX::selectionNotify, this, &Selection::sendSelectionNotify); - connect(transfer, &TransferWltoX::finished, this, [this, transfer]() { - Q_EMIT transferFinished(transfer->timestamp()); - - // TODO: serialize? see comment below. -// const bool wasActive = (transfer == m_wlToXTransfers[0]); - delete transfer; - m_wlToXTransfers.removeOne(transfer); - endTimeoutTransfersTimer(); -// if (wasActive && !m_wlToXTransfers.isEmpty()) { -// m_wlToXTransfers[0]->startTransferFromSource(); -// } - }); - - // add it to list of queued transfers - m_wlToXTransfers.append(transfer); - - // TODO: Do we need to serialize the transfers, or can we do - // them in parallel as we do it right now? - transfer->startTransferFromSource(); -// if (m_wlToXTransfers.size() == 1) { -// transfer->startTransferFromSource(); -// } - startTimeoutTransfersTimer(); -} - -void Selection::startTimeoutTransfersTimer() -{ - if (m_timeoutTransfers) { - return; - } - m_timeoutTransfers = new QTimer(this); - connect(m_timeoutTransfers, &QTimer::timeout, this, &Selection::timeoutTransfers); - m_timeoutTransfers->start(5000); -} - -void Selection::endTimeoutTransfersTimer() -{ - if (m_xToWlTransfers.isEmpty() && m_wlToXTransfers.isEmpty()) { - delete m_timeoutTransfers; - m_timeoutTransfers = nullptr; - } -} - -void Selection::timeoutTransfers() -{ - for (TransferXtoWl *transfer : m_xToWlTransfers) { - transfer->timeout(); - } - for (TransferWltoX *transfer : m_wlToXTransfers) { - transfer->timeout(); - } -} - -} // namespace Xwl -} // namespace KWin diff --git a/xwl/selection.h b/xwl/selection.h index f3916798325ed247545f78668cd8fa959d415e61..f91ac63294610393cb2e41055274b7209db13574 100644 --- a/xwl/selection.h +++ b/xwl/selection.h @@ -1,144 +1,651 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. +/* + SPDX-FileCopyrightText: 2019-2021 Roman Gilg <subdiff@gmail.com> + SPDX-FileCopyrightText: 2021 Francesco Sorrentino <francesco.sorr@gmail.com> -Copyright 2019 Roman Gilg <subdiff@gmail.com> + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once -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. +#include "selection_source.h" +#include "transfer.h" +#include "xwayland.h" -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 KWIN_XWL_SELECTION -#define KWIN_XWL_SELECTION +#include "atoms.h" +#include "wayland_server.h" +#include "win/x11/window.h" +#include "workspace.h" #include <QObject> +#include <QString> +#include <QStringList> +#include <QTimer> #include <QVector> #include <xcb/xcb.h> +#include <xcb/xcb_event.h> +#include <xcb/xfixes.h> +#include <xcbutils.h> -struct xcb_xfixes_selection_notify_event_t; +#include <Wrapland/Client/connection_thread.h> -class QTimer; +#include <memory> -namespace KWin -{ -namespace Xwl +namespace KWin::Xwl { class TransferWltoX; class TransferXtoWl; +template<typename, typename> class WlSource; +template<typename> class X11Source; +/* + * QObject attribute of a Selection. + * This is a hack around having a template QObject. + */ +class q_selection : public QObject +{ + Q_OBJECT + +public: +Q_SIGNALS: + void transferFinished(xcb_timestamp_t eventTime); +}; + /** - * Base class representing generic X selections and their respective - * Wayland counter-parts. - * - * The class needs to be subclassed and adjusted according to the - * selection, but provides common fucntionality to be expected of all - * selections. + * Data needed by X selections and their Wayland counter-parts. * * A selection should exist through the whole runtime of an Xwayland * session. + * Each selection holds an independent instance of this class, + * containing the source and the active transfers. * - * Independently of each other the class holds the currently active - * source instance and active transfers relative to the represented - * selection. + * This class can be specialized to support the core Wayland protocol + * (clipboard and dnd) as well as primary selection. */ -class Selection : public QObject +template<typename server_device, typename client_device> +struct selection_data { + using srv_data_source = typename server_device::source_t; + using clt_data_source = typename client_device::source_t; + + std::unique_ptr<q_selection> qobject; + server_device* srv_device{nullptr}; + client_device* clt_device{nullptr}; + + xcb_atom_t atom{XCB_ATOM_NONE}; + xcb_window_t window{XCB_WINDOW_NONE}; + + bool disown_pending{false}; + xcb_timestamp_t timestamp; + xcb_window_t requestor_window{XCB_WINDOW_NONE}; + + // Active source, if any. Only one of them at max can exist + // at the same time. + WlSource<server_device, srv_data_source>* wayland_source{nullptr}; + X11Source<clt_data_source>* x11_source{nullptr}; + + // active transfers + struct { + QVector<TransferWltoX*> wl_to_x11; + QVector<TransferXtoWl*> x11_to_wl; + QTimer* timeout{nullptr}; + } transfers; + + selection_data() = default; + selection_data(selection_data const&) = delete; + selection_data& operator=(selection_data const&) = delete; + selection_data(selection_data&&) noexcept = default; + selection_data& operator=(selection_data&&) noexcept = default; + + ~selection_data() + { + delete wayland_source; + delete x11_source; + wayland_source = nullptr; + x11_source = nullptr; + clt_device = nullptr; + srv_device = nullptr; + } +}; + +template<typename srv_data_source, typename client_data_device> +auto create_selection_data(xcb_atom_t atom, srv_data_source* sdev, client_data_device* cdev) { - Q_OBJECT + selection_data<srv_data_source, client_data_device> sel; -public: - static xcb_atom_t mimeTypeToAtom(const QString &mimeType); - static xcb_atom_t mimeTypeToAtomLiteral(const QString &mimeType); - static QStringList atomToMimeTypes(xcb_atom_t atom); - static QString atomName(xcb_atom_t atom); - static void sendSelectionNotify(xcb_selection_request_event_t *event, bool success); + sel.qobject.reset(new q_selection()); + sel.atom = atom; + sel.srv_device = sdev; + sel.clt_device = cdev; - // on selection owner changes by X clients (Xwl -> Wl) - bool handleXfixesNotify(xcb_xfixes_selection_notify_event_t *event); - bool filterEvent(xcb_generic_event_t *event); + auto xcb_con = kwinApp()->x11Connection(); + sel.window = xcb_generate_id(kwinApp()->x11Connection()); + sel.requestor_window = sel.window; + xcb_flush(xcb_con); - xcb_atom_t atom() const { - return m_atom; + return sel; +} + +inline void sendSelectionNotify(xcb_selection_request_event_t* event, bool success) +{ + xcb_selection_notify_event_t notify; + notify.response_type = XCB_SELECTION_NOTIFY; + notify.sequence = 0; + notify.time = event->time; + notify.requestor = event->requestor; + notify.selection = event->selection; + notify.target = event->target; + notify.property = success ? event->property : xcb_atom_t(XCB_ATOM_NONE); + + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_send_event(xcbConn, 0, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char*)¬ify); + xcb_flush(xcbConn); +} + +template<typename Selection> +void register_xfixes(Selection* sel) +{ + auto xcb_conn = kwinApp()->x11Connection(); + const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER + | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY + | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + xcb_xfixes_select_selection_input( + kwinApp()->x11Connection(), sel->data.window, sel->data.atom, mask); + xcb_flush(xcb_conn); +} + +// on selection owner changes by X clients (Xwl -> Wl) +template<typename Selection> +bool handle_xfixes_notify(Selection* sel, xcb_xfixes_selection_notify_event_t* event) +{ + if (!sel) { + return false; } - xcb_window_t window() const { - return m_window; + if (event->window != sel->data.window) { + return false; + } + if (event->selection != sel->data.atom) { + return false; + } + if (sel->data.disown_pending) { + // notify of our own disown - ignore it + sel->data.disown_pending = false; + return true; + } + if (event->owner == sel->data.window && sel->data.wayland_source) { + // When we claim a selection we must use XCB_TIME_CURRENT, + // grab the actual timestamp here to answer TIMESTAMP requests + // correctly + sel->data.wayland_source->setTimestamp(event->timestamp); + sel->data.timestamp = event->timestamp; + return true; } - void overwriteRequestorWindow(xcb_window_t window); -Q_SIGNALS: - void transferFinished(xcb_timestamp_t eventTime); + // Being here means some other X window has claimed the selection. + do_handle_xfixes_notify(sel, event); + return true; +} + +template<typename Selection> +void do_handle_xfixes_notify(Selection* sel, xcb_xfixes_selection_notify_event_t* event) +{ + create_x11_source(sel, nullptr); + + auto const& client = workspace()->activeClient(); + if (!qobject_cast<win::x11::window const*>(client)) { + // clipboard is only allowed to be acquired when Xwayland has focus + // TODO: can we make this stronger (window id comparison)? + return; + } -protected: - Selection(xcb_atom_t atom, QObject *parent); - void registerXfixes(); + create_x11_source(sel, event); - virtual void doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) = 0; - virtual void x11OffersChanged(const QStringList &added, const QStringList &removed) = 0; + if (auto const& source = sel->data.x11_source) { + source->getTargets(sel->data.requestor_window, sel->data.atom); + } +} - virtual bool handleClientMessage(xcb_client_message_event_t *event) { - Q_UNUSED(event); +template<typename Selection> +bool handle_client_message([[maybe_unused]] Selection* sel, + [[maybe_unused]] xcb_client_message_event_t* event) +{ + return false; +} + +template<typename Selection> +bool filter_event(Selection* sel, xcb_generic_event_t* event) +{ + switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { + case XCB_SELECTION_NOTIFY: + return handle_selection_notify(sel, reinterpret_cast<xcb_selection_notify_event_t*>(event)); + case XCB_PROPERTY_NOTIFY: + return handle_property_notify(sel, reinterpret_cast<xcb_property_notify_event_t*>(event)); + case XCB_SELECTION_REQUEST: + return handle_selection_request(sel, + reinterpret_cast<xcb_selection_request_event_t*>(event)); + case XCB_CLIENT_MESSAGE: + return handle_client_message(sel, reinterpret_cast<xcb_client_message_event_t*>(event)); + default: return false; } - // sets the current provider of the selection - void setWlSource(WlSource *source); - WlSource *wlSource() const { - return m_waylandSource; +} + +template<typename Selection> +bool handle_selection_request(Selection* sel, xcb_selection_request_event_t* event) +{ + if (event->selection != sel->data.atom) { + return false; } - void createX11Source(xcb_xfixes_selection_notify_event_t *event); - X11Source *x11Source() const { - return m_xSource; + + if (qobject_cast<win::x11::window*>(workspace()->activeClient()) == nullptr) { + // Receiving Wayland selection not allowed when no Xwayland surface active + // filter the event, but don't act upon it + sendSelectionNotify(event, false); + return true; } - // must be called in order to provide data from Wl to X - void ownSelection(bool own); - void setWindow(xcb_window_t window) { - m_window = window; + + if (sel->data.window != event->owner || !sel->data.wayland_source) { + if (event->time < sel->data.timestamp) { + // cancel earlier attempts at receiving a selection + // TODO: is this for sure without problems? + sendSelectionNotify(event, false); + return true; + } + return false; } + return sel->data.wayland_source->handleSelectionRequest(event); +} -private: - bool handleSelectionRequest(xcb_selection_request_event_t *event); - bool handleSelectionNotify(xcb_selection_notify_event_t *event); - bool handlePropertyNotify(xcb_property_notify_event_t *event); +template<typename Selection> +bool handle_selection_notify(Selection* sel, xcb_selection_notify_event_t* event) +{ + if (sel->data.x11_source && event->requestor == sel->data.requestor_window + && event->selection == sel->data.atom) { + if (sel->data.x11_source->handleSelectionNotify(event)) { + return true; + } + } + for (auto& transfer : sel->data.transfers.x11_to_wl) { + if (transfer->handleSelectionNotify(event)) { + return true; + } + } + return false; +} - void startTransferToWayland(xcb_atom_t target, qint32 fd); - void startTransferToX(xcb_selection_request_event_t *event, qint32 fd); +template<typename Selection> +bool handle_property_notify(Selection* sel, xcb_property_notify_event_t* event) +{ + for (auto& transfer : sel->data.transfers.x11_to_wl) { + if (transfer->handlePropertyNotify(event)) { + return true; + } + } + for (auto& transfer : sel->data.transfers.wl_to_x11) { + if (transfer->handlePropertyNotify(event)) { + return true; + } + } + return false; +} - // Timeout transfers, which have become inactive due to client errors. - void timeoutTransfers(); - void startTimeoutTransfersTimer(); - void endTimeoutTransfersTimer(); +// must be called in order to provide data from Wl to X +template<typename Selection> +void own_selection(Selection* sel, bool own) +{ + auto xcb_conn = kwinApp()->x11Connection(); + if (own) { + xcb_set_selection_owner(xcb_conn, sel->data.window, sel->data.atom, XCB_TIME_CURRENT_TIME); + } else { + sel->data.disown_pending = true; + xcb_set_selection_owner(xcb_conn, XCB_WINDOW_NONE, sel->data.atom, sel->data.timestamp); + } + xcb_flush(xcb_conn); +} - xcb_atom_t m_atom = XCB_ATOM_NONE; - xcb_window_t m_window = XCB_WINDOW_NONE; - xcb_window_t m_requestorWindow = XCB_WINDOW_NONE; - xcb_timestamp_t m_timestamp; +template<typename Selection> +void overwrite_requestor_window(Selection* sel, xcb_window_t window) +{ + assert(sel->data.x11_source); + sel->data.requestor_window = window == XCB_WINDOW_NONE ? sel->data.window : window; +} - // Active source, if any. Only one of them at max can exist - // at the same time. - WlSource *m_waylandSource = nullptr; - X11Source *m_xSource = nullptr; +// sets the current provider of the selection +template<typename Selection, typename srv_data_device, typename srv_data_source> +void set_wl_source(Selection* sel, WlSource<srv_data_device, srv_data_source>* source) +{ + delete sel->data.wayland_source; + delete sel->data.x11_source; + sel->data.wayland_source = nullptr; + sel->data.x11_source = nullptr; + if (source) { + sel->data.wayland_source = source; + QObject::connect(source->qobject(), + &qWlSource::transferReady, + sel->data.qobject.get(), + [sel](auto event, auto fd) { start_transfer_to_x11(sel, event, fd); }); + } +} - // active transfers - QVector<TransferWltoX *> m_wlToXTransfers; - QVector<TransferXtoWl *> m_xToWlTransfers; - QTimer *m_timeoutTransfers = nullptr; +template<typename Selection> +void create_x11_source(Selection* sel, xcb_xfixes_selection_notify_event_t* event) +{ + delete sel->data.wayland_source; + delete sel->data.x11_source; + sel->data.wayland_source = nullptr; + sel->data.x11_source = nullptr; + if (!event || event->owner == XCB_WINDOW_NONE) { + return; + } - bool m_disownPending = false; + using clt_data_source = typename decltype(sel->data)::clt_data_source; + sel->data.x11_source = new X11Source<clt_data_source>(event); - Q_DISABLE_COPY(Selection) -}; + QObject::connect(sel->data.x11_source->qobject(), + &qX11Source::offersChanged, + sel->data.qobject.get(), + [sel](auto const& added, auto const& removed) { + handle_x11_offer_change(sel, added, removed); + }); + QObject::connect(sel->data.x11_source->qobject(), + &qX11Source::transferReady, + sel->data.qobject.get(), + [sel](auto target, auto fd) { start_transfer_to_wayland(sel, target, fd); }); +} + +template<typename Selection> +void start_transfer_to_wayland(Selection* sel, xcb_atom_t target, qint32 fd) +{ + // create new x to wl data transfer object + auto transfer = new TransferXtoWl(sel->data.atom, + target, + fd, + sel->data.x11_source->timestamp(), + sel->data.requestor_window, + sel->data.qobject.get()); + sel->data.transfers.x11_to_wl << transfer; + + QObject::connect( + transfer, &TransferXtoWl::finished, sel->data.qobject.get(), [sel, transfer]() { + Q_EMIT sel->data.qobject->transferFinished(transfer->timestamp()); + delete transfer; + sel->data.transfers.x11_to_wl.removeOne(transfer); + end_timeout_transfers_timer(sel); + }); + start_timeout_transfers_timer(sel); +} + +template<typename Selection> +void start_transfer_to_x11(Selection* sel, xcb_selection_request_event_t* event, qint32 fd) +{ + // create new wl to x data transfer object + auto transfer = new TransferWltoX(sel->data.atom, event, fd, sel->data.qobject.get()); + + QObject::connect( + transfer, &TransferWltoX::selectionNotify, sel->data.qobject.get(), &sendSelectionNotify); + QObject::connect( + transfer, &TransferWltoX::finished, sel->data.qobject.get(), [sel, transfer]() { + Q_EMIT sel->data.qobject->transferFinished(transfer->timestamp()); + + // TODO: serialize? see comment below. + // const bool wasActive = (transfer == m_wlToXTransfers[0]); + delete transfer; + sel->data.transfers.wl_to_x11.removeOne(transfer); + end_timeout_transfers_timer(sel); + // if (wasActive && !m_wlToXTransfers.isEmpty()) { + // m_wlToXTransfers[0]->startTransferFromSource(); + // } + }); + + // add it to list of queued transfers + sel->data.transfers.wl_to_x11.append(transfer); + + // TODO: Do we need to serialize the transfers, or can we do + // them in parallel as we do it right now? + transfer->startTransferFromSource(); + // if (m_wlToXTransfers.size() == 1) { + // transfer->startTransferFromSource(); + // } + start_timeout_transfers_timer(sel); +} + +// Timeout transfers, which have become inactive due to client errors. +template<typename Selection> +void timeout_transfers(Selection* sel) +{ + for (auto& transfer : sel->data.transfers.x11_to_wl) { + transfer->timeout(); + } + for (auto& transfer : sel->data.transfers.wl_to_x11) { + transfer->timeout(); + } +} + +template<typename Selection> +void start_timeout_transfers_timer(Selection* sel) +{ + if (sel->data.transfers.timeout) { + return; + } + sel->data.transfers.timeout = new QTimer(sel->data.qobject.get()); + QObject::connect(sel->data.transfers.timeout, + &QTimer::timeout, + sel->data.qobject.get(), + [sel]() { timeout_transfers(sel); }); + sel->data.transfers.timeout->start(5000); +} + +template<typename Selection> +void end_timeout_transfers_timer(Selection* sel) +{ + if (sel->data.transfers.x11_to_wl.isEmpty() && sel->data.transfers.wl_to_x11.isEmpty()) { + delete sel->data.transfers.timeout; + sel->data.transfers.timeout = nullptr; + } +} + +inline xcb_atom_t mimeTypeToAtomLiteral(const QString& mimeType) +{ + return Xcb::Atom(mimeType.toLatin1(), false, kwinApp()->x11Connection()); +} + +inline xcb_atom_t mimeTypeToAtom(const QString& mimeType) +{ + if (mimeType == QLatin1String("text/plain;charset=utf-8")) { + return atoms->utf8_string; + } + if (mimeType == QLatin1String("text/plain")) { + return atoms->text; + } + if (mimeType == QLatin1String("text/x-uri")) { + return atoms->uri_list; + } + return mimeTypeToAtomLiteral(mimeType); +} + +inline QString atomName(xcb_atom_t atom) +{ + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom); + xcb_get_atom_name_reply_t* nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr); + if (!nameReply) { + return QString(); + } + + const size_t length = xcb_get_atom_name_name_length(nameReply); + QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length); + free(nameReply); + return name; +} + +inline QStringList atomToMimeTypes(xcb_atom_t atom) +{ + QStringList mimeTypes; + + if (atom == atoms->utf8_string) { + mimeTypes << QString::fromLatin1("text/plain;charset=utf-8"); + } else if (atom == atoms->text) { + mimeTypes << QString::fromLatin1("text/plain"); + } else if (atom == atoms->uri_list || atom == atoms->netscape_url || atom == atoms->moz_url) { + // We identify netscape and moz format as less detailed formats text/uri-list, + // text/x-uri and accept the information loss. + mimeTypes << QString::fromLatin1("text/uri-list") << QString::fromLatin1("text/x-uri"); + } else { + mimeTypes << atomName(atom); + } + return mimeTypes; +} + +template<typename Selection> +void register_x11_selection(Selection* sel, QSize const& window_size) +{ + auto xcbConn = kwinApp()->x11Connection(); + + uint32_t const values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; + xcb_create_window(xcbConn, + XCB_COPY_FROM_PARENT, + sel->data.window, + kwinApp()->x11RootWindow(), + 0, + 0, + window_size.width(), + window_size.height(), + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + Xwayland::self()->xcbScreen()->root_visual, + XCB_CW_EVENT_MASK, + values); + register_xfixes(sel); + xcb_flush(xcbConn); +} + +/** + * Check the current state of the selection and if a source needs to be created or destroyed. + */ +template<typename Selection> +void check_wl_source(Selection* sel) +{ + using srv_data_device = typename Selection::srv_data_device; + using srv_data_source = typename srv_data_device::source_t; + + auto remove_source = [sel] { + if (sel->data.wayland_source) { + set_wl_source<Selection, srv_data_device, srv_data_source>(sel, nullptr); + own_selection(sel, false); + } + }; + + auto srv_dev = sel->get_current_device(); + static_assert(std::is_same_v<srv_data_device, std::remove_pointer_t<decltype(srv_dev)>>, + "get current device type mismatch"); + + // Wayland source gets created when: + // - the Wl selection exists, + // - its source is not Xwayland, + // - a client is active, + // - this client is an Xwayland one. + // + // Otherwise the Wayland source gets destroyed to shield against snooping X clients. + + if (!srv_dev || sel->data.srv_device == srv_dev) { + // That means there is either no source or it's an Xwayland source. + QObject::disconnect(sel->source_check_connection); + sel->source_check_connection = QMetaObject::Connection(); + remove_source(); + return; + } + + if (!workspace()->activeClient() + || !workspace()->activeClient()->inherits("KWin::win::x11::window")) { + // No active client or active client is Wayland native. + remove_source(); + return; + } + + // Xwayland client is active and we need a Wayland source + if (sel->data.wayland_source) { + // source already exists, nothing more to do + return; + } + + auto wls = new WlSource<srv_data_device, srv_data_source>(srv_dev); + set_wl_source(sel, wls); + + auto srv_src = srv_dev->selection(); + if (srv_src) { + wls->setSourceIface(srv_src); + } + QObject::connect(srv_dev, + &srv_data_device::selectionChanged, + wls->qobject(), + [wls](auto srv_src) { wls->setSourceIface(srv_src); }); + own_selection(sel, true); +} + +/** + * React to Wl selection change. + */ +template<typename Selection> +void handle_wl_selection_change(Selection* sel) +{ + using srv_data_device = typename Selection::srv_data_device; + using srv_data_source = typename srv_data_device::source_t; + + auto srv_dev = sel->get_current_device(); + static_assert(std::is_same_v<srv_data_device, std::remove_pointer_t<decltype(srv_dev)>>, + "get current device type mismatch"); + + if (srv_dev && srv_dev != sel->data.srv_device) { + // Wayland native client provides new selection. + if (!sel->source_check_connection) { + sel->source_check_connection = QObject::connect(workspace(), + &Workspace::clientActivated, + sel->data.qobject.get(), + [sel] { check_wl_source(sel); }); + } + // Remove previous source so checkWlSource() can create a new one. + set_wl_source<Selection, srv_data_device, srv_data_source>(sel, nullptr); + } + check_wl_source(sel); +} + +template<typename Selection> +void handle_x11_offer_change(Selection* sel, QStringList const& added, QStringList const& removed) +{ + auto source = sel->data.x11_source; + if (!source) { + return; + } + + auto flush_and_dispatch = [] { + waylandServer()->internalClientConection()->flush(); + waylandServer()->dispatch(); + }; + + const Mimes offers = source->offers(); + if (offers.isEmpty()) { + sel->get_selection_setter()(nullptr); + flush_and_dispatch(); + return; + } + + if (!source->source() || !removed.isEmpty()) { + // create new Wl DataSource if there is none or when types + // were removed (Wl Data Sources can only add types) + auto dataDeviceManager = sel->get_internal_device_manager(); + auto dataSource = dataDeviceManager->createSource(source->qobject()); + + // also offers directly the currently available types + source->setSource(dataSource); + sel->data.clt_device->setSelection(0, dataSource); + sel->get_selection_setter()(sel->data.srv_device); + } else if (auto dataSource = source->source()) { + for (const QString& mime : added) { + dataSource->offer(mime); + } + } -} // namespace Xwl -} // namespace KWin + flush_and_dispatch(); +} -#endif +} diff --git a/xwl/selection_source.cpp b/xwl/selection_source.cpp index cf524fb11f26377389abb8a3a0bfbfc5119ffbd8..66c19f7b8e0e90a08edc8fc4537add324e00655c 100644 --- a/xwl/selection_source.cpp +++ b/xwl/selection_source.cpp @@ -18,70 +18,66 @@ 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 "selection_source.h" + #include "selection.h" #include "transfer.h" #include "atoms.h" #include "wayland_server.h" -#include <Wrapland/Client/connection_thread.h> -#include <Wrapland/Client/datadevicemanager.h> #include <Wrapland/Client/datadevice.h> #include <Wrapland/Client/datasource.h> +#include <Wrapland/Client/primary_selection.h> #include <Wrapland/Server/data_device.h> #include <Wrapland/Server/data_source.h> -#include <Wrapland/Server/seat.h> +#include <Wrapland/Server/primary_selection.h> -#include <unistd.h> #include <string> +#include <unistd.h> #include <xwayland_logging.h> -namespace KWin -{ -namespace Xwl +namespace KWin::Xwl { -SelectionSource::SelectionSource(Selection *selection) - : QObject(selection) - , m_selection(selection) - , m_window(selection->window()) +template<typename DeviceIface, typename SourceIface> +WlSource<DeviceIface, SourceIface>::WlSource(DeviceIface* di) + : m_di(di) + , m_qobject(new qWlSource) { + Q_ASSERT(di); } -WlSource::WlSource(Selection *selection, Wrapland::Server::DataDevice *ddi) - : SelectionSource(selection) - , m_ddi(ddi) +template<typename DeviceIface, typename SourceIface> +WlSource<DeviceIface, SourceIface>::~WlSource() { - Q_ASSERT(ddi); + delete m_qobject; } -void WlSource::setDataSourceIface(Wrapland::Server::DataSource *dsi) +template<typename DeviceIface, typename SourceIface> +void WlSource<DeviceIface, SourceIface>::setSourceIface(SourceIface* si) { - if (m_dsi == dsi) { + if (m_si == si) { return; } - for (const auto &mime : dsi->mimeTypes()) { + for (const auto& mime : si->mimeTypes()) { m_offers << QString::fromStdString(mime); } - m_offerConnection = connect(dsi, - &Wrapland::Server::DataSource::mimeTypeOffered, - this, &WlSource::receiveOffer); - m_dsi = dsi; + m_offerConnection = QObject::connect( + si, &SourceIface::mimeTypeOffered, qobject(), [this](auto mime) { receiveOffer(mime); }); + m_si = si; } -void WlSource::receiveOffer(std::string const &mime) +template<typename DeviceIface, typename SourceIface> +void WlSource<DeviceIface, SourceIface>::receiveOffer(std::string const& mime) { m_offers << QString::fromStdString(mime); } -void WlSource::sendSelectionNotify(xcb_selection_request_event_t *event, bool success) -{ - Selection::sendSelectionNotify(event, success); -} - -bool WlSource::handleSelectionRequest(xcb_selection_request_event_t *event) +template<typename DeviceIface, typename SourceIface> +bool WlSource<DeviceIface, SourceIface>::handleSelectionRequest( + xcb_selection_request_event_t* event) { if (event->target == atoms->targets) { sendTargets(event); @@ -98,7 +94,8 @@ bool WlSource::handleSelectionRequest(xcb_selection_request_event_t *event) return true; } -void WlSource::sendTargets(xcb_selection_request_event_t *event) +template<typename DeviceIface, typename SourceIface> +void WlSource<DeviceIface, SourceIface>::sendTargets(xcb_selection_request_event_t* event) { QVector<xcb_atom_t> targets; targets.resize(m_offers.size() + 2); @@ -106,8 +103,8 @@ void WlSource::sendTargets(xcb_selection_request_event_t *event) targets[1] = atoms->targets; size_t cnt = 2; - for (const auto mime : m_offers) { - targets[cnt] = Selection::mimeTypeToAtom(mime); + for (const auto& mime : m_offers) { + targets[cnt] = mimeTypeToAtom(mime); cnt++; } @@ -116,11 +113,14 @@ void WlSource::sendTargets(xcb_selection_request_event_t *event) event->requestor, event->property, XCB_ATOM_ATOM, - 32, cnt, targets.data()); + 32, + cnt, + targets.data()); sendSelectionNotify(event, true); } -void WlSource::sendTimestamp(xcb_selection_request_event_t *event) +template<typename DeviceIface, typename SourceIface> +void WlSource<DeviceIface, SourceIface>::sendTimestamp(xcb_selection_request_event_t* event) { const xcb_timestamp_t time = timestamp(); xcb_change_property(kwinApp()->x11Connection(), @@ -128,26 +128,29 @@ void WlSource::sendTimestamp(xcb_selection_request_event_t *event) event->requestor, event->property, XCB_ATOM_INTEGER, - 32, 1, &time); + 32, + 1, + &time); sendSelectionNotify(event, true); } -bool WlSource::checkStartTransfer(xcb_selection_request_event_t *event) +template<typename DeviceIface, typename SourceIface> +bool WlSource<DeviceIface, SourceIface>::checkStartTransfer(xcb_selection_request_event_t* event) { // check interfaces available - if (!m_ddi || !m_dsi) { + if (!m_di || !m_si) { return false; } - const auto targets = Selection::atomToMimeTypes(event->target); + const auto targets = atomToMimeTypes(event->target); if (targets.isEmpty()) { qCDebug(KWIN_XWL) << "Unknown selection atom. Ignoring request."; return false; } const std::string firstTarget = targets[0].toUtf8().constData(); - auto cmp = [firstTarget](std::string const &b) { + auto cmp = [firstTarget](std::string const& b) { if (firstTarget == "text/uri-list") { // Wayland sources might announce the old mime or the new standard return firstTarget == b || b == "text/x-uri"; @@ -155,7 +158,7 @@ bool WlSource::checkStartTransfer(xcb_selection_request_event_t *event) return firstTarget == b; }; // check supported mimes - const auto offers = m_dsi->mimeTypes(); + const auto offers = m_si->mimeTypes(); const auto mimeIt = std::find_if(offers.begin(), offers.end(), cmp); if (mimeIt == offers.end()) { // Requested Mime not supported. Not sending selection. @@ -168,48 +171,46 @@ bool WlSource::checkStartTransfer(xcb_selection_request_event_t *event) return false; } - m_dsi->requestData(*mimeIt, p[1]); + m_si->requestData(*mimeIt, p[1]); waylandServer()->dispatch(); - Q_EMIT transferReady(new xcb_selection_request_event_t(*event), p[0]); + Q_EMIT qobject()->transferReady(new xcb_selection_request_event_t(*event), p[0]); return true; } -X11Source::X11Source(Selection *selection, xcb_xfixes_selection_notify_event_t *event) - : SelectionSource(selection) - , m_owner(event->owner) +template<typename DataSource> +X11Source<DataSource>::X11Source(xcb_xfixes_selection_notify_event_t* event) + : m_owner(event->owner) + , m_timestamp(event->timestamp) + , m_qobject(new qX11Source) { - setTimestamp(event->timestamp); } -void X11Source::getTargets() +template<typename DataSource> +X11Source<DataSource>::~X11Source() { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + delete m_qobject; +} + +template<typename DataSource> +void X11Source<DataSource>::getTargets(xcb_window_t const window, xcb_atom_t const atom) const +{ + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); /* will lead to a selection request event for the new owner */ - xcb_convert_selection(xcbConn, - window(), - selection()->atom(), - atoms->targets, - atoms->wl_selection, - timestamp()); + xcb_convert_selection(xcbConn, window, atom, atoms->targets, atoms->wl_selection, timestamp()); xcb_flush(xcbConn); } using Mime = QPair<QString, xcb_atom_t>; -void X11Source::handleTargets() +template<typename DataSource> +void X11Source<DataSource>::handleTargets(xcb_window_t const requestor) { // receive targets - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - xcb_get_property_cookie_t cookie = xcb_get_property(xcbConn, - 1, - window(), - atoms->wl_selection, - XCB_GET_PROPERTY_TYPE_ANY, - 0, - 4096 - ); - auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_get_property_cookie_t cookie = xcb_get_property( + xcbConn, 1, requestor, atoms->wl_selection, XCB_GET_PROPERTY_TYPE_ANY, 0, 4096); + auto* reply = xcb_get_property_reply(xcbConn, cookie, nullptr); if (!reply) { return; } @@ -222,24 +223,22 @@ void X11Source::handleTargets() QStringList removed; Mimes all; - xcb_atom_t *value = static_cast<xcb_atom_t *>(xcb_get_property_value(reply)); + xcb_atom_t* value = static_cast<xcb_atom_t*>(xcb_get_property_value(reply)); for (uint32_t i = 0; i < reply->value_len; i++) { if (value[i] == XCB_ATOM_NONE) { continue; } - const auto mimeStrings = Selection::atomToMimeTypes(value[i]); + const auto mimeStrings = atomToMimeTypes(value[i]); if (mimeStrings.isEmpty()) { // TODO: this should never happen? assert? continue; } - - const auto mimeIt = std::find_if(m_offers.begin(), m_offers.end(), - [value, i](const Mime &mime) { - return mime.second == value[i]; - } - ); + const auto mimeIt + = std::find_if(m_offers.begin(), m_offers.end(), [value, i](const Mime& mime) { + return mime.second == value[i]; + }); auto mimePair = Mime(mimeStrings[0], value[i]); if (mimeIt == m_offers.end()) { @@ -250,75 +249,79 @@ void X11Source::handleTargets() all << mimePair; } // all left in m_offers are not in the updated targets - for (const auto mimePair : m_offers) { + for (const auto& mimePair : m_offers) { removed << mimePair.first; } m_offers = all; if (!added.isEmpty() || !removed.isEmpty()) { - Q_EMIT offersChanged(added, removed); + Q_EMIT qobject()->offersChanged(added, removed); } free(reply); } -void X11Source::setDataSource(Wrapland::Client::DataSource *dataSource) +template<typename DataSource> +void X11Source<DataSource>::setSource(DataSource* src) { - Q_ASSERT(dataSource); - if (m_dataSource) { - delete m_dataSource; + Q_ASSERT(src); + if (m_source) { + delete m_source; } - m_dataSource = dataSource; + m_source = src; - for (const Mime &offer : m_offers) { - dataSource->offer(offer.first); + for (const Mime& offer : m_offers) { + src->offer(offer.first); } - connect(dataSource, &Wrapland::Client::DataSource::sendDataRequested, - this, &X11Source::startTransfer); + QObject::connect(src, + &DataSource::sendDataRequested, + qobject(), + [this](auto const& mimeName, auto fd) { startTransfer(mimeName, fd); }); } -void X11Source::setOffers(const Mimes &offers) +template<typename DataSource> +void X11Source<DataSource>::setOffers(const Mimes& offers) { // TODO: share code with handleTargets and emit signals accordingly? m_offers = offers; } -bool X11Source::handleSelectionNotify(xcb_selection_notify_event_t *event) +template<typename DataSource> +bool X11Source<DataSource>::handleSelectionNotify(xcb_selection_notify_event_t* event) { - if (event->requestor != window()) { - return false; - } - if (event->selection != selection()->atom()) { - return false; - } if (event->property == XCB_ATOM_NONE) { qCWarning(KWIN_XWL) << "Incoming X selection conversion failed"; return true; } if (event->target == atoms->targets) { - handleTargets(); + handleTargets(event->requestor); return true; } return false; } -void X11Source::startTransfer(const QString &mimeName, qint32 fd) +template<typename DataSource> +void X11Source<DataSource>::startTransfer(const QString& mimeName, qint32 fd) { - const auto mimeIt = std::find_if(m_offers.begin(), m_offers.end(), - [mimeName](const Mime &mime) { - return mime.first == mimeName; - } - ); + const auto mimeIt + = std::find_if(m_offers.begin(), m_offers.end(), [mimeName](const Mime& mime) { + return mime.first == mimeName; + }); if (mimeIt == m_offers.end()) { qCDebug(KWIN_XWL) << "Sending X11 clipboard to Wayland failed: unsupported MIME."; close(fd); return; } - Q_EMIT transferReady((*mimeIt).second, fd); + Q_EMIT qobject()->transferReady((*mimeIt).second, fd); } -} // namespace Xwl -} // namespace KWin +// Templates specializations +template class WlSource<Wrapland::Server::DataDevice, Wrapland::Server::DataSource>; +template class X11Source<Wrapland::Client::DataSource>; +template class WlSource<Wrapland::Server::PrimarySelectionDevice, + Wrapland::Server::PrimarySelectionSource>; +template class X11Source<Wrapland::Client::PrimarySelectionSource>; +} diff --git a/xwl/selection_source.h b/xwl/selection_source.h index 5bfb0b7e657476aece5b6e34a9bbcbf897fe4cd5..546897209d9e087c72f8c23f9a4df07b71ef4117 100644 --- a/xwl/selection_source.h +++ b/xwl/selection_source.h @@ -30,105 +30,98 @@ class QSocketNotifier; struct xcb_selection_request_event_t; struct xcb_xfixes_selection_notify_event_t; -namespace Wrapland +namespace KWin::Xwl { -namespace Client -{ -class DataSource; -} -namespace Server -{ -class DataDevice; -class DataSource; -} -} -namespace KWin -{ -namespace Xwl +/* + * QObject attribute of a WlSource. + * This is a hack around having a template QObject. + */ +class qWlSource : public QObject { -class Selection; + Q_OBJECT + +public: + using QObject::QObject; + +Q_SIGNALS: + void transferReady(xcb_selection_request_event_t* event, qint32 fd); +}; /** - * Base class representing a data source. + * Representing a Wayland native data source. */ -class SelectionSource : public QObject +template<typename DeviceInterface, typename SourceInterface> +class WlSource { - Q_OBJECT - public: - SelectionSource(Selection *selection); + WlSource(DeviceInterface* di); + ~WlSource(); + + void setSourceIface(SourceInterface* si); + + bool handleSelectionRequest(xcb_selection_request_event_t* event); + void sendTargets(xcb_selection_request_event_t* event); + void sendTimestamp(xcb_selection_request_event_t* event); - xcb_timestamp_t timestamp() const { + void receiveOffer(const std::string& mime); + + xcb_timestamp_t timestamp() const + { return m_timestamp; } - void setTimestamp(xcb_timestamp_t time) { + void setTimestamp(xcb_timestamp_t time) + { m_timestamp = time; } -protected: - Selection *selection() const { - return m_selection; - } - void setWindow(xcb_window_t window) { - m_window = window; - } - xcb_window_t window() const { - return m_window; + qWlSource* qobject() const + { + return m_qobject; } private: + bool checkStartTransfer(xcb_selection_request_event_t* event); + + DeviceInterface* m_di = nullptr; + SourceInterface* m_si = nullptr; + + QVector<QString> m_offers; + QMetaObject::Connection m_offerConnection; + xcb_timestamp_t m_timestamp = XCB_CURRENT_TIME; - Selection *m_selection; - xcb_window_t m_window; + qWlSource* m_qobject; - Q_DISABLE_COPY(SelectionSource) + Q_DISABLE_COPY(WlSource) }; -/** - * Representing a Wayland native data source. +using Mimes = QVector<QPair<QString, xcb_atom_t>>; + +/* + * QObject attribute of a X11Source. + * This is a hack around having a template QObject. */ -class WlSource : public SelectionSource +class qX11Source : public QObject { Q_OBJECT public: - WlSource(Selection *selection, Wrapland::Server::DataDevice *ddi); - void setDataSourceIface(Wrapland::Server::DataSource *dsi); - - bool handleSelectionRequest(xcb_selection_request_event_t *event); - void sendTargets(xcb_selection_request_event_t *event); - void sendTimestamp(xcb_selection_request_event_t *event); - - void receiveOffer(const std::string &mime); - void sendSelectionNotify(xcb_selection_request_event_t *event, bool success); + using QObject::QObject; Q_SIGNALS: - void transferReady(xcb_selection_request_event_t *event, qint32 fd); - -private: - bool checkStartTransfer(xcb_selection_request_event_t *event); - - Wrapland::Server::DataDevice *m_ddi = nullptr; - Wrapland::Server::DataSource *m_dsi = nullptr; - - QVector<QString> m_offers; - QMetaObject::Connection m_offerConnection; - - Q_DISABLE_COPY(WlSource) + void offersChanged(const QStringList& added, const QStringList& removed); + void transferReady(xcb_atom_t target, qint32 fd); }; -using Mimes = QVector<QPair<QString, xcb_atom_t> >; - /** * Representing an X data source. */ -class X11Source : public SelectionSource +template<typename Source> +class X11Source { - Q_OBJECT - public: - X11Source(Selection *selection, xcb_xfixes_selection_notify_event_t *event); + X11Source(xcb_xfixes_selection_notify_event_t* event); + ~X11Source(); /** * @param ds must exist. @@ -136,40 +129,50 @@ public: * X11Source does not take ownership of it in general, but if the function * is called again, it will delete the previous data source. */ - void setDataSource(Wrapland::Client::DataSource *dataSource); - Wrapland::Client::DataSource *dataSource() const { - return m_dataSource; + void setSource(Source* src); + Source* source() const + { + return m_source; } - void getTargets(); + void getTargets(xcb_window_t const window, xcb_atom_t const atom) const; - Mimes offers() const { + Mimes offers() const + { return m_offers; } - void setOffers(const Mimes &offers); + void setOffers(const Mimes& offers); - bool handleSelectionNotify(xcb_selection_notify_event_t *event); + bool handleSelectionNotify(xcb_selection_notify_event_t* event); - void setRequestor(xcb_window_t window) { - setWindow(window); + xcb_timestamp_t timestamp() const + { + return m_timestamp; + } + void setTimestamp(xcb_timestamp_t time) + { + m_timestamp = time; } -Q_SIGNALS: - void offersChanged(const QStringList &added, const QStringList &removed); - void transferReady(xcb_atom_t target, qint32 fd); + qX11Source* qobject() const + { + return m_qobject; + } private: - void handleTargets(); - void startTransfer(const QString &mimeName, qint32 fd); + void handleTargets(xcb_window_t const requestor); + void startTransfer(const QString& mimeName, qint32 fd); xcb_window_t m_owner; - Wrapland::Client::DataSource *m_dataSource = nullptr; + Source* m_source = nullptr; Mimes m_offers; + xcb_timestamp_t m_timestamp = XCB_CURRENT_TIME; + qX11Source* m_qobject; + Q_DISABLE_COPY(X11Source) }; -} // namespace Xwl -} // namespace KWin +} #endif diff --git a/xwl/transfer.cpp b/xwl/transfer.cpp index 16b34f66e0c2a46788a7f816a6a49a09a7d8c339..c3d0607b3642281d121e8260abe535a4d610f603 100644 --- a/xwl/transfer.cpp +++ b/xwl/transfer.cpp @@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. *********************************************************************/ #include "transfer.h" -#include "databridge.h" #include "xwayland.h" #include "atoms.h" @@ -27,8 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "workspace.h" #include <Wrapland/Client/connection_thread.h> -#include <Wrapland/Client/datadevicemanager.h> #include <Wrapland/Client/datadevice.h> +#include <Wrapland/Client/datadevicemanager.h> #include <Wrapland/Client/datasource.h> #include <Wrapland/Server/data_device.h> @@ -51,7 +50,7 @@ namespace Xwl // in Bytes: equals 64KB static const uint32_t s_incrChunkSize = 63 * 1024; -Transfer::Transfer(xcb_atom_t selection, qint32 fd, xcb_timestamp_t timestamp, QObject *parent) +Transfer::Transfer(xcb_atom_t selection, qint32 fd, xcb_timestamp_t timestamp, QObject* parent) : QObject(parent) , m_atom(selection) , m_fd(fd) @@ -95,8 +94,10 @@ void Transfer::closeFd() m_fd = -1; } -TransferWltoX::TransferWltoX(xcb_atom_t selection, xcb_selection_request_event_t *request, - qint32 fd, QObject *parent) +TransferWltoX::TransferWltoX(xcb_atom_t selection, + xcb_selection_request_event_t* request, + qint32 fd, + QObject* parent) : Transfer(selection, fd, 0, parent) , m_request(request) { @@ -111,17 +112,15 @@ TransferWltoX::~TransferWltoX() void TransferWltoX::startTransferFromSource() { createSocketNotifier(QSocketNotifier::Read); - connect(socketNotifier(), &QSocketNotifier::activated, this, - [this](int socket) { - Q_UNUSED(socket); - readWlSource(); - } - ); + connect(socketNotifier(), &QSocketNotifier::activated, this, [this](int socket) { + Q_UNUSED(socket); + readWlSource(); + }); } int TransferWltoX::flushSourceData() { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); xcb_change_property(xcbConn, XCB_PROP_MODE_REPLACE, @@ -144,12 +143,10 @@ void TransferWltoX::startIncr() { Q_ASSERT(m_chunks.size() == 1); - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); - uint32_t mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; - xcb_change_window_attributes (xcbConn, - m_request->requestor, - XCB_CW_EVENT_MASK, mask); + uint32_t mask[] = {XCB_EVENT_MASK_PROPERTY_CHANGE}; + xcb_change_window_attributes(xcbConn, m_request->requestor, XCB_CW_EVENT_MASK, mask); // spec says to make the available space larger const uint32_t chunkSpace = 1024 + s_incrChunkSize; @@ -158,7 +155,9 @@ void TransferWltoX::startIncr() m_request->requestor, m_request->property, atoms->incr, - 32, 1, &chunkSpace); + 32, + 1, + &chunkSpace); xcb_flush(xcbConn); setIncr(true); @@ -171,8 +170,7 @@ void TransferWltoX::startIncr() void TransferWltoX::readWlSource() { - if (m_chunks.size() == 0 || - m_chunks.last().second == s_incrChunkSize) { + if (m_chunks.size() == 0 || m_chunks.last().second == s_incrChunkSize) { // append new chunk auto next = QPair<QByteArray, int>(); next.first.resize(s_incrChunkSize); @@ -229,11 +227,10 @@ void TransferWltoX::readWlSource() resetTimeout(); } -bool TransferWltoX::handlePropertyNotify(xcb_property_notify_event_t *event) +bool TransferWltoX::handlePropertyNotify(xcb_property_notify_event_t* event) { if (event->window == m_request->requestor) { - if (event->state == XCB_PROPERTY_DELETE && - event->atom == m_request->property) { + if (event->state == XCB_PROPERTY_DELETE && event->atom == m_request->property) { handlePropertyDelete(); } return true; @@ -252,19 +249,19 @@ void TransferWltoX::handlePropertyDelete() if (m_flushPropertyOnDelete) { if (!socketNotifier() && m_chunks.isEmpty()) { // transfer complete - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); uint32_t mask[] = {0}; - xcb_change_window_attributes (xcbConn, - m_request->requestor, - XCB_CW_EVENT_MASK, mask); + xcb_change_window_attributes(xcbConn, m_request->requestor, XCB_CW_EVENT_MASK, mask); xcb_change_property(xcbConn, XCB_PROP_MODE_REPLACE, m_request->requestor, m_request->property, m_request->target, - 8, 0, nullptr); + 8, + 0, + nullptr); xcb_flush(xcbConn); m_flushPropertyOnDelete = false; endTransfer(); @@ -274,40 +271,39 @@ void TransferWltoX::handlePropertyDelete() } } -TransferXtoWl::TransferXtoWl(xcb_atom_t selection, xcb_atom_t target, qint32 fd, - xcb_timestamp_t timestamp, xcb_window_t parentWindow, - QObject *parent) +TransferXtoWl::TransferXtoWl(xcb_atom_t selection, + xcb_atom_t target, + qint32 fd, + xcb_timestamp_t timestamp, + xcb_window_t parentWindow, + QObject* parent) : Transfer(selection, fd, timestamp, parent) { // create transfer window - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); m_window = xcb_generate_id(xcbConn); - const uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_PROPERTY_CHANGE }; + const uint32_t values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; xcb_create_window(xcbConn, XCB_COPY_FROM_PARENT, m_window, parentWindow, - 0, 0, - 10, 10, + 0, + 0, + 10, + 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, Xwayland::self()->xcbScreen()->root_visual, XCB_CW_EVENT_MASK, values); // convert selection - xcb_convert_selection(xcbConn, - m_window, - selection, - target, - atoms->wl_selection, - timestamp); + xcb_convert_selection(xcbConn, m_window, selection, target, atoms->wl_selection, timestamp); xcb_flush(xcbConn); } TransferXtoWl::~TransferXtoWl() { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); xcb_destroy_window(xcbConn, m_window); xcb_flush(xcbConn); @@ -315,11 +311,10 @@ TransferXtoWl::~TransferXtoWl() m_receiver = nullptr; } -bool TransferXtoWl::handlePropertyNotify(xcb_property_notify_event_t *event) +bool TransferXtoWl::handlePropertyNotify(xcb_property_notify_event_t* event) { if (event->window == m_window) { - if (event->state == XCB_PROPERTY_NEW_VALUE && - event->atom == atoms->wl_selection) { + if (event->state == XCB_PROPERTY_NEW_VALUE && event->atom == atoms->wl_selection) { getIncrChunk(); } return true; @@ -327,7 +322,7 @@ bool TransferXtoWl::handlePropertyNotify(xcb_property_notify_event_t *event) return false; } -bool TransferXtoWl::handleSelectionNotify(xcb_selection_notify_event_t *event) +bool TransferXtoWl::handleSelectionNotify(xcb_selection_notify_event_t* event) { if (event->requestor != m_window) { return false; @@ -364,17 +359,11 @@ bool TransferXtoWl::handleSelectionNotify(xcb_selection_notify_event_t *event) void TransferXtoWl::startTransfer() { - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - auto cookie = xcb_get_property(xcbConn, - 1, - m_window, - atoms->wl_selection, - XCB_GET_PROPERTY_TYPE_ANY, - 0, - 0x1fffffff - ); - - auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + auto cookie = xcb_get_property( + xcbConn, 1, m_window, atoms->wl_selection, XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff); + + auto* reply = xcb_get_property_reply(xcbConn, cookie, nullptr); if (reply == nullptr) { qCWarning(KWIN_XWL) << "Can't get selection property."; endTransfer(); @@ -402,17 +391,12 @@ void TransferXtoWl::getIncrChunk() // receive mechanism has not yet been setup return; } - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); - auto cookie = xcb_get_property(xcbConn, - 0, - m_window, - atoms->wl_selection, - XCB_GET_PROPERTY_TYPE_ANY, - 0, - 0x1fffffff); + auto cookie = xcb_get_property( + xcbConn, 0, m_window, atoms->wl_selection, XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff); - auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); + auto* reply = xcb_get_property_reply(xcbConn, cookie, nullptr); if (!reply) { qCWarning(KWIN_XWL) << "Can't get selection property."; endTransfer(); @@ -438,16 +422,16 @@ DataReceiver::~DataReceiver() } } -void DataReceiver::transferFromProperty(xcb_get_property_reply_t *reply) +void DataReceiver::transferFromProperty(xcb_get_property_reply_t* reply) { m_propertyStart = 0; m_propertyReply = reply; - setData(static_cast<char *>(xcb_get_property_value(reply)), + setData(static_cast<char*>(xcb_get_property_value(reply)), xcb_get_property_value_length(reply)); } -void DataReceiver::setData(const char *value, int length) +void DataReceiver::setData(const char* value, int length) { // simply set data without copy m_data = QByteArray::fromRawData(value, length); @@ -469,7 +453,7 @@ void DataReceiver::partRead(int length) } } -void NetscapeUrlReceiver::setData(const char *value, int length) +void NetscapeUrlReceiver::setData(const char* value, int length) { auto origData = QByteArray::fromRawData(value, length); @@ -507,14 +491,15 @@ void NetscapeUrlReceiver::setData(const char *value, int length) setDataInternal(data); } -void MozUrlReceiver::setData(const char *value, int length) +void MozUrlReceiver::setData(const char* value, int length) { // represent as QByteArray (guaranteed '\0'-terminated) const auto origData = QByteArray::fromRawData(value, length); // text/x-moz-url data is sent in utf-16 - copies the content // and converts it into 8 byte representation - const auto byteData = QString::fromUtf16(reinterpret_cast<const char16_t *>(origData.data())).toLatin1(); + const auto byteData + = QString::fromUtf16(reinterpret_cast<const char16_t*>(origData.data())).toLatin1(); if (byteData.indexOf('\n') == -1) { // there are no line breaks, not in text/x-moz-url format or empty, @@ -566,10 +551,8 @@ void TransferXtoWl::dataSourceWrite() // property completely transferred if (incr()) { clearSocketNotifier(); - xcb_connection_t *xcbConn = kwinApp()->x11Connection(); - xcb_delete_property(xcbConn, - m_window, - atoms->wl_selection); + xcb_connection_t* xcbConn = kwinApp()->x11Connection(); + xcb_delete_property(xcbConn, m_window, atoms->wl_selection); xcb_flush(xcbConn); } else { // transfer complete @@ -578,12 +561,10 @@ void TransferXtoWl::dataSourceWrite() } else { if (!socketNotifier()) { createSocketNotifier(QSocketNotifier::Write); - connect(socketNotifier(), &QSocketNotifier::activated, this, - [this](int socket) { - Q_UNUSED(socket); - dataSourceWrite(); - } - ); + connect(socketNotifier(), &QSocketNotifier::activated, this, [this](int socket) { + Q_UNUSED(socket); + dataSourceWrite(); + }); } } resetTimeout(); diff --git a/xwl/transfer.h b/xwl/transfer.h index 8e645c318ca8ac8c7d8b1c4be2870e6bbda5d78d..f11974e4acdee4a2d29e6acaba2d713e4957a900 100644 --- a/xwl/transfer.h +++ b/xwl/transfer.h @@ -57,14 +57,12 @@ class Transfer : public QObject Q_OBJECT public: - Transfer(xcb_atom_t selection, - qint32 fd, - xcb_timestamp_t timestamp, - QObject *parent = nullptr); + Transfer(xcb_atom_t selection, qint32 fd, xcb_timestamp_t timestamp, QObject* parent = nullptr); - virtual bool handlePropertyNotify(xcb_property_notify_event_t *event) = 0; + virtual bool handlePropertyNotify(xcb_property_notify_event_t* event) = 0; void timeout(); - xcb_timestamp_t timestamp() const { + xcb_timestamp_t timestamp() const + { return m_timestamp; } @@ -74,27 +72,34 @@ Q_SIGNALS: protected: void endTransfer(); - xcb_atom_t atom() const { + xcb_atom_t atom() const + { return m_atom; } - qint32 fd() const { + qint32 fd() const + { return m_fd; } - void setIncr(bool set) { + void setIncr(bool set) + { m_incr = set; } - bool incr() const { + bool incr() const + { return m_incr; } - void resetTimeout() { + void resetTimeout() + { m_timeout = false; } void createSocketNotifier(QSocketNotifier::Type type); void clearSocketNotifier(); - QSocketNotifier *socketNotifier() const { + QSocketNotifier* socketNotifier() const + { return m_notifier; } + private: void closeFd(); @@ -102,7 +107,7 @@ private: qint32 m_fd; xcb_timestamp_t m_timestamp = XCB_CURRENT_TIME; - QSocketNotifier *m_notifier = nullptr; + QSocketNotifier* m_notifier = nullptr; bool m_incr = false; bool m_timeout = false; @@ -118,16 +123,16 @@ class TransferWltoX : public Transfer public: TransferWltoX(xcb_atom_t selection, - xcb_selection_request_event_t *request, + xcb_selection_request_event_t* request, qint32 fd, - QObject *parent = nullptr); + QObject* parent = nullptr); ~TransferWltoX() override; void startTransferFromSource(); - bool handlePropertyNotify(xcb_property_notify_event_t *event) override; + bool handlePropertyNotify(xcb_property_notify_event_t* event) override; Q_SIGNALS: - void selectionNotify(xcb_selection_request_event_t *event, bool success); + void selectionNotify(xcb_selection_request_event_t* event, bool success); private: void startIncr(); @@ -135,12 +140,12 @@ private: int flushSourceData(); void handlePropertyDelete(); - xcb_selection_request_event_t *m_request = nullptr; + xcb_selection_request_event_t* m_request = nullptr; /* contains all received data portioned in chunks * TODO: explain second QPair component */ - QVector<QPair<QByteArray, int> > m_chunks; + QVector<QPair<QByteArray, int>> m_chunks; bool m_propertyIsSet = false; bool m_flushPropertyOnDelete = false; @@ -156,21 +161,21 @@ class DataReceiver public: virtual ~DataReceiver(); - void transferFromProperty(xcb_get_property_reply_t *reply); - + void transferFromProperty(xcb_get_property_reply_t* reply); - virtual void setData(const char *value, int length); + virtual void setData(const char* value, int length); QByteArray data() const; void partRead(int length); protected: - void setDataInternal(QByteArray data) { + void setDataInternal(QByteArray data) + { m_data = data; } private: - xcb_get_property_reply_t *m_propertyReply = nullptr; + xcb_get_property_reply_t* m_propertyReply = nullptr; int m_propertyStart = 0; QByteArray m_data; }; @@ -182,7 +187,7 @@ private: class NetscapeUrlReceiver : public DataReceiver { public: - void setData(const char *value, int length) override; + void setData(const char* value, int length) override; }; /** @@ -192,7 +197,7 @@ public: class MozUrlReceiver : public DataReceiver { public: - void setData(const char *value, int length) override; + void setData(const char* value, int length) override; }; /** @@ -206,12 +211,13 @@ public: TransferXtoWl(xcb_atom_t selection, xcb_atom_t target, qint32 fd, - xcb_timestamp_t timestamp, xcb_window_t parentWindow, - QObject *parent = nullptr); + xcb_timestamp_t timestamp, + xcb_window_t parentWindow, + QObject* parent = nullptr); ~TransferXtoWl() override; - bool handleSelectionNotify(xcb_selection_notify_event_t *event); - bool handlePropertyNotify(xcb_property_notify_event_t *event) override; + bool handleSelectionNotify(xcb_selection_notify_event_t* event); + bool handlePropertyNotify(xcb_property_notify_event_t* event) override; private: void dataSourceWrite(); @@ -219,7 +225,7 @@ private: void getIncrChunk(); xcb_window_t m_window; - DataReceiver *m_receiver = nullptr; + DataReceiver* m_receiver = nullptr; Q_DISABLE_COPY(TransferXtoWl) }; diff --git a/xwl/xwayland.cpp b/xwl/xwayland.cpp index 00c79afcae20b83a06cdebe751fd1ef9eab28d13..41a7f363b5e7648d2425427123e371c22326c986 100644 --- a/xwl/xwayland.cpp +++ b/xwl/xwayland.cpp @@ -45,8 +45,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <unistd.h> #endif -#include <sys/socket.h> #include <iostream> +#include <sys/socket.h> static void readDisplay(int pipe) { @@ -58,7 +58,7 @@ static void readDisplay(int pipe) QByteArray displayNumber = readPipe.readLine(); displayNumber.prepend(QByteArray(":")); - displayNumber.remove(displayNumber.size() -1, 1); + displayNumber.remove(displayNumber.size() - 1, 1); std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; setenv("DISPLAY", displayNumber.constData(), true); @@ -72,14 +72,14 @@ namespace KWin namespace Xwl { -Xwayland *s_self = nullptr; +Xwayland* s_self = nullptr; -Xwayland *Xwayland::self() +Xwayland* Xwayland::self() { return s_self; } -Xwayland::Xwayland(ApplicationWaylandAbstract *app, QObject *parent) +Xwayland::Xwayland(ApplicationWaylandAbstract* app, QObject* parent) : XwaylandInterface(parent) , m_app(app) { @@ -156,29 +156,34 @@ void Xwayland::init() } m_xwaylandProcess->setProcessEnvironment(env); m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), - QString::number(pipeFds[1]), - QStringLiteral("-rootless"), - QStringLiteral("-wm"), - QString::number(fd)}); - m_xwaylandFailConnection = connect(m_xwaylandProcess, &QProcess::errorOccurred, this, - [this] (QProcess::ProcessError error) { + QString::number(pipeFds[1]), + QStringLiteral("-rootless"), + QStringLiteral("-wm"), + QString::number(fd)}); + m_xwaylandFailConnection = connect( + m_xwaylandProcess, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl; } else { std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl; } Q_EMIT criticalError(1); - } - ); + }); const int xDisplayPipe = pipeFds[0]; - connect(m_xwaylandProcess, &QProcess::started, this, - [this, xDisplayPipe] { - QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this); - QObject::connect(watcher, &QFutureWatcher<void>::finished, this, &Xwayland::continueStartupWithX, Qt::QueuedConnection); - QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater, Qt::QueuedConnection); - watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); - } - ); + connect(m_xwaylandProcess, &QProcess::started, this, [this, xDisplayPipe] { + QFutureWatcher<void>* watcher = new QFutureWatcher<void>(this); + QObject::connect(watcher, + &QFutureWatcher<void>::finished, + this, + &Xwayland::continueStartupWithX, + Qt::QueuedConnection); + QObject::connect(watcher, + &QFutureWatcher<void>::finished, + watcher, + &QFutureWatcher<void>::deleteLater, + Qt::QueuedConnection); + watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); + }); m_xwaylandProcess->start(); close(pipeFds[1]); } @@ -192,7 +197,7 @@ void Xwayland::prepareDestroy() void Xwayland::createX11Connection() { int screenNumber = 0; - xcb_connection_t *c = nullptr; + xcb_connection_t* c = nullptr; if (m_xcbConnectionFd == -1) { c = xcb_connect(nullptr, &screenNumber); } else { @@ -217,13 +222,14 @@ void Xwayland::createX11Connection() void Xwayland::continueStartupWithX() { createX11Connection(); - xcb_connection_t *xcbConn = m_app->x11Connection(); + xcb_connection_t* xcbConn = m_app->x11Connection(); if (!xcbConn) { // about to quit Q_EMIT criticalError(1); return; } - QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcbConn), QSocketNotifier::Read, this); + QSocketNotifier* notifier + = new QSocketNotifier(xcb_get_file_descriptor(xcbConn), QSocketNotifier::Read, this); auto processXcbEvents = [this, xcbConn] { while (auto event = xcb_poll_for_event(xcbConn)) { if (m_dataBridge->filterEvent(event)) { @@ -231,14 +237,21 @@ void Xwayland::continueStartupWithX() continue; } long result = 0; - QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result); + QThread::currentThread()->eventDispatcher()->filterNativeEvent( + QByteArrayLiteral("xcb_generic_event_t"), event, &result); free(event); } xcb_flush(xcbConn); }; connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); - connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); - connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); + connect(QThread::currentThread()->eventDispatcher(), + &QAbstractEventDispatcher::aboutToBlock, + this, + processXcbEvents); + connect(QThread::currentThread()->eventDispatcher(), + &QAbstractEventDispatcher::awake, + this, + processXcbEvents); xcb_prefetch_extension_data(xcbConn, &xcb_xfixes_id); m_xfixes = xcb_get_extension_data(xcbConn, &xcb_xfixes_id); @@ -254,13 +267,15 @@ void Xwayland::continueStartupWithX() // Check whether another windowmanager is running const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}; - ScopedCPointer<xcb_generic_error_t> redirectCheck(xcb_request_check(connection(), - xcb_change_window_attributes_checked(connection(), - rootWindow(), - XCB_CW_EVENT_MASK, - maskValues))); + ScopedCPointer<xcb_generic_error_t> redirectCheck( + xcb_request_check(connection(), + xcb_change_window_attributes_checked( + connection(), rootWindow(), XCB_CW_EVENT_MASK, maskValues))); if (!redirectCheck.isNull()) { - fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr); + fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n") + .toLocal8Bit() + .constData(), + stderr); Q_EMIT criticalError(1); return; } @@ -274,7 +289,7 @@ void Xwayland::continueStartupWithX() Xcb::sync(); // Trigger possible errors, there's still a chance to abort } -DragEventReply Xwayland::dragMoveFilter(Toplevel *target, const QPoint &pos) +DragEventReply Xwayland::dragMoveFilter(Toplevel* target, const QPoint& pos) { if (!m_dataBridge) { return DragEventReply::Wayland; diff --git a/xwl/xwayland.h b/xwl/xwayland.h index b7ad6e63013c587d68f1c641094f9385eae81482..0ca11854e7930c852af69bff668e07d2ceda4af9 100644 --- a/xwl/xwayland.h +++ b/xwl/xwayland.h @@ -41,18 +41,20 @@ class KWIN_EXPORT Xwayland : public XwaylandInterface Q_OBJECT public: - static Xwayland *self(); + static Xwayland* self(); - Xwayland(ApplicationWaylandAbstract *app, QObject *parent = nullptr); + Xwayland(ApplicationWaylandAbstract* app, QObject* parent = nullptr); ~Xwayland() override; void init(); void prepareDestroy(); - xcb_screen_t *xcbScreen() const { + xcb_screen_t* xcbScreen() const + { return m_xcbScreen; } - const xcb_query_extension_reply_t *xfixes() const { + const xcb_query_extension_reply_t* xfixes() const + { return m_xfixes; } @@ -64,17 +66,17 @@ private: void createX11Connection(); void continueStartupWithX(); - DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos) override; + DragEventReply dragMoveFilter(Toplevel* target, const QPoint& pos) override; int m_xcbConnectionFd = -1; - QProcess *m_xwaylandProcess = nullptr; + QProcess* m_xwaylandProcess = nullptr; QMetaObject::Connection m_xwaylandFailConnection; - xcb_screen_t *m_xcbScreen = nullptr; - const xcb_query_extension_reply_t *m_xfixes = nullptr; - DataBridge *m_dataBridge = nullptr; + xcb_screen_t* m_xcbScreen = nullptr; + const xcb_query_extension_reply_t* m_xfixes = nullptr; + DataBridge* m_dataBridge = nullptr; - ApplicationWaylandAbstract *m_app; + ApplicationWaylandAbstract* m_app; Q_DISABLE_COPY(Xwayland) }; diff --git a/xwl/xwayland_interface.cpp b/xwl/xwayland_interface.cpp index 454b4ce939fb7fcf9a6a5713ccc511e8d8742228..c51198643a9a2d67c04c6c9b612790907f625cb5 100644 --- a/xwl/xwayland_interface.cpp +++ b/xwl/xwayland_interface.cpp @@ -22,14 +22,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. namespace KWin { -XwaylandInterface *s_self = nullptr; +XwaylandInterface* s_self = nullptr; -XwaylandInterface *XwaylandInterface::self() +XwaylandInterface* XwaylandInterface::self() { return s_self; } -XwaylandInterface::XwaylandInterface(QObject *parent) +XwaylandInterface::XwaylandInterface(QObject* parent) : QObject(parent) { s_self = this; diff --git a/xwl/xwayland_interface.h b/xwl/xwayland_interface.h index a7ef7cb98776cd4ba02405e131a992cc55313296..200508e80a295acdf3866b19a63b27f6ca1aa76d 100644 --- a/xwl/xwayland_interface.h +++ b/xwl/xwayland_interface.h @@ -46,19 +46,19 @@ class KWIN_EXPORT XwaylandInterface : public QObject Q_OBJECT public: - static XwaylandInterface *self(); + static XwaylandInterface* self(); - virtual Xwl::DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos) = 0; + virtual Xwl::DragEventReply dragMoveFilter(Toplevel* target, const QPoint& pos) = 0; protected: - explicit XwaylandInterface(QObject *parent = nullptr); + explicit XwaylandInterface(QObject* parent = nullptr); ~XwaylandInterface() override; private: Q_DISABLE_COPY(XwaylandInterface) }; -inline XwaylandInterface *xwayland() +inline XwaylandInterface* xwayland() { return XwaylandInterface::self(); }