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 |