kwin Library API Documentation

activation.cpp

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