kwin Library API Documentation

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 "utils.h" 00068 #include "client.h" 00069 #include "workspace.h" 00070 #include "tabbox.h" 00071 #include "popupinfo.h" 00072 #include "group.h" 00073 #include <kdebug.h> 00074 00075 namespace KWinInternal 00076 { 00077 00078 //******************************* 00079 // Workspace 00080 //******************************* 00081 00082 void Workspace::updateClientLayer( Client* c ) 00083 { 00084 if( c == NULL ) 00085 return; 00086 if( c->layer() == c->belongsToLayer()) 00087 return; 00088 StackingUpdatesBlocker blocker( this ); 00089 c->invalidateLayer(); // invalidate, will be updated when doing restacking 00090 for( ClientList::ConstIterator it = c->transients().begin(); 00091 it != c->transients().end(); 00092 ++it ) 00093 updateClientLayer( *it ); 00094 } 00095 00096 void Workspace::updateStackingOrder( bool propagate_new_clients ) 00097 { 00098 if( block_stacking_updates > 0 ) 00099 { 00100 blocked_propagating_new_clients |= propagate_new_clients; 00101 return; 00102 } 00103 ClientList new_stacking_order = constrainedStackingOrder(); 00104 bool changed = ( new_stacking_order != stacking_order ); 00105 stacking_order = new_stacking_order; 00106 #if 0 00107 kdDebug() << "stacking:" << changed << endl; 00108 if( changed || propagate_new_clients ) 00109 { 00110 for( ClientList::ConstIterator it = stacking_order.begin(); 00111 it != stacking_order.end(); 00112 ++it ) 00113 kdDebug() << (void*)(*it) << *it << endl; 00114 } 00115 #endif 00116 if( changed || propagate_new_clients ) 00117 propagateClients( propagate_new_clients ); 00118 } 00119 00124 void Workspace::propagateClients( bool propagate_new_clients ) 00125 { 00126 Window *cl; // MW we should not assume WId and Window to be compatible 00127 // when passig pointers around. 00128 00129 // restack the windows according to the stacking order 00130 Window* new_stack = new Window[ stacking_order.count() + 2 ]; 00131 int pos = 0; 00132 // Stack all windows under the support window. The support window is 00133 // not used for anything (besides the NETWM property), and it's not shown, 00134 // but it was lowered after kwin startup. Stacking all clients below 00135 // it ensures that no client will be ever shown above override-redirect 00136 // windows (e.g. popups). 00137 new_stack[ pos++ ] = supportWindow->winId(); 00138 int topmenu_space_pos = 1; // not 0, that's supportWindow !!! 00139 for( ClientList::ConstIterator it = stacking_order.fromLast(); 00140 it != stacking_order.end(); 00141 --it ) 00142 { 00143 new_stack[ pos++ ] = (*it)->frameId(); 00144 if( (*it)->isTopMenu()) 00145 topmenu_space_pos = pos; 00146 } 00147 if( topmenu_space != NULL ) 00148 { // make sure the topmenu space is below all topmenus, if there are any 00149 for( int i = pos; 00150 i > topmenu_space_pos; 00151 --i ) 00152 new_stack[ i ] = new_stack[ i - 1 ]; 00153 new_stack[ topmenu_space_pos ] = topmenu_space->winId(); 00154 ++pos; 00155 } 00156 // TODO isn't it too inefficient to restart always all clients? 00157 // TODO don't restack not visible windows? 00158 assert( new_stack[ 0 ] = supportWindow->winId()); 00159 XRestackWindows(qt_xdisplay(), new_stack, pos); 00160 delete [] new_stack; 00161 00162 if ( propagate_new_clients ) 00163 { 00164 cl = new Window[ desktops.count() + clients.count()]; 00165 pos = 0; 00166 // TODO this is still not completely in the map order 00167 for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it ) 00168 cl[pos++] = (*it)->window(); 00169 for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it ) 00170 cl[pos++] = (*it)->window(); 00171 rootInfo->setClientList( cl, pos ); 00172 delete [] cl; 00173 } 00174 00175 cl = new Window[ stacking_order.count()]; 00176 pos = 0; 00177 for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) 00178 cl[pos++] = (*it)->window(); 00179 rootInfo->setClientListStacking( cl, pos ); 00180 delete [] cl; 00181 00182 #if 0 // not necessary anymore? 00183 if ( tab_box->isVisible() ) 00184 tab_box->raise(); 00185 00186 if ( popupinfo->isVisible() ) 00187 popupinfo->raise(); 00188 00189 raiseElectricBorders(); 00190 #endif 00191 } 00192 00193 00199 // TODO misleading name for this method 00200 Client* Workspace::topClientOnDesktop( int desktop, bool unconstrained ) const 00201 { 00202 // TODO Q_ASSERT( block_stacking_updates == 0 ); 00203 ClientList::ConstIterator begin, end; 00204 if( !unconstrained ) 00205 { 00206 begin = stacking_order.fromLast(); 00207 end = stacking_order.end(); 00208 } 00209 else 00210 { 00211 begin = unconstrained_stacking_order.fromLast(); 00212 end = unconstrained_stacking_order.end(); 00213 } 00214 for( ClientList::ConstIterator it = begin; 00215 it != end; 00216 --it ) 00217 { 00218 if ( (*it)->isOnDesktop( desktop ) && !(*it)->isSpecialWindow() 00219 && (*it)->isShown( false ) && (*it)->wantsTabFocus()) 00220 return *it; 00221 } 00222 return 0; 00223 } 00224 00225 Client* Workspace::findDesktop( bool topmost, int desktop ) const 00226 { 00227 // TODO Q_ASSERT( block_stacking_updates == 0 ); 00228 if( topmost ) 00229 { 00230 for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) 00231 { 00232 if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop() 00233 && (*it)->isShown( true )) 00234 return *it; 00235 } 00236 } 00237 else // bottom-most 00238 { 00239 for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) 00240 { 00241 if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop() 00242 && (*it)->isShown( true )) 00243 return *it; 00244 } 00245 } 00246 return NULL; 00247 } 00248 00249 void Workspace::raiseOrLowerClient( Client *c) 00250 { 00251 if (!c) return; 00252 Client* topmost = NULL; 00253 // TODO Q_ASSERT( block_stacking_updates == 0 ); 00254 if ( most_recently_raised && stacking_order.contains( most_recently_raised ) && 00255 most_recently_raised->isShown( true ) && c->isOnCurrentDesktop()) 00256 topmost = most_recently_raised; 00257 else 00258 topmost = topClientOnDesktop( c->isOnAllDesktops() ? currentDesktop() : c->desktop()); 00259 00260 if( c == topmost) 00261 lowerClient(c); 00262 else 00263 raiseClient(c); 00264 } 00265 00266 00267 void Workspace::lowerClient( Client* c ) 00268 { 00269 if ( !c ) 00270 return; 00271 00272 c->cancelAutoRaise(); 00273 00274 StackingUpdatesBlocker blocker( this ); 00275 00276 unconstrained_stacking_order.remove( c ); 00277 unconstrained_stacking_order.prepend( c ); 00278 if( c->isTransient()) 00279 { 00280 // lower also mainclients, in their reversed stacking order 00281 ClientList mainclients = ensureStackingOrder( c->mainClients()); 00282 for( ClientList::ConstIterator it = mainclients.fromLast(); 00283 it != mainclients.end(); 00284 ++it ) 00285 lowerClient( *it ); 00286 } 00287 00288 if ( c == most_recently_raised ) 00289 most_recently_raised = 0; 00290 } 00291 00292 void Workspace::lowerClientWithinApplication( Client* c ) 00293 { 00294 if ( !c ) 00295 return; 00296 00297 c->cancelAutoRaise(); 00298 00299 StackingUpdatesBlocker blocker( this ); 00300 00301 unconstrained_stacking_order.remove( c ); 00302 bool lowered = false; 00303 // first try to put it below the bottom-most window of the application 00304 for( ClientList::Iterator it = unconstrained_stacking_order.begin(); 00305 it != unconstrained_stacking_order.end(); 00306 ++it ) 00307 if( Client::belongToSameApplication( *it, c )) 00308 { 00309 unconstrained_stacking_order.insert( it, c ); 00310 lowered = true; 00311 break; 00312 } 00313 if( !lowered ) 00314 unconstrained_stacking_order.prepend( c ); 00315 // ignore mainwindows 00316 } 00317 00318 void Workspace::raiseClient( Client* c ) 00319 { 00320 if ( !c ) 00321 return; 00322 00323 c->cancelAutoRaise(); 00324 00325 StackingUpdatesBlocker blocker( this ); 00326 00327 if( c->isTransient()) 00328 { 00329 ClientList mainclients = ensureStackingOrder( c->mainClients()); 00330 for( ClientList::ConstIterator it = mainclients.begin(); 00331 it != mainclients.end(); 00332 ++it ) 00333 raiseClient( *it ); 00334 } 00335 00336 unconstrained_stacking_order.remove( c ); 00337 unconstrained_stacking_order.append( c ); 00338 00339 if( !c->isSpecialWindow()) 00340 most_recently_raised = c; 00341 } 00342 00343 void Workspace::raiseClientWithinApplication( Client* c ) 00344 { 00345 if ( !c ) 00346 return; 00347 00348 c->cancelAutoRaise(); 00349 00350 StackingUpdatesBlocker blocker( this ); 00351 // ignore mainwindows 00352 00353 // first try to put it above the top-most window of the application 00354 for( ClientList::Iterator it = unconstrained_stacking_order.fromLast(); 00355 it != unconstrained_stacking_order.end(); 00356 --it ) 00357 { 00358 if( *it == c ) // don't lower it just because it asked to be raised 00359 return; 00360 if( Client::belongToSameApplication( *it, c )) 00361 { 00362 unconstrained_stacking_order.remove( c ); 00363 ++it; // insert after the found one 00364 unconstrained_stacking_order.insert( it, c ); 00365 return; 00366 } 00367 } 00368 } 00369 00370 void Workspace::raiseClientRequest( Client* c ) 00371 { 00372 if( allowFullClientRaising( c )) 00373 raiseClient( c ); 00374 else 00375 { 00376 raiseClientWithinApplication( c ); 00377 c->demandAttention(); 00378 } 00379 } 00380 00381 void Workspace::lowerClientRequest( Client* c ) 00382 { 00383 // If the client has support for all this focus stealing prevention stuff, 00384 // do only lowering within the application, as that's the more logical 00385 // variant of lowering when application requests it. 00386 // No demanding of attention here of course. 00387 if( c->hasUserTimeSupport()) 00388 lowerClientWithinApplication( c ); 00389 else 00390 lowerClient( c ); 00391 } 00392 00393 void Workspace::restackClientUnderActive( Client* c ) 00394 { 00395 if( !active_client || active_client == c ) 00396 { 00397 raiseClient( c ); 00398 return; 00399 } 00400 00401 // put in the stacking order below _all_ windows belonging to the active application 00402 assert( unconstrained_stacking_order.contains( active_client )); 00403 for( ClientList::Iterator it = unconstrained_stacking_order.begin(); 00404 it != unconstrained_stacking_order.end(); 00405 ++it ) 00406 { // TODO ignore topmenus? 00407 if( Client::belongToSameApplication( active_client, *it )) 00408 { 00409 if( *it != c ) 00410 { 00411 unconstrained_stacking_order.remove( c ); 00412 unconstrained_stacking_order.insert( it, c ); 00413 } 00414 break; 00415 } 00416 } 00417 assert( unconstrained_stacking_order.contains( c )); 00418 if( c->wantsTabFocus() && focus_chain.contains( active_client )) 00419 { 00420 // also put in focus_chain after all windows belonging to the active application 00421 focus_chain.remove( c ); 00422 for( ClientList::Iterator it = focus_chain.fromLast(); 00423 it != focus_chain.end(); 00424 --it ) 00425 { 00426 if( Client::belongToSameApplication( active_client, *it )) 00427 { 00428 focus_chain.insert( it, c ); 00429 break; 00430 } 00431 } 00432 } 00433 updateStackingOrder(); 00434 } 00435 00436 void Workspace::circulateDesktopApplications() 00437 { 00438 if ( desktops.count() > 1 ) 00439 { 00440 bool change_active = activeClient()->isDesktop(); 00441 raiseClient( findDesktop( false, currentDesktop())); 00442 if( change_active ) // if the previously topmost Desktop was active, activate this new one 00443 activateClient( findDesktop( true, currentDesktop())); 00444 } 00445 // if there's no active client, make desktop the active one 00446 if( desktops.count() > 0 && activeClient() == NULL && should_get_focus.count() == 0 ) 00447 activateClient( findDesktop( true, currentDesktop())); 00448 } 00449 00450 00454 ClientList Workspace::constrainedStackingOrder() 00455 { 00456 ClientList layer[ NumLayers ]; 00457 00458 #if 0 00459 kdDebug() << "stacking1:" << endl; 00460 #endif 00461 // build the order from layers 00462 for( ClientList::ConstIterator it = unconstrained_stacking_order.begin(); 00463 it != unconstrained_stacking_order.end(); 00464 ++it ) 00465 { 00466 #if 0 00467 kdDebug() << (void*)(*it) << *it << endl; 00468 #endif 00469 layer[ (*it)->layer() ].append( *it ); 00470 } 00471 ClientList stacking; 00472 for( Layer lay = FirstLayer; 00473 lay < NumLayers; 00474 ++lay ) 00475 stacking += layer[ lay ]; 00476 #if 0 00477 kdDebug() << "stacking2:" << endl; 00478 for( ClientList::ConstIterator it = stacking.begin(); 00479 it != stacking.end(); 00480 ++it ) 00481 kdDebug() << (void*)(*it) << *it << endl; 00482 #endif 00483 // now keep transients above their mainwindows 00484 // TODO this could(?) use some optimization 00485 for( ClientList::Iterator it = stacking.fromLast(); 00486 it != stacking.end(); 00487 ) 00488 { 00489 if( !(*it)->isTransient()) 00490 { 00491 --it; 00492 continue; 00493 } 00494 ClientList::Iterator it2 = stacking.end(); 00495 if( (*it)->groupTransient()) 00496 { 00497 if( (*it)->group()->members().count() > 0 ) 00498 { // find topmost client this one is transient for 00499 for( it2 = stacking.fromLast(); 00500 it2 != stacking.end(); 00501 --it2 ) 00502 { 00503 if( *it2 == *it ) 00504 { 00505 it2 = stacking.end(); // don't reorder 00506 break; 00507 } 00508 if( (*it2)->hasTransient( *it, true ) && keepTransientAbove( *it2, *it )) 00509 break; 00510 } 00511 } // else it2 remains pointing at stacking.end() 00512 } 00513 else 00514 { 00515 for( it2 = stacking.fromLast(); 00516 it2 != stacking.end(); 00517 --it2 ) 00518 { 00519 if( *it2 == *it ) 00520 { 00521 it2 = stacking.end(); // don't reorder 00522 break; 00523 } 00524 if( *it2 == (*it)->transientFor() && keepTransientAbove( *it2, *it )) 00525 break; 00526 } 00527 } 00528 // kdDebug() << "STACK:" << (*it) << ":" << ( it2 == stacking.end() ? ((Client*)0) : (*it2)) << endl; 00529 if( it2 == stacking.end()) 00530 { 00531 --it; 00532 continue; 00533 } 00534 Client* current = *it; 00535 ClientList::Iterator remove_it = it; 00536 --it; 00537 stacking.remove( remove_it ); 00538 if( !current->transients().isEmpty()) // this one now can be possibly above its transients, 00539 it = it2; // so go again higher in the stack order and possibly move those transients again 00540 ++it2; // insert after the mainwindow, it's ok if it2 is now stacking.end() 00541 stacking.insert( it2, current ); 00542 } 00543 #if 0 00544 kdDebug() << "stacking3:" << endl; 00545 for( ClientList::ConstIterator it = stacking.begin(); 00546 it != stacking.end(); 00547 ++it ) 00548 kdDebug() << (void*)(*it) << *it << endl; 00549 kdDebug() << "\n\n" << endl; 00550 #endif 00551 return stacking; 00552 } 00553 00554 void Workspace::blockStackingUpdates( bool block ) 00555 { 00556 if( block ) 00557 { 00558 if( block_stacking_updates == 0 ) 00559 blocked_propagating_new_clients = false; 00560 ++block_stacking_updates; 00561 } 00562 else // !block 00563 if( --block_stacking_updates == 0 ) 00564 updateStackingOrder( blocked_propagating_new_clients ); 00565 } 00566 00567 // Ensure list is in stacking order 00568 ClientList Workspace::ensureStackingOrder( const ClientList& list ) const 00569 { 00570 // TODO Q_ASSERT( block_stacking_updates == 0 ); 00571 if( list.count() < 2 ) 00572 return list; 00573 // TODO is this worth optimizing? 00574 ClientList result = list; 00575 for( ClientList::ConstIterator it = stacking_order.begin(); 00576 it != stacking_order.end(); 00577 ++it ) 00578 if( result.remove( *it ) != 0 ) 00579 result.append( *it ); 00580 return result; 00581 } 00582 00583 // check whether a transient should be actually kept above its mainwindow 00584 // there may be some special cases where this rule shouldn't be enfored 00585 bool Workspace::keepTransientAbove( const Client* mainwindow, const Client* transient ) 00586 { 00587 // When topmenu's mainwindow becomes active, topmenu is raised and shown. 00588 // They also belong to the Dock layer. This makes them to be very high. 00589 // Therefore don't keep group transients above them, otherwise this would move 00590 // group transients way too high. 00591 if( mainwindow->isTopMenu() && transient->groupTransient()) 00592 return false; 00593 return true; 00594 // #63223 - don't keep transients above docks, because the dock is kept high, 00595 // and e.g. dialogs for them would be too high too 00596 // TODO this doesn't really work - the transient should be raised after clicking 00597 // on the dock, but docks don't become active after clicking them 00598 if( mainwindow->isDock() && !mainwindow->keepBelow() 00599 && !mainwindow->isActive() && !transient->isActive()) // TODO !w->group()->isActive() ??? 00600 return false; 00601 return true; 00602 } 00603 00604 //******************************* 00605 // Client 00606 //******************************* 00607 00608 void Client::restackWindow( Window /*above TODO */, int detail, NET::RequestSource source, bool send_event ) 00609 { 00610 switch ( detail ) 00611 { 00612 case Above: 00613 case TopIf: 00614 if( source == NET::FromTool ) 00615 workspace()->raiseClient( this ); 00616 else 00617 workspace()->raiseClientRequest( this ); 00618 break; 00619 case Below: 00620 case BottomIf: 00621 if( source == NET::FromTool ) 00622 workspace()->lowerClient( this ); 00623 else 00624 workspace()->lowerClientRequest( this ); 00625 break; 00626 case Opposite: 00627 default: 00628 break; 00629 } 00630 if( send_event ) 00631 sendSyntheticConfigureNotify(); 00632 } 00633 00634 void Client::setKeepAbove( bool b ) 00635 { 00636 if ( b == keepAbove() ) 00637 return; 00638 setKeepBelow( false ); 00639 keep_above = b; 00640 info->setState( b ? NET::KeepAbove : 0, NET::KeepAbove ); 00641 // TODO emit a signal about the change to the style plugin 00642 workspace()->updateClientLayer( this ); 00643 } 00644 00645 void Client::setKeepBelow( bool b ) 00646 { 00647 if ( b == keepBelow() ) 00648 return; 00649 setKeepAbove( false ); 00650 keep_below = b; 00651 info->setState( b ? NET::KeepBelow : 0, NET::KeepBelow ); 00652 workspace()->updateClientLayer( this ); 00653 } 00654 00655 Layer Client::layer() const 00656 { 00657 if( in_layer == UnknownLayer ) 00658 const_cast< Client* >( this )->in_layer = belongsToLayer(); 00659 return in_layer; 00660 } 00661 00662 Layer Client::belongsToLayer() const 00663 { 00664 if( isDesktop()) 00665 return DesktopLayer; 00666 if( isSplash()) // no damn annoying splashscreens 00667 return NormalLayer; // getting in the way of everything else 00668 if( isDock() && keepBelow()) 00669 // slight hack for the 'allow window to cover panel' Kicker setting 00670 // don't move keepbelow docks below normal window, but only to the same 00671 // layer, so that both may be raised to cover the other 00672 return NormalLayer; 00673 if( keepBelow()) 00674 return BelowLayer; 00675 if( isDock() && !keepBelow()) 00676 return DockLayer; 00677 if( isTopMenu()) 00678 return DockLayer; 00679 // only raise fullscreen above docks if it's the topmost window in unconstrained stacking order, 00680 // i.e. the window set to be topmost by the user 00681 bool raise_special_active_windows = ( workspace()->topClientOnDesktop( desktop(), true ) == this ); 00682 if( keepAbove()) 00683 return AboveLayer; 00684 if( isFullScreen() && workspace()->activeClient() != NULL 00685 && ( workspace()->activeClient() == this || this->hasTransient( workspace()->activeClient(), true )) 00686 && raise_special_active_windows ) 00687 return ActiveLayer; 00688 return NormalLayer; 00689 } 00690 00691 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Dec 16 19:08:41 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003