/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2015 Martin Gräßlin SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "kwin_wayland_test.h" #if KWIN_BUILD_X11 #include "atoms.h" #endif #include "input_event.h" #include "inputmethod.h" #include "wayland-client/linuxdmabuf.h" #include "wayland-client/viewporter.h" #include "wayland-linux-dmabuf-unstable-v1-client-protocol.h" #include "wayland-viewporter-client-protocol.h" #include "wayland-zkde-screencast-unstable-v1-client-protocol.h" #include "wayland/display.h" #include "wayland_server.h" #include "workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // screenlocker #if KWIN_BUILD_SCREENLOCKER #include #endif #include #include #include #include // system #include #include #include #include namespace KWin { namespace Test { static std::unique_ptr s_waylandConnection; LayerShellV1::~LayerShellV1() { destroy(); } LayerSurfaceV1::~LayerSurfaceV1() { destroy(); } void LayerSurfaceV1::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) { Q_EMIT configureRequested(serial, QSize(width, height)); } void LayerSurfaceV1::zwlr_layer_surface_v1_closed() { Q_EMIT closeRequested(); } XdgShell::~XdgShell() { destroy(); } XdgSurface::XdgSurface(XdgShell *shell, KWayland::Client::Surface *surface, QObject *parent) : QObject(parent) , QtWayland::xdg_surface(shell->get_xdg_surface(*surface)) , m_surface(surface) { } XdgSurface::~XdgSurface() { destroy(); } KWayland::Client::Surface *XdgSurface::surface() const { return m_surface; } void XdgSurface::xdg_surface_configure(uint32_t serial) { Q_EMIT configureRequested(serial); } XdgToplevel::XdgToplevel(XdgSurface *surface, QObject *parent) : QObject(parent) , QtWayland::xdg_toplevel(surface->get_toplevel()) , m_xdgSurface(surface) { } XdgToplevel::~XdgToplevel() { destroy(); } XdgSurface *XdgToplevel::xdgSurface() const { return m_xdgSurface.get(); } void XdgToplevel::xdg_toplevel_configure(int32_t width, int32_t height, wl_array *states) { States requestedStates; const uint32_t *stateData = static_cast(states->data); const size_t stateCount = states->size / sizeof(uint32_t); for (size_t i = 0; i < stateCount; ++i) { switch (stateData[i]) { case QtWayland::xdg_toplevel::state_maximized: requestedStates |= State::Maximized; break; case QtWayland::xdg_toplevel::state_fullscreen: requestedStates |= State::Fullscreen; break; case QtWayland::xdg_toplevel::state_resizing: requestedStates |= State::Resizing; break; case QtWayland::xdg_toplevel::state_activated: requestedStates |= State::Activated; break; } } Q_EMIT configureRequested(QSize(width, height), requestedStates); } void XdgToplevel::xdg_toplevel_close() { Q_EMIT closeRequested(); } XdgPositioner::XdgPositioner(XdgShell *shell) : QtWayland::xdg_positioner(shell->create_positioner()) { } XdgPositioner::~XdgPositioner() { destroy(); } XdgPopup::XdgPopup(XdgSurface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, QObject *parent) : QObject(parent) , QtWayland::xdg_popup(surface->get_popup(parentSurface->object(), positioner->object())) , m_xdgSurface(surface) { } XdgPopup::~XdgPopup() { destroy(); } XdgSurface *XdgPopup::xdgSurface() const { return m_xdgSurface.get(); } void XdgPopup::xdg_popup_configure(int32_t x, int32_t y, int32_t width, int32_t height) { Q_EMIT configureRequested(Rect(x, y, width, height)); } void XdgPopup::xdg_popup_popup_done() { Q_EMIT doneReceived(); } void XdgPopup::xdg_popup_repositioned(uint32_t token) { Q_EMIT repositioned(token); } XdgDecorationManagerV1::~XdgDecorationManagerV1() { destroy(); } XdgToplevelDecorationV1::XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager, XdgToplevel *toplevel, QObject *parent) : QObject(parent) , QtWayland::zxdg_toplevel_decoration_v1(manager->get_toplevel_decoration(toplevel->object())) { } XdgToplevelDecorationV1::~XdgToplevelDecorationV1() { destroy(); } void XdgToplevelDecorationV1::zxdg_toplevel_decoration_v1_configure(uint32_t m) { Q_EMIT configureRequested(mode(m)); } IdleInhibitManagerV1::~IdleInhibitManagerV1() { destroy(); } IdleInhibitorV1::IdleInhibitorV1(IdleInhibitManagerV1 *manager, KWayland::Client::Surface *surface) : QtWayland::zwp_idle_inhibitor_v1(manager->create_inhibitor(*surface)) { } IdleInhibitorV1::~IdleInhibitorV1() { destroy(); } ScreenEdgeManagerV1::~ScreenEdgeManagerV1() { destroy(); } AutoHideScreenEdgeV1::AutoHideScreenEdgeV1(ScreenEdgeManagerV1 *manager, KWayland::Client::Surface *surface, uint32_t border) : QtWayland::kde_auto_hide_screen_edge_v1(manager->get_auto_hide_screen_edge(border, *surface)) { } AutoHideScreenEdgeV1::~AutoHideScreenEdgeV1() { destroy(); } CursorShapeManagerV1::~CursorShapeManagerV1() { destroy(); } CursorShapeDeviceV1::CursorShapeDeviceV1(CursorShapeManagerV1 *manager, KWayland::Client::Pointer *pointer) : QtWayland::wp_cursor_shape_device_v1(manager->get_pointer(*pointer)) { } CursorShapeDeviceV1::~CursorShapeDeviceV1() { destroy(); } FakeInput::~FakeInput() { destroy(); } SecurityContextManagerV1::~SecurityContextManagerV1() { destroy(); } XdgWmDialogV1::~XdgWmDialogV1() { destroy(); } XdgDialogV1::XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel) : QtWayland::xdg_dialog_v1(wm->get_xdg_dialog(toplevel->object())) { } XdgDialogV1::~XdgDialogV1() { destroy(); } MockInputMethod *inputMethod() { return s_waylandConnection->inputMethodV1; } KWayland::Client::Surface *inputPanelSurface() { return s_waylandConnection->inputMethodV1->inputPanelSurface(); } MockInputMethod::MockInputMethod(struct wl_registry *registry, int id, int version) : QtWayland::zwp_input_method_v1(registry, id, version) { } MockInputMethod::~MockInputMethod() { } void MockInputMethod::zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *context) { if (!m_inputSurface) { m_inputSurface = Test::createSurface(); m_inputMethodSurface = Test::createInputPanelSurfaceV1(m_inputSurface.get(), s_waylandConnection->outputs.first(), m_mode); } m_context = context; switch (m_mode) { case Mode::TopLevel: Test::render(m_inputSurface.get(), QSize(1280, 400), Qt::blue); break; case Mode::Overlay: Test::render(m_inputSurface.get(), QSize(200, 50), Qt::blue); break; } Q_EMIT activate(); } void MockInputMethod::setMode(MockInputMethod::Mode mode) { m_mode = mode; } void MockInputMethod::zwp_input_method_v1_deactivate(struct ::zwp_input_method_context_v1 *context) { QCOMPARE(context, m_context); zwp_input_method_context_v1_destroy(context); m_context = nullptr; if (m_inputSurface) { m_inputSurface->release(); m_inputSurface->destroy(); m_inputSurface.reset(); m_inputMethodSurface.reset(); } } bool setupWaylandConnection(AdditionalWaylandInterfaces flags) { if (s_waylandConnection) { return false; } s_waylandConnection = Connection::setup(flags); return bool(s_waylandConnection); } void destroyWaylandConnection() { s_waylandConnection.reset(); } std::unique_ptr Connection::setup(AdditionalWaylandInterfaces flags) { int sx[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { return nullptr; } KWin::waylandServer()->display()->createClient(sx[0]); // setup connection auto connection = std::make_unique(); connection->connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(connection->connection, &KWayland::Client::ConnectionThread::connected); if (!connectedSpy.isValid()) { return nullptr; } connection->connection->setSocketFd(sx[1]); connection->thread = new QThread(kwinApp()); connection->connection->moveToThread(connection->thread); connection->thread->start(); connection->connection->initConnection(); if (!connectedSpy.wait()) { return nullptr; } connection->queue = new KWayland::Client::EventQueue; connection->queue->setup(connection->connection); if (!connection->queue->isValid()) { return nullptr; } KWayland::Client::Registry *registry = new KWayland::Client::Registry; connection->registry = registry; registry->setEventQueue(connection->queue); QObject::connect(registry, &KWayland::Client::Registry::outputAnnounced, [c = connection.get()](quint32 name, quint32 version) { KWayland::Client::Output *output = c->registry->createOutput(name, version, c->registry); c->outputs << output; QObject::connect(output, &KWayland::Client::Output::removed, [=]() { output->deleteLater(); c->outputs.removeOne(output); }); QObject::connect(output, &KWayland::Client::Output::destroyed, [=]() { c->outputs.removeOne(output); }); }); QObject::connect(registry, &KWayland::Client::Registry::interfaceAnnounced, [c = connection.get(), flags](const QByteArray &interface, quint32 name, quint32 version) { if (flags & AdditionalWaylandInterface::InputMethodV1) { if (interface == QByteArrayLiteral("zwp_input_method_v1")) { c->inputMethodV1 = new MockInputMethod(*c->registry, name, version); } else if (interface == QByteArrayLiteral("zwp_input_panel_v1")) { c->inputPanelV1 = new QtWayland::zwp_input_panel_v1(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::LayerShellV1) { if (interface == QByteArrayLiteral("zwlr_layer_shell_v1")) { c->layerShellV1 = new LayerShellV1(); c->layerShellV1->init(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::TextInputManagerV3) { // do something if (interface == QByteArrayLiteral("zwp_text_input_manager_v3")) { c->textInputManagerV3 = new TextInputManagerV3(); c->textInputManagerV3->init(*c->registry, name, version); } } if (interface == QByteArrayLiteral("xdg_wm_base")) { c->xdgShell = new XdgShell(); c->xdgShell->init(*c->registry, name, version); } if (flags & AdditionalWaylandInterface::XdgDecorationV1) { if (interface == zxdg_decoration_manager_v1_interface.name) { c->xdgDecorationManagerV1 = new XdgDecorationManagerV1(); c->xdgDecorationManagerV1->init(*c->registry, name, version); return; } } if (flags & AdditionalWaylandInterface::IdleInhibitV1) { if (interface == zwp_idle_inhibit_manager_v1_interface.name) { c->idleInhibitManagerV1 = new IdleInhibitManagerV1(); c->idleInhibitManagerV1->init(*c->registry, name, version); return; } } if (flags & AdditionalWaylandInterface::OutputDeviceV2) { if (interface == kde_output_device_v2_interface.name) { WaylandOutputDeviceV2 *device = new WaylandOutputDeviceV2(name); device->init(*c->registry, name, version); c->outputDevicesV2 << device; QObject::connect(device, &WaylandOutputDeviceV2::destroyed, [=]() { c->outputDevicesV2.removeOne(device); device->deleteLater(); }); QObject::connect(c->registry, &KWayland::Client::Registry::interfaceRemoved, device, [c, name, device](const quint32 &interfaceName) { if (name == interfaceName) { c->outputDevicesV2.removeOne(device); device->deleteLater(); } }); return; } } if (flags & AdditionalWaylandInterface::OutputManagementV2) { if (interface == kde_output_management_v2_interface.name) { c->outputManagementV2 = new WaylandOutputManagementV2(*c->registry, name, version); return; } } if (flags & AdditionalWaylandInterface::FractionalScaleManagerV1) { if (interface == wp_fractional_scale_manager_v1_interface.name) { c->fractionalScaleManagerV1 = new FractionalScaleManagerV1(); c->fractionalScaleManagerV1->init(*c->registry, name, version); return; } } if (flags & AdditionalWaylandInterface::ScreencastingV1) { if (interface == zkde_screencast_unstable_v1_interface.name) { c->screencastingV1 = new ScreencastingV1(); c->screencastingV1->init(*c->registry, name, version); return; } } if (flags & AdditionalWaylandInterface::ScreenEdgeV1) { if (interface == kde_screen_edge_manager_v1_interface.name) { c->screenEdgeManagerV1 = new ScreenEdgeManagerV1(); c->screenEdgeManagerV1->init(*c->registry, name, version); return; } } if (flags & AdditionalWaylandInterface::CursorShapeV1) { if (interface == wp_cursor_shape_manager_v1_interface.name) { c->cursorShapeManagerV1 = new CursorShapeManagerV1(); c->cursorShapeManagerV1->init(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::FakeInput) { if (interface == org_kde_kwin_fake_input_interface.name) { c->fakeInput = new FakeInput(); c->fakeInput->init(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::SecurityContextManagerV1) { if (interface == wp_security_context_manager_v1_interface.name) { c->securityContextManagerV1 = new SecurityContextManagerV1(); c->securityContextManagerV1->init(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::XdgDialogV1) { if (interface == xdg_wm_dialog_v1_interface.name) { c->xdgWmDialogV1 = new XdgWmDialogV1(); c->xdgWmDialogV1->init(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::ColorManagement) { if (interface == wp_color_manager_v1_interface.name) { c->colorManager = std::make_unique(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::FifoV1) { if (interface == wp_fifo_manager_v1_interface.name) { c->fifoManager = std::make_unique(*c->registry, name, version); } } if (interface == wp_presentation_interface.name) { c->presentationTime = std::make_unique(*c->registry, name, version); } if (flags & AdditionalWaylandInterface::XdgActivation) { if (interface == xdg_activation_v1_interface.name) { c->xdgActivation = std::make_unique(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::XdgSessionV1) { if (interface == xx_session_manager_v1_interface.name) { c->sessionManager = std::make_unique(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::WpTabletV2) { if (interface == zwp_tablet_manager_v2_interface.name) { c->tabletManager = std::make_unique(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::KeyState && interface == org_kde_kwin_keystate_interface.name) { c->keyState = std::make_unique(*c->registry, name, version); } if (flags & AdditionalWaylandInterface::WpPrimarySelectionV1) { if (interface == zwp_primary_selection_device_manager_v1_interface.name) { c->primarySelectionManager = std::make_unique(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::XdgToplevelDragV1) { if (interface == xdg_toplevel_drag_manager_v1_interface.name) { c->toplevelDragManager = std::make_unique(*c->registry, name, version); } } if (flags & AdditionalWaylandInterface::LinuxDmabuf && interface == zwp_linux_dmabuf_v1_interface.name) { c->linuxDmabuf = std::make_unique(*c->registry, name, version); } if (flags & AdditionalWaylandInterface::ColorRepresentation && interface == wp_color_representation_manager_v1_interface.name) { c->colorRepresentation = std::make_unique(*c->registry, name, version); } if (flags & AdditionalWaylandInterface::Viewporter && interface == wp_viewporter_interface.name) { c->viewporter = std::make_unique(*c->registry, name, 1u); } if (flags.testFlag(AdditionalWaylandInterface::Seat) && interface == wl_seat_interface.name) { c->kwinSeat = std::make_unique(*c->registry, name, version); } }); QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced); if (!allAnnounced.isValid()) { return nullptr; } registry->create(connection->connection); if (!registry->isValid()) { return nullptr; } registry->setup(); if (!allAnnounced.wait()) { return nullptr; } connection->compositor = registry->createCompositor(registry->interface(KWayland::Client::Registry::Interface::Compositor).name, registry->interface(KWayland::Client::Registry::Interface::Compositor).version); if (!connection->compositor->isValid()) { return nullptr; } connection->subCompositor = registry->createSubCompositor(registry->interface(KWayland::Client::Registry::Interface::SubCompositor).name, registry->interface(KWayland::Client::Registry::Interface::SubCompositor).version); if (!connection->subCompositor->isValid()) { return nullptr; } connection->shm = registry->createShmPool(registry->interface(KWayland::Client::Registry::Interface::Shm).name, registry->interface(KWayland::Client::Registry::Interface::Shm).version); if (!connection->shm->isValid()) { return nullptr; } if (flags.testFlag(AdditionalWaylandInterface::Seat)) { connection->seat = registry->createSeat(registry->interface(KWayland::Client::Registry::Interface::Seat).name, registry->interface(KWayland::Client::Registry::Interface::Seat).version); if (!connection->seat->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::DataDeviceManager)) { connection->dataDeviceManager = registry->createDataDeviceManager(registry->interface(KWayland::Client::Registry::Interface::DataDeviceManager).name, registry->interface(KWayland::Client::Registry::Interface::DataDeviceManager).version); if (!connection->dataDeviceManager->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::ShadowManager)) { connection->shadowManager = registry->createShadowManager(registry->interface(KWayland::Client::Registry::Interface::Shadow).name, registry->interface(KWayland::Client::Registry::Interface::Shadow).version); if (!connection->shadowManager->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::PlasmaShell)) { connection->plasmaShell = registry->createPlasmaShell(registry->interface(KWayland::Client::Registry::Interface::PlasmaShell).name, registry->interface(KWayland::Client::Registry::Interface::PlasmaShell).version); if (!connection->plasmaShell->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::WindowManagement)) { connection->windowManagement = registry->createPlasmaWindowManagement(registry->interface(KWayland::Client::Registry::Interface::PlasmaWindowManagement).name, registry->interface(KWayland::Client::Registry::Interface::PlasmaWindowManagement).version); if (!connection->windowManagement->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::PointerConstraints)) { connection->pointerConstraints = registry->createPointerConstraints(registry->interface(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1).name, registry->interface(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1).version); if (!connection->pointerConstraints->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::AppMenu)) { connection->appMenu = registry->createAppMenuManager(registry->interface(KWayland::Client::Registry::Interface::AppMenu).name, registry->interface(KWayland::Client::Registry::Interface::AppMenu).version); if (!connection->appMenu->isValid()) { return nullptr; } } if (flags.testFlag(AdditionalWaylandInterface::TextInputManagerV2)) { connection->textInputManager = registry->createTextInputManager(registry->interface(KWayland::Client::Registry::Interface::TextInputManagerUnstableV2).name, registry->interface(KWayland::Client::Registry::Interface::TextInputManagerUnstableV2).version); if (!connection->textInputManager->isValid()) { return nullptr; } } return connection; } Connection::~Connection() { delete compositor; compositor = nullptr; delete subCompositor; subCompositor = nullptr; delete windowManagement; windowManagement = nullptr; delete plasmaShell; plasmaShell = nullptr; delete seat; seat = nullptr; delete dataDeviceManager; dataDeviceManager = nullptr; delete pointerConstraints; pointerConstraints = nullptr; delete xdgShell; xdgShell = nullptr; delete shadowManager; shadowManager = nullptr; delete idleInhibitManagerV1; idleInhibitManagerV1 = nullptr; delete shm; shm = nullptr; delete registry; registry = nullptr; delete appMenu; appMenu = nullptr; delete xdgDecorationManagerV1; xdgDecorationManagerV1 = nullptr; delete textInputManager; textInputManager = nullptr; delete inputPanelV1; inputPanelV1 = nullptr; delete layerShellV1; layerShellV1 = nullptr; delete outputManagementV2; outputManagementV2 = nullptr; delete fractionalScaleManagerV1; fractionalScaleManagerV1 = nullptr; delete screencastingV1; screencastingV1 = nullptr; delete screenEdgeManagerV1; screenEdgeManagerV1 = nullptr; delete cursorShapeManagerV1; cursorShapeManagerV1 = nullptr; delete fakeInput; fakeInput = nullptr; delete securityContextManagerV1; securityContextManagerV1 = nullptr; delete xdgWmDialogV1; xdgWmDialogV1 = nullptr; colorManager.reset(); fifoManager.reset(); presentationTime.reset(); xdgActivation.reset(); sessionManager.reset(); tabletManager.reset(); keyState.reset(); primarySelectionManager.reset(); toplevelDragManager.reset(); linuxDmabuf.reset(); colorRepresentation.reset(); viewporter.reset(); kwinSeat.reset(); delete queue; // Must be destroyed last queue = nullptr; if (thread) { connection->deleteLater(); thread->quit(); thread->wait(); delete thread; thread = nullptr; connection = nullptr; } outputs.clear(); outputDevicesV2.clear(); } class WaylandSyncPoint : public QObject { Q_OBJECT public: explicit WaylandSyncPoint(KWayland::Client::ConnectionThread *connection, KWayland::Client::EventQueue *eventQueue) { static const wl_callback_listener listener = { .done = [](void *data, wl_callback *callback, uint32_t callback_data) { auto syncPoint = static_cast(data); Q_EMIT syncPoint->done(); }, }; m_callback = wl_display_sync(connection->display()); eventQueue->addProxy(m_callback); wl_callback_add_listener(m_callback, &listener, this); } ~WaylandSyncPoint() override { wl_callback_destroy(m_callback); } Q_SIGNALS: void done(); private: wl_callback *m_callback; }; bool Connection::sync() { WaylandSyncPoint syncPoint(connection, queue); QSignalSpy doneSpy(&syncPoint, &WaylandSyncPoint::done); return doneSpy.wait(); } KWayland::Client::ConnectionThread *waylandConnection() { return s_waylandConnection->connection; } KWayland::Client::Compositor *waylandCompositor() { return s_waylandConnection->compositor; } KWayland::Client::SubCompositor *waylandSubCompositor() { return s_waylandConnection->subCompositor; } KWayland::Client::ShadowManager *waylandShadowManager() { return s_waylandConnection->shadowManager; } KWayland::Client::ShmPool *waylandShmPool() { return s_waylandConnection->shm; } KWayland::Client::Seat *waylandSeat() { return s_waylandConnection->seat; } KWayland::Client::DataDeviceManager *waylandDataDeviceManager() { return s_waylandConnection->dataDeviceManager; } KWayland::Client::PlasmaShell *waylandPlasmaShell() { return s_waylandConnection->plasmaShell; } KWayland::Client::PlasmaWindowManagement *waylandWindowManagement() { return s_waylandConnection->windowManagement; } KWayland::Client::PointerConstraints *waylandPointerConstraints() { return s_waylandConnection->pointerConstraints; } KWayland::Client::AppMenuManager *waylandAppMenuManager() { return s_waylandConnection->appMenu; } KWin::Test::WaylandOutputManagementV2 *waylandOutputManagementV2() { return s_waylandConnection->outputManagementV2; } KWayland::Client::TextInputManager *waylandTextInputManager() { return s_waylandConnection->textInputManager; } TextInputManagerV3 *waylandTextInputManagerV3() { return s_waylandConnection->textInputManagerV3; } QList waylandOutputs() { return s_waylandConnection->outputs; } KWayland::Client::Output *waylandOutput(const QString &name) { for (KWayland::Client::Output *output : std::as_const(s_waylandConnection->outputs)) { if (output->name() == name) { return output; } } return nullptr; } ScreencastingV1 *screencasting() { return s_waylandConnection->screencastingV1; } QList waylandOutputDevicesV2() { return s_waylandConnection->outputDevicesV2; } FakeInput *waylandFakeInput() { return s_waylandConnection->fakeInput; } SecurityContextManagerV1 *waylandSecurityContextManagerV1() { return s_waylandConnection->securityContextManagerV1; } ColorManagerV1 *colorManager() { return s_waylandConnection->colorManager.get(); } FifoManagerV1 *fifoManager() { return s_waylandConnection->fifoManager.get(); } PresentationTime *presentationTime() { return s_waylandConnection->presentationTime.get(); } XdgActivation *xdgActivation() { return s_waylandConnection->xdgActivation.get(); } WpTabletManagerV2 *tabletManager() { return s_waylandConnection->tabletManager.get(); } KeyStateV1 *keyState() { return s_waylandConnection->keyState.get(); } WpPrimarySelectionDeviceManagerV1 *primarySelectionManager() { return s_waylandConnection->primarySelectionManager.get(); } XdgToplevelDragManagerV1 *toplevelDragManager() { return s_waylandConnection->toplevelDragManager.get(); } WaylandClient::LinuxDmabufV1 *linuxDmabuf() { return s_waylandConnection->linuxDmabuf.get(); } ColorRepresentationV1 *colorRepresentation() { return s_waylandConnection->colorRepresentation.get(); } WaylandClient::Viewporter *viewporter() { return s_waylandConnection->viewporter.get(); } bool waitForWaylandSurface(Window *window) { if (window->surface()) { return true; } QSignalSpy surfaceChangedSpy(window, &Window::surfaceChanged); return surfaceChangedSpy.wait(); } bool waitForWaylandPointer() { if (!s_waylandConnection->seat) { return false; } return waitForWaylandPointer(s_waylandConnection->seat); } bool waitForWaylandPointer(KWayland::Client::Seat *seat) { QSignalSpy hasPointerSpy(seat, &KWayland::Client::Seat::hasPointerChanged); return hasPointerSpy.wait(); } bool waitForWaylandTouch() { if (!s_waylandConnection->seat) { return false; } return waitForWaylandTouch(s_waylandConnection->seat); } bool waitForWaylandTouch(KWayland::Client::Seat *seat) { QSignalSpy hasTouchSpy(seat, &KWayland::Client::Seat::hasTouchChanged); return hasTouchSpy.wait(); } bool waitForWaylandKeyboard() { if (!s_waylandConnection->seat) { return false; } return waitForWaylandKeyboard(s_waylandConnection->seat); } bool waitForWaylandKeyboard(KWayland::Client::Seat *seat) { QSignalSpy hasKeyboardSpy(seat, &KWayland::Client::Seat::hasKeyboardChanged); return hasKeyboardSpy.wait(); } bool waitForWaylandTabletTool(Test::WpTabletToolV2 *tool) { if (tool->ready()) { return true; } QSignalSpy doneSpy(tool, &WpTabletToolV2::done); return doneSpy.wait(); } void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format) { render(s_waylandConnection->shm, surface, size, color, format); } void render(KWayland::Client::ShmPool *shm, KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format) { QImage img(size, format); img.fill(color); render(surface, img); } void render(KWayland::Client::Surface *surface, const QImage &img) { render(s_waylandConnection->shm, surface, img); } void render(KWayland::Client::ShmPool *shm, KWayland::Client::Surface *surface, const QImage &img) { surface->attachBuffer(shm->createBuffer(img)); surface->damage(QRect(QPoint(0, 0), img.size())); surface->commit(KWayland::Client::Surface::CommitFlag::None); } Window *waitForWaylandWindowShown(int timeout) { QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded); if (!windowAddedSpy.isValid()) { return nullptr; } if (!windowAddedSpy.wait(timeout)) { return nullptr; } return windowAddedSpy.first().first().value(); } Window *renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format, int timeout) { return renderAndWaitForShown(s_waylandConnection->shm, surface, size, color, format, timeout); } Window *renderAndWaitForShown(KWayland::Client::ShmPool *shm, KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format, int timeout) { QImage img(size, format); img.fill(color); return renderAndWaitForShown(shm, surface, img, timeout); } Window *renderAndWaitForShown(KWayland::Client::Surface *surface, const QImage &img, int timeout) { return renderAndWaitForShown(s_waylandConnection->shm, surface, img, timeout); } Window *renderAndWaitForShown(KWayland::Client::ShmPool *shm, KWayland::Client::Surface *surface, const QImage &img, int timeout) { QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded); if (!windowAddedSpy.isValid()) { return nullptr; } render(shm, surface, img); flushWaylandConnection(); if (!windowAddedSpy.wait(timeout)) { return nullptr; } return windowAddedSpy.first().first().value(); } void flushWaylandConnection() { if (s_waylandConnection) { s_waylandConnection->connection->flush(); } } bool waylandSync() { return s_waylandConnection->sync(); } std::unique_ptr createSurface() { if (!s_waylandConnection->compositor) { return nullptr; } return createSurface(s_waylandConnection->compositor); } std::unique_ptr createSurface(KWayland::Client::Compositor *compositor) { std::unique_ptr s{compositor->createSurface()}; return s->isValid() ? std::move(s) : nullptr; } std::unique_ptr createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface) { if (!s_waylandConnection->subCompositor) { return nullptr; } std::unique_ptr s(s_waylandConnection->subCompositor->createSubSurface(surface, parentSurface)); if (!s->isValid()) { return nullptr; } return s; } std::unique_ptr createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output, LayerShellV1::layer layer) { LayerShellV1 *shell = s_waylandConnection->layerShellV1; if (!shell) { qWarning() << "Could not create a layer surface because the layer shell global is not bound"; return nullptr; } struct ::wl_output *nativeOutput = nullptr; if (output) { nativeOutput = *output; } auto shellSurface = std::make_unique(); shellSurface->init(shell->get_layer_surface(*surface, nativeOutput, layer, scope)); return shellSurface; } std::unique_ptr createInputPanelSurfaceV1(KWayland::Client::Surface *surface, KWayland::Client::Output *output, MockInputMethod::Mode mode) { if (!s_waylandConnection->inputPanelV1) { qWarning() << "Unable to create the input panel surface. The interface input_panel global is not bound"; return nullptr; } auto s = std::make_unique(s_waylandConnection->inputPanelV1->get_input_panel_surface(*surface)); if (!s->isInitialized()) { return nullptr; } switch (mode) { case MockInputMethod::Mode::TopLevel: s->set_toplevel(output->output(), QtWayland::zwp_input_panel_surface_v1::position_center_bottom); break; case MockInputMethod::Mode::Overlay: s->set_overlay_panel(); break; } return s; } std::unique_ptr createFractionalScaleV1(KWayland::Client::Surface *surface) { if (!s_waylandConnection->fractionalScaleManagerV1) { qWarning() << "Unable to create fractional scale surface. The global is not bound"; return nullptr; } auto scale = std::make_unique(); scale->init(s_waylandConnection->fractionalScaleManagerV1->get_fractional_scale(*surface)); return scale; } static void waitForConfigured(XdgSurface *shellSurface) { QSignalSpy surfaceConfigureRequestedSpy(shellSurface, &XdgSurface::configureRequested); shellSurface->surface()->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(surfaceConfigureRequestedSpy.wait()); shellSurface->ack_configure(surfaceConfigureRequestedSpy.last().first().toUInt()); } std::unique_ptr createXdgToplevelSurface(KWayland::Client::Surface *surface) { return createXdgToplevelSurface(surface, CreationSetup::CreateAndConfigure); } std::unique_ptr createXdgToplevelSurface(XdgShell *shell, KWayland::Client::Surface *surface) { return createXdgToplevelSurface(shell, surface, CreationSetup::CreateAndConfigure); } std::unique_ptr createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode) { XdgShell *shell = s_waylandConnection->xdgShell; if (!shell) { qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound"; return nullptr; } return createXdgToplevelSurface(shell, surface, configureMode); } std::unique_ptr createXdgToplevelSurface(XdgShell *shell, KWayland::Client::Surface *surface, CreationSetup configureMode) { XdgSurface *xdgSurface = new XdgSurface(shell, surface); std::unique_ptr xdgToplevel = std::make_unique(xdgSurface); if (configureMode == CreationSetup::CreateAndConfigure) { waitForConfigured(xdgSurface); } return xdgToplevel; } std::unique_ptr createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function setup) { XdgShell *shell = s_waylandConnection->xdgShell; if (!shell) { qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound"; return nullptr; } return createXdgToplevelSurface(shell, surface, setup); } std::unique_ptr createXdgToplevelSurface(XdgShell *shell, KWayland::Client::Surface *surface, std::function setup) { XdgSurface *xdgSurface = new XdgSurface(shell, surface); std::unique_ptr xdgToplevel = std::make_unique(xdgSurface); if (setup) { setup(xdgToplevel.get()); } waitForConfigured(xdgSurface); return xdgToplevel; } std::unique_ptr createXdgPositioner() { XdgShell *shell = s_waylandConnection->xdgShell; if (!shell) { qWarning() << "Could not create an xdg_positioner object because xdg_wm_base global is not bound"; return nullptr; } return std::make_unique(shell); } std::unique_ptr createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, CreationSetup configureMode) { XdgShell *shell = s_waylandConnection->xdgShell; if (!shell) { qWarning() << "Could not create an xdg_popup surface because xdg_wm_base global is not bound"; return nullptr; } XdgSurface *xdgSurface = new XdgSurface(shell, surface); std::unique_ptr xdgPopup = std::make_unique(xdgSurface, parentSurface, positioner); if (configureMode == CreationSetup::CreateAndConfigure) { waitForConfigured(xdgSurface); } return xdgPopup; } std::unique_ptr createXdgToplevelDecorationV1(XdgToplevel *toplevel) { XdgDecorationManagerV1 *manager = s_waylandConnection->xdgDecorationManagerV1; if (!manager) { qWarning() << "Could not create an xdg_toplevel_decoration_v1 because xdg_decoration_manager_v1 global is not bound"; return nullptr; } return std::make_unique(manager, toplevel); } std::unique_ptr createIdleInhibitorV1(KWayland::Client::Surface *surface) { IdleInhibitManagerV1 *manager = s_waylandConnection->idleInhibitManagerV1; if (!manager) { qWarning() << "Could not create an idle_inhibitor_v1 because idle_inhibit_manager_v1 global is not bound"; return nullptr; } return std::make_unique(manager, surface); } std::unique_ptr createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border) { ScreenEdgeManagerV1 *manager = s_waylandConnection->screenEdgeManagerV1; if (!manager) { qWarning() << "Could not create an kde_auto_hide_screen_edge_v1 because kde_screen_edge_manager_v1 global is not bound"; return nullptr; } return std::make_unique(manager, surface, border); } std::unique_ptr createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer) { CursorShapeManagerV1 *manager = s_waylandConnection->cursorShapeManagerV1; if (!manager) { qWarning() << "Could not create a wp_cursor_shape_device_v1 because wp_cursor_shape_manager_v1 global is not bound"; return nullptr; } return std::make_unique(manager, pointer); } std::unique_ptr createXdgDialogV1(XdgToplevel *toplevel) { XdgWmDialogV1 *wm = s_waylandConnection->xdgWmDialogV1; if (!wm) { qWarning() << "Could not create a xdg_dialog_v1 because xdg_wm_dialog_v1 global is not bound"; return nullptr; } return std::make_unique(wm, toplevel); } std::unique_ptr createXdgSessionV1(XdgSessionManagerV1::reason reason, const QString &sessionId) { XdgSessionManagerV1 *manager = s_waylandConnection->sessionManager.get(); if (!manager) { qWarning() << "Could not create a xx_session_v1 because xx_session_manager_v1 global is not bound"; return nullptr; } return createXdgSessionV1(manager, reason, sessionId); } std::unique_ptr createXdgSessionV1(XdgSessionManagerV1 *manager, XdgSessionManagerV1::reason reason, const QString &sessionId) { return std::make_unique(manager->get_session(reason, sessionId)); } bool waitForWindowClosed(Window *window) { QSignalSpy closedSpy(window, &Window::closed); if (!closedSpy.isValid()) { return false; } return closedSpy.wait(); } #if KWIN_BUILD_SCREENLOCKER bool lockScreen() { if (waylandServer()->isScreenLocked()) { return false; } QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); if (!lockStateChangedSpy.isValid()) { return false; } ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate); if (lockStateChangedSpy.count() != 1) { return false; } if (!waylandServer()->isScreenLocked()) { return false; } if (ScreenLocker::KSldApp::self()->lockState() != ScreenLocker::KSldApp::Locked) { QSignalSpy lockedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::locked); if (!lockedSpy.isValid()) { return false; } if (!lockedSpy.wait()) { return false; } } return true; } bool unlockScreen() { QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); if (!lockStateChangedSpy.isValid()) { return false; } using namespace ScreenLocker; const auto children = KSldApp::self()->children(); for (auto it = children.begin(); it != children.end(); ++it) { if (qstrcmp((*it)->metaObject()->className(), "LogindIntegration") != 0) { continue; } QMetaObject::invokeMethod(*it, "requestUnlock"); break; } if (waylandServer()->isScreenLocked()) { lockStateChangedSpy.wait(); } if (waylandServer()->isScreenLocked()) { return true; } if (ScreenLocker::KSldApp::self()->lockState() == ScreenLocker::KSldApp::Locked) { QSignalSpy lockedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::unlocked); if (!lockedSpy.isValid()) { return false; } if (!lockedSpy.wait()) { return false; } } return true; } #endif // KWIN_BUILD_LOCKSCREEN static bool haveDrmNode(int type) { #if !HAVE_LIBDRM_FAUX #if defined(Q_OS_LINUX) // Workaround for libdrm being unaware of faux bus. if (qEnvironmentVariableIsSet("CI")) { return true; } #endif #endif const int deviceCount = drmGetDevices2(0, nullptr, 0); if (deviceCount <= 0) { return false; } QList devices(deviceCount); if (drmGetDevices2(0, devices.data(), devices.size()) < 0) { return false; } auto deviceCleanup = qScopeGuard([&devices]() { drmFreeDevices(devices.data(), devices.size()); }); return std::any_of(devices.constBegin(), devices.constEnd(), [type](drmDevice *device) { return device->available_nodes & (1 << type); }); } bool renderNodeAvailable() { return haveDrmNode(DRM_NODE_RENDER); } bool primaryNodeAvailable() { return haveDrmNode(DRM_NODE_PRIMARY); } #if KWIN_BUILD_X11 void XcbConnectionDeleter::operator()(xcb_connection_t *pointer) { xcb_disconnect(pointer); }; Test::XcbConnectionPtr createX11Connection() { QFutureWatcher watcher; QEventLoop e; e.connect(&watcher, &QFutureWatcher::finished, &e, &QEventLoop::quit); QFuture future = QtConcurrent::run([]() { return xcb_connect(nullptr, nullptr); }); watcher.setFuture(future); e.exec(); return Test::XcbConnectionPtr(future.result()); } void applyMotifHints(xcb_connection_t *connection, xcb_window_t window, const MotifHints &hints) { if (hints.flags) { xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, atoms->motif_wm_hints, atoms->motif_wm_hints, 32, 5, &hints); } else { xcb_delete_property(connection, window, atoms->motif_wm_hints); } } #endif WaylandOutputManagementV2::WaylandOutputManagementV2(struct ::wl_registry *registry, int id, int version) : QObject() , QtWayland::kde_output_management_v2() { init(registry, id, version); } WaylandOutputConfigurationV2 *WaylandOutputManagementV2::createConfiguration() { return new WaylandOutputConfigurationV2(create_configuration()); } WaylandOutputConfigurationV2::WaylandOutputConfigurationV2(struct ::kde_output_configuration_v2 *object) : QObject() , QtWayland::kde_output_configuration_v2() { init(object); } void WaylandOutputConfigurationV2::kde_output_configuration_v2_applied() { Q_EMIT applied(); } void WaylandOutputConfigurationV2::kde_output_configuration_v2_failed() { Q_EMIT failed(); } WaylandOutputDeviceV2Mode::WaylandOutputDeviceV2Mode(struct ::kde_output_device_mode_v2 *object) : QtWayland::kde_output_device_mode_v2(object) { } WaylandOutputDeviceV2Mode::~WaylandOutputDeviceV2Mode() { kde_output_device_mode_v2_destroy(object()); } void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_size(int32_t width, int32_t height) { m_size = QSize(width, height); } void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_refresh(int32_t refresh) { m_refreshRate = refresh; } void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_preferred() { m_preferred = true; } void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_removed() { Q_EMIT removed(); } int WaylandOutputDeviceV2Mode::refreshRate() const { return m_refreshRate; } QSize WaylandOutputDeviceV2Mode::size() const { return m_size; } bool WaylandOutputDeviceV2Mode::preferred() const { return m_preferred; } bool WaylandOutputDeviceV2Mode::operator==(const WaylandOutputDeviceV2Mode &other) const { return m_size == other.m_size && m_refreshRate == other.m_refreshRate && m_preferred == other.m_preferred; } WaylandOutputDeviceV2Mode *WaylandOutputDeviceV2Mode::get(struct ::kde_output_device_mode_v2 *object) { auto mode = QtWayland::kde_output_device_mode_v2::fromObject(object); return static_cast(mode); } WaylandOutputDeviceV2::WaylandOutputDeviceV2(int id) : QObject() , kde_output_device_v2() , m_id(id) { } WaylandOutputDeviceV2::~WaylandOutputDeviceV2() { qDeleteAll(m_modes); kde_output_device_v2_destroy(object()); } void WaylandOutputDeviceV2::kde_output_device_v2_geometry(int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const QString &make, const QString &model, int32_t transform) { m_pos = QPoint(x, y); m_physicalSize = QSize(physical_width, physical_height); m_subpixel = subpixel; m_manufacturer = make; m_model = model; m_transform = transform; } void WaylandOutputDeviceV2::kde_output_device_v2_current_mode(struct ::kde_output_device_mode_v2 *mode) { auto m = WaylandOutputDeviceV2Mode::get(mode); if (*m == *m_mode) { // unchanged return; } m_mode = m; } void WaylandOutputDeviceV2::kde_output_device_v2_mode(struct ::kde_output_device_mode_v2 *mode) { WaylandOutputDeviceV2Mode *m = new WaylandOutputDeviceV2Mode(mode); // last mode sent is the current one m_mode = m; m_modes.append(m); connect(m, &WaylandOutputDeviceV2Mode::removed, this, [this, m]() { m_modes.removeOne(m); if (m_mode == m) { if (!m_modes.isEmpty()) { m_mode = m_modes.first(); } else { // was last mode qFatal("KWaylandBackend: no output modes available anymore, this seems like a compositor bug"); } } delete m; }); } QString WaylandOutputDeviceV2::modeId() const { return QString::number(m_modes.indexOf(m_mode)); } WaylandOutputDeviceV2Mode *WaylandOutputDeviceV2::deviceModeFromId(const int modeId) const { return m_modes.at(modeId); } QString WaylandOutputDeviceV2::modeName(const WaylandOutputDeviceV2Mode *m) const { return QString::number(m->size().width()) + QLatin1Char('x') + QString::number(m->size().height()) + QLatin1Char('@') + QString::number(qRound(m->refreshRate() / 1000.0)); } QString WaylandOutputDeviceV2::name() const { return QStringLiteral("%1 %2").arg(m_manufacturer, m_model); } QDebug operator<<(QDebug dbg, const WaylandOutputDeviceV2 *output) { dbg << "WaylandOutput(Id:" << output->id() << ", Name:" << QString(output->manufacturer() + QLatin1Char(' ') + output->model()) << ")"; return dbg; } void WaylandOutputDeviceV2::kde_output_device_v2_done() { Q_EMIT done(); } void WaylandOutputDeviceV2::kde_output_device_v2_scale(wl_fixed_t factor) { m_factor = wl_fixed_to_double(factor); } void WaylandOutputDeviceV2::kde_output_device_v2_edid(const QString &edid) { m_edid = QByteArray::fromBase64(edid.toUtf8()); } void WaylandOutputDeviceV2::kde_output_device_v2_enabled(int32_t enabled) { if (m_enabled != enabled) { m_enabled = enabled; Q_EMIT enabledChanged(); } } void WaylandOutputDeviceV2::kde_output_device_v2_uuid(const QString &uuid) { m_uuid = uuid; } void WaylandOutputDeviceV2::kde_output_device_v2_serial_number(const QString &serialNumber) { m_serialNumber = serialNumber; } void WaylandOutputDeviceV2::kde_output_device_v2_eisa_id(const QString &eisaId) { m_eisaId = eisaId; } void WaylandOutputDeviceV2::kde_output_device_v2_capabilities(uint32_t flags) { m_flags = flags; } void WaylandOutputDeviceV2::kde_output_device_v2_overscan(uint32_t overscan) { m_overscan = overscan; } void WaylandOutputDeviceV2::kde_output_device_v2_vrr_policy(uint32_t vrr_policy) { m_vrr_policy = vrr_policy; } void WaylandOutputDeviceV2::kde_output_device_v2_rgb_range(uint32_t rgb_range) { m_rgbRange = rgb_range; } QByteArray WaylandOutputDeviceV2::edid() const { return m_edid; } bool WaylandOutputDeviceV2::enabled() const { return m_enabled; } int WaylandOutputDeviceV2::id() const { return m_id; } qreal WaylandOutputDeviceV2::scale() const { return m_factor; } QString WaylandOutputDeviceV2::manufacturer() const { return m_manufacturer; } QString WaylandOutputDeviceV2::model() const { return m_model; } QPoint WaylandOutputDeviceV2::globalPosition() const { return m_pos; } QSize WaylandOutputDeviceV2::pixelSize() const { return m_mode->size(); } int WaylandOutputDeviceV2::refreshRate() const { return m_mode->refreshRate(); } uint32_t WaylandOutputDeviceV2::vrrPolicy() const { return m_vrr_policy; } uint32_t WaylandOutputDeviceV2::overscan() const { return m_overscan; } uint32_t WaylandOutputDeviceV2::capabilities() const { return m_flags; } uint32_t WaylandOutputDeviceV2::rgbRange() const { return m_rgbRange; } VirtualInputDeviceTabletTool::VirtualInputDeviceTabletTool(QObject *parent) : InputDeviceTabletTool(parent) { } void VirtualInputDeviceTabletTool::setSerialId(quint64 serialId) { m_serialId = serialId; } void VirtualInputDeviceTabletTool::setUniqueId(quint64 uniqueId) { m_uniqueId = uniqueId; } void VirtualInputDeviceTabletTool::setType(Type type) { m_type = type; } void VirtualInputDeviceTabletTool::setCapabilities(const QList &capabilities) { m_capabilities = capabilities; } quint64 VirtualInputDeviceTabletTool::serialId() const { return m_serialId; } quint64 VirtualInputDeviceTabletTool::uniqueId() const { return m_uniqueId; } VirtualInputDeviceTabletTool::Type VirtualInputDeviceTabletTool::type() const { return m_type; } QList VirtualInputDeviceTabletTool::capabilities() const { return m_capabilities; } VirtualInputDevice::VirtualInputDevice(QObject *parent) : InputDevice(parent) { } void VirtualInputDevice::setPointer(bool set) { m_pointer = set; } void VirtualInputDevice::setKeyboard(bool set) { m_keyboard = set; } void VirtualInputDevice::setTouch(bool set) { m_touch = set; } void VirtualInputDevice::setLidSwitch(bool set) { m_lidSwitch = set; } void VirtualInputDevice::setTabletPad(bool set) { m_tabletPad = set; } void VirtualInputDevice::setTabletTool(bool set) { m_tabletTool = set; } void VirtualInputDevice::setName(const QString &name) { m_name = name; } void VirtualInputDevice::setGroup(uintptr_t group) { m_group = reinterpret_cast(group); } QString VirtualInputDevice::name() const { return m_name; } void *VirtualInputDevice::group() const { return m_group; } bool VirtualInputDevice::isEnabled() const { return true; } void VirtualInputDevice::setEnabled(bool enabled) { } bool VirtualInputDevice::isKeyboard() const { return m_keyboard; } bool VirtualInputDevice::isPointer() const { return m_pointer; } bool VirtualInputDevice::isTouchpad() const { return false; } bool VirtualInputDevice::isTouch() const { return m_touch; } bool VirtualInputDevice::isTabletTool() const { return m_tabletTool; } bool VirtualInputDevice::isTabletPad() const { return m_tabletPad; } bool VirtualInputDevice::isTabletModeSwitch() const { return false; } bool VirtualInputDevice::isLidSwitch() const { return m_lidSwitch; } ColorManagerV1::ColorManagerV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::wp_color_manager_v1(registry, id, version) { } ColorManagerV1::~ColorManagerV1() { wp_color_manager_v1_destroy(object()); } ColorRepresentationV1::ColorRepresentationV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::wp_color_representation_manager_v1(registry, id, version) { } ColorRepresentationV1::~ColorRepresentationV1() { destroy(); } ColorRepresentationSurfaceV1::ColorRepresentationSurfaceV1(::wp_color_representation_surface_v1 *object) : QtWayland::wp_color_representation_surface_v1(object) { } ColorRepresentationSurfaceV1::~ColorRepresentationSurfaceV1() { destroy(); } FifoManagerV1::FifoManagerV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::wp_fifo_manager_v1(registry, id, version) { } FifoManagerV1::~FifoManagerV1() { wp_fifo_manager_v1_destroy(object()); } PresentationTime::PresentationTime(::wl_registry *registry, uint32_t id, int version) : QtWayland::wp_presentation(registry, id, version) { } PresentationTime::~PresentationTime() { wp_presentation_destroy(object()); } WpPresentationFeedback::WpPresentationFeedback(struct ::wp_presentation_feedback *obj) : QtWayland::wp_presentation_feedback(obj) { } WpPresentationFeedback::~WpPresentationFeedback() { wp_presentation_feedback_destroy(object()); } void WpPresentationFeedback::wp_presentation_feedback_presented(uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec, uint32_t refresh, uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) { const std::chrono::nanoseconds timestamp = std::chrono::seconds((uint64_t(tv_sec_hi) << 32) | tv_sec_lo) + std::chrono::nanoseconds(tv_nsec); Q_EMIT presented(timestamp, std::chrono::nanoseconds(refresh)); } void WpPresentationFeedback::wp_presentation_feedback_discarded() { Q_EMIT discarded(); } XdgActivationToken::XdgActivationToken(::xdg_activation_token_v1 *object) : QtWayland::xdg_activation_token_v1(object) { } XdgActivationToken::~XdgActivationToken() { destroy(); } QString XdgActivationToken::commitAndWait() { QSignalSpy received(this, &XdgActivationToken::tokenReceived); commit(); received.wait(); return m_token; } void XdgActivationToken::xdg_activation_token_v1_done(const QString &token) { m_token = token; Q_EMIT tokenReceived(); } XdgActivation::XdgActivation(::wl_registry *registry, uint32_t id, int version) : QtWayland::xdg_activation_v1(registry, id, version) { } XdgActivation::~XdgActivation() { destroy(); } std::unique_ptr XdgActivation::createToken() { return std::make_unique(get_activation_token()); } XdgToplevelSessionV1::XdgToplevelSessionV1(::xx_toplevel_session_v1 *session) : QtWayland::xx_toplevel_session_v1(session) { } XdgToplevelSessionV1::~XdgToplevelSessionV1() { destroy(); } void XdgToplevelSessionV1::xx_toplevel_session_v1_restored(struct ::xdg_toplevel *surface) { Q_EMIT restored(); } XdgSessionV1::XdgSessionV1(::xx_session_v1 *session) : QtWayland::xx_session_v1(session) { } XdgSessionV1::~XdgSessionV1() { destroy(); } std::unique_ptr XdgSessionV1::add(XdgToplevel *toplevel, const QString &toplevelId) { return std::make_unique(add_toplevel(toplevel->object(), toplevelId)); } std::unique_ptr XdgSessionV1::restore(XdgToplevel *toplevel, const QString &toplevelId) { return std::make_unique(restore_toplevel(toplevel->object(), toplevelId)); } void XdgSessionV1::xx_session_v1_created(const QString &id) { Q_EMIT created(id); } void XdgSessionV1::xx_session_v1_restored() { Q_EMIT restored(); } void XdgSessionV1::xx_session_v1_replaced() { Q_EMIT replaced(); } XdgSessionManagerV1::XdgSessionManagerV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::xx_session_manager_v1(registry, id, version) { } XdgSessionManagerV1::~XdgSessionManagerV1() { destroy(); } KeyStateV1::KeyStateV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::org_kde_kwin_keystate(registry, id, version) { fetchStates(); } KeyStateV1::~KeyStateV1() { destroy(); } void KeyStateV1::org_kde_kwin_keystate_stateChanged(uint32_t key, uint32_t state) { keyToState[key] = state; Q_EMIT stateChanged(); } WpTabletManagerV2::WpTabletManagerV2(::wl_registry *registry, uint32_t id, int version) : QtWayland::zwp_tablet_manager_v2(registry, id, version) { } WpTabletManagerV2::~WpTabletManagerV2() { destroy(); } std::unique_ptr WpTabletManagerV2::createSeat(KWayland::Client::Seat *seat) { return std::make_unique(get_tablet_seat(*seat)); } WpTabletSeatV2::WpTabletSeatV2(::zwp_tablet_seat_v2 *seat) : QtWayland::zwp_tablet_seat_v2(seat) { } WpTabletSeatV2::~WpTabletSeatV2() { destroy(); } void WpTabletSeatV2::zwp_tablet_seat_v2_tablet_added(::zwp_tablet_v2 *id) { m_tablets.emplace_back(std::make_unique(id)); } void WpTabletSeatV2::zwp_tablet_seat_v2_tool_added(::zwp_tablet_tool_v2 *id) { auto &tool = m_tools.emplace_back(std::make_unique(id)); Q_EMIT toolAdded(tool.get()); } void WpTabletSeatV2::zwp_tablet_seat_v2_pad_added(::zwp_tablet_pad_v2 *id) { m_pads.emplace_back(std::make_unique(id)); } WpTabletV2::WpTabletV2(::zwp_tablet_v2 *id) : QtWayland::zwp_tablet_v2(id) { } WpTabletV2::~WpTabletV2() { destroy(); } WpTabletToolV2::WpTabletToolV2(::zwp_tablet_tool_v2 *id) : QtWayland::zwp_tablet_tool_v2(id) { } WpTabletToolV2::~WpTabletToolV2() { destroy(); } bool WpTabletToolV2::ready() const { return m_ready; } void WpTabletToolV2::zwp_tablet_tool_v2_done() { m_ready = true; Q_EMIT done(); } void WpTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial) { Q_EMIT down(serial); } void WpTabletToolV2::zwp_tablet_tool_v2_up() { Q_EMIT up(); } void WpTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) { Q_EMIT motion(QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } WpTabletPadV2::WpTabletPadV2(::zwp_tablet_pad_v2 *id) : QtWayland::zwp_tablet_pad_v2(id) { } WpTabletPadV2::~WpTabletPadV2() { destroy(); } WpPrimarySelectionOfferV1::WpPrimarySelectionOfferV1(::zwp_primary_selection_offer_v1 *id) : QtWayland::zwp_primary_selection_offer_v1(id) { } WpPrimarySelectionOfferV1::~WpPrimarySelectionOfferV1() { destroy(); } QList WpPrimarySelectionOfferV1::mimeTypes() const { return m_mimeTypes; } void WpPrimarySelectionOfferV1::zwp_primary_selection_offer_v1_offer(const QString &mime_type) { m_mimeTypes.append(QMimeDatabase().mimeTypeForName(mime_type)); } WpPrimarySelectionSourceV1::WpPrimarySelectionSourceV1(::zwp_primary_selection_source_v1 *id) : QtWayland::zwp_primary_selection_source_v1(id) { } WpPrimarySelectionSourceV1::~WpPrimarySelectionSourceV1() { destroy(); } void WpPrimarySelectionSourceV1::zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd) { Q_EMIT sendDataRequested(mime_type, fd); } void WpPrimarySelectionSourceV1::zwp_primary_selection_source_v1_cancelled() { Q_EMIT cancelled(); } WpPrimarySelectionDeviceV1::WpPrimarySelectionDeviceV1(::zwp_primary_selection_device_v1 *id) : QtWayland::zwp_primary_selection_device_v1(id) { } WpPrimarySelectionDeviceV1::~WpPrimarySelectionDeviceV1() { destroy(); } WpPrimarySelectionOfferV1 *WpPrimarySelectionDeviceV1::offer() const { return m_offer.get(); } std::unique_ptr WpPrimarySelectionDeviceV1::takeOffer() { return std::move(m_offer); } void WpPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_data_offer(::zwp_primary_selection_offer_v1 *offer) { m_offer = std::make_unique(offer); } void WpPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_selection(::zwp_primary_selection_offer_v1 *id) { if (id) { Q_EMIT selectionOffered(m_offer.get()); } else { m_offer.reset(); Q_EMIT selectionCleared(); } } WpPrimarySelectionDeviceManagerV1::WpPrimarySelectionDeviceManagerV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::zwp_primary_selection_device_manager_v1(registry, id, version) { } WpPrimarySelectionDeviceManagerV1::~WpPrimarySelectionDeviceManagerV1() { destroy(); } std::unique_ptr WpPrimarySelectionDeviceManagerV1::getDevice(KWayland::Client::Seat *seat) { return std::make_unique(get_device(*seat)); } std::unique_ptr WpPrimarySelectionDeviceManagerV1::createSource() { return std::make_unique(create_source()); } XdgToplevelDragManagerV1::XdgToplevelDragManagerV1(::wl_registry *registry, uint32_t id, int version) : QtWayland::xdg_toplevel_drag_manager_v1(registry, id, version) { } std::unique_ptr XdgToplevelDragManagerV1::createDrag(KWayland::Client::DataSource *source) { return std::make_unique(get_xdg_toplevel_drag(*source)); } XdgToplevelDragManagerV1::~XdgToplevelDragManagerV1() { destroy(); } XdgToplevelDragV1::XdgToplevelDragV1(::xdg_toplevel_drag_v1 *id) : QtWayland::xdg_toplevel_drag_v1(id) { } XdgToplevelDragV1::~XdgToplevelDragV1() { destroy(); } WlSeat::WlSeat(::wl_registry *registry, uint32_t id, int version) : QtWayland::wl_seat(registry, id, version) { } WlSeat::~WlSeat() { release(); } std::unique_ptr WlSeat::getKeyboard() { return std::make_unique(get_keyboard()); } WlKeyboard::WlKeyboard(::wl_keyboard *object) : QtWayland::wl_keyboard(object) { } WlKeyboard::~WlKeyboard() { release(); } void WlKeyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size) { ::close(fd); } void WlKeyboard::keyboard_enter(uint32_t serial, ::wl_surface *surface, wl_array *keys) { Q_EMIT enter(serial, surface); } void WlKeyboard::keyboard_leave(uint32_t serial, ::wl_surface *surface) { Q_EMIT leave(serial, surface); } void WlKeyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t keyValue, uint32_t state) { Q_EMIT key(serial, time, keyValue, key_state(state)); } void keyboardKeyPressed(quint32 key, quint32 time) { auto virtualKeyboard = static_cast(kwinApp())->virtualKeyboard(); Q_EMIT virtualKeyboard->keyChanged(key, KeyboardKeyState::Pressed, std::chrono::milliseconds(time), virtualKeyboard); } void keyboardKeyReleased(quint32 key, quint32 time) { auto virtualKeyboard = static_cast(kwinApp())->virtualKeyboard(); Q_EMIT virtualKeyboard->keyChanged(key, KeyboardKeyState::Released, std::chrono::milliseconds(time), virtualKeyboard); } void pointerAxisHorizontal(qreal delta, quint32 time, qint32 discreteDelta, PointerAxisSource source) { auto virtualPointer = static_cast(kwinApp())->virtualPointer(); Q_EMIT virtualPointer->pointerAxisChanged(PointerAxis::Horizontal, delta, discreteDelta, source, false, std::chrono::milliseconds(time), virtualPointer); Q_EMIT virtualPointer->pointerFrame(virtualPointer); } void pointerAxisVertical(qreal delta, quint32 time, qint32 discreteDelta, PointerAxisSource source) { auto virtualPointer = static_cast(kwinApp())->virtualPointer(); Q_EMIT virtualPointer->pointerAxisChanged(PointerAxis::Vertical, delta, discreteDelta, source, false, std::chrono::milliseconds(time), virtualPointer); Q_EMIT virtualPointer->pointerFrame(virtualPointer); } void pointerButtonPressed(quint32 button, quint32 time) { auto virtualPointer = static_cast(kwinApp())->virtualPointer(); Q_EMIT virtualPointer->pointerButtonChanged(button, PointerButtonState::Pressed, std::chrono::milliseconds(time), virtualPointer); Q_EMIT virtualPointer->pointerFrame(virtualPointer); } void pointerButtonReleased(quint32 button, quint32 time) { auto virtualPointer = static_cast(kwinApp())->virtualPointer(); Q_EMIT virtualPointer->pointerButtonChanged(button, PointerButtonState::Released, std::chrono::milliseconds(time), virtualPointer); Q_EMIT virtualPointer->pointerFrame(virtualPointer); } void pointerMotion(const QPointF &position, quint32 time) { auto virtualPointer = static_cast(kwinApp())->virtualPointer(); Q_EMIT virtualPointer->pointerMotionAbsolute(position, std::chrono::milliseconds(time), virtualPointer); Q_EMIT virtualPointer->pointerFrame(virtualPointer); } void pointerMotionRelative(const QPointF &delta, quint32 time) { auto virtualPointer = static_cast(kwinApp())->virtualPointer(); Q_EMIT virtualPointer->pointerMotion(delta, delta, std::chrono::milliseconds(time), virtualPointer); Q_EMIT virtualPointer->pointerFrame(virtualPointer); } void touchCancel() { auto virtualTouch = static_cast(kwinApp())->virtualTouch(); Q_EMIT virtualTouch->touchCanceled(virtualTouch); } void touchDown(qint32 id, const QPointF &pos, quint32 time) { auto virtualTouch = static_cast(kwinApp())->virtualTouch(); Q_EMIT virtualTouch->touchDown(id, pos, std::chrono::milliseconds(time), virtualTouch); } void touchMotion(qint32 id, const QPointF &pos, quint32 time) { auto virtualTouch = static_cast(kwinApp())->virtualTouch(); Q_EMIT virtualTouch->touchMotion(id, pos, std::chrono::milliseconds(time), virtualTouch); } void touchUp(qint32 id, quint32 time) { auto virtualTouch = static_cast(kwinApp())->virtualTouch(); Q_EMIT virtualTouch->touchUp(id, std::chrono::milliseconds(time), virtualTouch); } void tabletPadButtonPressed(quint32 button, quint32 time) { auto virtualTabletPad = static_cast(kwinApp())->virtualTabletPad(); Q_EMIT virtualTabletPad->tabletPadButtonEvent(button, true, 0, 0, false, std::chrono::milliseconds(time), virtualTabletPad); } void tabletPadButtonReleased(quint32 button, quint32 time) { auto virtualTabletPad = static_cast(kwinApp())->virtualTabletPad(); Q_EMIT virtualTabletPad->tabletPadButtonEvent(button, false, 0, 0, false, std::chrono::milliseconds(time), virtualTabletPad); } void tabletPadDialEvent(double delta, int number, quint32 time) { auto virtualTabletPad = static_cast(kwinApp())->virtualTabletPad(); Q_EMIT virtualTabletPad->tabletPadDialEvent(number, delta, 0, std::chrono::milliseconds(time), virtualTabletPad); } void tabletPadRingEvent(qreal position, int number, quint32 group, quint32 mode, quint32 time) { auto virtualTabletPad = static_cast(kwinApp())->virtualTabletPad(); Q_EMIT virtualTabletPad->tabletPadRingEvent(number, position, true, group, mode, std::chrono::milliseconds(time), virtualTabletPad); } void tabletToolButtonPressed(quint32 button, quint32 time) { auto tablet = static_cast(kwinApp())->virtualTablet(); auto tool = static_cast(kwinApp())->virtualTabletTool(); Q_EMIT tablet->tabletToolButtonEvent(button, true, tool, std::chrono::milliseconds(time), tablet); } void tabletToolButtonReleased(quint32 button, quint32 time) { auto tablet = static_cast(kwinApp())->virtualTablet(); auto tool = static_cast(kwinApp())->virtualTabletTool(); Q_EMIT tablet->tabletToolButtonEvent(button, false, tool, std::chrono::milliseconds(time), tablet); } void tabletToolProximityEvent(const QPointF &pos, qreal xTilt, qreal yTilt, qreal rotation, qreal distance, bool tipNear, qreal sliderPosition, quint32 time) { auto tablet = static_cast(kwinApp())->virtualTablet(); auto tool = static_cast(kwinApp())->virtualTabletTool(); Q_EMIT tablet->tabletToolProximityEvent(pos, xTilt, yTilt, rotation, distance, tipNear, sliderPosition, tool, std::chrono::milliseconds(time), tablet); } void tabletToolAxisEvent(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal distance, bool tipDown, qreal sliderPosition, quint32 time) { auto tablet = static_cast(kwinApp())->virtualTablet(); auto tool = static_cast(kwinApp())->virtualTabletTool(); Q_EMIT tablet->tabletToolAxisEvent(pos, pressure, xTilt, yTilt, rotation, distance, tipDown, sliderPosition, tool, std::chrono::milliseconds(time), tablet); } void tabletToolTipEvent(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal distance, bool tipDown, qreal sliderPosition, quint32 time) { auto tablet = static_cast(kwinApp())->virtualTablet(); auto tool = static_cast(kwinApp())->virtualTabletTool(); Q_EMIT tablet->tabletToolTipEvent(pos, pressure, xTilt, yTilt, rotation, distance, tipDown, sliderPosition, tool, std::chrono::milliseconds(time), tablet); } XdgToplevelWindow::XdgToplevelWindow(const std::function &setup) : XdgToplevelWindow(s_waylandConnection.get(), setup) { } XdgToplevelWindow::XdgToplevelWindow(const std::function &setup) : XdgToplevelWindow(s_waylandConnection.get(), setup) { } XdgToplevelWindow::XdgToplevelWindow(Connection *connection, const std::function &setup) : m_connection(connection) , m_surface(createSurface(connection->compositor)) , m_toplevel(createXdgToplevelSurface(connection->xdgShell, m_surface.get(), setup)) { } XdgToplevelWindow::XdgToplevelWindow(Connection *connection, const std::function &setup) : m_connection(connection) , m_surface(createSurface(connection->compositor)) , m_toplevel(createXdgToplevelSurface(connection->xdgShell, m_surface.get(), [this, &setup](XdgToplevel *toplevel) { setup(m_surface.get(), toplevel); })) { } XdgToplevelWindow::~XdgToplevelWindow() { if (m_window) { m_toplevel.reset(); m_surface.reset(); waitForWindowClosed(m_window); } } bool XdgToplevelWindow::show(const QSize &size, const QColor &color) { m_window = renderAndWaitForShown(m_connection->shm, m_surface.get(), size, color); return m_window != nullptr; } bool XdgToplevelWindow::show(const QImage &image) { m_window = renderAndWaitForShown(m_connection->shm, m_surface.get(), image); return m_window != nullptr; } void XdgToplevelWindow::unmap() { m_surface->attachBuffer((wl_buffer *)nullptr); m_surface->commit(KWayland::Client::Surface::CommitFlag::None); // unmapping destroys the KWin::Window m_window = nullptr; } bool XdgToplevelWindow::unmapAndWaitForClosed() { Window *window = m_window; unmap(); return waitForWindowClosed(window); } bool XdgToplevelWindow::presentWait() { const auto feedback = std::make_unique(m_connection->presentationTime->feedback(*m_surface)); m_surface->commit(KWayland::Client::Surface::CommitFlag::None); QSignalSpy spy(feedback.get(), &Test::WpPresentationFeedback::presented); return spy.wait(); } bool XdgToplevelWindow::waitSurfaceConfigure() { QSignalSpy surfaceConfigure(m_toplevel->xdgSurface(), &Test::XdgSurface::configureRequested); return surfaceConfigure.wait(); } std::optional XdgToplevelWindow::handleConfigure(const QColor &color) { QSignalSpy toplevelConfigure(m_toplevel.get(), &Test::XdgToplevel::configureRequested); QSignalSpy surfaceConfigure(m_toplevel->xdgSurface(), &Test::XdgSurface::configureRequested); if (!toplevelConfigure.wait()) { return std::nullopt; } m_toplevel->xdgSurface()->ack_configure(surfaceConfigure.last().at(0).value()); const QSize ret = toplevelConfigure.last().at(0).toSize(); if (ret == m_surface->size()) { m_surface->commit(KWayland::Client::Surface::CommitFlag::None); return ret; } Test::render(m_connection->shm, m_surface.get(), toplevelConfigure.last().at(0).toSize(), color); QSignalSpy frameGeometryChanged(m_window, &KWin::Window::frameGeometryChanged); if (!frameGeometryChanged.wait()) { return std::nullopt; } return ret; } } } #include "test_helpers.moc"