From 0516e1e73b12dd6e5e161ffa62703ad92d4efd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Thu, 6 Jul 2006 13:17:44 +0000 Subject: [PATCH] Add support for translation and add two "plugins" that make windows transparent or shake them while moving. svn path=/branches/work/kwin_composite/; revision=559013 --- client.cpp | 58 ++------------ client.h | 43 +++++------ composite.cpp | 46 ++++++++++- effects.cpp | 190 ++++++++++++++++++++++++++++++++++++++++------ effects.h | 73 ++++++++++++++++-- events.cpp | 8 +- geometry.cpp | 22 +++--- manage.cpp | 2 +- scene.cpp | 8 ++ scene.h | 2 + scene_xrender.cpp | 77 +++++++++++++++++-- scene_xrender.h | 5 +- toplevel.h | 59 ++++++++++++++ unmanaged.cpp | 7 +- unmanaged.h | 1 + utils.cpp | 16 +++- utils.h | 14 ++++ workspace.h | 3 + 18 files changed, 498 insertions(+), 136 deletions(-) diff --git a/client.cpp b/client.cpp index 6acf9ee17e..1bb7966bec 100644 --- a/client.cpp +++ b/client.cpp @@ -1600,58 +1600,6 @@ bool Client::wantsInput() const return rules()->checkAcceptFocus( input || Ptakefocus ); } -bool Client::isDesktop() const - { - return windowType() == NET::Desktop; - } - -bool Client::isDock() const - { - return windowType() == NET::Dock; - } - -bool Client::isTopMenu() const - { - return windowType() == NET::TopMenu; - } - - -bool Client::isMenu() const - { - return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. - } - -bool Client::isToolbar() const - { - return windowType() == NET::Toolbar; - } - -bool Client::isSplash() const - { - return windowType() == NET::Splash; - } - -bool Client::isUtility() const - { - return windowType() == NET::Utility; - } - -bool Client::isDialog() const - { - return windowType() == NET::Dialog; - } - -bool Client::isNormalWindow() const - { - return windowType() == NET::Normal; - } - -bool Client::isSpecialWindow() const - { - return isDesktop() || isDock() || isSplash() || isTopMenu() - || isToolbar(); // TODO - } - NET::WindowType Client::windowType( bool direct, int supported_types ) const { NET::WindowType wt = info->windowType( supported_types ); @@ -1683,6 +1631,12 @@ NET::WindowType Client::windowType( bool direct, int supported_types ) const return wt; } +bool Client::isSpecialWindow() const + { + return isDesktop() || isDock() || isSplash() || isTopMenu() + || isToolbar(); // TODO + } + /*! Sets an appropriate cursor shape for the logical mouse position \a m diff --git a/client.h b/client.h index 38b77802a6..c0b2e58f21 100644 --- a/client.h +++ b/client.h @@ -67,12 +67,17 @@ class Client const Group* group() const; Group* group(); void checkGroup( Group* gr = NULL, bool force = false ); - // prefer isXXX() instead - NET::WindowType windowType( bool direct = false, int supported_types = SUPPORTED_WINDOW_TYPES_MASK ) const; const WindowRules* rules() const; void removeRule( Rules* r ); void setupWindowRules( bool ignore_temporary ); void applyWindowRules(); + virtual NET::WindowType windowType( bool direct = false, int supported_types = SUPPORTED_WINDOW_TYPES_MASK ) const; + // returns true for "special" windows and false for windows which are "normal" + // (normal=window which has a border, can be moved by the user, can be closed, etc.) + // true for Desktop, Dock, Splash, Override and TopMenu (and Toolbar??? - for now) + // false for Normal, Dialog, Utility and Menu (and Toolbar??? - not yet) TODO + bool isSpecialWindow() const; + bool hasNETSupport() const; QSize minSize() const; QSize maxSize() const; @@ -158,24 +163,9 @@ class Client // auxiliary functions, depend on the windowType bool wantsTabFocus() const; bool wantsInput() const; - bool hasNETSupport() const; - bool isMovable() const; - bool isDesktop() const; - bool isDock() const; - bool isToolbar() const; - bool isTopMenu() const; - bool isMenu() const; - bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient' - bool isDialog() const; - bool isSplash() const; - bool isUtility() const; - // returns true for "special" windows and false for windows which are "normal" - // (normal=window which has a border, can be moved by the user, can be closed, etc.) - // true for Desktop, Dock, Splash, Override and TopMenu (and Toolbar??? - for now) - // false for Normal, Dialog, Utility and Menu (and Toolbar??? - not yet) TODO - bool isSpecialWindow() const; bool isResizable() const; + bool isMovable() const; bool isCloseable() const; // may be closed by the user (may have a close button) void takeActivity( int flags, bool handled, allowed_t ); // takes ActivityFlags as arg (in utils.h) @@ -282,6 +272,15 @@ class Client void destroyClient(); void checkActiveModal(); bool hasStrut() const; + + bool isMove() const + { + return moveResizeMode && mode == PositionCenter; + } + bool isResize() const + { + return moveResizeMode && mode != PositionCenter; + } private slots: void autoRaise(); @@ -411,14 +410,6 @@ class Client bool move_faked_activity; Window move_resize_grab_window; bool unrestrictedMoveResize; - bool isMove() const - { - return moveResizeMode && mode == PositionCenter; - } - bool isResize() const - { - return moveResizeMode && mode != PositionCenter; - } Position mode; QPoint moveOffset; diff --git a/composite.cpp b/composite.cpp index 16955d2b19..0da1293167 100644 --- a/composite.cpp +++ b/composite.cpp @@ -74,6 +74,8 @@ void Workspace::addDamage( const QRect& r ) void Workspace::addDamage( int x, int y, int w, int h ) { + if( !compositing()) + return; XRectangle r; r.x = x; r.y = y; @@ -103,7 +105,45 @@ void Workspace::addDamage( XserverRegion r, bool destroy ) } } } - + +void Workspace::addDamage( Toplevel* c, const QRect& r ) + { + addDamage( c, r.x(), r.y(), r.width(), r.height()); + } + +void Workspace::addDamage( Toplevel* c, int x, int y, int w, int h ) + { + if( !compositing()) + return; + XRectangle r; + r.x = x; + r.y = y; + r.width = w; + r.height = h; + addDamage( c, XFixesCreateRegion( display(), &r, 1 ), true ); + } + +void Workspace::addDamage( Toplevel* c, XserverRegion r, bool destroy ) + { + if( !compositing()) + return; + if( !destroy ) + { + XserverRegion r2 = XFixesCreateRegion( display(), NULL, 0 ); + XFixesCopyRegion( display(), r2, r ); + r = r2; + destroy = true; + } + scene->transformWindowDamage( c, r ); + if( damage != None ) + { + XFixesUnionRegion( display(), damage, damage, r ); + XFixesDestroyRegion( display(), r ); + } + else + damage = r; + } + void Workspace::compositeTimeout() { if( damage == None ) @@ -177,9 +217,9 @@ void Toplevel::damageNotifyEvent( XDamageNotifyEvent* e ) { XserverRegion r = XFixesCreateRegion( display(), &e->area, 1 ); XFixesTranslateRegion( display(), r, x(), y()); - workspace()->addDamage( r, true ); + workspace()->addDamage( this, r, true ); } - + #endif } // namespace diff --git a/effects.cpp b/effects.cpp index d551f8a446..3c527b495d 100644 --- a/effects.cpp +++ b/effects.cpp @@ -10,6 +10,10 @@ License. See the file "COPYING" for the exact licensing terms. #include "effects.h" +#include "toplevel.h" +#include "client.h" +#include "scene.h" + namespace KWinInternal { @@ -37,6 +41,67 @@ Matrix::Matrix() m[ 3 ][ 3 ] = 1; } +Matrix operator*( const Matrix& m1, const Matrix& m2 ) + { + Matrix r; + for( int i = 0; + i < 4; + ++i ) + for( int j = 0; + j < 4; + ++j ) + { + double s = 0; + for( int k = 0; + k < 4; + ++k ) + s += m1.m[ i ][ k ] * m2.m[ k ][ j ]; + r.m[ i ][ j ] = s; + } + return r; + } + +bool Matrix::isIdentity() const + { + return m[ 0 ][ 0 ] == 1 + && m[ 0 ][ 1 ] == 0 + && m[ 0 ][ 2 ] == 0 + && m[ 0 ][ 3 ] == 0 + && m[ 1 ][ 0 ] == 0 + && m[ 1 ][ 1 ] == 1 + && m[ 1 ][ 2 ] == 0 + && m[ 1 ][ 3 ] == 0 + && m[ 2 ][ 0 ] == 0 + && m[ 2 ][ 1 ] == 0 + && m[ 2 ][ 2 ] == 1 + && m[ 2 ][ 3 ] == 0 + && m[ 3 ][ 0 ] == 0 + && m[ 3 ][ 1 ] == 0 + && m[ 3 ][ 2 ] == 0 + && m[ 3 ][ 3 ] == 1; + } + + +bool Matrix::isOnlyTranslate() const + { + return m[ 0 ][ 0 ] == 1 + && m[ 0 ][ 1 ] == 0 + && m[ 0 ][ 2 ] == 0 +// && m[ 0 ][ 3 ] == + && m[ 1 ][ 0 ] == 0 + && m[ 1 ][ 1 ] == 1 + && m[ 1 ][ 2 ] == 0 +// && m[ 1 ][ 3 ] == + && m[ 2 ][ 0 ] == 0 + && m[ 2 ][ 1 ] == 0 + && m[ 2 ][ 2 ] == 1 +// && m[ 2 ][ 3 ] == + && m[ 3 ][ 0 ] == 0 + && m[ 3 ][ 1 ] == 0 + && m[ 3 ][ 2 ] == 0 + && m[ 3 ][ 3 ] == 1; + } + //**************************************** // Effect //**************************************** @@ -45,61 +110,142 @@ Effect::~Effect() { } -void Effect::windowUserMoved( Toplevel* ) +void Effect::windowUserMovedResized( Toplevel* , bool, bool ) { } -void Effect::windowUserResized( Toplevel* ) +void Effect::transformWindow( Toplevel*, EffectData& ) { } -void Effect::paintWindow( Toplevel*, EffectData& ) +void Effect::transformWorkspace( Workspace*, EffectData& ) { } -void Effect::paintWorkspace( Workspace*, EffectData& ) +void Effect::windowDeleted( Toplevel* ) { } +void MakeHalfTransparent::transformWindow( Toplevel* c, EffectData& data ) + { + if( c->isDialog()) + data.opacity *= 0.8; + if( Client* c2 = dynamic_cast< Client* >( c )) + if( c2->isMove() || c2->isResize()) + data.opacity *= 0.5; + } + +void MakeHalfTransparent::windowUserMovedResized( Toplevel* c, bool first, bool last ) + { + if( first || last ) + c->workspace()->addDamage( c, c->geometry()); + } + +ShakyMove::ShakyMove() + { + connect( &timer, SIGNAL( timeout()), SLOT( tick())); + } + +static const int shaky_diff[] = { 0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1 }; +static const int SHAKY_MAX = sizeof( shaky_diff ) / sizeof( shaky_diff[ 0 ] ); + +void ShakyMove::transformWindow( Toplevel* c, EffectData& data ) + { + if( windows.contains( c )) + { + Matrix m; + m.m[ 0 ][ 3 ] = shaky_diff[ windows[ c ]]; + data.matrix *= m; + } + } + +void ShakyMove::windowUserMovedResized( Toplevel* c, bool first, bool last ) + { + if( first ) + { + if( windows.isEmpty()) + timer.start( 50 ); + windows[ c ] = 0; + } + else if( last ) + { + windows.remove( c ); + scene->updateTransformation( c ); + c->workspace()->addDamage( c, c->geometry().adjusted( -3, 7, 0, 0 )); + if( windows.isEmpty()) + timer.stop(); + } + } + +void ShakyMove::windowDeleted( Toplevel* c ) + { + windows.remove( c ); + if( windows.isEmpty()) + timer.stop(); + } + +void ShakyMove::tick() + { + for( QMap< Toplevel*, int >::Iterator it = windows.begin(); + it != windows.end(); + ++it ) + { + if( *it == SHAKY_MAX - 1 ) + *it = 0; + else + ++(*it); + scene->updateTransformation( it.key()); + it.key()->workspace()->addDamage( it.key(), it.key()->geometry().adjusted( -1, 2, 0, 0 )); + } + } + +static MakeHalfTransparent* mht; +static ShakyMove* sm; + //**************************************** // EffectsHandler //**************************************** -class MakeHalfTransparent - : public Effect - { - public: - virtual void paintWindow( Toplevel* c, EffectData& data ); - }; - -void MakeHalfTransparent::paintWindow( Toplevel*, EffectData& data ) - { - data.opacity *= 0.8; - } - -static MakeHalfTransparent* mht; EffectsHandler::EffectsHandler() { mht = new MakeHalfTransparent; + sm = new ShakyMove; } -void EffectsHandler::windowUserMoved( Toplevel* ) +void EffectsHandler::windowUserMovedResized( Toplevel* c, bool first, bool last ) { + if( mht ) + mht->windowUserMovedResized( c, first, last ); + if( sm ) + sm->windowUserMovedResized( c, first, last ); } -void EffectsHandler::windowUserResized( Toplevel* ) +void EffectsHandler::transformWindow( Toplevel* c, EffectData& data ) { + if( mht ) + mht->transformWindow( c, data ); + if( sm ) + sm->transformWindow( c, data ); } -void EffectsHandler::paintWindow( Toplevel* c, EffectData& data ) +void EffectsHandler::transformWorkspace( Workspace* w, EffectData& data ) { - mht->paintWindow( c, data ); + if( mht ) + mht->transformWorkspace( w, data ); + if( sm ) + sm->transformWorkspace( w, data ); } -void EffectsHandler::paintWorkspace( Workspace*, EffectData& ) +void EffectsHandler::windowDeleted( Toplevel* c ) { + if( mht ) + mht->windowDeleted( c ); + if( sm ) + sm->windowDeleted( c ); } EffectsHandler* effects; } // namespace + +#include "effects.moc" diff --git a/effects.h b/effects.h index 775ffe58e8..6997c5d226 100644 --- a/effects.h +++ b/effects.h @@ -13,6 +13,9 @@ License. See the file "COPYING" for the exact licensing terms. #ifndef KWIN_EFFECTS_H #define KWIN_EFFECTS_H +#include +#include + namespace KWinInternal { @@ -23,9 +26,37 @@ class Matrix { public: Matrix(); + Matrix& operator*=( const Matrix& m ); + bool isOnlyTranslate() const; + bool isIdentity() const; + double xTranslate() const; + double yTranslate() const; + double zTranslate() const; double m[ 4 ][ 4 ]; }; +Matrix operator*( const Matrix& m1, const Matrix& m2 ); + +inline Matrix& Matrix::operator*=( const Matrix& m ) + { + return *this = *this * m; + } + +inline double Matrix::xTranslate() const + { + return m[ 0 ][ 3 ]; + } + +inline double Matrix::yTranslate() const + { + return m[ 1 ][ 3 ]; + } + +inline double Matrix::zTranslate() const + { + return m[ 2 ][ 3 ]; + } + class EffectData { public: @@ -37,24 +68,50 @@ class Effect { public: virtual ~Effect(); - virtual void windowUserMoved( Toplevel* c ); - virtual void windowUserResized( Toplevel* c ); - virtual void paintWindow( Toplevel* c, EffectData& data ); - virtual void paintWorkspace( Workspace*, EffectData& data ); + // called when moved/resized or once after it's finished + virtual void windowUserMovedResized( Toplevel* c, bool first, bool last ); + virtual void windowDeleted( Toplevel* c ); + virtual void transformWindow( Toplevel* c, EffectData& data ); + virtual void transformWorkspace( Workspace*, EffectData& data ); }; class EffectsHandler { public: EffectsHandler(); - void windowUserMoved( Toplevel* c ); - void windowUserResized( Toplevel* c ); - void paintWindow( Toplevel* c, EffectData& data ); - void paintWorkspace( Workspace*, EffectData& data ); + void windowUserMovedResized( Toplevel* c, bool first, bool last ); + void windowDeleted( Toplevel* c ); + void transformWindow( Toplevel* c, EffectData& data ); + void transformWorkspace( Workspace*, EffectData& data ); }; extern EffectsHandler* effects; +class MakeHalfTransparent + : public Effect + { + public: + virtual void windowUserMovedResized( Toplevel* c, bool first, bool last ); + virtual void transformWindow( Toplevel* c, EffectData& data ); + }; + +class ShakyMove + : public QObject, public Effect + { + Q_OBJECT + public: + ShakyMove(); + virtual void windowUserMovedResized( Toplevel* c, bool first, bool last ); + virtual void transformWindow( Toplevel* c, EffectData& data ); + virtual void windowDeleted( Toplevel* c ); + private slots: + void tick(); + private: + QMap< Toplevel*, int > windows; + QTimer timer; + }; + + } // namespace #endif diff --git a/events.cpp b/events.cpp index 5bcb9a179d..4964e09544 100644 --- a/events.cpp +++ b/events.cpp @@ -577,7 +577,7 @@ bool Client::windowEvent( XEvent* e ) { if( compositing()) { - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); scene->windowOpacityChanged( this ); } else @@ -1613,7 +1613,7 @@ bool Unmanaged::windowEvent( XEvent* e ) if( dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity ) { scene->windowOpacityChanged( this ); - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); } switch (e->type) { @@ -1647,9 +1647,9 @@ void Unmanaged::configureNotifyEvent( XConfigureEvent* e ) { resetWindowPixmap(); // add old and new geometry to damage - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); geom = QRect( e->x, e->y, e->width, e->height ); - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); } // **************************************** diff --git a/geometry.cpp b/geometry.cpp index 6506ad2dc9..6e329cf02a 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1660,7 +1660,7 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) } if( force == NormalGeometrySet && geom == QRect( x, y, w, h )) return; - workspace()->addDamage( geometry()); // TODO cache the previous real geometry + workspace()->addDamage( this, geometry()); // TODO cache the previous real geometry geom = QRect( x, y, w, h ); updateWorkareaDiffs(); if( postpone_geometry_updates != 0 ) @@ -1686,7 +1686,7 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) updateWindowRules(); checkMaximizeGeometry(); resetWindowPixmap(); - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); } void Client::plainResize( int w, int h, ForceGeometry_t force ) @@ -1718,7 +1718,7 @@ void Client::plainResize( int w, int h, ForceGeometry_t force ) } if( force == NormalGeometrySet && geom.size() == QSize( w, h )) return; - workspace()->addDamage( geometry()); // TODO cache the previous real geometry + workspace()->addDamage( this, geometry()); // TODO cache the previous real geometry geom.setSize( QSize( w, h )); updateWorkareaDiffs(); if( postpone_geometry_updates != 0 ) @@ -1743,7 +1743,7 @@ void Client::plainResize( int w, int h, ForceGeometry_t force ) updateWindowRules(); checkMaximizeGeometry(); resetWindowPixmap(); - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); } /*! @@ -1753,7 +1753,7 @@ void Client::move( int x, int y, ForceGeometry_t force ) { if( force == NormalGeometrySet && geom.topLeft() == QPoint( x, y )) return; - workspace()->addDamage( geometry()); // TODO cache the previous real geometry + workspace()->addDamage( this, geometry()); // TODO cache the previous real geometry geom.moveTopLeft( QPoint( x, y )); updateWorkareaDiffs(); if( postpone_geometry_updates != 0 ) @@ -1765,7 +1765,7 @@ void Client::move( int x, int y, ForceGeometry_t force ) sendSyntheticConfigureNotify(); updateWindowRules(); checkMaximizeGeometry(); - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); } void Client::postponeGeometryUpdates( bool postpone ) @@ -2265,6 +2265,8 @@ bool Client::startMoveResize() // not needed anymore? kapp->installEventFilter( eater ); } Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart ); + if( effects ) + effects->windowUserMovedResized( this, true, false ); return true; } @@ -2278,6 +2280,8 @@ void Client::finishMoveResize( bool cancel ) checkMaximizeGeometry(); // FRAME update(); Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); + if( effects ) + effects->windowUserMovedResized( this, false, true ); } void Client::leaveMoveResize() @@ -2541,10 +2545,8 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) } if ( isMove() ) workspace()->clientMoved(globalPos, xTime()); - if( isMove()) - effects->windowUserMoved( this ); - else - effects->windowUserResized( this ); + if( effects ) + effects->windowUserMovedResized( this, false, false ); } } // namespace diff --git a/manage.cpp b/manage.cpp index a95c76c552..d58a3a82f9 100644 --- a/manage.cpp +++ b/manage.cpp @@ -523,7 +523,7 @@ bool Client::manage( Window w, bool isMapped ) delete session; if( isMapped ) // otherwise damage will come when the client paints it - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); ungrabXServer(); diff --git a/scene.cpp b/scene.cpp index 8e8f42c54d..48f77457e4 100644 --- a/scene.cpp +++ b/scene.cpp @@ -43,6 +43,14 @@ void Scene::windowDeleted( Toplevel* ) { } +void Scene::transformWindowDamage( Toplevel*, XserverRegion ) const + { + } + +void Scene::updateTransformation( Toplevel* ) + { + } + Scene* scene; } // namespace diff --git a/scene.h b/scene.h index 9db48c450b..c5543ad92b 100644 --- a/scene.h +++ b/scene.h @@ -28,6 +28,8 @@ class Scene virtual void windowGeometryShapeChanged( Toplevel* ); virtual void windowOpacityChanged( Toplevel* ); virtual void windowDeleted( Toplevel* ); + virtual void transformWindowDamage( Toplevel*, XserverRegion ) const; + virtual void updateTransformation( Toplevel* ); protected: Workspace* wspace; ToplevelList windows; diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 5653e15810..7dcbe4221f 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -64,19 +64,26 @@ void SceneXrender::paint( XserverRegion damage ) { Toplevel* c = windows[ i ]; checkWindowData( c ); - effects->paintWindow( c, window_data[ c ].effect ); + WindowData& data = window_data[ c ]; + effects->transformWindow( c, data.effect ); // TODO remove, instead add initWindow() to effects if( isOpaque( c )) { Picture picture = windowPicture( c ); - Picture shape = windowShape( c ); + XserverRegion shape = windowShape( c ); if( picture != None && shape != None ) { // Set the clip region for the buffer to the damage region, and // subtract the clients shape from the damage region XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage ); + const Matrix& matrix = data.effect.matrix; + if( !matrix.isIdentity()) + { + assert( matrix.isOnlyTranslate()); + XFixesTranslateRegion( display(), shape, int( matrix.xTranslate()), int( matrix.yTranslate())); + } XFixesSubtractRegion( display(), damage, damage, shape ); XRenderComposite( display(), PictOpSrc, picture, None, buffer, 0, 0, 0, 0, - c->x(), c->y(), c->width(), c->height()); + c->x() + int( matrix.xTranslate()), c->y() + int( matrix.yTranslate()), c->width(), c->height()); } } saveWindowClipRegion( c, damage ); @@ -102,9 +109,10 @@ void SceneXrender::paint( XserverRegion damage ) Picture alpha = windowAlphaMask( c ); if( picture != None ) { + const Matrix& matrix = window_data[ c ].effect.matrix; // TODO clip also using shape? also above? XRenderComposite( display(), PictOpOver, picture, alpha, buffer, 0, 0, 0, 0, - c->x(), c->y(), c->width(), c->height()); + c->x() + int( matrix.xTranslate()), c->y() + int( matrix.yTranslate()), c->width(), c->height()); } } XFixesDestroyRegion( display(), r ); @@ -115,6 +123,27 @@ void SceneXrender::paint( XserverRegion damage ) XFlush( display()); } +void SceneXrender::transformWindowDamage( Toplevel* c, XserverRegion r ) const + { + if( !window_data.contains( c )) + return; + const Matrix& matrix = window_data[ c ].effect.matrix; + if( matrix.isIdentity()) + return; + assert( matrix.isOnlyTranslate()); + // TODO the matrix here is not valid after it changes but before it's first painted + // (i.e. a changes to state where it should be translated but the matrix is not yet updated) + XFixesTranslateRegion( display(), r, int( matrix.xTranslate()), int( matrix.yTranslate())); + } + +void SceneXrender::updateTransformation( Toplevel* c ) + { + // TODO maybe only mark as invalid and update on-demand + checkWindowData( c ); + WindowData& data = window_data[ c ]; + effects->transformWindow( c, data.effect ); + } + void SceneXrender::checkWindowData( Toplevel* c ) { if( !window_data.contains( c )) @@ -139,7 +168,7 @@ void SceneXrender::windowGeometryShapeChanged( Toplevel* c ) XRenderFreePicture( display(), data.alpha ); data.alpha = None; if( data.shape != None ) - XRenderFreePicture( display(), data.shape ); + XFixesDestroyRegion( display(), data.shape ); data.shape = None; } @@ -220,8 +249,9 @@ Picture SceneXrender::windowAlphaMask( Toplevel* c ) return data.alpha; } -Picture SceneXrender::windowShape( Toplevel* c ) +XserverRegion SceneXrender::windowShape( Toplevel* c ) { +#if 0 // it probably doesn't make sense to cache this, and perhaps some others - they aren't roundtrips WindowData& data = window_data[ c ]; if( data.shape == None ) { @@ -229,6 +259,11 @@ Picture SceneXrender::windowShape( Toplevel* c ) XFixesTranslateRegion( display(), data.shape, c->x(), c->y()); } return data.shape; +#else + XserverRegion shape = XFixesCreateRegionFromWindow( display(), c->handle(), WindowRegionBounding ); + XFixesTranslateRegion( display(), shape, c->x(), c->y()); + return shape; +#endif } // TODO handle xrandr changes @@ -242,6 +277,36 @@ void SceneXrender::createBuffer() XFreePixmap( display(), pixmap ); // The picture owns the pixmap now } +void SceneXrender::setPictureMatrix( Picture pic, const Matrix& m ) + { + if( pic == None ) + return; + XTransform t; + // ignore z axis + t.matrix[ 0 ][ 0 ] = XDoubleToFixed( m.m[ 0 ][ 0 ] ); + t.matrix[ 0 ][ 1 ] = XDoubleToFixed( m.m[ 0 ][ 1 ] ); + t.matrix[ 0 ][ 2 ] = -XDoubleToFixed( m.m[ 0 ][ 3 ] ); // translation seems to be inverted + t.matrix[ 1 ][ 0 ] = XDoubleToFixed( m.m[ 1 ][ 0 ] ); + t.matrix[ 1 ][ 1 ] = XDoubleToFixed( m.m[ 1 ][ 1 ] ); + t.matrix[ 1 ][ 2 ] = -XDoubleToFixed( m.m[ 1 ][ 3 ] ); + t.matrix[ 2 ][ 0 ] = XDoubleToFixed( m.m[ 3 ][ 0 ] ); + t.matrix[ 2 ][ 1 ] = XDoubleToFixed( m.m[ 3 ][ 1 ] ); + t.matrix[ 2 ][ 2 ] = XDoubleToFixed( m.m[ 3 ][ 3 ] ); + XRenderSetPictureTransform( display(), pic, &t ); + if( t.matrix[ 0 ][ 0 ] != XDoubleToFixed( 1 ) // fast filter for identity or translation + || t.matrix[ 1 ][ 1 ] != XDoubleToFixed( 1 ) + || t.matrix[ 2 ][ 2 ] != XDoubleToFixed( 1 ) + || t.matrix[ 0 ][ 1 ] != XDoubleToFixed( 0 ) + || t.matrix[ 1 ][ 0 ] != XDoubleToFixed( 0 )) + { + XRenderSetPictureFilter( display(), pic, const_cast< char* >( FilterGood ), 0, 0 ); + } + else + { + XRenderSetPictureFilter( display(), pic, const_cast< char* >( FilterFast ), 0, 0 ); + } + } + SceneXrender::WindowData::WindowData() : picture( None ) , format( NULL ) diff --git a/scene_xrender.h b/scene_xrender.h index ec62de0292..cd64f7264c 100644 --- a/scene_xrender.h +++ b/scene_xrender.h @@ -34,6 +34,8 @@ class SceneXrender virtual void windowGeometryShapeChanged( Toplevel* ); virtual void windowOpacityChanged( Toplevel* ); virtual void windowDeleted( Toplevel* ); + virtual void transformWindowDamage( Toplevel*, XserverRegion ) const; + virtual void updateTransformation( Toplevel* ); private: void createBuffer(); void checkWindowData( Toplevel* c ); @@ -42,7 +44,8 @@ class SceneXrender XserverRegion savedWindowClipRegion( Toplevel* c ); bool isOpaque( Toplevel* c ) const; Picture windowAlphaMask( Toplevel* c ); - Picture windowShape( Toplevel* c ); + XserverRegion windowShape( Toplevel* c ); + static void setPictureMatrix( Picture pic, const Matrix& m ); XRenderPictFormat* format; Picture front; Picture buffer; diff --git a/toplevel.h b/toplevel.h index 371cf2366b..aa5bf8a415 100644 --- a/toplevel.h +++ b/toplevel.h @@ -39,6 +39,20 @@ class Toplevel int y() const; int width() const; int height() const; + + // prefer isXXX() instead + virtual NET::WindowType windowType( bool direct = false, int supported_types = SUPPORTED_WINDOW_TYPES_MASK ) const = 0; + bool hasNETSupport() const; + bool isDesktop() const; + bool isDock() const; + bool isToolbar() const; + bool isTopMenu() const; + bool isMenu() const; + bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient' + bool isDialog() const; + bool isSplash() const; + bool isUtility() const; + Pixmap windowPixmap() const; Visual* visual() const; virtual double opacity() const = 0; @@ -120,6 +134,51 @@ inline Visual* Toplevel::visual() const return vis; } +inline bool Toplevel::isDesktop() const + { + return windowType() == NET::Desktop; + } + +inline bool Toplevel::isDock() const + { + return windowType() == NET::Dock; + } + +inline bool Toplevel::isTopMenu() const + { + return windowType() == NET::TopMenu; + } + +inline bool Toplevel::isMenu() const + { + return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. + } + +inline bool Toplevel::isToolbar() const + { + return windowType() == NET::Toolbar; + } + +inline bool Toplevel::isSplash() const + { + return windowType() == NET::Splash; + } + +inline bool Toplevel::isUtility() const + { + return windowType() == NET::Utility; + } + +inline bool Toplevel::isDialog() const + { + return windowType() == NET::Dialog; + } + +inline bool Toplevel::isNormalWindow() const + { + return windowType() == NET::Normal; + } + #ifdef NDEBUG inline kndbgstream& operator<<( kndbgstream& stream, const Toplevel* ) { return stream; } diff --git a/unmanaged.cpp b/unmanaged.cpp index e063cb45fc..5b0a2e335f 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -47,7 +47,7 @@ bool Unmanaged::track( Window w ) setupCompositing(); resetWindowPixmap(); - workspace()->addDamage( geometry()); + workspace()->addDamage( this, geometry()); return true; } @@ -63,6 +63,11 @@ void Unmanaged::deleteUnmanaged( Unmanaged* c, allowed_t ) delete c; } +NET::WindowType Unmanaged::windowType( bool, int supported_types ) const + { + return info->windowType( supported_types ); + } + double Unmanaged::opacity() const { if( info->opacity() == 0xffffffff ) diff --git a/unmanaged.h b/unmanaged.h index 2d62417b16..becb8755eb 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -28,6 +28,7 @@ class Unmanaged bool windowEvent( XEvent* e ); void release(); bool track( Window w ); + virtual NET::WindowType windowType( bool direct = false, int supported_types = SUPPORTED_WINDOW_TYPES_MASK ) const; static void deleteUnmanaged( Unmanaged* c, allowed_t ); virtual double opacity() const; protected: diff --git a/utils.cpp b/utils.cpp index b096a0d259..6d42a6b1f6 100644 --- a/utils.cpp +++ b/utils.cpp @@ -315,6 +315,20 @@ bool grabbedXServer() return server_grab_count > 0; } +kdbgstream& operator<<( kdbgstream& stream, RegionDebug r ) + { + if( r.rr == None ) + return stream << "EMPTY"; + int num; + XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num ); + if( rects == NULL || num == 0 ) + return stream << "EMPTY"; + for( int i = 0; + i < num; + ++i ) + stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]"; + return stream; + } #endif bool isLocalMachine( const QByteArray& host ) @@ -381,8 +395,6 @@ void ShortcutDialog::accept() KShortcutDialog::accept(); } #endif - - } // namespace #ifndef KCMRULES diff --git a/utils.h b/utils.h index 251d720a1c..564f78ee06 100644 --- a/utils.h +++ b/utils.h @@ -120,6 +120,20 @@ enum allowed_t { Allowed }; // some enums to have more readable code, instead of using bools enum ForceGeometry_t { NormalGeometrySet, ForceGeometrySet }; + +struct RegionDebug + { + RegionDebug( XserverRegion r ) : rr( r ) {} + XserverRegion rr; + }; + +#ifdef NDEBUG +inline +kndbgstream& operator<<( kndbgstream& stream, RegionDebug ) { return stream; } +#else +kdbgstream& operator<<( kdbgstream& stream, RegionDebug r ); +#endif + // Areas, mostly related to Xinerama enum clientAreaOption { diff --git a/workspace.h b/workspace.h index 8fe701041b..4fcfbb6dcf 100644 --- a/workspace.h +++ b/workspace.h @@ -286,6 +286,9 @@ class Workspace : public QObject, public KDecorationDefines void addDamage( const QRect& r ); void addDamage( int x, int y, int w, int h ); void addDamage( XserverRegion r, bool destroy ); + void addDamage( Toplevel* c, const QRect& r ); + void addDamage( Toplevel* c, int x, int y, int w, int h ); + void addDamage( Toplevel* c, XserverRegion r, bool destroy ); public slots: void refresh();