1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include "flipswitch.h"
22 #include <kwinconfig.h>
24 #include <kapplication.h>
25 #include <kcolorscheme.h>
26 #include <kconfiggroup.h>
28 #include <kwinglutils.h>
30 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
37 KWIN_EFFECT( flipswitch
, FlipSwitchEffect
)
38 KWIN_EFFECT_SUPPORTED( flipswitch
, FlipSwitchEffect::supported() )
40 FlipSwitchEffect::FlipSwitchEffect()
42 , animateFlip( false )
46 , addFullRepaint( false )
47 , rearrangeWindows( 0 )
48 , stopRequested( false )
49 , startRequested( false )
52 reconfigure( ReconfigureAll
);
55 FlipSwitchEffect::~FlipSwitchEffect()
59 bool FlipSwitchEffect::supported()
61 return effects
->compositingType() == OpenGLCompositing
;
64 void FlipSwitchEffect::reconfigure( ReconfigureFlags
)
66 KConfigGroup conf
= effects
->effectConfig( "FlipSwitch" );
67 mFlipDuration
= animationTime( conf
, "FlipDuration", 200 );
68 mAnimation
= conf
.readEntry( "AnimateFlip", true );
69 timeLine
.setCurveShape( TimeLine::EaseInOutCurve
);
70 timeLine
.setDuration( mFlipDuration
);
73 void FlipSwitchEffect::prePaintScreen( ScreenPrePaintData
& data
, int time
)
75 if( mActivated
|| stopRequested
|| stop
)
77 data
.mask
|= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
;
78 if( mAnimation
&& ( start
|| stop
|| animateFlip
) )
79 timeLine
.addTime( (double)time
);
81 effects
->prePaintScreen(data
, time
);
84 void FlipSwitchEffect::paintScreen( int mask
, QRegion region
, ScreenPaintData
& data
)
86 effects
->paintScreen( mask
, region
, data
);
87 if( mActivated
|| stopRequested
|| stop
)
89 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
90 glMatrixMode( GL_PROJECTION
);
92 glPushAttrib( GL_CURRENT_BIT
| GL_ENABLE_BIT
);
94 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
97 glGetIntegerv( GL_VIEWPORT
, viewport
);
99 QRect fullArea
= effects
->clientArea( FullArea
, effects
->activeScreen(), effects
->currentDesktop());
102 if( effects
->clientArea( FullScreenArea
, effects
->activeScreen(), effects
->currentDesktop()).x() == 0
103 && effects
->clientArea( FullScreenArea
, effects
->activeScreen()==0?1:0, effects
->currentDesktop()).x() == 0 )
106 // have to correct the yPos for top bottom constellation
107 yPos
= area
.height()-area
.y();
108 if( ( area
.height() * 2 != fullArea
.height() ) ||
109 ( effects
->clientArea( FullScreenArea
, effects
->activeScreen(), effects
->currentDesktop()).width() !=
110 effects
->clientArea( FullScreenArea
, effects
->activeScreen()==0?1:0, effects
->currentDesktop()).width() ) )
112 // different resolutions
116 yPos
= fullArea
.height() - area
.height();
122 if( ( area
.width() * 2 != fullArea
.width() ) ||
123 ( effects
->clientArea( FullScreenArea
, effects
->activeScreen(), effects
->currentDesktop()).height() !=
124 effects
->clientArea( FullScreenArea
, effects
->activeScreen()==0?1:0, effects
->currentDesktop()).height() ) )
126 // different resolutions
127 yPos
= area
.y() + fullArea
.height() - area
.height();
131 float left
, right
, top
, bottom
;
132 left
= -area
.width() * 0.5f
;
133 right
= area
.width() * 0.5f
;
134 top
= area
.height()*0.5f
;
135 bottom
= -area
.height()*0.5f
;
136 if( twinview
&& ( start
|| stop
) )
138 // special handling for start and stop animation in twin view setup
139 glViewport( fullArea
.x(), fullArea
.y(), fullArea
.width(), fullArea
.height() );
140 left
= -(area
.x() + area
.width() * 0.5f
);
141 right
= fullArea
.width() + left
;
142 bottom
= -(area
.y() + area
.height() * 0.5f
);
143 top
= fullArea
.height() + bottom
;
147 glViewport( area
.x(), yPos
, area
.width(), area
.height() );
149 glFrustum( left
, right
, top
, bottom
, 10, 50 );
151 glMatrixMode( GL_MODELVIEW
);
153 float xOffset
= area
.width()*0.33f
;
154 float zOffset
= 10.0;
156 // bring the selected window to the back of the list
157 QList
< EffectWindow
* > tempList
= effects
->currentTabBoxWindowList();
158 int index
= tempList
.indexOf( effects
->currentTabBoxWindow() );
159 QList
< EffectWindow
* > windowList
;
160 for( int i
=index
; i
<tempList
.count(); i
++)
162 windowList
.push_front( tempList
[i
] );
164 for( int i
=0; i
<index
; i
++)
166 windowList
.push_front( tempList
[i
] );
168 // do we have to rearrange the windows as an animation has to follow?
169 if( rearrangeWindows
!= 0 )
171 if( rearrangeWindows
< 0 )
173 for( int i
=0; i
>rearrangeWindows
; i
-- )
175 EffectWindow
* w
= windowList
.front();
176 windowList
.pop_front();
177 windowList
.append( w
);
182 for( int i
=0; i
<rearrangeWindows
; i
++ )
184 EffectWindow
* w
= windowList
.back();
185 windowList
.pop_back();
186 windowList
.prepend( w
);
191 // do we have a start or stop animation
192 if( ( start
|| stop
) && mAnimation
)
194 for( int i
=0; i
<windowList
.count(); i
++)
197 EffectWindow
*w
= windowList
[ i
];
198 // Position of the window in OpenGL
199 float x
= w
->x()+left
;
200 float y
= bottom
-area
.height()+w
->y()+w
->height();
202 if( w
->isMinimized() )
204 // use icon instead of window
205 x
= w
->iconGeometry().x()+left
;
206 y
= bottom
-area
.height()+w
->iconGeometry().y()+w
->height();
208 // Position of the window in the stack
209 float stackX
= -area
.width()*0.25f
-(xOffset
*windowList
.count())+xOffset
*(i
+1);
210 float stackY
= -area
.height()*0.5f
;
211 float stackZ
= (-1*zOffset
*windowList
.count()) -12.5+zOffset
*(i
+1);
213 float animateXOffset
;
214 float animateYOffset
;
215 float animateZOffset
;
217 // if start move to stack, if stop move from stack
220 animateXOffset
= x
+timeLine
.value()*(stackX
-x
);
221 animateYOffset
= y
+timeLine
.value()*(stackY
-y
);
222 animateZOffset
= z
+timeLine
.value()*(stackZ
-z
);
223 rotation
= timeLine
.value()*0.25;
227 animateXOffset
= stackX
+timeLine
.value()*(x
-stackX
);
228 animateYOffset
= stackY
+timeLine
.value()*(y
-stackY
);
229 animateZOffset
= stackZ
+timeLine
.value()*(z
-stackZ
);
230 rotation
= 0.25-timeLine
.value()*0.25;
233 // go to current position and rotate by the time based factor
234 glTranslatef(animateXOffset
, animateYOffset
, animateZOffset
);
235 glRotatef(rotation
, 0.0, 1.0, 0.0);
237 // top most window has to be painted not drawn.
238 if( i
<windowList
.count()-1 )
240 paintWindowFlip( windowList
[i
] );
242 else paintWindowFlip( windowList
[i
], false);
245 // time elapsed - so no more animation
246 if( timeLine
.value() == 1.0 )
248 timeLine
.setProgress( 0.0 );
252 // more animations have to follow?
253 if( rearrangeWindows
!= 0 )
256 if( rearrangeWindows
< 0 )
267 else if( stopRequested
)
269 // no more animations but effect has to stop
271 stopRequested
= false;
279 // tabbox already referenced again - so restart
281 startRequested
= false;
287 // we need one more FullRepaint
288 addFullRepaint
= true;
293 // normal behaviour - no start or stop animation
296 double localProgress
= timeLine
.value();
298 glTranslatef(-area
.width()*0.25f
-(xOffset
*windowList
.count()),
300 (-1*zOffset
*windowList
.count()) -12.5);
301 if( animateFlip
&& windowList
.count() > 1 )
303 if( timeLine
.value() < 1.0 )
305 float animateXOffset
= timeLine
.value()*xOffset
;
306 float animateZOffset
= timeLine
.value()*zOffset
;
309 if( animateXOffset
> xOffset
) animateXOffset
= xOffset
;
310 if( animateZOffset
> zOffset
) animateZOffset
= zOffset
;
311 glTranslatef(animateXOffset
, 0.0, animateZOffset
);
312 EffectWindow
* w
= windowList
.front();
313 windowList
.pop_front();
314 windowList
.append( w
);
318 animateXOffset
= xOffset
- animateXOffset
;
319 animateZOffset
= zOffset
- animateZOffset
;
320 if( animateXOffset
< 0.0 ) animateXOffset
= 0.0;
321 if( animateZOffset
< 0.0 ) animateZOffset
= 0.0;
322 glTranslatef(animateXOffset
, 0.0, animateZOffset
);
327 timeLine
.setProgress( 0.0 );
328 if( rearrangeWindows
!= 0 )
331 if( rearrangeWindows
< 0 )
350 stopRequested
= false;
355 for( int i
=0; i
<windowList
.count(); i
++)
358 glTranslatef(xOffset
*(i
+1), 0.0, zOffset
*(i
+1));
359 glRotatef(0.25, 0.0, 1.0, 0.0);
360 // top most window has to be painted not drawn.
361 if( i
<windowList
.count()-1 )
363 paintWindowFlip( windowList
[i
] );
367 // last window - change opacity if animated
369 if( animateFlip
&& windowList
.count() > 1 )
371 if( forward
) opacity
= opacity
- localProgress
*opacity
;
372 else opacity
= localProgress
*opacity
;
374 paintWindowFlip( windowList
[i
], false, opacity
);
381 glMatrixMode( GL_PROJECTION
);
383 glMatrixMode( GL_MODELVIEW
);
384 glViewport( viewport
[0], viewport
[1], viewport
[2], viewport
[3] );
386 // caption of selected window
389 color_frame
= KColorScheme( QPalette::Active
, KColorScheme::Window
).background().color();
390 color_frame
.setAlphaF( 0.9 );
391 color_text
= KColorScheme( QPalette::Active
, KColorScheme::Window
).foreground().color();
394 color_frame
.setAlphaF( 0.9 * timeLine
.value() );
395 color_text
.setAlphaF( timeLine
.value() );
399 color_frame
.setAlphaF( 0.9 - 0.9 * timeLine
.value() );
400 color_text
.setAlphaF( 1.0 - timeLine
.value() );
402 else if( addFullRepaint
)
404 // special case: timeLine was reset, but actual frame will be painted
405 color_frame
.setAlphaF( 0.0 );
406 color_text
.setAlphaF( 0.0 );
409 text_font
.setBold( true );
410 text_font
.setPointSize( 20 );
411 glPushAttrib( GL_CURRENT_BIT
);
412 glColor4f( color_frame
.redF(), color_frame
.greenF(), color_frame
.blueF(), color_frame
.alphaF());
413 QRect frameRect
= QRect( area
.width()*0.25f
+ area
.x(),
414 area
.height()*0.9f
+ area
.y(),
416 QFontMetrics( text_font
).height() * 1.2 );
417 renderRoundBoxWithEdge( frameRect
);
418 effects
->paintText( effects
->currentTabBoxWindow()->caption(),
420 frameRect
.width() - 100,
424 // icon of selected window
425 QPixmap iconPixmap
= effects
->currentTabBoxWindow()->icon();
426 if( start
|| stop
|| addFullRepaint
)
428 int alpha
= 255.0f
* timeLine
.value();
431 alpha
= 255.0f
- alpha
;
433 else if( addFullRepaint
)
437 QPixmap transparency
= iconPixmap
.copy( iconPixmap
.rect() );
438 transparency
.fill( QColor( 255, 255, 255, alpha
) );
439 iconPixmap
.setAlphaChannel( transparency
.alphaChannel() );
441 GLTexture
* icon
= new GLTexture( iconPixmap
);
443 glPushAttrib( GL_CURRENT_BIT
| GL_ENABLE_BIT
);
445 glEnable( GL_BLEND
);
446 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
447 // icon takes 80 % of the height of the frame. So each 10 % space left on the top and bottom
448 QRect iconRect
= QRect( frameRect
.x() + frameRect
.height()*0.1f
,
449 frameRect
.y() + frameRect
.height()*0.1f
,
450 frameRect
.height()*0.8f
,
451 frameRect
.height()*0.8f
);
452 icon
->render( region
, iconRect
);
455 glDisable( GL_BLEND
);
461 void FlipSwitchEffect::postPaintScreen()
463 if( (mActivated
&& ( animateFlip
|| start
)) || stopRequested
|| stop
)
465 effects
->addRepaintFull();
469 addFullRepaint
= false;
470 effects
->setActiveFullScreenEffect( 0 );
471 effects
->addRepaintFull();
473 effects
->postPaintScreen();
476 void FlipSwitchEffect::paintWindow( EffectWindow
* w
, int mask
, QRegion region
, WindowPaintData
& data
)
478 if( mActivated
|| stopRequested
|| stop
)
480 if( !( mask
& PAINT_WINDOW_TRANSFORMED
) && ( !w
->isDesktop() ) )
482 if( ( start
|| stop
) && w
->isDock() )
484 data
.opacity
= 1.0 - timeLine
.value();
486 data
.opacity
= timeLine
.value();
492 effects
->paintWindow( w
, mask
, region
, data
);
495 void FlipSwitchEffect::tabBoxAdded( int mode
)
497 if( effects
->activeFullScreenEffect() && effects
->activeFullScreenEffect() != this )
501 // only for windows mode
502 if( mode
== TabBoxWindowsMode
&& effects
->currentTabBoxWindowList().count() > 0 )
504 input
= effects
->createFullScreenInputWindow( this, Qt::BlankCursor
);
505 effects
->refTabBox();
506 effects
->setActiveFullScreenEffect( this );
507 selectedWindow
= effects
->currentTabBoxWindowList().indexOf(effects
->currentTabBoxWindow());
508 if( !stop
&& !stopRequested
)
512 effects
->addRepaintFull();
516 // last tabbox effect still running - schedule start effect
517 startRequested
= true;
520 // Calculation of correct area
521 area
= effects
->clientArea( FullScreenArea
, effects
->activeScreen(), effects
->currentDesktop());
522 QRect fullArea
= effects
->clientArea( FullArea
, effects
->activeScreen(), effects
->currentDesktop());
524 if( area
.height() != fullArea
.height() || area
.width() != fullArea
.width() )
531 void FlipSwitchEffect::tabBoxClosed()
535 // if animation than deactivate after animation
537 effects
->unrefTabBox();
539 effects
->destroyInputWindow( input
);
542 if( start
&& rearrangeWindows
== 0 )
546 timeLine
.setProgress( 1.0 - timeLine
.value() );
548 else if( start
|| animateFlip
)
549 stopRequested
= true;
553 effects
->addRepaintFull();
558 effects
->setActiveFullScreenEffect( 0 );
563 void FlipSwitchEffect::tabBoxUpdated()
567 if( mAnimation
&& effects
->currentTabBoxWindowList().count() > 1 )
569 // determine the switch direction
570 int index
= effects
->currentTabBoxWindowList().indexOf(effects
->currentTabBoxWindow());
571 bool direction
= false;
572 int windowCount
= effects
->currentTabBoxWindowList().count();
573 if( index
> selectedWindow
)
575 if( index
== windowCount
-1 && selectedWindow
== 0 ) forward
= false;
576 else direction
= true;
578 else if( index
== 0 && ( selectedWindow
== windowCount
-1 ) )
582 else if( index
== selectedWindow
) return; // nothing changed
587 selectedWindow
= index
;
588 if( !animateFlip
&& !start
)
595 if( direction
) rearrangeWindows
--;
596 else rearrangeWindows
++;
597 if( rearrangeWindows
>= windowCount
) rearrangeWindows
= rearrangeWindows
% windowCount
;
598 else if( (-1*rearrangeWindows
) >= windowCount
) rearrangeWindows
= -1*((-1*rearrangeWindows
) % windowCount
);
601 effects
->addRepaintFull();
605 void FlipSwitchEffect::paintWindowFlip( EffectWindow
* w
, bool draw
, float opacity
)
607 WindowPaintData
data( w
);
610 int y
= area
.height() - w
->geometry().height();
612 setPositionTransformations( data
,
614 QRect( x
, y
, w
->geometry().width(), w
->geometry().height() ),
615 Qt::KeepAspectRatio
);
617 data
.opacity
*= opacity
;
618 thumbnail
= infiniteRegion();
619 // if paintWindow() is used the window behind the initial selected window is not painted on the stack,
620 // but painted when it is selected
621 // if drawWindow() is used the front window does not glide through the monitor during animation
622 // so use drawWindow() for all windows but the selected and paintWindow() for the selected window
624 effects
->drawWindow( w
,
625 PAINT_WINDOW_TRANSFORMED
,
628 effects
->paintWindow( w
,
629 PAINT_WINDOW_TRANSFORMED
,