• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

group.cpp

Go to the documentation of this file.
00001 /********************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 *********************************************************************/
00021 
00022 /*
00023 
00024  This file contains things relevant to window grouping.
00025 
00026 */
00027 
00028 //#define QT_CLEAN_NAMESPACE
00029 
00030 #include "group.h"
00031 #include <QTextStream>
00032 #include "workspace.h"
00033 #include "client.h"
00034 #include "effects.h"
00035 
00036 #include <assert.h>
00037 #include <kstartupinfo.h>
00038 #include <QX11Info>
00039 
00040 
00041 /*
00042  TODO
00043  Rename as many uses of 'transient' as possible (hasTransient->hasSubwindow,etc.),
00044  or I'll get it backwards in half of the cases again.
00045 */
00046 
00047 namespace KWin
00048 {
00049 
00050 /*
00051  Consistency checks for window relations. Since transients are determinated
00052  using Client::transiency_list and main windows are determined using Client::transientFor()
00053  or the group for group transients, these have to match both ways.
00054 */
00055 //#define ENABLE_TRANSIENCY_CHECK
00056 
00057 #ifdef NDEBUG
00058 #undef ENABLE_TRANSIENCY_CHECK
00059 #endif
00060 
00061 #ifdef ENABLE_TRANSIENCY_CHECK
00062 static bool transiencyCheckNonExistent = false;
00063 
00064 bool performTransiencyCheck()
00065     {
00066     bool ret = true;
00067     ClientList clients = Workspace::self()->clients;
00068     for( ClientList::ConstIterator it1 = clients.constBegin();
00069          it1 != clients.constEnd();
00070          ++it1 )
00071         {
00072         if( (*it1)->deleting )
00073             continue;
00074         if( (*it1)->in_group == NULL )
00075             {
00076             kDebug(1212) << "TC: " << *it1 << " in not in a group" << endl;
00077             ret = false;
00078             }
00079         else if( !(*it1)->in_group->members().contains( *it1 ))
00080             {
00081             kDebug(1212) << "TC: " << *it1 << " has a group " << (*it1)->in_group << " but group does not contain it" << endl;
00082             ret = false;
00083             }
00084         if( !(*it1)->isTransient())
00085             {
00086             if( !(*it1)->mainClients().isEmpty())
00087                 {
00088                 kDebug(1212) << "TC: " << *it1 << " is not transient, has main clients:" << (*it1)->mainClients() << endl;
00089                 ret = false;
00090                 }
00091             }
00092         else
00093             {
00094             ClientList mains = (*it1)->mainClients();
00095             for( ClientList::ConstIterator it2 = mains.constBegin();
00096                  it2 != mains.constEnd();
00097                  ++it2 )
00098                 {
00099                 if( transiencyCheckNonExistent
00100                     && !Workspace::self()->clients.contains( *it2 )
00101                     && !Workspace::self()->desktops.contains( *it2 ))
00102                     {
00103                     kDebug(1212) << "TC:" << *it1 << " has non-existent main client ";
00104                     kDebug(1212) << "TC2:" << *it2; // this may crash
00105                     ret = false;
00106                     continue;
00107                     }
00108                 if( !(*it2)->transients_list.contains( *it1 ))
00109                     {
00110                     kdDebug(1212) << "TC:" << *it1 << " has main client " << *it2 << " but main client does not have it as a transient" << endl;
00111                     ret = false;
00112                     }
00113                 }
00114             }
00115         ClientList trans = (*it1)->transients_list;
00116         for( ClientList::ConstIterator it2 = trans.constBegin();
00117              it2 != trans.constEnd();
00118              ++it2 )
00119             {
00120             if( transiencyCheckNonExistent
00121                 && !Workspace::self()->clients.contains( *it2 )
00122                 && !Workspace::self()->desktops.contains( *it2 ))
00123                 {
00124                 kDebug(1212) << "TC:" << *it1 << " has non-existent transient ";
00125                 kDebug(1212) << "TC2:" << *it2; // this may crash
00126                 ret = false;
00127                 continue;
00128                 }
00129             if( !(*it2)->mainClients().contains( *it1 ))
00130                 {
00131                 kdDebug(1212) << "TC:" << *it1 << " has transient " << *it2 << " but transient does not have it as a main client" << endl;
00132                 ret = false;
00133                 }
00134             }
00135         }
00136     GroupList groups = Workspace::self()->groups;
00137     for( GroupList::ConstIterator it1 = groups.constBegin();
00138          it1 != groups.constEnd();
00139          ++it1 )
00140         {
00141         ClientList members = (*it1)->members();
00142         for( ClientList::ConstIterator it2 = members.constBegin();
00143              it2 != members.constEnd();
00144              ++it2 )
00145             {
00146             if( (*it2)->in_group != *it1 )
00147                 {
00148                 kDebug(1212) << "TC: Group " << *it1 << " contains client " << *it2 << " but client is not in that group" << endl;
00149                 ret = false;
00150                 }
00151             }
00152         }
00153     return ret;
00154     }
00155 
00156 static QString transiencyCheckStartBt;
00157 static const Client* transiencyCheckClient;
00158 static int transiencyCheck = 0;
00159 
00160 static void startTransiencyCheck( const QString& bt, const Client* c, bool ne )
00161     {
00162     if( ++transiencyCheck == 1 )
00163         {
00164         transiencyCheckStartBt = bt;
00165         transiencyCheckClient = c;
00166         }
00167     if( ne )
00168         transiencyCheckNonExistent = true;
00169     }
00170 static void checkTransiency()
00171     {
00172     if( --transiencyCheck == 0 )
00173         {
00174         if( !performTransiencyCheck())
00175             {
00176             kDebug(1212) << "BT:" << transiencyCheckStartBt << endl;
00177             kDebug(1212) << "CLIENT:" << transiencyCheckClient << endl;
00178             abort();
00179             }
00180         transiencyCheckNonExistent = false;
00181         }
00182     }
00183 class TransiencyChecker
00184     {
00185     public:
00186         TransiencyChecker( const QString& bt, const Client*c ) { startTransiencyCheck( bt, c, false ); }
00187         ~TransiencyChecker() { checkTransiency(); }
00188     };
00189 
00190 void checkNonExistentClients()
00191     {
00192     startTransiencyCheck( kdBacktrace(), NULL, true );
00193     checkTransiency();
00194     }
00195 
00196 #define TRANSIENCY_CHECK( c ) TransiencyChecker transiency_checker( kdBacktrace(), c )
00197 
00198 #else
00199 
00200 #define TRANSIENCY_CHECK( c )
00201 
00202 void checkNonExistentClients()
00203     {
00204     }
00205 
00206 #endif
00207 
00208 //********************************************
00209 // Group
00210 //********************************************
00211 
00212 Group::Group( Window leader_P, Workspace* workspace_P )
00213     :   leader_client( NULL ),
00214         leader_wid( leader_P ),
00215         _workspace( workspace_P ),
00216         leader_info( NULL ),
00217         user_time( -1U ),
00218         refcount( 0 )
00219     {
00220     if( leader_P != None )
00221         {
00222         leader_client = workspace_P->findClient( WindowMatchPredicate( leader_P ));
00223         unsigned long properties[ 2 ] = { 0, NET::WM2StartupId };
00224         leader_info = new NETWinInfo2( display(), leader_P, rootWindow(),
00225             properties, 2 );
00226         }
00227     effect_group = new EffectWindowGroupImpl( this );
00228     workspace()->addGroup( this, Allowed );
00229     }
00230 
00231 Group::~Group()
00232     {
00233     delete leader_info;
00234     delete effect_group;
00235     }
00236 
00237 QPixmap Group::icon() const
00238     {
00239     if( leader_client != NULL )
00240         return leader_client->icon();
00241     else if( leader_wid != None )
00242         {
00243         QPixmap ic;
00244         Client::readIcons( leader_wid, &ic, NULL );
00245         return ic;
00246         }
00247     return QPixmap();
00248     }
00249 
00250 QPixmap Group::miniIcon() const
00251     {
00252     if( leader_client != NULL )
00253         return leader_client->miniIcon();
00254     else if( leader_wid != None )
00255         {
00256         QPixmap ic;
00257         Client::readIcons( leader_wid, NULL, &ic );
00258         return ic;
00259         }
00260     return QPixmap();
00261     }
00262 
00263 void Group::addMember( Client* member_P )
00264     {
00265     TRANSIENCY_CHECK( member_P );
00266     _members.append( member_P );
00267 //    kDebug(1212) << "GROUPADD:" << this << ":" << member_P;
00268 //    kDebug(1212) << kBacktrace();
00269     }
00270 
00271 void Group::removeMember( Client* member_P )
00272     {
00273     TRANSIENCY_CHECK( member_P );
00274 //    kDebug(1212) << "GROUPREMOVE:" << this << ":" << member_P;
00275 //    kDebug(1212) << kBacktrace();
00276     Q_ASSERT( _members.contains( member_P ));
00277     _members.removeAll( member_P );
00278 // there are cases when automatic deleting of groups must be delayed,
00279 // e.g. when removing a member and doing some operation on the possibly
00280 // other members of the group (which would be however deleted already
00281 // if there were no other members)
00282     if( refcount == 0 && _members.isEmpty())
00283         {
00284         workspace()->removeGroup( this, Allowed );
00285         delete this;
00286         }
00287     }
00288 
00289 void Group::ref()
00290     {
00291     ++refcount;
00292     }
00293 
00294 void Group::deref()
00295     {
00296     if( --refcount == 0 && _members.isEmpty())
00297         {
00298         workspace()->removeGroup( this, Allowed );
00299         delete this;
00300         }
00301     }
00302 
00303 void Group::gotLeader( Client* leader_P )
00304     {
00305     assert( leader_P->window() == leader_wid );
00306     leader_client = leader_P;
00307     }
00308 
00309 void Group::lostLeader()
00310     {
00311     assert( !_members.contains( leader_client ));
00312     leader_client = NULL;
00313     if( _members.isEmpty())
00314         {
00315         workspace()->removeGroup( this, Allowed );
00316         delete this;
00317         }
00318     }
00319 
00320 void Group::getIcons()
00321     {
00322     // TODO - also needs adding the flag to NETWinInfo
00323     }
00324 
00325 //***************************************
00326 // Workspace
00327 //***************************************
00328 
00329 Group* Workspace::findGroup( Window leader ) const
00330     {
00331     assert( leader != None );
00332     for( GroupList::ConstIterator it = groups.constBegin();
00333          it != groups.constEnd();
00334          ++it )
00335         if( (*it)->leader() == leader )
00336             return *it;
00337     return NULL;
00338     }
00339 
00340 // Client is group transient, but has no group set. Try to find
00341 // group with windows with the same client leader.
00342 Group* Workspace::findClientLeaderGroup( const Client* c ) const
00343     {
00344     TRANSIENCY_CHECK( c );
00345     Group* ret = NULL;
00346     for( ClientList::ConstIterator it = clients.constBegin();
00347          it != clients.constEnd();
00348          ++it )
00349         {
00350         if( *it == c )
00351             continue;
00352         if( (*it)->wmClientLeader() == c->wmClientLeader())
00353             {
00354             if( ret == NULL || ret == (*it)->group())
00355                 ret = (*it)->group();
00356             else
00357                 {
00358                 // There are already two groups with the same client leader.
00359                 // This most probably means the app uses group transients without
00360                 // setting group for its windows. Merging the two groups is a bad
00361                 // hack, but there's no really good solution for this case.
00362                 ClientList old_group = (*it)->group()->members();
00363                 // old_group autodeletes when being empty
00364                 for( int pos = 0;
00365                      pos < old_group.count();
00366                      ++pos )
00367                     {
00368                     Client* tmp = old_group[ pos ];
00369                     if( tmp != c )
00370                         tmp->changeClientLeaderGroup( ret );
00371                     }
00372                 }
00373             }
00374         }
00375     return ret;
00376     }
00377 
00378 void Workspace::updateMinimizedOfTransients( Client* c )
00379     {
00380     // if mainwindow is minimized or shaded, minimize transients too
00381     if ( c->isMinimized() || c->isShade() )
00382         {
00383         for( ClientList::ConstIterator it = c->transients().constBegin();
00384              it != c->transients().constEnd();
00385              ++it )
00386             {
00387             if( !(*it)->isMinimized()
00388                  && !(*it)->isTopMenu() ) // topmenus are not minimized, they're hidden
00389                 {
00390                 (*it)->minimize();
00391                 updateMinimizedOfTransients( (*it) );
00392                 }
00393             }
00394         if( c->isModal()) // if a modal dialog is minimized, minimize its mainwindow too
00395             {
00396             foreach( Client* c2, c->mainClients())
00397                 c2->minimize();
00398             }
00399         }
00400     else
00401         { // else unmiminize the transients
00402         for( ClientList::ConstIterator it = c->transients().constBegin();
00403              it != c->transients().constEnd();
00404              ++it )
00405             {
00406             if( (*it)->isMinimized()
00407                 && !(*it)->isTopMenu())
00408                 {
00409                 (*it)->unminimize();
00410                 updateMinimizedOfTransients( (*it) );
00411                 }
00412             }
00413         if( c->isModal())
00414             {
00415             foreach( Client* c2, c->mainClients())
00416                 c2->unminimize();
00417             }
00418         }
00419     }
00420 
00421 
00425 void Workspace::updateOnAllDesktopsOfTransients( Client* c )
00426     {
00427     for( ClientList::ConstIterator it = c->transients().constBegin();
00428          it != c->transients().constEnd();
00429          ++it)
00430         {
00431         if( (*it)->isOnAllDesktops() != c->isOnAllDesktops())
00432             (*it)->setOnAllDesktops( c->isOnAllDesktops());
00433         }
00434     }
00435 
00436 // A new window has been mapped. Check if it's not a mainwindow for some already existing transient window.
00437 void Workspace::checkTransients( Window w )
00438     {
00439     TRANSIENCY_CHECK( NULL );
00440     for( ClientList::ConstIterator it = clients.constBegin();
00441          it != clients.constEnd();
00442          ++it )
00443         (*it)->checkTransient( w );
00444     }
00445 
00446 
00447 //****************************************
00448 // Toplevel
00449 //****************************************
00450 
00451 // hacks for broken apps here
00452 // all resource classes are forced to be lowercase
00453 bool Toplevel::resourceMatch( const Toplevel* c1, const Toplevel* c2 )
00454     {
00455     // xv has "xv" as resource name, and different strings starting with "XV" as resource class
00456     if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
00457          return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv";
00458     // Mozilla has "Mozilla" as resource name, and different strings as resource class
00459     if( c1->resourceName() == "mozilla" )
00460         return c2->resourceName() == "mozilla";
00461     return c1->resourceClass() == c2->resourceClass();
00462     }
00463 
00464 
00465 //****************************************
00466 // Client
00467 //****************************************
00468 
00469 bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack )
00470     {
00471     bool same_app = false;
00472 
00473     // tests that definitely mean they belong together
00474     if( c1 == c2 )
00475         same_app = true;
00476     else if( c1->isTransient() && c2->hasTransient( c1, true ))
00477         same_app = true; // c1 has c2 as mainwindow
00478     else if( c2->isTransient() && c1->hasTransient( c2, true ))
00479         same_app = true; // c2 has c1 as mainwindow
00480     else if( c1->group() == c2->group())
00481         same_app = true; // same group
00482     else if( c1->wmClientLeader() == c2->wmClientLeader()
00483         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00484         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00485         same_app = true; // same client leader
00486 
00487     // tests that mean they most probably don't belong together
00488     else if( c1->pid() != c2->pid()
00489         || c1->wmClientMachine( false ) != c2->wmClientMachine( false ))
00490         ; // different processes
00491     else if( c1->wmClientLeader() != c2->wmClientLeader()
00492         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00493         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00494         ; // different client leader
00495     else if( !resourceMatch( c1, c2 ))
00496         ; // different apps
00497     else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00498         ; // "different" apps
00499     else if( c1->pid() == 0 || c2->pid() == 0 )
00500         ; // old apps that don't have _NET_WM_PID, consider them different
00501           // if they weren't found to match above
00502     else
00503         same_app = true; // looks like it's the same app
00504 
00505     return same_app;
00506     }
00507 
00508 // Non-transient windows with window role containing '#' are always
00509 // considered belonging to different applications (unless
00510 // the window role is exactly the same). KMainWindow sets
00511 // window role this way by default, and different KMainWindow
00512 // usually "are" different application from user's point of view.
00513 // This help with no-focus-stealing for e.g. konqy reusing.
00514 // On the other hand, if one of the windows is active, they are
00515 // considered belonging to the same application. This is for
00516 // the cases when opening new mainwindow directly from the application,
00517 // e.g. 'Open New Window' in konqy ( active_hack == true ).
00518 bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack )
00519     {
00520     if( c1->isTransient())
00521         {
00522         while( c1->transientFor() != NULL )
00523             c1 = c1->transientFor();
00524         if( c1->groupTransient())
00525             return c1->group() == c2->group();
00526 #if 0
00527                 // if a group transient is in its own group, it didn't possibly have a group,
00528                 // and therefore should be considered belonging to the same app like
00529                 // all other windows from the same app
00530                 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00531 #endif
00532         }
00533     if( c2->isTransient())
00534         {
00535         while( c2->transientFor() != NULL )
00536             c2 = c2->transientFor();
00537         if( c2->groupTransient())
00538             return c1->group() == c2->group();
00539 #if 0
00540                 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00541 #endif
00542         }
00543     int pos1 = c1->windowRole().indexOf( '#' );
00544     int pos2 = c2->windowRole().indexOf( '#' );
00545     if(( pos1 >= 0 && pos2 >= 0 )
00546         ||
00547     // hacks here
00548         // Mozilla has resourceName() and resourceClass() swapped
00549         ( c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" ))
00550         {
00551         if( !active_hack )   // without the active hack for focus stealing prevention,
00552             return c1 == c2; // different mainwindows are always different apps
00553         if( !c1->isActive() && !c2->isActive())
00554             return c1 == c2;
00555         else
00556             return true;
00557         }
00558     return true;
00559     }
00560 
00561 /*
00562 
00563  Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3
00564 
00565  WM_TRANSIENT_FOR is basically means "this is my mainwindow".
00566  For NET::Unknown windows, transient windows are considered to be NET::Dialog
00567  windows, for compatibility with non-NETWM clients. KWin may adjust the value
00568  of this property in some cases (window pointing to itself or creating a loop,
00569  keeping NET::Splash windows above other windows from the same app, etc.).
00570 
00571  Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after
00572  possibly being adjusted by KWin. Client::transient_for points to the Client
00573  this Client is transient for, or is NULL. If Client::transient_for_id is
00574  poiting to the root window, the window is considered to be transient
00575  for the whole window group, as suggested in NETWM 7.3.
00576 
00577  In the case of group transient window, Client::transient_for is NULL,
00578  and Client::groupTransient() returns true. Such window is treated as
00579  if it were transient for every window in its window group that has been
00580  mapped _before_ it (or, to be exact, was added to the same group before it).
00581  Otherwise two group transients can create loops, which can lead very very
00582  nasty things (bug #67914 and all its dupes).
00583 
00584  Client::original_transient_for_id is the value of the property, which
00585  may be different if Client::transient_for_id if e.g. forcing NET::Splash
00586  to be kept on top of its window group, or when the mainwindow is not mapped
00587  yet, in which case the window is temporarily made group transient,
00588  and when the mainwindow is mapped, transiency is re-evaluated.
00589 
00590  This can get a bit complicated with with e.g. two Konqueror windows created
00591  by the same process. They should ideally appear like two independent applications
00592  to the user. This should be accomplished by all windows in the same process
00593  having the same window group (needs to be changed in Qt at the moment), and
00594  using non-group transients poiting to their relevant mainwindow for toolwindows
00595  etc. KWin should handle both group and non-group transient dialogs well.
00596 
00597  In other words:
00598  - non-transient windows     : isTransient() == false
00599  - normal transients         : transientFor() != NULL
00600  - group transients          : groupTransient() == true
00601 
00602  - list of mainwindows       : mainClients()  (call once and loop over the result)
00603  - list of transients        : transients()
00604  - every window in the group : group()->members()
00605 */
00606 
00607 void Client::readTransient()
00608     {
00609     TRANSIENCY_CHECK( this );
00610     Window new_transient_for_id;
00611     if( XGetTransientForHint( display(), window(), &new_transient_for_id ))
00612         {
00613         original_transient_for_id = new_transient_for_id;
00614         new_transient_for_id = verifyTransientFor( new_transient_for_id, true );
00615         }
00616     else
00617         {
00618         original_transient_for_id = None;
00619         new_transient_for_id = verifyTransientFor( None, false );
00620         }
00621     setTransient( new_transient_for_id );
00622     }
00623 
00624 void Client::setTransient( Window new_transient_for_id )
00625     {
00626     TRANSIENCY_CHECK( this );
00627     if( new_transient_for_id != transient_for_id )
00628         {
00629         removeFromMainClients();
00630         transient_for = NULL;
00631         transient_for_id = new_transient_for_id;
00632         if( transient_for_id != None && !groupTransient())
00633             {
00634             transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id ));
00635             assert( transient_for != NULL ); // verifyTransient() had to check this
00636             transient_for->addTransient( this );
00637             } // checkGroup() will check 'check_active_modal'
00638         checkGroup( NULL, true ); // force, because transiency has changed
00639         if( isTopMenu())
00640             workspace()->updateCurrentTopMenu();
00641         workspace()->updateClientLayer( this );
00642         }
00643     }
00644 
00645 void Client::removeFromMainClients()
00646     {
00647     TRANSIENCY_CHECK( this );
00648     if( transientFor() != NULL )
00649         transientFor()->removeTransient( this );
00650     if( groupTransient())
00651         {
00652         for( ClientList::ConstIterator it = group()->members().constBegin();
00653              it != group()->members().constEnd();
00654              ++it )
00655             (*it)->removeTransient( this );
00656         }
00657     }
00658 
00659 // *sigh* this transiency handling is madness :(
00660 // This one is called when destroying/releasing a window.
00661 // It makes sure this client is removed from all grouping
00662 // related lists.
00663 void Client::cleanGrouping()
00664     {
00665     TRANSIENCY_CHECK( this );
00666 //    kDebug(1212) << "CLEANGROUPING:" << this;
00667 //    for( ClientList::ConstIterator it = group()->members().begin();
00668 //         it != group()->members().end();
00669 //         ++it )
00670 //        kDebug(1212) << "CL:" << *it;
00671 //    ClientList mains;
00672 //    mains = mainClients();
00673 //    for( ClientList::ConstIterator it = mains.begin();
00674 //         it != mains.end();
00675 //         ++it )
00676 //        kDebug(1212) << "MN:" << *it;
00677     removeFromMainClients();
00678 //    kDebug(1212) << "CLEANGROUPING2:" << this;
00679 //    for( ClientList::ConstIterator it = group()->members().begin();
00680 //         it != group()->members().end();
00681 //         ++it )
00682 //        kDebug(1212) << "CL2:" << *it;
00683 //    mains = mainClients();
00684 //    for( ClientList::ConstIterator it = mains.begin();
00685 //         it != mains.end();
00686 //         ++it )
00687 //        kDebug(1212) << "MN2:" << *it;
00688     for( ClientList::ConstIterator it = transients_list.constBegin();
00689          it != transients_list.constEnd();
00690          )
00691         {
00692         if( (*it)->transientFor() == this )
00693             {
00694             removeTransient( *it );
00695             it = transients_list.constBegin(); // restart, just in case something more has changed with the list
00696             }
00697         else
00698             ++it;
00699         }
00700 //    kDebug(1212) << "CLEANGROUPING3:" << this;
00701 //    for( ClientList::ConstIterator it = group()->members().begin();
00702 //         it != group()->members().end();
00703 //         ++it )
00704 //        kDebug(1212) << "CL3:" << *it;
00705 //    mains = mainClients();
00706 //    for( ClientList::ConstIterator it = mains.begin();
00707 //         it != mains.end();
00708 //         ++it )
00709 //        kDebug(1212) << "MN3:" << *it;
00710     // HACK
00711     // removeFromMainClients() did remove 'this' from transient
00712     // lists of all group members, but then made windows that
00713     // were transient for 'this' group transient, which again
00714     // added 'this' to those transient lists :(
00715     ClientList group_members = group()->members();
00716     group()->removeMember( this );
00717     in_group = NULL;
00718     for( ClientList::ConstIterator it = group_members.constBegin();
00719          it != group_members.constEnd();
00720          ++it )
00721         (*it)->removeTransient( this );
00722 //    kDebug(1212) << "CLEANGROUPING4:" << this;
00723 //    for( ClientList::ConstIterator it = group_members.begin();
00724 //         it != group_members.end();
00725 //         ++it )
00726 //        kDebug(1212) << "CL4:" << *it;
00727     }
00728 
00729 // Make sure that no group transient is considered transient
00730 // for a window that is (directly or indirectly) transient for it
00731 // (including another group transients).
00732 // Non-group transients not causing loops are checked in verifyTransientFor().
00733 void Client::checkGroupTransients()
00734     {
00735     TRANSIENCY_CHECK( this );
00736     for( ClientList::ConstIterator it1 = group()->members().constBegin();
00737          it1 != group()->members().constEnd();
00738          ++it1 )
00739         {
00740         if( !(*it1)->groupTransient()) // check all group transients in the group
00741             continue;                  // TODO optimize to check only the changed ones?
00742         for( ClientList::ConstIterator it2 = group()->members().constBegin();
00743              it2 != group()->members().constEnd();
00744              ++it2 ) // group transients can be transient only for others in the group,
00745             {        // so don't make them transient for the ones that are transient for it
00746             if( *it1 == *it2 )
00747                 continue;
00748             for( Client* cl = (*it2)->transientFor();
00749                  cl != NULL;
00750                  cl = cl->transientFor())
00751                 {
00752                 if( cl == *it1 )
00753                     { // don't use removeTransient(), that would modify *it2 too
00754                     (*it2)->transients_list.removeAll( *it1 );
00755                     continue;
00756                     }
00757                 }
00758             // if *it1 and *it2 are both group transients, and are transient for each other,
00759             // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later,
00760             // and should be therefore on top of *it1
00761             // TODO This could possibly be optimized, it also requires hasTransient() to check for loops.
00762             if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00763                 (*it2)->transients_list.removeAll( *it1 );
00764             // if there are already windows W1 and W2, W2 being transient for W1, and group transient W3
00765             // is added, make it transient only for W2, not for W1, because it's already indirectly
00766             // transient for it - the indirect transiency actually shouldn't break anything,
00767             // but it can lead to exponentially expensive operations (#95231)
00768             // TODO this is pretty slow as well
00769             for( ClientList::ConstIterator it3 = group()->members().constBegin();
00770                  it3 != group()->members().constEnd();
00771                  ++it3 )
00772                 {
00773                 if( *it1 == *it2 || *it2 == *it3 || *it1 == *it3 )
00774                     continue;
00775                 if( (*it2)->hasTransient( *it1, false ) && (*it3)->hasTransient( *it1, false ))
00776                     {
00777                     if( (*it2)->hasTransient( *it3, true ))
00778                         (*it2)->transients_list.removeAll( *it1 );
00779                     if( (*it3)->hasTransient( *it2, true ))
00780                         (*it3)->transients_list.removeAll( *it1 );
00781                     }
00782                 }
00783             }
00784         }
00785     }
00786 
00790 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00791     {
00792     Window new_property_value = new_transient_for;
00793     // make sure splashscreens are shown above all their app's windows, even though
00794     // they're in Normal layer
00795     if( isSplash() && new_transient_for == None )
00796         new_transient_for = rootWindow();
00797     if( new_transient_for == None )
00798         {
00799         if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window
00800             new_property_value = new_transient_for = rootWindow();
00801         else
00802             return None;
00803         }
00804     if( new_transient_for == window()) // pointing to self
00805         { // also fix the property itself
00806         kWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." ;
00807         new_property_value = new_transient_for = rootWindow();
00808         }
00809 //  The transient_for window may be embedded in another application,
00810 //  so kwin cannot see it. Try to find the managed client for the
00811 //  window and fix the transient_for property if possible.
00812     WId before_search = new_transient_for;
00813     while( new_transient_for != None
00814            && new_transient_for != rootWindow()
00815            && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00816         {
00817         Window root_return, parent_return;
00818         Window* wins = NULL;
00819         unsigned int nwins;
00820         int r = XQueryTree(display(), new_transient_for, &root_return, &parent_return, &wins, &nwins);
00821         if ( wins )
00822             XFree((void *) wins);
00823         if ( r == 0)
00824             break;
00825         new_transient_for = parent_return;
00826         }
00827     if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00828         {
00829         if( new_transient_for != before_search )
00830             {
00831             kDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00832                 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00833             new_property_value = new_transient_for; // also fix the property
00834             }
00835         }
00836     else
00837         new_transient_for = before_search; // nice try
00838 // loop detection
00839 // group transients cannot cause loops, because they're considered transient only for non-transient
00840 // windows in the group
00841     int count = 20;
00842     Window loop_pos = new_transient_for;
00843     while( loop_pos != None && loop_pos != rootWindow())
00844         {
00845         Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00846         if( pos == NULL )
00847             break;
00848         loop_pos = pos->transient_for_id;
00849         if( --count == 0 || pos == this )
00850             {
00851             kWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." ;
00852             new_transient_for = rootWindow();
00853             }
00854         }
00855     if( new_transient_for != rootWindow()
00856         && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00857         { // it's transient for a specific window, but that window is not mapped
00858         new_transient_for = rootWindow();
00859         }
00860     if( new_property_value != original_transient_for_id )
00861         XSetTransientForHint( display(), window(), new_property_value );
00862     return new_transient_for;
00863     }
00864 
00865 void Client::addTransient( Client* cl )
00866     {
00867     TRANSIENCY_CHECK( this );
00868     assert( !transients_list.contains( cl ));
00869 //    assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients()
00870     assert( cl != this );
00871     transients_list.append( cl );
00872     if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())        
00873         check_active_modal = true;
00874 //    kDebug(1212) << "ADDTRANS:" << this << ":" << cl;
00875 //    kDebug(1212) << kBacktrace();
00876 //    for( ClientList::ConstIterator it = transients_list.begin();
00877 //         it != transients_list.end();
00878 //         ++it )
00879 //        kDebug(1212) << "AT:" << (*it);
00880     }
00881 
00882 void Client::removeTransient( Client* cl )
00883     {
00884     TRANSIENCY_CHECK( this );
00885 //    kDebug(1212) << "REMOVETRANS:" << this << ":" << cl;
00886 //    kDebug(1212) << kBacktrace();
00887     transients_list.removeAll( cl );
00888     // cl is transient for this, but this is going away
00889     // make cl group transient
00890     if( cl->transientFor() == this )
00891         {
00892         cl->transient_for_id = None;
00893         cl->transient_for = NULL; // SELI
00894 // SELI       cl->setTransient( rootWindow());
00895         cl->setTransient( None );
00896         }
00897     }
00898 
00899 // A new window has been mapped. Check if it's not a mainwindow for this already existing window.
00900 void Client::checkTransient( Window w )
00901     {
00902     TRANSIENCY_CHECK( this );
00903     if( original_transient_for_id != w )
00904         return;
00905     w = verifyTransientFor( w, true );
00906     setTransient( w );
00907     }
00908 
00909 // returns true if cl is the transient_for window for this client,
00910 // or recursively the transient_for window
00911 bool Client::hasTransient( const Client* cl, bool indirect ) const
00912     {
00913     // checkGroupTransients() uses this to break loops, so hasTransient() must detect them
00914     ConstClientList set;
00915     return hasTransientInternal( cl, indirect, set );
00916     }
00917 
00918 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00919     {
00920     if( cl->transientFor() != NULL )
00921         {
00922         if( cl->transientFor() == this )
00923             return true;
00924         if( !indirect )
00925             return false;
00926         if( set.contains( cl ))
00927             return false;
00928         set.append( cl );
00929         return hasTransientInternal( cl->transientFor(), indirect, set );
00930         }
00931     if( !cl->isTransient())
00932         return false;
00933     if( group() != cl->group())
00934         return false;
00935     // cl is group transient, search from top
00936     if( transients().contains( const_cast< Client* >( cl )))
00937         return true;
00938     if( !indirect )
00939         return false;
00940     if( set.contains( this ))
00941         return false;
00942     set.append( this );
00943     for( ClientList::ConstIterator it = transients().constBegin();
00944          it != transients().constEnd();
00945          ++it )
00946         if( (*it)->hasTransientInternal( cl, indirect, set ))
00947             return true;
00948     return false;
00949     }
00950 
00951 ClientList Client::mainClients() const
00952     {
00953     if( !isTransient())
00954         return ClientList();
00955     if( transientFor() != NULL )
00956         return ClientList() << const_cast< Client* >( transientFor());
00957     ClientList result;
00958     for( ClientList::ConstIterator it = group()->members().constBegin();
00959          it != group()->members().constEnd();
00960          ++it )
00961         if((*it)->hasTransient( this, false ))
00962             result.append( *it );
00963     return result;
00964     }
00965 
00966 ClientList Client::allMainClients() const
00967     {
00968     ClientList result = mainClients();
00969     foreach( const Client* cl, result )
00970         result += cl->allMainClients();
00971     return result;
00972     }
00973 
00974 Client* Client::findModal( bool allow_itself )
00975     {
00976     for( ClientList::ConstIterator it = transients().constBegin();
00977          it != transients().constEnd();
00978          ++it )
00979         if( Client* ret = (*it)->findModal( true ))
00980             return ret;
00981     if( isModal() && allow_itself )
00982         return this;
00983     return NULL;
00984     }
00985 
00986 // Client::window_group only holds the contents of the hint,
00987 // but it should be used only to find the group, not for anything else
00988 // Argument is only when some specific group needs to be set.
00989 void Client::checkGroup( Group* set_group, bool force )
00990     {
00991     TRANSIENCY_CHECK( this );
00992     Group* old_group = in_group;
00993     if( old_group != NULL )
00994         old_group->ref(); // turn off automatic deleting
00995     if( set_group != NULL )
00996         {
00997         if( set_group != in_group )
00998             {
00999             if( in_group != NULL )
01000                 in_group->removeMember( this );
01001             in_group = set_group;
01002             in_group->addMember( this );
01003             }
01004         }
01005     else if( window_group != None )
01006         {
01007         Group* new_group = workspace()->findGroup( window_group );
01008         if( transientFor() != NULL && transientFor()->group() != new_group )
01009             { // move the window to the right group (e.g. a dialog provided
01010               // by different app, but transient for this one, so make it part of that group)
01011             new_group = transientFor()->group();
01012             }
01013         if( new_group == NULL ) // doesn't exist yet
01014             new_group = new Group( window_group, workspace());
01015         if( new_group != in_group )
01016             {
01017             if( in_group != NULL )
01018                 in_group->removeMember( this );
01019             in_group = new_group;
01020             in_group->addMember( this );
01021             }
01022         }
01023     else
01024         {
01025         if( transientFor() != NULL )
01026             { // doesn't have window group set, but is transient for something
01027           // so make it part of that group
01028             Group* new_group = transientFor()->group();
01029             if( new_group != in_group )
01030                 {
01031                 if( in_group != NULL )
01032                     in_group->removeMember( this );
01033                 in_group = transientFor()->group();
01034                 in_group->addMember( this );
01035                 }
01036             }
01037         else if( groupTransient())
01038             { // group transient which actually doesn't have a group :(
01039               // try creating group with other windows with the same client leader
01040             Group* new_group = workspace()->findClientLeaderGroup( this );
01041             if( new_group == NULL )
01042                 new_group = new Group( None, workspace());
01043             if( new_group != in_group )
01044                 {
01045                 if( in_group != NULL )
01046                     in_group->removeMember( this );
01047                 in_group = new_group;
01048                 in_group->addMember( this );
01049                 }
01050             }
01051         else // Not transient without a group, put it in its client leader group.
01052             { // This might be stupid if grouping was used for e.g. taskbar grouping
01053               // or minimizing together the whole group, but as long as its used
01054               // only for dialogs it's better to keep windows from one app in one group.
01055             Group* new_group = workspace()->findClientLeaderGroup( this );
01056             if( in_group != NULL && in_group != new_group )
01057                 {
01058                 in_group->removeMember( this );            
01059                 in_group = NULL;
01060                 }
01061             if( new_group == NULL )
01062                 new_group = new Group( None, workspace() );
01063             if( in_group != new_group )
01064                 {
01065                 in_group = new_group;
01066                 in_group->addMember( this );
01067                 }
01068             }
01069         }
01070     if( in_group != old_group || force )
01071         {
01072         for( ClientList::Iterator it = transients_list.begin();
01073              it != transients_list.end();
01074              )
01075             { // group transients in the old group are no longer transient for it
01076             if( (*it)->groupTransient() && (*it)->group() != group())
01077                 it = transients_list.erase( it );
01078             else
01079                 ++it;
01080             }
01081         if( groupTransient())
01082             {
01083             // no longer transient for ones in the old group
01084             if( old_group != NULL )
01085                 {
01086                 for( ClientList::ConstIterator it = old_group->members().constBegin();
01087                      it != old_group->members().constEnd();
01088                      ++it )
01089                     (*it)->removeTransient( this );
01090                 }
01091             // and make transient for all in the new group
01092             for( ClientList::ConstIterator it = group()->members().constBegin();
01093                  it != group()->members().constEnd();
01094                  ++it )
01095                 {
01096                 if( *it == this )
01097                     break; // this means the window is only transient for windows mapped before it
01098                 (*it)->addTransient( this );
01099                 }
01100             }
01101         // group transient splashscreens should be transient even for windows
01102         // in group mapped later
01103         for( ClientList::ConstIterator it = group()->members().constBegin();
01104              it != group()->members().constEnd();
01105              ++it )
01106             {
01107             if( !(*it)->isSplash())
01108                 continue;
01109             if( !(*it)->groupTransient())
01110                 continue;
01111             if( *it == this || hasTransient( *it, true )) // TODO indirect?
01112                 continue;
01113             addTransient( *it );
01114         }
01115         }
01116     if( old_group != NULL )
01117         old_group->deref(); // can be now deleted if empty
01118     checkGroupTransients();
01119     checkActiveModal();
01120     workspace()->updateClientLayer( this );
01121     }
01122 
01123 // used by Workspace::findClientLeaderGroup()
01124 void Client::changeClientLeaderGroup( Group* gr )
01125     {
01126     // transientFor() != NULL are in the group of their mainwindow, so keep them there
01127     if( transientFor() != NULL )
01128         return;
01129     // also don't change the group for window which have group set
01130     if( window_group )
01131         return;
01132     checkGroup( gr ); // change group
01133     }
01134 
01135 bool Client::check_active_modal = false;
01136 
01137 void Client::checkActiveModal()
01138     {
01139     // if the active window got new modal transient, activate it.
01140     // cannot be done in AddTransient(), because there may temporarily
01141     // exist loops, breaking findModal
01142     Client* check_modal = workspace()->mostRecentlyActivatedClient();
01143     if( check_modal != NULL && check_modal->check_active_modal )
01144         {
01145         Client* new_modal = check_modal->findModal();
01146         if( new_modal != NULL && new_modal != check_modal )
01147             {
01148             if( !new_modal->isManaged())
01149                 return; // postpone check until end of manage()
01150             workspace()->activateClient( new_modal );
01151             }
01152         check_modal->check_active_modal = false;
01153         }
01154     }
01155 
01156 } // namespace

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal