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 <tqpopupmenu.h> 00020 #include <kxerrorhandler.h> 00021 #include <kstartupinfo.h> 00022 #include <kstringhandler.h> 00023 #include <klocale.h> 00024 00025 #include "client.h" 00026 #include "workspace.h" 00027 #include <fixx11h.h> 00028 00029 #include "notifications.h" 00030 #include "atoms.h" 00031 #include "group.h" 00032 #include "rules.h" 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 TQWidget::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( active_popup && active_popup_client != c && set_active_client_recursion == 0 ) 00219 closeActivePopup(); 00220 StackingUpdatesBlocker blocker( this ); 00221 ++set_active_client_recursion; 00222 updateFocusMousePosition( TQCursor::pos()); 00223 if( active_client != NULL ) 00224 { // note that this may call setActiveClient( NULL ), therefore the recursion counter 00225 active_client->setActive( false, !c || !c->isModal() || c != active_client->transientFor() ); 00226 } 00227 active_client = c; 00228 if (set_active_client_recursion == 1) 00229 { 00230 // Only unset next_active_client if activateClient() wasn't called by 00231 // Client::setActive() to set the active window to null before 00232 // activating another window. 00233 next_active_client = NULL; 00234 } 00235 Q_ASSERT( c == NULL || c->isActive()); 00236 if( active_client != NULL ) 00237 last_active_client = active_client; 00238 if ( active_client ) 00239 { 00240 updateFocusChains( active_client, FocusChainMakeFirst ); 00241 active_client->demandAttention( false ); 00242 } 00243 pending_take_activity = NULL; 00244 00245 updateCurrentTopMenu(); 00246 updateToolWindows( false ); 00247 if( c ) 00248 disableGlobalShortcutsForClient( c->rules()->checkDisableGlobalShortcuts( false )); 00249 else 00250 disableGlobalShortcutsForClient( false ); 00251 00252 updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active 00253 00254 rootInfo->setActiveWindow( active_client? active_client->window() : 0 ); 00255 updateColormap(); 00256 --set_active_client_recursion; 00257 } 00258 00270 void Workspace::activateClient( Client* c, bool force ) 00271 { 00272 if( c == NULL ) 00273 { 00274 focusToNull(); 00275 setActiveClient( NULL, Allowed ); 00276 return; 00277 } 00278 raiseClient( c ); 00279 if (!c->isOnDesktop(currentDesktop()) ) 00280 { 00281 ++block_focus; 00282 setCurrentDesktop( c->desktop() ); 00283 --block_focus; 00284 } 00285 if( c->isMinimized()) 00286 c->unminimize(); 00287 00288 // TODO force should perhaps allow this only if the window already contains the mouse 00289 if( options->focusPolicyIsReasonable() || force ) 00290 requestFocus( c, force ); 00291 00292 // Don't update user time for clients that have focus stealing workaround. 00293 // As they usually belong to the current active window but fail to provide 00294 // this information, updating their user time would make the user time 00295 // of the currently active window old, and reject further activation for it. 00296 // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround), 00297 // and then kdesktop shows dialog about SSL certificate. 00298 // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp(). 00299 if( !c->ignoreFocusStealing()) 00300 c->updateUserTime(); 00301 } 00302 00310 void Workspace::requestFocus( Client* c, bool force ) 00311 { 00312 takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false); 00313 } 00314 00315 void Workspace::takeActivity( Client* c, int flags, bool handled ) 00316 { 00317 // the 'if( c == active_client ) return;' optimization mustn't be done here 00318 if (!focusChangeEnabled() && ( c != active_client) ) 00319 flags &= ~ActivityFocus; 00320 00321 if ( !c ) 00322 { 00323 focusToNull(); 00324 return; 00325 } 00326 00327 if( flags & ActivityFocus ) 00328 { 00329 Client* modal = c->findModal(); 00330 if( modal != NULL && modal != c ) 00331 { 00332 next_active_client = modal; 00333 if( !modal->isOnDesktop( c->desktop())) 00334 { 00335 modal->setDesktop( c->desktop()); 00336 if( modal->desktop() != c->desktop()) // forced desktop 00337 activateClient( modal ); 00338 } 00339 // if the click was inside the window (i.e. handled is set), 00340 // but it has a modal, there's no need to use handled mode, because 00341 // the modal doesn't get the click anyway 00342 // raising of the original window needs to be still done 00343 if( flags & ActivityRaise ) 00344 raiseClient( c ); 00345 c = modal; 00346 handled = false; 00347 } 00348 cancelDelayFocus(); 00349 } 00350 if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) ) 00351 flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced 00352 if( c->isShade()) 00353 { 00354 if( c->wantsInput() && ( flags & ActivityFocus )) 00355 { 00356 // client cannot accept focus, but at least the window should be active (window menu, et. al. ) 00357 c->setActive( true ); 00358 focusToNull(); 00359 } 00360 if( c->wantsInput()) 00361 next_active_client = c; 00362 flags &= ~ActivityFocus; 00363 handled = false; // no point, can't get clicks 00364 } 00365 if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed 00366 { 00367 next_active_client = c; 00368 kdWarning( 1212 ) << "takeActivity: not shown" << endl; 00369 return; 00370 } 00371 c->takeActivity( flags, handled, Allowed ); 00372 if( !c->isOnScreen( active_screen )) 00373 active_screen = c->screen(); 00374 } 00375 00376 void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags ) 00377 { 00378 if( pending_take_activity != c ) // pending_take_activity is reset when doing restack or activation 00379 return; 00380 if(( flags & ActivityRaise ) != 0 ) 00381 raiseClient( c ); 00382 if(( flags & ActivityFocus ) != 0 && c->isShown( false )) 00383 c->takeFocus( Allowed ); 00384 pending_take_activity = NULL; 00385 } 00386 00394 void Workspace::clientHidden( Client* c ) 00395 { 00396 assert( !c->isShown( true ) || !c->isOnCurrentDesktop()); 00397 activateNextClient( c ); 00398 } 00399 00400 // deactivates 'c' and activates next client 00401 bool Workspace::activateNextClient( Client* c ) 00402 { 00403 // if 'c' is not the active or the to-become active one, do nothing 00404 if( !( c == active_client 00405 || ( should_get_focus.count() > 0 && c == should_get_focus.last()))) 00406 return false; 00407 closeActivePopup(); 00408 if( c != NULL ) 00409 { 00410 if( c == active_client ) 00411 setActiveClient( NULL, Allowed ); 00412 should_get_focus.remove( c ); 00413 } 00414 if( focusChangeEnabled()) 00415 { 00416 if ( options->focusPolicyIsReasonable()) 00417 { // search the focus_chain for a client to transfer focus to 00418 // if 'c' is transient, transfer focus to the first suitable mainwindow 00419 Client* get_focus = NULL; 00420 const ClientList mainwindows = ( c != NULL ? c->mainClients() : ClientList()); 00421 for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast(); 00422 it != focus_chain[currentDesktop()].end(); 00423 --it ) 00424 { 00425 if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop()) 00426 continue; 00427 if( options->separateScreenFocus ) 00428 { 00429 if( c != NULL && !(*it)->isOnScreen( c->screen())) 00430 continue; 00431 if( c == NULL && !(*it)->isOnScreen( activeScreen())) 00432 continue; 00433 } 00434 if( mainwindows.contains( *it )) 00435 { 00436 get_focus = *it; 00437 break; 00438 } 00439 if( get_focus == NULL ) 00440 get_focus = *it; 00441 } 00442 if( get_focus == NULL ) 00443 get_focus = findDesktop( true, currentDesktop()); 00444 if( get_focus != NULL ) 00445 requestFocus( get_focus ); 00446 else 00447 focusToNull(); 00448 } 00449 else 00450 return false; 00451 } 00452 else 00453 // if blocking focus, move focus to the desktop later if needed 00454 // in order to avoid flickering 00455 focusToNull(); 00456 return true; 00457 } 00458 00459 void Workspace::setCurrentScreen( int new_screen ) 00460 { 00461 if (new_screen < 0 || new_screen > numScreens()) 00462 return; 00463 if ( !options->focusPolicyIsReasonable()) 00464 return; 00465 closeActivePopup(); 00466 Client* get_focus = NULL; 00467 for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast(); 00468 it != focus_chain[currentDesktop()].end(); 00469 --it ) 00470 { 00471 if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop()) 00472 continue; 00473 if( !(*it)->screen() == new_screen ) 00474 continue; 00475 get_focus = *it; 00476 break; 00477 } 00478 if( get_focus == NULL ) 00479 get_focus = findDesktop( true, currentDesktop()); 00480 if( get_focus != NULL && get_focus != mostRecentlyActivatedClient()) 00481 requestFocus( get_focus ); 00482 active_screen = new_screen; 00483 } 00484 00485 void Workspace::gotFocusIn( const Client* c ) 00486 { 00487 if( should_get_focus.contains( const_cast< Client* >( c ))) 00488 { // remove also all sooner elements that should have got FocusIn, 00489 // but didn't for some reason (and also won't anymore, because they were sooner) 00490 while( should_get_focus.first() != c ) 00491 should_get_focus.pop_front(); 00492 should_get_focus.pop_front(); // remove 'c' 00493 } 00494 } 00495 00496 void Workspace::setShouldGetFocus( Client* c ) 00497 { 00498 should_get_focus.append( c ); 00499 updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active 00500 } 00501 00502 // focus_in -> the window got FocusIn event 00503 // session_active -> the window was active when saving session 00504 bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in ) 00505 { 00506 // options->focusStealingPreventionLevel : 00507 // 0 - none - old KWin behaviour, new windows always get focus 00508 // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed 00509 // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed, 00510 // this is the default 00511 // 3 - high - new window gets focus only if it belongs to the active application, 00512 // or when no window is currently active 00513 // 4 - extreme - no window gets focus without user intervention 00514 if( time == -1U ) 00515 time = c->userTime(); 00516 int level = c->rules()->checkFSP( options->focusStealingPreventionLevel ); 00517 if( session_saving && level <= 2 ) // <= normal 00518 { 00519 return true; 00520 } 00521 Client* ac = mostRecentlyActivatedClient(); 00522 if( focus_in ) 00523 { 00524 if( should_get_focus.contains( const_cast< Client* >( c ))) 00525 return true; // FocusIn was result of KWin's action 00526 // Before getting FocusIn, the active Client already 00527 // got FocusOut, and therefore got deactivated. 00528 ac = last_active_client; 00529 } 00530 if( time == 0 ) // explicitly asked not to get focus 00531 return false; 00532 if( level == 0 ) // none 00533 return true; 00534 if( level == 4 ) // extreme 00535 return false; 00536 if( !c->isOnCurrentDesktop()) 00537 return false; // allow only with level == 0 00538 if( c->ignoreFocusStealing()) 00539 return true; 00540 if( ac == NULL || ac->isDesktop()) 00541 { 00542 // kdDebug( 1212 ) << "Activation: No client active, allowing" << endl; 00543 return true; // no active client -> always allow 00544 } 00545 // TODO window urgency -> return true? 00546 if( Client::belongToSameApplication( c, ac, true )) 00547 { 00548 // kdDebug( 1212 ) << "Activation: Belongs to active application" << endl; 00549 return true; 00550 } 00551 if( level == 3 ) // high 00552 return false; 00553 if( time == -1U ) // no time known 00554 { 00555 // kdDebug( 1212 ) << "Activation: No timestamp at all" << endl; 00556 if( level == 1 ) // low 00557 return true; 00558 // no timestamp at all, don't activate - because there's also creation timestamp 00559 // done on CreateNotify, this case should happen only in case application 00560 // maps again already used window, i.e. this won't happen after app startup 00561 return false; 00562 } 00563 // level == 2 // normal 00564 Time user_time = ac->userTime(); 00565 // kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time 00566 // << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl; 00567 return timestampCompare( time, user_time ) >= 0; // time >= user_time 00568 } 00569 00570 // basically the same like allowClientActivation(), this time allowing 00571 // a window to be fully raised upon its own request (XRaiseWindow), 00572 // if refused, it will be raised only on top of windows belonging 00573 // to the same application 00574 bool Workspace::allowFullClientRaising( const Client* c, Time time ) 00575 { 00576 int level = c->rules()->checkFSP( options->focusStealingPreventionLevel ); 00577 if( session_saving && level <= 2 ) // <= normal 00578 { 00579 return true; 00580 } 00581 Client* ac = mostRecentlyActivatedClient(); 00582 if( level == 0 ) // none 00583 return true; 00584 if( level == 4 ) // extreme 00585 return false; 00586 if( ac == NULL || ac->isDesktop()) 00587 { 00588 // kdDebug( 1212 ) << "Raising: No client active, allowing" << endl; 00589 return true; // no active client -> always allow 00590 } 00591 if( c->ignoreFocusStealing()) 00592 return true; 00593 // TODO window urgency -> return true? 00594 if( Client::belongToSameApplication( c, ac, true )) 00595 { 00596 // kdDebug( 1212 ) << "Raising: Belongs to active application" << endl; 00597 return true; 00598 } 00599 if( level == 3 ) // high 00600 return false; 00601 Time user_time = ac->userTime(); 00602 // kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time 00603 // << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl; 00604 return timestampCompare( time, user_time ) >= 0; // time >= user_time 00605 } 00606 00607 // called from Client after FocusIn that wasn't initiated by KWin and the client 00608 // wasn't allowed to activate 00609 void Workspace::restoreFocus() 00610 { 00611 // this updateXTime() is necessary - as FocusIn events don't have 00612 // a timestamp *sigh*, kwin's timestamp would be older than the timestamp 00613 // that was used by whoever caused the focus change, and therefore 00614 // the attempt to restore the focus would fail due to old timestamp 00615 updateXTime(); 00616 if( should_get_focus.count() > 0 ) 00617 requestFocus( should_get_focus.last()); 00618 else if( last_active_client ) 00619 requestFocus( last_active_client ); 00620 } 00621 00622 void Workspace::clientAttentionChanged( Client* c, bool set ) 00623 { 00624 if( set ) 00625 { 00626 attention_chain.remove( c ); 00627 attention_chain.prepend( c ); 00628 } 00629 else 00630 attention_chain.remove( c ); 00631 } 00632 00633 // This is used when a client should be shown active immediately after requestFocus(), 00634 // without waiting for the matching FocusIn that will really make the window the active one. 00635 // Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows, 00636 bool Workspace::fakeRequestedActivity( Client* c ) 00637 { 00638 if( should_get_focus.count() > 0 && should_get_focus.last() == c ) 00639 { 00640 if( c->isActive()) 00641 return false; 00642 c->setActive( true ); 00643 return true; 00644 } 00645 return false; 00646 } 00647 00648 void Workspace::unfakeActivity( Client* c ) 00649 { 00650 if( should_get_focus.count() > 0 && should_get_focus.last() == c ) 00651 { // TODO this will cause flicker, and probably is not needed 00652 if( last_active_client != NULL ) 00653 last_active_client->setActive( true ); 00654 else 00655 c->setActive( false ); 00656 } 00657 } 00658 00659 00660 //******************************************** 00661 // Client 00662 //******************************************** 00663 00670 void Client::updateUserTime( Time time ) 00671 { // copied in Group::updateUserTime 00672 if( time == CurrentTime ) 00673 time = GET_QT_X_TIME(); 00674 if( time != -1U 00675 && ( user_time == CurrentTime 00676 || timestampCompare( time, user_time ) > 0 )) // time > user_time 00677 user_time = time; 00678 group()->updateUserTime( user_time ); 00679 } 00680 00681 Time Client::readUserCreationTime() const 00682 { 00683 long result = -1; // Time == -1 means none 00684 Atom type; 00685 int format, status; 00686 unsigned long nitems = 0; 00687 unsigned long extra = 0; 00688 unsigned char *data = 0; 00689 KXErrorHandler handler; // ignore errors? 00690 status = XGetWindowProperty( qt_xdisplay(), window(), 00691 atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL, 00692 &type, &format, &nitems, &extra, &data ); 00693 if (status == Success ) 00694 { 00695 if (data && nitems > 0) 00696 result = *((long*) data); 00697 XFree(data); 00698 } 00699 return result; 00700 } 00701 00702 void Client::demandAttention( bool set ) 00703 { 00704 if( isActive()) 00705 set = false; 00706 if( demands_attention == set ) 00707 return; 00708 demands_attention = set; 00709 if( demands_attention ) 00710 { 00711 // Demand attention flag is often set right from manage(), when focus stealing prevention 00712 // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place 00713 // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry 00714 // to be set. 00715 // Delayed call to KNotify also solves the problem of having X server grab in manage(), 00716 // which may deadlock when KNotify (or KLauncher when launching KNotify) need to access X. 00717 Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; 00718 // Setting the demands attention state needs to be done directly in KWin, because 00719 // KNotify would try to set it, resulting in a call to KNotify again, etc. 00720 if( Notify::makeDemandAttention( e )) 00721 info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); 00722 00723 if( demandAttentionKNotifyTimer == NULL ) 00724 { 00725 demandAttentionKNotifyTimer = new TQTimer( this ); 00726 connect( demandAttentionKNotifyTimer, TQT_SIGNAL( timeout()), TQT_SLOT( demandAttentionKNotify())); 00727 } 00728 demandAttentionKNotifyTimer->start( 1000, true ); 00729 } 00730 else 00731 info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); 00732 workspace()->clientAttentionChanged( this, set ); 00733 } 00734 00735 void Client::demandAttentionKNotify() 00736 { 00737 Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; 00738 Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this ); 00739 demandAttentionKNotifyTimer->stop(); 00740 demandAttentionKNotifyTimer->deleteLater(); 00741 demandAttentionKNotifyTimer = NULL; 00742 } 00743 00744 // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it 00745 KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*, 00746 // ignore already existing splashes, toolbars, utilities, menus and topmenus, 00747 // as the app may show those before the main window 00748 !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu() 00749 && Client::belongToSameApplication( cl, value, true ) && cl != value); 00750 00751 Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, 00752 bool session ) const 00753 { 00754 Time time = info->userTime(); 00755 // kdDebug( 1212 ) << "User timestamp, initial:" << time << endl; 00756 // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0 00757 // helps e.g. with konqy reusing 00758 if( asn_data != NULL && time != 0 ) 00759 { 00760 // prefer timestamp from ASN id (timestamp from data is obsolete way) 00761 if( asn_id->timestamp() != 0 00762 && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 )) 00763 { 00764 time = asn_id->timestamp(); 00765 } 00766 else if( asn_data->timestamp() != -1U 00767 && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 )) 00768 { 00769 time = asn_data->timestamp(); 00770 } 00771 } 00772 // kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl; 00773 if( time == -1U ) 00774 { // The window doesn't have any timestamp. 00775 // If it's the first window for its application 00776 // (i.e. there's no other window from the same app), 00777 // use the _KDE_NET_WM_USER_CREATION_TIME trick. 00778 // Otherwise, refuse activation of a window 00779 // from already running application if this application 00780 // is not the active one (unless focus stealing prevention is turned off). 00781 Client* act = workspace()->mostRecentlyActivatedClient(); 00782 if( act != NULL && !belongToSameApplication( act, this, true )) 00783 { 00784 bool first_window = true; 00785 if( isTransient()) 00786 { 00787 if( act->hasTransient( this, true )) 00788 ; // is transient for currently active window, even though it's not 00789 // the same app (e.g. kcookiejar dialog) -> allow activation 00790 else if( groupTransient() && 00791 findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL ) 00792 ; // standalone transient 00793 else 00794 first_window = false; 00795 } 00796 else 00797 { 00798 if( workspace()->findClient( SameApplicationActiveHackPredicate( this ))) 00799 first_window = false; 00800 } 00801 // don't refuse if focus stealing prevention is turned off 00802 if( !first_window && rules()->checkFSP( options->focusStealingPreventionLevel ) > 0 ) 00803 { 00804 // kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl; 00805 return 0; // refuse activation 00806 } 00807 } 00808 // Creation time would just mess things up during session startup, 00809 // as possibly many apps are started up at the same time. 00810 // If there's no active window yet, no timestamp will be needed, 00811 // as plain Workspace::allowClientActivation() will return true 00812 // in such case. And if there's already active window, 00813 // it's better not to activate the new one. 00814 // Unless it was the active window at the time 00815 // of session saving and there was no user interaction yet, 00816 // this check will be done in manage(). 00817 if( session ) 00818 return -1U; 00819 if( ignoreFocusStealing() && act != NULL ) 00820 time = act->userTime(); 00821 else 00822 time = readUserCreationTime(); 00823 } 00824 // kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl; 00825 return time; 00826 } 00827 00828 Time Client::userTime() const 00829 { 00830 Time time = user_time; 00831 if( time == 0 ) // doesn't want focus after showing 00832 return 0; 00833 assert( group() != NULL ); 00834 if( time == -1U 00835 || ( group()->userTime() != -1U 00836 && timestampCompare( group()->userTime(), time ) > 0 )) 00837 time = group()->userTime(); 00838 return time; 00839 } 00840 00852 void Client::setActive( bool act, bool updateOpacity_) 00853 { 00854 if ( active == act ) 00855 return; 00856 active = act; 00857 workspace()->setActiveClient( act ? this : NULL, Allowed ); 00858 00859 if (updateOpacity_) updateOpacity(); 00860 if (isModal() && transientFor()) 00861 { 00862 if (!act) transientFor()->updateOpacity(); 00863 else if (!transientFor()->custom_opacity) transientFor()->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); 00864 } 00865 updateShadowSize(); 00866 00867 if ( active ) 00868 { 00869 Notify::raise( Notify::Activate ); 00870 if (options->shadowEnabled(true)) 00871 { 00872 if (options->shadowEnabled(false)) 00873 { 00874 // Wait for inactive shadow to expose occluded windows and give 00875 // them a chance to redraw before painting the active shadow 00876 removeShadow(); 00877 drawDelayedShadow(); 00878 if (!isDesktop() && 00879 this != workspace()->topClientOnDesktop(desktop())) 00880 // If the newly activated window's isn't the desktop, wait 00881 // for its shadow to draw, then redraw any shadows 00882 // overlapping it. 00883 drawOverlappingShadows(true); 00884 } 00885 else 00886 drawShadow(); 00887 } 00888 } 00889 else 00890 { 00891 removeShadow(); 00892 00893 if (options->shadowEnabled(false)) 00894 if (this == workspace()->topClientOnDesktop(desktop())) 00895 { 00896 /* If the newly deactivated window is the top client on the 00897 * desktop, then the newly activated window is below it; ensure 00898 * that the deactivated window's shadow draws after the 00899 * activated window's shadow. 00900 */ 00901 if ((shadowAfterClient = workspace()->activeClient())) 00902 drawShadowAfter(shadowAfterClient); 00903 } 00904 else 00905 drawDelayedShadow(); 00906 } 00907 00908 if( !active ) 00909 cancelAutoRaise(); 00910 00911 if( !active && shade_mode == ShadeActivated ) 00912 setShade( ShadeNormal ); 00913 00914 StackingUpdatesBlocker blocker( workspace()); 00915 workspace()->updateClientLayer( this ); // active windows may get different layer 00916 // TODO optimize? mainClients() may be a bit expensive 00917 ClientList mainclients = mainClients(); 00918 for( ClientList::ConstIterator it = mainclients.begin(); 00919 it != mainclients.end(); 00920 ++it ) 00921 if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active 00922 workspace()->updateClientLayer( *it ); 00923 if( decoration != NULL ) 00924 decoration->activeChange(); 00925 updateMouseGrab(); 00926 updateUrgency(); // demand attention again if it's still urgent 00927 } 00928 00929 void Client::startupIdChanged() 00930 { 00931 KStartupInfoId asn_id; 00932 KStartupInfoData asn_data; 00933 bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data ); 00934 if( !asn_valid ) 00935 return; 00936 // If the ASN contains desktop, move it to the desktop, otherwise move it to the current 00937 // desktop (since the new ASN should make the window act like if it's a new application 00938 // launched). However don't affect the window's desktop if it's set to be on all desktops. 00939 int desktop = workspace()->currentDesktop(); 00940 if( asn_data.desktop() != 0 ) 00941 desktop = asn_data.desktop(); 00942 if( !isOnAllDesktops()) 00943 workspace()->sendClientToDesktop( this, desktop, true ); 00944 if( asn_data.xinerama() != -1 ) 00945 workspace()->sendClientToScreen( this, asn_data.xinerama()); 00946 Time timestamp = asn_id.timestamp(); 00947 if( timestamp == 0 && asn_data.timestamp() != -1U ) 00948 timestamp = asn_data.timestamp(); 00949 if( timestamp != 0 ) 00950 { 00951 bool activate = workspace()->allowClientActivation( this, timestamp ); 00952 if( asn_data.desktop() != 0 && !isOnCurrentDesktop()) 00953 activate = false; // it was started on different desktop than current one 00954 if( activate ) 00955 workspace()->activateClient( this ); 00956 else 00957 demandAttention(); 00958 } 00959 } 00960 00961 void Client::updateUrgency() 00962 { 00963 if( urgency ) 00964 demandAttention(); 00965 } 00966 00967 void Client::shortcutActivated() 00968 { 00969 workspace()->activateClient( this, true ); // force 00970 } 00971 00972 //**************************************** 00973 // Group 00974 //**************************************** 00975 00976 void Group::startupIdChanged() 00977 { 00978 KStartupInfoId asn_id; 00979 KStartupInfoData asn_data; 00980 bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data ); 00981 if( !asn_valid ) 00982 return; 00983 if( asn_id.timestamp() != 0 && user_time != -1U 00984 && timestampCompare( asn_id.timestamp(), user_time ) > 0 ) 00985 { 00986 user_time = asn_id.timestamp(); 00987 } 00988 else if( asn_data.timestamp() != -1U && user_time != -1U 00989 && timestampCompare( asn_data.timestamp(), user_time ) > 0 ) 00990 { 00991 user_time = asn_data.timestamp(); 00992 } 00993 } 00994 00995 void Group::updateUserTime( Time time ) 00996 { // copy of Client::updateUserTime 00997 if( time == CurrentTime ) 00998 time = GET_QT_X_TIME(); 00999 if( time != -1U 01000 && ( user_time == CurrentTime 01001 || timestampCompare( time, user_time ) > 0 )) // time > user_time 01002 user_time = time; 01003 } 01004 01005 } // namespace