not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / effects / snow.cpp
blob377ad201257b155ac8733400cd05d951ed2dea0f
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 *********************************************************************/
22 #include "snow.h"
24 #include <kwinconfig.h>
26 #include <kaction.h>
27 #include <kactioncollection.h>
28 #include <kconfiggroup.h>
29 #include <kglobal.h>
30 #include <klocale.h>
31 #include <kstandarddirs.h>
33 #include <kdebug.h>
35 #include <ctime>
37 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
38 #include <GL/gl.h>
39 #endif
41 namespace KWin
44 KWIN_EFFECT( snow, SnowEffect )
45 KWIN_EFFECT_SUPPORTED( snow, SnowEffect::supported() )
47 SnowEffect::SnowEffect()
48 : texture( NULL )
49 , active( false)
50 , snowBehindWindows( false )
51 , mShader( 0 )
52 , mInited( false )
53 , mUseShader( true )
54 , hasSnown( false )
56 srandom( std::time( NULL ) );
57 nextFlakeMillis = 0;
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()
68 delete texture;
69 if( active )
70 delete mShader;
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 )
91 if ( active )
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)
98 int size = 0;
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)
106 // and the fps
107 long next = ((500/(time+5))*(Effect::displayHeight()/flake.getVSpeed()))/mNumberFlakes;
108 nextFlakeMillis = next;
110 data.mask |= PAINT_SCREEN_TRANSFORMED;
111 hasSnown = false;
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 )
122 snowing( region );
125 void SnowEffect::snowing( QRegion& region )
127 if(! texture ) loadTexture();
128 if( texture )
130 glActiveTexture(GL_TEXTURE0);
131 texture->bind();
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 );
138 if( mUseShader )
140 mShader->bind();
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() );
147 else
148 glNewList( list, GL_COMPILE_AND_EXECUTE );
149 for (int i=0; i<flakes.count(); i++)
151 SnowFlake& flake = flakes[i];
152 if( !hasSnown )
154 // only update during first paint in one frame
155 if( flake.addFrame() == 0 )
157 flakes.removeAt( i-- );
158 continue;
162 if( mUseShader )
164 // use shader
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() );
172 glCallList( list );
174 else
176 if( !hasSnown )
178 // only update during first paint in one frame
179 flake.updateSpeedAndRotation();
181 // no shader
182 // save the matrix
183 glPushMatrix();
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
198 glPopMatrix();
201 if( mUseShader )
203 mShader->unbind();
205 else
206 glEndList();
207 glDisable( GL_BLEND );
208 texture->unbind();
209 glPopAttrib();
210 hasSnown = true;
214 void SnowEffect::postPaintScreen()
216 if( active )
218 if( snowBehindWindows )
219 effects->addRepaint( repaintRegion );
220 else
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);
238 if( mUseShader )
240 // have to adjust the y values to fit OpenGL
241 // in OpenGL y==0 is at bottom, in Qt at top
242 int y = 0;
243 if( effects->numScreens() > 1 )
245 QRect fullArea = effects->clientArea( FullArea, 0, 1 );
246 if( fullArea.height() != rect.height() )
248 if( rect.y() == 0 )
249 y = fullArea.height() - rect.height();
250 else
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);
258 glPushMatrix();
259 glTranslatef( rect.x() + data.xTranslate, rect.y() + data.yTranslate, 0.0 );
260 glScalef( data.xScale, data.yScale, 1.0 );
261 snowing( snowRegion );
262 glPopMatrix();
266 void SnowEffect::toggle()
268 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
269 active = !active;
270 if( active )
272 list = glGenLists(1);
274 else
276 glDeleteLists( list, 1 );
277 flakes.clear();
278 if( mUseShader )
280 delete mShader;
281 mInited = false;
282 mUseShader = true;
285 effects->addRepaintFull();
286 #endif
289 bool SnowEffect::loadShader()
291 mInited = true;
292 if( !(GLShader::fragmentShaderSupported() &&
293 (effects->compositingType() == OpenGLCompositing)) )
295 kDebug(1212) << "Shaders not supported - waisting CPU cycles" << endl;
296 return false;
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;
303 return false;
306 mShader = new GLShader(vertexshader, fragmentshader);
307 if(!mShader->isValid())
309 kDebug(1212) << "The shader failed to load!" << endl;
310 return false;
312 else
314 mShader->bind();
315 mShader->setUniform( "snowTexture", 0 );
316 mShader->unbind();
318 kDebug(1212) << "using shader";
320 glNewList( list, GL_COMPILE );
321 glBegin( GL_QUADS );
322 glTexCoord2f( 0.0f, 0.0f );
323 glVertex2i( 0, 0 );
324 glTexCoord2f( 1.0f, 0.0f );
325 glVertex2i( 0, 0 );
326 glTexCoord2f( 1.0f, 1.0f );
327 glVertex2i( 0, 0 );
328 glTexCoord2f( 0.0f, 1.0f );
329 glVertex2i( 0, 0 );
330 glEnd();
331 glEndList();
332 return true;
335 void SnowEffect::loadTexture()
337 QString file = KGlobal::dirs()->findResource( "appdata", "snowflake.png" );
338 if( file.isEmpty())
339 return;
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
354 rotationAngle = 0;
355 rotationSpeed = random()%4 - 2;
356 if(rotationSpeed == 0) rotationSpeed = 0.5;
357 rect = QRect(x, y, width, height);
358 frameCounter = 0;
359 maxFrames = displayHeight() / vSpeed;
360 if( hSpeed > 0 )
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()
372 return hSpeed;
375 QRect SnowFlake::getRect()
377 return rect;
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()
398 return vSpeed;
401 int SnowFlake::getHeight()
403 return rect.height();
406 int SnowFlake::getWidth()
408 return rect.width();
411 int SnowFlake::getX()
413 return rect.x();
416 int SnowFlake::getY()
418 return rect.y();
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)
433 rect.setX(x);
436 void SnowFlake::setY(int y)
438 rect.setY(y);
441 int SnowFlake::addFrame()
443 return ( maxFrames - (++frameCounter) );
446 int SnowFlake::getFrames()
448 return frameCounter;
451 } // namespace