diff --git a/activation.cpp b/activation.cpp index 069ed94675..aebb1fab2c 100644 --- a/activation.cpp +++ b/activation.cpp @@ -472,6 +472,46 @@ bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in return timestampCompare( time, user_time ) >= 0; // time >= user_time } +// basically the same like allowClientActivation(), this time allowing +// a window to be fully raised upon its own request (XRaiseWindow), +// if refused, it will be raised only on top of windows belonging +// to the same application +bool Workspace::allowFullClientRaising( const Client* c ) + { + if( session_saving + && options->focusStealingPreventionLevel <= 3 ) // <= normal + { + return true; + } + Client* ac = activeClient(); + if( options->focusStealingPreventionLevel == 0 ) // none + return true; + if( options->focusStealingPreventionLevel == 5 ) // extreme + return false; + if( ac == NULL || ac->isDesktop()) + { + kdDebug( 1212 ) << "Raising: No client active, allowing" << endl; + return true; // no active client -> always allow + } + // TODO window urgency -> return true? + if( Client::belongToSameApplication( c, ac, true )) + { + kdDebug( 1212 ) << "Raising: Belongs to active application" << endl; + return true; + } + if( options->focusStealingPreventionLevel == 4 ) // high + return false; + if( !c->hasUserTimeSupport()) + { + kdDebug() << "Raising: No support" << endl; + if( options->focusStealingPreventionLevel == 1 ) // low + return true; + } + // options->focusStealingPreventionLevel == 2 // normal + kdDebug() << "Raising: Refusing" << endl; + return false; + } + // called from Client after FocusIn that wasn't initiated by KWin and the client // wasn't allowed to activate void Workspace::restoreFocus() diff --git a/client.h b/client.h index 8938803719..94a6c989fe 100644 --- a/client.h +++ b/client.h @@ -228,7 +228,8 @@ class Client : public QObject, public KDecorationDefines const QPoint calculateGravitation( bool invert ) const; // FRAME public? void NETMoveResize( int x_root, int y_root, NET::Direction direction ); - + void restackWindow( Window above, int detail, NET::RequestSource source, bool send_event = false ); + void gotPing( Time timestamp ); static QCString staticWindowRole(WId); @@ -240,6 +241,7 @@ class Client : public QObject, public KDecorationDefines void checkWorkspacePosition(); void updateUserTime( Time time = CurrentTime ); Time userTime() const; + bool hasUserTimeSupport() const; // does 'delete c;' static void deleteClient( Client* c, allowed_t ); @@ -768,6 +770,11 @@ inline void Client::resize( const QSize& s, bool force ) resize( s.width(), s.height(), force ); } +inline bool Client::hasUserTimeSupport() const + { + return info->userTime() != -1U; + } + #ifdef NDEBUG kndbgstream& operator<<( kndbgstream& stream, const Client* ); #else diff --git a/events.cpp b/events.cpp index 286e2e6fdd..5a992c2963 100644 --- a/events.cpp +++ b/events.cpp @@ -143,6 +143,11 @@ void RootInfo::gotPing( Window w, Time timestamp ) c->gotPing( timestamp ); } +void RootInfo::restackWindow( Window w, Window above, int detail ) + { + if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) + c->restackWindow( above, detail, NET::FromTool, true ); + } // **************************************** // Workspace @@ -684,9 +689,6 @@ void Client::configureRequestEvent( XConfigureRequestEvent* e ) } } - bool stacking = e->value_mask & CWStackMode; - int stack_mode = e->detail; - if ( e->value_mask & CWBorderWidth ) { // first, get rid of a window border @@ -785,27 +787,8 @@ void Client::configureRequestEvent( XConfigureRequestEvent* e ) } } - - if ( stacking ) - { - switch (stack_mode) - { - case Above: - case TopIf: - if( workspace()->allowClientActivation( this )) // not really activation, - workspace()->raiseClient( this ); // but it's the same, showing - else // unwanted window on top - workspace()->restackClientUnderActive( this ); // would be obtrusive - break; - case Below: - case BottomIf: - workspace()->lowerClient( this ); - break; - case Opposite: - default: - break; - } - } + if ( e->value_mask & CWStackMode ) + restackWindow( e->above, e->detail, NET::FromApplication ); // TODO sending a synthetic configure notify always is fine, even in cases where // the ICCCM doesn't require this - it can be though of as 'the WM decided to move diff --git a/layers.cpp b/layers.cpp index ec04074549..5047c0f4b2 100644 --- a/layers.cpp +++ b/layers.cpp @@ -261,6 +261,30 @@ void Workspace::lowerClient( Client* c ) most_recently_raised = 0; } +void Workspace::lowerClientWithinApplication( Client* c ) + { + if ( !c ) + return; + + StackingUpdatesBlocker blocker( this ); + + unconstrained_stacking_order.remove( c ); + bool lowered = false; + // first try to put it below the bottom-most window of the application + for( ClientList::Iterator it = unconstrained_stacking_order.begin(); + it != unconstrained_stacking_order.end(); + ++it ) + if( Client::belongToSameApplication( *it, c )) + { + unconstrained_stacking_order.insert( it, c ); + lowered = true; + break; + } + if( !lowered ) + unconstrained_stacking_order.prepend( c ); + // ignore mainwindows + } + void Workspace::raiseClient( Client* c ) { if ( !c ) @@ -284,6 +308,54 @@ void Workspace::raiseClient( Client* c ) most_recently_raised = c; } +void Workspace::raiseClientWithinApplication( Client* c ) + { + if ( !c ) + return; + + StackingUpdatesBlocker blocker( this ); + // ignore mainwindows + + unconstrained_stacking_order.remove( c ); + bool raised = false; + // first try to put it above the top-most window of the application + for( ClientList::Iterator it = unconstrained_stacking_order.fromLast(); + it != unconstrained_stacking_order.end(); + --it ) + if( Client::belongToSameApplication( *it, c )) + { + ++it; // insert after the found one + unconstrained_stacking_order.insert( it, c ); + raised = true; + break; + } + if( !raised ) + restackClientUnderActive( c ); + } + +void Workspace::raiseClientRequest( Client* c ) + { + if( allowFullClientRaising( c )) + raiseClient( c ); + else + { + raiseClientWithinApplication( c ); + c->demandAttention(); + } + } + +void Workspace::lowerClientRequest( Client* c ) + { + // If the client has support for all this focus stealing prevention stuff, + // do only lowering within the application, as that's the more logical + // variant of lowering when application requests it. + // No demanding of attention here of course. + if( c->hasUserTimeSupport()) + lowerClientWithinApplication( c ); + else + lowerClient( c ); + } + void Workspace::restackClientUnderActive( Client* c ) { if( !active_client || active_client == c ) @@ -485,6 +557,32 @@ bool Workspace::keepTransientAbove( const Client* mainwindow, const Client* tran // Client //******************************* +void Client::restackWindow( Window /*above TODO */, int detail, NET::RequestSource source, bool send_event ) + { + switch ( detail ) + { + case Above: + case TopIf: + if( source == NET::FromTool ) + workspace()->raiseClient( this ); + else + workspace()->raiseClientRequest( this ); + break; + case Below: + case BottomIf: + if( source == NET::FromTool ) + workspace()->lowerClient( this ); + else + workspace()->lowerClientRequest( this ); + break; + case Opposite: + default: + break; + } + if( send_event ) + sendSyntheticConfigureNotify(); + } + void Client::setKeepAbove( bool b ) { if ( b == keepAbove() ) diff --git a/workspace.h b/workspace.h index c81c2fb998..f7420914c2 100644 --- a/workspace.h +++ b/workspace.h @@ -168,6 +168,8 @@ class Workspace : public QObject, virtual public KWinInterface, public KDecorati QPoint adjustClientPosition( Client* c, QPoint pos ); void raiseClient( Client* c ); void lowerClient( Client* c ); + void raiseClientRequest( Client* c ); + void lowerClientRequest( Client* c ); void restackClientUnderActive( Client* ); void updateClientLayer( Client* c ); void raiseOrLowerClient( Client * ); @@ -370,6 +372,9 @@ class Workspace : public QObject, virtual public KWinInterface, public KDecorati void updateStackingOrder( bool propagate_new_clients = false ); void propagateClients( bool propagate_new_clients ); // called only from updateStackingOrder ClientList constrainedStackingOrder(); + void raiseClientWithinApplication( Client* c ); + void lowerClientWithinApplication( Client* c ); + bool allowFullClientRaising( const Client* c ); bool keepTransientAbove( const Client* mainwindow, const Client* transient ); void blockStackingUpdates( bool block ); void updateCurrentTopMenu(); @@ -564,6 +569,7 @@ class RootInfo : public NETRootInfo2 virtual void closeWindow(Window w); virtual void moveResize(Window w, int x_root, int y_root, unsigned long direction); virtual void gotPing(Window w, Time timestamp); + virtual void restackWindow(Window w, Window above, int detail); private: Workspace* workspace; };