kwin/effects/presentwindows.cpp
Rivo Laks c22b4809a5 Add working config modules for PresentWindows and Shadow effects.
You can configure shadow's offset and opacity and presentwindow's mouse activation areas
  (e.g. activate when mouse it at top-right corner).

svn path=/trunk/KDE/kdebase/workspace/; revision=669040
2007-05-28 11:34:12 +00:00

790 lines
28 KiB
C++

/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#include "presentwindows.h"
#include <kactioncollection.h>
#include <kaction.h>
#include <klocale.h>
#include <QMouseEvent>
#include <qpainter.h>
#include <math.h>
#include <assert.h>
#include <limits.h>
namespace KWin
{
KWIN_EFFECT( presentwindows, PresentWindowsEffect )
PresentWindowsEffect::PresentWindowsEffect()
: mShowWindowsFromAllDesktops ( false )
, mActivated( false )
, mActiveness( 0.0 )
, mRearranging( 1.0 )
, hasKeyboardGrab( false )
, mHoverWindow( NULL )
#ifdef HAVE_OPENGL
, filterTexture( NULL )
#endif
{
KConfig c("kwinrc");
KConfigGroup conf(&c, "Effect-PresentWindows");
KActionCollection* actionCollection = new KActionCollection( this );
KAction* a = (KAction*)actionCollection->addAction( "Expose" );
a->setText( i18n("Toggle Expose effect" ));
a->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F10));
connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleActive()));
KAction* b = (KAction*)actionCollection->addAction( "ExposeAll" );
b->setText( i18n("Toggle Expose effect (incl other desktops)" ));
b->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F11));
connect(b, SIGNAL(triggered(bool)), this, SLOT(toggleActiveAllDesktops()));
borderActivate = (ElectricBorder)conf.readEntry("BorderActivate", (int)ElectricTopRight);
borderActivateAll = (ElectricBorder)conf.readEntry("BorderActivateAll", (int)ElectricNone);
effects->reserveElectricBorder( borderActivate );
effects->reserveElectricBorder( borderActivateAll );
}
PresentWindowsEffect::~PresentWindowsEffect()
{
effects->unreserveElectricBorder( borderActivate );
effects->unreserveElectricBorder( borderActivateAll );
discardFilterTexture();
}
void PresentWindowsEffect::prePaintScreen( int* mask, QRegion* region, int time )
{
// How long does it take for the effect to get it's full strength (in ms)
const float changeTime = 300;
if(mActivated)
{
mActiveness = qMin(1.0f, mActiveness + time/changeTime);
if( mRearranging < 1 )
mRearranging = qMin(1.0f, mRearranging + time/changeTime);
}
else if(mActiveness > 0.0f)
{
mActiveness = qMax(0.0f, mActiveness - time/changeTime);
if(mActiveness <= 0.0f)
effectTerminated();
}
// We need to mark the screen windows as transformed. Otherwise the whole
// screen won't be repainted, resulting in artefacts
if( mActiveness > 0.0f )
*mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
effects->prePaintScreen(mask, region, time);
}
void PresentWindowsEffect::prePaintWindow( EffectWindow* w, int* mask, QRegion* paint, QRegion* clip, int time )
{
if( mActiveness > 0.0f )
{
if( mWindowData.contains(w) )
{
// This window will be transformed by the effect
*mask |= Effect::PAINT_WINDOW_TRANSFORMED;
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_MINIMIZE );
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
// If it's minimized window or on another desktop and effect is not
// fully active, then apply some transparency
if( mActiveness < 1.0f && (w->isMinimized() || !w->isOnCurrentDesktop() ))
*mask |= Effect::PAINT_WINDOW_TRANSLUCENT;
// Change window's hover according to cursor pos
WindowData& windata = mWindowData[w];
const float hoverchangetime = 200;
if( windata.area.contains(cursorPos()) )
windata.hover = qMin(1.0f, windata.hover + time / hoverchangetime);
else
windata.hover = qMax(0.0f, windata.hover - time / hoverchangetime);
}
else if( !w->isDesktop())
w->disablePainting( EffectWindow::PAINT_DISABLED );
}
effects->prePaintWindow( w, mask, paint, clip, time );
}
void PresentWindowsEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
{
effects->paintScreen( mask, region, data );
#ifdef HAVE_OPENGL
if( filterTexture && region.intersects( filterTextureRect ))
{
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
filterTexture->bind();
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
filterTexture->render( mask, region, filterTextureRect );
filterTexture->unbind();
glPopAttrib();
}
#endif
}
void PresentWindowsEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
if(mActiveness > 0.0f && mWindowData.contains(w))
{
// Change window's position and scale
const WindowData& windata = mWindowData[w];
if( mRearranging < 1 ) // rearranging
{
if( windata.old_area.isEmpty()) // no old position
{
data.xScale = windata.scale;
data.yScale = windata.scale;
data.xTranslate = windata.area.left() - w->x();
data.yTranslate = windata.area.top() - w->y();
data.opacity *= interpolate(0.0, 1.0, mRearranging);
}
else
{
data.xScale = interpolate(windata.old_scale, windata.scale, mRearranging);
data.yScale = interpolate(windata.old_scale, windata.scale, mRearranging);
data.xTranslate = (int)interpolate(windata.old_area.left() - w->x(),
windata.area.left() - w->x(), mRearranging);
data.yTranslate = (int)interpolate(windata.old_area.top() - w->y(),
windata.area.top() - w->y(), mRearranging);
}
}
else
{
data.xScale = interpolate(data.xScale, windata.scale, mActiveness);
data.yScale = interpolate(data.xScale, windata.scale, mActiveness);
data.xTranslate = (int)interpolate(data.xTranslate, windata.area.left() - w->x(), mActiveness);
data.yTranslate = (int)interpolate(data.yTranslate, windata.area.top() - w->y(), mActiveness);
}
// Darken all windows except for the one under the cursor
data.brightness *= interpolate(1.0, 0.7, mActiveness * (1.0f - windata.hover));
// If it's minimized window or on another desktop and effect is not
// fully active, then apply some transparency
if( mActiveness < 1.0f && (w->isMinimized() || !w->isOnCurrentDesktop() ))
data.opacity *= interpolate(0.0, 1.0, mActiveness);
}
// Call the next effect.
effects->paintWindow( w, mask, region, data );
}
void PresentWindowsEffect::postPaintScreen()
{
if( mActivated && mActiveness < 1.0 ) // activating effect
effects->addRepaintFull();
if( mActivated && mRearranging < 1.0 ) // rearranging
effects->addRepaintFull();
if( !mActivated && mActiveness > 0.0 ) // deactivating effect
effects->addRepaintFull();
foreach( const WindowData& d, mWindowData )
{
if( d.hover > 0 && d.hover < 1 ) // changing highlight
effects->addRepaintFull();
}
// Call the next effect.
effects->postPaintScreen();
}
void PresentWindowsEffect::windowInputMouseEvent( Window w, QEvent* e )
{
assert( w == mInput );
if( e->type() == QEvent::MouseMove )
{ // Repaint if the hovered-over window changed.
// (No need to use cursorMoved(), this takes care of it as well)
for( DataHash::ConstIterator it = mWindowData.begin();
it != mWindowData.end();
++it )
{
if( (*it).area.contains( cursorPos()))
{
if( mHoverWindow != it.key())
{
mHoverWindow = it.key();
effects->addRepaintFull(); // screen is transformed, so paint all
}
return;
}
}
return;
}
if( e->type() != QEvent::MouseButtonPress )
return;
if( static_cast< QMouseEvent* >( e )->button() != Qt::LeftButton )
{
setActive( false );
return;
}
// Find out which window (if any) was clicked and activate it
QPoint pos = static_cast< QMouseEvent* >( e )->pos();
for( DataHash::iterator it = mWindowData.begin();
it != mWindowData.end(); ++it )
{
if( it.value().area.contains(pos) )
{
effects->activateWindow( it.key() );
// mWindowData gets cleared and rebuilt when a window is
// activated, so it's dangerous (and unnecessary) to continue
break;
}
}
// Deactivate effect, no matter if any window was actually activated
setActive(false);
}
void PresentWindowsEffect::windowClosed( EffectWindow* w )
{
if( mHoverWindow == w )
mHoverWindow = NULL;
mWindowsToPresent.remove( w );
rearrangeWindows();
}
void PresentWindowsEffect::setActive(bool active)
{
if( mActivated == active )
return;
mActivated = active;
mHoverWindow = NULL;
if( mActivated )
{
mWindowData.clear();
effectActivated();
mActiveness = 0;
windowFilter.clear();
mWindowsToPresent.clear();
const EffectWindowList& originalwindowlist = effects->stackingOrder();
// Filter out special windows such as panels and taskbars
foreach( EffectWindow* window, originalwindowlist )
{
if( window->isSpecialWindow() )
continue;
if( window->isDeleted())
continue;
if( !mShowWindowsFromAllDesktops && !window->isOnCurrentDesktop() )
continue;
mWindowsToPresent.append(window);
}
rearrangeWindows();
}
else
{
mWindowsToPresent.clear();
mRearranging = 1; // turn off
mActiveness = 1; // go back from arranged position
discardFilterTexture();
}
effects->addRepaintFull(); // trigger next animation repaint
}
void PresentWindowsEffect::effectActivated()
{
// Create temporary input window to catch mouse events
mInput = effects->createFullScreenInputWindow( this, Qt::PointingHandCursor );
hasKeyboardGrab = effects->grabKeyboard( this );
}
void PresentWindowsEffect::effectTerminated()
{
// Destroy the temporary input window
effects->destroyInputWindow( mInput );
if( hasKeyboardGrab )
effects->ungrabKeyboard();
hasKeyboardGrab = false;
}
void PresentWindowsEffect::rearrangeWindows()
{
if( !mActivated )
return;
EffectWindowList windowlist;
if( windowFilter.isEmpty())
windowlist = mWindowsToPresent;
else
{
foreach( EffectWindow* w, mWindowsToPresent )
{
if( w->caption().contains( windowFilter, Qt::CaseInsensitive )
|| w->windowClass().contains( windowFilter, Qt::CaseInsensitive ))
windowlist.append( w );
}
}
if( windowlist.isEmpty())
{
mWindowData.clear();
effects->addRepaintFull();
return;
}
if( !mWindowData.isEmpty()) // this is not the first arranging
{
bool rearrange = canRearrangeClosest( windowlist ); // called before manipulating mWindowData
DataHash newdata;
EffectWindowList newlist = windowlist;
EffectWindowList oldlist = mWindowData.keys();
qSort( newlist );
qSort( oldlist );
for( DataHash::ConstIterator it = mWindowData.begin();
it != mWindowData.end();
++it )
if( windowlist.contains( it.key())) // remove windows that are not in the window list
newdata[ it.key() ] = *it;
mWindowData = newdata;
if( !rearrange && newlist == oldlist )
return;
for( DataHash::Iterator it = mWindowData.begin();
it != mWindowData.end();
++it )
{
(*it).old_area = (*it).area;
(*it).old_scale = (*it).scale;
}
// Initialize new entries
foreach( EffectWindow* w, windowlist )
if( !mWindowData.contains( w ))
{
mWindowData[ w ].hover = 0;
}
mRearranging = 0; // start animation again
}
// Calculate new positions and scales for windows
// calculateWindowTransformationsDumb( windowlist );
// calculateWindowTransformationsKompose( windowlist );
calculateWindowTransformationsClosest( windowlist );
// Schedule entire desktop to be repainted
effects->addRepaintFull();
}
void PresentWindowsEffect::calculateWindowTransformationsDumb(EffectWindowList windowlist)
{
// Calculate number of rows/cols
int rows = windowlist.count() / 4 + 1;
int cols = windowlist.count() / rows + windowlist.count() % rows;
// Get rect which we can use on current desktop. This excludes e.g. panels
QRect placementRect = effects->clientArea( PlacementArea, QPoint( 0, 0 ), 0 );
// Size of one cell
int cellwidth = placementRect.width() / cols;
int cellheight = placementRect.height() / rows;
kDebug() << k_funcinfo << "Got " << windowlist.count() << " clients, using " << rows << "x" << cols << " grid" << endl;
// Calculate position and scale factor for each window
int i = 0;
foreach( EffectWindow* window, windowlist )
{
// Row/Col of this window
int r = i / cols;
int c = i % cols;
mWindowData[window].hover = 0.0f;
mWindowData[window].scale = qMin(cellwidth / (float)window->width(), cellheight / (float)window->height());
mWindowData[window].area.setLeft(placementRect.left() + cellwidth * c);
mWindowData[window].area.setTop(placementRect.top() + cellheight * r);
mWindowData[window].area.setWidth((int)(window->width() * mWindowData[window].scale));
mWindowData[window].area.setHeight((int)(window->height() * mWindowData[window].scale));
kDebug() << k_funcinfo << "Window '" << window->caption() << "' gets moved to (" <<
mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
"), scale: " << mWindowData[window].scale << endl;
i++;
}
}
float PresentWindowsEffect::windowAspectRatio(EffectWindow* c)
{
return c->width() / (float)c->height();
}
int PresentWindowsEffect::windowWidthForHeight(EffectWindow* c, int h)
{
return (int)((h / (float)c->height()) * c->width());
}
int PresentWindowsEffect::windowHeightForWidth(EffectWindow* c, int w)
{
return (int)((w / (float)c->width()) * c->height());
}
void PresentWindowsEffect::calculateWindowTransformationsKompose(EffectWindowList windowlist)
{
// Get rect which we can use on current desktop. This excludes e.g. panels
QRect availRect = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
// Following code is taken from Kompose 0.5.4, src/komposelayout.cpp
int spacing = 10;
int rows, columns;
float parentRatio = availRect.width() / (float)availRect.height();
// Use more columns than rows when parent's width > parent's height
if ( parentRatio > 1 )
{
columns = (int)ceil( sqrt(windowlist.count()) );
rows = (int)ceil( (double)windowlist.count() / (double)columns );
}
else
{
rows = (int)ceil( sqrt(windowlist.count()) );
columns = (int)ceil( (double)windowlist.count() / (double)rows );
}
kDebug() << k_funcinfo << "Using " << rows << " rows & " << columns << " columns for " << windowlist.count() << " clients" << endl;
// Calculate width & height
int w = (availRect.width() - (columns+1) * spacing ) / columns;
int h = (availRect.height() - (rows+1) * spacing ) / rows;
EffectWindowList::iterator it( windowlist.begin() );
QList<QRect> geometryRects;
QList<int> maxRowHeights;
// Process rows
for ( int i=0; i<rows; ++i )
{
int xOffsetFromLastCol = 0;
int maxHeightInRow = 0;
// Process columns
for ( int j=0; j<columns; ++j )
{
EffectWindow* window;
// Check for end of List
if ( it == windowlist.end() )
break;
window = *it;
// Calculate width and height of widget
float ratio = windowAspectRatio(window);
int widgetw = 100;
int widgeth = 100;
int usableW = w;
int usableH = h;
// use width of two boxes if there is no right neighbour
if (window == windowlist.last() && j != columns-1)
{
usableW = 2*w;
}
++it; // We need access to the neighbour in the following
// expand if right neighbour has ratio < 1
if (j != columns-1 && it != windowlist.end() && windowAspectRatio(*it) < 1)
{
int addW = w - windowWidthForHeight(*it, h);
if ( addW > 0 )
{
usableW = w + addW;
}
}
if ( ratio == -1 )
{
widgetw = w;
widgeth = h;
}
else
{
double widthForHeight = windowWidthForHeight(window, usableH);
double heightForWidth = windowHeightForWidth(window, usableW);
if ( (ratio >= 1.0 && heightForWidth <= usableH) ||
(ratio < 1.0 && widthForHeight > usableW) )
{
widgetw = usableW;
widgeth = (int)heightForWidth;
}
else if ( (ratio < 1.0 && widthForHeight <= usableW) ||
(ratio >= 1.0 && heightForWidth > usableH) )
{
widgeth = usableH;
widgetw = (int)widthForHeight;
}
}
// Set the Widget's size
int alignmentXoffset = 0;
int alignmentYoffset = 0;
if ( i==0 && h > widgeth )
alignmentYoffset = h - widgeth;
if ( j==0 && w > widgetw )
alignmentXoffset = w - widgetw;
QRect geom( availRect.x() + j * (w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol,
availRect.y() + i * (h + spacing) + spacing + alignmentYoffset,
widgetw, widgeth );
geometryRects.append(geom);
// Set the x offset for the next column
if (alignmentXoffset==0)
xOffsetFromLastCol += widgetw-w;
if (maxHeightInRow < widgeth)
maxHeightInRow = widgeth;
}
maxRowHeights.append(maxHeightInRow);
}
int topOffset = 0;
for( int i = 0; i < rows; i++ )
{
for( int j = 0; j < columns; j++ )
{
int pos = i*columns + j;
if(pos >= windowlist.count())
break;
EffectWindow* window = windowlist[pos];
QRect geom = geometryRects[pos];
geom.setY( geom.y() + topOffset );
mWindowData[window].area = geom;
mWindowData[window].scale = geom.width() / (float)window->width();
mWindowData[window].hover = 0.0f;
kDebug() << k_funcinfo << "Window '" << window->caption() << "' gets moved to (" <<
mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
"), scale: " << mWindowData[window].scale << endl;
}
if ( maxRowHeights[i]-h > 0 )
topOffset += maxRowHeights[i]-h;
}
}
void PresentWindowsEffect::calculateWindowTransformationsClosest(EffectWindowList windowlist)
{
QRect area = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
int columns = int( ceil( sqrt( windowlist.count())));
int rows = int( ceil( windowlist.count() / double( columns )));
foreach( EffectWindow* w, windowlist )
mWindowData[ w ].slot = -1;
for(;;)
{
// Assign each window to the closest available slot
assignSlots( area, columns, rows );
// Leave only the closest window in each slot, remove further conflicts
getBestAssignments();
bool all_assigned = true;
foreach( EffectWindow* w, windowlist )
if( mWindowData[ w ].slot == -1 )
{
all_assigned = false;
break;
}
if( all_assigned )
break; // ok
}
int slotwidth = area.width() / columns;
int slotheight = area.height() / rows;
for( DataHash::Iterator it = mWindowData.begin();
it != mWindowData.end();
++it )
{
QRect geom( area.x() + ((*it).slot % columns ) * slotwidth,
area.y() + ((*it).slot / columns ) * slotheight,
slotwidth, slotheight );
geom.adjust( 10, 10, -10, -10 ); // borders
float scale;
EffectWindow* w = it.key();
if( geom.width() / float( w->width()) < geom.height() / float( w->height()))
{ // center vertically
scale = geom.width() / float( w->width());
geom.moveTop( geom.top() + ( geom.height() - int( w->height() * scale )) / 2 );
geom.setHeight( int( w->height() * scale ));
}
else
{ // center horizontally
scale = geom.height() / float( w->height());
geom.moveLeft( geom.left() + ( geom.width() - int( w->width() * scale )) / 2 );
geom.setWidth( int( w->width() * scale ));
}
(*it).area = geom;
(*it).scale = scale;
}
}
void PresentWindowsEffect::assignSlots( const QRect& area, int columns, int rows )
{
QVector< bool > taken;
taken.fill( false, columns * rows );
foreach( const WindowData& d, mWindowData )
{
if( d.slot != -1 )
taken[ d.slot ] = true;
}
int slotwidth = area.width() / columns;
int slotheight = area.height() / rows;
for( DataHash::Iterator it = mWindowData.begin();
it != mWindowData.end();
++it )
{
if( (*it).slot != -1 )
continue; // it already has a slot
QPoint pos = it.key()->geometry().center();
if( pos.x() < area.left())
pos.setX( area.left());
if( pos.x() > area.right())
pos.setX( area.right());
if( pos.y() < area.top())
pos.setY( area.top());
if( pos.y() > area.bottom())
pos.setY( area.bottom());
int distance = INT_MAX;
for( int x = 0;
x < columns;
++x )
for( int y = 0;
y < rows;
++y )
{
int slot = x + y * columns;
if( taken[ slot ] )
continue;
int xdiff = pos.x() - ( area.x() + slotwidth * x + slotwidth / 2 ); // slotwidth/2 for center
int ydiff = pos.y() - ( area.y() + slotheight * y + slotheight / 2 );
int dist = int( sqrt( xdiff * xdiff + ydiff * ydiff ));
if( dist < distance )
{
distance = dist;
(*it).slot = slot;
(*it).slot_distance = distance;
}
}
}
}
void PresentWindowsEffect::getBestAssignments()
{
for( DataHash::Iterator it1 = mWindowData.begin();
it1 != mWindowData.end();
++it1 )
{
for( DataHash::ConstIterator it2 = mWindowData.begin();
it2 != mWindowData.end();
++it2 )
{
if( it1.key() != it2.key() && (*it1).slot == (*it2).slot
&& (*it1).slot_distance >= (*it2).slot_distance )
{
(*it1).slot = -1;
}
}
}
}
bool PresentWindowsEffect::canRearrangeClosest(EffectWindowList windowlist)
{
QRect area = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
int columns = int( ceil( sqrt( windowlist.count())));
int rows = int( ceil( windowlist.count() / double( columns )));
int old_columns = int( ceil( sqrt( mWindowData.count())));
int old_rows = int( ceil( mWindowData.count() / double( columns )));
return old_columns != columns || old_rows != rows;
}
bool PresentWindowsEffect::borderActivated( ElectricBorder border )
{
if( border == borderActivate && !mActivated )
{
toggleActive();
return true;
}
if( border == borderActivateAll && !mActivated )
{
toggleActiveAllDesktops();
return true;
}
return false;
}
void PresentWindowsEffect::grabbedKeyboardEvent( QKeyEvent* e )
{
if( e->type() != QEvent::KeyPress )
return;
if( e->key() == Qt::Key_Escape )
{
setActive( false );
return;
}
if( e->key() == Qt::Key_Backspace )
{
if( !windowFilter.isEmpty())
{
windowFilter.remove( windowFilter.length() - 1, 1 );
updateFilterTexture();
rearrangeWindows();
}
return;
}
if( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
{
if( mHoverWindow != NULL )
{
effects->activateWindow( mHoverWindow );
setActive( false );
return;
}
if( mWindowData.count() == 1 ) // only one window shown
{
effects->activateWindow( mWindowData.begin().key());
setActive( false );
}
return;
}
if( !e->text().isEmpty())
{
windowFilter.append( e->text());
updateFilterTexture();
rearrangeWindows();
return;
}
}
void PresentWindowsEffect::discardFilterTexture()
{
#ifdef HAVE_OPENGL
delete filterTexture;
filterTexture = NULL;
#endif
}
void PresentWindowsEffect::updateFilterTexture()
{
#ifdef HAVE_OPENGL
discardFilterTexture();
if( windowFilter.isEmpty())
return;
QFont font;
font.setPointSize( font.pointSize() * 2 );
font.setBold( true );
QRect rect = QFontMetrics( font ).boundingRect( windowFilter );
const int border = 10;
rect.adjust( -border, -border, border, border );
QRect area = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
QImage im( rect.width(), rect.height(), QImage::Format_ARGB32 );
QColor col = QPalette().highlight();
col.setAlpha( 128 ); // 0.5
im.fill( col.rgba());
QPainter p( &im );
p.setFont( font );
p.setPen( QPalette().highlightedText());
p.drawText( -rect.topLeft(), windowFilter );
p.end();
filterTexture = new GLTexture( im );
filterTextureRect = QRect( area.x() + ( area.width() - rect.width()) / 2,
area.y() + ( area.height() - rect.height()) / 2, rect.width(), rect.height());
effects->addRepaint( filterTextureRect );
#endif
}
} // namespace
#include "presentwindows.moc"