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

kwin

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

kwin

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

kwin

Skip menu "kwin"
  • kate
  • kwin
  •   lib
  • libkonq
Generated for kwin by doxygen 1.5.6
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |