diff --git a/plugins/platforms/wayland/wayland_backend.cpp b/plugins/platforms/wayland/wayland_backend.cpp index e0e3b6f39..455ff66b9 100644 --- a/plugins/platforms/wayland/wayland_backend.cpp +++ b/plugins/platforms/wayland/wayland_backend.cpp @@ -1,851 +1,838 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg Copyright 2013 Martin Gräßlin 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 . *********************************************************************/ #include "wayland_backend.h" #if HAVE_WAYLAND_EGL #include "egl_wayland_backend.h" #endif #include "logging.h" #include "scene_qpainter_wayland_backend.h" #include "wayland_output.h" #include "composite.h" #include "cursor.h" #include "input.h" #include "main.h" #include "outputscreens.h" #include "pointer_input.h" #include "screens.h" #include "wayland_cursor_theme.h" #include "wayland_server.h" #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { namespace Wayland { using namespace KWayland::Client; WaylandCursor::WaylandCursor(WaylandBackend *backend) : QObject(backend) , m_backend(backend) { resetSurface(); } void WaylandCursor::resetSurface() { delete m_surface; m_surface = backend()->compositor()->createSurface(this); } void WaylandCursor::init() { installImage(); } WaylandCursor::~WaylandCursor() { delete m_surface; } void WaylandCursor::installImage() { const QImage image = m_backend->softwareCursor(); if (image.isNull() || image.size().isEmpty()) { doInstallImage(nullptr, QSize()); return; } wl_buffer *imageBuffer = *(m_backend->shmPool()->createBuffer(image).data()); doInstallImage(imageBuffer, image.size()); } void WaylandCursor::doInstallImage(wl_buffer *image, const QSize &size) { auto *pointer = m_backend->seat()->pointer(); if (!pointer || !pointer->isValid()) { return; } pointer->setCursor(m_surface, image ? m_backend->softwareCursorHotspot() : QPoint()); drawSurface(image, size); } void WaylandCursor::drawSurface(wl_buffer *image, const QSize &size) { m_surface->attachBuffer(image); m_surface->damage(QRect(QPoint(0,0), size)); m_surface->commit(Surface::CommitFlag::None); m_backend->flush(); } WaylandSubSurfaceCursor::WaylandSubSurfaceCursor(WaylandBackend *backend) : WaylandCursor(backend) { } void WaylandSubSurfaceCursor::init() { if (auto *pointer = backend()->seat()->pointer()) { pointer->hideCursor(); } } WaylandSubSurfaceCursor::~WaylandSubSurfaceCursor() { delete m_subSurface; } void WaylandSubSurfaceCursor::changeOutput(WaylandOutput *output) { delete m_subSurface; m_subSurface = nullptr; m_output = output; if (!output) { return; } createSubSurface(); surface()->commit(); } void WaylandSubSurfaceCursor::createSubSurface() { if (m_subSurface) { return; } if (!m_output) { return; } resetSurface(); m_subSurface = backend()->subCompositor()->createSubSurface(surface(), m_output->surface(), this); m_subSurface->setMode(SubSurface::Mode::Desynchronized); } void WaylandSubSurfaceCursor::doInstallImage(wl_buffer *image, const QSize &size) { if (!image) { delete m_subSurface; m_subSurface = nullptr; return; } createSubSurface(); // cursor position might have changed due to different cursor hot spot move(input()->pointer()->pos()); drawSurface(image, size); } QPointF WaylandSubSurfaceCursor::absoluteToRelativePosition(const QPointF &position) { auto ret = position - m_output->geometry().topLeft() - backend()->softwareCursorHotspot(); return ret; } void WaylandSubSurfaceCursor::move(const QPointF &globalPosition) { auto *output = backend()->getOutputAt(globalPosition.toPoint()); if (!m_output || (output && m_output != output)) { changeOutput(output); if (!m_output) { // cursor might be off the grid return; } installImage(); return; } if (!m_subSurface) { return; } // place the sub-surface relative to the output it is on and factor in the hotspot const auto relativePosition = globalPosition.toPoint() - backend()->softwareCursorHotspot() - m_output->geometry().topLeft(); m_subSurface->setPosition(relativePosition); Compositor::self()->addRepaintFull(); } WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend) : QObject(nullptr) , m_seat(new Seat(this)) , m_pointer(nullptr) , m_keyboard(nullptr) , m_touch(nullptr) , m_enteredSerial(0) , m_backend(backend) { m_seat->setup(seat); connect(m_seat, &Seat::hasKeyboardChanged, this, [this](bool hasKeyboard) { if (hasKeyboard) { m_keyboard = m_seat->createKeyboard(this); connect(m_keyboard, &Keyboard::keyChanged, this, [this](quint32 key, Keyboard::KeyState state, quint32 time) { switch (state) { case Keyboard::KeyState::Pressed: if (key == KEY_RIGHTCTRL) { m_backend->togglePointerLock(); } m_backend->keyboardKeyPressed(key, time); break; case Keyboard::KeyState::Released: m_backend->keyboardKeyReleased(key, time); break; default: Q_UNREACHABLE(); } } ); connect(m_keyboard, &Keyboard::modifiersChanged, this, [this](quint32 depressed, quint32 latched, quint32 locked, quint32 group) { m_backend->keyboardModifiers(depressed, latched, locked, group); } ); connect(m_keyboard, &Keyboard::keymapChanged, this, [this](int fd, quint32 size) { m_backend->keymapChange(fd, size); } ); } else { destroyKeyboard(); } } ); connect(m_seat, &Seat::hasPointerChanged, this, [this](bool hasPointer) { if (hasPointer && !m_pointer) { m_pointer = m_seat->createPointer(this); setupPointerGestures(); connect(m_pointer, &Pointer::entered, this, [this](quint32 serial, const QPointF &relativeToSurface) { Q_UNUSED(relativeToSurface) m_enteredSerial = serial; } ); connect(m_pointer, &Pointer::motion, this, [this](const QPointF &relativeToSurface, quint32 time) { m_backend->pointerMotionRelativeToOutput(relativeToSurface, time); } ); connect(m_pointer, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { Q_UNUSED(serial) switch (state) { case Pointer::ButtonState::Pressed: m_backend->pointerButtonPressed(button, time); break; case Pointer::ButtonState::Released: m_backend->pointerButtonReleased(button, time); break; default: Q_UNREACHABLE(); } } ); // TODO: Send discreteDelta and source as well. connect(m_pointer, &Pointer::axisChanged, this, [this](quint32 time, Pointer::Axis axis, qreal delta) { switch (axis) { case Pointer::Axis::Horizontal: m_backend->pointerAxisHorizontal(delta, time); break; case Pointer::Axis::Vertical: m_backend->pointerAxisVertical(delta, time); break; default: Q_UNREACHABLE(); } } ); } else { destroyPointer(); } } ); connect(m_seat, &Seat::hasTouchChanged, [this] (bool hasTouch) { if (hasTouch && !m_touch) { m_touch = m_seat->createTouch(this); connect(m_touch, &Touch::sequenceCanceled, m_backend, &Platform::touchCancel); connect(m_touch, &Touch::frameEnded, m_backend, &Platform::touchFrame); connect(m_touch, &Touch::sequenceStarted, this, [this] (TouchPoint *tp) { m_backend->touchDown(tp->id(), tp->position(), tp->time()); } ); connect(m_touch, &Touch::pointAdded, this, [this] (TouchPoint *tp) { m_backend->touchDown(tp->id(), tp->position(), tp->time()); } ); connect(m_touch, &Touch::pointRemoved, this, [this] (TouchPoint *tp) { m_backend->touchUp(tp->id(), tp->time()); } ); connect(m_touch, &Touch::pointMoved, this, [this] (TouchPoint *tp) { m_backend->touchMotion(tp->id(), tp->position(), tp->time()); } ); } else { destroyTouch(); } } ); WaylandServer *server = waylandServer(); if (server) { using namespace KWayland::Server; SeatInterface *si = server->seat(); connect(m_seat, &Seat::hasKeyboardChanged, si, &SeatInterface::setHasKeyboard); connect(m_seat, &Seat::hasPointerChanged, si, &SeatInterface::setHasPointer); connect(m_seat, &Seat::hasTouchChanged, si, &SeatInterface::setHasTouch); connect(m_seat, &Seat::nameChanged, si, &SeatInterface::setName); } } void WaylandBackend::pointerMotionRelativeToOutput(const QPointF &position, quint32 time) { auto outputIt = std::find_if(m_outputs.begin(), m_outputs.end(), [this](WaylandOutput *wo) { return wo->surface() == m_seat->pointer()->enteredSurface(); }); Q_ASSERT(outputIt != m_outputs.end()); const QPointF outputPosition = (*outputIt)->geometry().topLeft() + position; Platform::pointerMotion(outputPosition, time); } WaylandSeat::~WaylandSeat() { destroyPointer(); destroyKeyboard(); destroyTouch(); } void WaylandSeat::destroyPointer() { delete m_pinchGesture; m_pinchGesture = nullptr; delete m_swipeGesture; m_swipeGesture = nullptr; delete m_pointer; m_pointer = nullptr; } void WaylandSeat::destroyKeyboard() { delete m_keyboard; m_keyboard = nullptr; } void WaylandSeat::destroyTouch() { delete m_touch; m_touch = nullptr; } void WaylandSeat::setupPointerGestures() { if (!m_pointer || !m_gesturesInterface) { return; } if (m_pinchGesture || m_swipeGesture) { return; } m_pinchGesture = m_gesturesInterface->createPinchGesture(m_pointer, this); m_swipeGesture = m_gesturesInterface->createSwipeGesture(m_pointer, this); connect(m_pinchGesture, &PointerPinchGesture::started, m_backend, [this] (quint32 serial, quint32 time) { Q_UNUSED(serial); m_backend->processPinchGestureBegin(m_pinchGesture->fingerCount(), time); } ); connect(m_pinchGesture, &PointerPinchGesture::updated, m_backend, [this] (const QSizeF &delta, qreal scale, qreal rotation, quint32 time) { m_backend->processPinchGestureUpdate(scale, rotation, delta, time); } ); connect(m_pinchGesture, &PointerPinchGesture::ended, m_backend, [this] (quint32 serial, quint32 time) { Q_UNUSED(serial) m_backend->processPinchGestureEnd(time); } ); connect(m_pinchGesture, &PointerPinchGesture::cancelled, m_backend, [this] (quint32 serial, quint32 time) { Q_UNUSED(serial) m_backend->processPinchGestureCancelled(time); } ); connect(m_swipeGesture, &PointerSwipeGesture::started, m_backend, [this] (quint32 serial, quint32 time) { Q_UNUSED(serial) m_backend->processSwipeGestureBegin(m_swipeGesture->fingerCount(), time); } ); connect(m_swipeGesture, &PointerSwipeGesture::updated, m_backend, &Platform::processSwipeGestureUpdate); connect(m_swipeGesture, &PointerSwipeGesture::ended, m_backend, [this] (quint32 serial, quint32 time) { Q_UNUSED(serial) m_backend->processSwipeGestureEnd(time); } ); connect(m_swipeGesture, &PointerSwipeGesture::cancelled, m_backend, [this] (quint32 serial, quint32 time) { Q_UNUSED(serial) m_backend->processSwipeGestureCancelled(time); } ); } WaylandBackend::WaylandBackend(QObject *parent) : Platform(parent) , m_display(nullptr) , m_eventQueue(new EventQueue(this)) , m_registry(new Registry(this)) , m_compositor(new KWayland::Client::Compositor(this)) , m_subCompositor(new KWayland::Client::SubCompositor(this)) - , m_shell(new Shell(this)) , m_shm(new ShmPool(this)) , m_connectionThreadObject(new ConnectionThread(nullptr)) , m_connectionThread(nullptr) { connect(this, &WaylandBackend::connectionFailed, this, &WaylandBackend::initFailed); } WaylandBackend::~WaylandBackend() { if (m_pointerConstraints) { m_pointerConstraints->release(); } delete m_waylandCursor; qDeleteAll(m_outputs); if (m_xdgShell) { m_xdgShell->release(); } - m_shell->release(); m_subCompositor->release(); m_compositor->release(); m_registry->release(); delete m_seat; m_shm->release(); m_eventQueue->release(); m_connectionThreadObject->deleteLater(); m_connectionThread->quit(); m_connectionThread->wait(); qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display"; } void WaylandBackend::init() { connect(m_registry, &Registry::compositorAnnounced, this, [this](quint32 name) { m_compositor->setup(m_registry->bindCompositor(name, 1)); } ); connect(m_registry, &Registry::subCompositorAnnounced, this, [this](quint32 name) { m_subCompositor->setup(m_registry->bindSubCompositor(name, 1)); } ); - connect(m_registry, &Registry::shellAnnounced, this, - [this](quint32 name) { - m_shell->setup(m_registry->bindShell(name, 1)); - } - ); connect(m_registry, &Registry::seatAnnounced, this, [this](quint32 name) { if (Application::usesLibinput()) { return; } m_seat = new WaylandSeat(m_registry->bindSeat(name, 2), this); } ); connect(m_registry, &Registry::shmAnnounced, this, [this](quint32 name) { m_shm->setup(m_registry->bindShm(name, 1)); } ); connect(m_registry, &Registry::relativePointerManagerUnstableV1Announced, this, [this](quint32 name, quint32 version) { if (m_relativePointerManager) { return; } m_relativePointerManager = m_registry->createRelativePointerManager(name, version, this); if (m_pointerConstraints) { emit pointerLockSupportedChanged(); } } ); connect(m_registry, &Registry::pointerConstraintsUnstableV1Announced, this, [this](quint32 name, quint32 version) { if (m_pointerConstraints) { return; } m_pointerConstraints = m_registry->createPointerConstraints(name, version, this); if (m_relativePointerManager) { emit pointerLockSupportedChanged(); } } ); connect(m_registry, &Registry::interfacesAnnounced, this, &WaylandBackend::createOutputs); connect(m_registry, &Registry::interfacesAnnounced, this, [this] { if (!m_seat) { return; } const auto gi = m_registry->interface(Registry::Interface::PointerGesturesUnstableV1); if (gi.name == 0) { return; } auto gesturesInterface = m_registry->createPointerGestures(gi.name, gi.version, m_seat); m_seat->installGesturesInterface(gesturesInterface); m_waylandCursor = new WaylandCursor(this); } ); if (!deviceIdentifier().isEmpty()) { m_connectionThreadObject->setSocketName(deviceIdentifier()); } connect(this, &WaylandBackend::cursorChanged, this, [this] { if (!m_seat) { return; } m_waylandCursor->installImage(); markCursorAsRendered(); } ); connect(this, &WaylandBackend::pointerLockChanged, this, [this](bool locked) { delete m_waylandCursor; if (locked) { Q_ASSERT(!m_relativePointer); m_waylandCursor = new WaylandSubSurfaceCursor(this); m_waylandCursor->move(input()->pointer()->pos()); m_relativePointer = m_relativePointerManager->createRelativePointer(m_seat->pointer(), this); if (!m_relativePointer->isValid()) { return; } connect(m_relativePointer, &RelativePointer::relativeMotion, this, &WaylandBackend::relativeMotionHandler); } else { delete m_relativePointer; m_relativePointer = nullptr; m_waylandCursor = new WaylandCursor(this); } m_waylandCursor->init(); }); initConnection(); } void WaylandBackend::relativeMotionHandler(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp) { Q_UNUSED(deltaNonAccelerated) Q_ASSERT(m_waylandCursor); const auto oldGlobalPos = input()->pointer()->pos(); const QPointF newPos = oldGlobalPos + QPointF(delta.width(), delta.height()); m_waylandCursor->move(newPos); Platform::pointerMotion(newPos, timestamp); } void WaylandBackend::initConnection() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this]() { // create the event queue for the main gui thread m_display = m_connectionThreadObject->display(); m_eventQueue->setup(m_connectionThreadObject); m_registry->setEventQueue(m_eventQueue); // setup registry m_registry->create(m_display); m_registry->setup(); }, Qt::QueuedConnection); connect(m_connectionThreadObject, &ConnectionThread::connectionDied, this, [this]() { setReady(false); emit systemCompositorDied(); delete m_seat; m_shm->destroy(); qDeleteAll(m_outputs); m_outputs.clear(); - if (m_shell) { - m_shell->destroy(); - } if (m_xdgShell) { m_xdgShell->destroy(); } m_subCompositor->destroy(); m_compositor->destroy(); m_registry->destroy(); m_eventQueue->destroy(); if (m_display) { m_display = nullptr; } }, Qt::QueuedConnection); connect(m_connectionThreadObject, &ConnectionThread::failed, this, &WaylandBackend::connectionFailed, Qt::QueuedConnection); m_connectionThread = new QThread(this); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void WaylandBackend::updateScreenSize(WaylandOutput *output) { auto it = std::find(m_outputs.begin(), m_outputs.end(), output); int nextLogicalPosition = output->geometry().topRight().x(); while (++it != m_outputs.end()) { const QRect geo = (*it)->geometry(); (*it)->setGeometry(QPoint(nextLogicalPosition, 0), geo.size()); nextLogicalPosition = geo.topRight().x(); } } void WaylandBackend::createOutputs() { using namespace KWayland::Client; const auto ssdManagerIface = m_registry->interface(Registry::Interface::ServerSideDecorationManager); ServerSideDecorationManager *ssdManager = ssdManagerIface.name == 0 ? nullptr : m_registry->createServerSideDecorationManager(ssdManagerIface.name, ssdManagerIface.version, this); const auto xdgIface = m_registry->interface(Registry::Interface::XdgShellUnstableV6); if (xdgIface.name != 0) { m_xdgShell = m_registry->createXdgShell(xdgIface.name, xdgIface.version, this); } // we need to multiply the initial window size with the scale in order to // create an output window of this size in the end const int pixelWidth = initialWindowSize().width() * initialOutputScale() + 0.5; const int pixelHeight = initialWindowSize().height() * initialOutputScale() + 0.5; const int logicalWidth = initialWindowSize().width(); int logicalWidthSum = 0; for (int i = 0; i < initialOutputCount(); i++) { auto surface = m_compositor->createSurface(this); if (!surface || !surface->isValid()) { qCCritical(KWIN_WAYLAND_BACKEND) << "Creating Wayland Surface failed"; return; } if (ssdManager) { auto decoration = ssdManager->create(surface, this); connect(decoration, &ServerSideDecoration::modeChanged, this, [this, decoration] { if (decoration->mode() != ServerSideDecoration::Mode::Server) { decoration->requestMode(ServerSideDecoration::Mode::Server); } } ); } WaylandOutput *waylandOutput = nullptr; if (m_xdgShell && m_xdgShell->isValid()) { waylandOutput = new XdgShellOutput(surface, m_xdgShell, this, i+1); - } else if (m_shell->isValid()) { - waylandOutput = new ShellOutput(surface, m_shell, this); } if (!waylandOutput) { qCCritical(KWIN_WAYLAND_BACKEND) << "Binding to all shell interfaces failed for output" << i; return; } waylandOutput->init(QPoint(logicalWidthSum, 0), QSize(pixelWidth, pixelHeight)); connect(waylandOutput, &WaylandOutput::sizeChanged, this, [this, waylandOutput](const QSize &size) { Q_UNUSED(size) updateScreenSize(waylandOutput); Compositor::self()->addRepaintFull(); }); connect(waylandOutput, &WaylandOutput::frameRendered, this, &WaylandBackend::checkBufferSwap); logicalWidthSum += logicalWidth; m_outputs << waylandOutput; } setReady(true); emit screensQueried(); } Screens *WaylandBackend::createScreens(QObject *parent) { return new OutputScreens(this, parent); } OpenGLBackend *WaylandBackend::createOpenGLBackend() { #if HAVE_WAYLAND_EGL return new EglWaylandBackend(this); #else return nullptr; #endif } QPainterBackend *WaylandBackend::createQPainterBackend() { return new WaylandQPainterBackend(this); } void WaylandBackend::checkBufferSwap() { const bool allRendered = std::all_of(m_outputs.begin(), m_outputs.end(), [](WaylandOutput *o) { return o->rendered(); }); if (!allRendered) { // need to wait more // TODO: what if one does not need to be rendered (no damage)? return; } for (auto *output : m_outputs) { if (!output->rendered()) { return; } } Compositor::self()->bufferSwapComplete(); for (auto *output : m_outputs) { output->resetRendered(); } } void WaylandBackend::flush() { if (m_connectionThreadObject) { m_connectionThreadObject->flush(); } } WaylandOutput* WaylandBackend::getOutputAt(const QPointF globalPosition) { const auto pos = globalPosition.toPoint(); auto checkPosition = [pos](WaylandOutput *output) { return output->geometry().contains(pos); }; auto it = std::find_if(m_outputs.begin(), m_outputs.end(), checkPosition); return it == m_outputs.end() ? nullptr : *it; } bool WaylandBackend::supportsPointerLock() { return m_pointerConstraints && m_relativePointerManager; } void WaylandBackend::togglePointerLock() { if (!m_pointerConstraints) { return; } if (!m_relativePointerManager) { return; } if (!m_seat) { return; } auto pointer = m_seat->pointer(); if (!pointer) { return; } if (m_outputs.isEmpty()) { return; } for (auto output : m_outputs) { output->lockPointer(m_seat->pointer(), !m_pointerLockRequested); } m_pointerLockRequested = !m_pointerLockRequested; flush(); } bool WaylandBackend::pointerIsLocked() { for (auto *output : m_outputs) { if (output->pointerIsLocked()) { return true; } } return false; } QVector WaylandBackend::supportedCompositors() const { if (selectedCompositor() != NoCompositing) { return {selectedCompositor()}; } #if HAVE_WAYLAND_EGL return QVector{OpenGLCompositing, QPainterCompositing}; #else return QVector{QPainterCompositing}; #endif } Outputs WaylandBackend::outputs() const { return m_outputs; } Outputs WaylandBackend::enabledOutputs() const { // all outputs are enabled return m_outputs; } } } // KWin diff --git a/plugins/platforms/wayland/wayland_backend.h b/plugins/platforms/wayland/wayland_backend.h index e6f2e2d3a..5495b5b8b 100644 --- a/plugins/platforms/wayland/wayland_backend.h +++ b/plugins/platforms/wayland/wayland_backend.h @@ -1,284 +1,282 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg Copyright 2013 Martin Gräßlin 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 . *********************************************************************/ #ifndef KWIN_WAYLAND_BACKEND_H #define KWIN_WAYLAND_BACKEND_H // KWin #include "platform.h" #include #include // Qt #include #include #include #include #include class QTemporaryFile; struct wl_buffer; struct wl_display; struct wl_event_queue; struct wl_seat; namespace KWayland { namespace Client { class Buffer; class ShmPool; class Compositor; class ConnectionThread; class EventQueue; class Keyboard; class Pointer; class PointerConstraints; class PointerGestures; class PointerSwipeGesture; class PointerPinchGesture; class Registry; class RelativePointer; class RelativePointerManager; class Seat; -class Shell; class SubCompositor; class SubSurface; class Surface; class Touch; class XdgShell; } } namespace KWin { class WaylandCursorTheme; namespace Wayland { class WaylandBackend; class WaylandSeat; class WaylandOutput; class WaylandCursor : public QObject { Q_OBJECT public: explicit WaylandCursor(WaylandBackend *backend); ~WaylandCursor() override; virtual void init(); virtual void move(const QPointF &globalPosition) { Q_UNUSED(globalPosition) } void installImage(); protected: void resetSurface(); virtual void doInstallImage(wl_buffer *image, const QSize &size); void drawSurface(wl_buffer *image, const QSize &size); KWayland::Client::Surface *surface() const { return m_surface; } WaylandBackend *backend() const { return m_backend; } private: WaylandBackend *m_backend; KWayland::Client::Pointer *m_pointer; KWayland::Client::Surface *m_surface = nullptr; }; class WaylandSubSurfaceCursor : public WaylandCursor { Q_OBJECT public: explicit WaylandSubSurfaceCursor(WaylandBackend *backend); ~WaylandSubSurfaceCursor() override; void init() override; void move(const QPointF &globalPosition) override; private: void changeOutput(WaylandOutput *output); void doInstallImage(wl_buffer *image, const QSize &size) override; void createSubSurface(); QPointF absoluteToRelativePosition(const QPointF &position); WaylandOutput *m_output = nullptr; KWayland::Client::SubSurface *m_subSurface = nullptr; }; class WaylandSeat : public QObject { Q_OBJECT public: WaylandSeat(wl_seat *seat, WaylandBackend *backend); ~WaylandSeat() override; KWayland::Client::Pointer *pointer() const { return m_pointer; } void installGesturesInterface(KWayland::Client::PointerGestures *gesturesInterface) { m_gesturesInterface = gesturesInterface; setupPointerGestures(); } private: void destroyPointer(); void destroyKeyboard(); void destroyTouch(); void setupPointerGestures(); KWayland::Client::Seat *m_seat; KWayland::Client::Pointer *m_pointer; KWayland::Client::Keyboard *m_keyboard; KWayland::Client::Touch *m_touch; KWayland::Client::PointerGestures *m_gesturesInterface = nullptr; KWayland::Client::PointerPinchGesture *m_pinchGesture = nullptr; KWayland::Client::PointerSwipeGesture *m_swipeGesture = nullptr; uint32_t m_enteredSerial; WaylandBackend *m_backend; }; /** * @brief Class encapsulating all Wayland data structures needed by the Egl backend. * * It creates the connection to the Wayland Compositor, sets up the registry and creates * the Wayland output surfaces and its shell mappings. */ class KWIN_EXPORT WaylandBackend : public Platform { Q_OBJECT Q_INTERFACES(KWin::Platform) Q_PLUGIN_METADATA(IID "org.kde.kwin.Platform" FILE "wayland.json") public: explicit WaylandBackend(QObject *parent = nullptr); ~WaylandBackend() override; void init() override; wl_display *display(); KWayland::Client::Compositor *compositor(); KWayland::Client::SubCompositor *subCompositor(); KWayland::Client::ShmPool *shmPool(); Screens *createScreens(QObject *parent = nullptr) override; OpenGLBackend *createOpenGLBackend() override; QPainterBackend *createQPainterBackend() override; void flush(); WaylandSeat *seat() const { return m_seat; } KWayland::Client::PointerConstraints *pointerConstraints() const { return m_pointerConstraints; } void pointerMotionRelativeToOutput(const QPointF &position, quint32 time); bool supportsPointerLock(); void togglePointerLock(); bool pointerIsLocked(); QVector supportedCompositors() const override; void checkBufferSwap(); WaylandOutput* getOutputAt(const QPointF globalPosition); Outputs outputs() const override; Outputs enabledOutputs() const override; QVector waylandOutputs() const { return m_outputs; } Q_SIGNALS: void outputAdded(WaylandOutput *output); void outputRemoved(WaylandOutput *output); void systemCompositorDied(); void connectionFailed(); void pointerLockSupportedChanged(); void pointerLockChanged(bool locked); private: void initConnection(); void createOutputs(); void updateScreenSize(WaylandOutput *output); void relativeMotionHandler(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp); wl_display *m_display; KWayland::Client::EventQueue *m_eventQueue; KWayland::Client::Registry *m_registry; KWayland::Client::Compositor *m_compositor; KWayland::Client::SubCompositor *m_subCompositor; - KWayland::Client::Shell *m_shell; KWayland::Client::XdgShell *m_xdgShell = nullptr; KWayland::Client::ShmPool *m_shm; KWayland::Client::ConnectionThread *m_connectionThreadObject; WaylandSeat *m_seat = nullptr; KWayland::Client::RelativePointer *m_relativePointer = nullptr; KWayland::Client::RelativePointerManager *m_relativePointerManager = nullptr; KWayland::Client::PointerConstraints *m_pointerConstraints = nullptr; QThread *m_connectionThread; QVector m_outputs; WaylandCursor *m_waylandCursor = nullptr; bool m_pointerLockRequested = false; }; inline wl_display *WaylandBackend::display() { return m_display; } inline KWayland::Client::Compositor *WaylandBackend::compositor() { return m_compositor; } inline KWayland::Client::SubCompositor *WaylandBackend::subCompositor() { return m_subCompositor; } inline KWayland::Client::ShmPool* WaylandBackend::shmPool() { return m_shm; } } // namespace Wayland } // namespace KWin #endif // KWIN_WAYLAND_BACKEND_H diff --git a/plugins/platforms/wayland/wayland_output.cpp b/plugins/platforms/wayland/wayland_output.cpp index 7aae099c1..16a552e17 100644 --- a/plugins/platforms/wayland/wayland_output.cpp +++ b/plugins/platforms/wayland/wayland_output.cpp @@ -1,192 +1,178 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg 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 . *********************************************************************/ #include "wayland_output.h" #include "wayland_backend.h" #include "wayland_server.h" #include #include -#include #include #include namespace KWin { namespace Wayland { using namespace KWayland::Client; WaylandOutput::WaylandOutput(Surface *surface, WaylandBackend *backend) : AbstractWaylandOutput(backend) , m_surface(surface) , m_backend(backend) { connect(surface, &Surface::frameRendered, [this] { m_rendered = true; emit frameRendered(); }); } WaylandOutput::~WaylandOutput() { m_surface->destroy(); delete m_surface; } void WaylandOutput::init(const QPoint &logicalPosition, const QSize &pixelSize) { KWayland::Server::OutputDeviceInterface::Mode mode; mode.id = 0; mode.size = pixelSize; mode.flags = KWayland::Server::OutputDeviceInterface::ModeFlag::Current; mode.refreshRate = 60000; // TODO: can we get refresh rate data from Wayland host? initInterfaces("model_TODO", "manufacturer_TODO", "UUID_TODO", pixelSize, { mode }); setGeometry(logicalPosition, pixelSize); setScale(backend()->initialOutputScale()); } void WaylandOutput::setGeometry(const QPoint &logicalPosition, const QSize &pixelSize) { // TODO: set mode to have updated pixelSize Q_UNUSED(pixelSize) setGlobalPos(logicalPosition); } -ShellOutput::ShellOutput(Surface *surface, Shell *shell, WaylandBackend *backend) - : WaylandOutput(surface, backend) -{ - auto shellSurface = shell->createSurface(surface, this); - shellSurface->setToplevel(); -} - -ShellOutput::~ShellOutput() -{ - m_shellSurface->destroy(); - delete m_shellSurface; -} - XdgShellOutput::XdgShellOutput(Surface *surface, XdgShell *xdgShell, WaylandBackend *backend, int number) : WaylandOutput(surface, backend) , m_number(number) { m_xdgShellSurface = xdgShell->createSurface(surface, this); updateWindowTitle(); connect(m_xdgShellSurface, &XdgShellSurface::configureRequested, this, &XdgShellOutput::handleConfigure); connect(m_xdgShellSurface, &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit); connect(backend, &WaylandBackend::pointerLockSupportedChanged, this, &XdgShellOutput::updateWindowTitle); connect(backend, &WaylandBackend::pointerLockChanged, this, [this](bool locked) { if (locked) { if (!m_hasPointerLock) { // some other output has locked the pointer // this surface can stop trying to lock the pointer lockPointer(nullptr, false); // set it true for the other surface m_hasPointerLock = true; } } else { // just try unlocking lockPointer(nullptr, false); } updateWindowTitle(); }); } XdgShellOutput::~XdgShellOutput() { m_xdgShellSurface->destroy(); delete m_xdgShellSurface; } void XdgShellOutput::handleConfigure(const QSize &size, XdgShellSurface::States states, quint32 serial) { Q_UNUSED(states); if (size.width() == 0 || size.height() == 0) { return; } setGeometry(geometry().topLeft(), size); m_xdgShellSurface->ackConfigure(serial); emit sizeChanged(size); } void XdgShellOutput::updateWindowTitle() { QString grab; if (m_hasPointerLock) { grab = i18n("Press right control to ungrab pointer"); } else if (backend()->pointerConstraints()) { grab = i18n("Press right control key to grab pointer"); } const QString title = i18nc("Title of nested KWin Wayland with Wayland socket identifier as argument", "KDE Wayland Compositor #%1 (%2)", m_number, waylandServer()->display()->socketName()); if (grab.isEmpty()) { m_xdgShellSurface->setTitle(title); } else { m_xdgShellSurface->setTitle(title + QStringLiteral(" — ") + grab); } } void XdgShellOutput::lockPointer(Pointer *pointer, bool lock) { if (!lock) { const bool surfaceWasLocked = m_pointerLock && m_hasPointerLock; delete m_pointerLock; m_pointerLock = nullptr; m_hasPointerLock = false; if (surfaceWasLocked) { emit backend()->pointerLockChanged(false); } return; } Q_ASSERT(!m_pointerLock); m_pointerLock = backend()->pointerConstraints()->lockPointer(surface(), pointer, nullptr, PointerConstraints::LifeTime::OneShot, this); if (!m_pointerLock->isValid()) { delete m_pointerLock; m_pointerLock = nullptr; return; } connect(m_pointerLock, &LockedPointer::locked, this, [this] { m_hasPointerLock = true; emit backend()->pointerLockChanged(true); } ); connect(m_pointerLock, &LockedPointer::unlocked, this, [this] { delete m_pointerLock; m_pointerLock = nullptr; m_hasPointerLock = false; emit backend()->pointerLockChanged(false); } ); } } } diff --git a/plugins/platforms/wayland/wayland_output.h b/plugins/platforms/wayland/wayland_output.h index bce052af6..02ae8bb39 100644 --- a/plugins/platforms/wayland/wayland_output.h +++ b/plugins/platforms/wayland/wayland_output.h @@ -1,134 +1,122 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg 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 . *********************************************************************/ #ifndef KWIN_WAYLAND_OUTPUT_H #define KWIN_WAYLAND_OUTPUT_H #include "abstract_wayland_output.h" #include #include namespace KWayland { namespace Client { class Surface; class Shell; class ShellSurface; class Pointer; class LockedPointer; } } namespace KWin { namespace Wayland { class WaylandBackend; class WaylandOutput : public AbstractWaylandOutput { Q_OBJECT public: WaylandOutput(KWayland::Client::Surface *surface, WaylandBackend *backend); ~WaylandOutput() override; void init(const QPoint &logicalPosition, const QSize &pixelSize); virtual void lockPointer(KWayland::Client::Pointer *pointer, bool lock) { Q_UNUSED(pointer) Q_UNUSED(lock) } virtual bool pointerIsLocked() { return false; } /** * @brief defines the geometry of the output * @param logicalPosition top left position of the output in compositor space * @param pixelSize output size as seen from the outside */ void setGeometry(const QPoint &logicalPosition, const QSize &pixelSize); KWayland::Client::Surface* surface() const { return m_surface; } bool rendered() const { return m_rendered; } void resetRendered() { m_rendered = false; } Q_SIGNALS: void sizeChanged(const QSize &size); void frameRendered(); protected: WaylandBackend *backend() { return m_backend; } private: KWayland::Client::Surface *m_surface; WaylandBackend *m_backend; bool m_rendered = false; }; -class ShellOutput : public WaylandOutput -{ -public: - ShellOutput(KWayland::Client::Surface *surface, - KWayland::Client::Shell *shell, - WaylandBackend *backend); - ~ShellOutput() override; - -private: - KWayland::Client::ShellSurface *m_shellSurface = nullptr; -}; - class XdgShellOutput : public WaylandOutput { public: XdgShellOutput(KWayland::Client::Surface *surface, KWayland::Client::XdgShell *xdgShell, WaylandBackend *backend, int number); ~XdgShellOutput() override; void lockPointer(KWayland::Client::Pointer *pointer, bool lock) override; private: void handleConfigure(const QSize &size, KWayland::Client::XdgShellSurface::States states, quint32 serial); void updateWindowTitle(); KWayland::Client::XdgShellSurface *m_xdgShellSurface = nullptr; int m_number; KWayland::Client::LockedPointer *m_pointerLock = nullptr; bool m_hasPointerLock = false; }; } } #endif