From 12220a0d59d9beccbfd66647bc33d46636b74058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 27 Mar 2011 12:33:07 +0200 Subject: [PATCH 01/14] Initial implementation of new Shadows in KWin For a complete documentation of new functionality refer to: http://community.kde.org/KWin/Shadow The current implementation includes a new Shadow class and Toplevel holds a pointer to an instance of this class. The Shadow class reads the data from the X11 Property. There is one extended class located in SceneOpenGL to render the shadow. Compositor is adjusted to include the shadow region into the painting passes. Implementation for XRender still missing and Shadow needs to respond to size changes of the Toplevel to update cached shadow region and WindowQuads. --- CMakeLists.txt | 1 + atoms.cpp | 3 + atoms.h | 1 + composite.cpp | 18 +++ events.cpp | 2 + libkwineffects/kwineffects.h | 9 ++ scene.cpp | 4 + scene_opengl.cpp | 226 +++++++++++++++++++++++++++-------- scene_opengl.h | 37 +++++- shadow.cpp | 173 +++++++++++++++++++++++++++ shadow.h | 136 +++++++++++++++++++++ toplevel.cpp | 28 +++++ toplevel.h | 22 ++++ workspace.cpp | 1 + 14 files changed, 606 insertions(+), 55 deletions(-) create mode 100644 shadow.cpp create mode 100644 shadow.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 976c831587..2d6c7c4263 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ set(kwin_KDEINIT_SRCS events.cpp killwindow.cpp geometrytip.cpp + shadow.cpp sm.cpp group.cpp bridge.cpp diff --git a/atoms.cpp b/atoms.cpp index 5e93b2ad91..f674b2aa1a 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -114,6 +114,9 @@ Atoms::Atoms() atoms[n] = &net_wm_sync_request; names[n++] = (char*) "_NET_WM_SYNC_REQUEST"; + atoms[n] = &kde_net_wm_shadow; + names[n++] = (char*) "_KDE_NET_WM_SHADOW"; + assert(n <= max); XInternAtoms(display(), names, n, false, atoms_return); diff --git a/atoms.h b/atoms.h index 7d5752c2ae..aed9d83e4a 100644 --- a/atoms.h +++ b/atoms.h @@ -60,6 +60,7 @@ public: Atom kde_net_wm_frame_strut; Atom net_wm_sync_request_counter; Atom net_wm_sync_request; + Atom kde_net_wm_shadow; }; diff --git a/composite.cpp b/composite.cpp index 6783d3240b..44a3865be4 100644 --- a/composite.cpp +++ b/composite.cpp @@ -51,6 +51,7 @@ along with this program. If not, see . #include "scene_basic.h" #include "scene_xrender.h" #include "scene_opengl.h" +#include "shadow.h" #include "compositingprefs.h" #include "notifications.h" @@ -383,6 +384,9 @@ void Workspace::performCompositing() repaints_region |= c->repaints().translated(c->pos()); repaints_region |= c->decorationPendingRegion(); c->resetRepaints(c->decorationRect()); + if (c->hasShadow()) { + c->resetRepaints(c->shadow()->shadowRegion().boundingRect()); + } } QRegion repaints = repaints_region; // clear all repaints, so that post-pass can add repaints for the next repaint @@ -665,6 +669,7 @@ void Toplevel::setupCompositing() damage_region = QRegion(0, 0, width(), height()); effect_window = new EffectWindowImpl(); effect_window->setWindow(this); + getShadow(); unredirect = false; workspace()->checkUnredirect(true); #endif @@ -685,6 +690,10 @@ void Toplevel::finishCompositing() damage_region = QRegion(); repaints_region = QRegion(); effect_window = NULL; + if (hasShadow()) { + delete m_shadow; + m_shadow = NULL; + } #endif } @@ -857,6 +866,9 @@ void Toplevel::addRepaint(int x, int y, int w, int h) void Toplevel::addRepaintFull() { repaints_region = rect(); + if (hasShadow()) { + repaints_region = repaints_region.united(shadow()->shadowRegion()); + } workspace()->checkCompositeTimer(); } @@ -947,6 +959,9 @@ bool Client::shouldUnredirect() const void Client::addRepaintFull() { repaints_region = decorationRect(); + if (hasShadow()) { + repaints_region = repaints_region.united(shadow()->shadowRegion()); + } workspace()->checkCompositeTimer(); } @@ -990,6 +1005,9 @@ bool Deleted::shouldUnredirect() const void Deleted::addRepaintFull() { repaints_region = decorationRect(); + if (hasShadow()) { + repaints_region = repaints_region.united(shadow()->shadowRegion()); + } workspace()->checkCompositeTimer(); } diff --git a/events.cpp b/events.cpp index 05cf864c65..f28e1bc02e 100644 --- a/events.cpp +++ b/events.cpp @@ -1666,6 +1666,8 @@ void Toplevel::propertyNotifyEvent(XPropertyEvent* e) getWmClientLeader(); else if (e->atom == atoms->wm_window_role) getWindowRole(); + else if (e->atom == atoms->kde_net_wm_shadow) + getShadow(); break; } emit propertyNotify(this, e->atom); diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 4944ab4152..0fa7f9e704 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -174,6 +174,15 @@ enum WindowQuadType { WindowQuadError, // for the stupid default ctor WindowQuadContents, WindowQuadDecoration, + // Shadow Quad types + WindowQuadShadowTop, + WindowQuadShadowTopRight, + WindowQuadShadowRight, + WindowQuadShadowBottomRight, + WindowQuadShadowBottom, + WindowQuadShadowBottomLeft, + WindowQuadShadowLeft, + WindowQuadShadowTopLeft, EFFECT_QUAD_TYPE_START = 100 ///< @internal }; diff --git a/scene.cpp b/scene.cpp index eadaad0500..c85d325b31 100644 --- a/scene.cpp +++ b/scene.cpp @@ -77,6 +77,7 @@ along with this program. If not, see . #include "deleted.h" #include "effects.h" #include "lanczosfilter.h" +#include "shadow.h" #include @@ -493,6 +494,9 @@ WindowQuadList Scene::Window::buildQuads(bool force) const ret += makeQuads(WindowQuadDecoration, right); } } + if (toplevel->hasShadow()) { + ret << toplevel->shadow()->shadowQuads(); + } effects->buildQuads(static_cast(toplevel)->effectWindow(), ret); cached_quad_list = new WindowQuadList(ret); return ret; diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 58ea7d7277..8b1e072085 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak +Copyright (C) 2009, 2010, 2011 Martin Gräßlin Based on glcompmgr code by Felix Bellaby. Using code from Compiz and Beryl. @@ -511,6 +512,17 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); + // shadow + if (toplevel->hasShadow()) { + paintShadow(WindowQuadShadowTop, region, data); + paintShadow(WindowQuadShadowTopRight, region, data); + paintShadow(WindowQuadShadowRight, region, data); + paintShadow(WindowQuadShadowBottomRight, region, data); + paintShadow(WindowQuadShadowBottom, region, data); + paintShadow(WindowQuadShadowBottomLeft, region, data); + paintShadow(WindowQuadShadowLeft, region, data); + paintShadow(WindowQuadShadowTopLeft, region, data); + } // decorations Client *client = dynamic_cast(toplevel); Deleted *deleted = dynamic_cast(toplevel); @@ -654,6 +666,25 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType #endif } +void SceneOpenGL::Window::paintShadow(WindowQuadType type, const QRegion ®ion, const WindowPaintData &data) +{ + WindowQuadList quads = data.quads.select(type); + Texture *texture = static_cast(toplevel->shadow())->textureForQuadType(type); + if (!texture) { + return; + } + if (filter == ImageFilterGood) + texture->setFilter(GL_LINEAR); + else + texture->setFilter(GL_NEAREST); + texture->setWrapMode(GL_CLAMP_TO_EDGE); + texture->bind(); + prepareStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); + renderQuads(0, region, quads); + restoreStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); + texture->unbind(); +} + void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, const QRect& rect) const { QVector vertices; @@ -710,8 +741,38 @@ void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double { if (shader) prepareShaderRenderStates(type, opacity, brightness, saturation, shader); - else - prepareRenderStates(type, opacity, brightness, saturation); + else { + Texture *tex = NULL; + switch(type) { + case Content: + tex = &texture; + break; + case DecorationTop: + tex = &topTexture; + break; + case DecorationLeft: + tex = &leftTexture; + break; + case DecorationRight: + tex = &rightTexture; + break; + case DecorationBottom: + tex = &bottomTexture; + break; + default: + return; + } + prepareStates(type, opacity, brightness, saturation, shader, tex); + } +} + +void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture) +{ + if (shader) { + prepareShaderRenderStates(type, opacity, brightness, saturation, shader); + } else { + prepareRenderStates(type, opacity, brightness, saturation, texture); + } } void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) @@ -750,45 +811,23 @@ void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opa shader->setUniform(GLShader::TextureHeight, texh >= 0.0 ? texh : toplevel->height()); } -void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation) +void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex) { #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(type) Q_UNUSED(opacity) Q_UNUSED(brightness) Q_UNUSED(saturation) + Q_UNUSED(tex) #else - Texture* tex; bool alpha = false; bool opaque = true; - switch(type) { - case Content: - tex = &texture; + if (type == Content) { alpha = toplevel->hasAlpha(); opaque = isOpaque() && opacity == 1.0; - break; - case DecorationTop: - tex = &topTexture; + } else { alpha = true; opaque = false; - break; - case DecorationLeft: - tex = &leftTexture; - alpha = true; - opaque = false; - break; - case DecorationRight: - tex = &rightTexture; - alpha = true; - opaque = false; - break; - case DecorationBottom: - tex = &bottomTexture; - alpha = true; - opaque = false; - break; - default: - return; } // setup blending of transparent windows glPushAttrib(GL_ENABLE_BIT); @@ -912,8 +951,38 @@ void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double { if (shader) restoreShaderRenderStates(type, opacity, brightness, saturation, shader); - else - restoreRenderStates(type, opacity, brightness, saturation); + else { + Texture *tex = NULL; + switch(type) { + case Content: + tex = &texture; + break; + case DecorationTop: + tex = &topTexture; + break; + case DecorationLeft: + tex = &leftTexture; + break; + case DecorationRight: + tex = &rightTexture; + break; + case DecorationBottom: + tex = &bottomTexture; + break; + default: + return; + } + restoreStates(type, opacity, brightness, saturation, shader, tex); + } +} + +void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture) +{ + if (shader) { + restoreShaderRenderStates(type, opacity, brightness, saturation, shader); + } else { + restoreRenderStates(type, opacity, brightness, saturation, texture); + } } void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) @@ -933,34 +1002,15 @@ void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opa #endif } -void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation) +void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex) { -#ifdef KWIN_HAVE_OPENGLES Q_UNUSED(type) +#ifdef KWIN_HAVE_OPENGLES Q_UNUSED(opacity) Q_UNUSED(brightness) Q_UNUSED(saturation) + Q_UNUSED(tex) #else - Texture* tex; - switch(type) { - case Content: - tex = &texture; - break; - case DecorationTop: - tex = &topTexture; - break; - case DecorationLeft: - tex = &leftTexture; - break; - case DecorationRight: - tex = &rightTexture; - break; - case DecorationBottom: - tex = &bottomTexture; - break; - default: - return; - } if (opacity != 1.0 || saturation != 1.0 || brightness != 1.0f) { if (saturation != 1.0 && tex->saturationSupported()) { glActiveTexture(GL_TEXTURE3); @@ -1449,6 +1499,78 @@ void SceneOpenGL::EffectFrame::cleanup() m_unstyledPixmap = NULL; } +//**************************************** +// SceneOpenGL::Shadow +//**************************************** +SceneOpenGLShadow::SceneOpenGLShadow(Toplevel *toplevel) + : Shadow(toplevel) +{ +} + +SceneOpenGLShadow::~SceneOpenGLShadow() +{ + for (int i=0; itexture() != None) { + glBindTexture(texture->target(), texture->texture()); + } else if (!pixmap.isNull()) { + const bool success = texture->load(pixmap.handle(), pixmap.size(), pixmap.depth()); + if (!success) { + kDebug(1212) << "Failed to bind shadow pixmap"; + return NULL; + } + } else { + return NULL; + } + } + return texture; +} + } // namespace #endif diff --git a/scene_opengl.h b/scene_opengl.h index 66862a8190..fa349f2f6c 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak +Copyright (C) 2009, 2010, 2011 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 @@ -22,6 +23,7 @@ along with this program. If not, see . #define KWIN_SCENE_OPENGL_H #include "scene.h" +#include "shadow.h" #include "kwinglutils.h" @@ -156,18 +158,22 @@ protected: DecorationTop, DecorationLeft, DecorationRight, - DecorationBottom + DecorationBottom, + Shadow }; QMatrix4x4 transformation(int mask, const WindowPaintData &data) const; void paintDecoration(const QPixmap* decoration, TextureType decorationType, const QRegion& region, const QRect& rect, const WindowPaintData& data, const WindowQuadList& quads, bool updateDeco); + void paintShadow(WindowQuadType type, const QRegion ®ion, const WindowPaintData &data); void makeDecorationArrays(const WindowQuadList& quads, const QRect& rect) const; void renderQuads(int mask, const QRegion& region, const WindowQuadList& quads); void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); - void prepareRenderStates(TextureType type, double opacity, double brightness, double saturation); + void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture); + void prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex); void prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); - void restoreRenderStates(TextureType type, double opacity, double brightness, double saturation); + void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture); + void restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex); void restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); private: @@ -215,6 +221,31 @@ private: static void updateUnstyledTexture(); // Update OpenGL unstyled frame texture }; +/** + * @short OpenGL implementation of Shadow. + * + * This class extends Shadow by the Elements required for OpenGL rendering. + * @author Martin Gräßlin + **/ +class SceneOpenGLShadow + : public Shadow +{ +public: + SceneOpenGLShadow(Toplevel *toplevel); + virtual ~SceneOpenGLShadow(); + + /** + * Returns the Texture for a specific ShadowQuad. The method takes care of performing + * the Texture from Pixmap operation. The calling method can use the returned Texture + * directly. + * In error case the method returns @c NULL. + * @return OpenGL Texture for the Shadow Quad. May be @c NULL. + **/ + SceneOpenGL::Texture *textureForQuadType(WindowQuadType type); +private: + SceneOpenGL::Texture m_shadowTextures[ShadowElementsCount]; +}; + } // namespace #endif diff --git a/shadow.cpp b/shadow.cpp new file mode 100644 index 0000000000..9d35d41a06 --- /dev/null +++ b/shadow.cpp @@ -0,0 +1,173 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 "shadow.h" +// kwin +#include "atoms.h" +#include "toplevel.h" +#ifdef KWIN_HAVE_OPENGL_COMPOSITING +#include "scene_opengl.h" +#endif + +namespace KWin +{ + +Shadow::Shadow(Toplevel *toplevel) + : QObject(toplevel) + , m_topLevel(toplevel) +{ +} + +Shadow::~Shadow() +{ +} + +Shadow *Shadow::createShadow(Toplevel *toplevel) +{ + if (!effects) { + return NULL; + } + QVector data = Shadow::readX11ShadowProperty(toplevel->window()); + if (!data.isEmpty()) { + Shadow *shadow = NULL; + if (effects->compositingType() == OpenGLCompositing) { +#ifdef KWIN_HAVE_OPENGL_COMPOSITING + shadow = new SceneOpenGLShadow(toplevel); +#endif + } + if (shadow) { + shadow->init(data); + } + return shadow; + } else { + return NULL; + } +} + +QVector< long > Shadow::readX11ShadowProperty(WId id) +{ + QVector ret; + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + status = XGetWindowProperty(display(), id, atoms->kde_net_wm_shadow, 0, 12, false, XA_CARDINAL, &type, &format, &nitems, &extra, &data); + if (status == Success && type == XA_CARDINAL && format == 32 && nitems == 12) { + long* shadow = reinterpret_cast< long* >(data); + ret.reserve(12); + for (int i=0; i<12; ++i) { + ret << shadow[i]; + } + XFree(data); + } + return ret; +} + +void Shadow::init(const QVector< long > &data) +{ + for (int i=0; iwidth(), m_topOffset); + const QRect rightRect(m_topLevel->width(), - m_topOffset, m_rightOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); + const QRect bottomRect(0, m_topLevel->height(), m_topLevel->width(), m_bottomOffset); + const QRect leftRect(- m_leftOffset, - m_topOffset, m_leftOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); + m_shadowRegion = QRegion(topRect).united(rightRect).united(bottomRect).united(leftRect); + buildQuads(); +} + +void Shadow::buildQuads() +{ + // prepare window quads + m_shadowQuads.clear(); + const QRect topRect(QPoint(0, 0), m_shadowElements[ShadowElementTop].size()); + const QRect topRightRect(QPoint(0, 0), m_shadowElements[ShadowElementTopRight].size()); + const QRect rightRect(QPoint(0, 0), m_shadowElements[ShadowElementRight].size()); + const QRect bottomRightRect(QPoint(0, 0), m_shadowElements[ShadowElementBottomRight].size()); + const QRect bottomRect(QPoint(0, 0), m_shadowElements[ShadowElementBottom].size()); + const QRect bottomLeftRect(QPoint(0, 0), m_shadowElements[ShadowElementBottomLeft].size()); + const QRect leftRect(QPoint(0, 0), m_shadowElements[ShadowElementLeft].size()); + const QRect topLeftRect(QPoint(0, 0), m_shadowElements[ShadowElementTopLeft].size()); + WindowQuad topLeftQuad(WindowQuadShadowTopLeft); + topLeftQuad[ 0 ] = WindowVertex(-m_leftOffset, -m_topOffset, 0.0, 0.0); + topLeftQuad[ 1 ] = WindowVertex(-m_leftOffset + topLeftRect.width(), -m_topOffset, 1.0, 0.0); + topLeftQuad[ 2 ] = WindowVertex(-m_leftOffset + topLeftRect.width(), -m_topOffset + topLeftRect.height(), 1.0, 1.0); + topLeftQuad[ 3 ] = WindowVertex(-m_leftOffset, -m_topOffset + topLeftRect.height(), 0.0, 1.0); + m_shadowQuads.append(topLeftQuad); + WindowQuad topQuad(WindowQuadShadowTop); + topQuad[ 0 ] = WindowVertex(-m_leftOffset + topLeftRect.width(), -m_topOffset, 0.0, 0.0); + topQuad[ 1 ] = WindowVertex(m_topLevel->width() + m_rightOffset - topRightRect.width(), -m_topOffset, 1.0, 0.0); + topQuad[ 2 ] = WindowVertex(m_topLevel->width() + m_rightOffset - topRightRect.width(), -m_topOffset + topRect.height(), 1.0, 1.0); + topQuad[ 3 ] = WindowVertex(-m_leftOffset + topLeftRect.width(), -m_topOffset + topRect.height(), 0.0, 1.0); + m_shadowQuads.append(topQuad); + WindowQuad topRightQuad(WindowQuadShadowTopRight); + topRightQuad[ 0 ] = WindowVertex(m_topLevel->width() + m_rightOffset - topRightRect.width(), -m_topOffset, 0.0, 0.0); + topRightQuad[ 1 ] = WindowVertex(m_topLevel->width() + m_rightOffset, -m_topOffset, 1.0, 0.0); + topRightQuad[ 2 ] = WindowVertex(m_topLevel->width() + m_rightOffset, -m_topOffset + topRightRect.height(), 1.0, 1.0); + topRightQuad[ 3 ] = WindowVertex(m_topLevel->width() + m_rightOffset - topRightRect.width(), -m_topOffset + topRightRect.height(), 0.0, 1.0); + m_shadowQuads.append(topRightQuad); + WindowQuad rightQuad(WindowQuadShadowRight); + rightQuad[ 0 ] = WindowVertex(m_topLevel->width() + m_rightOffset - rightRect.width(), -m_topOffset + topRightRect.height(), 0.0, 0.0); + rightQuad[ 1 ] = WindowVertex(m_topLevel->width() + m_rightOffset, -m_topOffset + topRightRect.height(), 1.0, 0.0); + rightQuad[ 2 ] = WindowVertex(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset - bottomRightRect.height(), 1.0, 1.0); + rightQuad[ 3 ] = WindowVertex(m_topLevel->width() + m_rightOffset - rightRect.width(), m_topLevel->height() + m_bottomOffset - bottomRightRect.height(), 0.0, 1.0); + m_shadowQuads.append(rightQuad); + WindowQuad bottomRightQuad(WindowQuadShadowBottomRight); + bottomRightQuad[ 0 ] = WindowVertex(m_topLevel->width() + m_rightOffset - bottomRightRect.width(), m_topLevel->height() + m_bottomOffset - bottomRightRect.height(), 0.0, 0.0); + bottomRightQuad[ 1 ] = WindowVertex(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset - bottomRightRect.height(), 1.0, 0.0); + bottomRightQuad[ 2 ] = WindowVertex(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset, 1.0, 1.0); + bottomRightQuad[ 3 ] = WindowVertex(m_topLevel->width() + m_rightOffset - bottomRightRect.width(), m_topLevel->height() + m_bottomOffset, 0.0, 1.0); + m_shadowQuads.append(bottomRightQuad); + WindowQuad bottomQuad(WindowQuadShadowBottom); + bottomQuad[ 0 ] = WindowVertex(-m_leftOffset + bottomLeftRect.width(), m_topLevel->height() + m_bottomOffset - bottomRect.height(), 0.0, 0.0); + bottomQuad[ 1 ] = WindowVertex(m_topLevel->width() + m_rightOffset - bottomRightRect.width(), m_topLevel->height() + m_bottomOffset - bottomRect.height(), 1.0, 0.0); + bottomQuad[ 2 ] = WindowVertex(m_topLevel->width() + m_rightOffset - bottomRightRect.width(), m_topLevel->height() + m_bottomOffset, 1.0, 1.0); + bottomQuad[ 3 ] = WindowVertex(-m_leftOffset + bottomLeftRect.width(), m_topLevel->height() + m_bottomOffset, 0.0, 1.0); + m_shadowQuads.append(bottomQuad); + WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft); + bottomLeftQuad[ 0 ] = WindowVertex(-m_leftOffset, m_topLevel->height() + m_bottomOffset - bottomLeftRect.height(), 0.0, 0.0); + bottomLeftQuad[ 1 ] = WindowVertex(-m_leftOffset + bottomLeftRect.width(), m_topLevel->height() + m_bottomOffset - bottomLeftRect.height(), 1.0, 0.0); + bottomLeftQuad[ 2 ] = WindowVertex(-m_leftOffset + bottomLeftRect.width(), m_topLevel->height() + m_bottomOffset, 1.0, 1.0); + bottomLeftQuad[ 3 ] = WindowVertex(-m_leftOffset, m_topLevel->height() + m_bottomOffset, 0.0, 1.0); + m_shadowQuads.append(bottomLeftQuad); + WindowQuad leftQuad(WindowQuadShadowLeft); + leftQuad[ 0 ] = WindowVertex(-m_leftOffset, -m_topOffset + topLeftRect.height(), 0.0, 0.0); + leftQuad[ 1 ] = WindowVertex(-m_leftOffset + leftRect.width(), -m_topOffset + topLeftRect.height(), 1.0, 0.0); + leftQuad[ 2 ] = WindowVertex(-m_leftOffset + leftRect.width(), m_topLevel->height() + m_bottomOffset - bottomLeftRect.height(), 1.0, 1.0); + leftQuad[ 3 ] = WindowVertex(-m_leftOffset, m_topLevel->height() + m_bottomOffset - bottomLeftRect.height(), 0.0, 1.0); + m_shadowQuads.append(leftQuad); +} + +bool Shadow::updateShadow() +{ + QVector data = Shadow::readX11ShadowProperty(m_topLevel->window()); + if (data.isEmpty()) { + return false; + } + init(data); + return true; +} + +} // namespace diff --git a/shadow.h b/shadow.h new file mode 100644 index 0000000000..2a5aeda4e9 --- /dev/null +++ b/shadow.h @@ -0,0 +1,136 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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_SHADOW_H +#define KWIN_SHADOW_H + +#include +#include +#include + +namespace KWin { + +class Toplevel; + +/** + * @short Class representing a Window's Shadow to be rendered by the Compositor. + * + * This class holds all information about the Shadow to be rendered together with the + * window during the Compositing stage. The Shadow consists of several pixmaps and offsets. + * For a complete description please refer to http://community.kde.org/KWin/Shadow + * + * To create a Shadow instance use the static factory method @link createShadow which will + * create an instance for the currently used Compositing Backend. It will read the X11 Property + * and create the Shadow and all required data (such as WindowQuads). If there is no Shadow + * defined for the Toplevel the factory method returns @c NULL. + * + * @author Martin Gräßlin + * @todo React on Toplevel size changes. + **/ +class Shadow : public QObject +{ + Q_OBJECT +public: + virtual ~Shadow(); + + /** + * @return Region of the shadow. + **/ + const QRegion &shadowRegion() { + return m_shadowRegion; + }; + /** + * @return Cached Shadow Quads + **/ + const WindowQuadList &shadowQuads() { + return m_shadowQuads; + }; + + /** + * This method updates the Shadow when the property has been changed. + * It is the responsibility of the owner of the Shadow to call this method + * whenever the owner receives a PropertyNotify event. + * This method will invoke a re-read of the Property. In case the Property has + * been withdrawn the method returns @c false. In that case the owner should + * delete the Shadow. + * @returns @c true when the shadow has been updated, @c false if the property is not set anymore. + **/ + virtual bool updateShadow(); + + /** + * Factory Method to create the shadow from the property. + * This method takes care of creating an instance of the + * Shadow class for the current Compositing Backend. + * + * If there is no shadow defined for @p toplevel this method + * will return @c NULL. + * @param toplevel The Toplevel for which the shadow should be created + * @return Created Shadow or @c NULL in case there is no shadow defined. + **/ + static Shadow *createShadow(Toplevel *toplevel); + +protected: + Shadow(Toplevel *toplevel); + enum ShadowElements { + ShadowElementTop, + ShadowElementTopRight, + ShadowElementRight, + ShadowElementBottomRight, + ShadowElementBottom, + ShadowElementBottomLeft, + ShadowElementLeft, + ShadowElementTopLeft, + ShadowElementsCount + }; + const QPixmap &shadowPixmap(ShadowElements element) const { + return m_shadowElements[element]; + }; + int topOffset() const { + return m_topOffset; + }; + int rightOffset() const { + return m_rightOffset; + }; + int bottomOffset() const { + return m_bottomOffset; + }; + int leftOffset() const { + return m_leftOffset; + }; + virtual void buildQuads(); + +private: + static QVector readX11ShadowProperty(WId id); + void init(const QVector &data); + Toplevel *m_topLevel; + // shadow pixmaps + QPixmap m_shadowElements[ShadowElementsCount]; + // shadow offsets + int m_topOffset; + int m_rightOffset; + int m_bottomOffset; + int m_leftOffset; + // caches + QRegion m_shadowRegion; + WindowQuadList m_shadowQuads; +}; + +} + +#endif // KWIN_SHADOW_H diff --git a/toplevel.cpp b/toplevel.cpp index c19a228328..68a4350491 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -25,6 +25,7 @@ along with this program. If not, see . #include "atoms.h" #include "client.h" #include "effects.h" +#include "shadow.h" namespace KWin { @@ -33,6 +34,7 @@ Toplevel::Toplevel(Workspace* ws) : vis(NULL) , info(NULL) , ready_for_painting(true) + , m_shadow(NULL) , client(None) , frame(None) , wspace(ws) @@ -131,6 +133,11 @@ void Toplevel::copyToDeleted(Toplevel* c) // this needs to be done already here, otherwise 'c' could very likely // call discardWindowPixmap() in something called during cleanup c->window_pix = None; + if (c->hasShadow()) { + m_shadow = c->m_shadow; + c->m_shadow->setParent(this); + c->m_shadow = NULL; + } } // before being deleted, remove references to everything that's now @@ -142,6 +149,9 @@ void Toplevel::disownDataPassedToDeleted() QRect Toplevel::visibleRect() const { + if (hasShadow()) { + return shadow()->shadowRegion().boundingRect().translated(geometry().topLeft()); + } return geometry(); } @@ -348,6 +358,24 @@ bool Toplevel::isOnScreen(int screen) const return workspace()->screenGeometry(screen).intersects(geometry()); } +void Toplevel::getShadow() +{ + if (hasShadow()) { + m_shadow->updateShadow(); + } else { + m_shadow = Shadow::createShadow(this); + } +} + +bool Toplevel::hasShadow() const +{ + return m_shadow != NULL; +} + +Shadow *Toplevel::shadow() const +{ + return m_shadow; +} } // namespace diff --git a/toplevel.h b/toplevel.h index 9eb5de8c9d..1ac1538165 100644 --- a/toplevel.h +++ b/toplevel.h @@ -44,6 +44,7 @@ namespace KWin class Workspace; class EffectWindowImpl; +class Shadow; class Toplevel : public QObject, public KDecorationDefines @@ -135,6 +136,21 @@ public: void resetDamage(const QRect& r); EffectWindowImpl* effectWindow(); + /** + * @returns Whether the Toplevel has a Shadow or not + * @see shadow + **/ + bool hasShadow() const; + /** + * Returns the pointer to the Toplevel's Shadow. A Shadow + * is only available if Compositing is enabled and the corresponding X window + * has the Shadow property set. + * If a shadow is available @link hasShadow returns @c true. + * @returns The Shadow belonging to this Toplevel, may be @c NULL. + * @see hasShadow + **/ + Shadow *shadow() const; + signals: void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity); void damaged(KWin::Toplevel* toplevel, const QRect& damage); @@ -157,6 +173,11 @@ protected: void getWmClientMachine(); void getResourceClass(); void getWindowRole(); + /** + * Updates the Shadow associated with this Toplevel from X11 Property. + * Call this method when the Property changes or Compositing is started. + **/ + void getShadow(); virtual void debug(QDebug& stream) const = 0; void copyToDeleted(Toplevel* c); void disownDataPassedToDeleted(); @@ -169,6 +190,7 @@ protected: NETWinInfo2* info; bool ready_for_painting; QRegion repaints_region; // updating, repaint just requires repaint of that area + Shadow *m_shadow; private: static QByteArray staticWindowRole(WId); static QByteArray staticSessionId(WId); diff --git a/workspace.cpp b/workspace.cpp index 3c7751174b..77b9dcb602 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -335,6 +335,7 @@ void Workspace::init() NET::WM2DesktopLayout | NET::WM2FullPlacement | NET::WM2FullscreenMonitors | + NET::WM2KDEShadow | 0 , NET::ActionMove | From bd12134f0599de7d899cf0c73bc6a25117b49624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 30 Mar 2011 20:29:54 +0200 Subject: [PATCH 02/14] Deep copy the shadow pixmaps At least with NVIDIA drivers we get bad alloc errors in glXCreatePixmap for the shared pixmaps. --- shadow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shadow.cpp b/shadow.cpp index 9d35d41a06..0dba4e8650 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -83,7 +83,8 @@ QVector< long > Shadow::readX11ShadowProperty(WId id) void Shadow::init(const QVector< long > &data) { for (int i=0; i Date: Wed, 30 Mar 2011 20:31:36 +0200 Subject: [PATCH 03/14] Shadow texcoords are normalized Set texture width/height to 1.0, so that we see the shadow. --- scene_opengl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 8b1e072085..0a79312b53 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -680,6 +680,10 @@ void SceneOpenGL::Window::paintShadow(WindowQuadType type, const QRegion ®ion texture->setWrapMode(GL_CLAMP_TO_EDGE); texture->bind(); prepareStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); + if (data.shader) { + data.shader->setUniform(GLShader::TextureWidth, 1.0f); + data.shader->setUniform(GLShader::TextureHeight, 1.0f); + } renderQuads(0, region, quads); restoreStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); texture->unbind(); From ea17b3562cc5ca1b68a6995ae6690e840d1b7a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 30 Mar 2011 20:32:27 +0200 Subject: [PATCH 04/14] Add debug rendering for shadow code --- scene_opengl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 0a79312b53..07f1c87953 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -687,6 +687,13 @@ void SceneOpenGL::Window::paintShadow(WindowQuadType type, const QRegion ®ion renderQuads(0, region, quads); restoreStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); texture->unbind(); +#ifndef KWIN_HAVE_OPENGLES + if (static_cast(scene)->debug) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + renderQuads(0, region, quads); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +#endif } void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, const QRect& rect) const From 89aa8ee8dbcf6b0f1a49946c8e353c923d478bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 1 Apr 2011 21:49:44 +0200 Subject: [PATCH 05/14] Fix repainting of shadow after window closes Unmanaged needs to repaint the complete geometry including shadow when it ends compositing. Therefore we need to track the shadow passing to deleted correctly. Disadvantage: when turning off compositing the shadow is kept. Need to solve in a better way! --- composite.cpp | 4 ---- shadow.cpp | 6 ++++++ shadow.h | 7 +++++++ toplevel.cpp | 4 ++-- unmanaged.cpp | 2 +- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/composite.cpp b/composite.cpp index 44a3865be4..3cb8bc8ae7 100644 --- a/composite.cpp +++ b/composite.cpp @@ -690,10 +690,6 @@ void Toplevel::finishCompositing() damage_region = QRegion(); repaints_region = QRegion(); effect_window = NULL; - if (hasShadow()) { - delete m_shadow; - m_shadow = NULL; - } #endif } diff --git a/shadow.cpp b/shadow.cpp index 0dba4e8650..422313e1e6 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -171,4 +171,10 @@ bool Shadow::updateShadow() return true; } +void Shadow::setToplevel(Toplevel *topLevel) +{ + m_topLevel = topLevel; + setParent(topLevel); +} + } // namespace diff --git a/shadow.h b/shadow.h index 2a5aeda4e9..6b4719ed57 100644 --- a/shadow.h +++ b/shadow.h @@ -85,6 +85,13 @@ public: **/ static Shadow *createShadow(Toplevel *toplevel); + /** + * Reparents the shadow to @p toplevel. + * Used when a window is deleted. + * @param toplevel The new parent + **/ + void setToplevel(Toplevel *toplevel); + protected: Shadow(Toplevel *toplevel); enum ShadowElements { diff --git a/toplevel.cpp b/toplevel.cpp index 68a4350491..99a5f42237 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -135,8 +135,7 @@ void Toplevel::copyToDeleted(Toplevel* c) c->window_pix = None; if (c->hasShadow()) { m_shadow = c->m_shadow; - c->m_shadow->setParent(this); - c->m_shadow = NULL; + m_shadow->setToplevel(this); } } @@ -145,6 +144,7 @@ void Toplevel::copyToDeleted(Toplevel* c) void Toplevel::disownDataPassedToDeleted() { info = NULL; + m_shadow = NULL; } QRect Toplevel::visibleRect() const diff --git a/unmanaged.cpp b/unmanaged.cpp index e717bf89f9..6fcd47398d 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -92,7 +92,7 @@ void Unmanaged::release() XShapeSelectInput(display(), window(), NoEventMask); XSelectInput(display(), window(), NoEventMask); } - addWorkspaceRepaint(geometry()); + addWorkspaceRepaint(visibleRect()); disownDataPassedToDeleted(); del->unrefWindow(); deleteUnmanaged(this, Allowed); From 9f02766fd9d609e0128dbb5ed3f62dec68d615db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 1 Apr 2011 22:52:37 +0200 Subject: [PATCH 06/14] Trigger full repaint when adding a shadow --- toplevel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/toplevel.cpp b/toplevel.cpp index 99a5f42237..a5766b7331 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -364,6 +364,7 @@ void Toplevel::getShadow() m_shadow->updateShadow(); } else { m_shadow = Shadow::createShadow(this); + addRepaintFull(); } } From c82fed057ffd825268ff0f7bdf491d249915a0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sat, 2 Apr 2011 19:01:16 +0200 Subject: [PATCH 07/14] Verify that Shadow pixmaps are valid At least in rekonq the shadow pixmaps are not valid causing an exception to be thrown and crashing kwin. --- shadow.cpp | 11 +++++++++-- shadow.h | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/shadow.cpp b/shadow.cpp index 422313e1e6..5f3fa4de56 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -52,7 +52,10 @@ Shadow *Shadow::createShadow(Toplevel *toplevel) #endif } if (shadow) { - shadow->init(data); + if (!shadow->init(data)) { + delete shadow; + return NULL; + } } return shadow; } else { @@ -80,10 +83,13 @@ QVector< long > Shadow::readX11ShadowProperty(WId id) return ret; } -void Shadow::init(const QVector< long > &data) +bool Shadow::init(const QVector< long > &data) { for (int i=0; i &data) const QRect leftRect(- m_leftOffset, - m_topOffset, m_leftOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); m_shadowRegion = QRegion(topRect).united(rightRect).united(bottomRect).united(leftRect); buildQuads(); + return true; } void Shadow::buildQuads() diff --git a/shadow.h b/shadow.h index 6b4719ed57..eb2009f610 100644 --- a/shadow.h +++ b/shadow.h @@ -124,7 +124,7 @@ protected: private: static QVector readX11ShadowProperty(WId id); - void init(const QVector &data); + bool init(const QVector &data); Toplevel *m_topLevel; // shadow pixmaps QPixmap m_shadowElements[ShadowElementsCount]; From 38e9ab9a4eecaf3d3000e5c24699036911c9ce43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 3 Apr 2011 11:31:33 +0200 Subject: [PATCH 08/14] Move ownership on Shadow from Toplevel to Scene::Window The Shadow is clearly an aspect of the compositor. Therefore the Shadow has to be owned and controlled by the Scene::Window. Nevertheless Toplevel needs to know about the Shadow cause of reading the property. --- scene.cpp | 6 ++++-- scene.h | 23 +++++++++++++++++++++++ scene_opengl.cpp | 9 +++++++-- shadow.cpp | 8 +++++--- shadow.h | 2 +- toplevel.cpp | 32 +++++++++++++++++++++----------- toplevel.h | 21 ++++++++++++++------- unmanaged.cpp | 2 +- 8 files changed, 76 insertions(+), 27 deletions(-) diff --git a/scene.cpp b/scene.cpp index c85d325b31..6ef81f8c52 100644 --- a/scene.cpp +++ b/scene.cpp @@ -353,6 +353,7 @@ QRegion Scene::selfCheckRegion() const Scene::Window::Window(Toplevel * c) : toplevel(c) , filter(ImageFilterFast) + , m_shadow(NULL) , disable_painting(0) , shape_valid(false) , cached_quad_list(NULL) @@ -362,6 +363,7 @@ Scene::Window::Window(Toplevel * c) Scene::Window::~Window() { delete cached_quad_list; + delete m_shadow; } void Scene::Window::discardShape() @@ -494,8 +496,8 @@ WindowQuadList Scene::Window::buildQuads(bool force) const ret += makeQuads(WindowQuadDecoration, right); } } - if (toplevel->hasShadow()) { - ret << toplevel->shadow()->shadowQuads(); + if (m_shadow) { + ret << m_shadow->shadowQuads(); } effects->buildQuads(static_cast(toplevel)->effectWindow(), ret); cached_quad_list = new WindowQuadList(ret); diff --git a/scene.h b/scene.h index 89e6038b24..564cb74761 100644 --- a/scene.h +++ b/scene.h @@ -35,6 +35,7 @@ class Deleted; class EffectFrameImpl; class EffectWindowImpl; class LanczosFilter; +class Shadow; // The base class for compositing backends. class Scene @@ -210,10 +211,14 @@ public: // creates initial quad list for the window virtual WindowQuadList buildQuads(bool force = false) const; void suspendUnredirect(bool suspend); + void updateShadow(Shadow* shadow); + const Shadow* shadow() const; + Shadow* shadow(); protected: WindowQuadList makeQuads(WindowQuadType type, const QRegion& reg) const; Toplevel* toplevel; ImageFilterType filter; + Shadow *m_shadow; private: int disable_painting; mutable QRegion shape_region; @@ -319,6 +324,24 @@ void Scene::Window::suspendUnredirect(bool suspend) toplevel->suspendUnredirect(suspend); } +inline +void Scene::Window::updateShadow(Shadow* shadow) +{ + m_shadow = shadow; +} + +inline +const Shadow* Scene::Window::shadow() const +{ + return m_shadow; +} + +inline +Shadow* Scene::Window::shadow() +{ + return m_shadow; +} + } // namespace #endif diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 07f1c87953..fc249ce85a 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -235,6 +235,8 @@ void SceneOpenGL::windowAdded(Toplevel* c) assert(!windows.contains(c)); windows[ c ] = new Window(c); c->effectWindow()->setSceneWindow(windows[ c ]); + c->getShadow(); + windows[ c ]->updateShadow(c->shadow()); } void SceneOpenGL::windowClosed(Toplevel* c, Deleted* deleted) @@ -244,6 +246,9 @@ void SceneOpenGL::windowClosed(Toplevel* c, Deleted* deleted) // replace c with deleted Window* w = windows.take(c); w->updateToplevel(deleted); + if (w->shadow()) { + w->shadow()->setToplevel(deleted); + } windows[ deleted ] = w; } else { delete windows.take(c); @@ -513,7 +518,7 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData vbo->reset(); // shadow - if (toplevel->hasShadow()) { + if (m_shadow) { paintShadow(WindowQuadShadowTop, region, data); paintShadow(WindowQuadShadowTopRight, region, data); paintShadow(WindowQuadShadowRight, region, data); @@ -669,7 +674,7 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType void SceneOpenGL::Window::paintShadow(WindowQuadType type, const QRegion ®ion, const WindowPaintData &data) { WindowQuadList quads = data.quads.select(type); - Texture *texture = static_cast(toplevel->shadow())->textureForQuadType(type); + Texture *texture = static_cast(m_shadow)->textureForQuadType(type); if (!texture) { return; } diff --git a/shadow.cpp b/shadow.cpp index 5f3fa4de56..309a146b18 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . #include "shadow.h" // kwin #include "atoms.h" +#include "effects.h" #include "toplevel.h" #ifdef KWIN_HAVE_OPENGL_COMPOSITING #include "scene_opengl.h" @@ -29,8 +30,7 @@ namespace KWin { Shadow::Shadow(Toplevel *toplevel) - : QObject(toplevel) - , m_topLevel(toplevel) + : m_topLevel(toplevel) { } @@ -56,6 +56,9 @@ Shadow *Shadow::createShadow(Toplevel *toplevel) delete shadow; return NULL; } + if (toplevel->effectWindow() && toplevel->effectWindow()->sceneWindow()) { + toplevel->effectWindow()->sceneWindow()->updateShadow(shadow); + } } return shadow; } else { @@ -181,7 +184,6 @@ bool Shadow::updateShadow() void Shadow::setToplevel(Toplevel *topLevel) { m_topLevel = topLevel; - setParent(topLevel); } } // namespace diff --git a/shadow.h b/shadow.h index eb2009f610..ee6dec35dc 100644 --- a/shadow.h +++ b/shadow.h @@ -52,7 +52,7 @@ public: /** * @return Region of the shadow. **/ - const QRegion &shadowRegion() { + const QRegion &shadowRegion() const { return m_shadowRegion; }; /** diff --git a/toplevel.cpp b/toplevel.cpp index a5766b7331..b73c152256 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -34,7 +34,6 @@ Toplevel::Toplevel(Workspace* ws) : vis(NULL) , info(NULL) , ready_for_painting(true) - , m_shadow(NULL) , client(None) , frame(None) , wspace(ws) @@ -133,10 +132,6 @@ void Toplevel::copyToDeleted(Toplevel* c) // this needs to be done already here, otherwise 'c' could very likely // call discardWindowPixmap() in something called during cleanup c->window_pix = None; - if (c->hasShadow()) { - m_shadow = c->m_shadow; - m_shadow->setToplevel(this); - } } // before being deleted, remove references to everything that's now @@ -144,7 +139,6 @@ void Toplevel::copyToDeleted(Toplevel* c) void Toplevel::disownDataPassedToDeleted() { info = NULL; - m_shadow = NULL; } QRect Toplevel::visibleRect() const @@ -361,21 +355,37 @@ bool Toplevel::isOnScreen(int screen) const void Toplevel::getShadow() { if (hasShadow()) { - m_shadow->updateShadow(); + effectWindow()->sceneWindow()->shadow()->updateShadow(); } else { - m_shadow = Shadow::createShadow(this); + Shadow::createShadow(this); addRepaintFull(); } } bool Toplevel::hasShadow() const { - return m_shadow != NULL; + if (effectWindow() && effectWindow()->sceneWindow()) { + return effectWindow()->sceneWindow()->shadow() != NULL; + } + return false; } -Shadow *Toplevel::shadow() const +Shadow *Toplevel::shadow() { - return m_shadow; + if (effectWindow() && effectWindow()->sceneWindow()) { + return effectWindow()->sceneWindow()->shadow(); + } else { + return NULL; + } +} + +const Shadow *Toplevel::shadow() const +{ + if (effectWindow() && effectWindow()->sceneWindow()) { + return effectWindow()->sceneWindow()->shadow(); + } else { + return NULL; + } } } // namespace diff --git a/toplevel.h b/toplevel.h index 1ac1538165..49ac824b71 100644 --- a/toplevel.h +++ b/toplevel.h @@ -135,6 +135,7 @@ public: QRegion damage() const; void resetDamage(const QRect& r); EffectWindowImpl* effectWindow(); + const EffectWindowImpl* effectWindow() const; /** * @returns Whether the Toplevel has a Shadow or not @@ -149,7 +150,13 @@ public: * @returns The Shadow belonging to this Toplevel, may be @c NULL. * @see hasShadow **/ - Shadow *shadow() const; + const Shadow *shadow() const; + Shadow *shadow(); + /** + * Updates the Shadow associated with this Toplevel from X11 Property. + * Call this method when the Property changes or Compositing is started. + **/ + void getShadow(); signals: void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity); @@ -173,11 +180,6 @@ protected: void getWmClientMachine(); void getResourceClass(); void getWindowRole(); - /** - * Updates the Shadow associated with this Toplevel from X11 Property. - * Call this method when the Property changes or Compositing is started. - **/ - void getShadow(); virtual void debug(QDebug& stream) const = 0; void copyToDeleted(Toplevel* c); void disownDataPassedToDeleted(); @@ -190,7 +192,6 @@ protected: NETWinInfo2* info; bool ready_for_painting; QRegion repaints_region; // updating, repaint just requires repaint of that area - Shadow *m_shadow; private: static QByteArray staticWindowRole(WId); static QByteArray staticSessionId(WId); @@ -414,6 +415,12 @@ EffectWindowImpl* Toplevel::effectWindow() return effect_window; } +inline +const EffectWindowImpl* Toplevel::effectWindow() const +{ + return effect_window; +} + inline bool Toplevel::isOnAllDesktops() const { return desktop() == NET::OnAllDesktops; diff --git a/unmanaged.cpp b/unmanaged.cpp index 6fcd47398d..7c36451b49 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -92,7 +92,7 @@ void Unmanaged::release() XShapeSelectInput(display(), window(), NoEventMask); XSelectInput(display(), window(), NoEventMask); } - addWorkspaceRepaint(visibleRect()); + addWorkspaceRepaint(del->visibleRect()); disownDataPassedToDeleted(); del->unrefWindow(); deleteUnmanaged(this, Allowed); From 24f36b7fae4c575bbc30afb183d242b5cd297b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 3 Apr 2011 12:13:20 +0200 Subject: [PATCH 09/14] Shadow is read when Scene::Window is created --- composite.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/composite.cpp b/composite.cpp index 3cb8bc8ae7..0c8d7090b2 100644 --- a/composite.cpp +++ b/composite.cpp @@ -669,7 +669,6 @@ void Toplevel::setupCompositing() damage_region = QRegion(0, 0, width(), height()); effect_window = new EffectWindowImpl(); effect_window->setWindow(this); - getShadow(); unredirect = false; workspace()->checkUnredirect(true); #endif From 7287019050c58d25939c9fdd686abd4a2e09a07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 3 Apr 2011 12:43:57 +0200 Subject: [PATCH 10/14] Update cached Shadow information on size changes In order to notice when the geometry changes a new signal is added to toplevel and both Unmanaged and Client connect all their signals which are emitted whenever the geometry changes in some way to this new signal. Shadow connects to the signal and updates the quads and region whenever the size changes. --- client.cpp | 4 ++++ shadow.cpp | 22 +++++++++++++++++++--- shadow.h | 5 +++++ toplevel.h | 1 + unmanaged.cpp | 1 + 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/client.cpp b/client.cpp index ec60bde9c7..0172a10c85 100644 --- a/client.cpp +++ b/client.cpp @@ -194,6 +194,10 @@ Client::Client(Workspace* ws) ready_for_painting = false; // wait for first damage or sync reply #endif + connect(this, SIGNAL(clientGeometryShapeChanged(KWin::Client*,QRect)), SIGNAL(geometryChanged())); + connect(this, SIGNAL(clientMaximizedStateChanged(KWin::Client*,KDecorationDefines::MaximizeMode)), SIGNAL(geometryChanged())); + connect(this, SIGNAL(clientStepUserMovedResized(KWin::Client*,QRect)), SIGNAL(geometryChanged())); + // SELI TODO: Initialize xsizehints?? } diff --git a/shadow.cpp b/shadow.cpp index 309a146b18..9e9179a608 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -31,7 +31,9 @@ namespace KWin Shadow::Shadow(Toplevel *toplevel) : m_topLevel(toplevel) + , m_cachedSize(toplevel->geometry().size()) { + connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); } Shadow::~Shadow() @@ -99,14 +101,18 @@ bool Shadow::init(const QVector< long > &data) m_rightOffset = data[ShadowElementsCount+1]; m_bottomOffset = data[ShadowElementsCount+2]; m_leftOffset = data[ShadowElementsCount+3]; - // prepare shadow region + updateShadowRegion(); + buildQuads(); + return true; +} + +void Shadow::updateShadowRegion() +{ const QRect topRect(0, - m_topOffset, m_topLevel->width(), m_topOffset); const QRect rightRect(m_topLevel->width(), - m_topOffset, m_rightOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); const QRect bottomRect(0, m_topLevel->height(), m_topLevel->width(), m_bottomOffset); const QRect leftRect(- m_leftOffset, - m_topOffset, m_leftOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); m_shadowRegion = QRegion(topRect).united(rightRect).united(bottomRect).united(leftRect); - buildQuads(); - return true; } void Shadow::buildQuads() @@ -184,6 +190,16 @@ bool Shadow::updateShadow() void Shadow::setToplevel(Toplevel *topLevel) { m_topLevel = topLevel; + connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); +} +void Shadow::geometryChanged() +{ + if (m_cachedSize == m_topLevel->geometry().size()) { + return; + } + m_cachedSize = m_topLevel->geometry().size(); + updateShadowRegion(); + buildQuads(); } } // namespace diff --git a/shadow.h b/shadow.h index ee6dec35dc..7127db95ae 100644 --- a/shadow.h +++ b/shadow.h @@ -92,6 +92,9 @@ public: **/ void setToplevel(Toplevel *toplevel); +public Q_SLOTS: + void geometryChanged(); + protected: Shadow(Toplevel *toplevel); enum ShadowElements { @@ -121,6 +124,7 @@ protected: return m_leftOffset; }; virtual void buildQuads(); + void updateShadowRegion(); private: static QVector readX11ShadowProperty(WId id); @@ -136,6 +140,7 @@ private: // caches QRegion m_shadowRegion; WindowQuadList m_shadowQuads; + QSize m_cachedSize; }; } diff --git a/toplevel.h b/toplevel.h index 49ac824b71..15a9aa361a 100644 --- a/toplevel.h +++ b/toplevel.h @@ -162,6 +162,7 @@ signals: void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity); void damaged(KWin::Toplevel* toplevel, const QRect& damage); void propertyNotify(KWin::Toplevel* toplevel, long a); + void geometryChanged(); protected: virtual ~Toplevel(); diff --git a/unmanaged.cpp b/unmanaged.cpp index 7c36451b49..32908aabd2 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -32,6 +32,7 @@ namespace KWin Unmanaged::Unmanaged(Workspace* ws) : Toplevel(ws) { + connect(this, SIGNAL(unmanagedGeometryShapeChanged(KWin::Unmanaged*,QRect)), SIGNAL(geometryChanged())); } Unmanaged::~Unmanaged() From a9ce4aa2af62d680ab0ad9656277fa95d7ece298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 3 Apr 2011 13:05:35 +0200 Subject: [PATCH 11/14] Don't render a Shadow if the Shadow overlaps the window --- shadow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shadow.cpp b/shadow.cpp index 9e9179a608..5127a42ed3 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -127,6 +127,14 @@ void Shadow::buildQuads() const QRect bottomLeftRect(QPoint(0, 0), m_shadowElements[ShadowElementBottomLeft].size()); const QRect leftRect(QPoint(0, 0), m_shadowElements[ShadowElementLeft].size()); const QRect topLeftRect(QPoint(0, 0), m_shadowElements[ShadowElementTopLeft].size()); + if ((leftRect.width() - m_leftOffset > m_topLevel->width()) || + (rightRect.width() - m_rightOffset > m_topLevel->width()) || + (topRect.height() - m_topOffset > m_topLevel->height()) || + (bottomRect.height() - m_bottomOffset > m_topLevel->height())) { + // if our shadow is bigger than the window, we don't render the shadow + m_shadowRegion = QRegion(); + return; + } WindowQuad topLeftQuad(WindowQuadShadowTopLeft); topLeftQuad[ 0 ] = WindowVertex(-m_leftOffset, -m_topOffset, 0.0, 0.0); topLeftQuad[ 1 ] = WindowVertex(-m_leftOffset + topLeftRect.width(), -m_topOffset, 1.0, 0.0); From 275cf05184bf4ed29b2e4b1d18617b82ecc5b040 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Da Costa Date: Mon, 4 Apr 2011 09:42:11 +0200 Subject: [PATCH 12/14] make sure Shadow X11 property is not set on decorated windows, since it is handled by the decoration itself (and therefore should not be by kwin). --- clients/oxygen/oxygenclient.cpp | 24 +++++++++++++++++++++++- clients/oxygen/oxygenclient.h | 7 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/clients/oxygen/oxygenclient.cpp b/clients/oxygen/oxygenclient.cpp index b3a1212dda..dd9c052484 100644 --- a/clients/oxygen/oxygenclient.cpp +++ b/clients/oxygen/oxygenclient.cpp @@ -42,8 +42,12 @@ #include #include #include +#include #include +#include +#include + namespace Oxygen { @@ -59,7 +63,8 @@ namespace Oxygen _forceActive( false ), _mouseButton( Qt::NoButton ), _itemData( this ), - _sourceItem( -1 ) + _sourceItem( -1 ), + _shadowAtom( 0 ) {} //___________________________________________ @@ -167,6 +172,9 @@ namespace Oxygen } else if( hasSizeGrip() ) deleteSizeGrip(); + // needs to remove shadow property on window since shadows are handled by the decoration + removeShadowHint(); + } //___________________________________________ @@ -1827,4 +1835,18 @@ namespace Oxygen _sizeGrip = 0; } + //_________________________________________________________________ + void Client::removeShadowHint( void ) + { + + // do nothing if no window id + if( !windowId() ) return; + + // create atom + if( !_shadowAtom ) + { _shadowAtom = XInternAtom( QX11Info::display(), "_KDE_NET_WM_SHADOW", False); } + + XDeleteProperty(QX11Info::display(), windowId(), _shadowAtom); + } + } diff --git a/clients/oxygen/oxygenclient.h b/clients/oxygen/oxygenclient.h index e65873392c..088c40f995 100644 --- a/clients/oxygen/oxygenclient.h +++ b/clients/oxygen/oxygenclient.h @@ -41,6 +41,7 @@ #include #include +#include namespace Oxygen { @@ -398,6 +399,9 @@ namespace Oxygen //@} + //! remove shadow hint + void removeShadowHint( void ); + protected slots: //! set target item to -1 @@ -459,6 +463,9 @@ namespace Oxygen */ QBasicTimer _dragStartTimer; + //! shadow atom + Atom _shadowAtom; + }; } // namespace Oxygen From 026dd17d887e54b4f139241a41ae5ea1066130a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 4 Apr 2011 18:50:30 +0200 Subject: [PATCH 13/14] Repaint also old shadow region on geometry change --- events.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.cpp b/events.cpp index 7ce6dad10a..66a94197fe 100644 --- a/events.cpp +++ b/events.cpp @@ -1642,7 +1642,7 @@ void Unmanaged::configureNotifyEvent(XConfigureEvent* e) static_cast(effects)->checkInputWindowStacking(); // keep them on top QRect newgeom(e->x, e->y, e->width, e->height); if (newgeom != geom) { - addWorkspaceRepaint(geometry()); // damage old area + addWorkspaceRepaint(visibleRect()); // damage old area QRect old = geom; geom = newgeom; addRepaintFull(); From 2d066a710c68a4a944dad048b422b15ea10028c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 10 Apr 2011 21:24:27 +0200 Subject: [PATCH 14/14] Fix repainting of Shadows for torn-off toolbars Toolbars and Dock Widgets are clients which did not yet have proper Shadow support. Especially the visibleRect and decorationRect which is incorrectly used to refer to the complete window geometry had to be updated. --- client.cpp | 12 ++++++++++++ client.h | 8 ++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/client.cpp b/client.cpp index 98f3290cf3..31eaa442bc 100644 --- a/client.cpp +++ b/client.cpp @@ -44,6 +44,7 @@ along with this program. If not, see . #include "notifications.h" #include "rules.h" #include "scene.h" +#include "shadow.h" #include "deleted.h" #include "paintredirector.h" #include "tabbox.h" @@ -2239,6 +2240,17 @@ void Client::setSessionInteract(bool needed) needsSessionInteract = needed; } +QRect Client::decorationRect() const +{ + if (decoration && decoration->widget()) { + return decoration->widget()->rect().translated(-padding_left, -padding_top); + } else if (hasShadow()) { + return shadow()->shadowRegion().boundingRect(); + } else { + return QRect(0, 0, width(), height()); + } +} + } // namespace #include "client.moc" diff --git a/client.h b/client.h index 5dd47027fd..77bbe0f376 100644 --- a/client.h +++ b/client.h @@ -401,11 +401,7 @@ public: bool decorationPixmapRequiresRepaint(); void ensureDecorationPixmapsPainted(); - QRect decorationRect() const { - return (decoration && decoration->widget()) ? - decoration->widget()->rect().translated(-padding_left, -padding_top) : - QRect(0, 0, width(), height()); - } + QRect decorationRect() const; QRect transparentRect() const; @@ -967,7 +963,7 @@ inline QSize Client::clientSize() const inline QRect Client::visibleRect() const { - return geometry().adjusted(-padding_left, -padding_top, padding_right, padding_bottom); + return Toplevel::visibleRect().adjusted(-padding_left, -padding_top, padding_right, padding_bottom); } inline void Client::setGeometry(const QRect& r, ForceGeometry_t force, bool emitJs)