From bfa9646d1dc695698dc948f73a08d82c924022d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 2 Oct 2015 10:44:29 +0200 Subject: [PATCH] [wayland] Add a virtual framebuffer backend A new backend which doesn't present the rendered output. It uses a QPainter scene, renders to a QImage but doesn't present it anywhere. Thus a real virtual backend. By exporting the environment variable KWIN_WAYLAND_VIRTUAL_SCREENSHOTS the backend creates a temporary dir, prints the path to std-out and saves each rendered frame into that directory. Of course with exit it will be deleted again. --- backends/CMakeLists.txt | 1 + backends/virtual/CMakeLists.txt | 15 +++ .../scene_qpainter_virtual_backend.cpp | 99 +++++++++++++++++++ .../virtual/scene_qpainter_virtual_backend.h | 58 +++++++++++ backends/virtual/screens_virtual.cpp | 70 +++++++++++++ backends/virtual/screens_virtual.h | 47 +++++++++ backends/virtual/virtual.json | 8 ++ backends/virtual/virtual_backend.cpp | 61 ++++++++++++ backends/virtual/virtual_backend.h | 59 +++++++++++ main_wayland.cpp | 10 +- 10 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 backends/virtual/CMakeLists.txt create mode 100644 backends/virtual/scene_qpainter_virtual_backend.cpp create mode 100644 backends/virtual/scene_qpainter_virtual_backend.h create mode 100644 backends/virtual/screens_virtual.cpp create mode 100644 backends/virtual/screens_virtual.h create mode 100644 backends/virtual/virtual.json create mode 100644 backends/virtual/virtual_backend.cpp create mode 100644 backends/virtual/virtual_backend.h diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index d3dc674bfe..104c891d0d 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(fbdev) if(HAVE_LIBHYBRIS) add_subdirectory(hwcomposer) endif() +add_subdirectory(virtual) add_subdirectory(wayland) if(X11_XCB_FOUND) add_subdirectory(x11) diff --git a/backends/virtual/CMakeLists.txt b/backends/virtual/CMakeLists.txt new file mode 100644 index 0000000000..82c8a395b7 --- /dev/null +++ b/backends/virtual/CMakeLists.txt @@ -0,0 +1,15 @@ +set(VIRTUAL_SOURCES + virtual_backend.cpp + scene_qpainter_virtual_backend.cpp + screens_virtual.cpp +) + +add_library(KWinWaylandVirtualBackend MODULE ${VIRTUAL_SOURCES}) +target_link_libraries(KWinWaylandVirtualBackend kwin) + +install( + TARGETS + KWinWaylandVirtualBackend + DESTINATION + ${PLUGIN_INSTALL_DIR}/org.kde.kwin.waylandbackends/ +) diff --git a/backends/virtual/scene_qpainter_virtual_backend.cpp b/backends/virtual/scene_qpainter_virtual_backend.cpp new file mode 100644 index 0000000000..c4e8754095 --- /dev/null +++ b/backends/virtual/scene_qpainter_virtual_backend.cpp @@ -0,0 +1,99 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 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 "scene_qpainter_virtual_backend.h" +#include "virtual_backend.h" +#include "cursor.h" + +#include +#include +#include + +namespace KWin +{ +VirtualQPainterBackend::VirtualQPainterBackend(VirtualBackend *backend) + : QPainterBackend() + , m_backBuffer(backend->size(), QImage::Format_RGB32) + , m_backend(backend) +{ + if (qEnvironmentVariableIsSet("KWIN_WAYLAND_VIRTUAL_SCREENSHOTS")) { + m_screenshotDir.reset(new QTemporaryDir); + if (!m_screenshotDir->isValid()) { + m_screenshotDir.reset(); + } + if (!m_screenshotDir.isNull()) { + qDebug() << "Screenshots saved to: " << m_screenshotDir->path(); + } + } +} + +VirtualQPainterBackend::~VirtualQPainterBackend() = default; + +QImage *VirtualQPainterBackend::buffer() +{ + return &m_backBuffer; +} + +bool VirtualQPainterBackend::needsFullRepaint() const +{ + return true; +} + +void VirtualQPainterBackend::prepareRenderingFrame() +{ +} + +void VirtualQPainterBackend::screenGeometryChanged(const QSize &size) +{ + if (m_backBuffer.size() != size) { + m_backBuffer = QImage(size, QImage::Format_RGB32); + m_backBuffer.fill(Qt::black); + } +} + +void VirtualQPainterBackend::present(int mask, const QRegion &damage) +{ + Q_UNUSED(mask) + Q_UNUSED(damage) + if (!m_screenshotDir.isNull()) { + m_backBuffer.save(QStringLiteral("%1/%2.png").arg(m_screenshotDir->path()).arg(QString::number(m_frameCounter++))); + } +} + +bool VirtualQPainterBackend::usesOverlayWindow() const +{ + return false; +} + +void VirtualQPainterBackend::renderCursor(QPainter *painter) +{ + if (!m_backend->usesSoftwareCursor()) { + return; + } + const QImage img = m_backend->softwareCursor(); + if (img.isNull()) { + return; + } + const QPoint cursorPos = Cursor::pos(); + const QPoint hotspot = m_backend->softwareCursorHotspot(); + painter->drawImage(cursorPos - hotspot, img); + m_backend->markCursorAsRendered(); +} + +} diff --git a/backends/virtual/scene_qpainter_virtual_backend.h b/backends/virtual/scene_qpainter_virtual_backend.h new file mode 100644 index 0000000000..996dfe856f --- /dev/null +++ b/backends/virtual/scene_qpainter_virtual_backend.h @@ -0,0 +1,58 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 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_SCENE_QPAINTER_X11_BACKEND_H +#define KWIN_SCENE_QPAINTER_X11_BACKEND_H + +#include "scene_qpainter.h" + +#include + +class QTemporaryDir; + +namespace KWin +{ + +class VirtualBackend; + +class VirtualQPainterBackend : public QObject, public QPainterBackend +{ + Q_OBJECT +public: + VirtualQPainterBackend(VirtualBackend *backend); + virtual ~VirtualQPainterBackend(); + + QImage *buffer() override; + bool needsFullRepaint() const override; + bool usesOverlayWindow() const override; + void prepareRenderingFrame() override; + void present(int mask, const QRegion &damage) override; + void screenGeometryChanged(const QSize &size) override; + void renderCursor(QPainter *painter) override; + +private: + QImage m_backBuffer; + VirtualBackend *m_backend; + QScopedPointer m_screenshotDir; + int m_frameCounter = 0; +}; + +} + +#endif diff --git a/backends/virtual/screens_virtual.cpp b/backends/virtual/screens_virtual.cpp new file mode 100644 index 0000000000..b03aeff846 --- /dev/null +++ b/backends/virtual/screens_virtual.cpp @@ -0,0 +1,70 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 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 "screens_virtual.h" +#include "virtual_backend.h" + +namespace KWin +{ + +VirtualScreens::VirtualScreens(VirtualBackend *backend, QObject *parent) + : Screens(parent) + , m_backend(backend) +{ +} + +VirtualScreens::~VirtualScreens() = default; + +void VirtualScreens::init() +{ + KWin::Screens::init(); + connect(m_backend, &VirtualBackend::sizeChanged, + this, &VirtualScreens::startChangedTimer); + updateCount(); + emit changed(); +} + +QRect VirtualScreens::geometry(int screen) const +{ + if (screen == 0) { + return QRect(QPoint(0, 0), size(screen)); + } + return QRect(); +} + +QSize VirtualScreens::size(int screen) const +{ + if (screen == 0) { + return m_backend->size(); + } + return QSize(); +} + +void VirtualScreens::updateCount() +{ + setCount(1); +} + +int VirtualScreens::number(const QPoint &pos) const +{ + Q_UNUSED(pos) + return 0; +} + +} diff --git a/backends/virtual/screens_virtual.h b/backends/virtual/screens_virtual.h new file mode 100644 index 0000000000..4f1ec7dbc4 --- /dev/null +++ b/backends/virtual/screens_virtual.h @@ -0,0 +1,47 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 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_SCREENS_VIRTUAL_H +#define KWIN_SCREENS_VIRTUAL_H +#include "screens.h" + +namespace KWin +{ +class VirtualBackend; + +class VirtualScreens : public Screens +{ + Q_OBJECT +public: + VirtualScreens(VirtualBackend *backend, QObject *parent = nullptr); + virtual ~VirtualScreens(); + void init() override; + QRect geometry(int screen) const override; + int number(const QPoint &pos) const override; + QSize size(int screen) const override; + void updateCount() override; + +private: + VirtualBackend *m_backend; +}; + +} + +#endif + diff --git a/backends/virtual/virtual.json b/backends/virtual/virtual.json new file mode 100644 index 0000000000..1437e36307 --- /dev/null +++ b/backends/virtual/virtual.json @@ -0,0 +1,8 @@ +{ + "KPlugin": { + "Id": "KWinWaylandVirtualBackend", + "Name": "virtual", + "Description": "Render to a virtual framebuffer." + }, + "input": true +} diff --git a/backends/virtual/virtual_backend.cpp b/backends/virtual/virtual_backend.cpp new file mode 100644 index 0000000000..601358f96b --- /dev/null +++ b/backends/virtual/virtual_backend.cpp @@ -0,0 +1,61 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 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 "virtual_backend.h" +#include "scene_qpainter_virtual_backend.h" +#include "screens_virtual.h" +#include "wayland_server.h" +// KWayland +#include + +namespace KWin +{ + +VirtualBackend::VirtualBackend(QObject *parent) + : AbstractBackend(parent) +{ + setSoftWareCursor(true); + setSupportsPointerWarping(true); + // currently only QPainter - enforce it + qputenv("KWIN_COMPOSE", QByteArrayLiteral("Q")); +} + +VirtualBackend::~VirtualBackend() = default; + +void VirtualBackend::init() +{ + m_size = initialWindowSize(); + setReady(true); + waylandServer()->seat()->setHasPointer(true); + waylandServer()->seat()->setHasKeyboard(true); + waylandServer()->seat()->setHasTouch(true); + emit screensQueried(); +} + +Screens *VirtualBackend::createScreens(QObject *parent) +{ + return new VirtualScreens(this, parent); +} + +QPainterBackend *VirtualBackend::createQPainterBackend() +{ + return new VirtualQPainterBackend(this); +} + +} diff --git a/backends/virtual/virtual_backend.h b/backends/virtual/virtual_backend.h new file mode 100644 index 0000000000..e59903c4f6 --- /dev/null +++ b/backends/virtual/virtual_backend.h @@ -0,0 +1,59 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 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_X11WINDOWED_BACKEND_H +#define KWIN_X11WINDOWED_BACKEND_H +#include "abstract_backend.h" + +#include + +#include +#include + +namespace KWin +{ + +class KWIN_EXPORT VirtualBackend : public AbstractBackend +{ + Q_OBJECT + Q_INTERFACES(KWin::AbstractBackend) + Q_PLUGIN_METADATA(IID "org.kde.kwin.AbstractBackend" FILE "virtual.json") + Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) +public: + VirtualBackend(QObject *parent = nullptr); + virtual ~VirtualBackend(); + void init() override; + + QSize size() const { + return m_size; + } + + Screens *createScreens(QObject *parent = nullptr) override; + QPainterBackend* createQPainterBackend() override; + +Q_SIGNALS: + void sizeChanged(); + +private: + QSize m_size; +}; + +} + +#endif diff --git a/main_wayland.cpp b/main_wayland.cpp index 949a1a26e5..eeaaea7501 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -326,6 +326,7 @@ static const QString s_drmPlugin = QStringLiteral("KWinWaylandDrmBackend"); #if HAVE_LIBHYBRIS static const QString s_hwcomposerPlugin = QStringLiteral("KWinWaylandHwcomposerBackend"); #endif +static const QString s_virtualPlugin = QStringLiteral("KWinWaylandVirtualBackend"); static QString automaticBackendSelection() { @@ -411,6 +412,7 @@ int main(int argc, char * argv[]) QCommandLineOption waylandDisplayOption(QStringLiteral("wayland-display"), i18n("The Wayland Display to use in windowed mode on platform Wayland."), QStringLiteral("display")); + QCommandLineOption virtualFbOption(QStringLiteral("virtual"), i18n("Render to a virtual framebuffer.")); QCommandLineOption widthOption(QStringLiteral("width"), i18n("The width for windowed mode. Default width is 1024."), QStringLiteral("width")); @@ -437,7 +439,10 @@ int main(int argc, char * argv[]) parser.addOption(framebufferOption); parser.addOption(framebufferDeviceOption); } - if (hasPlugin(KWin::s_x11Plugin)) { + if (hasPlugin(KWin::s_virtualPlugin)) { + parser.addOption(virtualFbOption); + } + if (hasPlugin(KWin::s_x11Plugin) || hasPlugin(KWin::s_virtualPlugin)) { parser.addOption(widthOption); parser.addOption(heightOption); } @@ -545,6 +550,9 @@ int main(int argc, char * argv[]) pluginName = KWin::s_hwcomposerPlugin; } #endif + if (parser.isSet(virtualFbOption)) { + pluginName = KWin::s_virtualPlugin; + } if (pluginName.isEmpty()) { std::cerr << "No backend specified through command line argument, trying auto resolution" << std::endl;