diff --git a/CMakeLists.txt b/CMakeLists.txt
index 70541ab458..fca269fddd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -148,6 +148,7 @@ set(kwin_KDEINIT_SRCS
scene.cpp
scene_xrender.cpp
scene_opengl.cpp
+ scene_qpainter.cpp
glxbackend.cpp
thumbnailitem.cpp
lanczosfilter.cpp
diff --git a/composite.cpp b/composite.cpp
index c9df4de3ae..754e06b119 100644
--- a/composite.cpp
+++ b/composite.cpp
@@ -33,6 +33,7 @@ along with this program. If not, see .
#include "scene.h"
#include "scene_xrender.h"
#include "scene_opengl.h"
+#include "scene_qpainter.h"
#include "shadow.h"
#include "useractions.h"
#include "compositingprefs.h"
@@ -211,6 +212,10 @@ void Compositor::slotCompositingOptionsInitialized()
m_scene = SceneXrender::createScene();
break;
#endif
+ case QPainterCompositing:
+ qDebug() << "Initializing QPainter compositing";
+ m_scene = SceneQPainter::createScene();
+ break;
default:
qDebug() << "No compositing enabled";
m_starting = false;
@@ -816,6 +821,8 @@ QString Compositor::compositingType() const
#else
return QStringLiteral("gl2");
#endif
+ case QPainterCompositing:
+ return "qpainter";
case NoCompositing:
default:
return QStringLiteral("none");
diff --git a/libkwineffects/kwinglobals.h b/libkwineffects/kwinglobals.h
index e5d98e7704..a9f2dd3cdd 100644
--- a/libkwineffects/kwinglobals.h
+++ b/libkwineffects/kwinglobals.h
@@ -47,6 +47,7 @@ enum CompositingType {
**/
OpenGLCompositing = 1,
XRenderCompositing = 1<<1,
+ QPainterCompositing = 1<< 2,
OpenGL1Compositing = 1<<2 | OpenGLCompositing,
OpenGL2Compositing = 1<<3 | OpenGLCompositing
};
diff --git a/options.cpp b/options.cpp
index fb1d6ac380..f6eae0f3c7 100644
--- a/options.cpp
+++ b/options.cpp
@@ -874,6 +874,8 @@ bool Options::loadCompositingConfig (bool force)
QString compositingBackend = config.readEntry("Backend", "OpenGL");
if (compositingBackend == QStringLiteral("XRender"))
compositingMode = XRenderCompositing;
+ else if (compositingBackend == "QPainter")
+ compositingMode = QPainterCompositing;
else
compositingMode = OpenGLCompositing;
@@ -889,6 +891,11 @@ bool Options::loadCompositingConfig (bool force)
compositingMode = XRenderCompositing;
useCompositing = true;
break;
+ case 'Q':
+ qDebug() << "Compositing forced to QPainter mode by environment variable";
+ compositingMode = QPainterCompositing;
+ useCompositing = true;
+ break;
case 'N':
if (getenv("KDE_FAILSAFE"))
qDebug() << "Compositing disabled forcefully by KDE failsafe mode";
diff --git a/paintredirector.cpp b/paintredirector.cpp
index 600c85acc0..7deaed14af 100644
--- a/paintredirector.cpp
+++ b/paintredirector.cpp
@@ -43,9 +43,12 @@ PaintRedirector *PaintRedirector::create(Client *c, KDecoration *deco)
{
if (effects->isOpenGLCompositing()) {
return new OpenGLPaintRedirector(c, deco);
- } else {
+ } else if (effects->compositingType() == XRenderCompositing) {
return new RasterXRenderPaintRedirector(c, deco);
+ } else if (effects->compositingType() == QPainterCompositing) {
+ return new QImagePaintRedirector(c, deco);
}
+ return NULL;
}
PaintRedirector::PaintRedirector(Client *c, KDecoration *deco)
@@ -267,6 +270,12 @@ xcb_render_picture_t PaintRedirector::picture(PaintRedirector::DecorationPixmap
return XCB_RENDER_PICTURE_NONE;
}
+const QImage *PaintRedirector::image(PaintRedirector::DecorationPixmap border) const
+{
+ Q_UNUSED(border)
+ return NULL;
+}
+
void PaintRedirector::resize(DecorationPixmap border, const QSize &size)
{
Q_UNUSED(border)
@@ -469,6 +478,38 @@ void RasterXRenderPaintRedirector::paint(PaintRedirector::DecorationPixmap borde
img.width(), img.height(), offset.x(), offset.y(), 0, 32, img.byteCount(), img.constBits());
}
+QImagePaintRedirector::QImagePaintRedirector(Client *c, KDecoration *deco)
+ : ImageBasedPaintRedirector(c, deco)
+{
+ resizePixmaps();
+}
+
+QImagePaintRedirector::~QImagePaintRedirector()
+{
+}
+
+void QImagePaintRedirector::paint(PaintRedirector::DecorationPixmap border, const QRect &r, const QRect &b, const QRegion ®)
+{
+ // clip the sub area
+ const QRect bounding = reg.boundingRect();
+ const QPoint offset = bounding.topLeft() - r.topLeft();
+
+ QPainter painter(&m_images[border]);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.drawImage(offset, scratchImage(), QRect(bounding.topLeft() - b.topLeft(), bounding.size()));
+}
+
+void QImagePaintRedirector::resize(PaintRedirector::DecorationPixmap border, const QSize &size)
+{
+ m_images[border] = QImage(size, QImage::Format_ARGB32_Premultiplied);
+ m_images[border].fill(Qt::transparent);
+}
+
+const QImage *QImagePaintRedirector::image(PaintRedirector::DecorationPixmap border) const
+{
+ return &m_images[border];
+}
+
} // namespace
#include "paintredirector.moc"
diff --git a/paintredirector.h b/paintredirector.h
index 8b19c5b8ee..f11fb67d84 100644
--- a/paintredirector.h
+++ b/paintredirector.h
@@ -96,6 +96,7 @@ protected:
PaintRedirector(Client *c, KDecoration *deco);
virtual xcb_render_picture_t picture(DecorationPixmap border) const;
virtual GLTexture *texture(DecorationPixmap border) const;
+ virtual const QImage *image(DecorationPixmap border) const;
virtual void resizePixmaps(const QRect *rects);
virtual void resize(DecorationPixmap border, const QSize &size);
virtual void preparePaint(const QPixmap &pending);
@@ -182,6 +183,21 @@ private:
QImage m_tempImage;
};
+class QImagePaintRedirector : public ImageBasedPaintRedirector
+{
+ Q_OBJECT
+public:
+ QImagePaintRedirector(Client *c, KDecoration *deco);
+ virtual ~QImagePaintRedirector();
+
+protected:
+ virtual void resize(DecorationPixmap border, const QSize &size) override;
+ virtual void paint(DecorationPixmap border, const QRect &r, const QRect &b, const QRegion ®) override;
+ virtual const QImage* image(DecorationPixmap border) const override;
+private:
+ QImage m_images[PixmapCount];
+};
+
template <>
inline
GLTexture *PaintRedirector::bottomDecoPixmap() const
@@ -238,6 +254,34 @@ xcb_render_picture_t PaintRedirector::topDecoPixmap() const
return picture(TopPixmap);
}
+template <>
+inline
+const QImage *PaintRedirector::bottomDecoPixmap() const
+{
+ return image(BottomPixmap);
+}
+
+template <>
+inline
+const QImage *PaintRedirector::leftDecoPixmap() const
+{
+ return image(LeftPixmap);
+}
+
+template <>
+inline
+const QImage *PaintRedirector::rightDecoPixmap() const
+{
+ return image(RightPixmap);
+}
+
+template <>
+inline
+const QImage *PaintRedirector::topDecoPixmap() const
+{
+ return image(TopPixmap);
+}
+
inline
const QImage &ImageBasedPaintRedirector::scratchImage() const
{
diff --git a/scene_qpainter.cpp b/scene_qpainter.cpp
new file mode 100644
index 0000000000..35dae41748
--- /dev/null
+++ b/scene_qpainter.cpp
@@ -0,0 +1,615 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 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 "scene_qpainter.h"
+// KWin
+#include "client.h"
+#include "composite.h"
+#include "deleted.h"
+#include "effects.h"
+#include "paintredirector.h"
+#include "toplevel.h"
+#ifdef WAYLAND_FOUND
+#include "wayland_backend.h"
+#endif
+#include "workspace.h"
+#include "xcbutils.h"
+// Qt
+#include
+#include
+
+namespace KWin
+{
+
+//****************************************
+// QPainterBackend
+//****************************************
+QPainterBackend::QPainterBackend()
+ : m_failed(false)
+{
+}
+
+QPainterBackend::~QPainterBackend()
+{
+}
+
+bool QPainterBackend::isLastFrameRendered() const
+{
+ return true;
+}
+
+OverlayWindow* QPainterBackend::overlayWindow()
+{
+ return NULL;
+}
+
+void QPainterBackend::showOverlay()
+{
+}
+
+void QPainterBackend::screenGeometryChanged(const QSize &size)
+{
+ Q_UNUSED(size)
+}
+
+void QPainterBackend::setFailed(const QString &reason)
+{
+ qWarning() << "Creating the XRender backend failed: " << reason;
+ m_failed = true;
+}
+
+#ifdef WAYLAND_FOUND
+//****************************************
+// WaylandQPainterBackend
+//****************************************
+static void handleFrameCallback(void *data, wl_callback *callback, uint32_t time)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(time)
+ reinterpret_cast(data)->lastFrameRendered();
+
+ if (callback) {
+ wl_callback_destroy(callback);
+ }
+}
+
+static const struct wl_callback_listener s_surfaceFrameListener = {
+ handleFrameCallback
+};
+
+WaylandQPainterBackend::WaylandQPainterBackend()
+ : QPainterBackend()
+ , m_lastFrameRendered(true)
+ , m_needsFullRepaint(true)
+ , m_backBuffer(QImage(QSize(displayWidth(), displayHeight()), QImage::Format_ARGB32_Premultiplied))
+ , m_buffer(NULL)
+{
+ connect(Wayland::WaylandBackend::self()->shmPool(), SIGNAL(poolResized()), SLOT(remapBuffer()));
+}
+
+WaylandQPainterBackend::~WaylandQPainterBackend()
+{
+ if (m_buffer) {
+ m_buffer->setUsed(false);
+ }
+}
+
+bool WaylandQPainterBackend::isLastFrameRendered() const
+{
+ return m_lastFrameRendered;
+}
+
+bool WaylandQPainterBackend::usesOverlayWindow() const
+{
+ return false;
+}
+
+void WaylandQPainterBackend::present(int mask, const QRegion &damage)
+{
+ Q_UNUSED(mask)
+ Wayland::WaylandBackend *wl = Wayland::WaylandBackend::self();
+ if (m_backBuffer.isNull()) {
+ return;
+ }
+ m_lastFrameRendered = false;
+ m_needsFullRepaint = false;
+ wl_surface *surface = wl->surface();
+ wl_callback *callback = wl_surface_frame(surface);
+ wl_callback_add_listener(callback, &s_surfaceFrameListener, this);
+ wl_surface_attach(surface, m_buffer->buffer(), 0, 0);
+ Q_FOREACH (const QRect &rect, damage.rects()) {
+ wl_surface_damage(surface, rect.x(), rect.y(), rect.width(), rect.height());
+ }
+ wl_surface_commit(surface);
+ wl->dispatchEvents();
+}
+
+void WaylandQPainterBackend::lastFrameRendered()
+{
+ m_lastFrameRendered = true;
+ Compositor::self()->lastFrameRendered();
+}
+
+void WaylandQPainterBackend::screenGeometryChanged(const QSize &size)
+{
+ Q_UNUSED(size)
+ m_buffer->setUsed(false);
+ m_buffer = NULL;
+}
+
+QImage *WaylandQPainterBackend::buffer()
+{
+ return &m_backBuffer;
+}
+
+void WaylandQPainterBackend::prepareRenderingFrame()
+{
+ if (m_buffer) {
+ if (m_buffer->isReleased()) {
+ // we can re-use this buffer
+ m_buffer->setReleased(false);
+ return;
+ } else {
+ // buffer is still in use, get a new one
+ m_buffer->setUsed(false);
+ }
+ }
+ m_buffer = NULL;
+ const QSize size(displayWidth(), displayHeight());
+ m_buffer = Wayland::WaylandBackend::self()->shmPool()->getBuffer(size, size.width() * 4);
+ if (!m_buffer) {
+ qDebug() << "Did not get a new Buffer from Shm Pool";
+ m_backBuffer = QImage();
+ return;
+ }
+ m_buffer->setUsed(true);
+ m_backBuffer = QImage(m_buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
+ m_backBuffer.fill(Qt::transparent);
+ m_needsFullRepaint = true;
+ qDebug() << "Created a new back buffer";
+}
+
+void WaylandQPainterBackend::remapBuffer()
+{
+ if (!m_buffer || !m_buffer->isUsed()) {
+ return;
+ }
+ const QSize size = m_backBuffer.size();
+ m_backBuffer = QImage(m_buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
+ qDebug() << "Remapped our back buffer";
+}
+
+bool WaylandQPainterBackend::needsFullRepaint() const
+{
+ return m_needsFullRepaint;
+}
+
+#endif
+
+//****************************************
+// SceneQPainter
+//****************************************
+SceneQPainter *SceneQPainter::createScene()
+{
+ QScopedPointer backend;
+#ifdef WAYLAND_FOUND
+ if (Wayland::WaylandBackend::self()) {
+ backend.reset(new WaylandQPainterBackend);
+ if (backend->isFailed()) {
+ return NULL;
+ }
+ return new SceneQPainter(backend.take());
+ }
+#endif
+ return NULL;
+}
+
+SceneQPainter::SceneQPainter(QPainterBackend* backend)
+ : Scene(Workspace::self())
+ , m_backend(backend)
+ , m_painter(new QPainter())
+{
+}
+
+SceneQPainter::~SceneQPainter()
+{
+}
+
+CompositingType SceneQPainter::compositingType() const
+{
+ return QPainterCompositing;
+}
+
+bool SceneQPainter::initFailed() const
+{
+ return false;
+}
+
+void SceneQPainter::paintGenericScreen(int mask, ScreenPaintData data)
+{
+ m_painter->save();
+ m_painter->translate(data.xTranslation(), data.yTranslation());
+ m_painter->scale(data.xScale(), data.yScale());
+ Scene::paintGenericScreen(mask, data);
+ m_painter->restore();
+}
+
+qint64 SceneQPainter::paint(QRegion damage, ToplevelList toplevels)
+{
+ QElapsedTimer renderTimer;
+ renderTimer.start();
+
+ createStackingOrder(toplevels);
+
+ int mask = 0;
+ m_backend->prepareRenderingFrame();
+ m_painter->begin(m_backend->buffer());
+ if (m_backend->needsFullRepaint()) {
+ mask |= Scene::PAINT_SCREEN_BACKGROUND_FIRST;
+ damage = QRegion(0, 0, displayWidth(), displayHeight());
+ }
+ QRegion updateRegion, validRegion;
+ paintScreen(&mask, damage, QRegion(), &updateRegion, &validRegion);
+
+ m_backend->showOverlay();
+
+ m_painter->end();
+ m_backend->present(mask, updateRegion);
+ // do cleanup
+ clearStackingOrder();
+
+ return renderTimer.nsecsElapsed();
+}
+
+void SceneQPainter::paintBackground(QRegion region)
+{
+ m_painter->setBrush(Qt::black);
+ m_painter->drawRects(region.rects());
+}
+
+Scene::Window *SceneQPainter::createWindow(Toplevel *toplevel)
+{
+ return new SceneQPainter::Window(this, toplevel);
+}
+
+Scene::EffectFrame *SceneQPainter::createEffectFrame(EffectFrameImpl *frame)
+{
+ return new QPainterEffectFrame(frame, this);
+}
+
+Shadow *SceneQPainter::createShadow(Toplevel *toplevel)
+{
+ return new SceneQPainterShadow(toplevel);
+}
+
+//****************************************
+// SceneQPainter::Window
+//****************************************
+SceneQPainter::Window::Window(SceneQPainter *scene, Toplevel *c)
+ : Scene::Window(c)
+ , m_scene(scene)
+{
+}
+
+SceneQPainter::Window::~Window()
+{
+ discardShape();
+}
+
+void SceneQPainter::Window::performPaint(int mask, QRegion region, WindowPaintData data)
+{
+ if (!(mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED)))
+ region &= toplevel->visibleRect();
+
+ if (region.isEmpty())
+ return;
+ QPainterWindowPixmap *pixmap = windowPixmap();
+ if (!pixmap || !pixmap->isValid()) {
+ return;
+ }
+ if (!toplevel->damage().isEmpty()) {
+ pixmap->update(toplevel->damage());
+ toplevel->resetDamage();
+ }
+
+ QPainter *scenePainter = m_scene->painter();
+ QPainter *painter = scenePainter;
+ painter->setClipRegion(region);
+ painter->setClipping(true);
+
+ painter->save();
+ painter->translate(x(), y());
+ if (mask & PAINT_WINDOW_TRANSFORMED) {
+ painter->translate(data.xTranslation(), data.yTranslation());
+ painter->scale(data.xScale(), data.yScale());
+ }
+
+ const bool opaque = qFuzzyCompare(1.0, data.opacity());
+ QImage tempImage;
+ QPainter tempPainter;
+ if (!opaque) {
+ // need a temp render target which we later on blit to the screen
+ tempImage = QImage(toplevel->visibleRect().size(), QImage::Format_ARGB32_Premultiplied);
+ tempImage.fill(Qt::transparent);
+ tempPainter.begin(&tempImage);
+ tempPainter.save();
+ tempPainter.translate(toplevel->geometry().topLeft() - toplevel->visibleRect().topLeft());
+ painter = &tempPainter;
+ }
+ renderShadow(painter);
+ renderWindowDecorations(painter);
+
+ // render content
+ const QRect src = QRect(toplevel->clientPos(), toplevel->clientSize());
+ painter->drawImage(toplevel->clientPos(), pixmap->image(), src);
+
+ if (!opaque) {
+ tempPainter.restore();
+ tempPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ QColor translucent(Qt::transparent);
+ translucent.setAlphaF(data.opacity());
+ tempPainter.fillRect(QRect(QPoint(0, 0), toplevel->visibleRect().size()), translucent);
+ tempPainter.end();
+ painter = scenePainter;
+ painter->drawImage(toplevel->visibleRect().topLeft() - toplevel->geometry().topLeft(), tempImage);
+ }
+
+ painter->restore();
+
+ painter->setClipRegion(QRegion());
+ painter->setClipping(false);
+}
+
+void SceneQPainter::Window::renderShadow(QPainter* painter)
+{
+ if (!toplevel->shadow()) {
+ return;
+ }
+ SceneQPainterShadow *shadow = static_cast(toplevel->shadow());
+ const QPixmap &topLeft = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementTopLeft);
+ const QPixmap &top = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementTop);
+ const QPixmap &topRight = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementTopRight);
+ const QPixmap &bottomLeft = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementBottomLeft);
+ const QPixmap &bottom = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementBottom);
+ const QPixmap &bottomRight = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementBottomRight);
+ const QPixmap &left = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementLeft);
+ const QPixmap &right = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementRight);
+
+ const int leftOffset = shadow->leftOffset();
+ const int topOffset = shadow->topOffset();
+ const int rightOffset = shadow->rightOffset();
+ const int bottomOffset = shadow->bottomOffset();
+
+ // top left
+ painter->drawPixmap(-leftOffset, -topOffset, topLeft);
+ // top right
+ painter->drawPixmap(toplevel->width() - topRight.width() + rightOffset, -topOffset, topRight);
+ // bottom left
+ painter->drawPixmap(-leftOffset, toplevel->height() - bottomLeft.height() + bottomOffset, bottomLeft);
+ // bottom right
+ painter->drawPixmap(toplevel->width() - bottomRight.width() + rightOffset,
+ toplevel->height() - bottomRight.height() + bottomOffset,
+ bottomRight);
+ // top
+ painter->drawPixmap(topLeft.width() - leftOffset, -topOffset,
+ toplevel->width() - topLeft.width() - topRight.width() + leftOffset + rightOffset,
+ top.height(),
+ top);
+ // left
+ painter->drawPixmap(-leftOffset, topLeft.height() - topOffset, left.width(),
+ toplevel->height() - topLeft.height() - bottomLeft.height() + topOffset + bottomOffset,
+ left);
+ // right
+ painter->drawPixmap(toplevel->width() - right.width() + rightOffset,
+ topRight.height() - topOffset,
+ right.width(),
+ toplevel->height() - topRight.height() - bottomRight.height() + topOffset + bottomOffset,
+ right);
+ // bottom
+ painter->drawPixmap(bottomLeft.width() - leftOffset,
+ toplevel->height() - bottom.height() + bottomOffset,
+ toplevel->width() - bottomLeft.width() - bottomRight.width() + leftOffset + rightOffset,
+ bottom.height(),
+ bottom);
+}
+
+void SceneQPainter::Window::renderWindowDecorations(QPainter *painter)
+{
+ // TODO: custom decoration opacity
+ Client *client = dynamic_cast(toplevel);
+ Deleted *deleted = dynamic_cast(toplevel);
+ if (!client && !deleted) {
+ return;
+ }
+
+ bool noBorder = true;
+ PaintRedirector *redirector = NULL;
+ QRect dtr, dlr, drr, dbr;
+ if (client && !client->noBorder()) {
+ redirector = client->decorationPaintRedirector();
+ client->layoutDecorationRects(dlr, dtr, drr, dbr, Client::WindowRelative);
+ noBorder = false;
+ } else if (deleted && !deleted->noBorder()) {
+ noBorder = false;
+ redirector = deleted->decorationPaintRedirector();
+ deleted->layoutDecorationRects(dlr, dtr, drr, dbr);
+ }
+ if (noBorder || !redirector) {
+ return;
+ }
+
+ redirector->ensurePixmapsPainted();
+ const QImage *left = redirector->leftDecoPixmap();
+ const QImage *top = redirector->topDecoPixmap();
+ const QImage *right = redirector->rightDecoPixmap();
+ const QImage *bottom = redirector->bottomDecoPixmap();
+
+ painter->drawImage(dtr, *top);
+ painter->drawImage(dlr, *left);
+ painter->drawImage(drr, *right);
+ painter->drawImage(dbr, *bottom);
+
+ redirector->markAsRepainted();
+}
+
+WindowPixmap *SceneQPainter::Window::createWindowPixmap()
+{
+ return new QPainterWindowPixmap(this);
+}
+
+//****************************************
+// QPainterWindowPixmap
+//****************************************
+QPainterWindowPixmap::QPainterWindowPixmap(Scene::Window *window)
+ : WindowPixmap(window)
+ , m_shm(new Xcb::Shm)
+{
+}
+
+QPainterWindowPixmap::~QPainterWindowPixmap()
+{
+}
+
+void QPainterWindowPixmap::create()
+{
+ if (isValid() || !m_shm->isValid()) {
+ return;
+ }
+ KWin::WindowPixmap::create();
+ if (!isValid()) {
+ return;
+ }
+ m_image = QImage((uchar*)m_shm->buffer(), size().width(), size().height(), QImage::Format_ARGB32_Premultiplied);
+}
+
+bool QPainterWindowPixmap::update(const QRegion &damage)
+{
+ Q_UNUSED(damage)
+ if (!m_shm->isValid()) {
+ return false;
+ }
+
+ // TODO: optimize by only updating the damaged areas
+ xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), pixmap(),
+ 0, 0, size().width(), size().height(),
+ ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, m_shm->segment(), 0);
+
+ ScopedCPointer image(xcb_shm_get_image_reply(connection(), cookie, NULL));
+ if (image.isNull()) {
+ return false;
+ }
+ return true;
+}
+
+QPainterEffectFrame::QPainterEffectFrame(EffectFrameImpl *frame, SceneQPainter *scene)
+ : Scene::EffectFrame(frame)
+ , m_scene(scene)
+{
+}
+
+QPainterEffectFrame::~QPainterEffectFrame()
+{
+}
+
+void QPainterEffectFrame::render(QRegion region, double opacity, double frameOpacity)
+{
+ Q_UNUSED(region)
+ Q_UNUSED(opacity)
+ // TODO: adjust opacity
+ if (m_effectFrame->geometry().isEmpty()) {
+ return; // Nothing to display
+ }
+ QPainter *painter = m_scene->painter();
+
+
+ // Render the actual frame
+ if (m_effectFrame->style() == EffectFrameUnstyled) {
+ painter->save();
+ painter->setPen(Qt::NoPen);
+ QColor color(Qt::black);
+ color.setAlphaF(frameOpacity);
+ painter->setBrush(color);
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->drawRoundedRect(m_effectFrame->geometry().adjusted(-5, -5, 5, 5), 5.0, 5.0);
+ painter->restore();
+ } else if (m_effectFrame->style() == EffectFrameStyled) {
+ qreal left, top, right, bottom;
+ m_effectFrame->frame().getMargins(left, top, right, bottom); // m_geometry is the inner geometry
+ QRect geom = m_effectFrame->geometry().adjusted(-left, -top, right, bottom);
+ painter->drawPixmap(geom, m_effectFrame->frame().framePixmap());
+ }
+ if (!m_effectFrame->selection().isNull()) {
+ painter->drawPixmap(m_effectFrame->selection(), m_effectFrame->selectionFrame().framePixmap());
+ }
+
+ // Render icon
+ if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
+ const QPoint topLeft(m_effectFrame->geometry().x(),
+ m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2);
+
+ const QRect geom = QRect(topLeft, m_effectFrame->iconSize());
+ painter->drawPixmap(geom, m_effectFrame->icon().pixmap(m_effectFrame->iconSize()));
+ }
+
+ // Render text
+ if (!m_effectFrame->text().isEmpty()) {
+ // Determine position on texture to paint text
+ QRect rect(QPoint(0, 0), m_effectFrame->geometry().size());
+ if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
+ rect.setLeft(m_effectFrame->iconSize().width());
+ }
+
+ // If static size elide text as required
+ QString text = m_effectFrame->text();
+ if (m_effectFrame->isStatic()) {
+ QFontMetrics metrics(m_effectFrame->text());
+ text = metrics.elidedText(text, Qt::ElideRight, rect.width());
+ }
+
+ painter->save();
+ painter->setFont(m_effectFrame->font());
+ if (m_effectFrame->style() == EffectFrameStyled) {
+ painter->setPen(m_effectFrame->styledTextColor());
+ } else {
+ // TODO: What about no frame? Custom color setting required
+ painter->setPen(Qt::white);
+ }
+ painter->drawText(rect.translated(m_effectFrame->geometry().topLeft()), m_effectFrame->alignment(), text);
+ painter->restore();
+ }
+}
+
+//****************************************
+// QPainterShadow
+//****************************************
+SceneQPainterShadow::SceneQPainterShadow(Toplevel* toplevel)
+ : Shadow(toplevel)
+{
+}
+
+SceneQPainterShadow::~SceneQPainterShadow()
+{
+}
+
+bool SceneQPainterShadow::prepareBackend()
+{
+ return true;
+}
+
+} // KWin
diff --git a/scene_qpainter.h b/scene_qpainter.h
new file mode 100644
index 0000000000..f062f85303
--- /dev/null
+++ b/scene_qpainter.h
@@ -0,0 +1,263 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 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_SCENE_QPAINTER_H
+#define KWIN_SCENE_QPAINTER_H
+
+#include "scene.h"
+#include "shadow.h"
+#include
+
+namespace KWin {
+
+namespace Xcb {
+ class Shm;
+}
+
+class QPainterBackend
+{
+public:
+ virtual ~QPainterBackend();
+ virtual void present(int mask, const QRegion &damage) = 0;
+
+ /**
+ * @brief Returns the OverlayWindow used by the backend.
+ *
+ * A backend does not have to use an OverlayWindow, this is mostly for the X world.
+ * In case the backend does not use an OverlayWindow it is allowed to return @c null.
+ * It's the task of the caller to check whether it is @c null.
+ *
+ * @return :OverlayWindow*
+ **/
+ virtual OverlayWindow *overlayWindow();
+ virtual bool usesOverlayWindow() const = 0;
+ virtual void prepareRenderingFrame() = 0;
+ /**
+ * @brief Shows the Overlay Window
+ *
+ * Default implementation does nothing.
+ */
+ virtual void showOverlay();
+ /**
+ * @brief React on screen geometry changes.
+ *
+ * Default implementation does nothing. Override if specific functionality is required.
+ *
+ * @param size The new screen size
+ */
+ virtual void screenGeometryChanged(const QSize &size);
+ /**
+ * @brief Backend specific code to determine whether the last frame got rendered.
+ *
+ * Default implementation always returns @c true. That is it's always assumed that the last
+ * frame got rendered. If a backend needs more control it needs to implement this method.
+ */
+ virtual bool isLastFrameRendered() const;
+ /**
+ * @brief Whether the creation of the Backend failed.
+ *
+ * The SceneQPainter should test whether the Backend got constructed correctly. If this method
+ * returns @c true, the SceneQPainter should not try to start the rendering.
+ *
+ * @return bool @c true if the creation of the Backend failed, @c false otherwise.
+ **/
+ bool isFailed() const {
+ return m_failed;
+ }
+
+ virtual QImage *buffer() = 0;
+ virtual bool needsFullRepaint() const = 0;
+
+protected:
+ QPainterBackend();
+ /**
+ * @brief Sets the backend initialization to failed.
+ *
+ * This method should be called by the concrete subclass in case the initialization failed.
+ * The given @p reason is logged as a warning.
+ *
+ * @param reason The reason why the initialization failed.
+ **/
+ void setFailed(const QString &reason);
+
+private:
+ bool m_failed;
+};
+
+#ifdef WAYLAND_FOUND
+namespace Wayland {
+class Buffer;
+}
+class WaylandQPainterBackend : public QObject, public QPainterBackend
+{
+ Q_OBJECT
+public:
+ WaylandQPainterBackend();
+ virtual ~WaylandQPainterBackend();
+
+ virtual void present(int mask, const QRegion& damage) override;
+ virtual bool usesOverlayWindow() const override;
+ virtual bool isLastFrameRendered() const override;
+ virtual void screenGeometryChanged(const QSize &size) override;
+ virtual QImage *buffer() override;
+ virtual void prepareRenderingFrame() override;
+ virtual bool needsFullRepaint() const override;
+ void lastFrameRendered();
+private Q_SLOTS:
+ void remapBuffer();
+private:
+ bool m_lastFrameRendered;
+ bool m_needsFullRepaint;
+ QImage m_backBuffer;
+ Wayland::Buffer *m_buffer;
+};
+#endif
+
+class SceneQPainter : public Scene
+{
+ Q_OBJECT
+
+public:
+ virtual ~SceneQPainter();
+ virtual bool isLastFrameRendered() const override;
+ virtual bool usesOverlayWindow() const override;
+ virtual OverlayWindow* overlayWindow() override;
+ virtual qint64 paint(QRegion damage, ToplevelList windows) override;
+ virtual void paintGenericScreen(int mask, ScreenPaintData data) override;
+ virtual CompositingType compositingType() const override;
+ virtual bool initFailed() const override;
+ virtual EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;
+ virtual Shadow *createShadow(Toplevel *toplevel) override;
+
+ QPainter *painter();
+
+ static SceneQPainter *createScene();
+
+protected:
+ virtual void paintBackground(QRegion region) override;
+ virtual Scene::Window *createWindow(Toplevel *toplevel) override;
+
+private:
+ explicit SceneQPainter(QPainterBackend *backend);
+ QScopedPointer m_backend;
+ QScopedPointer m_painter;
+ class Window;
+};
+
+class SceneQPainter::Window : public Scene::Window
+{
+public:
+ Window(SceneQPainter *scene, Toplevel *c);
+ virtual ~Window();
+ virtual void performPaint(int mask, QRegion region, WindowPaintData data) override;
+protected:
+ virtual WindowPixmap *createWindowPixmap() override;
+private:
+ void renderShadow(QPainter *painter);
+ void renderWindowDecorations(QPainter *painter);
+ SceneQPainter *m_scene;
+};
+
+class QPainterWindowPixmap : public WindowPixmap
+{
+public:
+ explicit QPainterWindowPixmap(Scene::Window *window);
+ virtual ~QPainterWindowPixmap();
+ virtual void create() override;
+
+ bool update(const QRegion &damage);
+ const QImage &image();
+private:
+ QScopedPointer m_shm;
+ QImage m_image;
+};
+
+class QPainterEffectFrame : public Scene::EffectFrame
+{
+public:
+ QPainterEffectFrame(EffectFrameImpl *frame, SceneQPainter *scene);
+ virtual ~QPainterEffectFrame();
+ virtual void crossFadeIcon() override {}
+ virtual void crossFadeText() override {}
+ virtual void free() override {}
+ virtual void freeIconFrame() override {}
+ virtual void freeTextFrame() override {}
+ virtual void freeSelection() override {}
+ virtual void render(QRegion region, double opacity, double frameOpacity) override;
+private:
+ SceneQPainter *m_scene;
+};
+
+class SceneQPainterShadow : public Shadow
+{
+public:
+ SceneQPainterShadow(Toplevel* toplevel);
+ virtual ~SceneQPainterShadow();
+ using Shadow::ShadowElements;
+ using Shadow::ShadowElementTop;
+ using Shadow::ShadowElementTopRight;
+ using Shadow::ShadowElementRight;
+ using Shadow::ShadowElementBottomRight;
+ using Shadow::ShadowElementBottom;
+ using Shadow::ShadowElementBottomLeft;
+ using Shadow::ShadowElementLeft;
+ using Shadow::ShadowElementTopLeft;
+ using Shadow::ShadowElementsCount;
+ using Shadow::shadowPixmap;
+ using Shadow::topOffset;
+ using Shadow::leftOffset;
+ using Shadow::rightOffset;
+ using Shadow::bottomOffset;
+protected:
+ virtual bool prepareBackend() override;
+};
+
+inline
+bool SceneQPainter::usesOverlayWindow() const
+{
+ return m_backend->usesOverlayWindow();
+}
+
+inline
+OverlayWindow* SceneQPainter::overlayWindow()
+{
+ return m_backend->overlayWindow();
+}
+
+inline
+bool SceneQPainter::isLastFrameRendered() const
+{
+ return m_backend->isLastFrameRendered();
+}
+
+inline
+QPainter* SceneQPainter::painter()
+{
+ return m_painter.data();
+}
+
+inline
+const QImage &QPainterWindowPixmap::image()
+{
+ return m_image;
+}
+
+} // KWin
+
+#endif // KWIN_SCENEQPAINTER_H
diff --git a/workspace.cpp b/workspace.cpp
index 2dc7ac9f53..e4cd0c00b9 100644
--- a/workspace.cpp
+++ b/workspace.cpp
@@ -1560,6 +1560,9 @@ QString Workspace::supportInformation() const
case XRenderCompositing:
support.append(QStringLiteral("Compositing Type: XRender\n"));
break;
+ case QPainterCompositing:
+ support.append("Compositing Type: QPainter\n");
+ break;
case NoCompositing:
default:
support.append(QStringLiteral("Something is really broken, neither OpenGL nor XRender is used"));