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