• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

activation.cpp

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

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal