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( options->focusStealingPreventionLevel == 0 ) // none
00448         return true;
00449     if( options->focusStealingPreventionLevel == 4 ) // extreme
00450         return false;
00451     if( ac == NULL || ac->isDesktop())
00452         {
00453         kdDebug( 1212 ) << "Activation: No client active, allowing" << endl;
00454         return true; // no active client -> always allow
00455         }
00456     if( c->ignoreFocusStealing())
00457         return true;
00458     if( time == 0 ) // explicitly asked not to get focus
00459         return false;
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     assert( group() != NULL );
00710     if( time == -1U
00711          || ( group()->userTime() != -1U
00712                  && timestampCompare( group()->userTime(), time ) > 0 ))
00713         time = group()->userTime();
00714     return time;
00715     }
00716 
00728 void Client::setActive( bool act)
00729     {
00730     if ( active == act )
00731         return;
00732     active = act;
00733     workspace()->setActiveClient( act ? this : NULL, Allowed );
00734 
00735     if ( active )
00736         Notify::raise( Notify::Activate );
00737 
00738     if( !active )
00739         cancelAutoRaise();
00740 
00741     if( !active && shade_mode == ShadeActivated )
00742         setShade( ShadeNormal );
00743 
00744     StackingUpdatesBlocker blocker( workspace());
00745     workspace()->updateClientLayer( this ); // active windows may get different layer
00746     // TODO optimize? mainClients() may be a bit expensive
00747     ClientList mainclients = mainClients();
00748     for( ClientList::ConstIterator it = mainclients.begin();
00749          it != mainclients.end();
00750          ++it )
00751         if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active
00752             workspace()->updateClientLayer( *it );
00753     if( decoration != NULL )
00754         decoration->activeChange();
00755     updateMouseGrab();
00756     updateUrgency(); // demand attention again if it's still urgent
00757     }
00758 
00759 void Client::startupIdChanged()
00760     {
00761     KStartupInfoData asn_data;
00762     bool asn_valid = workspace()->checkStartupNotification( window(), asn_data );
00763     if( !asn_valid )
00764         return;
00765     if( asn_data.desktop() != 0 )
00766         workspace()->sendClientToDesktop( this, asn_data.desktop(), true );
00767     if( asn_data.timestamp() != -1U )
00768         {
00769         bool activate = workspace()->allowClientActivation( this, asn_data.timestamp());
00770         if( asn_data.desktop() != 0 && !isOnCurrentDesktop())
00771             activate = false; // it was started on different desktop than current one
00772         if( activate )
00773             workspace()->activateClient( this );
00774         else
00775             demandAttention();
00776         }
00777     }
00778 
00779 void Client::updateUrgency()
00780     {
00781     if( urgency )
00782         demandAttention();
00783     }
00784 
00785 //****************************************
00786 // Group
00787 //****************************************
00788     
00789 void Group::startupIdChanged()
00790     {
00791     KStartupInfoData asn_data;
00792     bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_data );
00793     if( !asn_valid )
00794         return;
00795     if( asn_data.timestamp() != -1U && user_time != -1U
00796         &&timestampCompare( asn_data.timestamp(), user_time ) > 0 )
00797         user_time = asn_data.timestamp();
00798     }
00799 
00800 void Group::updateUserTime( Time time )
00801     { // copy of Client::updateUserTime
00802     if( time == CurrentTime )
00803         time = qt_x_time;
00804     if( time != -1U
00805         && ( user_time == CurrentTime
00806             || timestampCompare( time, user_time ) > 0 )) // time > user_time
00807         user_time = time;
00808     }
00809 
00810 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Apr 11 13:44:51 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003