diff --git a/CMakeLists.txt b/CMakeLists.txt index 869b52382a..c510e98812 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ set(kwin_KDEINIT_SRCS glutils.cpp effects.cpp effects/fadein.cpp + effects/fadeout.cpp effects/maketransparent.cpp effects/scalein.cpp effects/shakymove.cpp diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 05531b5f25..2ef0dd499b 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -45,9 +45,6 @@ General TODO - maybe posted paint events need to be processed immediatelly, or maybe the compositing code should not update the window until the decoration is finished painting -/ handle XRandr changes - - output buffers and similar probably need recreating when the screen size changes - ! compile even without OpenGL or XRender - kwin_composite currently requires both OpenGL and XRender to build - compiling without either results in compile errors, needs to be fixed @@ -66,6 +63,10 @@ General TODO + make effects configurable without having to recompile - possibly also add something like KWIN_COMPOSE for debugging purposes +* handle properly stacking order of deleted windows for showing in effects + +* handle properly deleted windows that reappear (windowReadded() function?) + OpenGL TODO ================================= @@ -147,10 +148,7 @@ XRender TODO Effects framework TODO ============================== -/ design framework for graphical effects - - modelling it after compiz seems to make a lot of sense - -* solve somehow disappearing windows +/ solve somehow disappearing windows - i.e. when a window is e.g. closed, the Client/Unmanaged object is destroyed, but animations should be going on ? - maybe animations could be done actually before the state change, it makes sense to destroy @@ -185,6 +183,14 @@ Effects framework TODO which means that if an effect sets PAINT_WINDOW_TRANSFORMED it needs to set it also in prePaintScreen() +* PAINT_WINDOW_DISABLED turning off from effects needs some utility functions + - a window may have painting disabled for various reasons and their numbers may increase + over time + - so e.g. an effect showing minimized windows cannot simply turn off DISABLED + for minimized windows, because it may be disabled also for other reasons + - there should be some utility function that will be called by the effect + with arguments saying which disabled windows it wants enabled + Effects TODO =============================== @@ -219,10 +225,8 @@ Effects TODO - Client::animateMinimizeOrUnminimize() - Client::setShade() -+ zoom effect +/ zoom effect - enlarge a portion of the screen -! - would require adding xScale/yScale to ScreenPaintData - - should be doable even for XRender + logout effect * - should be triggered by ksmserver somehow diff --git a/client.cpp b/client.cpp index c41acff764..2edfccd33b 100644 --- a/client.cpp +++ b/client.cpp @@ -108,7 +108,6 @@ Client::Client( Workspace *ws ) shade_mode = ShadeNone; active = false; - deleting = false; keep_above = false; keep_below = false; motif_noborder = false; @@ -175,9 +174,11 @@ void Client::deleteClient( Client* c, allowed_t ) */ void Client::releaseWindow( bool on_shutdown ) { - assert( !deleting ); - deleting = true; - finishCompositing(); + assert( !deleting()); + delete_refcount = 1; + if( effects ) + effects->windowClosed( this ); + finishCompositing( false ); // don't discard pixmap workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) @@ -192,7 +193,9 @@ void Client::releaseWindow( bool on_shutdown ) if( !on_shutdown ) workspace()->clientHidden( this ); XUnmapWindow( display(), frameId()); // destroying decoration would cause ugly visual effect - destroyDecoration(); +// destroyDecoration(); + delete decoration; + decoration = NULL; cleanGrouping(); if( !on_shutdown ) { @@ -200,9 +203,11 @@ void Client::releaseWindow( bool on_shutdown ) // only when the window is being unmapped, not when closing down KWin // (NETWM sections 5.5,5.7) info->setDesktop( 0 ); - desk = 0; + // desk = 0; - do not reset internal state, it may still be used by effects info->setState( 0, info->state()); // reset all state flags } + else + workspace()->addDeleted( this, Allowed ); XDeleteProperty( display(), client, atoms->kde_net_wm_user_creation_time); XDeleteProperty( display(), client, atoms->net_frame_extents ); XDeleteProperty( display(), client, atoms->kde_net_wm_frame_strut ); @@ -220,22 +225,25 @@ void Client::releaseWindow( bool on_shutdown ) // may do map+unmap before we initially map the window by calling rawShow() from manage(). XUnmapWindow( display(), client ); } + cleanUp(); client = None; XDestroyWindow( display(), wrapper ); wrapper = None; XDestroyWindow( display(), frameId()); // frame = None; --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - deleteClient( this, Allowed ); + unrefWindow(); // will delete if recount is == 0 } // like releaseWindow(), but this one is called when the window has been already destroyed // (e.g. the application closed it) void Client::destroyClient() { - assert( !deleting ); - deleting = true; - finishCompositing(); + assert( !deleting()); + delete_refcount = 1; + if( effects ) + effects->windowClosed( this ); + finishCompositing( false ); // don't discard pixmap workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) @@ -247,15 +255,50 @@ void Client::destroyClient() setModal( false ); hidden = true; // so that it's not considered visible anymore workspace()->clientHidden( this ); - destroyDecoration(); +// destroyDecoration(); + delete decoration; + decoration = NULL; cleanGrouping(); workspace()->removeClient( this, Allowed ); + cleanUp(); client = None; // invalidate XDestroyWindow( display(), wrapper ); wrapper = None; XDestroyWindow( display(), frameId()); // frame = None; --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + unrefWindow(); // will delete if recount is == 0 + } + +// All clean-up code shared between releaseWindow() and destroyClient(). +// Clean up everything that should not affect a deleted Client until it's +// really deleted by unrefWindow(). I.e. stop watching events, turn off timers +// and so on. +void Client::cleanUp() + { + if( Extensions::shapeAvailable()) + XShapeSelectInput( display(), client, NoEventMask ); + XSelectInput( display(), client, NoEventMask ); + delete autoRaiseTimer; + autoRaiseTimer = NULL; + delete shadeHoverTimer; + shadeHoverTimer = NULL; + delete ping_timer; + ping_timer = NULL; + delete process_killer; + process_killer = NULL; + delete demandAttentionKNotifyTimer; + demandAttentionKNotifyTimer = NULL; + } + +void Client::unrefWindow() + { + if( --delete_refcount > 0 ) + return; + workspace()->removeDeleted( this ); + workspace()->addDamage( geometry()); + destroyDecoration( true ); // only now gravitate etc., otherwise window pixmap would be wrong + discardWindowPixmap(); deleteClient( this, Allowed ); } @@ -300,9 +343,9 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) addDamageFull(); } -void Client::destroyDecoration() +void Client::destroyDecoration( bool force ) { - if( decoration != NULL ) + if( decoration != NULL || force ) { delete decoration; decoration = NULL; @@ -317,7 +360,7 @@ void Client::destroyDecoration() workarea_diff_y = save_workarea_diff_y; if( compositing() ) discardWindowPixmap(); - if( scene != NULL ) + if( scene != NULL && !deleting()) scene->windowGeometryShapeChanged( this ); addDamageFull(); } @@ -852,7 +895,7 @@ void Client::toggleShade() void Client::updateVisibility() { - if( deleting ) + if( deleting()) return; bool show = true; if( hidden ) @@ -910,7 +953,7 @@ void Client::updateVisibility() void Client::setMappingState(int s) { assert( client != None ); - assert( !deleting || s == WithdrawnState ); + assert( !deleting() || s == WithdrawnState ); if( mapping_state == s ) return; bool was_unmanaged = ( mapping_state == WithdrawnState ); diff --git a/client.h b/client.h index 4c03fbe5ee..9a458cef90 100644 --- a/client.h +++ b/client.h @@ -28,6 +28,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "workspace.h" #include "kdecoration.h" #include "rules.h" +#include "toplevel.h" class QTimer; class KProcess; @@ -42,7 +43,8 @@ class WinInfo; class SessionInfo; class Bridge; -class Client : public QObject, public KDecorationDefines +class Client + : public Toplevel { Q_OBJECT public: @@ -52,7 +54,6 @@ class Client : public QObject, public KDecorationDefines Window wrapperId() const; Window decorationId() const; - Workspace* workspace() const; const Client* transientFor() const; Client* transientFor(); bool isTransient() const; @@ -66,24 +67,20 @@ class Client : public QObject, public KDecorationDefines const Group* group() const; Group* group(); void checkGroup( Group* gr = NULL, bool force = false ); - void changeClientLeaderGroup( Group* gr ); - // 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; - QRect geometry() const; - QSize size() const; QSize minSize() const; QSize maxSize() const; - QPoint pos() const; - QRect rect() const; - int x() const; - int y() const; - int width() const; - int height() const; QPoint clientPos() const; // inside of geometry() QSize clientSize() const; @@ -93,6 +90,8 @@ class Client : public QObject, public KDecorationDefines bool manage( Window w, bool isMapped ); void releaseWindow( bool on_shutdown = false ); + void destroyClient(); + virtual void unrefWindow(); enum Sizemode // how to resize the window in order to obey constains (mainly aspect ratios) { @@ -166,24 +165,9 @@ class Client : public QObject, public KDecorationDefines // 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) @@ -196,9 +180,10 @@ class Client : public QObject, public KDecorationDefines void updateDecoration( bool check_workspace_pos, bool force = false ); void checkBorderSizes(); - // shape extensions - bool shape() const; void updateShape(); + + virtual double opacity() const; + void setOpacity( double opacity ); void setGeometry( int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet ); void setGeometry( const QRect& r, ForceGeometry_t force = NormalGeometrySet ); @@ -284,10 +269,18 @@ class Client : public QObject, public KDecorationDefines void showContextHelp(); void cancelShadeHover(); void cancelAutoRaise(); - 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(); void shadeHover(); @@ -321,6 +314,7 @@ class Client : public QObject, public KDecorationDefines void clientMessageEvent( XClientMessageEvent* e ); void enterNotifyEvent( XCrossingEvent* e ); void leaveNotifyEvent( XCrossingEvent* e ); + void visibilityNotifyEvent( XVisibilityEvent* e ); void focusInEvent( XFocusInEvent* e ); void focusOutEvent( XFocusOutEvent* e ); @@ -330,6 +324,9 @@ class Client : public QObject, public KDecorationDefines void processDecorationButtonPress( int button, int state, int x, int y, int x_root, int y_root ); + protected: + virtual void debug( kdbgstream& stream ) const; + private slots: void pingTimeout(); void processKillerExited(); @@ -361,6 +358,7 @@ class Client : public QObject, public KDecorationDefines void updateWindowRules(); void finishWindowRules(); void setShortcutInternal( const KShortcut& cut ); + void cleanUp(); void updateWorkareaDiffs(); void checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area ); @@ -368,7 +366,7 @@ class Client : public QObject, public KDecorationDefines void configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool ); NETExtendedStrut strut() const; int checkShadeGeometry( int w, int h ); - void postponeGeometryUpdates( bool postpone ); + void blockGeometryUpdates( bool block ); bool startMoveResize(); void finishMoveResize( bool cancel ); @@ -389,7 +387,7 @@ class Client : public QObject, public KDecorationDefines void embedClient( Window w, const XWindowAttributes &attr ); void detectNoBorder(); - void destroyDecoration(); + void destroyDecoration( bool force = false ); void updateFrameExtents(); void rawShow(); // just shows it @@ -400,12 +398,10 @@ class Client : public QObject, public KDecorationDefines Time readUserCreationTime() const; static bool sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ); void startupIdChanged(); - + Window client; Window wrapper; - Window frame; KDecoration* decoration; - Workspace* wspace; Bridge* bridge; int desk; bool buttonDown; @@ -413,14 +409,6 @@ class Client : public QObject, public KDecorationDefines 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; @@ -444,9 +432,7 @@ class Client : public QObject, public KDecorationDefines ClientList transients_list; // SELI make this ordered in stacking order? ShadeMode shade_mode; uint active :1; - uint deleting : 1; // true when doing cleanup and destroying the client uint keep_above : 1; // NET::KeepAbove (was stays_on_top) - uint is_shape :1; uint skip_taskbar :1; uint original_skip_taskbar :1; // unaffected by KWin uint Pdeletewindow :1; // does the window understand the DeleteWindow protocol? @@ -466,6 +452,7 @@ class Client : public QObject, public KDecorationDefines uint modal : 1; // NET::Modal uint noborder : 1; uint user_noborder : 1; + uint not_obscured : 1; uint urgency : 1; // XWMHints, UrgencyHint uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client uint demands_attention : 1; @@ -503,10 +490,10 @@ class Client : public QObject, public KDecorationDefines Time ping_timestamp; Time user_time; unsigned long allowed_actions; - QRect frame_geometry; QSize client_size; - int postpone_geometry_updates; // >0 - new geometry is remembered, but not actually set + int block_geometry_updates; // >0 - new geometry is remembered, but not actually set bool pending_geometry_update; + QRect geom_before_block; bool shade_geometry_change; int border_left, border_right, border_top, border_bottom; QRegion _mask; @@ -516,22 +503,20 @@ class Client : public QObject, public KDecorationDefines friend struct FetchNameInternalPredicate; friend struct CheckIgnoreFocusStealingProcedure; friend struct ResetupRulesProcedure; - friend class GeometryUpdatesPostponer; + friend class GeometryUpdatesBlocker; void show() { assert( false ); } // SELI remove after Client is no longer QWidget void hide() { assert( false ); } QTimer* demandAttentionKNotifyTimer; - - friend bool performTransiencyCheck(); }; -// helper for Client::postponeGeometryUpdates() being called in pairs (true/false) -class GeometryUpdatesPostponer +// helper for Client::blockGeometryUpdates() being called in pairs (true/false) +class GeometryUpdatesBlocker { public: - GeometryUpdatesPostponer( Client* c ) - : cl( c ) { cl->postponeGeometryUpdates( true ); } - ~GeometryUpdatesPostponer() - { cl->postponeGeometryUpdates( false ); } + GeometryUpdatesBlocker( Client* c ) + : cl( c ) { cl->blockGeometryUpdates( true ); } + ~GeometryUpdatesBlocker() + { cl->blockGeometryUpdates( false ); } private: Client* cl; }; @@ -558,7 +543,7 @@ inline Window Client::window() const inline Window Client::frameId() const { - return frame; + return handle(); } inline Window Client::wrapperId() const @@ -571,11 +556,6 @@ inline Window Client::decorationId() const return decoration != NULL ? decoration->widget()->winId() : None; } -inline Workspace* Client::workspace() const - { - return wspace; - } - inline const Client* Client::transientFor() const { return transient_for; @@ -731,12 +711,6 @@ inline bool Client::keepBelow() const return keep_below; } -inline bool Client::shape() const - { - return is_shape; - } - - inline bool Client::isFullScreen() const { return fullscreen_mode != FullScreenNone; @@ -792,46 +766,6 @@ inline QByteArray Client::windowRole() const return window_role; } -inline QRect Client::geometry() const - { - return frame_geometry; - } - -inline QSize Client::size() const - { - return frame_geometry.size(); - } - -inline QPoint Client::pos() const - { - return frame_geometry.topLeft(); - } - -inline int Client::x() const - { - return frame_geometry.x(); - } - -inline int Client::y() const - { - return frame_geometry.y(); - } - -inline int Client::width() const - { - return frame_geometry.width(); - } - -inline int Client::height() const - { - return frame_geometry.height(); - } - -inline QRect Client::rect() const - { - return QRect( 0, 0, width(), height()); - } - inline QPoint Client::clientPos() const { return QPoint( border_left, border_top ); @@ -877,7 +811,7 @@ inline const WindowRules* Client::rules() const return &client_rules; } -KWIN_PROCEDURE( CheckIgnoreFocusStealingProcedure, cl->ignore_focus_stealing = options->checkIgnoreFocusStealing( cl )); +KWIN_PROCEDURE( CheckIgnoreFocusStealingProcedure, Client, cl->ignore_focus_stealing = options->checkIgnoreFocusStealing( cl )); inline Window Client::moveResizeGrabWindow() const { @@ -894,22 +828,9 @@ inline void Client::removeRule( Rules* rule ) client_rules.remove( rule ); } -#ifdef NDEBUG -inline -kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; } -inline -kndbgstream& operator<<( kndbgstream& stream, const ClientList& ) { return stream; } -inline -kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; } -#else -kdbgstream& operator<<( kdbgstream& stream, const Client* ); -kdbgstream& operator<<( kdbgstream& stream, const ClientList& ); -kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& ); -#endif - -KWIN_COMPARE_PREDICATE( WindowMatchPredicate, Window, cl->window() == value ); -KWIN_COMPARE_PREDICATE( FrameIdMatchPredicate, Window, cl->frameId() == value ); -KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Window, cl->wrapperId() == value ); +KWIN_COMPARE_PREDICATE( WindowMatchPredicate, Client, Window, cl->window() == value ); +KWIN_COMPARE_PREDICATE( FrameIdMatchPredicate, Client, Window, cl->frameId() == value ); +KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value ); } // namespace diff --git a/composite.cpp b/composite.cpp index 61efc2ccb2..94c43c22c3 100644 --- a/composite.cpp +++ b/composite.cpp @@ -222,6 +222,8 @@ void Workspace::performCompositing() else if( Unmanaged* c = findUnmanaged( HandleMatchPredicate( children[ i ] ))) windows.append( c ); } + foreach( Toplevel* c, pending_deleted ) // TODO remember stacking order somehow + windows.append( c ); QRegion damage = damage_region; // clear all damage, so that post-pass can add damage for the next repaint damage_region = QRegion(); @@ -290,12 +292,13 @@ void Toplevel::setupCompositing() damage_region = QRegion( 0, 0, width(), height()); } -void Toplevel::finishCompositing() +void Toplevel::finishCompositing( bool discard_pixmap ) { if( damage_handle == None ) return; XDamageDestroy( display(), damage_handle ); - discardWindowPixmap(); + if( discard_pixmap ) + discardWindowPixmap(); damage_handle = None; damage_region = QRegion(); } diff --git a/effects.cpp b/effects.cpp index 4e78e97327..6e8de4a0c1 100644 --- a/effects.cpp +++ b/effects.cpp @@ -16,6 +16,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "effects/dialogparent.h" #include "effects/fadein.h" +#include "effects/fadeout.h" #include "effects/howto.h" #include "effects/maketransparent.h" #include "effects/presentwindows.h" @@ -46,6 +47,10 @@ void Effect::windowAdded( Toplevel* ) { } +void Effect::windowClosed( Toplevel* ) + { + } + void Effect::windowDeleted( Toplevel* ) { } @@ -115,6 +120,7 @@ EffectsHandler::EffectsHandler( Workspace* ws ) // effects.append( new ShakyMoveEffect ); // effects.append( new ShiftWorkspaceUpEffect( ws )); // effects.append( new FadeInEffect ); + effects.append( new FadeOutEffect ); // effects.append( new ScaleInEffect ); // effects.append( new DialogParentEffect ); @@ -147,6 +153,12 @@ void EffectsHandler::windowDeleted( Toplevel* c ) e->windowDeleted( c ); } +void EffectsHandler::windowClosed( Toplevel* c ) + { + foreach( Effect* e, effects ) + e->windowClosed( c ); + } + void EffectsHandler::windowActivated( Toplevel* c ) { foreach( Effect* e, effects ) diff --git a/effects.h b/effects.h index ea7369bde5..607b3cd2b6 100644 --- a/effects.h +++ b/effects.h @@ -74,6 +74,7 @@ class Effect virtual void windowUserMovedResized( Toplevel* c, bool first, bool last ); virtual void windowAdded( Toplevel* c ); virtual void windowDeleted( Toplevel* c ); + virtual void windowClosed( Toplevel* c ); virtual void windowActivated( Toplevel* c ); virtual void windowMinimized( Toplevel* c ); virtual void windowUnminimized( Toplevel* c ); @@ -110,6 +111,7 @@ class EffectsHandler void startPaint(); void windowUserMovedResized( Toplevel* c, bool first, bool last ); void windowAdded( Toplevel* c ); + void windowClosed( Toplevel* c ); void windowDeleted( Toplevel* c ); void windowActivated( Toplevel* c ); void windowMinimized( Toplevel* c ); diff --git a/effects/fadeout.cpp b/effects/fadeout.cpp new file mode 100644 index 0000000000..ac37504f3e --- /dev/null +++ b/effects/fadeout.cpp @@ -0,0 +1,69 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2006 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "fadeout.h" + +#include + +namespace KWinInternal +{ + +void FadeOutEffect::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time ) + { + if( windows.contains( w->window())) + { + windows[ w->window() ] -= time / 1000.; // complete change in 1000ms + if( windows[ w->window() ] > 0 ) + { + *mask |= Scene::PAINT_WINDOW_TRANSLUCENT; + *mask &= ~( Scene::PAINT_WINDOW_OPAQUE | Scene::PAINT_WINDOW_DISABLED ); + } + else + { + windows.remove( w->window()); + w->window()->unrefWindow(); + } + } + effects->prePaintWindow( w, mask, region, time ); + } + +void FadeOutEffect::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data ) + { + if( windows.contains( w->window())) + { + data.opacity *= windows[ w->window()]; + } + effects->paintWindow( w, mask, region, data ); + } + +void FadeOutEffect::postPaintWindow( Scene::Window* w ) + { + if( windows.contains( w->window())) + w->window()->addDamageFull(); // trigger next animation repaint + effects->postPaintWindow( w ); + } + +void FadeOutEffect::windowClosed( Toplevel* c ) + { + Client* cc = dynamic_cast< Client* >( c ); + if( cc == NULL || cc->isOnCurrentDesktop()) + { + windows[ c ] = 1; // count down to 0 + c->addDamageFull(); + c->refWindow(); + } + } + +void FadeOutEffect::windowDeleted( Toplevel* c ) + { + windows.remove( c ); + } + +} // namespace diff --git a/effects/fadeout.h b/effects/fadeout.h new file mode 100644 index 0000000000..2b6dfb9983 --- /dev/null +++ b/effects/fadeout.h @@ -0,0 +1,37 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2006 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +// TODO MIT or some other licence, perhaps move to some lib + +#ifndef KWIN_FADEOUT_H +#define KWIN_FADEOUT_H + +#include + +namespace KWinInternal +{ + +class FadeOutEffect + : public Effect + { + public: + virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time ); + virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data ); + virtual void postPaintWindow( Scene::Window* w ); + // TODO react also on virtual desktop changes + virtual void windowClosed( Toplevel* c ); + virtual void windowDeleted( Toplevel* c ); + private: + QMap< const Toplevel*, double > windows; + }; + +} // namespace + +#endif diff --git a/scene.cpp b/scene.cpp index ef6fe7620f..ffe738ff87 100644 --- a/scene.cpp +++ b/scene.cpp @@ -290,6 +290,8 @@ QRegion Scene::Window::shape() const bool Scene::Window::isVisible() const { + if( toplevel->deleting()) + return false; if( Client* c = dynamic_cast< Client* >( toplevel )) return c->isShown( true ) && c->isOnCurrentDesktop(); return true; // Unmanaged is always visible diff --git a/toplevel.cpp b/toplevel.cpp index f1679b8199..618af3e875 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -15,6 +15,7 @@ namespace KWinInternal Toplevel::Toplevel( Workspace* ws ) : vis( None ) + , delete_refcount( -1 ) , id( None ) , wspace( ws ) , window_pix( None ) diff --git a/toplevel.h b/toplevel.h index 3320ceea2f..14fa0081b1 100644 --- a/toplevel.h +++ b/toplevel.h @@ -59,12 +59,18 @@ class Toplevel int depth() const; bool hasAlpha() const; void setupCompositing(); - void finishCompositing(); + void finishCompositing( bool discard_pixmap = true ); void addDamage( const QRect& r ); void addDamage( int x, int y, int w, int h ); void addDamageFull(); QRegion damage() const; void resetDamage( const QRect& r ); + + // used by effects to keep the window around for e.g. fadeout effects when it's destroyed + void refWindow(); + virtual void unrefWindow() = 0; + bool deleting() const; + protected: virtual ~Toplevel(); void setHandle( Window id ); @@ -75,6 +81,7 @@ class Toplevel QRect geom; Visual* vis; int bit_depth; + int delete_refcount; virtual void debug( kdbgstream& stream ) const = 0; friend kdbgstream& operator<<( kdbgstream& stream, const Toplevel* ); private: @@ -219,6 +226,17 @@ inline bool Toplevel::hasAlpha() const return depth() == 32; } +inline void Toplevel::refWindow() + { + assert( delete_refcount >= 0 ); + ++delete_refcount; + } + +inline bool Toplevel::deleting() const + { + return delete_refcount >= 0; + } + #ifdef NDEBUG inline kndbgstream& operator<<( kndbgstream& stream, const Toplevel* ) { return stream; } diff --git a/unmanaged.cpp b/unmanaged.cpp index 7f9a6f324c..56981c5c4a 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -70,10 +70,26 @@ bool Unmanaged::track( Window w ) void Unmanaged::release() { - workspace()->addDamage( geometry()); - finishCompositing(); + assert( !deleting()); + delete_refcount = 1; + if( effects ) + effects->windowClosed( this ); + finishCompositing( false ); // don't discard pixmap workspace()->removeUnmanaged( this, Allowed ); - delete this; + if( Extensions::shapeAvailable()) + XShapeSelectInput( display(), handle(), NoEventMask ); + XSelectInput( display(), handle(), NoEventMask ); + unrefWindow(); // will delete if recount is == 0 + } + +void Unmanaged::unrefWindow() + { + if( --delete_refcount > 0 ) + return; + discardWindowPixmap(); + workspace()->removeDeleted( this ); + workspace()->addDamage( geometry()); + deleteUnmanaged( this, Allowed ); } void Unmanaged::deleteUnmanaged( Unmanaged* c, allowed_t ) diff --git a/unmanaged.h b/unmanaged.h index 7cc6dc87aa..d7feb99738 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -30,6 +30,7 @@ class Unmanaged 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; + virtual void unrefWindow(); protected: virtual void debug( kdbgstream& stream ) const; private: diff --git a/workspace.cpp b/workspace.cpp index 34f9d72756..2c0408959d 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -583,10 +583,6 @@ void Workspace::removeClient( Client* c, allowed_t ) Notify::raise( Notify::Delete ); Q_ASSERT( clients.contains( c ) || desktops.contains( c )); - if( scene ) - scene->windowDeleted( c ); - if( effects ) - effects->windowDeleted( c ); clients.removeAll( c ); desktops.removeAll( c ); unconstrained_stacking_order.removeAll( c ); @@ -620,16 +616,31 @@ void Workspace::removeClient( Client* c, allowed_t ) tab_box->repaint(); updateClientArea(); + + addDeleted( c, Allowed ); } void Workspace::removeUnmanaged( Unmanaged* c, allowed_t ) { assert( unmanaged.contains( c )); + unmanaged.removeAll( c ); + addDeleted( c, Allowed ); + } + +void Workspace::addDeleted( Toplevel* c, allowed_t ) + { + assert( !pending_deleted.contains( c )); + pending_deleted.append( c ); + } + +void Workspace::removeDeleted( Toplevel* c ) + { + assert( pending_deleted.contains( c )); if( scene ) scene->windowDeleted( c ); if( effects ) effects->windowDeleted( c ); - unmanaged.removeAll( c ); + pending_deleted.removeAll( c ); } void Workspace::updateFocusChains( Client* c, FocusChainChange change ) diff --git a/workspace.h b/workspace.h index 19f721fcb7..e96c7f8f7e 100644 --- a/workspace.h +++ b/workspace.h @@ -18,6 +18,8 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include +#include #include "utils.h" #include "kdecoration.h" @@ -77,7 +79,7 @@ class Workspace : public QObject, public KDecorationDefines virtual ~Workspace(); static Workspace * self() { return _self; } - + bool workspaceEvent( XEvent * ); KDecoration* createDecoration( KDecorationBridge* bridge ); @@ -87,6 +89,9 @@ class Workspace : public QObject, public KDecorationDefines template< typename T > Client* findClient( T predicate ); template< typename T1, typename T2 > void forEachClient( T1 procedure, T2 predicate ); template< typename T > void forEachClient( T procedure ); + template< typename T > Unmanaged* findUnmanaged( T predicate ); + template< typename T1, typename T2 > void forEachUnmanaged( T1 procedure, T2 predicate ); + template< typename T > void forEachUnmanaged( T procedure ); QRect clientArea( clientAreaOption, const QPoint& p, int desktop ) const; QRect clientArea( clientAreaOption, const Client* c ) const; @@ -181,7 +186,7 @@ class Workspace : public QObject, public KDecorationDefines ClientList ensureStackingOrder( const ClientList& clients ) const; - Client* topClientOnDesktop( int desktop, bool unconstrained = false, bool only_normal = true ) const; + Client* topClientOnDesktop( int desktop, bool unconstrained = false ) const; Client* findDesktop( bool topmost, int desktop ) const; void sendClientToDesktop( Client* c, int desktop, bool dont_activate ); void windowToPreviousDesktop( Client* c ); @@ -234,14 +239,17 @@ class Workspace : public QObject, public KDecorationDefines void sendPingToWindow( Window w, Time timestamp ); // called from Client::pingWindow() void sendTakeActivity( Client* c, Time timestamp, long flags ); // called from Client::takeActivity() - // only called from Client::destroyClient() or Client::releaseWindow() - void removeClient( Client*, allowed_t ); + void removeClient( Client*, allowed_t ); // only called from Client::destroyClient() or Client::releaseWindow() void setActiveClient( Client*, allowed_t ); Group* findGroup( Window leader ) const; void addGroup( Group* group, allowed_t ); void removeGroup( Group* group, allowed_t ); Group* findClientLeaderGroup( const Client* c ) const; + void removeUnmanaged( Unmanaged*, allowed_t ); // only called from Unmanaged::release() + void removeDeleted( Toplevel* ); + void addDeleted( Toplevel*, allowed_t ); + bool checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ); void focusToNull(); // SELI public? @@ -276,6 +284,17 @@ class Workspace : public QObject, public KDecorationDefines void requestDelayFocus( Client* ); void toggleTopDockShadows(bool on); + + void addDamage( const QRect& r ); + void addDamage( int x, int y, int w, int h ); + void addDamageFull(); + // creates XComposite overlay window, cal initOverlay() afterwards + bool createOverlay(); + // init overlay and the destination window in it + void setupOverlay( Window window ); + // destroys XComposite overlay window + void destroyOverlay(); + Window overlayWindow(); public slots: void refresh(); @@ -406,12 +425,10 @@ class Workspace : public QObject, public KDecorationDefines void cleanupTemporaryRules(); void writeWindowRules(); void slotBlockShortcuts(int data); - void slotReloadConfig(); - // kompmgr - void setPopupClientOpacity(int v); - void resetClientOpacity(); - void setTransButtonText(int value); - // end + void setPopupClientOpacity( QAction* action ); + void setupCompositing(); + void performCompositing(); + void lostCMSelection(); protected: bool keyPressMouseEmulation( XKeyEvent& ev ); @@ -459,6 +476,8 @@ class Workspace : public QObject, public KDecorationDefines // this is the right way to create a new client Client* createClient( Window w, bool is_mapped ); void addClient( Client* c, allowed_t ); + Unmanaged* createUnmanaged( Window w ); + void addUnmanaged( Unmanaged* c, allowed_t ); Window findSpecialEventWindow( XEvent* e ); @@ -500,6 +519,8 @@ class Workspace : public QObject, public KDecorationDefines void closeActivePopup(); void updateClientArea( bool force ); + + void finishCompositing(); SystemTrayWindowList systemTrayWins; @@ -536,10 +557,12 @@ class Workspace : public QObject, public KDecorationDefines ClientList clients; ClientList desktops; + UnmanagedList unmanaged; + ToplevelList pending_deleted; - ClientList unconstrained_stacking_order; // topmost last - ClientList stacking_order; // topmost last - QVector< ClientList > focus_chain; // currently ative last + ClientList unconstrained_stacking_order; + ClientList stacking_order; + QVector< ClientList > focus_chain; ClientList global_focus_chain; // this one is only for things like tabbox's MRU ClientList should_get_focus; // last is most recent ClientList attention_chain; @@ -573,6 +596,7 @@ class Workspace : public QObject, public KDecorationDefines QMenu *popup; QMenu *advanced_popup; + QMenu *trans_popup; QMenu *desk_popup; int desk_popup_index; @@ -656,12 +680,14 @@ class Workspace : public QObject, public KDecorationDefines bool forced_global_mouse_grab; friend class StackingUpdatesBlocker; - //kompmgr + KSelectionOwner* cm_selection; + QTimer compositeTimer; + QTime lastCompositePaint; + int compositeRate; + QRegion damage_region; + Window overlay; // XComposite overlay window QSlider *transSlider; QPushButton *transButton; - - private: - friend bool performTransiencyCheck(); }; // helper for Workspace::blockStackingUpdates() being called in pairs (true/false) @@ -804,6 +830,11 @@ inline bool Workspace::globalShortcutsDisabled() const return global_shortcuts_disabled || global_shortcuts_disabled_for_client; } +inline Window Workspace::overlayWindow() + { + return overlay; + } + template< typename T > inline Client* Workspace::findClient( T predicate ) { @@ -831,7 +862,27 @@ inline void Workspace::forEachClient( T procedure ) return forEachClient( procedure, TruePredicate()); } -KWIN_COMPARE_PREDICATE( ClientMatchPredicate, const Client*, cl == value ); +template< typename T > +inline Unmanaged* Workspace::findUnmanaged( T predicate ) + { + return findUnmanagedInList( unmanaged, predicate ); + } + +template< typename T1, typename T2 > +inline void Workspace::forEachUnmanaged( T1 procedure, T2 predicate ) + { + for ( UnmanagedList::ConstIterator it = unmanaged.begin(); it != unmanaged.end(); ++it) + if ( predicate( const_cast< const Unmanaged* >( *it))) + procedure( *it ); + } + +template< typename T > +inline void Workspace::forEachUnmanaged( T procedure ) + { + return forEachUnmanaged( procedure, TruePredicate()); + } + +KWIN_COMPARE_PREDICATE( ClientMatchPredicate, Client, const Client*, cl == value ); inline bool Workspace::hasClient( const Client* c ) { return findClient( ClientMatchPredicate( c ));