/***************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Philip Falkner You can Freely distribute this program under the GNU General Public License. See the file "COPYING" for the exact licensing terms. ******************************************************************/ #include #include "boxswitch.h" #include #include #include #include #include #include #ifdef HAVE_OPENGL #include #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 } BoxSwitchEffect::~BoxSwitchEffect() { } void BoxSwitchEffect::prePaintScreen( ScreenPrePaintData& data, int time ) { effects->prePaintScreen( data, time ); } void BoxSwitchEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) { if( mActivated ) { if( mMode == TabBoxWindowsMode ) { if( windows.contains( w ) && w != selected_window ) data.setTranslucent(); } 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( text_area, 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( text_area, 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()) { if( w != selected_window ) 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_area.setHeight( int( kapp->fontMetrics().height() * 1.2 )); // Shrink the size until all windows/desktops can fit onscreen frame_area.setWidth( frame_margin * 2 + itemcount * item_max_size.width()); while( frame_area.width() > displayWidth()) { 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() + text_area.height()); text_area.setWidth( frame_area.width() - frame_margin * 2 ); frame_area.moveTo(( displayWidth() - frame_area.width()) / 2, ( displayHeight() - frame_area.height()) / 2 ); text_area.moveTo( frame_area.x() + frame_margin, frame_area.y() + frame_margin + item_max_size.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() { QColor color = KColorScheme( KColorScheme::Window ).background(); color.setAlphaF( 0.9 ); #ifdef HAVE_OPENGL if( effects->compositingType() == OpenGLCompositing ) { glPushAttrib( GL_CURRENT_BIT ); glColor4f( color.redF(), color.greenF(), color.blueF(), color.alphaF()); renderRoundBox( 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.alphaF() * 0xffff ); col.red = int( color.redF() * color.alphaF() * 0xffff ); col.green = int( color.greenF() * color.alphaF() * 0xffff ); col.blue = int( color.blueF() * color.alphaF() * 0xffff ); XRenderFillRectangle( display(), PictOpSrc, pic, &col, 0, 0, frame_area.width(), frame_area.height()); XRenderComposite( display(), color.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 ) { QColor color = KColorScheme( KColorScheme::Selection ).background(); color.setAlphaF( 0.9 ); #ifdef HAVE_OPENGL if( effects->compositingType() == OpenGLCompositing ) { glPushAttrib( GL_CURRENT_BIT ); glColor4f( color.redF(), color.greenF(), color.blueF(), color.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.alphaF() * 0xffff ); col.red = int( color.redF() * color.alphaF() * 0xffff ); col.green = int( color.greenF() * color.alphaF() * 0xffff ); col.blue = int( color.blueF() * color.alphaF() * 0xffff ); XRenderFillRectangle( display(), PictOpSrc, pic, &col, 0, 0, area.width(), area.height()); XRenderComposite( display(), color.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( QRect area, QString text ) { QPainter p; textPixmap = QPixmap( area.width(), area.height()); textPixmap.fill( Qt::transparent ); p.begin( &textPixmap ); p.setRenderHint( QPainter::TextAntialiasing ); p.setPen( KColorScheme( KColorScheme::Window ).foreground()); p.drawText( 0, 0, textPixmap.width(), textPixmap.height(), Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, p.fontMetrics().elidedText( text, Qt::ElideMiddle, textPixmap.width())); p.end(); #ifdef HAVE_OPENGL if( effects->compositingType() == OpenGLCompositing ) { textTexture.load( textPixmap, GL_TEXTURE_RECTANGLE_ARB ); glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); textTexture.bind(); const float verts[ 4 * 2 ] = { area.x(), area.y(), area.x(), area.y() + area.height(), area.x() + area.width(), area.y() + area.height(), area.x() + area.width(), area.y() }; const float texcoords[ 4 * 2 ] = { 0, textPixmap.height(), 0, 0, textPixmap.width(), 0, textPixmap.width(), textPixmap.height() }; renderGLGeometry( 4, verts, texcoords ); textTexture.unbind(); glPopAttrib(); } #endif #ifdef HAVE_XRENDER if( effects->compositingType() == XRenderCompositing ) { if( textPicture != None ) XRenderFreePicture( display(), textPicture ); textPicture = XRenderCreatePicture( display(), textPixmap.handle(), alphaFormat, 0, NULL ); XRenderComposite( display(), textPixmap.depth() == 32 ? PictOpOver : PictOpSrc, textPicture, None, effects->xrenderBufferPicture(), 0, 0, 0, 0, area.x(), area.y(), area.width(), area.height()); } #endif } } // namespace