1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2007 Martin Gräßlin <ubuntu@martin-graesslin.com>
6 Copyright (C) 2008 Torgny Johansson <torgny.johansson@gmail.com>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
24 #include <kwinconfig.h>
27 #include <kactioncollection.h>
28 #include <kconfiggroup.h>
31 #include <kstandarddirs.h>
37 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
44 KWIN_EFFECT( snow
, SnowEffect
)
45 KWIN_EFFECT_SUPPORTED( snow
, SnowEffect::supported() )
47 SnowEffect::SnowEffect()
50 , snowBehindWindows( false )
56 srandom( std::time( NULL
) );
58 KActionCollection
* actionCollection
= new KActionCollection( this );
59 KAction
* a
= static_cast< KAction
* >( actionCollection
->addAction( "Snow" ));
60 a
->setText( i18n("Snow" ));
61 a
->setGlobalShortcut( KShortcut( Qt::CTRL
+ Qt::META
+ Qt::Key_F12
));
62 connect( a
, SIGNAL( triggered( bool )), this, SLOT( toggle()));
63 reconfigure( ReconfigureAll
);
66 SnowEffect::~SnowEffect()
73 bool SnowEffect::supported()
75 return effects
->compositingType() == OpenGLCompositing
;
78 void SnowEffect::reconfigure( ReconfigureFlags
)
80 KConfigGroup conf
= effects
->effectConfig("Snow");
81 mNumberFlakes
= conf
.readEntry("Number", 200);
82 mMinFlakeSize
= conf
.readEntry("MinFlakes", 10);
83 mMaxFlakeSize
= conf
.readEntry("MaxFlakes", 50);
84 mMaxVSpeed
= conf
.readEntry("MaxVSpeed", 2);
85 mMaxHSpeed
= conf
.readEntry("MaxHSpeed", 1);
86 snowBehindWindows
= conf
.readEntry("BehindWindows", true);
89 void SnowEffect::prePaintScreen( ScreenPrePaintData
& data
, int time
)
93 // if number of active snowflakes is smaller than maximum number
94 // create a random new snowflake
95 nextFlakeMillis
-= time
;
96 if ( ( nextFlakeMillis
<= 0 ) && flakes
.count() < mNumberFlakes
)
99 while ( size
< mMinFlakeSize
)
100 size
= random() % mMaxFlakeSize
;
101 SnowFlake flake
= SnowFlake( random() % (displayWidth() - size
), -1 * size
, size
, size
, mMaxVSpeed
, mMaxHSpeed
);
102 flakes
.append( flake
);
104 // calculation of next time of snowflake
105 // depends on the time the flow needs to get to the bottom (screen size)
107 long next
= ((500/(time
+5))*(Effect::displayHeight()/flake
.getVSpeed()))/mNumberFlakes
;
108 nextFlakeMillis
= next
;
110 data
.mask
|= PAINT_SCREEN_TRANSFORMED
;
113 effects
->prePaintScreen( data
, time
);
116 void SnowEffect::paintScreen( int mask
, QRegion region
, ScreenPaintData
& data
)
118 if( active
&& snowBehindWindows
)
119 repaintRegion
= QRegion( 0, 0, displayWidth(), displayHeight() );
120 effects
->paintScreen( mask
, region
, data
); // paint normal screen
121 if( active
&& !snowBehindWindows
)
125 void SnowEffect::snowing( QRegion
& region
)
127 if(! texture
) loadTexture();
130 glActiveTexture(GL_TEXTURE0
);
132 if( mUseShader
&& !mInited
)
133 mUseShader
= loadShader();
134 glPushAttrib( GL_CURRENT_BIT
| GL_ENABLE_BIT
);
135 glEnable( GL_BLEND
);
136 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
141 QRect rect
= region
.boundingRect();
142 mShader
->setUniform( "left", rect
.x() );
143 mShader
->setUniform( "right", rect
.x() + rect
.width() );
144 mShader
->setUniform( "top", rect
.y() );
145 mShader
->setUniform( "bottom", rect
.y() + rect
.height() );
148 glNewList( list
, GL_COMPILE_AND_EXECUTE
);
149 for (int i
=0; i
<flakes
.count(); i
++)
151 SnowFlake
& flake
= flakes
[i
];
154 // only update during first paint in one frame
155 if( flake
.addFrame() == 0 )
157 flakes
.removeAt( i
-- );
165 mShader
->setUniform( "angle", flake
.getRotationSpeed() );
166 mShader
->setUniform( "x", flake
.getX() );
167 mShader
->setUniform( "hSpeed", flake
.getHSpeed() );
168 mShader
->setUniform( "vSpeed", flake
.getVSpeed() );
169 mShader
->setUniform( "frames", flake
.getFrames() );
170 mShader
->setUniform( "width", flake
.getWidth() );
171 mShader
->setUniform( "height", flake
.getHeight() );
178 // only update during first paint in one frame
179 flake
.updateSpeedAndRotation();
185 // translate to the center of the flake
186 glTranslatef(flake
.getWidth()/2 + flake
.getX(), flake
.getHeight()/2 + flake
.getY(), 0);
188 // rotate around the Z-axis
189 glRotatef(flake
.getRotationAngle(), 0.0, 0.0, 1.0);
191 // translate back to the starting point
192 glTranslatef(-flake
.getWidth()/2 - flake
.getX(), -flake
.getHeight()/2 - flake
.getY(), 0);
194 // paint the snowflake
195 texture
->render( region
, flake
.getRect());
197 // restore the matrix
207 glDisable( GL_BLEND
);
214 void SnowEffect::postPaintScreen()
218 if( snowBehindWindows
)
219 effects
->addRepaint( repaintRegion
);
221 effects
->addRepaintFull();
223 effects
->postPaintScreen();
226 void SnowEffect::paintWindow( EffectWindow
* w
, int mask
, QRegion region
, WindowPaintData
& data
)
228 if( active
&& snowBehindWindows
&& !w
->isDesktop() && !w
->isDock() )
230 repaintRegion
-= QRegion( w
->geometry() );
232 effects
->paintWindow( w
, mask
, region
, data
);
233 if( active
&& w
->isDesktop() && snowBehindWindows
)
235 QRect rect
= effects
->clientArea( FullScreenArea
, w
->screen(), effects
->currentDesktop());
236 QRegion
snowRegion( rect
.x() + data
.xTranslate
, rect
.y() + data
.yTranslate
,
237 (double)rect
.width()*data
.xScale
, (double)rect
.height()*data
.yScale
);
240 // have to adjust the y values to fit OpenGL
241 // in OpenGL y==0 is at bottom, in Qt at top
243 if( effects
->numScreens() > 1 )
245 QRect fullArea
= effects
->clientArea( FullArea
, 0, 1 );
246 if( fullArea
.height() != rect
.height() )
249 y
= fullArea
.height() - rect
.height();
251 y
= fullArea
.height() - rect
.y() - rect
.height();
254 snowRegion
= QRegion( rect
.x() + data
.xTranslate
,
255 y
+ rect
.height() - data
.yTranslate
- (double)rect
.height()*data
.yScale
,
256 (double)rect
.width()*data
.xScale
, (double)rect
.height()*data
.yScale
);
259 glTranslatef( rect
.x() + data
.xTranslate
, rect
.y() + data
.yTranslate
, 0.0 );
260 glScalef( data
.xScale
, data
.yScale
, 1.0 );
261 snowing( snowRegion
);
266 void SnowEffect::toggle()
268 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
272 list
= glGenLists(1);
276 glDeleteLists( list
, 1 );
285 effects
->addRepaintFull();
289 bool SnowEffect::loadShader()
292 if( !(GLShader::fragmentShaderSupported() &&
293 (effects
->compositingType() == OpenGLCompositing
)) )
295 kDebug(1212) << "Shaders not supported - waisting CPU cycles" << endl
;
298 QString fragmentshader
= KGlobal::dirs()->findResource("data", "kwin/snow.frag");
299 QString vertexshader
= KGlobal::dirs()->findResource("data", "kwin/snow.vert");
300 if(fragmentshader
.isEmpty() || vertexshader
.isEmpty())
302 kDebug(1212) << "Couldn't locate shader files" << endl
;
306 mShader
= new GLShader(vertexshader
, fragmentshader
);
307 if(!mShader
->isValid())
309 kDebug(1212) << "The shader failed to load!" << endl
;
315 mShader
->setUniform( "snowTexture", 0 );
318 kDebug(1212) << "using shader";
320 glNewList( list
, GL_COMPILE
);
322 glTexCoord2f( 0.0f
, 0.0f
);
324 glTexCoord2f( 1.0f
, 0.0f
);
326 glTexCoord2f( 1.0f
, 1.0f
);
328 glTexCoord2f( 0.0f
, 1.0f
);
335 void SnowEffect::loadTexture()
337 QString file
= KGlobal::dirs()->findResource( "appdata", "snowflake.png" );
340 texture
= new GLTexture( file
);
344 // the snowflake class
345 SnowFlake::SnowFlake(int x
, int y
, int width
, int height
, int maxVSpeed
, int maxHSpeed
)
347 int minVSpeed
= maxVSpeed
- 8; // 8 gives a nice difference in speed
348 if(minVSpeed
< 1) minVSpeed
= 1;
349 vSpeed
= random()%maxVSpeed
+ minVSpeed
;
351 hSpeed
= random()%(maxHSpeed
+1);
352 if(random()%2 < 1) hSpeed
= -hSpeed
; // to create negative hSpeeds at random
355 rotationSpeed
= random()%4 - 2;
356 if(rotationSpeed
== 0) rotationSpeed
= 0.5;
357 rect
= QRect(x
, y
, width
, height
);
359 maxFrames
= displayHeight() / vSpeed
;
361 maxFrames
= qMin( maxFrames
, (displayWidth() - x
)/hSpeed
);
362 else if( hSpeed
< 0 )
363 maxFrames
= qMin( maxFrames
, (x
+ width
)/(-hSpeed
) );
366 SnowFlake::~SnowFlake()
370 int SnowFlake::getHSpeed()
375 QRect
SnowFlake::getRect()
380 void SnowFlake::updateSpeedAndRotation()
382 rotationAngle
= rotationAngle
+rotationSpeed
;
383 rect
.translate(hSpeed
, vSpeed
);
386 float SnowFlake::getRotationAngle()
388 return rotationAngle
;
391 float SnowFlake::getRotationSpeed()
393 return rotationSpeed
;
396 int SnowFlake::getVSpeed()
401 int SnowFlake::getHeight()
403 return rect
.height();
406 int SnowFlake::getWidth()
411 int SnowFlake::getX()
416 int SnowFlake::getY()
421 void SnowFlake::setHeight(int height
)
423 rect
.setHeight(height
);
426 void SnowFlake::setWidth(int width
)
428 rect
.setWidth(width
);
431 void SnowFlake::setX(int x
)
436 void SnowFlake::setY(int y
)
441 int SnowFlake::addFrame()
443 return ( maxFrames
- (++frameCounter
) );
446 int SnowFlake::getFrames()