You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
974 lines
44 KiB
974 lines
44 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org> |
|
Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.com> |
|
Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com> |
|
|
|
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" |
|
#include "shadow_helper.h" |
|
|
|
#include <kwinglutils.h> |
|
|
|
#include <kconfiggroup.h> |
|
#include <kdebug.h> |
|
#include <KStandardDirs> |
|
#include <kcolorscheme.h> |
|
#include <KGlobalSettings> |
|
|
|
#include <cmath> |
|
|
|
namespace KWin |
|
{ |
|
|
|
KWIN_EFFECT( shadow, ShadowEffect ) |
|
|
|
ShadowEffect::ShadowEffect() |
|
: shadowSize( 0 ) |
|
{ |
|
reconfigure( ReconfigureAll ); |
|
connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), |
|
this, SLOT(updateShadowColor())); |
|
} |
|
|
|
ShadowEffect::~ShadowEffect() |
|
{ |
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING |
|
for( int i = 0; i < mShadowTextures.size(); i++ ) |
|
for( int j = 0; j < mShadowTextures.at( i ).size(); j++ ) |
|
delete mShadowTextures.at( i ).at( j ); |
|
for( int i = 0; i < mDefaultShadowTextures.size(); i++ ) |
|
delete mDefaultShadowTextures.at( i ); |
|
#endif |
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
for( int i = 0; i < mShadowPics.size(); i++ ) |
|
for( int j = 0; j < mShadowPics.at( i ).size(); j++ ) |
|
delete mShadowPics.at( i ).at( j ); |
|
for( int i = 0; i < mDefaultShadowPics.size(); i++ ) |
|
delete mDefaultShadowPics.at( i ); |
|
#endif |
|
} |
|
|
|
void ShadowEffect::reconfigure( ReconfigureFlags ) |
|
{ |
|
KConfigGroup conf = effects->effectConfig("Shadow"); |
|
shadowXOffset = conf.readEntry( "XOffset", 0 ); |
|
shadowYOffset = conf.readEntry( "YOffset", 3 ); |
|
shadowOpacity = conf.readEntry( "Opacity", 0.25 ); |
|
shadowFuzzyness = conf.readEntry( "Fuzzyness", 10 ); |
|
shadowSize = conf.readEntry( "Size", 5 ); |
|
intensifyActiveShadow = conf.readEntry( "IntensifyActiveShadow", true ); |
|
updateShadowColor(); |
|
forceDecorated = conf.readEntry( "forceDecoratedToDefault", false ); |
|
forceUndecorated = conf.readEntry( "forceUndecoratedToDefault", false ); |
|
forceOther = conf.readEntry( "forceOtherToDefault", false ); |
|
|
|
// Load decoration shadow related things |
|
bool reconfiguring = false; |
|
if( mShadowQuadTypes.count() ) |
|
reconfiguring = true; |
|
mShadowQuadTypes.clear(); // Changed decoration? TODO: Unregister? |
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING |
|
if( effects->compositingType() == OpenGLCompositing ) |
|
{ |
|
// Delete any other textures in memory |
|
for( int i = 0; i < mShadowTextures.size(); i++ ) |
|
for( int j = 0; j < mShadowTextures.at( i ).size(); j++ ) |
|
delete mShadowTextures.at( i ).at( j ); |
|
mShadowTextures.clear(); |
|
for( int i = 0; i < mDefaultShadowTextures.size(); i++ ) |
|
delete mDefaultShadowTextures.at( i ); |
|
mDefaultShadowTextures.clear(); |
|
|
|
// Create decoration shadows |
|
if( effects->hasDecorationShadows() ) |
|
{ |
|
QList< QList<QImage> > shadowImages = effects->shadowTextures(); |
|
for( int i = 0; i < shadowImages.size(); i++ ) |
|
{ |
|
mShadowQuadTypes.append( effects->newWindowQuadType() ); |
|
QList<GLTexture*> textures; |
|
for( int j = 0; j < shadowImages.at( i ).size(); j++ ) |
|
textures.append( new GLTexture( shadowImages.at( i ).at( j ))); |
|
mShadowTextures.append( textures ); |
|
} |
|
} |
|
|
|
// Create default textures |
|
mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister? |
|
QImage shadowImage( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" )); |
|
int hw = shadowImage.width() / 2; |
|
int hh = shadowImage.height() / 2; |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, 0, hw, hh ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, 0, 1, hh ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, 0, hw, hh ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, hh, hw, 1 ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, 1, 1 ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, hw, 1 ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, hh, hw, hh ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, 1, hh ))); |
|
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, hw, hh ))); |
|
} |
|
#endif |
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
if( effects->compositingType() == XRenderCompositing ) |
|
{ |
|
// Delete any other pictures in memory |
|
for( int i = 0; i < mShadowPics.size(); i++ ) |
|
for( int j = 0; j < mShadowPics.at( i ).size(); j++ ) |
|
delete mShadowPics.at( i ).at( j ); |
|
mShadowPics.clear(); |
|
for( int i = 0; i < mDefaultShadowPics.size(); i++ ) |
|
delete mDefaultShadowPics.at( i ); |
|
mDefaultShadowPics.clear(); |
|
|
|
// Create decoration pictures |
|
if( effects->hasDecorationShadows() ) |
|
{ |
|
QList< QList<QImage> > shadowImages = effects->shadowTextures(); |
|
for( int i = 0; i < shadowImages.size(); i++ ) |
|
{ |
|
mShadowQuadTypes.append( effects->newWindowQuadType() ); |
|
QList<XRenderPicture*> pictures; |
|
for( int j = 0; j < shadowImages.at( i ).size(); j++ ) |
|
pictures.append( new XRenderPicture( QPixmap::fromImage( shadowImages.at( i ).at( j )))); |
|
mShadowPics.append( pictures ); |
|
} |
|
} |
|
|
|
// Create default pictures |
|
mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister? |
|
QPixmap shadowPixmap( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" )); |
|
shadowPixmap = shadowPixmap.scaled( QSize( shadowFuzzyness * 4, shadowFuzzyness * 4 ), |
|
Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); |
|
int hw = shadowPixmap.width() / 2; |
|
int hh = shadowPixmap.height() / 2; |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, 0, hw, hh ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, 0, 1, hh ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, 0, hw, hh ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, hh, hw, 1 ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, 1, 1 ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, hw, 1 ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, hh, hw, hh ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, 1, hh ))); |
|
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, hw, hh ))); |
|
|
|
// Apply repeat attribute to all pictures |
|
XRenderPictureAttributes pa; |
|
pa.repeat = true; |
|
for( int i = 0; i < mShadowPics.size(); i++ ) |
|
for( int j = 0; j < mShadowPics.at( i ).size(); j++ ) |
|
XRenderChangePicture( display(), *mShadowPics.at( i ).at( j ), CPRepeat, &pa ); |
|
for( int i = 0; i < mDefaultShadowPics.size(); i++ ) |
|
XRenderChangePicture( display(), *mDefaultShadowPics.at( i ), CPRepeat, &pa ); |
|
} |
|
#endif |
|
|
|
if( reconfiguring ) |
|
{ // Force rebuild of all quads to clear their caches |
|
foreach( EffectWindow *w, effects->stackingOrder() ) |
|
w->buildQuads( true ); |
|
} |
|
} |
|
|
|
void ShadowEffect::updateShadowColor() |
|
{ |
|
KConfigGroup conf = effects->effectConfig("Shadow"); |
|
shadowColor = conf.readEntry( "Color", schemeShadowColor() ); |
|
} |
|
|
|
QRect ShadowEffect::shadowRectangle( EffectWindow* w, const QRect& windowRectangle ) const |
|
{ |
|
QRectF shadowRect; |
|
bool shadowDefined = false; |
|
if( effects->hasDecorationShadows() ) |
|
{ // TODO: This function is called for EVERY damage, we need to cache this |
|
// (But how? Decoration shadow can change shape if it wanted to) |
|
if( w->hasDecoration() && !forceDecorated ) |
|
{ // Decorated windows must be normal windows |
|
foreach( const QRect &r, w->shadowQuads( ShadowBorderedActive )) |
|
{ |
|
shadowDefined = true; |
|
shadowRect |= r; |
|
} |
|
} |
|
else if( w->isNormalWindow() && !forceUndecorated ) |
|
{ // No decoration on a normal window |
|
foreach( const QRect &r, w->shadowQuads( ShadowBorderlessActive )) |
|
{ |
|
shadowDefined = true; |
|
shadowRect |= r; |
|
} |
|
} |
|
else if( !forceOther ) |
|
{ // All other undecorated windows |
|
foreach( const QRect &r, w->shadowQuads( ShadowOther )) |
|
{ |
|
shadowDefined = true; |
|
shadowRect |= r; |
|
} |
|
} |
|
} |
|
if( !shadowDefined ) |
|
{ |
|
int shadowGrow = shadowFuzzyness + shadowSize; |
|
return windowRectangle.adjusted( shadowXOffset - shadowGrow, shadowYOffset - shadowGrow, |
|
shadowXOffset + shadowGrow, shadowYOffset + shadowGrow); |
|
} |
|
return windowRectangle.adjusted( |
|
qMin( shadowRect.x(), qreal(0.0) ), |
|
qMin( shadowRect.y(), qreal(0.0) ), |
|
qMax( shadowRect.x() + shadowRect.width() - w->width(), qreal(0.0) ), |
|
qMax( shadowRect.y() + shadowRect.height() - w->height(), qreal(0.0) ) |
|
); |
|
} |
|
|
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
static ScreenPaintData gScreenData; |
|
#endif |
|
|
|
void ShadowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) |
|
{ |
|
shadowDatas.clear(); |
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
if ((mask & PAINT_SCREEN_TRANSFORMED) && |
|
(effects->compositingType() == XRenderCompositing)) // TODO: copy constructor? |
|
{ |
|
gScreenData.xTranslate = data.xTranslate; |
|
gScreenData.yTranslate = data.yTranslate; |
|
gScreenData.xScale = data.xScale; |
|
gScreenData.yScale = data.yScale; |
|
} |
|
#endif |
|
// Draw windows |
|
effects->paintScreen( mask, region, data ); |
|
|
|
// Draw shadows |
|
drawQueuedShadows( 0 ); |
|
} |
|
|
|
void ShadowEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) |
|
{ |
|
if( useShadow( w )) |
|
data.paint |= shadowRectangle( w, data.paint.boundingRect() ); |
|
effects->prePaintWindow( w, data, time ); |
|
} |
|
|
|
void ShadowEffect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) |
|
{ |
|
// Whether the shadow drawing can be delayed or not. |
|
bool optimize = !( mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED | |
|
PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_WINDOW_TRANSLUCENT )); |
|
if( !optimize ) |
|
{ |
|
// Transformed or translucent windows are drawn bottom-to-top, so |
|
// first we need to draw all queued shadows. |
|
drawQueuedShadows( w ); |
|
} |
|
if( useShadow( w )) |
|
{ |
|
if( !optimize ) |
|
{ |
|
// For translucent windows, shadow needs to be drawn before the |
|
// window itself. |
|
drawShadow( w, mask, region, data ); |
|
} |
|
else |
|
{ |
|
// For opaque windows, just schedule the shadow to be drawn later |
|
ShadowData d(w, data); |
|
d.clip = w->shape().translated( w->x(), w->y()); |
|
if( !shadowDatas.isEmpty()) |
|
d.clip |= shadowDatas.last().clip; |
|
d.mask = mask; |
|
foreach( const QRect &r, region.rects() ) |
|
d.region |= shadowRectangle( w, r ); |
|
d.region &= region; |
|
shadowDatas.append(d); |
|
} |
|
} |
|
|
|
effects->drawWindow( w, mask, region, data ); |
|
} |
|
|
|
void ShadowEffect::buildQuads( EffectWindow* w, WindowQuadList& quadList ) |
|
{ |
|
bool shadowDefined = false; |
|
if( effects->hasDecorationShadows() ) |
|
{ |
|
// TODO: shadowQuads() is allowed to return different quads for |
|
// active and inactive shadows. Is implementing it worth |
|
// the performance drop? |
|
int id = 0; |
|
if( w->hasDecoration() && !forceDecorated ) |
|
{ // Decorated windows must be normal windows |
|
foreach( const QRect &r, w->shadowQuads( ShadowBorderedActive )) |
|
{ |
|
shadowDefined = true; |
|
WindowQuad quad( mShadowQuadTypes.at( effects->shadowTextureList( ShadowBorderedActive )), id++ ); |
|
quad[ 0 ] = WindowVertex( r.x(), r.y(), 0, 1 ); |
|
quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), 1, 1 ); |
|
quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), 1, 0 ); |
|
quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), 0, 0 ); |
|
quadList.append( quad ); |
|
} |
|
} |
|
else if( w->isNormalWindow() && !forceUndecorated ) |
|
{ // No decoration on a normal window |
|
foreach( const QRect &r, w->shadowQuads( ShadowBorderlessActive )) |
|
{ |
|
shadowDefined = true; |
|
WindowQuad quad( mShadowQuadTypes.at( effects->shadowTextureList( ShadowBorderlessActive )), id++ ); |
|
quad[ 0 ] = WindowVertex( r.x(), r.y(), 0, 1 ); |
|
quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), 1, 1 ); |
|
quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), 1, 0 ); |
|
quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), 0, 0 ); |
|
quadList.append( quad ); |
|
} |
|
} |
|
else if( !forceOther ) |
|
{ // All other undecorated windows |
|
foreach( const QRect &r, w->shadowQuads( ShadowOther )) |
|
{ |
|
shadowDefined = true; |
|
WindowQuad quad( mShadowQuadTypes.at( effects->shadowTextureList( ShadowOther )), id++ ); |
|
quad[ 0 ] = WindowVertex( r.x(), r.y(), 0, 1 ); |
|
quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), 1, 1 ); |
|
quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), 1, 0 ); |
|
quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), 0, 0 ); |
|
quadList.append( quad ); |
|
} |
|
} |
|
} |
|
if( !shadowDefined ) |
|
{ |
|
//TODO: add config option to not have shadows for menus, etc. |
|
// Make our own shadow as the decoration doesn't support it |
|
int fuzzy = shadowFuzzyness; |
|
// Shadow's size must be a least 2*fuzzy in both directions (or the corners will be broken) |
|
int width = qMax( fuzzy * 2, w->width() + 2 * shadowSize ); |
|
int height = qMax( fuzzy * 2, w->height() + 2 * shadowSize ); |
|
double x1, y1, x2, y2; |
|
int id = 0; |
|
// top-left |
|
x1 = shadowXOffset - shadowSize + 0 - fuzzy; |
|
y1 = shadowYOffset - shadowSize + 0 - fuzzy; |
|
x2 = shadowXOffset - shadowSize + 0 + fuzzy; |
|
y2 = shadowYOffset - shadowSize + 0 + fuzzy; |
|
WindowQuad topLeftQuad( mDefaultShadowQuadType, id++ ); |
|
topLeftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
topLeftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
topLeftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
topLeftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( topLeftQuad ); |
|
// top |
|
x1 = shadowXOffset - shadowSize + 0 + fuzzy; |
|
y1 = shadowYOffset - shadowSize + 0 - fuzzy; |
|
x2 = shadowXOffset - shadowSize + width - fuzzy; |
|
y2 = shadowYOffset - shadowSize + 0 + fuzzy; |
|
WindowQuad topQuad( mDefaultShadowQuadType, id++ ); |
|
topQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
topQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
topQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
topQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( topQuad ); |
|
// top-right |
|
x1 = shadowXOffset - shadowSize + width - fuzzy; |
|
y1 = shadowYOffset - shadowSize + 0 - fuzzy; |
|
x2 = shadowXOffset - shadowSize + width + fuzzy; |
|
y2 = shadowYOffset - shadowSize + 0 + fuzzy; |
|
WindowQuad topRightQuad( mDefaultShadowQuadType, id++ ); |
|
topRightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
topRightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
topRightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
topRightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( topRightQuad ); |
|
// left |
|
x1 = shadowXOffset - shadowSize + 0 - fuzzy; |
|
y1 = shadowYOffset - shadowSize + 0 + fuzzy; |
|
x2 = shadowXOffset - shadowSize + 0 + fuzzy; |
|
y2 = shadowYOffset - shadowSize + height - fuzzy; |
|
WindowQuad leftQuad( mDefaultShadowQuadType, id++ ); |
|
leftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
leftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
leftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
leftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( leftQuad ); |
|
// center |
|
x1 = shadowXOffset - shadowSize + 0 + fuzzy; |
|
y1 = shadowYOffset - shadowSize + 0 + fuzzy; |
|
x2 = shadowXOffset - shadowSize + width - fuzzy; |
|
y2 = shadowYOffset - shadowSize + height - fuzzy; |
|
WindowQuad contentsQuad( mDefaultShadowQuadType, id++ ); |
|
contentsQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
contentsQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
contentsQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
contentsQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( contentsQuad ); |
|
// right |
|
x1 = shadowXOffset - shadowSize + width - fuzzy; |
|
y1 = shadowYOffset - shadowSize + 0 + fuzzy; |
|
x2 = shadowXOffset - shadowSize + width + fuzzy; |
|
y2 = shadowYOffset - shadowSize + height - fuzzy; |
|
WindowQuad rightQuad( mDefaultShadowQuadType, id++ ); |
|
rightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
rightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
rightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
rightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( rightQuad ); |
|
// bottom-left |
|
x1 = shadowXOffset - shadowSize + 0 - fuzzy; |
|
y1 = shadowYOffset - shadowSize + height - fuzzy; |
|
x2 = shadowXOffset - shadowSize + 0 + fuzzy; |
|
y2 = shadowYOffset - shadowSize + height + fuzzy; |
|
WindowQuad bottomLeftQuad( mDefaultShadowQuadType, id++ ); |
|
bottomLeftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
bottomLeftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
bottomLeftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
bottomLeftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( bottomLeftQuad ); |
|
// bottom |
|
x1 = shadowXOffset - shadowSize + 0 + fuzzy; |
|
y1 = shadowYOffset - shadowSize + height - fuzzy; |
|
x2 = shadowXOffset - shadowSize + width - fuzzy; |
|
y2 = shadowYOffset - shadowSize + height + fuzzy; |
|
WindowQuad bottomQuad( mDefaultShadowQuadType, id++ ); |
|
bottomQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
bottomQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
bottomQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
bottomQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( bottomQuad ); |
|
// bottom-right |
|
x1 = shadowXOffset - shadowSize + width - fuzzy; |
|
y1 = shadowYOffset - shadowSize + height - fuzzy; |
|
x2 = shadowXOffset - shadowSize + width + fuzzy; |
|
y2 = shadowYOffset - shadowSize + height + fuzzy; |
|
WindowQuad bottomRightQuad( mDefaultShadowQuadType, id++ ); |
|
bottomRightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 ); |
|
bottomRightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 ); |
|
bottomRightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 ); |
|
bottomRightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 ); |
|
quadList.append( bottomRightQuad ); |
|
} // This is called for menus, tooltips, windows where the user has disabled borders and shaped windows |
|
|
|
effects->buildQuads( w, quadList ); |
|
} |
|
|
|
QRect ShadowEffect::transformWindowDamage( EffectWindow* w, const QRect& r ) |
|
{ |
|
if( !useShadow( w )) |
|
return effects->transformWindowDamage( w, r ); |
|
QRect r2 = r | shadowRectangle( w, r ); |
|
return effects->transformWindowDamage( w, r2 ); |
|
} |
|
|
|
void ShadowEffect::windowClosed( EffectWindow* c ) |
|
{ |
|
effects->addRepaint( shadowRectangle( c, c->geometry() )); |
|
} |
|
|
|
bool ShadowEffect::useShadow( EffectWindow* w ) const |
|
{ |
|
return !w->isDeleted() && !w->isDesktop() && !w->isDock() |
|
// popups may have shadow even if shaped, their shape is almost rectangular |
|
&& ( !w->hasOwnShape() || w->isDropdownMenu() || w->isPopupMenu() || w->isComboBox()); |
|
} |
|
|
|
void ShadowEffect::addQuadVertices(QVector<float>& verts, float x1, float y1, float x2, float y2) const |
|
{ |
|
verts << x1 << y1; |
|
verts << x1 << y2; |
|
verts << x2 << y2; |
|
verts << x2 << y1; |
|
} |
|
|
|
void ShadowEffect::drawQueuedShadows( EffectWindow* behindWindow ) |
|
{ |
|
QList<ShadowData> newShadowDatas; |
|
QList<ShadowData> thisTime; |
|
EffectWindowList stack = effects->stackingOrder(); |
|
foreach( const ShadowData &d, shadowDatas ) |
|
{ |
|
// If behindWindow is given then only render shadows of the windows |
|
// that are behind that window. |
|
if( !behindWindow || stack.indexOf(d.w) < stack.indexOf(behindWindow)) |
|
thisTime.append(d); |
|
else |
|
newShadowDatas.append(d); |
|
} |
|
if( thisTime.count() ) |
|
{ // Render them in stacking order |
|
foreach( EffectWindow *w, stack ) |
|
for( int i = 0; i < thisTime.size(); i++ ) |
|
{ // Cannot use foreach() due to thisTime.removeOne() |
|
const ShadowData d = thisTime.at(i); |
|
if( d.w == w ) |
|
{ |
|
drawShadow( d.w, d.mask, |
|
d.region.subtracted( d.clip ), d.data ); |
|
thisTime.removeAt( i ); |
|
break; |
|
} |
|
} |
|
} |
|
// Render the rest on the top (For menus, etc.) |
|
foreach( const ShadowData &d, thisTime ) |
|
drawShadow( d.w, d.mask, d.region.subtracted( d.clip ), d.data ); |
|
shadowDatas = newShadowDatas; |
|
} |
|
|
|
// Modified version of SceneOpenGL::Window::prepareRenderStates() from scene_opengl.cpp |
|
void ShadowEffect::prepareRenderStates( GLTexture *texture, double opacity, double brightness, double saturation ) |
|
{ |
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING |
|
// setup blending of transparent windows |
|
glPushAttrib( GL_ENABLE_BIT ); |
|
/*if( saturation != 1.0 && texture->saturationSupported() ) |
|
{ |
|
// First we need to get the color from [0; 1] range to [0.5; 1] range |
|
glActiveTexture( GL_TEXTURE0 ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); |
|
const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5 }; |
|
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant ); |
|
texture->bind(); |
|
|
|
// Then we take dot product of the result of previous pass and |
|
// saturation_constant. This gives us completely unsaturated |
|
// (greyscale) image |
|
// Note that both operands have to be in range [0.5; 1] since opengl |
|
// automatically substracts 0.5 from them |
|
glActiveTexture( GL_TEXTURE1 ); |
|
float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation }; |
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); |
|
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant ); |
|
texture->bind(); |
|
|
|
// Finally we need to interpolate between the original image and the |
|
// greyscale image to get wanted level of saturation |
|
glActiveTexture( GL_TEXTURE2 ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); |
|
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant ); |
|
// Also replace alpha by primary color's alpha here |
|
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); |
|
// And make primary color contain the wanted opacity |
|
glColor4f( opacity, opacity, opacity, opacity ); |
|
texture->bind(); |
|
|
|
if( brightness != 1.0 ) |
|
{ |
|
glActiveTexture( GL_TEXTURE3 ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); |
|
// The color has to be multiplied by both opacity and brightness |
|
float opacityByBrightness = opacity * brightness; |
|
glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); |
|
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); |
|
texture->bind(); |
|
} |
|
|
|
glActiveTexture(GL_TEXTURE0 ); |
|
} |
|
else*/ if( opacity != 1.0 || brightness != 1.0 ) |
|
{ |
|
// the window is additionally configured to have its opacity adjusted, |
|
// do it |
|
float opacityByBrightness = opacity * brightness; |
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); |
|
glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity); |
|
} |
|
#endif |
|
} |
|
|
|
// Modified version of SceneOpenGL::Window::restoreRenderStates() from scene_opengl.cpp |
|
void ShadowEffect::restoreRenderStates( GLTexture *texture, double opacity, double brightness, double saturation ) |
|
{ |
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING |
|
if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0 ) |
|
{ |
|
/*if( saturation != 1.0 && texture->saturationSupported()) |
|
{ |
|
glActiveTexture(GL_TEXTURE3); |
|
glDisable( texture->target() ); |
|
glActiveTexture(GL_TEXTURE2); |
|
glDisable( texture->target() ); |
|
glActiveTexture(GL_TEXTURE1); |
|
glDisable( texture->target() ); |
|
glActiveTexture(GL_TEXTURE0); |
|
}*/ |
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); |
|
glColor4f( 0, 0, 0, 0 ); |
|
} |
|
|
|
glPopAttrib(); // ENABLE_BIT |
|
#endif |
|
} |
|
|
|
void ShadowEffect::drawShadowQuadOpenGL( GLTexture *texture, QVector<float> verts, QVector<float> texCoords, |
|
QColor color, QRegion region, float opacity, float brightness, float saturation ) |
|
{ |
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING |
|
if( color.isValid() ) |
|
glColor4f( color.redF(), color.greenF(), color.blueF(), 1.0 ); |
|
else |
|
glColor4f( 1.0, 1.0, 1.0, 1.0 ); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
|
prepareRenderStates( texture, opacity, brightness, saturation ); |
|
texture->bind(); |
|
texture->enableNormalizedTexCoords(); |
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); |
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); |
|
renderGLGeometry( region, 4, verts.data(), texCoords.data() ); |
|
texture->disableNormalizedTexCoords(); |
|
texture->unbind(); |
|
restoreRenderStates( texture, opacity, brightness, saturation ); |
|
#endif |
|
} |
|
|
|
void ShadowEffect::drawShadowQuadXRender( XRenderPicture *picture, QRect rect, float xScale, float yScale, |
|
QColor color, float opacity, float brightness, float saturation ) |
|
{ |
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
XRenderColor xc; |
|
if( color.isValid() ) |
|
xc = preMultiply( color, opacity ); |
|
else |
|
xc = preMultiply( QColor( 255, 255, 255 ), opacity ); |
|
XRenderPicture fill = xRenderFill( &xc ); |
|
|
|
// Scale if required |
|
if( xScale != 1.0 || yScale != 1.0 ) |
|
{ |
|
XTransform xform = {{ |
|
{ XDoubleToFixed( 1.0 / xScale ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) }, |
|
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 / yScale ), XDoubleToFixed( 0.0 ) }, |
|
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) } |
|
}}; |
|
XRenderSetPictureTransform( display(), *picture, &xform ); |
|
} |
|
|
|
// Render it |
|
// TODO: This always uses the fast filter, detect when to use smooth instead |
|
if( color.isValid() ) |
|
XRenderComposite( display(), PictOpOver, fill, *picture, effects->xrenderBufferPicture(), 0, 0, 0, 0, |
|
rect.x(), rect.y(), rect.width(), rect.height() ); |
|
else |
|
XRenderComposite( display(), PictOpOver, *picture, fill, effects->xrenderBufferPicture(), 0, 0, 0, 0, |
|
rect.x(), rect.y(), rect.width(), rect.height() ); |
|
|
|
// Fake brightness by overlaying black |
|
// Cannot use XRenderFillRectangle() due to ARGB |
|
XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - brightness ) * opacity }; |
|
fill = xRenderFill( &col ); |
|
XRenderComposite( display(), PictOpOver, fill, *picture, effects->xrenderBufferPicture(), 0, 0, 0, 0, |
|
rect.x(), rect.y(), rect.width(), rect.height() ); |
|
|
|
// Return to scale to 1.0 |
|
if( xScale != 1.0 || yScale != 1.0 ) |
|
{ |
|
XTransform xform = {{ |
|
{ XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) }, |
|
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ) }, |
|
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) } |
|
}}; |
|
XRenderSetPictureTransform( display(), *picture, &xform ); |
|
} |
|
#endif |
|
} |
|
|
|
void ShadowEffect::drawShadow( EffectWindow* window, int mask, QRegion region, const WindowPaintData& data ) |
|
{ |
|
// Don't allow windows to cast shadows on other displays |
|
QRegion clipperGeom; |
|
for( int screen = 0; screen < effects->numScreens(); screen++ ) |
|
{ |
|
QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); |
|
if( !( window->geometry() & screenGeom ).isNull() ) |
|
clipperGeom |= screenGeom; |
|
} |
|
PaintClipper pc( clipperGeom ); |
|
|
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING |
|
if( effects->compositingType() == OpenGLCompositing ) |
|
{ |
|
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT ); |
|
glEnable( GL_BLEND ); |
|
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); |
|
|
|
foreach( const WindowQuad &quad, data.quads ) |
|
{ |
|
if( !mShadowQuadTypes.contains( quad.type() ) && quad.type() != mDefaultShadowQuadType ) |
|
continue; // Not a shadow quad |
|
|
|
glPushMatrix(); |
|
|
|
// Use the window's top-left as the origin |
|
glTranslatef( window->x(), window->y(), 0 ); |
|
if( mask & PAINT_WINDOW_TRANSFORMED ) |
|
glTranslatef( data.xTranslate, data.yTranslate, data.zTranslate ); |
|
if(( mask & PAINT_WINDOW_TRANSFORMED ) && ( data.xScale != 1 || data.yScale != 1 )) |
|
glScalef( data.xScale, data.yScale, data.zScale ); |
|
if(( mask & PAINT_WINDOW_TRANSFORMED ) && data.rotation ) |
|
{ |
|
glTranslatef( data.rotation->xRotationPoint, |
|
data.rotation->yRotationPoint, |
|
data.rotation->zRotationPoint ); |
|
float xAxis = 0.0; |
|
float yAxis = 0.0; |
|
float zAxis = 0.0; |
|
switch( data.rotation->axis ) |
|
{ |
|
case RotationData::XAxis: |
|
xAxis = 1.0; |
|
break; |
|
case RotationData::YAxis: |
|
yAxis = 1.0; |
|
break; |
|
case RotationData::ZAxis: |
|
zAxis = 1.0; |
|
break; |
|
} |
|
glRotatef( data.rotation->angle, xAxis, yAxis, zAxis ); |
|
glTranslatef( -data.rotation->xRotationPoint, |
|
-data.rotation->yRotationPoint, |
|
-data.rotation->zRotationPoint ); |
|
} |
|
|
|
// Create our polygon |
|
QVector<float> verts, texcoords; |
|
verts.reserve(8); |
|
texcoords.reserve(8); |
|
verts << quad[0].x() << quad[0].y(); |
|
verts << quad[1].x() << quad[1].y(); |
|
verts << quad[2].x() << quad[2].y(); |
|
verts << quad[3].x() << quad[3].y(); |
|
texcoords << quad[0].textureX() << quad[0].textureY(); |
|
texcoords << quad[1].textureX() << quad[1].textureY(); |
|
texcoords << quad[2].textureX() << quad[2].textureY(); |
|
texcoords << quad[3].textureX() << quad[3].textureY(); |
|
|
|
// Work out which texture to use |
|
int texture = mShadowQuadTypes.indexOf( quad.type() ); |
|
if( texture != -1 && texture < mShadowTextures.size() ) // TODO: Needed? |
|
{ |
|
// Render it! |
|
// Cheat a little, assume the active and inactive shadows have identical quads |
|
if( effects->hasDecorationShadows() ) |
|
{ |
|
if( window->hasDecoration() && |
|
effects->shadowTextureList( ShadowBorderedActive ) == texture ) |
|
{ // Decorated windows |
|
// Active shadow |
|
drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ), |
|
verts, texcoords, QColor(), region, |
|
data.opacity * window->shadowOpacity( ShadowBorderedActive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderedActive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderedActive )); |
|
|
|
// Inactive shadow |
|
texture = effects->shadowTextureList( ShadowBorderedInactive ); |
|
drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ), |
|
verts, texcoords, QColor(), region, |
|
data.opacity * window->shadowOpacity( ShadowBorderedInactive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderedInactive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderedInactive )); |
|
} |
|
else if( effects->shadowTextureList( ShadowBorderlessActive ) == texture ) |
|
{ // Decoration-less normal windows |
|
if( effects->activeWindow() == window ) |
|
{ |
|
drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ), |
|
verts, texcoords, QColor(), region, |
|
data.opacity * window->shadowOpacity( ShadowBorderlessActive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderlessActive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderlessActive )); |
|
} |
|
else |
|
{ |
|
texture = effects->shadowTextureList( ShadowBorderlessInactive ); |
|
drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ), |
|
verts, texcoords, QColor(), region, |
|
data.opacity * window->shadowOpacity( ShadowBorderlessInactive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderlessInactive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderlessInactive )); |
|
} |
|
} |
|
else |
|
{ // Other windows |
|
drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ), |
|
verts, texcoords, QColor(), region, |
|
data.opacity * window->shadowOpacity( ShadowOther ), |
|
data.brightness * window->shadowBrightness( ShadowOther ), |
|
data.saturation * window->shadowSaturation( ShadowOther )); |
|
} |
|
} |
|
} |
|
if( quad.type() == mDefaultShadowQuadType ) |
|
{ // Default shadow |
|
float opacity = shadowOpacity; |
|
if( intensifyActiveShadow && window == effects->activeWindow() ) |
|
opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity ); |
|
|
|
drawShadowQuadOpenGL( mDefaultShadowTextures.at( quad.id() ), |
|
verts, texcoords, shadowColor, region, |
|
data.opacity * opacity, |
|
data.brightness, |
|
data.saturation ); |
|
} |
|
|
|
glPopMatrix(); |
|
} |
|
|
|
glPopAttrib(); |
|
} |
|
#endif |
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
if( effects->compositingType() == XRenderCompositing ) |
|
{ |
|
XRenderSetPictureClipRegion( display(), effects->xrenderBufferPicture(), region.handle() ); |
|
|
|
foreach( const WindowQuad &quad, data.quads ) |
|
{ |
|
if( !mShadowQuadTypes.contains( quad.type() ) && quad.type() != mDefaultShadowQuadType ) |
|
continue; // Not a shadow quad |
|
|
|
// Determine transformed quad position |
|
QRect windowRect = window->geometry(); |
|
float xScale = 1.0; |
|
float yScale = 1.0; |
|
float xTranslate = 0.0; |
|
float yTranslate = 0.0; |
|
if( mask & PAINT_SCREEN_TRANSFORMED) |
|
{ |
|
xScale = gScreenData.xScale; |
|
yScale = gScreenData.yScale; |
|
xTranslate += ( xScale - 1.0 ) * windowRect.x() + gScreenData.xTranslate; |
|
yTranslate += ( yScale - 1.0 ) * windowRect.y() + gScreenData.yTranslate; |
|
} |
|
if( mask & PAINT_WINDOW_TRANSFORMED) |
|
{ |
|
xTranslate += xScale * data.xTranslate; |
|
yTranslate += yScale * data.yTranslate; |
|
xScale *= data.xScale; |
|
yScale *= data.yScale; |
|
} |
|
QRect quadRect( |
|
window->x() + quad[0].x() * xScale + xTranslate, |
|
window->y() + quad[0].y() * yScale + yTranslate, |
|
( quad[2].x() - quad[0].x() ) * xScale, |
|
( quad[2].y() - quad[0].y() ) * yScale ); |
|
|
|
// Work out which texture to use |
|
int texture = mShadowQuadTypes.indexOf( quad.type() ); |
|
if( texture != -1 ) |
|
{ |
|
// Render it! |
|
// Cheat a little, assume the active and inactive shadows have identical quads |
|
if( effects->hasDecorationShadows() ) |
|
{ |
|
if( window->hasDecoration() && |
|
effects->shadowTextureList( ShadowBorderedActive ) == texture ) |
|
{ // Decorated windows |
|
// Active shadow |
|
drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, |
|
xScale, yScale, QColor(), |
|
data.opacity * window->shadowOpacity( ShadowBorderedActive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderedActive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderedActive )); |
|
|
|
// Inactive shadow |
|
texture = effects->shadowTextureList( ShadowBorderedInactive ); |
|
drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, |
|
xScale, yScale, QColor(), |
|
data.opacity * window->shadowOpacity( ShadowBorderedInactive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderedInactive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderedInactive )); |
|
} |
|
else if( effects->shadowTextureList( ShadowBorderlessActive ) == texture ) |
|
{ // Decoration-less normal windows |
|
if( effects->activeWindow() == window ) |
|
{ // Active shadow |
|
drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, |
|
xScale, yScale, QColor(), |
|
data.opacity * window->shadowOpacity( ShadowBorderlessActive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderlessActive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderlessActive )); |
|
} |
|
else |
|
{ // Inactive shadow |
|
texture = effects->shadowTextureList( ShadowBorderedInactive ); |
|
drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, |
|
xScale, yScale, QColor(), |
|
data.opacity * window->shadowOpacity( ShadowBorderlessInactive ), |
|
data.brightness * window->shadowBrightness( ShadowBorderlessInactive ), |
|
data.saturation * window->shadowSaturation( ShadowBorderlessInactive )); |
|
} |
|
} |
|
else |
|
{ // Other windows |
|
drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, |
|
xScale, yScale, QColor(), |
|
data.opacity * window->shadowOpacity( ShadowOther ), |
|
data.brightness * window->shadowBrightness( ShadowOther ), |
|
data.saturation * window->shadowSaturation( ShadowOther )); |
|
} |
|
} |
|
} |
|
if( quad.type() == mDefaultShadowQuadType ) |
|
{ // Default shadow |
|
float opacity = shadowOpacity; |
|
if( intensifyActiveShadow && window == effects->activeWindow() ) |
|
opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity ); |
|
|
|
drawShadowQuadXRender( mDefaultShadowPics.at( quad.id() ), quadRect, xScale, yScale, |
|
shadowColor, opacity * data.opacity, data.brightness, data.saturation ); |
|
} |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
} // namespace |
|
|
|
#include "shadow.h"
|
|
|