layers.cpp

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 You can Freely distribute this program under the GNU General Public
00009 License. See the file "COPYING" for the exact licensing terms.
00010 ******************************************************************/
00011 
00012 // SELI zmenit doc
00013 
00014 /*
00015 
00016  This file contains things relevant to stacking order and layers.
00017 
00018  Design:
00019 
00020  Normal unconstrained stacking order, as requested by the user (by clicking
00021  on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order.
00022  That list shouldn't be used at all, except for building
00023  Workspace::stacking_order. The building is done
00024  in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should
00025  be used to get the stacking order, because it also checks the stacking order
00026  is up to date.
00027  All clients are also stored in Workspace::clients (except for isDesktop() clients,
00028  as those are very special, and are stored in Workspace::desktops), in the order
00029  the clients were created.
00030 
00031  Every window has one layer assigned in which it is. There are 6 layers,
00032  from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer
00033  and ActiveLayer (see also NETWM sect.7.10.). The layer a window is in depends
00034  on the window type, and on other things like whether the window is active.
00035 
00036  NET::Splash clients belong to the Normal layer. NET::TopMenu clients
00037  belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow
00038  are in the Normal layer in order to keep the 'allow window to cover
00039  the panel' Kicker setting to work as intended (this may look like a slight
00040  spec violation, but a) I have no better idea, b) the spec allows adjusting
00041  the stacking order if the WM thinks it's a good idea . We put all
00042  NET::KeepAbove above all Docks too, even though the spec suggests putting
00043  them in the same layer.
00044 
00045  Most transients are in the same layer as their mainwindow,
00046  see Workspace::constrainedStackingOrder(), they may also be in higher layers, but
00047  they should never be below their mainwindow.
00048 
00049  When some client attribute changes (above/below flag, transiency...),
00050  Workspace::updateClientLayer() should be called in order to make
00051  sure it's moved to the appropriate layer ClientList if needed.
00052 
00053  Currently the things that affect client in which layer a client
00054  belongs: KeepAbove/Keep Below flags, window type, fullscreen
00055  state and whether the client is active, mainclient (transiency).
00056 
00057  Make sure updateStackingOrder() is called in order to make
00058  Workspace::stackingOrder() up to date and propagated to the world.
00059  Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker
00060  helper class) it's possible to temporarily disable updates
00061  and the stacking order will be updated once after it's allowed again.
00062 
00063 */
00064 
00065 #include <assert.h>
00066 
00067 #include <kdebug.h>
00068 
00069 #include "utils.h"
00070 #include "client.h"
00071 #include "workspace.h"
00072 #include "tabbox.h"
00073 #include "group.h"
00074 #include "rules.h"
00075 
00076 extern Time qt_x_time;
00077 
00078 namespace KWinInternal
00079 {
00080 
00081 //*******************************
00082 // Workspace
00083 //*******************************
00084 
00085 void Workspace::updateClientLayer( Client* c )
00086     {
00087     if( c == NULL )
00088         return;
00089     if( c->layer() == c->belongsToLayer())
00090         return;
00091     StackingUpdatesBlocker blocker( this );
00092     c->invalidateLayer(); // invalidate, will be updated when doing restacking
00093     for( ClientList::ConstIterator it = c->transients().begin();
00094          it != c->transients().end();
00095          ++it )
00096         updateClientLayer( *it );
00097     }
00098 
00099 void Workspace::updateStackingOrder( bool propagate_new_clients )
00100     {
00101     if( block_stacking_updates > 0 )
00102         {
00103         blocked_propagating_new_clients = blocked_propagating_new_clients || propagate_new_clients;
00104         return;
00105         }
00106     ClientList new_stacking_order = constrainedStackingOrder();
00107     bool changed = ( new_stacking_order != stacking_order );
00108     stacking_order = new_stacking_order;
00109 #if 0
00110     kdDebug() << "stacking:" << changed << endl;
00111     if( changed || propagate_new_clients )
00112         {
00113         for( ClientList::ConstIterator it = stacking_order.begin();
00114              it != stacking_order.end();
00115              ++it )
00116             kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl;
00117         }
00118 #endif
00119     if( changed || propagate_new_clients )
00120         {
00121         propagateClients( propagate_new_clients );
00122         if( active_client )
00123             active_client->updateMouseGrab();
00124         }
00125     }
00126 
00131 void Workspace::propagateClients( bool propagate_new_clients )
00132     {
00133     Window *cl; // MW we should not assume WId and Window to be compatible
00134                                 // when passig pointers around.
00135 
00136     // restack the windows according to the stacking order
00137     Window* new_stack = new Window[ stacking_order.count() + 2 ];
00138     int pos = 0;
00139     // Stack all windows under the support window. The support window is
00140     // not used for anything (besides the NETWM property), and it's not shown,
00141     // but it was lowered after kwin startup. Stacking all clients below
00142     // it ensures that no client will be ever shown above override-redirect
00143     // windows (e.g. popups).
00144     new_stack[ pos++ ] = supportWindow->winId();
00145     int topmenu_space_pos = 1; // not 0, that's supportWindow !!!
00146     for( ClientList::ConstIterator it = stacking_order.fromLast();
00147          it != stacking_order.end();
00148          --it )
00149         {
00150         new_stack[ pos++ ] = (*it)->frameId();
00151         if( (*it)->belongsToLayer() >= DockLayer )
00152             topmenu_space_pos = pos;
00153         }
00154     if( topmenu_space != NULL )
00155         { // make sure the topmenu space is below all topmenus, fullscreens, etc.
00156         for( int i = pos;
00157              i > topmenu_space_pos;
00158              --i )
00159             new_stack[ i ] = new_stack[ i - 1 ];
00160         new_stack[ topmenu_space_pos ] = topmenu_space->winId();
00161         ++pos;
00162         }
00163     // TODO isn't it too inefficient to restart always all clients?
00164     // TODO don't restack not visible windows?
00165     assert( new_stack[ 0 ] = supportWindow->winId());
00166     XRestackWindows(qt_xdisplay(), new_stack, pos);
00167     delete [] new_stack;
00168 
00169     if ( propagate_new_clients )
00170         {
00171         cl = new Window[ desktops.count() + clients.count()];
00172         pos = 0;
00173     // TODO this is still not completely in the map order
00174         for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it )
00175             cl[pos++] =  (*it)->window();
00176         for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it )
00177             cl[pos++] =  (*it)->window();
00178         rootInfo->setClientList( cl, pos );
00179         delete [] cl;
00180         }
00181 
00182     cl = new Window[ stacking_order.count()];
00183     pos = 0;
00184     for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
00185         cl[pos++] =  (*it)->window();
00186     rootInfo->setClientListStacking( cl, pos );
00187     delete [] cl;
00188     }
00189 
00190 
00196 // TODO misleading name for this method
00197 Client* Workspace::topClientOnDesktop( int desktop, bool unconstrained, bool only_normal ) const
00198     {
00199 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00200     ClientList::ConstIterator begin, end;
00201     if( !unconstrained )
00202         {
00203         begin = stacking_order.fromLast();
00204         end = stacking_order.end();
00205         }
00206     else
00207         {
00208         begin = unconstrained_stacking_order.fromLast();
00209         end = unconstrained_stacking_order.end();
00210         }
00211     for( ClientList::ConstIterator it = begin;
00212         it != end;
00213         --it )
00214         {
00215         if( (*it)->isOnDesktop( desktop ) && (*it)->isShown( false ))
00216             {
00217             if( !only_normal )
00218                 return *it;
00219             if( (*it)->wantsTabFocus() && !(*it)->isSpecialWindow())
00220                 return *it;
00221             }
00222         }
00223     return 0;
00224     }
00225 
00226 Client* Workspace::findDesktop( bool topmost, int desktop ) const
00227     {
00228 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00229     if( topmost )
00230         {
00231         for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it)
00232             {
00233             if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop()
00234                 && (*it)->isShown( true ))
00235                 return *it;
00236             }
00237         }
00238     else // bottom-most
00239         {
00240         for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
00241             {
00242             if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop()
00243                 && (*it)->isShown( true ))
00244                 return *it;
00245             }
00246         }
00247     return NULL;
00248     }
00249 
00250 void Workspace::raiseOrLowerClient( Client *c)
00251     {
00252     if (!c) return;
00253     Client* topmost = NULL;
00254 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00255     if ( most_recently_raised && stacking_order.contains( most_recently_raised ) &&
00256          most_recently_raised->isShown( true ) && c->isOnCurrentDesktop())
00257         topmost = most_recently_raised;
00258     else
00259         topmost = topClientOnDesktop( c->isOnAllDesktops() ? currentDesktop() : c->desktop());
00260 
00261     if( c == topmost)
00262         lowerClient(c);
00263     else
00264         raiseClient(c);
00265     }
00266 
00267 
00268 void Workspace::lowerClient( Client* c )
00269     {
00270     if ( !c )
00271         return;
00272     if( c->isTopMenu())
00273         return;
00274 
00275     c->cancelAutoRaise();
00276 
00277     StackingUpdatesBlocker blocker( this );
00278 
00279     unconstrained_stacking_order.remove( c );
00280     unconstrained_stacking_order.prepend( c );
00281     if( c->isTransient())
00282         {
00283         // lower also mainclients, in their reversed stacking order
00284         ClientList mainclients = ensureStackingOrder( c->mainClients());
00285         for( ClientList::ConstIterator it = mainclients.fromLast();
00286              it != mainclients.end();
00287              ++it )
00288             lowerClient( *it );
00289         }
00290 
00291     if ( c == most_recently_raised )
00292         most_recently_raised = 0;
00293     }
00294 
00295 void Workspace::lowerClientWithinApplication( Client* c )
00296     {
00297     if ( !c )
00298         return;
00299     if( c->isTopMenu())
00300         return;
00301 
00302     c->cancelAutoRaise();
00303 
00304     StackingUpdatesBlocker blocker( this );
00305 
00306     unconstrained_stacking_order.remove( c );
00307     bool lowered = false;
00308     // first try to put it below the bottom-most window of the application
00309     for( ClientList::Iterator it = unconstrained_stacking_order.begin();
00310          it != unconstrained_stacking_order.end();
00311          ++it )
00312         if( Client::belongToSameApplication( *it, c ))
00313             {
00314             unconstrained_stacking_order.insert( it, c );
00315             lowered = true;
00316             break;
00317             }
00318     if( !lowered )
00319         unconstrained_stacking_order.prepend( c );
00320     // ignore mainwindows
00321     }
00322 
00323 void Workspace::raiseClient( Client* c )
00324     {
00325     if ( !c )
00326         return;
00327     if( c->isTopMenu())
00328         return;
00329 
00330     c->cancelAutoRaise();
00331 
00332     StackingUpdatesBlocker blocker( this );
00333 
00334     if( c->isTransient())
00335         {
00336         ClientList mainclients = ensureStackingOrder( c->mainClients());
00337         for( ClientList::ConstIterator it = mainclients.begin();
00338              it != mainclients.end();
00339              ++it )
00340             raiseClient( *it );
00341         }
00342 
00343     unconstrained_stacking_order.remove( c );
00344     unconstrained_stacking_order.append( c );
00345 
00346     if( !c->isSpecialWindow())
00347         {
00348         most_recently_raised = c;
00349         pending_take_activity = NULL;
00350         }
00351     }
00352 
00353 void Workspace::raiseClientWithinApplication( Client* c )
00354     {
00355     if ( !c )
00356         return;
00357     if( c->isTopMenu())
00358         return;
00359 
00360     c->cancelAutoRaise();
00361 
00362     StackingUpdatesBlocker blocker( this );
00363     // ignore mainwindows
00364     
00365     // first try to put it above the top-most window of the application
00366     for( ClientList::Iterator it = unconstrained_stacking_order.fromLast();
00367          it != unconstrained_stacking_order.end();
00368          --it )
00369         {
00370         if( *it == c ) // don't lower it just because it asked to be raised
00371             return;
00372         if( Client::belongToSameApplication( *it, c ))
00373             {
00374             unconstrained_stacking_order.remove( c );
00375             ++it; // insert after the found one
00376             unconstrained_stacking_order.insert( it, c );
00377             return;
00378             }
00379         }
00380     }
00381 
00382 void Workspace::raiseClientRequest( Client* c, NET::RequestSource src, Time timestamp )
00383     {
00384     if( src == NET::FromTool || allowFullClientRaising( c, timestamp ))
00385         raiseClient( c );
00386     else
00387         {
00388         raiseClientWithinApplication( c );
00389         c->demandAttention();
00390         }
00391     }
00392 
00393 void Workspace::lowerClientRequest( Client* c, NET::RequestSource src, Time /*timestamp*/ )
00394     {
00395     // If the client has support for all this focus stealing prevention stuff,
00396     // do only lowering within the application, as that's the more logical
00397     // variant of lowering when application requests it.
00398     // No demanding of attention here of course.
00399     if( src == NET::FromTool || !c->hasUserTimeSupport())
00400         lowerClient( c );
00401     else
00402         lowerClientWithinApplication( c );
00403     }
00404 
00405 void Workspace::restackClientUnderActive( Client* c )
00406     {
00407     if( c->isTopMenu())
00408         return;
00409     if( !active_client || active_client == c )
00410         {
00411         raiseClient( c );
00412         return;
00413         }
00414 
00415     assert( unconstrained_stacking_order.contains( active_client ));
00416     if( Client::belongToSameApplication( active_client, c ))
00417         { // put it below the active window if it's the same app
00418         unconstrained_stacking_order.remove( c );
00419         unconstrained_stacking_order.insert( unconstrained_stacking_order.find( active_client ), c );
00420         }
00421     else
00422         { // put in the stacking order below _all_ windows belonging to the active application
00423         for( ClientList::Iterator it = unconstrained_stacking_order.begin();
00424              it != unconstrained_stacking_order.end();
00425              ++it )
00426             { // TODO ignore topmenus?
00427             if( Client::belongToSameApplication( active_client, *it ))
00428                 {
00429                 if( *it != c )
00430                     {
00431                     unconstrained_stacking_order.remove( c );
00432                     unconstrained_stacking_order.insert( it, c );
00433                     }
00434                 break;
00435                 }
00436             }
00437         }
00438     assert( unconstrained_stacking_order.contains( c ));
00439     for( int desktop = 1;
00440          desktop <= numberOfDesktops();
00441          ++desktop )
00442         { // do for every virtual desktop to handle the case of onalldesktop windows
00443         if( c->wantsTabFocus() && c->isOnDesktop( desktop ) && focus_chain[ desktop ].contains( active_client ))
00444             {
00445             if( Client::belongToSameApplication( active_client, c ))
00446                 { // put it after the active window if it's the same app
00447                 focus_chain[ desktop ].remove( c );
00448                 focus_chain[ desktop ].insert( focus_chain[ desktop ].find( active_client ), c );
00449                 }
00450             else
00451                 { // put it in focus_chain[currentDesktop()] after all windows belonging to the active applicationa
00452                 focus_chain[ desktop ].remove( c );
00453                 for( ClientList::Iterator it = focus_chain[ desktop ].fromLast();
00454                      it != focus_chain[ desktop ].end();
00455                      --it )
00456                     {
00457                     if( Client::belongToSameApplication( active_client, *it ))
00458                         {
00459                         focus_chain[ desktop ].insert( it, c );
00460                         break;
00461                         }
00462                     }
00463                 }
00464             }
00465         }
00466     // the same for global_focus_chain
00467     if( c->wantsTabFocus() && global_focus_chain.contains( active_client ))
00468         {
00469         if( Client::belongToSameApplication( active_client, c ))
00470             {
00471             global_focus_chain.remove( c );
00472             global_focus_chain.insert( global_focus_chain.find( active_client ), c );
00473             }
00474         else
00475             {
00476             global_focus_chain.remove( c );
00477             for( ClientList::Iterator it = global_focus_chain.fromLast();
00478                  it != global_focus_chain.end();
00479                  --it )
00480                 {
00481                 if( Client::belongToSameApplication( active_client, *it ))
00482                     {
00483                     global_focus_chain.insert( it, c );
00484                     break;
00485                     }
00486                 }
00487             }
00488         }
00489     updateStackingOrder();
00490     }
00491 
00492 void Workspace::circulateDesktopApplications()
00493     {
00494     if ( desktops.count() > 1 )
00495         {
00496         bool change_active = activeClient()->isDesktop();
00497         raiseClient( findDesktop( false, currentDesktop()));
00498         if( change_active ) // if the previously topmost Desktop was active, activate this new one
00499             activateClient( findDesktop( true, currentDesktop()));
00500         }
00501     // if there's no active client, make desktop the active one
00502     if( desktops.count() > 0 && activeClient() == NULL && should_get_focus.count() == 0 )
00503         activateClient( findDesktop( true, currentDesktop()));
00504     }
00505 
00506 
00510 ClientList Workspace::constrainedStackingOrder()
00511     {
00512     ClientList layer[ NumLayers ];
00513 
00514 #if 0
00515     kdDebug() << "stacking1:" << endl;
00516 #endif
00517     // build the order from layers
00518     QMap< Group*, Layer > minimum_layer;
00519     for( ClientList::ConstIterator it = unconstrained_stacking_order.begin();
00520          it != unconstrained_stacking_order.end();
00521          ++it )
00522         {
00523         Layer l = (*it)->layer();
00524         // If a window is raised above some other window in the same window group
00525         // which is in the ActiveLayer (i.e. it's fulscreened), make sure it stays
00526         // above that window (see #95731).
00527         if( minimum_layer.contains( (*it)->group())
00528             && minimum_layer[ (*it)->group() ] == ActiveLayer
00529             && ( l == NormalLayer || l == AboveLayer ))
00530             {
00531             l = minimum_layer[ (*it)->group() ];
00532             }
00533         minimum_layer[ (*it)->group() ] = l;
00534         layer[ l ].append( *it );
00535         }
00536     ClientList stacking;    
00537     for( Layer lay = FirstLayer;
00538          lay < NumLayers;
00539          ++lay )    
00540         stacking += layer[ lay ];
00541 #if 0
00542     kdDebug() << "stacking2:" << endl;
00543     for( ClientList::ConstIterator it = stacking.begin();
00544          it != stacking.end();
00545          ++it )
00546         kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl;
00547 #endif
00548     // now keep transients above their mainwindows
00549     // TODO this could(?) use some optimization
00550     for( ClientList::Iterator it = stacking.fromLast();
00551          it != stacking.end();
00552          )
00553         {
00554         if( !(*it)->isTransient())
00555             {
00556             --it;
00557             continue;
00558             }
00559         ClientList::Iterator it2 = stacking.end();
00560         if( (*it)->groupTransient())
00561             {
00562             if( (*it)->group()->members().count() > 0 )
00563                 { // find topmost client this one is transient for
00564                 for( it2 = stacking.fromLast();
00565                      it2 != stacking.end();
00566                      --it2 )
00567                     {
00568                     if( *it2 == *it )
00569                         {
00570                         it2 = stacking.end(); // don't reorder
00571                         break;
00572                         }
00573                     if( (*it2)->hasTransient( *it, true ) && keepTransientAbove( *it2, *it ))
00574                         break;
00575                     }
00576                 } // else it2 remains pointing at stacking.end()
00577             }
00578         else
00579             {
00580             for( it2 = stacking.fromLast();
00581                  it2 != stacking.end();
00582                  --it2 )
00583                 {
00584                 if( *it2 == *it )
00585                     {
00586                     it2 = stacking.end(); // don't reorder
00587                     break;
00588                     }
00589                 if( *it2 == (*it)->transientFor() && keepTransientAbove( *it2, *it ))
00590                     break;
00591                 }
00592             }
00593 //        kdDebug() << "STACK:" << (*it) << ":" << ( it2 == stacking.end() ? ((Client*)0) : (*it2)) << endl;
00594         if( it2 == stacking.end())
00595             {
00596             --it;
00597             continue;
00598             }
00599         Client* current = *it;
00600         ClientList::Iterator remove_it = it;
00601         --it;
00602         stacking.remove( remove_it );
00603         if( !current->transients().isEmpty())  // this one now can be possibly above its transients,
00604             it = it2; // so go again higher in the stack order and possibly move those transients again
00605         ++it2; // insert after the mainwindow, it's ok if it2 is now stacking.end()
00606         stacking.insert( it2, current );
00607         }
00608 #if 0
00609     kdDebug() << "stacking3:" << endl;
00610     for( ClientList::ConstIterator it = stacking.begin();
00611          it != stacking.end();
00612          ++it )
00613         kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl;
00614     kdDebug() << "\n\n" << endl;
00615 #endif
00616     return stacking;
00617     }
00618 
00619 void Workspace::blockStackingUpdates( bool block )
00620     {
00621     if( block )
00622         {
00623         if( block_stacking_updates == 0 )
00624             blocked_propagating_new_clients = false;
00625         ++block_stacking_updates;
00626         }
00627     else // !block
00628         if( --block_stacking_updates == 0 )
00629             updateStackingOrder( blocked_propagating_new_clients );
00630     }
00631 
00632 // Ensure list is in stacking order
00633 ClientList Workspace::ensureStackingOrder( const ClientList& list ) const
00634     {
00635 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00636     if( list.count() < 2 )
00637         return list;
00638     // TODO is this worth optimizing?
00639     ClientList result = list;
00640     for( ClientList::ConstIterator it = stacking_order.begin();
00641          it != stacking_order.end();
00642          ++it )
00643         if( result.remove( *it ) != 0 )
00644             result.append( *it );
00645     return result;
00646     }
00647 
00648 // check whether a transient should be actually kept above its mainwindow
00649 // there may be some special cases where this rule shouldn't be enfored
00650 bool Workspace::keepTransientAbove( const Client* mainwindow, const Client* transient )
00651     {
00652     // When topmenu's mainwindow becomes active, topmenu is raised and shown.
00653     // They also belong to the Dock layer. This makes them to be very high.
00654     // Therefore don't keep group transients above them, otherwise this would move
00655     // group transients way too high.
00656     if( mainwindow->isTopMenu() && transient->groupTransient())
00657         return false;
00658     // #93832 - don't keep splashscreens above dialogs
00659     if( transient->isSplash() && mainwindow->isDialog())
00660         return false;
00661     // This is rather a hack for #76026. Don't keep non-modal dialogs above
00662     // the mainwindow, but only if they're group transient (since only such dialogs
00663     // have taskbar entry in Kicker). A proper way of doing this (both kwin and kicker)
00664     // needs to be found.
00665     if( transient->isDialog() && !transient->isModal() && transient->groupTransient())
00666         return false;
00667     // #63223 - don't keep transients above docks, because the dock is kept high,
00668     // and e.g. dialogs for them would be too high too
00669     if( mainwindow->isDock())
00670         return false;
00671     return true;
00672     }
00673 
00674 //*******************************
00675 // Client
00676 //*******************************
00677 
00678 void Client::restackWindow( Window /*above TODO */, int detail, NET::RequestSource src, Time timestamp, bool send_event )
00679     {
00680     switch ( detail )
00681         {
00682         case Above:
00683         case TopIf:
00684             workspace()->raiseClientRequest( this, src, timestamp );
00685           break;
00686         case Below:
00687         case BottomIf:
00688             workspace()->lowerClientRequest( this, src, timestamp );
00689           break;
00690         case Opposite:
00691         default:
00692             break;
00693         }
00694     if( send_event )
00695         sendSyntheticConfigureNotify();
00696     }
00697     
00698 void Client::setKeepAbove( bool b )
00699     {
00700     b = rules()->checkKeepAbove( b );
00701     if( b && !rules()->checkKeepBelow( false ))
00702         setKeepBelow( false );
00703     if ( b == keepAbove())
00704         { // force hint change if different
00705         if( bool( info->state() & NET::KeepAbove ) != keepAbove())
00706             info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove );
00707         return;
00708         }
00709     keep_above = b;
00710     info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove );
00711     if( decoration != NULL )
00712         decoration->emitKeepAboveChanged( keepAbove());
00713     workspace()->updateClientLayer( this );
00714     updateWindowRules();
00715     }
00716 
00717 void Client::setKeepBelow( bool b )
00718     {
00719     b = rules()->checkKeepBelow( b );
00720     if( b && !rules()->checkKeepAbove( false ))
00721         setKeepAbove( false );
00722     if ( b == keepBelow())
00723         { // force hint change if different
00724         if( bool( info->state() & NET::KeepBelow ) != keepBelow())
00725             info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow );
00726         return;
00727         }
00728     keep_below = b;
00729     info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow );
00730     if( decoration != NULL )
00731         decoration->emitKeepBelowChanged( keepBelow());
00732     workspace()->updateClientLayer( this );
00733     updateWindowRules();
00734     }
00735 
00736 Layer Client::layer() const
00737     {
00738     if( in_layer == UnknownLayer )
00739         const_cast< Client* >( this )->in_layer = belongsToLayer();
00740     return in_layer;
00741     }
00742 
00743 Layer Client::belongsToLayer() const
00744     {
00745     if( isDesktop())
00746         return DesktopLayer;
00747     if( isSplash())         // no damn annoying splashscreens
00748         return NormalLayer; // getting in the way of everything else
00749     if( isDock() && keepBelow())
00750         // slight hack for the 'allow window to cover panel' Kicker setting
00751         // don't move keepbelow docks below normal window, but only to the same
00752         // layer, so that both may be raised to cover the other
00753         return NormalLayer;
00754     if( keepBelow())
00755         return BelowLayer;
00756     if( isDock() && !keepBelow())
00757         return DockLayer;
00758     if( isTopMenu())
00759         return DockLayer;
00760     // only raise fullscreen above docks if it's the topmost window in unconstrained stacking order,
00761     // i.e. the window set to be topmost by the user (also includes transients of the fullscreen window)
00762     const Client* ac = workspace()->mostRecentlyActivatedClient(); // instead of activeClient() - avoids flicker
00763     const Client* top = workspace()->topClientOnDesktop( desktop(), true );
00764     if( isFullScreen() && ac != NULL && top != NULL
00765         && ( ac == this || this->group() == ac->group())
00766         && ( top == this || this->group() == top->group()))
00767         return ActiveLayer;
00768     if( keepAbove())
00769         return AboveLayer;
00770     return NormalLayer;
00771     }
00772 
00773 } // namespace
KDE Home | KDE Accessibility Home | Description of Access Keys