kwin Library API Documentation

activation.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 /* 00013 00014 This file contains things relevant to window activation and focus 00015 stealing prevention. 00016 00017 */ 00018 00019 #include "client.h" 00020 #include "workspace.h" 00021 00022 #include <fixx11h.h> 00023 #include <qpopupmenu.h> 00024 #include <kxerrorhandler.h> 00025 #include <kstartupinfo.h> 00026 00027 #include "notifications.h" 00028 #include "atoms.h" 00029 #include "group.h" 00030 #include "rules.h" 00031 00032 extern Time qt_x_time; 00033 00034 namespace KWinInternal 00035 { 00036 00037 /* 00038 Prevention of focus stealing: 00039 00040 KWin tries to prevent unwanted changes of focus, that would result 00041 from mapping a new window. Also, some nasty applications may try 00042 to force focus change even in cases when ICCCM 4.2.7 doesn't allow it 00043 (e.g. they may try to activate their main window because the user 00044 definitely "needs" to see something happened - misusing 00045 of QWidget::setActiveWindow() may be such case). 00046 00047 There are 4 ways how a window may become active: 00048 - the user changes the active window (e.g. focus follows mouse, clicking 00049 on some window's titlebar) - the change of focus will 00050 be done by KWin, so there's nothing to solve in this case 00051 - the change of active window will be requested using the _NET_ACTIVE_WINDOW 00052 message (handled in RootInfo::changeActiveWindow()) - such requests 00053 will be obeyed, because this request is meant mainly for e.g. taskbar 00054 asking the WM to change the active window as a result of some user action. 00055 Normal applications should use this request only rarely in special cases. 00056 See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER. 00057 - the change of active window will be done by performing XSetInputFocus() 00058 on a window that's not currently active. ICCCM 4.2.7 describes when 00059 the application may perform change of input focus. In order to handle 00060 misbehaving applications, KWin will try to detect focus changes to 00061 windows that don't belong to currently active application, and restore 00062 focus back to the currently active window, instead of activating the window 00063 that got focus (unfortunately there's no way to FocusChangeRedirect similar 00064 to e.g. SubstructureRedirect, so there will be short time when the focus 00065 will be changed). The check itself that's done is 00066 Workspace::allowClientActivation() (see below). 00067 - a new window will be mapped - this is the most complicated case. If 00068 the new window belongs to the currently active application, it may be safely 00069 mapped on top and activated. The same if there's no active window, 00070 or the active window is the desktop. These checks are done by 00071 Workspace::allowClientActivation(). 00072 Following checks need to compare times. One time is the timestamp 00073 of last user action in the currently active window, the other time is 00074 the timestamp of the action that originally caused mapping of the new window 00075 (e.g. when the application was started). If the first time is newer than 00076 the second one, the window will not be activated, as that indicates 00077 futher user actions took place after the action leading to this new 00078 mapped window. This check is done by Workspace::allowClientActivation(). 00079 There are several ways how to get the timestamp of action that caused 00080 the new mapped window (done in Client::readUserTimeMapTimestamp()) : 00081 - the window may have the _NET_WM_USER_TIME property. This way 00082 the application may either explicitly request that the window is not 00083 activated (by using 0 timestamp), or the property contains the time 00084 of last user action in the application. 00085 - KWin itself tries to detect time of last user action in every window, 00086 by watching KeyPress and ButtonPress events on windows. This way some 00087 events may be missed (if they don't propagate to the toplevel window), 00088 but it's good as a fallback for applications that don't provide 00089 _NET_WM_USER_TIME, and missing some events may at most lead 00090 to unwanted focus stealing. 00091 - the timestamp may come from application startup notification. 00092 Application startup notification, if it exists for the new mapped window, 00093 should include time of the user action that caused it. 00094 - if there's no timestamp available, it's checked whether the new window 00095 belongs to some already running application - if yes, the timestamp 00096 will be 0 (i.e. refuse activation) 00097 - if the window is from session restored window, the timestamp will 00098 be 0 too, unless this application was the active one at the time 00099 when the session was saved, in which case the window will be 00100 activated if there wasn't any user interaction since the time 00101 KWin was started. 00102 - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp 00103 is used. For every toplevel window that is created (see CreateNotify 00104 handling), this property is set to the at that time current time. 00105 Since at this time it's known that the new window doesn't belong 00106 to any existing application (better said, the application doesn't 00107 have any other window mapped), it is either the very first window 00108 of the application, or its the only window of the application 00109 that was hidden before. The latter case is handled by removing 00110 the property from windows before withdrawing them, making 00111 the timestamp empty for next mapping of the window. In the sooner 00112 case, the timestamp will be used. This helps in case when 00113 an application is launched without application startup notification, 00114 it creates its mainwindow, and starts its initialization (that 00115 may possibly take long time). The timestamp used will be older 00116 than any user action done after launching this application. 00117 - if no timestamp is found at all, the window is activated. 00118 The check whether two windows belong to the same application (same 00119 process) is done in Client::belongToSameApplication(). Not 100% reliable, 00120 but hopefully 99,99% reliable. 00121 00122 As a somewhat special case, window activation is always enabled when 00123 session saving is in progress. When session saving, the session 00124 manager allows only one application to interact with the user. 00125 Not allowing window activation in such case would result in e.g. dialogs 00126 not becoming active, so focus stealing prevention would cause here 00127 more harm than good. 00128 00129 Windows that attempted to become active but KWin prevented this will 00130 be marked as demanding user attention. They'll get 00131 the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark 00132 them specially (blink, etc.). The state will be reset when the window 00133 eventually really becomes active. 00134 00135 There are one more ways how a window can become obstrusive, window stealing 00136 focus: By showing above the active window, by either raising itself, 00137 or by moving itself on the active desktop. 00138 - KWin will refuse raising non-active window above the active one, 00139 unless they belong to the same application. Applications shouldn't 00140 raise their windows anyway (unless the app wants to raise one 00141 of its windows above another of its windows). 00142 - KWin activates windows moved to the current desktop (as that seems 00143 logical from the user's point of view, after sending the window 00144 there directly from KWin, or e.g. using pager). This means 00145 applications shouldn't send their windows to another desktop 00146 (SELI TODO - but what if they do?) 00147 00148 Special cases I can think of: 00149 - konqueror reusing, i.e. kfmclient tells running Konqueror instance 00150 to open new window 00151 - without focus stealing prevention - no problem 00152 - with ASN (application startup notification) - ASN is forwarded, 00153 and because it's newer than the instance's user timestamp, 00154 it takes precedence 00155 - without ASN - user timestamp needs to be reset, otherwise it would 00156 be used, and it's old; moreover this new window mustn't be detected 00157 as window belonging to already running application, or it wouldn't 00158 be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly) 00159 hack 00160 - konqueror preloading, i.e. window is created in advance, and kfmclient 00161 tells this Konqueror instance to show it later 00162 - without focus stealing prevention - no problem 00163 - with ASN - ASN is forwarded, and because it's newer than the instance's 00164 user timestamp, it takes precedence 00165 - without ASN - user timestamp needs to be reset, otherwise it would 00166 be used, and it's old; also, creation timestamp is changed to 00167 the time the instance starts (re-)initializing the window, 00168 this ensures creation timestamp will still work somewhat even in this case 00169 - KUniqueApplication - when the window is already visible, and the new instance 00170 wants it to activate 00171 - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem 00172 - with ASN - ASN is forwarded, and set on the already visible window, KWin 00173 treats the window as new with that ASN 00174 - without ASN - _NET_ACTIVE_WINDOW as application request is used, 00175 and there's no really usable timestamp, only timestamp 00176 from the time the (new) application instance was started, 00177 so KWin will activate the window *sigh* 00178 - the bad thing here is that there's absolutely no chance to recognize 00179 the case of starting this KUniqueApp from Konsole (and thus wanting 00180 the already visible window to become active) from the case 00181 when something started this KUniqueApp without ASN (in which case 00182 the already visible window shouldn't become active) 00183 - the only solution is using ASN for starting applications, at least silent 00184 (i.e. without feedback) 00185 - when one application wants to activate another application's window (e.g. KMail 00186 activating already running KAddressBook window ?) 00187 - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem 00188 - with ASN - can't be here, it's the KUniqueApp case then 00189 - without ASN - _NET_ACTIVE_WINDOW as application request should be used, 00190 KWin will activate the new window depending on the timestamp and 00191 whether it belongs to the currently active application 00192 00193 _NET_ACTIVE_WINDOW usage: 00194 data.l[0]= 1 ->app request 00195 = 2 ->pager request 00196 = 0 - backwards compatibility 00197 data.l[1]= timestamp 00198 */ 00199 00200 00201 //**************************************** 00202 // Workspace 00203 //**************************************** 00204 00205 00214 void Workspace::setActiveClient( Client* c, allowed_t ) 00215 { 00216 if ( active_client == c ) 00217 return; 00218 if( popup && popup_client != c && set_active_client_recursion == 0 ) 00219 { 00220 popup->close(); 00221 popup_client = 0; 00222 } 00223 StackingUpdatesBlocker blocker( this ); 00224 ++set_active_client_recursion; 00225 if( active_client != NULL ) 00226 { // note that this may call setActiveClient( NULL ), therefore the recursion counter 00227 active_client->setActive( false ); 00228 } 00229 active_client = c; 00230 Q_ASSERT( c == NULL || c->isActive()); 00231 if( active_client != NULL ) 00232 last_active_client = active_client; 00233 if ( active_client ) 00234 { 00235 focus_chain.remove( c ); 00236 if ( c->wantsTabFocus() ) 00237 focus_chain.append( c ); 00238 active_client->demandAttention( false ); 00239 } 00240 pending_take_activity = NULL; 00241 00242 updateCurrentTopMenu(); 00243 updateToolWindows( false ); 00244 00245 updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active 00246 00247 rootInfo->setActiveWindow( active_client? active_client->window() : 0 ); 00248 updateColormap(); 00249 --set_active_client_recursion; 00250 } 00251 00263 void Workspace::activateClient( Client* c, bool force ) 00264 { 00265 if( c == NULL ) 00266 { 00267 setActiveClient( NULL, Allowed ); 00268 return; 00269 } 00270 raiseClient( c ); 00271 if (!c->isOnDesktop(currentDesktop()) ) 00272 { 00273 ++block_focus; 00274 setCurrentDesktop( c->desktop() ); 00275 --block_focus; 00276 // popupinfo->showInfo( desktopName(currentDesktop()) ); // AK - not sure 00277 } 00278 if( c->isMinimized()) 00279 c->unminimize(); 00280 00281 if( options->focusPolicyIsReasonable()) 00282 requestFocus( c, force ); 00283 00284 // Don't update user time for clients that have focus stealing workaround. 00285 // As they usually belong to the current active window but fail to provide 00286 // this information, updating their user time would make the user time 00287 // of the currently active window old, and reject further activation for it. 00288 // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround), 00289 // and then kdesktop shows dialog about SSL certificate. 00290 // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp(). 00291 if( !c->ignoreFocusStealing()) 00292 c->updateUserTime(); 00293 } 00294 00302 void Workspace::requestFocus( Client* c, bool force ) 00303 { 00304 takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false); 00305 } 00306 00307 void Workspace::takeActivity( Client* c, int flags, bool handled ) 00308 { 00309 // the 'if( c == active_client ) return;' optimization mustn't be done here 00310 if (!focusChangeEnabled() && ( c != active_client) ) 00311 flags &= ~ActivityFocus; 00312 00313 if ( !c ) 00314 { 00315 focusToNull(); 00316 return; 00317 } 00318 00319 if( flags & ActivityFocus ) 00320 { 00321 Client* modal = c->findModal(); 00322 if( modal != NULL && modal != c ) 00323 { 00324 if( !modal->isOnDesktop( c->desktop())) 00325 { 00326 modal->setDesktop( c->desktop()); 00327 if( modal->desktop() != c->desktop()) // forced desktop 00328 activateClient( modal ); 00329 } 00330 // if the click was inside the window (i.e. handled is set), 00331 // but it has a modal, there's no need to use handled mode, because 00332 // the modal doesn't get the click anyway 00333 // raising of the original window needs to be still done 00334 if( flags & ActivityRaise ) 00335 raiseClient( c ); 00336 flags &= ~ActivityRaise; 00337 c = modal; 00338 handled = false; 00339 } 00340 cancelDelayFocus(); 00341 } 00342 if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) ) 00343 flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced 00344 if( c->isShade()) 00345 { 00346 if( c->wantsInput() && ( flags & ActivityFocus )) 00347 { 00348 // client cannot accept focus, but at least the window should be active (window menu, et. al. ) 00349 c->setActive( true ); 00350 focusToNull(); 00351 } 00352 flags &= ~ActivityFocus; 00353 handled = false; // no point, can't get clicks 00354 } 00355 if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed 00356 { 00357 kdWarning( 1212 ) << "takeActivity: not shown" << endl; 00358 return; 00359 } 00360 c->takeActivity( flags, handled, Allowed ); 00361 } 00362 00363 void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags ) 00364 { 00365 if( pending_take_activity != c ) // pending_take_activity is reset when doing restack or activation 00366 return; 00367 if(( flags & ActivityRaise ) != 0 ) 00368 raiseClient( c ); 00369 if(( flags & ActivityFocus ) != 0 && c->isShown( false )) 00370 c->takeFocus( Allowed ); 00371 pending_take_activity = NULL; 00372 } 00373 00381 void Workspace::clientHidden( Client* c ) 00382 { 00383 assert( !c->isShown( true ) || !c->isOnCurrentDesktop()); 00384 activateNextClient( c ); 00385 } 00386 00387 // deactivates 'c' and activates next client 00388 void Workspace::activateNextClient( Client* c ) 00389 { 00390 // if 'c' is not the active or the to-become active one, do nothing 00391 if( !( c == active_client 00392 || ( should_get_focus.count() > 0 && c == should_get_focus.last()))) 00393 return; 00394 if( popup ) 00395 popup->close(); 00396 if( c == active_client ) 00397 setActiveClient( NULL, Allowed ); 00398 should_get_focus.remove( c ); 00399 if( focusChangeEnabled()) 00400 { 00401 if ( c->wantsTabFocus() && focus_chain.contains( c ) ) 00402 { 00403 focus_chain.remove( c ); 00404 focus_chain.prepend( c ); 00405 } 00406 if ( options->focusPolicyIsReasonable()) 00407 { // search the focus_chain for a client to transfer focus to 00408 // if 'c' is transient, transfer focus to the first suitable mainwindow 00409 Client* get_focus = NULL; 00410 const ClientList mainwindows = c->mainClients(); 00411 for( ClientList::ConstIterator it = focus_chain.fromLast(); 00412 it != focus_chain.end(); 00413 --it ) 00414 { 00415 if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop()) 00416 continue; 00417 if( mainwindows.contains( *it )) 00418 { 00419 get_focus = *it; 00420 break; 00421 } 00422 if( get_focus == NULL ) 00423 get_focus = *it; 00424 } 00425 if( get_focus == NULL ) 00426 get_focus = findDesktop( true, currentDesktop()); 00427 if( get_focus != NULL ) 00428 requestFocus( get_focus ); 00429 else 00430 focusToNull(); 00431 } 00432 } 00433 else 00434 // if blocking focus, move focus to the desktop later if needed 00435 // in order to avoid flickering 00436 focusToNull(); 00437 } 00438 00439 00440 void Workspace::gotFocusIn( const Client* c ) 00441 { 00442 if( should_get_focus.contains( const_cast< Client* >( c ))) 00443 { // remove also all sooner elements that should have got FocusIn, 00444 // but didn't for some reason (and also won't anymore, because they were sooner) 00445 while( should_get_focus.first() != c ) 00446 should_get_focus.pop_front(); 00447 should_get_focus.pop_front(); // remove 'c' 00448 } 00449 } 00450 00451 void Workspace::setShouldGetFocus( Client* c ) 00452 { 00453 should_get_focus.append( c ); 00454 } 00455 00456 // focus_in -> the window got FocusIn event 00457 // session_active -> the window was active when saving session 00458 bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in ) 00459 { 00460 // options->focusStealingPreventionLevel : 00461 // 0 - none - old KWin behaviour, new windows always get focus 00462 // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed 00463 // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed, 00464 // this is the default 00465 // 3 - high - new window gets focus only if it belongs to the active application, 00466 // or when no window is currently active 00467 // 4 - extreme - no window gets focus without user intervention 00468 if( time == -1U ) 00469 time = c->userTime(); 00470 int level = c->rules()->checkFSP( options->focusStealingPreventionLevel ); 00471 if( session_saving && level <= 2 ) // <= normal 00472 { 00473 return true; 00474 } 00475 Client* ac = mostRecentlyActivatedClient(); 00476 if( focus_in ) 00477 { 00478 if( should_get_focus.contains( const_cast< Client* >( c ))) 00479 return true; // FocusIn was result of KWin's action 00480 // Before getting FocusIn, the active Client already 00481 // got FocusOut, and therefore got deactivated. 00482 ac = last_active_client; 00483 } 00484 if( time == 0 ) // explicitly asked not to get focus 00485 return false; 00486 if( level == 0 ) // none 00487 return true; 00488 if( level == 4 ) // extreme 00489 return false; 00490 if( !c->isOnCurrentDesktop()) 00491 return false; // allow only with level == 0 00492 if( c->ignoreFocusStealing()) 00493 return true; 00494 if( ac == NULL || ac->isDesktop()) 00495 { 00496 kdDebug( 1212 ) << "Activation: No client active, allowing" << endl; 00497 return true; // no active client -> always allow 00498 } 00499 // TODO window urgency -> return true? 00500 if( Client::belongToSameApplication( c, ac, true )) 00501 { 00502 kdDebug( 1212 ) << "Activation: Belongs to active application" << endl; 00503 return true; 00504 } 00505 if( level == 3 ) // high 00506 return false; 00507 if( time == -1U ) // no time known 00508 { 00509 kdDebug( 1212 ) << "Activation: No timestamp at all" << endl; 00510 if( level == 1 ) // low 00511 return true; 00512 // no timestamp at all, don't activate - because there's also creation timestamp 00513 // done on CreateNotify, this case should happen only in case application 00514 // maps again already used window, i.e. this won't happen after app startup 00515 return false; 00516 } 00517 // level == 2 // normal 00518 Time user_time = ac->userTime(); 00519 kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time 00520 << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl; 00521 return timestampCompare( time, user_time ) >= 0; // time >= user_time 00522 } 00523 00524 // basically the same like allowClientActivation(), this time allowing 00525 // a window to be fully raised upon its own request (XRaiseWindow), 00526 // if refused, it will be raised only on top of windows belonging 00527 // to the same application 00528 bool Workspace::allowFullClientRaising( const Client* c, Time time ) 00529 { 00530 int level = c->rules()->checkFSP( options->focusStealingPreventionLevel ); 00531 if( session_saving && level <= 2 ) // <= normal 00532 { 00533 return true; 00534 } 00535 Client* ac = mostRecentlyActivatedClient(); 00536 if( level == 0 ) // none 00537 return true; 00538 if( level == 4 ) // extreme 00539 return false; 00540 if( ac == NULL || ac->isDesktop()) 00541 { 00542 kdDebug( 1212 ) << "Raising: No client active, allowing" << endl; 00543 return true; // no active client -> always allow 00544 } 00545 if( c->ignoreFocusStealing()) 00546 return true; 00547 // TODO window urgency -> return true? 00548 if( Client::belongToSameApplication( c, ac, true )) 00549 { 00550 kdDebug( 1212 ) << "Raising: Belongs to active application" << endl; 00551 return true; 00552 } 00553 if( level == 3 ) // high 00554 return false; 00555 Time user_time = ac->userTime(); 00556 kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time 00557 << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl; 00558 return timestampCompare( time, user_time ) >= 0; // time >= user_time 00559 } 00560 00561 // called from Client after FocusIn that wasn't initiated by KWin and the client 00562 // wasn't allowed to activate 00563 void Workspace::restoreFocus() 00564 { 00565 // this updateXTime() is necessary - as FocusIn events don't have 00566 // a timestamp *sigh*, kwin's timestamp would be older than the timestamp 00567 // that was used by whoever caused the focus change, and therefore 00568 // the attempt to restore the focus would fail due to old timestamp 00569 updateXTime(); 00570 if( should_get_focus.count() > 0 ) 00571 requestFocus( should_get_focus.last()); 00572 else if( last_active_client ) 00573 requestFocus( last_active_client ); 00574 } 00575 00576 void Workspace::clientAttentionChanged( Client* c, bool set ) 00577 { 00578 if( set ) 00579 { 00580 attention_chain.remove( c ); 00581 attention_chain.prepend( c ); 00582 } 00583 else 00584 attention_chain.remove( c ); 00585 } 00586 00587 // This is used when a client should be shown active immediately after requestFocus(), 00588 // without waiting for the matching FocusIn that will really make the window the active one. 00589 // Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows, 00590 bool Workspace::fakeRequestedActivity( Client* c ) 00591 { 00592 if( should_get_focus.count() > 0 && should_get_focus.last() == c ) 00593 { 00594 if( c->isActive()) 00595 return false; 00596 c->setActive( true ); 00597 return true; 00598 } 00599 return false; 00600 } 00601 00602 void Workspace::unfakeActivity( Client* c ) 00603 { 00604 if( should_get_focus.count() > 0 && should_get_focus.last() == c ) 00605 { // TODO this will cause flicker, and probably is not needed 00606 if( last_active_client != NULL ) 00607 last_active_client->setActive( true ); 00608 else 00609 c->setActive( false ); 00610 } 00611 } 00612 00613 00614 //******************************************** 00615 // Client 00616 //******************************************** 00617 00624 void Client::updateUserTime( Time time ) 00625 { // copied in Group::updateUserTime 00626 if( time == CurrentTime ) 00627 time = qt_x_time; 00628 if( time != -1U 00629 && ( user_time == CurrentTime 00630 || timestampCompare( time, user_time ) > 0 )) // time > user_time 00631 user_time = time; 00632 } 00633 00634 Time Client::readUserCreationTime() const 00635 { 00636 long result = -1; // Time == -1 means none 00637 Atom type; 00638 int format, status; 00639 unsigned long nitems = 0; 00640 unsigned long extra = 0; 00641 unsigned char *data = 0; 00642 KXErrorHandler handler; // ignore errors? 00643 status = XGetWindowProperty( qt_xdisplay(), window(), 00644 atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL, 00645 &type, &format, &nitems, &extra, &data ); 00646 if (status == Success ) 00647 { 00648 if (data && nitems > 0) 00649 result = *((long*) data); 00650 XFree(data); 00651 } 00652 return result; 00653 } 00654 00655 void Client::demandAttention( bool set ) 00656 { 00657 if( isActive()) 00658 set = false; 00659 info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); 00660 workspace()->clientAttentionChanged( this, set ); 00661 } 00662 00663 // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it 00664 KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*, 00665 // ignore already existing splashes, toolbars, utilities, menus and topmenus, 00666 // as the app may show those before the main window 00667 !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu() 00668 && Client::belongToSameApplication( cl, value, true ) && cl != value); 00669 00670 Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, 00671 bool session ) const 00672 { 00673 Time time = info->userTime(); 00674 kdDebug( 1212 ) << "User timestamp, initial:" << time << endl; 00675 // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0 00676 // helps e.g. with konqy reusing 00677 if( asn_data != NULL && time != 0 ) 00678 { 00679 // prefer timestamp from ASN id (timestamp from data is obsolete way) 00680 if( asn_id->timestamp() != 0 00681 && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 )) 00682 { 00683 time = asn_id->timestamp(); 00684 } 00685 else if( asn_data->timestamp() != -1U 00686 && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 )) 00687 { 00688 time = asn_data->timestamp(); 00689 } 00690 } 00691 kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl; 00692 if( time == -1U ) 00693 { // The window doesn't have any timestamp. 00694 // If it's the first window for its application 00695 // (i.e. there's no other window from the same app), 00696 // use the _KDE_NET_WM_USER_CREATION_TIME trick. 00697 // Otherwise, refuse activation of a window 00698 // from already running application if this application 00699 // is not the active one. 00700 Client* act = workspace()->mostRecentlyActivatedClient(); 00701 if( act != NULL && !belongToSameApplication( act, this, true )) 00702 { 00703 bool first_window = true; 00704 if( isTransient()) 00705 { 00706 if( act->hasTransient( this, true )) 00707 ; // is transient for currently active window, even though it's not 00708 // the same app (e.g. kcookiejar dialog) -> allow activation 00709 else if( groupTransient() && 00710 findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL ) 00711 ; // standalone transient 00712 else 00713 first_window = false; 00714 } 00715 else 00716 { 00717 if( workspace()->findClient( SameApplicationActiveHackPredicate( this ))) 00718 first_window = false; 00719 } 00720 if( !first_window ) 00721 { 00722 kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl; 00723 return 0; // refuse activation 00724 } 00725 } 00726 // Creation time would just mess things up during session startup, 00727 // as possibly many apps are started up at the same time. 00728 // If there's no active window yet, no timestamp will be needed, 00729 // as plain Workspace::allowClientActivation() will return true 00730 // in such case. And if there's already active window, 00731 // it's better not to activate the new one. 00732 // Unless it was the active window at the time 00733 // of session saving and there was no user interaction yet, 00734 // this check will be done in manage(). 00735 if( session ) 00736 return -1U; 00737 if( ignoreFocusStealing() && act != NULL ) 00738 time = act->userTime(); 00739 else 00740 time = readUserCreationTime(); 00741 } 00742 kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl; 00743 return time; 00744 } 00745 00746 Time Client::userTime() const 00747 { 00748 Time time = user_time; 00749 if( time == 0 ) // doesn't want focus after showing 00750 return 0; 00751 assert( group() != NULL ); 00752 if( time == -1U 00753 || ( group()->userTime() != -1U 00754 && timestampCompare( group()->userTime(), time ) > 0 )) 00755 time = group()->userTime(); 00756 return time; 00757 } 00758 00770 void Client::setActive( bool act) 00771 { 00772 if ( active == act ) 00773 return; 00774 active = act; 00775 workspace()->setActiveClient( act ? this : NULL, Allowed ); 00776 00777 if ( active ) 00778 Notify::raise( Notify::Activate ); 00779 00780 if( !active ) 00781 cancelAutoRaise(); 00782 00783 if( !active && shade_mode == ShadeActivated ) 00784 setShade( ShadeNormal ); 00785 00786 StackingUpdatesBlocker blocker( workspace()); 00787 workspace()->updateClientLayer( this ); // active windows may get different layer 00788 // TODO optimize? mainClients() may be a bit expensive 00789 ClientList mainclients = mainClients(); 00790 for( ClientList::ConstIterator it = mainclients.begin(); 00791 it != mainclients.end(); 00792 ++it ) 00793 if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active 00794 workspace()->updateClientLayer( *it ); 00795 if( decoration != NULL ) 00796 decoration->activeChange(); 00797 updateMouseGrab(); 00798 updateUrgency(); // demand attention again if it's still urgent 00799 } 00800 00801 void Client::startupIdChanged() 00802 { 00803 KStartupInfoId asn_id; 00804 KStartupInfoData asn_data; 00805 bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data ); 00806 if( !asn_valid ) 00807 return; 00808 if( asn_data.desktop() != 0 ) 00809 workspace()->sendClientToDesktop( this, asn_data.desktop(), true ); 00810 Time timestamp = asn_id.timestamp(); 00811 if( timestamp == 0 && asn_data.timestamp() != -1U ) 00812 timestamp = asn_data.timestamp(); 00813 if( timestamp != 0 ) 00814 { 00815 bool activate = workspace()->allowClientActivation( this, asn_data.timestamp()); 00816 if( asn_data.desktop() != 0 && !isOnCurrentDesktop()) 00817 activate = false; // it was started on different desktop than current one 00818 if( activate ) 00819 workspace()->activateClient( this ); 00820 else 00821 demandAttention(); 00822 } 00823 } 00824 00825 void Client::updateUrgency() 00826 { 00827 if( urgency ) 00828 demandAttention(); 00829 } 00830 00831 //**************************************** 00832 // Group 00833 //**************************************** 00834 00835 void Group::startupIdChanged() 00836 { 00837 KStartupInfoId asn_id; 00838 KStartupInfoData asn_data; 00839 bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data ); 00840 if( !asn_valid ) 00841 return; 00842 if( asn_id.timestamp() != 0 && user_time != -1U 00843 && timestampCompare( asn_id.timestamp(), user_time ) > 0 ) 00844 { 00845 user_time = asn_id.timestamp(); 00846 } 00847 else if( asn_data.timestamp() != -1U && user_time != -1U 00848 && timestampCompare( asn_data.timestamp(), user_time ) > 0 ) 00849 { 00850 user_time = asn_data.timestamp(); 00851 } 00852 } 00853 00854 void Group::updateUserTime( Time time ) 00855 { // copy of Client::updateUserTime 00856 if( time == CurrentTime ) 00857 time = qt_x_time; 00858 if( time != -1U 00859 && ( user_time == CurrentTime 00860 || timestampCompare( time, user_time ) > 0 )) // time > user_time 00861 user_time = time; 00862 } 00863 00864 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 13 21:47:04 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003