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