not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / effects / shadow.cpp
blob967353ee70fdfd188894c5858e550de1692a41f0
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
6 Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.com>
7 Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *********************************************************************/
23 #include "shadow.h"
24 #include "shadow_helper.h"
26 #include <kwinglutils.h>
28 #include <kconfiggroup.h>
29 #include <kdebug.h>
30 #include <KStandardDirs>
31 #include <kcolorscheme.h>
32 #include <KGlobalSettings>
34 #include <cmath>
36 namespace KWin
39 KWIN_EFFECT( shadow, ShadowEffect )
41 ShadowEffect::ShadowEffect()
42 : shadowSize( 0 )
44 reconfigure( ReconfigureAll );
45 connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
46 this, SLOT(updateShadowColor()));
49 ShadowEffect::~ShadowEffect()
51 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
52 for( int i = 0; i < mShadowTextures.size(); i++ )
53 for( int j = 0; j < mShadowTextures.at( i ).size(); j++ )
54 delete mShadowTextures.at( i ).at( j );
55 for( int i = 0; i < mDefaultShadowTextures.size(); i++ )
56 delete mDefaultShadowTextures.at( i );
57 #endif
58 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
59 for( int i = 0; i < mShadowPics.size(); i++ )
60 for( int j = 0; j < mShadowPics.at( i ).size(); j++ )
61 delete mShadowPics.at( i ).at( j );
62 for( int i = 0; i < mDefaultShadowPics.size(); i++ )
63 delete mDefaultShadowPics.at( i );
64 #endif
67 void ShadowEffect::reconfigure( ReconfigureFlags )
69 KConfigGroup conf = effects->effectConfig("Shadow");
70 shadowXOffset = conf.readEntry( "XOffset", 0 );
71 shadowYOffset = conf.readEntry( "YOffset", 3 );
72 shadowOpacity = conf.readEntry( "Opacity", 0.25 );
73 shadowFuzzyness = conf.readEntry( "Fuzzyness", 10 );
74 shadowSize = conf.readEntry( "Size", 5 );
75 intensifyActiveShadow = conf.readEntry( "IntensifyActiveShadow", true );
76 updateShadowColor();
77 forceDecorated = conf.readEntry( "forceDecoratedToDefault", false );
78 forceUndecorated = conf.readEntry( "forceUndecoratedToDefault", false );
79 forceOther = conf.readEntry( "forceOtherToDefault", false );
81 // Load decoration shadow related things
82 bool reconfiguring = false;
83 if( mShadowQuadTypes.count() )
84 reconfiguring = true;
85 mShadowQuadTypes.clear(); // Changed decoration? TODO: Unregister?
86 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
87 if( effects->compositingType() == OpenGLCompositing )
89 // Delete any other textures in memory
90 for( int i = 0; i < mShadowTextures.size(); i++ )
91 for( int j = 0; j < mShadowTextures.at( i ).size(); j++ )
92 delete mShadowTextures.at( i ).at( j );
93 mShadowTextures.clear();
94 for( int i = 0; i < mDefaultShadowTextures.size(); i++ )
95 delete mDefaultShadowTextures.at( i );
96 mDefaultShadowTextures.clear();
98 // Create decoration shadows
99 if( effects->hasDecorationShadows() )
101 QList< QList<QImage> > shadowImages = effects->shadowTextures();
102 for( int i = 0; i < shadowImages.size(); i++ )
104 mShadowQuadTypes.append( effects->newWindowQuadType() );
105 QList<GLTexture*> textures;
106 for( int j = 0; j < shadowImages.at( i ).size(); j++ )
107 textures.append( new GLTexture( shadowImages.at( i ).at( j )));
108 mShadowTextures.append( textures );
112 // Create default textures
113 mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister?
114 QImage shadowImage( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" ));
115 int hw = shadowImage.width() / 2;
116 int hh = shadowImage.height() / 2;
117 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, 0, hw, hh )));
118 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, 0, 1, hh )));
119 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, 0, hw, hh )));
120 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, hh, hw, 1 )));
121 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, 1, 1 )));
122 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, hw, 1 )));
123 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, hh, hw, hh )));
124 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, 1, hh )));
125 mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, hw, hh )));
127 #endif
128 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
129 if( effects->compositingType() == XRenderCompositing )
131 // Delete any other pictures in memory
132 for( int i = 0; i < mShadowPics.size(); i++ )
133 for( int j = 0; j < mShadowPics.at( i ).size(); j++ )
134 delete mShadowPics.at( i ).at( j );
135 mShadowPics.clear();
136 for( int i = 0; i < mDefaultShadowPics.size(); i++ )
137 delete mDefaultShadowPics.at( i );
138 mDefaultShadowPics.clear();
140 // Create decoration pictures
141 if( effects->hasDecorationShadows() )
143 QList< QList<QImage> > shadowImages = effects->shadowTextures();
144 for( int i = 0; i < shadowImages.size(); i++ )
146 mShadowQuadTypes.append( effects->newWindowQuadType() );
147 QList<XRenderPicture*> pictures;
148 for( int j = 0; j < shadowImages.at( i ).size(); j++ )
149 pictures.append( new XRenderPicture( QPixmap::fromImage( shadowImages.at( i ).at( j ))));
150 mShadowPics.append( pictures );
154 // Create default pictures
155 mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister?
156 QPixmap shadowPixmap( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" ));
157 shadowPixmap = shadowPixmap.scaled( QSize( shadowFuzzyness * 4, shadowFuzzyness * 4 ),
158 Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
159 int hw = shadowPixmap.width() / 2;
160 int hh = shadowPixmap.height() / 2;
161 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, 0, hw, hh )));
162 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, 0, 1, hh )));
163 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, 0, hw, hh )));
164 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, hh, hw, 1 )));
165 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, 1, 1 )));
166 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, hw, 1 )));
167 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, hh, hw, hh )));
168 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, 1, hh )));
169 mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, hw, hh )));
171 // Apply repeat attribute to all pictures
172 XRenderPictureAttributes pa;
173 pa.repeat = true;
174 for( int i = 0; i < mShadowPics.size(); i++ )
175 for( int j = 0; j < mShadowPics.at( i ).size(); j++ )
176 XRenderChangePicture( display(), *mShadowPics.at( i ).at( j ), CPRepeat, &pa );
177 for( int i = 0; i < mDefaultShadowPics.size(); i++ )
178 XRenderChangePicture( display(), *mDefaultShadowPics.at( i ), CPRepeat, &pa );
180 #endif
182 if( reconfiguring )
183 { // Force rebuild of all quads to clear their caches
184 foreach( EffectWindow *w, effects->stackingOrder() )
185 w->buildQuads( true );
189 void ShadowEffect::updateShadowColor()
191 KConfigGroup conf = effects->effectConfig("Shadow");
192 shadowColor = conf.readEntry( "Color", schemeShadowColor() );
195 QRect ShadowEffect::shadowRectangle( EffectWindow* w, const QRect& windowRectangle ) const
197 QRectF shadowRect;
198 bool shadowDefined = false;
199 if( effects->hasDecorationShadows() )
200 { // TODO: This function is called for EVERY damage, we need to cache this
201 // (But how? Decoration shadow can change shape if it wanted to)
202 if( w->hasDecoration() && !forceDecorated )
203 { // Decorated windows must be normal windows
204 foreach( const QRect &r, w->shadowQuads( ShadowBorderedActive ))
206 shadowDefined = true;
207 shadowRect |= r;
210 else if( w->isNormalWindow() && !forceUndecorated )
211 { // No decoration on a normal window
212 foreach( const QRect &r, w->shadowQuads( ShadowBorderlessActive ))
214 shadowDefined = true;
215 shadowRect |= r;
218 else if( !forceOther )
219 { // All other undecorated windows
220 foreach( const QRect &r, w->shadowQuads( ShadowOther ))
222 shadowDefined = true;
223 shadowRect |= r;
227 if( !shadowDefined )
229 int shadowGrow = shadowFuzzyness + shadowSize;
230 return windowRectangle.adjusted( shadowXOffset - shadowGrow, shadowYOffset - shadowGrow,
231 shadowXOffset + shadowGrow, shadowYOffset + shadowGrow);
233 return windowRectangle.adjusted(
234 qMin( shadowRect.x(), qreal(0.0) ),
235 qMin( shadowRect.y(), qreal(0.0) ),
236 qMax( shadowRect.x() + shadowRect.width() - w->width(), qreal(0.0) ),
237 qMax( shadowRect.y() + shadowRect.height() - w->height(), qreal(0.0) )
241 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
242 static ScreenPaintData gScreenData;
243 #endif
245 void ShadowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
247 shadowDatas.clear();
248 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
249 if ((mask & PAINT_SCREEN_TRANSFORMED) &&
250 (effects->compositingType() == XRenderCompositing)) // TODO: copy constructor?
252 gScreenData.xTranslate = data.xTranslate;
253 gScreenData.yTranslate = data.yTranslate;
254 gScreenData.xScale = data.xScale;
255 gScreenData.yScale = data.yScale;
257 #endif
258 // Draw windows
259 effects->paintScreen( mask, region, data );
261 // Draw shadows
262 drawQueuedShadows( 0 );
265 void ShadowEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
267 if( useShadow( w ))
268 data.paint |= shadowRectangle( w, data.paint.boundingRect() );
269 effects->prePaintWindow( w, data, time );
272 void ShadowEffect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
274 // Whether the shadow drawing can be delayed or not.
275 bool optimize = !( mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED |
276 PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_WINDOW_TRANSLUCENT ));
277 if( !optimize )
279 // Transformed or translucent windows are drawn bottom-to-top, so
280 // first we need to draw all queued shadows.
281 drawQueuedShadows( w );
283 if( useShadow( w ))
285 if( !optimize )
287 // For translucent windows, shadow needs to be drawn before the
288 // window itself.
289 drawShadow( w, mask, region, data );
291 else
293 // For opaque windows, just schedule the shadow to be drawn later
294 ShadowData d(w, data);
295 d.clip = w->shape().translated( w->x(), w->y());
296 if( !shadowDatas.isEmpty())
297 d.clip |= shadowDatas.last().clip;
298 d.mask = mask;
299 foreach( const QRect &r, region.rects() )
300 d.region |= shadowRectangle( w, r );
301 d.region &= region;
302 shadowDatas.append(d);
306 effects->drawWindow( w, mask, region, data );
309 void ShadowEffect::buildQuads( EffectWindow* w, WindowQuadList& quadList )
311 bool shadowDefined = false;
312 if( effects->hasDecorationShadows() )
314 // TODO: shadowQuads() is allowed to return different quads for
315 // active and inactive shadows. Is implementing it worth
316 // the performance drop?
317 int id = 0;
318 if( w->hasDecoration() && !forceDecorated )
319 { // Decorated windows must be normal windows
320 foreach( const QRect &r, w->shadowQuads( ShadowBorderedActive ))
322 shadowDefined = true;
323 WindowQuad quad( mShadowQuadTypes.at( effects->shadowTextureList( ShadowBorderedActive )), id++ );
324 quad[ 0 ] = WindowVertex( r.x(), r.y(), 0, 1 );
325 quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), 1, 1 );
326 quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), 1, 0 );
327 quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), 0, 0 );
328 quadList.append( quad );
331 else if( w->isNormalWindow() && !forceUndecorated )
332 { // No decoration on a normal window
333 foreach( const QRect &r, w->shadowQuads( ShadowBorderlessActive ))
335 shadowDefined = true;
336 WindowQuad quad( mShadowQuadTypes.at( effects->shadowTextureList( ShadowBorderlessActive )), id++ );
337 quad[ 0 ] = WindowVertex( r.x(), r.y(), 0, 1 );
338 quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), 1, 1 );
339 quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), 1, 0 );
340 quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), 0, 0 );
341 quadList.append( quad );
344 else if( !forceOther )
345 { // All other undecorated windows
346 foreach( const QRect &r, w->shadowQuads( ShadowOther ))
348 shadowDefined = true;
349 WindowQuad quad( mShadowQuadTypes.at( effects->shadowTextureList( ShadowOther )), id++ );
350 quad[ 0 ] = WindowVertex( r.x(), r.y(), 0, 1 );
351 quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), 1, 1 );
352 quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), 1, 0 );
353 quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), 0, 0 );
354 quadList.append( quad );
358 if( !shadowDefined )
360 //TODO: add config option to not have shadows for menus, etc.
361 // Make our own shadow as the decoration doesn't support it
362 int fuzzy = shadowFuzzyness;
363 // Shadow's size must be a least 2*fuzzy in both directions (or the corners will be broken)
364 int width = qMax( fuzzy * 2, w->width() + 2 * shadowSize );
365 int height = qMax( fuzzy * 2, w->height() + 2 * shadowSize );
366 double x1, y1, x2, y2;
367 int id = 0;
368 // top-left
369 x1 = shadowXOffset - shadowSize + 0 - fuzzy;
370 y1 = shadowYOffset - shadowSize + 0 - fuzzy;
371 x2 = shadowXOffset - shadowSize + 0 + fuzzy;
372 y2 = shadowYOffset - shadowSize + 0 + fuzzy;
373 WindowQuad topLeftQuad( mDefaultShadowQuadType, id++ );
374 topLeftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
375 topLeftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
376 topLeftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
377 topLeftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
378 quadList.append( topLeftQuad );
379 // top
380 x1 = shadowXOffset - shadowSize + 0 + fuzzy;
381 y1 = shadowYOffset - shadowSize + 0 - fuzzy;
382 x2 = shadowXOffset - shadowSize + width - fuzzy;
383 y2 = shadowYOffset - shadowSize + 0 + fuzzy;
384 WindowQuad topQuad( mDefaultShadowQuadType, id++ );
385 topQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
386 topQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
387 topQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
388 topQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
389 quadList.append( topQuad );
390 // top-right
391 x1 = shadowXOffset - shadowSize + width - fuzzy;
392 y1 = shadowYOffset - shadowSize + 0 - fuzzy;
393 x2 = shadowXOffset - shadowSize + width + fuzzy;
394 y2 = shadowYOffset - shadowSize + 0 + fuzzy;
395 WindowQuad topRightQuad( mDefaultShadowQuadType, id++ );
396 topRightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
397 topRightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
398 topRightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
399 topRightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
400 quadList.append( topRightQuad );
401 // left
402 x1 = shadowXOffset - shadowSize + 0 - fuzzy;
403 y1 = shadowYOffset - shadowSize + 0 + fuzzy;
404 x2 = shadowXOffset - shadowSize + 0 + fuzzy;
405 y2 = shadowYOffset - shadowSize + height - fuzzy;
406 WindowQuad leftQuad( mDefaultShadowQuadType, id++ );
407 leftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
408 leftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
409 leftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
410 leftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
411 quadList.append( leftQuad );
412 // center
413 x1 = shadowXOffset - shadowSize + 0 + fuzzy;
414 y1 = shadowYOffset - shadowSize + 0 + fuzzy;
415 x2 = shadowXOffset - shadowSize + width - fuzzy;
416 y2 = shadowYOffset - shadowSize + height - fuzzy;
417 WindowQuad contentsQuad( mDefaultShadowQuadType, id++ );
418 contentsQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
419 contentsQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
420 contentsQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
421 contentsQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
422 quadList.append( contentsQuad );
423 // right
424 x1 = shadowXOffset - shadowSize + width - fuzzy;
425 y1 = shadowYOffset - shadowSize + 0 + fuzzy;
426 x2 = shadowXOffset - shadowSize + width + fuzzy;
427 y2 = shadowYOffset - shadowSize + height - fuzzy;
428 WindowQuad rightQuad( mDefaultShadowQuadType, id++ );
429 rightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
430 rightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
431 rightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
432 rightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
433 quadList.append( rightQuad );
434 // bottom-left
435 x1 = shadowXOffset - shadowSize + 0 - fuzzy;
436 y1 = shadowYOffset - shadowSize + height - fuzzy;
437 x2 = shadowXOffset - shadowSize + 0 + fuzzy;
438 y2 = shadowYOffset - shadowSize + height + fuzzy;
439 WindowQuad bottomLeftQuad( mDefaultShadowQuadType, id++ );
440 bottomLeftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
441 bottomLeftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
442 bottomLeftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
443 bottomLeftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
444 quadList.append( bottomLeftQuad );
445 // bottom
446 x1 = shadowXOffset - shadowSize + 0 + fuzzy;
447 y1 = shadowYOffset - shadowSize + height - fuzzy;
448 x2 = shadowXOffset - shadowSize + width - fuzzy;
449 y2 = shadowYOffset - shadowSize + height + fuzzy;
450 WindowQuad bottomQuad( mDefaultShadowQuadType, id++ );
451 bottomQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
452 bottomQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
453 bottomQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
454 bottomQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
455 quadList.append( bottomQuad );
456 // bottom-right
457 x1 = shadowXOffset - shadowSize + width - fuzzy;
458 y1 = shadowYOffset - shadowSize + height - fuzzy;
459 x2 = shadowXOffset - shadowSize + width + fuzzy;
460 y2 = shadowYOffset - shadowSize + height + fuzzy;
461 WindowQuad bottomRightQuad( mDefaultShadowQuadType, id++ );
462 bottomRightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
463 bottomRightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
464 bottomRightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
465 bottomRightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
466 quadList.append( bottomRightQuad );
467 } // This is called for menus, tooltips, windows where the user has disabled borders and shaped windows
469 effects->buildQuads( w, quadList );
472 QRect ShadowEffect::transformWindowDamage( EffectWindow* w, const QRect& r )
474 if( !useShadow( w ))
475 return effects->transformWindowDamage( w, r );
476 QRect r2 = r | shadowRectangle( w, r );
477 return effects->transformWindowDamage( w, r2 );
480 void ShadowEffect::windowClosed( EffectWindow* c )
482 effects->addRepaint( shadowRectangle( c, c->geometry() ));
485 bool ShadowEffect::useShadow( EffectWindow* w ) const
487 return !w->isDeleted() && !w->isDesktop() && !w->isDock()
488 // popups may have shadow even if shaped, their shape is almost rectangular
489 && ( !w->hasOwnShape() || w->isDropdownMenu() || w->isPopupMenu() || w->isComboBox());
492 void ShadowEffect::addQuadVertices(QVector<float>& verts, float x1, float y1, float x2, float y2) const
494 verts << x1 << y1;
495 verts << x1 << y2;
496 verts << x2 << y2;
497 verts << x2 << y1;
500 void ShadowEffect::drawQueuedShadows( EffectWindow* behindWindow )
502 QList<ShadowData> newShadowDatas;
503 QList<ShadowData> thisTime;
504 EffectWindowList stack = effects->stackingOrder();
505 foreach( const ShadowData &d, shadowDatas )
507 // If behindWindow is given then only render shadows of the windows
508 // that are behind that window.
509 if( !behindWindow || stack.indexOf(d.w) < stack.indexOf(behindWindow))
510 thisTime.append(d);
511 else
512 newShadowDatas.append(d);
514 if( thisTime.count() )
515 { // Render them in stacking order
516 foreach( EffectWindow *w, stack )
517 for( int i = 0; i < thisTime.size(); i++ )
518 { // Cannot use foreach() due to thisTime.removeOne()
519 const ShadowData d = thisTime.at(i);
520 if( d.w == w )
522 drawShadow( d.w, d.mask,
523 d.region.subtracted( d.clip ), d.data );
524 thisTime.removeAt( i );
525 break;
529 // Render the rest on the top (For menus, etc.)
530 foreach( const ShadowData &d, thisTime )
531 drawShadow( d.w, d.mask, d.region.subtracted( d.clip ), d.data );
532 shadowDatas = newShadowDatas;
535 // Modified version of SceneOpenGL::Window::prepareRenderStates() from scene_opengl.cpp
536 void ShadowEffect::prepareRenderStates( GLTexture *texture, double opacity, double brightness, double saturation )
538 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
539 // setup blending of transparent windows
540 glPushAttrib( GL_ENABLE_BIT );
541 /*if( saturation != 1.0 && texture->saturationSupported() )
543 // First we need to get the color from [0; 1] range to [0.5; 1] range
544 glActiveTexture( GL_TEXTURE0 );
545 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
546 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
547 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
548 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
549 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
550 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
551 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
552 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
553 const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5 };
554 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant );
555 texture->bind();
557 // Then we take dot product of the result of previous pass and
558 // saturation_constant. This gives us completely unsaturated
559 // (greyscale) image
560 // Note that both operands have to be in range [0.5; 1] since opengl
561 // automatically substracts 0.5 from them
562 glActiveTexture( GL_TEXTURE1 );
563 float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation };
564 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
565 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB );
566 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
567 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
568 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
569 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
570 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
571 texture->bind();
573 // Finally we need to interpolate between the original image and the
574 // greyscale image to get wanted level of saturation
575 glActiveTexture( GL_TEXTURE2 );
576 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
577 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
578 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 );
579 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
580 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS );
581 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
582 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
583 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
584 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
585 // Also replace alpha by primary color's alpha here
586 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
587 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR );
588 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
589 // And make primary color contain the wanted opacity
590 glColor4f( opacity, opacity, opacity, opacity );
591 texture->bind();
593 if( brightness != 1.0 )
595 glActiveTexture( GL_TEXTURE3 );
596 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
597 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
598 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
599 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
600 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR );
601 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
602 // The color has to be multiplied by both opacity and brightness
603 float opacityByBrightness = opacity * brightness;
604 glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity );
605 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
606 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS );
607 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
608 texture->bind();
611 glActiveTexture(GL_TEXTURE0 );
613 else*/ if( opacity != 1.0 || brightness != 1.0 )
615 // the window is additionally configured to have its opacity adjusted,
616 // do it
617 float opacityByBrightness = opacity * brightness;
618 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
619 glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity);
621 #endif
624 // Modified version of SceneOpenGL::Window::restoreRenderStates() from scene_opengl.cpp
625 void ShadowEffect::restoreRenderStates( GLTexture *texture, double opacity, double brightness, double saturation )
627 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
628 if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0 )
630 /*if( saturation != 1.0 && texture->saturationSupported())
632 glActiveTexture(GL_TEXTURE3);
633 glDisable( texture->target() );
634 glActiveTexture(GL_TEXTURE2);
635 glDisable( texture->target() );
636 glActiveTexture(GL_TEXTURE1);
637 glDisable( texture->target() );
638 glActiveTexture(GL_TEXTURE0);
640 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
641 glColor4f( 0, 0, 0, 0 );
644 glPopAttrib(); // ENABLE_BIT
645 #endif
648 void ShadowEffect::drawShadowQuadOpenGL( GLTexture *texture, QVector<float> verts, QVector<float> texCoords,
649 QColor color, QRegion region, float opacity, float brightness, float saturation )
651 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
652 if( color.isValid() )
653 glColor4f( color.redF(), color.greenF(), color.blueF(), 1.0 );
654 else
655 glColor4f( 1.0, 1.0, 1.0, 1.0 );
656 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
657 prepareRenderStates( texture, opacity, brightness, saturation );
658 texture->bind();
659 texture->enableNormalizedTexCoords();
660 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
661 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
662 renderGLGeometry( region, 4, verts.data(), texCoords.data() );
663 texture->disableNormalizedTexCoords();
664 texture->unbind();
665 restoreRenderStates( texture, opacity, brightness, saturation );
666 #endif
669 void ShadowEffect::drawShadowQuadXRender( XRenderPicture *picture, QRect rect, float xScale, float yScale,
670 QColor color, float opacity, float brightness, float saturation )
672 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
673 XRenderColor xc;
674 if( color.isValid() )
675 xc = preMultiply( color, opacity );
676 else
677 xc = preMultiply( QColor( 255, 255, 255 ), opacity );
678 XRenderPicture fill = xRenderFill( &xc );
680 // Scale if required
681 if( xScale != 1.0 || yScale != 1.0 )
683 XTransform xform = {{
684 { XDoubleToFixed( 1.0 / xScale ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) },
685 { XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 / yScale ), XDoubleToFixed( 0.0 ) },
686 { XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) }
688 XRenderSetPictureTransform( display(), *picture, &xform );
691 // Render it
692 // TODO: This always uses the fast filter, detect when to use smooth instead
693 if( color.isValid() )
694 XRenderComposite( display(), PictOpOver, fill, *picture, effects->xrenderBufferPicture(), 0, 0, 0, 0,
695 rect.x(), rect.y(), rect.width(), rect.height() );
696 else
697 XRenderComposite( display(), PictOpOver, *picture, fill, effects->xrenderBufferPicture(), 0, 0, 0, 0,
698 rect.x(), rect.y(), rect.width(), rect.height() );
700 // Fake brightness by overlaying black
701 // Cannot use XRenderFillRectangle() due to ARGB
702 XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - brightness ) * opacity };
703 fill = xRenderFill( &col );
704 XRenderComposite( display(), PictOpOver, fill, *picture, effects->xrenderBufferPicture(), 0, 0, 0, 0,
705 rect.x(), rect.y(), rect.width(), rect.height() );
707 // Return to scale to 1.0
708 if( xScale != 1.0 || yScale != 1.0 )
710 XTransform xform = {{
711 { XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) },
712 { XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ) },
713 { XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) }
715 XRenderSetPictureTransform( display(), *picture, &xform );
717 #endif
720 void ShadowEffect::drawShadow( EffectWindow* window, int mask, QRegion region, const WindowPaintData& data )
722 // Don't allow windows to cast shadows on other displays
723 QRegion clipperGeom;
724 for( int screen = 0; screen < effects->numScreens(); screen++ )
726 QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 );
727 if( !( window->geometry() & screenGeom ).isNull() )
728 clipperGeom |= screenGeom;
730 PaintClipper pc( clipperGeom );
732 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
733 if( effects->compositingType() == OpenGLCompositing )
735 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT );
736 glEnable( GL_BLEND );
737 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
739 foreach( const WindowQuad &quad, data.quads )
741 if( !mShadowQuadTypes.contains( quad.type() ) && quad.type() != mDefaultShadowQuadType )
742 continue; // Not a shadow quad
744 glPushMatrix();
746 // Use the window's top-left as the origin
747 glTranslatef( window->x(), window->y(), 0 );
748 if( mask & PAINT_WINDOW_TRANSFORMED )
749 glTranslatef( data.xTranslate, data.yTranslate, data.zTranslate );
750 if(( mask & PAINT_WINDOW_TRANSFORMED ) && ( data.xScale != 1 || data.yScale != 1 ))
751 glScalef( data.xScale, data.yScale, data.zScale );
752 if(( mask & PAINT_WINDOW_TRANSFORMED ) && data.rotation )
754 glTranslatef( data.rotation->xRotationPoint,
755 data.rotation->yRotationPoint,
756 data.rotation->zRotationPoint );
757 float xAxis = 0.0;
758 float yAxis = 0.0;
759 float zAxis = 0.0;
760 switch( data.rotation->axis )
762 case RotationData::XAxis:
763 xAxis = 1.0;
764 break;
765 case RotationData::YAxis:
766 yAxis = 1.0;
767 break;
768 case RotationData::ZAxis:
769 zAxis = 1.0;
770 break;
772 glRotatef( data.rotation->angle, xAxis, yAxis, zAxis );
773 glTranslatef( -data.rotation->xRotationPoint,
774 -data.rotation->yRotationPoint,
775 -data.rotation->zRotationPoint );
778 // Create our polygon
779 QVector<float> verts, texcoords;
780 verts.reserve(8);
781 texcoords.reserve(8);
782 verts << quad[0].x() << quad[0].y();
783 verts << quad[1].x() << quad[1].y();
784 verts << quad[2].x() << quad[2].y();
785 verts << quad[3].x() << quad[3].y();
786 texcoords << quad[0].textureX() << quad[0].textureY();
787 texcoords << quad[1].textureX() << quad[1].textureY();
788 texcoords << quad[2].textureX() << quad[2].textureY();
789 texcoords << quad[3].textureX() << quad[3].textureY();
791 // Work out which texture to use
792 int texture = mShadowQuadTypes.indexOf( quad.type() );
793 if( texture != -1 && texture < mShadowTextures.size() ) // TODO: Needed?
795 // Render it!
796 // Cheat a little, assume the active and inactive shadows have identical quads
797 if( effects->hasDecorationShadows() )
799 if( window->hasDecoration() &&
800 effects->shadowTextureList( ShadowBorderedActive ) == texture )
801 { // Decorated windows
802 // Active shadow
803 drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ),
804 verts, texcoords, QColor(), region,
805 data.opacity * window->shadowOpacity( ShadowBorderedActive ),
806 data.brightness * window->shadowBrightness( ShadowBorderedActive ),
807 data.saturation * window->shadowSaturation( ShadowBorderedActive ));
809 // Inactive shadow
810 texture = effects->shadowTextureList( ShadowBorderedInactive );
811 drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ),
812 verts, texcoords, QColor(), region,
813 data.opacity * window->shadowOpacity( ShadowBorderedInactive ),
814 data.brightness * window->shadowBrightness( ShadowBorderedInactive ),
815 data.saturation * window->shadowSaturation( ShadowBorderedInactive ));
817 else if( effects->shadowTextureList( ShadowBorderlessActive ) == texture )
818 { // Decoration-less normal windows
819 if( effects->activeWindow() == window )
821 drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ),
822 verts, texcoords, QColor(), region,
823 data.opacity * window->shadowOpacity( ShadowBorderlessActive ),
824 data.brightness * window->shadowBrightness( ShadowBorderlessActive ),
825 data.saturation * window->shadowSaturation( ShadowBorderlessActive ));
827 else
829 texture = effects->shadowTextureList( ShadowBorderlessInactive );
830 drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ),
831 verts, texcoords, QColor(), region,
832 data.opacity * window->shadowOpacity( ShadowBorderlessInactive ),
833 data.brightness * window->shadowBrightness( ShadowBorderlessInactive ),
834 data.saturation * window->shadowSaturation( ShadowBorderlessInactive ));
837 else
838 { // Other windows
839 drawShadowQuadOpenGL( mShadowTextures.at( texture ).at( quad.id() ),
840 verts, texcoords, QColor(), region,
841 data.opacity * window->shadowOpacity( ShadowOther ),
842 data.brightness * window->shadowBrightness( ShadowOther ),
843 data.saturation * window->shadowSaturation( ShadowOther ));
847 if( quad.type() == mDefaultShadowQuadType )
848 { // Default shadow
849 float opacity = shadowOpacity;
850 if( intensifyActiveShadow && window == effects->activeWindow() )
851 opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity );
853 drawShadowQuadOpenGL( mDefaultShadowTextures.at( quad.id() ),
854 verts, texcoords, shadowColor, region,
855 data.opacity * opacity,
856 data.brightness,
857 data.saturation );
860 glPopMatrix();
863 glPopAttrib();
865 #endif
866 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
867 if( effects->compositingType() == XRenderCompositing )
869 XRenderSetPictureClipRegion( display(), effects->xrenderBufferPicture(), region.handle() );
871 foreach( const WindowQuad &quad, data.quads )
873 if( !mShadowQuadTypes.contains( quad.type() ) && quad.type() != mDefaultShadowQuadType )
874 continue; // Not a shadow quad
876 // Determine transformed quad position
877 QRect windowRect = window->geometry();
878 float xScale = 1.0;
879 float yScale = 1.0;
880 float xTranslate = 0.0;
881 float yTranslate = 0.0;
882 if( mask & PAINT_SCREEN_TRANSFORMED)
884 xScale = gScreenData.xScale;
885 yScale = gScreenData.yScale;
886 xTranslate += ( xScale - 1.0 ) * windowRect.x() + gScreenData.xTranslate;
887 yTranslate += ( yScale - 1.0 ) * windowRect.y() + gScreenData.yTranslate;
889 if( mask & PAINT_WINDOW_TRANSFORMED)
891 xTranslate += xScale * data.xTranslate;
892 yTranslate += yScale * data.yTranslate;
893 xScale *= data.xScale;
894 yScale *= data.yScale;
896 QRect quadRect(
897 window->x() + quad[0].x() * xScale + xTranslate,
898 window->y() + quad[0].y() * yScale + yTranslate,
899 ( quad[2].x() - quad[0].x() ) * xScale,
900 ( quad[2].y() - quad[0].y() ) * yScale );
902 // Work out which texture to use
903 int texture = mShadowQuadTypes.indexOf( quad.type() );
904 if( texture != -1 )
906 // Render it!
907 // Cheat a little, assume the active and inactive shadows have identical quads
908 if( effects->hasDecorationShadows() )
910 if( window->hasDecoration() &&
911 effects->shadowTextureList( ShadowBorderedActive ) == texture )
912 { // Decorated windows
913 // Active shadow
914 drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect,
915 xScale, yScale, QColor(),
916 data.opacity * window->shadowOpacity( ShadowBorderedActive ),
917 data.brightness * window->shadowBrightness( ShadowBorderedActive ),
918 data.saturation * window->shadowSaturation( ShadowBorderedActive ));
920 // Inactive shadow
921 texture = effects->shadowTextureList( ShadowBorderedInactive );
922 drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect,
923 xScale, yScale, QColor(),
924 data.opacity * window->shadowOpacity( ShadowBorderedInactive ),
925 data.brightness * window->shadowBrightness( ShadowBorderedInactive ),
926 data.saturation * window->shadowSaturation( ShadowBorderedInactive ));
928 else if( effects->shadowTextureList( ShadowBorderlessActive ) == texture )
929 { // Decoration-less normal windows
930 if( effects->activeWindow() == window )
931 { // Active shadow
932 drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect,
933 xScale, yScale, QColor(),
934 data.opacity * window->shadowOpacity( ShadowBorderlessActive ),
935 data.brightness * window->shadowBrightness( ShadowBorderlessActive ),
936 data.saturation * window->shadowSaturation( ShadowBorderlessActive ));
938 else
939 { // Inactive shadow
940 texture = effects->shadowTextureList( ShadowBorderedInactive );
941 drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect,
942 xScale, yScale, QColor(),
943 data.opacity * window->shadowOpacity( ShadowBorderlessInactive ),
944 data.brightness * window->shadowBrightness( ShadowBorderlessInactive ),
945 data.saturation * window->shadowSaturation( ShadowBorderlessInactive ));
948 else
949 { // Other windows
950 drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect,
951 xScale, yScale, QColor(),
952 data.opacity * window->shadowOpacity( ShadowOther ),
953 data.brightness * window->shadowBrightness( ShadowOther ),
954 data.saturation * window->shadowSaturation( ShadowOther ));
958 if( quad.type() == mDefaultShadowQuadType )
959 { // Default shadow
960 float opacity = shadowOpacity;
961 if( intensifyActiveShadow && window == effects->activeWindow() )
962 opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity );
964 drawShadowQuadXRender( mDefaultShadowPics.at( quad.id() ), quadRect, xScale, yScale,
965 shadowColor, opacity * data.opacity, data.brightness, data.saturation );
969 #endif
972 } // namespace
974 #include "shadow.h"