diff --git a/client.h b/client.h index b18b62e357..b671d09bac 100644 --- a/client.h +++ b/client.h @@ -344,7 +344,7 @@ private slots: QString readName() const; void setCaption( const QString& s, bool force = false ); bool hasTransientInternal( const Client* c, bool indirect, ConstClientList& set ) const; - void setupWindowRules(); + void setupWindowRules( bool ignore_temporary ); void updateWindowRules(); void finishWindowRules(); diff --git a/manage.cpp b/manage.cpp index d24b902160..209a9a5856 100644 --- a/manage.cpp +++ b/manage.cpp @@ -111,7 +111,7 @@ bool Client::manage( Window w, bool isMapped ) // and only then really set the caption using setCaption(), which checks for duplicates etc. // and also relies on rules already existing cap_normal = readName(); - setupWindowRules(); + setupWindowRules( false ); setCaption( cap_normal, true ); detectNoBorder(); @@ -482,6 +482,9 @@ bool Client::manage( Window w, bool isMapped ) delete session; ungrabXServer(); + + if( client_rules->discardTemporary( true )) + setupWindowRules( true ); return true; } diff --git a/rules.cpp b/rules.cpp index e04b2c60e3..3182adb3bf 100644 --- a/rules.cpp +++ b/rules.cpp @@ -12,6 +12,9 @@ License. See the file "COPYING" for the exact licensing terms. #include #include +#include +#include +#include #include "client.h" #include "workspace.h" @@ -22,7 +25,8 @@ namespace KWinInternal static WindowRules dummyRules; // dummy used to avoid NULL checks WindowRules::WindowRules() - : wmclassregexp( false ) + : temporary_state( 0 ) + , wmclassregexp( false ) , wmclasscomplete( false ) , windowroleregexp( false ) , titleregexp( false ) @@ -53,6 +57,22 @@ WindowRules::WindowRules() { } +WindowRules::WindowRules( const QString& str, bool temporary ) + : temporary_state( temporary ? 2 : 0 ) + { + KTempFile file; + QFile* f = file.file(); + if( f != NULL ) + { + QCString s = str.utf8(); + f->writeBlock( s.data(), s.length()); + } + file.close(); + KSimpleConfig cfg( file.name()); + readFromCfg( cfg ); + file.unlink(); + } + #define READ_MATCH_STRING( var, func ) \ var = cfg.readEntry( #var ) func; \ var##regexp = cfg.readBoolEntry( #var "regexp" ); @@ -70,6 +90,12 @@ WindowRules::WindowRules() var##rule = readForceRule( cfg, #var "rule" ); WindowRules::WindowRules( KConfig& cfg ) + : temporary_state( 0 ) + { + readFromCfg( cfg ); + } + +void WindowRules::readFromCfg( KConfig& cfg ) { wmclass = cfg.readEntry( "wmclass" ).lower().latin1(); wmclassregexp = cfg.readBoolEntry( "wmclassregexp" ); @@ -107,7 +133,7 @@ WindowRules::WindowRules( KConfig& cfg ) READ_FORCE_RULE( acceptfocus, Bool, ); READ_FORCE_RULE( moveresizemode, , Options::stringToMoveResizeMode ); READ_FORCE_RULE( closeable, Bool, ); - kdDebug() << "READ RULE:" << wmclass << endl; + kdDebug() << "READ RULE:" << wmclass << "/" << title << endl; } #undef READ_MATCH_STRING @@ -212,29 +238,31 @@ bool WindowRules::match( const Client* c ) const { if( types != NET::AllTypesMask ) { - if( !NET::typeMatchesMask( c->windowType( true ), types )) // direct + NET::WindowType t = c->windowType( true ); // direct type + if( t == NET::Unknown ) + t = NET::Normal; // NET::Unknown->NET::Normal is only here for matching + if( !NET::typeMatchesMask( t, types )) return false; } - // TODO exactMatch() for regexp? if( !wmclass.isEmpty()) { // TODO optimize? QCString cwmclass = wmclasscomplete ? c->resourceName() + ' ' + c->resourceClass() : c->resourceClass(); - if( wmclassregexp && !QRegExp( wmclass ).exactMatch( cwmclass )) + if( wmclassregexp && QRegExp( wmclass ).search( cwmclass ) == -1 ) return false; if( !wmclassregexp && wmclass != cwmclass ) return false; } if( !windowrole.isEmpty()) { - if( windowroleregexp && !QRegExp( windowrole ).exactMatch( c->windowRole())) + if( windowroleregexp && QRegExp( windowrole ).search( c->windowRole()) == -1 ) return false; if( !windowroleregexp && windowrole != c->windowRole()) return false; } if( !title.isEmpty()) { - if( titleregexp && !QRegExp( title ).exactMatch( c->caption( false ))) + if( titleregexp && QRegExp( title ).search( c->caption( false )) == -1 ) return false; if( !titleregexp && title != c->caption( false )) return false; @@ -243,8 +271,8 @@ bool WindowRules::match( const Client* c ) const if( !clientmachine.isEmpty()) { if( clientmachineregexp - && !QRegExp( clientmachine ).exactMatch( c->wmClientMachine( true )) - && !QRegExp( clientmachine ).exactMatch( c->wmClientMachine( false ))) + && QRegExp( clientmachine ).search( c->wmClientMachine( true )) == -1 + && QRegExp( clientmachine ).search( c->wmClientMachine( false )) == -1 ) return false; if( !clientmachineregexp && clientmachine != c->wmClientMachine( true ) @@ -404,11 +432,29 @@ bool WindowRules::checkCloseable( bool closeable ) const return checkForceRule( closeablerule ) ? this->closeable : closeable; } +bool WindowRules::isTemporary() const + { + return temporary_state > 0; + } + +bool WindowRules::discardTemporary( bool force ) + { + if( temporary_state == 0 ) // not temporary + return false; + if( force || --temporary_state == 0 ) // too old + { + kdDebug() << "DISCARD:" << wmclass << "/" << title << endl; + delete this; + return true; + } + return false; + } + // Client -void Client::setupWindowRules() +void Client::setupWindowRules( bool ignore_temporary ) { - client_rules = workspace()->findWindowRules( this ); + client_rules = workspace()->findWindowRules( this, ignore_temporary ); // check only after getting the rules, because there may be a rule forcing window type if( isTopMenu()) // TODO cannot have restrictions client_rules = &dummyRules; @@ -427,17 +473,22 @@ void Client::finishWindowRules() // Workspace -WindowRules* Workspace::findWindowRules( const Client* c ) const +WindowRules* Workspace::findWindowRules( const Client* c, bool ignore_temporary ) { for( QValueList< WindowRules* >::ConstIterator it = windowRules.begin(); it != windowRules.end(); ++it ) { - // chaining for wildcard matches + if( ignore_temporary && (*it)->isTemporary()) + continue; + // TODO chaining for wildcard matches if( (*it)->match( c )) { kdDebug() << "RULE FOUND:" << c << endl; - return *it; + WindowRules* ret = *it; + if( ret->isTemporary()) + windowRules.remove( *it ); + return ret; } } kdDebug() << "RULE DUMMY:" << c << endl; @@ -478,11 +529,49 @@ void Workspace::writeWindowRules() int i = 1; for( QValueList< WindowRules* >::ConstIterator it = windowRules.begin(); it != windowRules.end(); - ++it, ++i ) + ++it ) { + if( (*it)->isTemporary()) + continue; cfg.setGroup( QString::number( i )); (*it)->write( cfg ); + ++i; } } +void Workspace::gotTemporaryRulesMessage( const QString& message ) + { + bool was_temporary = false; + for( QValueList< WindowRules* >::ConstIterator it = windowRules.begin(); + it != windowRules.end(); + ++it ) + if( (*it)->isTemporary()) + was_temporary = true; + WindowRules* rule = new WindowRules( message, true ); + windowRules.prepend( rule ); // highest priority first + if( !was_temporary ) + QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); + } + +void Workspace::cleanupTemporaryRules() + { + kdDebug() << "CLEANUP" << endl; + bool has_temporary = false; + for( QValueList< WindowRules* >::Iterator it = windowRules.begin(); + it != windowRules.end(); + ) + { + if( (*it)->discardTemporary( false )) + it = windowRules.remove( it ); + else + { + if( (*it)->isTemporary()) + has_temporary = true; + ++it; + } + } + if( has_temporary ) + QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); + } + } // namespace diff --git a/rules.h b/rules.h index 8194a3c063..e81f753316 100644 --- a/rules.h +++ b/rules.h @@ -42,8 +42,11 @@ class WindowRules public: WindowRules(); WindowRules( KConfig& ); + WindowRules( const QString&, bool temporary ); void write( KConfig& ) const; void update( Client* ); + bool isTemporary() const; + bool discardTemporary( bool force ); // removes if temporary and forced or too old bool match( const Client* c ) const; Placement::Policy checkPlacement( Placement::Policy placement ) const; QRect checkGeometry( const QRect& rect, bool init = false ) const; @@ -68,11 +71,13 @@ class WindowRules Options::MoveResizeMode checkMoveResizeMode( Options::MoveResizeMode mode ) const; bool checkCloseable( bool closeable ) const; private: + void readFromCfg( KConfig& cfg ); static SettingRule readRule( KConfig&, const QString& key ); static SettingRule readForceRule( KConfig&, const QString& key ); static NET::WindowType readType( KConfig&, const QString& key ); static bool checkRule( SettingRule rule, bool init ); static bool checkForceRule( SettingRule rule ); + int temporary_state; // e.g. for kstart QCString wmclass; bool wmclassregexp; bool wmclasscomplete; diff --git a/workspace.cpp b/workspace.cpp index 2dd47e145d..4e4c15caf9 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -66,6 +66,7 @@ Workspace::Workspace( bool restore ) number_of_desktops(0), popup_client (0), desktop_widget (0), + temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ), active_client (0), last_active_client (0), most_recently_raised (0), @@ -109,6 +110,9 @@ Workspace::Workspace( bool restore ) installed_colormap = default_colormap; session.setAutoDelete( TRUE ); + connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )), + this, SLOT( gotTemporaryRulesMessage( const QString& ))); + updateXTime(); // needed for proper initialization of user_time in Client ctor delayFocusTimer = 0; @@ -247,6 +251,7 @@ void Workspace::init() NET::WM2RestackWindow | NET::WM2MoveResizeWindow | NET::WM2ExtendedStrut | + NET::WM2KDETemporaryRules | 0 , NET::ActionMove | diff --git a/workspace.h b/workspace.h index 2e3e713b9f..ec2bd2e6a0 100644 --- a/workspace.h +++ b/workspace.h @@ -16,6 +16,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include #include "KWinInterface.h" #include "utils.h" @@ -202,7 +203,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void storeSession( KConfig* config, SMSavePhase phase ); SessionInfo* takeSessionInfo( Client* ); - WindowRules* findWindowRules( const Client* ) const; + WindowRules* findWindowRules( const Client*, bool ); // dcop interface void cascadeDesktop(); @@ -330,6 +331,8 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void lostTopMenuSelection(); void lostTopMenuOwner(); void delayFocus(); + void gotTemporaryRulesMessage( const QString& ); + void cleanupTemporaryRules(); protected: bool keyPressMouseEmulation( XKeyEvent& ev ); @@ -431,6 +434,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine QPtrList session; QValueList windowRules; + KXMessages temporaryRulesMessages; static const char* windowTypeToTxt( NET::WindowType type ); static NET::WindowType txtToWindowType( const char* txt ); static bool sessionInfoWindowTypeMatch( Client* c, SessionInfo* info );