kwin/effects/boxswitch.cpp
Luboš Luňák d6b7ef180d No kde3/qt3 support in kwin core. Saves about 150M of memory in gdb
by not loading the support libraries and the complete kdelibs stack it depends
on. Gdb taking 0.5M and lousy handling of near-OOM in Linux kernel
can be a pretty bad combination :(.


svn path=/trunk/KDE/kdebase/workspace/; revision=741856
2007-11-26 15:42:44 +00:00

631 lines
20 KiB
C++

/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Philip Falkner <philip.falkner@gmail.com>
You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#include <config-X11.h>
#include "boxswitch.h"
#include <QCursor>
#include <QMouseEvent>
#include <QPainter>
#include <QSize>
#include <kapplication.h>
#include <kcolorscheme.h>
#ifdef HAVE_OPENGL
#include <GL/gl.h>
#endif
namespace KWin
{
KWIN_EFFECT( boxswitch, BoxSwitchEffect )
BoxSwitchEffect::BoxSwitchEffect()
: mActivated( 0 )
, mMode( 0 )
, painting_desktop( 0 )
{
frame_margin = 10;
highlight_margin = 5;
#ifdef HAVE_XRENDER
alphaFormat = XRenderFindStandardFormat( display(), PictStandardARGB32 );
#endif
color_frame = KColorScheme( QPalette::Active, KColorScheme::Window ).background().color();
color_frame.setAlphaF( 0.9 );
color_highlight = KColorScheme( QPalette::Active, KColorScheme::Selection ).background().color();
color_highlight.setAlphaF( 0.9 );
color_text = KColorScheme( QPalette::Active, KColorScheme::Window ).foreground().color();
}
BoxSwitchEffect::~BoxSwitchEffect()
{
}
void BoxSwitchEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
{
if( mActivated )
{
if( mMode == TabBoxWindowsMode )
{
if( windows.contains( w ))
{
if( w != selected_window )
data.setTranslucent();
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_MINIMIZE );
}
}
else
{
if( painting_desktop )
{
if( w->isOnDesktop( painting_desktop ))
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
else
w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
}
}
}
effects->prePaintWindow( w, data, time );
}
void BoxSwitchEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
{
effects->paintScreen( mask, region, data );
if( mActivated )
{
if( mMode == TabBoxWindowsMode )
{
paintFrame();
foreach( EffectWindow* w, windows.keys())
{
if( w == selected_window )
{
paintHighlight( windows[ w ]->area );
}
paintWindowThumbnail( w );
paintWindowIcon( w );
}
paintText( selected_window->caption() );
}
else
{
if( !painting_desktop )
{
paintFrame();
foreach( painting_desktop, desktops.keys())
{
if( painting_desktop == selected_desktop )
{
paintHighlight( desktops[ painting_desktop ]->area ); //effects->desktopName( painting_desktop )
}
paintDesktopThumbnail( painting_desktop );
}
paintText( effects->desktopName( selected_desktop ));
painting_desktop = 0;
}
}
}
}
void BoxSwitchEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
if( mActivated )
{
if( mMode == TabBoxWindowsMode )
{
if( windows.contains( w ) && w != selected_window )
{
data.opacity *= 0.2;
}
}
}
effects->paintWindow( w, mask, region, data );
}
void BoxSwitchEffect::windowInputMouseEvent( Window w, QEvent* e )
{
assert( w == mInput );
if( e->type() != QEvent::MouseButtonPress )
return;
QPoint pos = static_cast< QMouseEvent* >( e )->pos();
pos += frame_area.topLeft();
// determine which item was clicked
if( mMode == TabBoxWindowsMode )
{
foreach( EffectWindow* w, windows.keys())
{
if( windows[ w ]->clickable.contains( pos ))
{
effects->setTabBoxWindow( w );
}
}
}
else
{
foreach( int i, desktops.keys())
{
if( desktops[ i ]->clickable.contains( pos ))
{
effects->setTabBoxDesktop( i );
}
}
}
}
void BoxSwitchEffect::windowDamaged( EffectWindow* w, const QRect& damage )
{
if( mActivated )
{
if( mMode == TabBoxWindowsMode )
{
if( windows.contains( w ))
{
effects->addRepaint( windows[ w ]->area );
}
}
else
{
if( w->isOnAllDesktops())
{
foreach( ItemInfo* info, desktops )
effects->addRepaint( info->area );
}
else
{
effects->addRepaint( desktops[ w->desktop() ]->area );
}
}
}
}
void BoxSwitchEffect::windowGeometryShapeChanged( EffectWindow* w, const QRect& old )
{
if( mActivated )
{
if( mMode == TabBoxWindowsMode )
{
if( windows.contains( w ) && w->size() != old.size())
{
effects->addRepaint( windows[ w ]->area );
}
}
else
{
if( w->isOnAllDesktops())
{
foreach( ItemInfo* info, desktops )
effects->addRepaint( info->area );
}
else
{
effects->addRepaint( desktops[ w->desktop() ]->area );
}
}
}
}
void BoxSwitchEffect::tabBoxAdded( int mode )
{
if( !mActivated )
{
if( mode == TabBoxWindowsMode )
{
if( effects->currentTabBoxWindowList().count() > 0 )
{
mMode = mode;
effects->refTabBox();
setActive();
}
}
else
{ // DesktopMode
if( effects->currentTabBoxDesktopList().count() > 0 )
{
mMode = mode;
painting_desktop = 0;
effects->refTabBox();
setActive();
}
}
}
}
void BoxSwitchEffect::tabBoxClosed()
{
if( mActivated )
setInactive();
}
void BoxSwitchEffect::tabBoxUpdated()
{
if( mActivated )
{
if( mMode == TabBoxWindowsMode )
{
if( selected_window != NULL )
{
if( windows.contains( selected_window ))
effects->addRepaint( windows.value( selected_window )->area );
selected_window->addRepaintFull();
}
selected_window = effects->currentTabBoxWindow();
if( windows.contains( selected_window ))
effects->addRepaint( windows.value( selected_window )->area );
selected_window->addRepaintFull();
effects->addRepaint( text_area );
if( effects->currentTabBoxWindowList() == original_windows )
return;
original_windows = effects->currentTabBoxWindowList();
}
else
{ // DesktopMode
if( desktops.contains( selected_desktop ))
effects->addRepaint( desktops.value( selected_desktop )->area );
selected_desktop = effects->currentTabBoxDesktop();
if( desktops.contains( selected_desktop ))
effects->addRepaint( desktops.value( selected_desktop )->area );
effects->addRepaint( text_area );
if( effects->currentTabBoxDesktopList() == original_desktops )
return;
original_desktops = effects->currentTabBoxDesktopList();
}
effects->addRepaint( frame_area );
calculateFrameSize();
calculateItemSizes();
moveResizeInputWindow( frame_area.x(), frame_area.y(), frame_area.width(), frame_area.height());
effects->addRepaint( frame_area );
}
}
void BoxSwitchEffect::setActive()
{
mActivated = true;
if( mMode == TabBoxWindowsMode )
{
original_windows = effects->currentTabBoxWindowList();
selected_window = effects->currentTabBoxWindow();
}
else
{
original_desktops = effects->currentTabBoxDesktopList();
selected_desktop = effects->currentTabBoxDesktop();
}
calculateFrameSize();
calculateItemSizes();
mInput = effects->createInputWindow( this, frame_area.x(), frame_area.y(),
frame_area.width(), frame_area.height(), Qt::ArrowCursor );
effects->addRepaint( frame_area );
if( mMode == TabBoxWindowsMode )
{
foreach( EffectWindow* w, windows.keys())
{
w->addRepaintFull();
}
}
}
void BoxSwitchEffect::setInactive()
{
mActivated = false;
effects->unrefTabBox();
if( mInput != None )
{
effects->destroyInputWindow( mInput );
mInput = None;
}
if( mMode == TabBoxWindowsMode )
{
foreach( EffectWindow* w, windows.keys())
{
if( w != selected_window )
w->addRepaintFull();
}
foreach( ItemInfo* i, windows )
{
#ifdef HAVE_XRENDER
if( effects->compositingType() == XRenderCompositing )
{
if( i->iconPicture != None )
XRenderFreePicture( display(), i->iconPicture );
i->iconPicture = None;
}
#endif
delete i;
}
windows.clear();
}
else
{ // DesktopMode
foreach( ItemInfo* i, desktops )
delete i;
desktops.clear();
}
effects->addRepaint( frame_area );
frame_area = QRect();
}
void BoxSwitchEffect::moveResizeInputWindow( int x, int y, int width, int height )
{
XMoveWindow( display(), mInput, x, y );
XResizeWindow( display(), mInput, width, height );
}
void BoxSwitchEffect::calculateFrameSize()
{
int itemcount;
if( mMode == TabBoxWindowsMode )
{
itemcount = original_windows.count();
item_max_size.setWidth( 200 );
item_max_size.setHeight( 200 );
}
else
{
itemcount = original_desktops.count();
item_max_size.setWidth( 200 );
item_max_size.setHeight( 200 );
}
// How much height to reserve for a one-line text label
text_font.setBold( true );
text_font.setPointSize( 12 );
text_area.setHeight( QFontMetrics( text_font ).height() * 1.2 );
// Separator space between items and text
const int separator_height = 6;
// Shrink the size until all windows/desktops can fit onscreen
frame_area.setWidth( frame_margin * 2 + itemcount * item_max_size.width());
QRect screenr = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop());
while( frame_area.width() > screenr.width())
{
item_max_size /= 2;
frame_area.setWidth( frame_margin * 2 + itemcount * item_max_size.width());
}
frame_area.setHeight( frame_margin * 2 + item_max_size.height() +
separator_height + text_area.height());
text_area.setWidth( frame_area.width() - frame_margin * 2 );
frame_area.moveTo( screenr.x() + ( screenr.width() - frame_area.width()) / 2,
screenr.y() + ( screenr.height() - frame_area.height()) / 2 );
text_area.moveTo( frame_area.x() + frame_margin,
frame_area.y() + frame_margin + item_max_size.height() + separator_height);
}
void BoxSwitchEffect::calculateItemSizes()
{
if( mMode == TabBoxWindowsMode )
{
windows.clear();
for( int i = 0; i < original_windows.count(); i++ )
{
EffectWindow* w = original_windows.at( i );
windows[ w ] = new ItemInfo();
windows[ w ]->area = QRect( frame_area.x() + frame_margin
+ i * item_max_size.width(),
frame_area.y() + frame_margin,
item_max_size.width(), item_max_size.height());
windows[ w ]->clickable = windows[ w ]->area;
}
}
else
{
desktops.clear();
for( int i = 0; i < original_desktops.count(); i++ )
{
int it = original_desktops.at( i );
desktops[ it ] = new ItemInfo();
desktops[ it ]->area = QRect( frame_area.x() + frame_margin
+ i * item_max_size.width(),
frame_area.y() + frame_margin,
item_max_size.width(), item_max_size.height());
desktops[ it ]->clickable = desktops[ it ]->area;
}
}
}
void BoxSwitchEffect::paintFrame()
{
#ifdef HAVE_OPENGL
if( effects->compositingType() == OpenGLCompositing )
{
glPushAttrib( GL_CURRENT_BIT );
glColor4f( color_frame.redF(), color_frame.greenF(), color_frame.blueF(), color_frame.alphaF());
renderRoundBoxWithEdge( frame_area );
glPopAttrib();
}
#endif
#ifdef HAVE_XRENDER
if( effects->compositingType() == XRenderCompositing )
{
Pixmap pixmap = XCreatePixmap( display(), rootWindow(),
frame_area.width(), frame_area.height(), 32 );
Picture pic = XRenderCreatePicture( display(), pixmap, alphaFormat, 0, NULL );
XFreePixmap( display(), pixmap );
XRenderColor col;
col.alpha = int( color_frame.alphaF() * 0xffff );
col.red = int( color_frame.redF() * color_frame.alphaF() * 0xffff );
col.green = int( color_frame.greenF() * color_frame.alphaF() * 0xffff );
col.blue = int( color_frame.blueF() * color_frame.alphaF() * 0xffff );
XRenderFillRectangle( display(), PictOpSrc, pic, &col, 0, 0,
frame_area.width(), frame_area.height());
XRenderComposite( display(), color_frame.alphaF() != 1.0 ? PictOpOver : PictOpSrc,
pic, None, effects->xrenderBufferPicture(),
0, 0, 0, 0, frame_area.x(), frame_area.y(), frame_area.width(), frame_area.height());
XRenderFreePicture( display(), pic );
}
#endif
}
void BoxSwitchEffect::paintHighlight( QRect area )
{
#ifdef HAVE_OPENGL
if( effects->compositingType() == OpenGLCompositing )
{
glPushAttrib( GL_CURRENT_BIT );
glColor4f( color_highlight.redF(), color_highlight.greenF(), color_highlight.blueF(), color_highlight.alphaF());
renderRoundBox( area, 6 );
glPopAttrib();
}
#endif
#ifdef HAVE_XRENDER
if( effects->compositingType() == XRenderCompositing )
{
Pixmap pixmap = XCreatePixmap( display(), rootWindow(),
area.width(), area.height(), 32 );
Picture pic = XRenderCreatePicture( display(), pixmap, alphaFormat, 0, NULL );
XFreePixmap( display(), pixmap );
XRenderColor col;
col.alpha = int( color_highlight.alphaF() * 0xffff );
col.red = int( color_highlight.redF() * color_highlight.alphaF() * 0xffff );
col.green = int( color_highlight.greenF() * color_highlight.alphaF() * 0xffff );
col.blue = int( color_highlight.blueF() * color_highlight.alphaF() * 0xffff );
XRenderFillRectangle( display(), PictOpSrc, pic, &col, 0, 0,
area.width(), area.height());
XRenderComposite( display(), color_highlight.alphaF() != 1.0 ? PictOpOver : PictOpSrc,
pic, None, effects->xrenderBufferPicture(),
0, 0, 0, 0, area.x(), area.y(), area.width(), area.height());
XRenderFreePicture( display(), pic );
}
#endif
}
void BoxSwitchEffect::paintWindowThumbnail( EffectWindow* w )
{
if( !windows.contains( w ))
return;
WindowPaintData data( w );
setPositionTransformations( data,
windows[ w ]->thumbnail, w,
windows[ w ]->area.adjusted( highlight_margin, highlight_margin, -highlight_margin, -highlight_margin ),
Qt::KeepAspectRatio );
effects->drawWindow( w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
windows[ w ]->thumbnail, data );
}
void BoxSwitchEffect::paintDesktopThumbnail( int iDesktop )
{
if( !desktops.contains( iDesktop ))
return;
ScreenPaintData data;
QRect region;
QRect r = desktops[ iDesktop ]->area.adjusted( highlight_margin, highlight_margin,
-highlight_margin, -highlight_margin );
QSize size = QSize( displayWidth(), displayHeight());
size.scale( r.size(), Qt::KeepAspectRatio );
data.xScale = size.width() / double( displayWidth());
data.yScale = size.height() / double( displayHeight());
int width = int( displayWidth() * data.xScale );
int height = int( displayHeight() * data.yScale );
int x = r.x() + ( r.width() - width ) / 2;
int y = r.y() + ( r.height() - height ) / 2;
region = QRect( x, y, width, height );
data.xTranslate = x;
data.yTranslate = y;
effects->paintScreen( PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST,
region, data );
}
void BoxSwitchEffect::paintWindowIcon( EffectWindow* w )
{
if( !windows.contains( w ))
return;
// Don't render null icons
if( w->icon().isNull() )
{
return;
}
if( windows[ w ]->icon.serialNumber() != w->icon().serialNumber())
{ // make sure windows[ w ]->icon is the right QPixmap, and rebind
windows[ w ]->icon = w->icon();
#ifdef HAVE_OPENGL
if( effects->compositingType() == OpenGLCompositing )
{
windows[ w ]->iconTexture.load( windows[ w ]->icon );
windows[ w ]->iconTexture.setFilter( GL_LINEAR );
}
#endif
#ifdef HAVE_XRENDER
if( effects->compositingType() == XRenderCompositing )
{
if( windows[ w ]->iconPicture != None )
XRenderFreePicture( display(), windows[ w ]->iconPicture );
windows[ w ]->iconPicture = XRenderCreatePicture( display(),
windows[ w ]->icon.handle(), alphaFormat, 0, NULL );
}
#endif
}
int width = windows[ w ]->icon.width();
int height = windows[ w ]->icon.height();
int x = windows[ w ]->area.x() + windows[ w ]->area.width() - width - highlight_margin;
int y = windows[ w ]->area.y() + windows[ w ]->area.height() - height - highlight_margin;
#ifdef HAVE_OPENGL
if( effects->compositingType() == OpenGLCompositing )
{
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Render some background
glColor4f( 0, 0, 0, 0.5 );
renderRoundBox( QRect( x-3, y-3, width+6, height+6 ), 3 );
// Render the icon
glColor4f( 1, 1, 1, 1 );
windows[ w ]->iconTexture.bind();
const float verts[ 4 * 2 ] =
{
x, y,
x, y + height,
x + width, y + height,
x + width, y
};
const float texcoords[ 4 * 2 ] =
{
0, 1,
0, 0,
1, 0,
1, 1
};
renderGLGeometry( 4, verts, texcoords );
windows[ w ]->iconTexture.unbind();
glPopAttrib();
}
#endif
#ifdef HAVE_XRENDER
if( effects->compositingType() == XRenderCompositing )
{
XRenderComposite( display(),
windows[ w ]->icon.depth() == 32 ? PictOpOver : PictOpSrc,
windows[ w ]->iconPicture, None,
effects->xrenderBufferPicture(),
0, 0, 0, 0, x, y, width, height );
}
#endif
}
void BoxSwitchEffect::paintText( const QString& text )
{
int maxwidth = text_area.width();
effects->paintText( text, text_area.center(), maxwidth, color_text, text_font );
}
} // namespace