• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • twin
 

twin

activation.cpp

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

twin

Skip menu "twin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.6.3
This website is maintained by Timothy Pearson.