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"));