Merge branch 'oxygen-shadows'

This commit is contained in:
Martin Gräßlin 2011-04-12 18:13:49 +02:00
commit 6264370237
20 changed files with 764 additions and 64 deletions

View file

@ -98,6 +98,7 @@ set(kwin_KDEINIT_SRCS
events.cpp
killwindow.cpp
geometrytip.cpp
shadow.cpp
sm.cpp
group.cpp
bridge.cpp

View file

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

View file

@ -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;
};

View file

@ -44,6 +44,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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"

View file

@ -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)

View file

@ -42,8 +42,12 @@
#include <QtGui/QLabel>
#include <QtGui/QPainter>
#include <QtGui/QBitmap>
#include <QtGui/QX11Info>
#include <QtCore/QObjectList>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
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);
}
}

View file

@ -41,6 +41,7 @@
#include <QtCore/QTextStream>
#include <QtCore/QTimerEvent>
#include <X11/Xdefs.h>
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

View file

@ -51,6 +51,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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();
}

View file

@ -1642,7 +1642,7 @@ void Unmanaged::configureNotifyEvent(XConfigureEvent* e)
static_cast<EffectsHandlerImpl*>(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);

View file

@ -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
};

View file

@ -77,6 +77,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "deleted.h"
#include "effects.h"
#include "lanczosfilter.h"
#include "shadow.h"
#include <kephal/screens.h>
@ -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<Client*>(toplevel)->effectWindow(), ret);
cached_quad_list = new WindowQuadList(ret);
return ret;

23
scene.h
View file

@ -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

View file

@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
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<Client*>(toplevel);
Deleted *deleted = dynamic_cast<Deleted*>(toplevel);
@ -654,6 +671,36 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType
#endif
}
void SceneOpenGL::Window::paintShadow(WindowQuadType type, const QRegion &region, const WindowPaintData &data)
{
WindowQuadList quads = data.quads.select(type);
Texture *texture = static_cast<SceneOpenGLShadow*>(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<SceneOpenGL*>(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<float> 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; i<ShadowElementsCount; ++i) {
m_shadowTextures[i].discard();
}
}
SceneOpenGL::Texture *SceneOpenGLShadow::textureForQuadType(WindowQuadType type)
{
SceneOpenGL::Texture *texture = NULL;
QPixmap pixmap;
switch (type) {
case WindowQuadShadowTop:
texture = &m_shadowTextures[ShadowElementTop];
pixmap = shadowPixmap(ShadowElementTop);
break;
case WindowQuadShadowTopRight:
texture = &m_shadowTextures[ShadowElementTopRight];
pixmap = shadowPixmap(ShadowElementTopRight);
break;
case WindowQuadShadowRight:
texture = &m_shadowTextures[ShadowElementRight];
pixmap = shadowPixmap(ShadowElementRight);
break;
case WindowQuadShadowBottomRight:
texture = &m_shadowTextures[ShadowElementBottomRight];
pixmap = shadowPixmap(ShadowElementBottomRight);
break;
case WindowQuadShadowBottom:
texture = &m_shadowTextures[ShadowElementBottom];
pixmap = shadowPixmap(ShadowElementBottom);
break;
case WindowQuadShadowBottomLeft:
texture = &m_shadowTextures[ShadowElementBottomLeft];
pixmap = shadowPixmap(ShadowElementBottomLeft);
break;
case WindowQuadShadowLeft:
texture = &m_shadowTextures[ShadowElementLeft];
pixmap = shadowPixmap(ShadowElementLeft);
break;
case WindowQuadShadowTopLeft:
texture = &m_shadowTextures[ShadowElementTopLeft];
pixmap = shadowPixmap(ShadowElementTopLeft);
break;
default:
// nothing
break;
}
if (texture) {
if (texture->texture() != 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

View file

@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
#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 &region, 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 <mgraesslin@kde.org>
**/
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

213
shadow.cpp Normal file
View file

@ -0,0 +1,213 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#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<long> 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<long> 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; i<ShadowElementsCount; ++i) {
QPixmap pix = QPixmap::fromX11Pixmap(data[i]);
if (pix.isNull()) {
return false;
}
m_shadowElements[i] = pix.copy(0, 0, pix.width(), pix.height());
}
m_topOffset = data[ShadowElementsCount];
m_rightOffset = data[ShadowElementsCount+1];
m_bottomOffset = data[ShadowElementsCount+2];
m_leftOffset = data[ShadowElementsCount+3];
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);
}
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<long> 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

148
shadow.h Normal file
View file

@ -0,0 +1,148 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_SHADOW_H
#define KWIN_SHADOW_H
#include <QtCore/QObject>
#include <QtGui/QPixmap>
#include <kwineffects.h>
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 <mgraesslin@kde.org>
* @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<long> readX11ShadowProperty(WId id);
bool init(const QVector<long> &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

View file

@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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

View file

@ -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;

View file

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

View file

@ -336,6 +336,7 @@ void Workspace::init()
NET::WM2DesktopLayout |
NET::WM2FullPlacement |
NET::WM2FullscreenMonitors |
NET::WM2KDEShadow |
0
,
NET::ActionMove |