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