51168e97a4
the logout by using new information provided by KSMServer; Allow effects to read and detect root window property notify events. svn path=/trunk/KDE/kdebase/workspace/; revision=1083335
786 lines
33 KiB
C++
786 lines
33 KiB
C++
/********************************************************************
|
|
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 )
|
|
|
|
enum {
|
|
ShadowOverrideRole = 0x22A982D1
|
|
};
|
|
|
|
ShadowEffect::ShadowEffect()
|
|
: shadowSize( 0 )
|
|
{
|
|
cachedBlendPicture = X::None;
|
|
reconfigure( ReconfigureAll );
|
|
connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
|
|
this, SLOT(updateShadowColor()));
|
|
shadowOverride = XInternAtom( display(), "_KDE_SHADOW_OVERRIDE", False );
|
|
effects->registerPropertyType( shadowOverride, true );
|
|
}
|
|
|
|
ShadowEffect::~ShadowEffect()
|
|
{
|
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
|
|
for( int i = 0; i < mDefaultShadowTextures.size(); i++ )
|
|
delete mDefaultShadowTextures.at( i );
|
|
#endif
|
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
|
|
for( int i = 0; i < mDefaultShadowPics.size(); i++ )
|
|
delete mDefaultShadowPics.at( i );
|
|
#endif
|
|
effects->registerPropertyType( shadowOverride, false );
|
|
}
|
|
|
|
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();
|
|
|
|
// Load decoration shadow related things
|
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
|
|
if( effects->compositingType() == OpenGLCompositing )
|
|
{
|
|
// Delete any other textures in memory
|
|
for( int i = 0; i < mDefaultShadowTextures.size(); i++ )
|
|
delete mDefaultShadowTextures.at( i );
|
|
mDefaultShadowTextures.clear();
|
|
|
|
//-------------------------
|
|
// Create default textures
|
|
|
|
mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister?
|
|
QImage shadowImageSrc( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" ));
|
|
|
|
// Might as well process the shadow color here
|
|
QImage shadowImage( shadowImageSrc.width(), shadowImageSrc.height(), QImage::Format_ARGB32 );
|
|
QPainter painter( &shadowImage );
|
|
//painter.setCompositionMode( QPainter::CompositionMode_Multiply );
|
|
painter.setPen( Qt::NoPen );
|
|
painter.setBrush( shadowColor );
|
|
painter.drawRect( 0, 0, shadowImage.width(), shadowImage.height() );
|
|
painter.end();
|
|
shadowImage.setAlphaChannel( shadowImageSrc.alphaChannel() ); // Cheat, just use the alpha mask
|
|
|
|
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 < mDefaultShadowPics.size(); i++ )
|
|
delete mDefaultShadowPics.at( i );
|
|
mDefaultShadowPics.clear();
|
|
|
|
// 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 < mDefaultShadowPics.size(); i++ )
|
|
XRenderChangePicture( display(), *mDefaultShadowPics.at( i ), CPRepeat, &pa );
|
|
}
|
|
#endif
|
|
|
|
// Force rebuild of all quads to clear their caches
|
|
foreach( EffectWindow *w, effects->stackingOrder() )
|
|
if( w ) // TODO/HACK: stackingOrder() should NOT return NULL
|
|
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
|
|
{
|
|
Q_UNUSED( w );
|
|
int shadowGrow = shadowFuzzyness + shadowSize;
|
|
return windowRectangle.adjusted( shadowXOffset - shadowGrow, shadowYOffset - shadowGrow,
|
|
shadowXOffset + shadowGrow, shadowYOffset + shadowGrow);
|
|
}
|
|
|
|
#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 | PAINT_DECORATION_ONLY ));
|
|
|
|
QRegion rgn = region;
|
|
if ( w->keepAbove() )
|
|
{
|
|
QRegion shape = w->shape().translated( w->x(), w->y());
|
|
if (optimize)
|
|
{
|
|
rgn -= shape;
|
|
QList<ShadowData>::iterator i;
|
|
for( i = shadowDatas.begin(); i != shadowDatas.end(); ++i )
|
|
(*i).clip |= shape;
|
|
}
|
|
optimize = false;
|
|
}
|
|
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 ) && (!(mask & PAINT_DECORATION_ONLY) || w->keepAbove()) )
|
|
{
|
|
if( !optimize )
|
|
{
|
|
// For translucent windows, shadow needs to be drawn before the
|
|
// window itself.
|
|
drawShadow( w, mask, rgn, 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 )
|
|
{
|
|
// 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 );
|
|
|
|
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::propertyNotify( EffectWindow* w, long atom )
|
|
{
|
|
if( !w || atom != shadowOverride )
|
|
return;
|
|
const QByteArray value = w->readProperty( atom, atom, 32 );
|
|
w->setData( ShadowOverrideRole, !value.isNull() );
|
|
effects->addRepaint( shadowRectangle( w, w->geometry() ));
|
|
}
|
|
|
|
void ShadowEffect::windowAdded( EffectWindow* c )
|
|
{
|
|
const QByteArray value = c->readProperty( shadowOverride, shadowOverride, 32 );
|
|
c->setData( ShadowOverrideRole, !value.isNull() );
|
|
}
|
|
|
|
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())
|
|
// Ignore windows that draw their own shadows
|
|
&& !w->data( ShadowOverrideRole ).toBool()
|
|
// If decoration has it's own shadow leave it alone
|
|
&& !( w->hasDecoration() && effects->hasDecorationShadows() )
|
|
&& !( w->windowClass() == "ksmserver ksmserver" &&
|
|
( w->windowRole() == "logoutdialog" || w->windowRole() == "logouteffect" ));
|
|
}
|
|
|
|
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 )
|
|
{
|
|
Q_UNUSED( texture );
|
|
Q_UNUSED( 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
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
glColor4f( brightness, brightness, brightness, opacity );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Modified version of SceneOpenGL::Window::restoreRenderStates() from scene_opengl.cpp
|
|
void ShadowEffect::restoreRenderStates( GLTexture *texture, double opacity, double brightness, double saturation )
|
|
{
|
|
Q_UNUSED( texture );
|
|
#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, 0.0, 0.0 );
|
|
}
|
|
|
|
glPopAttrib(); // ENABLE_BIT
|
|
#endif
|
|
}
|
|
|
|
void ShadowEffect::drawShadowQuadOpenGL( GLTexture *texture, QVector<float> verts, QVector<float> texCoords,
|
|
QRegion region, float opacity, float brightness, float saturation, GLShader* shader )
|
|
{
|
|
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
|
|
glColor4f( 1.0, 1.0, 1.0, 1.0 );
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
|
|
prepareRenderStates( texture, opacity, brightness, saturation ); // Usually overrides the above
|
|
if( shader )
|
|
{
|
|
shader->setUniform("opacity", opacity);
|
|
shader->setUniform("saturation", saturation);
|
|
shader->setUniform("brightness", brightness);
|
|
shader->setUniform("textureWidth", 1.0f);
|
|
shader->setUniform("textureHeight", 1.0f);
|
|
}
|
|
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 )
|
|
{
|
|
Q_UNUSED( saturation );
|
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
|
|
XRenderPicture fill;
|
|
if( color.isValid() )
|
|
{
|
|
if ( color == cachedColor && cachedBlendPicture != X::None )
|
|
fill = cachedBlendPicture;
|
|
else
|
|
{
|
|
XRenderColor xc = preMultiply( color, opacity );
|
|
cachedBlendPicture = fill = xRenderFill( &xc );
|
|
cachedColor = color;
|
|
}
|
|
}
|
|
else
|
|
fill = xRenderBlendPicture(opacity);
|
|
|
|
// 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
|
|
fill = xRenderBlendPicture(( 1 - brightness ) * opacity);
|
|
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( 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
|
|
float opacity = shadowOpacity;
|
|
if( intensifyActiveShadow && window == effects->activeWindow() )
|
|
opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity );
|
|
|
|
drawShadowQuadOpenGL( mDefaultShadowTextures.at( quad.id() ),
|
|
verts, texcoords, region,
|
|
data.opacity * opacity,
|
|
data.brightness,
|
|
data.saturation,
|
|
data.shader);
|
|
|
|
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( 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
|
|
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
|
|
|