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     c->updateUserTime();
00282     }
00283 
00291 void Workspace::requestFocus( Client* c, bool force )
00292     { // the 'if( c == active_client ) return;' optimization mustn't be done here
00293     if (!focusChangeEnabled() && ( c != active_client) )
00294         return;
00295 
00296     //TODO will be different for non-root clients. (subclassing?)
00297     if ( !c ) 
00298         {
00299         focusToNull();
00300         return;
00301         }
00302 
00303     if( !c->isOnCurrentDesktop()) // shouldn't happen, call activateClient() if needed
00304         {
00305         kdWarning( 1212 ) << "requestFocus: not on current desktop" << endl;
00306         return;
00307         }
00308 
00309     Client* modal = c->findModal();
00310     if( modal != NULL && modal != c )   
00311         { 
00312         if( !modal->isOnDesktop( c->desktop())) // move the modal to client's desktop
00313             modal->setDesktop( c->isOnAllDesktops() ? currentDesktop() : c->desktop());
00314         requestFocus( modal, force );
00315         return;
00316         }
00317     if ( c->isShown( false ) ) 
00318         {
00319         c->takeFocus( force, Allowed );
00320         should_get_focus.append( c );
00321         focus_chain.remove( c );
00322         if ( c->wantsTabFocus() )
00323             focus_chain.append( c );
00324         }
00325     else if ( c->isShade() && c->wantsInput()) 
00326         {
00327         // client cannot accept focus, but at least the window should be active (window menu, et. al. )
00328         c->setActive( true );
00329         focusToNull();
00330         }
00331     }
00332 
00340 void Workspace::clientHidden( Client* c )
00341     {
00342     assert( !c->isShown( true ) || !c->isOnCurrentDesktop());
00343     activateNextClient( c );
00344     }
00345 
00346 // deactivates 'c' and activates next client    
00347 void Workspace::activateNextClient( Client* c )
00348     {
00349     // if 'c' is not the active or the to-become active one, do nothing
00350     if( !( c == active_client
00351             || ( should_get_focus.count() > 0 && c == should_get_focus.last())))
00352         return;
00353     if( popup )
00354         popup->close();
00355     if( c == active_client )
00356         setActiveClient( NULL, Allowed );
00357     should_get_focus.remove( c );
00358     if( focusChangeEnabled())
00359         {
00360         if ( c->wantsTabFocus() && focus_chain.contains( c ) )
00361             {
00362             focus_chain.remove( c );
00363             focus_chain.prepend( c );
00364             }
00365         if ( options->focusPolicyIsReasonable())
00366             { // search the focus_chain for a client to transfer focus to
00367           // if 'c' is transient, transfer focus to the first suitable mainwindow
00368             Client* get_focus = NULL;
00369             const ClientList mainwindows = c->mainClients();
00370             for( ClientList::ConstIterator it = focus_chain.fromLast();
00371                  it != focus_chain.end();
00372                  --it )
00373                 {
00374                 if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
00375                     continue;
00376                 if( mainwindows.contains( *it ))
00377                     {
00378                     get_focus = *it;
00379                     break;
00380                     }
00381                 if( get_focus == NULL )
00382                     get_focus = *it;
00383                 }
00384             if( get_focus == NULL )
00385                 get_focus = findDesktop( true, currentDesktop());
00386             if( get_focus != NULL )
00387                 requestFocus( get_focus );
00388             else
00389                 focusToNull();
00390             }
00391         }
00392     else
00393         // if blocking focus, move focus to the desktop later if needed
00394         // in order to avoid flickering
00395         focusToNull();
00396     }
00397 
00398 
00399 void Workspace::gotFocusIn( const Client* c )
00400     {
00401     if( should_get_focus.contains( const_cast< Client* >( c )))
00402         { // remove also all sooner elements that should have got FocusIn,
00403       // but didn't for some reason (and also won't anymore, because they were sooner)
00404         while( should_get_focus.first() != c )
00405             should_get_focus.pop_front();
00406         }
00407     }
00408 
00409 
00410 // focus_in -> the window got FocusIn event
00411 // session_active -> the window was active when saving session
00412 bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in, bool session_active )
00413     {
00414     // options->focusStealingPreventionLevel :
00415     // 0 - none    - old KWin behaviour, new windows always get focus
00416     // 1 - low     - focus stealing prevention is applied normally, when unsure, activation is allowed
00417     // 2 - normal  - focus stealing prevention is applied normally, when unsure, activation is not allowed,
00418     //              this is the default
00419     // 3 - high    - new window gets focus only if it belongs to the active application,
00420     //              or when no window is currently active
00421     // 4 - extreme - no window gets focus without user intervention
00422     if( session_saving
00423         && options->focusStealingPreventionLevel <= 2 ) // <= normal
00424         {
00425         return true;
00426         }
00427     Client* ac = mostRecentlyActivatedClient();
00428     if( focus_in )
00429         {
00430         if( should_get_focus.contains( const_cast< Client* >( c )))
00431             return true; // FocusIn was result of KWin's action
00432         // Before getting FocusIn, the active Client already
00433         // got FocusOut, and therefore got deactivated.
00434         ac = last_active_client;
00435         }
00436     if( options->focusStealingPreventionLevel == 0 ) // none
00437         return true;
00438     if( options->focusStealingPreventionLevel == 4 ) // extreme
00439         return false;
00440     if( ac == NULL || ac->isDesktop())
00441         {
00442         kdDebug( 1212 ) << "Activation: No client active, allowing" << endl;
00443         return true; // no active client -> always allow
00444         }
00445     if( options->ignoreFocusStealingClasses.contains(QString::fromLatin1(c->resourceClass())))
00446         return true;
00447     if( time == 0 ) // explicitly asked not to get focus
00448         return false;
00449     // TODO window urgency  -> return true?
00450     if( Client::belongToSameApplication( c, ac, true ))
00451         {
00452         kdDebug( 1212 ) << "Activation: Belongs to active application" << endl;
00453         return true;
00454         }
00455     if( options->focusStealingPreventionLevel == 3 ) // high
00456         return false;
00457     if( time == -1U )  // no time known
00458         if( session_active )
00459             return !was_user_interaction; // see Client::readUserTimeMapTimestamp()
00460         else
00461         {
00462         kdDebug() << "Activation: No timestamp at all" << endl;
00463         if( options->focusStealingPreventionLevel == 1 ) // low
00464             return true;
00465         // no timestamp at all, don't activate - because there's also creation timestamp
00466         // done on CreateNotify, this case should happen only in case application
00467         // maps again already used window, i.e. this won't happen after app startup
00468         return false; 
00469         }
00470     // options->focusStealingPreventionLevel == 2 // normal
00471     Time user_time = ac->userTime();
00472     kdDebug( 1212 ) << "Activation, compared:" << time << ":" << user_time
00473         << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
00474     return timestampCompare( time, user_time ) >= 0; // time >= user_time
00475     }
00476 
00477 // basically the same like allowClientActivation(), this time allowing
00478 // a window to be fully raised upon its own request (XRaiseWindow),
00479 // if refused, it will be raised only on top of windows belonging
00480 // to the same application
00481 bool Workspace::allowFullClientRaising( const Client* c )
00482     {
00483     if( session_saving
00484         && options->focusStealingPreventionLevel <= 2 ) // <= normal
00485         {
00486         return true;
00487         }
00488     Client* ac = mostRecentlyActivatedClient();
00489     if( options->focusStealingPreventionLevel == 0 ) // none
00490         return true;
00491     if( options->focusStealingPreventionLevel == 4 ) // extreme
00492         return false;
00493     if( ac == NULL || ac->isDesktop())
00494         {
00495         kdDebug( 1212 ) << "Raising: No client active, allowing" << endl;
00496         return true; // no active client -> always allow
00497         }
00498     if( options->ignoreFocusStealingClasses.contains(QString::fromLatin1(c->resourceClass())))
00499         return true;
00500     // TODO window urgency  -> return true?
00501     if( Client::belongToSameApplication( c, ac, true ))
00502         {
00503         kdDebug( 1212 ) << "Raising: Belongs to active application" << endl;
00504         return true;
00505         }
00506     if( options->focusStealingPreventionLevel == 3 ) // high
00507         return false;
00508     if( !c->hasUserTimeSupport())
00509         {
00510         kdDebug() << "Raising: No support" << endl;
00511         if( options->focusStealingPreventionLevel == 1 ) // low
00512             return true;
00513         }
00514     // options->focusStealingPreventionLevel == 2 // normal
00515     kdDebug() << "Raising: Refusing" << endl;
00516     return false;
00517     }
00518 
00519 // called from Client after FocusIn that wasn't initiated by KWin and the client
00520 // wasn't allowed to activate
00521 void Workspace::restoreFocus()
00522     {
00523     // this updateXTime() is necessary - as FocusIn events don't have
00524     // a timestamp *sigh*, kwin's timestamp would be older than the timestamp
00525     // that was used by whoever caused the focus change, and therefore
00526     // the attempt to restore the focus would fail due to old timestamp
00527     updateXTime();
00528     if( should_get_focus.count() > 0 )
00529         requestFocus( should_get_focus.last());
00530     else if( last_active_client )
00531         requestFocus( last_active_client );
00532     }
00533 
00534 void Workspace::clientAttentionChanged( Client* c, bool set )
00535     {
00536     if( set )
00537         {
00538         attention_chain.remove( c );
00539         attention_chain.prepend( c );
00540         }
00541     else
00542         attention_chain.remove( c );
00543     }
00544 
00545 // This is used when a client should be shown active immediately after requestFocus(),
00546 // without waiting for the matching FocusIn that will really make the window the active one.
00547 // Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows,
00548 bool Workspace::fakeRequestedActivity( Client* c )
00549     {
00550     if( should_get_focus.count() > 0 && should_get_focus.last() == c )
00551         {
00552         if( c->isActive())
00553             return false;
00554         c->setActive( true );
00555         return true;
00556         }
00557     return false;
00558     }
00559 
00560 void Workspace::unfakeActivity( Client* c )
00561     {
00562     if( should_get_focus.count() > 0 && should_get_focus.last() == c )
00563         { // TODO this will cause flicker, and probably is not needed
00564         if( last_active_client != NULL )
00565             last_active_client->setActive( true );
00566         else
00567             c->setActive( false );
00568         }
00569     }
00570 
00571 
00572 //********************************************
00573 // Client
00574 //********************************************
00575 
00582 void Client::updateUserTime( Time time )
00583     { // copied in Group::updateUserTime
00584     if( time == CurrentTime )
00585         time = qt_x_time;
00586     if( time != -1U
00587         && ( user_time == CurrentTime
00588             || timestampCompare( time, user_time ) > 0 )) // time > user_time
00589         user_time = time;
00590     }
00591 
00592 Time Client::readUserCreationTime() const
00593     {
00594     long result = -1; // Time == -1 means none
00595     Atom type;
00596     int format, status;
00597     unsigned long nitems = 0;
00598     unsigned long extra = 0;
00599     unsigned char *data = 0;
00600     KXErrorHandler handler; // ignore errors?
00601     status = XGetWindowProperty( qt_xdisplay(), window(),
00602         atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL,
00603         &type, &format, &nitems, &extra, &data );
00604     if (status  == Success )
00605         {
00606         if (data && nitems > 0)
00607             result = *((long*) data);
00608         XFree(data);
00609         }
00610     return result;       
00611     }
00612 
00613 void Client::demandAttention( bool set )
00614     {
00615     if( isActive())
00616         set = false;
00617     info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
00618     workspace()->clientAttentionChanged( this, set );
00619     }
00620 
00621 // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it
00622 KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*,
00623     // ignore already existing splashes, toolbars, utilities, menus and topmenus,
00624     // as the app may show those before the main window
00625     !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu()
00626     && Client::belongToSameApplication( cl, value, true ) && cl != value);
00627 
00628 Time Client::readUserTimeMapTimestamp( const KStartupInfoData* asn_data,
00629     const SessionInfo* session ) const
00630     {
00631     Time time = info->userTime();
00632     kdDebug( 1212 ) << "User timestamp, initial:" << time << endl;
00633     // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0
00634     // helps e.g. with konqy reusing
00635     if( asn_data != NULL && time != 0
00636         && ( time == -1U
00637             || ( asn_data->timestamp() != -1U
00638                 && timestampCompare( asn_data->timestamp(), time ) > 0 )))
00639         time = asn_data->timestamp();
00640     kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl;
00641     if( time == -1U )
00642         { // The window doesn't have any timestamp.
00643       // If it's the first window for its application
00644       // (i.e. there's no other window from the same app),
00645       // use the _KDE_NET_WM_USER_CREATION_TIME trick.
00646       // Otherwise, refuse activation of a window
00647       // from already running application if this application
00648       // is not the active one.
00649         Client* act = workspace()->mostRecentlyActivatedClient();
00650         if( act != NULL && !belongToSameApplication( act, this, true ))
00651             {
00652             bool first_window = true;
00653             if( isTransient())
00654                 {
00655                 if( act->hasTransient( this, true ))
00656                     ; // is transient for currently active window, even though it's not
00657                       // the same app (e.g. kcookiejar dialog) -> allow activation
00658                 else if( groupTransient() &&
00659                     findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL )
00660                     ; // standalone transient
00661                 else
00662                     first_window = false;
00663                 }
00664             else
00665                 {
00666                 if( workspace()->findClient( SameApplicationActiveHackPredicate( this )))
00667                     first_window = false;
00668                 }
00669             if( !first_window )
00670                 {
00671                 kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl;
00672                 return 0; // refuse activation
00673                 }
00674             }
00675         // Creation time would just mess things up during session startup,
00676         // as possibly many apps are started up at the same time.
00677         // If there's no active window yet, no timestamp will be needed,
00678         // as plain Workspace::allowClientActivation() will return true
00679         // in such case. And if there's already active window,
00680         // it's better not to activate the new one.
00681         // Unless it was the active window at the time
00682         // of session saving and there was no user interaction yet,
00683         // this check will be done in Workspace::allowClientActiovationTimestamp().
00684         if( session && !session->fake )
00685             return -1U;
00686         time = readUserCreationTime();
00687         }
00688     kdDebug( 1212 ) << "User timestamp, final:" << time << endl;
00689     return time;
00690     }
00691 
00692 Time Client::userTime() const
00693     {
00694     Time time = user_time;
00695     assert( group() != NULL );
00696     if( time == -1U
00697          || ( group()->userTime() != -1U
00698                  && timestampCompare( group()->userTime(), time ) > 0 ))
00699         time = group()->userTime();
00700     return time;
00701     }
00702 
00714 void Client::setActive( bool act)
00715     {
00716     if ( active == act )
00717         return;
00718     active = act;
00719     workspace()->setActiveClient( act ? this : NULL, Allowed );
00720 
00721     if ( active )
00722         Notify::raise( Notify::Activate );
00723 
00724     if( !active )
00725         cancelAutoRaise();
00726 
00727     if( !active && shade_mode == ShadeActivated )
00728         setShade( ShadeNormal );
00729 
00730     StackingUpdatesBlocker blocker( workspace());
00731     workspace()->updateClientLayer( this ); // active windows may get different layer
00732     // TODO optimize? mainClients() may be a bit expensive
00733     ClientList mainclients = mainClients();
00734     for( ClientList::ConstIterator it = mainclients.begin();
00735          it != mainclients.end();
00736          ++it )
00737         if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active
00738             workspace()->updateClientLayer( *it );
00739     if( decoration != NULL )
00740         decoration->activeChange();
00741     updateMouseGrab();
00742     updateUrgency(); // demand attention again if it's still urgent
00743     }
00744 
00745 void Client::startupIdChanged()
00746     {
00747     KStartupInfoData asn_data;
00748     bool asn_valid = workspace()->checkStartupNotification( window(), asn_data );
00749     if( !asn_valid )
00750         return;
00751     if( asn_data.desktop() != 0 )
00752         workspace()->sendClientToDesktop( this, asn_data.desktop(), true );
00753     if( asn_data.timestamp() != -1U )
00754         {
00755         bool activate = workspace()->allowClientActivation( this, asn_data.timestamp());
00756         if( asn_data.desktop() != 0 && !isOnCurrentDesktop())
00757             activate = false; // it was started on different desktop than current one
00758         if( activate )
00759             workspace()->activateClient( this );
00760         else
00761             demandAttention();
00762         }
00763     }
00764 
00765 void Client::updateUrgency()
00766     {
00767     if( urgency )
00768         demandAttention();
00769     }
00770 
00771 //****************************************
00772 // Group
00773 //****************************************
00774     
00775 void Group::startupIdChanged()
00776     {
00777     KStartupInfoData asn_data;
00778     bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_data );
00779     if( !asn_valid )
00780         return;
00781     if( asn_data.timestamp() != -1U && user_time != -1U
00782         &&timestampCompare( asn_data.timestamp(), user_time ) > 0 )
00783         user_time = asn_data.timestamp();
00784     }
00785 
00786 void Group::updateUserTime( Time time )
00787     { // copy of Client::updateUserTime
00788     if( time == CurrentTime )
00789         time = qt_x_time;
00790     if( time != -1U
00791         && ( user_time == CurrentTime
00792             || timestampCompare( time, user_time ) > 0 )) // time > user_time
00793         user_time = time;
00794     }
00795 
00796 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 5 04:41:13 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003