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 5ba1930bd4..f53ef31d83 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -117,6 +117,9 @@ Atoms::Atoms() atoms[n] = &kde_net_wm_block_compositing; names[n++] = (char*) "_KDE_NET_WM_BLOCK_COMPOSITING"; + 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 aa6cfed110..e51411fc25 100644 --- a/atoms.h +++ b/atoms.h @@ -61,6 +61,7 @@ public: Atom net_wm_sync_request_counter; Atom net_wm_sync_request; Atom kde_net_wm_block_compositing; + Atom kde_net_wm_shadow; }; diff --git a/client.cpp b/client.cpp index 57e324b631..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" @@ -195,6 +196,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?? } @@ -2235,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) 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 diff --git a/composite.cpp b/composite.cpp index 79a8b3c449..66638b11de 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" @@ -409,6 +410,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 @@ -883,6 +887,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(); } @@ -973,6 +980,9 @@ bool Client::shouldUnredirect() const void Client::addRepaintFull() { repaints_region = decorationRect(); + if (hasShadow()) { + repaints_region = repaints_region.united(shadow()->shadowRegion()); + } workspace()->checkCompositeTimer(); } @@ -1016,6 +1026,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 0d5dc7c1a9..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(); @@ -1668,6 +1668,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..6ef81f8c52 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 @@ -352,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) @@ -361,6 +363,7 @@ Scene::Window::Window(Toplevel * c) Scene::Window::~Window() { delete cached_quad_list; + delete m_shadow; } void Scene::Window::discardShape() @@ -493,6 +496,9 @@ WindowQuadList Scene::Window::buildQuads(bool force) const ret += makeQuads(WindowQuadDecoration, right); } } + if (m_shadow) { + ret << m_shadow->shadowQuads(); + } effects->buildQuads(static_cast(toplevel)->effectWindow(), ret); cached_quad_list = new WindowQuadList(ret); return 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 58ea7d7277..fc249ce85a 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. @@ -234,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) @@ -243,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); @@ -511,6 +517,17 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); + // shadow + if (m_shadow) { + 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 +671,36 @@ 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(m_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); + 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(); +#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 { QVector vertices; @@ -710,8 +757,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 +827,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 +967,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 +1018,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 +1515,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..5127a42ed3 --- /dev/null +++ b/shadow.cpp @@ -0,0 +1,213 @@ +/******************************************************************** + 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 "effects.h" +#include "toplevel.h" +#ifdef KWIN_HAVE_OPENGL_COMPOSITING +#include "scene_opengl.h" +#endif + +namespace KWin +{ + +Shadow::Shadow(Toplevel *toplevel) + : m_topLevel(toplevel) + , m_cachedSize(toplevel->geometry().size()) +{ + connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); +} + +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) { + if (!shadow->init(data)) { + delete shadow; + return NULL; + } + if (toplevel->effectWindow() && toplevel->effectWindow()->sceneWindow()) { + toplevel->effectWindow()->sceneWindow()->updateShadow(shadow); + } + } + 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; +} + +bool 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); +} + +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()); + 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); + 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; +} + +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 new file mode 100644 index 0000000000..7127db95ae --- /dev/null +++ b/shadow.h @@ -0,0 +1,148 @@ +/******************************************************************** + 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() const { + 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); + + /** + * Reparents the shadow to @p toplevel. + * Used when a window is deleted. + * @param toplevel The new parent + **/ + void setToplevel(Toplevel *toplevel); + +public Q_SLOTS: + void geometryChanged(); + +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(); + void updateShadowRegion(); + +private: + static QVector readX11ShadowProperty(WId id); + bool 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; + QSize m_cachedSize; +}; + +} + +#endif // KWIN_SHADOW_H diff --git a/toplevel.cpp b/toplevel.cpp index c19a228328..b73c152256 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 { @@ -142,6 +143,9 @@ void Toplevel::disownDataPassedToDeleted() QRect Toplevel::visibleRect() const { + if (hasShadow()) { + return shadow()->shadowRegion().boundingRect().translated(geometry().topLeft()); + } return geometry(); } @@ -348,6 +352,41 @@ bool Toplevel::isOnScreen(int screen) const return workspace()->screenGeometry(screen).intersects(geometry()); } +void Toplevel::getShadow() +{ + if (hasShadow()) { + effectWindow()->sceneWindow()->shadow()->updateShadow(); + } else { + Shadow::createShadow(this); + addRepaintFull(); + } +} + +bool Toplevel::hasShadow() const +{ + if (effectWindow() && effectWindow()->sceneWindow()) { + return effectWindow()->sceneWindow()->shadow() != NULL; + } + return false; +} + +Shadow *Toplevel::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 9eb5de8c9d..15a9aa361a 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 @@ -134,11 +135,34 @@ public: QRegion damage() const; void resetDamage(const QRect& r); EffectWindowImpl* effectWindow(); + const EffectWindowImpl* effectWindow() const; + + /** + * @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 + **/ + 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); void damaged(KWin::Toplevel* toplevel, const QRect& damage); void propertyNotify(KWin::Toplevel* toplevel, long a); + void geometryChanged(); protected: virtual ~Toplevel(); @@ -392,6 +416,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 e717bf89f9..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() @@ -92,7 +93,7 @@ void Unmanaged::release() XShapeSelectInput(display(), window(), NoEventMask); XSelectInput(display(), window(), NoEventMask); } - addWorkspaceRepaint(geometry()); + addWorkspaceRepaint(del->visibleRect()); disownDataPassedToDeleted(); del->unrefWindow(); deleteUnmanaged(this, Allowed); diff --git a/workspace.cpp b/workspace.cpp index b4f6fa46a2..f5fc5eaa8d 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -336,6 +336,7 @@ void Workspace::init() NET::WM2DesktopLayout | NET::WM2FullPlacement | NET::WM2FullscreenMonitors | + NET::WM2KDEShadow | 0 , NET::ActionMove |