summaryrefslogtreecommitdiffstats
path: root/kwin/activation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kwin/activation.cpp')
-rw-r--r--kwin/activation.cpp1005
1 files changed, 0 insertions, 1005 deletions
diff --git a/kwin/activation.cpp b/kwin/activation.cpp
deleted file mode 100644
index d0212def2..000000000
--- a/kwin/activation.cpp
+++ /dev/null
@@ -1,1005 +0,0 @@
-/*****************************************************************
- KWin - the KDE window manager
- This file is part of the KDE project.
-
-Copyright (C) 1999, 2000 Matthias Ettrich <[email protected]>
-Copyright (C) 2003 Lubos Lunak <[email protected]>
-
-You can Freely distribute this program under the GNU General Public
-License. See the file "COPYING" for the exact licensing terms.
-******************************************************************/
-
-/*
-
- This file contains things relevant to window activation and focus
- stealing prevention.
-
-*/
-
-#include <tqpopupmenu.h>
-#include <kxerrorhandler.h>
-#include <kstartupinfo.h>
-#include <kstringhandler.h>
-#include <klocale.h>
-
-#include "client.h"
-#include "workspace.h"
-#include <fixx11h.h>
-
-#include "notifications.h"
-#include "atoms.h"
-#include "group.h"
-#include "rules.h"
-
-namespace KWinInternal
-{
-
-/*
- Prevention of focus stealing:
-
- KWin tries to prevent unwanted changes of focus, that would result
- from mapping a new window. Also, some nasty applications may try
- to force focus change even in cases when ICCCM 4.2.7 doesn't allow it
- (e.g. they may try to activate their main window because the user
- definitely "needs" to see something happened - misusing
- of TQWidget::setActiveWindow() may be such case).
-
- There are 4 ways how a window may become active:
- - the user changes the active window (e.g. focus follows mouse, clicking
- on some window's titlebar) - the change of focus will
- be done by KWin, so there's nothing to solve in this case
- - the change of active window will be requested using the _NET_ACTIVE_WINDOW
- message (handled in RootInfo::changeActiveWindow()) - such requests
- will be obeyed, because this request is meant mainly for e.g. taskbar
- asking the WM to change the active window as a result of some user action.
- Normal applications should use this request only rarely in special cases.
- See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER.
- - the change of active window will be done by performing XSetInputFocus()
- on a window that's not currently active. ICCCM 4.2.7 describes when
- the application may perform change of input focus. In order to handle
- misbehaving applications, KWin will try to detect focus changes to
- windows that don't belong to currently active application, and restore
- focus back to the currently active window, instead of activating the window
- that got focus (unfortunately there's no way to FocusChangeRedirect similar
- to e.g. SubstructureRedirect, so there will be short time when the focus
- will be changed). The check itself that's done is
- Workspace::allowClientActivation() (see below).
- - a new window will be mapped - this is the most complicated case. If
- the new window belongs to the currently active application, it may be safely
- mapped on top and activated. The same if there's no active window,
- or the active window is the desktop. These checks are done by
- Workspace::allowClientActivation().
- Following checks need to compare times. One time is the timestamp
- of last user action in the currently active window, the other time is
- the timestamp of the action that originally caused mapping of the new window
- (e.g. when the application was started). If the first time is newer than
- the second one, the window will not be activated, as that indicates
- futher user actions took place after the action leading to this new
- mapped window. This check is done by Workspace::allowClientActivation().
- There are several ways how to get the timestamp of action that caused
- the new mapped window (done in Client::readUserTimeMapTimestamp()) :
- - the window may have the _NET_WM_USER_TIME property. This way
- the application may either explicitly request that the window is not
- activated (by using 0 timestamp), or the property contains the time
- of last user action in the application.
- - KWin itself tries to detect time of last user action in every window,
- by watching KeyPress and ButtonPress events on windows. This way some
- events may be missed (if they don't propagate to the toplevel window),
- but it's good as a fallback for applications that don't provide
- _NET_WM_USER_TIME, and missing some events may at most lead
- to unwanted focus stealing.
- - the timestamp may come from application startup notification.
- Application startup notification, if it exists for the new mapped window,
- should include time of the user action that caused it.
- - if there's no timestamp available, it's checked whether the new window
- belongs to some already running application - if yes, the timestamp
- will be 0 (i.e. refuse activation)
- - if the window is from session restored window, the timestamp will
- be 0 too, unless this application was the active one at the time
- when the session was saved, in which case the window will be
- activated if there wasn't any user interaction since the time
- KWin was started.
- - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp
- is used. For every toplevel window that is created (see CreateNotify
- handling), this property is set to the at that time current time.
- Since at this time it's known that the new window doesn't belong
- to any existing application (better said, the application doesn't
- have any other window mapped), it is either the very first window
- of the application, or its the only window of the application
- that was hidden before. The latter case is handled by removing
- the property from windows before withdrawing them, making
- the timestamp empty for next mapping of the window. In the sooner
- case, the timestamp will be used. This helps in case when
- an application is launched without application startup notification,
- it creates its mainwindow, and starts its initialization (that
- may possibly take long time). The timestamp used will be older
- than any user action done after launching this application.
- - if no timestamp is found at all, the window is activated.
- The check whether two windows belong to the same application (same
- process) is done in Client::belongToSameApplication(). Not 100% reliable,
- but hopefully 99,99% reliable.
-
- As a somewhat special case, window activation is always enabled when
- session saving is in progress. When session saving, the session
- manager allows only one application to interact with the user.
- Not allowing window activation in such case would result in e.g. dialogs
- not becoming active, so focus stealing prevention would cause here
- more harm than good.
-
- Windows that attempted to become active but KWin prevented this will
- be marked as demanding user attention. They'll get
- the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark
- them specially (blink, etc.). The state will be reset when the window
- eventually really becomes active.
-
- There are one more ways how a window can become obstrusive, window stealing
- focus: By showing above the active window, by either raising itself,
- or by moving itself on the active desktop.
- - KWin will refuse raising non-active window above the active one,
- unless they belong to the same application. Applications shouldn't
- raise their windows anyway (unless the app wants to raise one
- of its windows above another of its windows).
- - KWin activates windows moved to the current desktop (as that seems
- logical from the user's point of view, after sending the window
- there directly from KWin, or e.g. using pager). This means
- applications shouldn't send their windows to another desktop
- (SELI TODO - but what if they do?)
-
- Special cases I can think of:
- - konqueror reusing, i.e. kfmclient tells running Konqueror instance
- to open new window
- - without focus stealing prevention - no problem
- - with ASN (application startup notification) - ASN is forwarded,
- and because it's newer than the instance's user timestamp,
- it takes precedence
- - without ASN - user timestamp needs to be reset, otherwise it would
- be used, and it's old; moreover this new window mustn't be detected
- as window belonging to already running application, or it wouldn't
- be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly)
- hack
- - konqueror preloading, i.e. window is created in advance, and kfmclient
- tells this Konqueror instance to show it later
- - without focus stealing prevention - no problem
- - with ASN - ASN is forwarded, and because it's newer than the instance's
- user timestamp, it takes precedence
- - without ASN - user timestamp needs to be reset, otherwise it would
- be used, and it's old; also, creation timestamp is changed to
- the time the instance starts (re-)initializing the window,
- this ensures creation timestamp will still work somewhat even in this case
- - KUniqueApplication - when the window is already visible, and the new instance
- wants it to activate
- - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
- - with ASN - ASN is forwarded, and set on the already visible window, KWin
- treats the window as new with that ASN
- - without ASN - _NET_ACTIVE_WINDOW as application request is used,
- and there's no really usable timestamp, only timestamp
- from the time the (new) application instance was started,
- so KWin will activate the window *sigh*
- - the bad thing here is that there's absolutely no chance to recognize
- the case of starting this KUniqueApp from Konsole (and thus wanting
- the already visible window to become active) from the case
- when something started this KUniqueApp without ASN (in which case
- the already visible window shouldn't become active)
- - the only solution is using ASN for starting applications, at least silent
- (i.e. without feedback)
- - when one application wants to activate another application's window (e.g. KMail
- activating already running KAddressBook window ?)
- - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
- - with ASN - can't be here, it's the KUniqueApp case then
- - without ASN - _NET_ACTIVE_WINDOW as application request should be used,
- KWin will activate the new window depending on the timestamp and
- whether it belongs to the currently active application
-
- _NET_ACTIVE_WINDOW usage:
- data.l[0]= 1 ->app request
- = 2 ->pager request
- = 0 - backwards compatibility
- data.l[1]= timestamp
-*/
-
-
-//****************************************
-// Workspace
-//****************************************
-
-
-/*!
- Informs the workspace about the active client, i.e. the client that
- has the focus (or None if no client has the focus). This functions
- is called by the client itself that gets focus. It has no other
- effect than fixing the focus chain and the return value of
- activeClient(). And of course, to propagate the active client to the
- world.
- */
-void Workspace::setActiveClient( Client* c, allowed_t )
- {
- if ( active_client == c )
- return;
- if( active_popup && active_popup_client != c && set_active_client_recursion == 0 )
- closeActivePopup();
- StackingUpdatesBlocker blocker( this );
- ++set_active_client_recursion;
- updateFocusMousePosition( TQCursor::pos());
- if( active_client != NULL )
- { // note that this may call setActiveClient( NULL ), therefore the recursion counter
- active_client->setActive( false, !c || !c->isModal() || c != active_client->transientFor() );
- }
- active_client = c;
- if (set_active_client_recursion == 1)
- {
- // Only unset next_active_client if activateClient() wasn't called by
- // Client::setActive() to set the active window to null before
- // activating another window.
- next_active_client = NULL;
- }
- Q_ASSERT( c == NULL || c->isActive());
- if( active_client != NULL )
- last_active_client = active_client;
- if ( active_client )
- {
- updateFocusChains( active_client, FocusChainMakeFirst );
- active_client->demandAttention( false );
- }
- pending_take_activity = NULL;
-
- updateCurrentTopMenu();
- updateToolWindows( false );
- if( c )
- disableGlobalShortcutsForClient( c->rules()->checkDisableGlobalShortcuts( false ));
- else
- disableGlobalShortcutsForClient( false );
-
- updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
-
- rootInfo->setActiveWindow( active_client? active_client->window() : 0 );
- updateColormap();
- --set_active_client_recursion;
- }
-
-/*!
- Tries to activate the client \a c. This function performs what you
- expect when clicking the respective entry in a taskbar: showing and
- raising the client (this may imply switching to the another virtual
- desktop) and putting the focus onto it. Once X really gave focus to
- the client window as requested, the client itself will call
- setActiveClient() and the operation is complete. This may not happen
- with certain focus policies, though.
-
- \sa stActiveClient(), requestFocus()
- */
-void Workspace::activateClient( Client* c, bool force )
- {
- if( c == NULL )
- {
- focusToNull();
- setActiveClient( NULL, Allowed );
- return;
- }
- raiseClient( c );
- if (!c->isOnDesktop(currentDesktop()) )
- {
- ++block_focus;
- setCurrentDesktop( c->desktop() );
- --block_focus;
- }
- if( c->isMinimized())
- c->unminimize();
-
-// TODO force should perhaps allow this only if the window already contains the mouse
- if( options->focusPolicyIsReasonable() || force )
- requestFocus( c, force );
-
- // Don't update user time for clients that have focus stealing workaround.
- // As they usually belong to the current active window but fail to provide
- // this information, updating their user time would make the user time
- // of the currently active window old, and reject further activation for it.
- // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround),
- // and then kdesktop shows dialog about SSL certificate.
- // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp().
- if( !c->ignoreFocusStealing())
- c->updateUserTime();
- }
-
-/*!
- Tries to activate the client by asking X for the input focus. This
- function does not perform any show, raise or desktop switching. See
- Workspace::activateClient() instead.
-
- \sa Workspace::activateClient()
- */
-void Workspace::requestFocus( Client* c, bool force )
- {
- takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false);
- }
-
-void Workspace::takeActivity( Client* c, int flags, bool handled )
- {
- // the 'if( c == active_client ) return;' optimization mustn't be done here
- if (!focusChangeEnabled() && ( c != active_client) )
- flags &= ~ActivityFocus;
-
- if ( !c )
- {
- focusToNull();
- return;
- }
-
- if( flags & ActivityFocus )
- {
- Client* modal = c->findModal();
- if( modal != NULL && modal != c )
- {
- next_active_client = modal;
- if( !modal->isOnDesktop( c->desktop()))
- {
- modal->setDesktop( c->desktop());
- if( modal->desktop() != c->desktop()) // forced desktop
- activateClient( modal );
- }
- // if the click was inside the window (i.e. handled is set),
- // but it has a modal, there's no need to use handled mode, because
- // the modal doesn't get the click anyway
- // raising of the original window needs to be still done
- if( flags & ActivityRaise )
- raiseClient( c );
- c = modal;
- handled = false;
- }
- cancelDelayFocus();
- }
- if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) )
- flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced
- if( c->isShade())
- {
- if( c->wantsInput() && ( flags & ActivityFocus ))
- {
- // client cannot accept focus, but at least the window should be active (window menu, et. al. )
- c->setActive( true );
- focusToNull();
- }
- if( c->wantsInput())
- next_active_client = c;
- flags &= ~ActivityFocus;
- handled = false; // no point, can't get clicks
- }
- if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed
- {
- next_active_client = c;
- kdWarning( 1212 ) << "takeActivity: not shown" << endl;
- return;
- }
- c->takeActivity( flags, handled, Allowed );
- if( !c->isOnScreen( active_screen ))
- active_screen = c->screen();
- }
-
-void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags )
- {
- if( pending_take_activity != c ) // pending_take_activity is reset when doing restack or activation
- return;
- if(( flags & ActivityRaise ) != 0 )
- raiseClient( c );
- if(( flags & ActivityFocus ) != 0 && c->isShown( false ))
- c->takeFocus( Allowed );
- pending_take_activity = NULL;
- }
-
-/*!
- Informs the workspace that the client \a c has been hidden. If it
- was the active client (or to-become the active client),
- the workspace activates another one.
-
- \a c may already be destroyed
- */
-void Workspace::clientHidden( Client* c )
- {
- assert( !c->isShown( true ) || !c->isOnCurrentDesktop());
- activateNextClient( c );
- }
-
-// deactivates 'c' and activates next client
-bool Workspace::activateNextClient( Client* c )
- {
- // if 'c' is not the active or the to-become active one, do nothing
- if( !( c == active_client
- || ( should_get_focus.count() > 0 && c == should_get_focus.last())))
- return false;
- closeActivePopup();
- if( c != NULL )
- {
- if( c == active_client )
- setActiveClient( NULL, Allowed );
- should_get_focus.remove( c );
- }
- if( focusChangeEnabled())
- {
- if ( options->focusPolicyIsReasonable())
- { // search the focus_chain for a client to transfer focus to
- // if 'c' is transient, transfer focus to the first suitable mainwindow
- Client* get_focus = NULL;
- const ClientList mainwindows = ( c != NULL ? c->mainClients() : ClientList());
- for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
- it != focus_chain[currentDesktop()].end();
- --it )
- {
- if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
- continue;
- if( options->separateScreenFocus )
- {
- if( c != NULL && !(*it)->isOnScreen( c->screen()))
- continue;
- if( c == NULL && !(*it)->isOnScreen( activeScreen()))
- continue;
- }
- if( mainwindows.contains( *it ))
- {
- get_focus = *it;
- break;
- }
- if( get_focus == NULL )
- get_focus = *it;
- }
- if( get_focus == NULL )
- get_focus = findDesktop( true, currentDesktop());
- if( get_focus != NULL )
- requestFocus( get_focus );
- else
- focusToNull();
- }
- else
- return false;
- }
- else
- // if blocking focus, move focus to the desktop later if needed
- // in order to avoid flickering
- focusToNull();
- return true;
- }
-
-void Workspace::setCurrentScreen( int new_screen )
- {
- if (new_screen < 0 || new_screen > numScreens())
- return;
- if ( !options->focusPolicyIsReasonable())
- return;
- closeActivePopup();
- Client* get_focus = NULL;
- for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
- it != focus_chain[currentDesktop()].end();
- --it )
- {
- if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
- continue;
- if( !(*it)->screen() == new_screen )
- continue;
- get_focus = *it;
- break;
- }
- if( get_focus == NULL )
- get_focus = findDesktop( true, currentDesktop());
- if( get_focus != NULL && get_focus != mostRecentlyActivatedClient())
- requestFocus( get_focus );
- active_screen = new_screen;
- }
-
-void Workspace::gotFocusIn( const Client* c )
- {
- if( should_get_focus.contains( const_cast< Client* >( c )))
- { // remove also all sooner elements that should have got FocusIn,
- // but didn't for some reason (and also won't anymore, because they were sooner)
- while( should_get_focus.first() != c )
- should_get_focus.pop_front();
- should_get_focus.pop_front(); // remove 'c'
- }
- }
-
-void Workspace::setShouldGetFocus( Client* c )
- {
- should_get_focus.append( c );
- updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
- }
-
-// focus_in -> the window got FocusIn event
-// session_active -> the window was active when saving session
-bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in )
- {
- // options->focusStealingPreventionLevel :
- // 0 - none - old KWin behaviour, new windows always get focus
- // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed
- // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed,
- // this is the default
- // 3 - high - new window gets focus only if it belongs to the active application,
- // or when no window is currently active
- // 4 - extreme - no window gets focus without user intervention
- if( time == -1U )
- time = c->userTime();
- int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
- if( session_saving && level <= 2 ) // <= normal
- {
- return true;
- }
- Client* ac = mostRecentlyActivatedClient();
- if( focus_in )
- {
- if( should_get_focus.contains( const_cast< Client* >( c )))
- return true; // FocusIn was result of KWin's action
- // Before getting FocusIn, the active Client already
- // got FocusOut, and therefore got deactivated.
- ac = last_active_client;
- }
- if( time == 0 ) // explicitly asked not to get focus
- return false;
- if( level == 0 ) // none
- return true;
- if( level == 4 ) // extreme
- return false;
- if( !c->isOnCurrentDesktop())
- return false; // allow only with level == 0
- if( c->ignoreFocusStealing())
- return true;
- if( ac == NULL || ac->isDesktop())
- {
-// kdDebug( 1212 ) << "Activation: No client active, allowing" << endl;
- return true; // no active client -> always allow
- }
- // TODO window urgency -> return true?
- if( Client::belongToSameApplication( c, ac, true ))
- {
-// kdDebug( 1212 ) << "Activation: Belongs to active application" << endl;
- return true;
- }
- if( level == 3 ) // high
- return false;
- if( time == -1U ) // no time known
- {
-// kdDebug( 1212 ) << "Activation: No timestamp at all" << endl;
- if( level == 1 ) // low
- return true;
- // no timestamp at all, don't activate - because there's also creation timestamp
- // done on CreateNotify, this case should happen only in case application
- // maps again already used window, i.e. this won't happen after app startup
- return false;
- }
- // level == 2 // normal
- Time user_time = ac->userTime();
-// kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time
-// << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
- return timestampCompare( time, user_time ) >= 0; // time >= user_time
- }
-
-// basically the same like allowClientActivation(), this time allowing
-// a window to be fully raised upon its own request (XRaiseWindow),
-// if refused, it will be raised only on top of windows belonging
-// to the same application
-bool Workspace::allowFullClientRaising( const Client* c, Time time )
- {
- int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
- if( session_saving && level <= 2 ) // <= normal
- {
- return true;
- }
- Client* ac = mostRecentlyActivatedClient();
- if( level == 0 ) // none
- return true;
- if( level == 4 ) // extreme
- return false;
- if( ac == NULL || ac->isDesktop())
- {
-// kdDebug( 1212 ) << "Raising: No client active, allowing" << endl;
- return true; // no active client -> always allow
- }
- if( c->ignoreFocusStealing())
- return true;
- // TODO window urgency -> return true?
- if( Client::belongToSameApplication( c, ac, true ))
- {
-// kdDebug( 1212 ) << "Raising: Belongs to active application" << endl;
- return true;
- }
- if( level == 3 ) // high
- return false;
- Time user_time = ac->userTime();
-// kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time
-// << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
- return timestampCompare( time, user_time ) >= 0; // time >= user_time
- }
-
-// called from Client after FocusIn that wasn't initiated by KWin and the client
-// wasn't allowed to activate
-void Workspace::restoreFocus()
- {
- // this updateXTime() is necessary - as FocusIn events don't have
- // a timestamp *sigh*, twin's timestamp would be older than the timestamp
- // that was used by whoever caused the focus change, and therefore
- // the attempt to restore the focus would fail due to old timestamp
- updateXTime();
- if( should_get_focus.count() > 0 )
- requestFocus( should_get_focus.last());
- else if( last_active_client )
- requestFocus( last_active_client );
- }
-
-void Workspace::clientAttentionChanged( Client* c, bool set )
- {
- if( set )
- {
- attention_chain.remove( c );
- attention_chain.prepend( c );
- }
- else
- attention_chain.remove( c );
- }
-
-// This is used when a client should be shown active immediately after requestFocus(),
-// without waiting for the matching FocusIn that will really make the window the active one.
-// Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows,
-bool Workspace::fakeRequestedActivity( Client* c )
- {
- if( should_get_focus.count() > 0 && should_get_focus.last() == c )
- {
- if( c->isActive())
- return false;
- c->setActive( true );
- return true;
- }
- return false;
- }
-
-void Workspace::unfakeActivity( Client* c )
- {
- if( should_get_focus.count() > 0 && should_get_focus.last() == c )
- { // TODO this will cause flicker, and probably is not needed
- if( last_active_client != NULL )
- last_active_client->setActive( true );
- else
- c->setActive( false );
- }
- }
-
-
-//********************************************
-// Client
-//********************************************
-
-/*!
- Updates the user time (time of last action in the active window).
- This is called inside twin for every action with the window
- that qualifies for user interaction (clicking on it, activate it
- externally, etc.).
- */
-void Client::updateUserTime( Time time )
- { // copied in Group::updateUserTime
- if( time == CurrentTime )
- time = GET_QT_X_TIME();
- if( time != -1U
- && ( user_time == CurrentTime
- || timestampCompare( time, user_time ) > 0 )) // time > user_time
- user_time = time;
- group()->updateUserTime( user_time );
- }
-
-Time Client::readUserCreationTime() const
- {
- long result = -1; // Time == -1 means none
- Atom type;
- int format, status;
- unsigned long nitems = 0;
- unsigned long extra = 0;
- unsigned char *data = 0;
- KXErrorHandler handler; // ignore errors?
- status = XGetWindowProperty( qt_xdisplay(), window(),
- atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL,
- &type, &format, &nitems, &extra, &data );
- if (status == Success )
- {
- if (data && nitems > 0)
- result = *((long*) data);
- XFree(data);
- }
- return result;
- }
-
-void Client::demandAttention( bool set )
- {
- if( isActive())
- set = false;
- if( demands_attention == set )
- return;
- demands_attention = set;
- if( demands_attention )
- {
- // Demand attention flag is often set right from manage(), when focus stealing prevention
- // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place
- // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry
- // to be set.
- // Delayed call to KNotify also solves the problem of having X server grab in manage(),
- // which may deadlock when KNotify (or KLauncher when launching KNotify) need to access X.
- Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
- // Setting the demands attention state needs to be done directly in KWin, because
- // KNotify would try to set it, resulting in a call to KNotify again, etc.
- if( Notify::makeDemandAttention( e ))
- info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
-
- if( demandAttentionKNotifyTimer == NULL )
- {
- demandAttentionKNotifyTimer = new TQTimer( this );
- connect( demandAttentionKNotifyTimer, TQT_SIGNAL( timeout()), TQT_SLOT( demandAttentionKNotify()));
- }
- demandAttentionKNotifyTimer->start( 1000, true );
- }
- else
- info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
- workspace()->clientAttentionChanged( this, set );
- }
-
-void Client::demandAttentionKNotify()
- {
- Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
- Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this );
- demandAttentionKNotifyTimer->stop();
- demandAttentionKNotifyTimer->deleteLater();
- demandAttentionKNotifyTimer = NULL;
- }
-
-// TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it
-KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*,
- // ignore already existing splashes, toolbars, utilities, menus and topmenus,
- // as the app may show those before the main window
- !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu()
- && Client::belongToSameApplication( cl, value, true ) && cl != value);
-
-Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data,
- bool session ) const
- {
- Time time = info->userTime();
-// kdDebug( 1212 ) << "User timestamp, initial:" << time << endl;
- // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0
- // helps e.g. with konqy reusing
- if( asn_data != NULL && time != 0 )
- {
- // prefer timestamp from ASN id (timestamp from data is obsolete way)
- if( asn_id->timestamp() != 0
- && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 ))
- {
- time = asn_id->timestamp();
- }
- else if( asn_data->timestamp() != -1U
- && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 ))
- {
- time = asn_data->timestamp();
- }
- }
-// kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl;
- if( time == -1U )
- { // The window doesn't have any timestamp.
- // If it's the first window for its application
- // (i.e. there's no other window from the same app),
- // use the _KDE_NET_WM_USER_CREATION_TIME trick.
- // Otherwise, refuse activation of a window
- // from already running application if this application
- // is not the active one (unless focus stealing prevention is turned off).
- Client* act = workspace()->mostRecentlyActivatedClient();
- if( act != NULL && !belongToSameApplication( act, this, true ))
- {
- bool first_window = true;
- if( isTransient())
- {
- if( act->hasTransient( this, true ))
- ; // is transient for currently active window, even though it's not
- // the same app (e.g. kcookiejar dialog) -> allow activation
- else if( groupTransient() &&
- findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL )
- ; // standalone transient
- else
- first_window = false;
- }
- else
- {
- if( workspace()->findClient( SameApplicationActiveHackPredicate( this )))
- first_window = false;
- }
- // don't refuse if focus stealing prevention is turned off
- if( !first_window && rules()->checkFSP( options->focusStealingPreventionLevel ) > 0 )
- {
-// kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl;
- return 0; // refuse activation
- }
- }
- // Creation time would just mess things up during session startup,
- // as possibly many apps are started up at the same time.
- // If there's no active window yet, no timestamp will be needed,
- // as plain Workspace::allowClientActivation() will return true
- // in such case. And if there's already active window,
- // it's better not to activate the new one.
- // Unless it was the active window at the time
- // of session saving and there was no user interaction yet,
- // this check will be done in manage().
- if( session )
- return -1U;
- if( ignoreFocusStealing() && act != NULL )
- time = act->userTime();
- else
- time = readUserCreationTime();
- }
-// kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl;
- return time;
- }
-
-Time Client::userTime() const
- {
- Time time = user_time;
- if( time == 0 ) // doesn't want focus after showing
- return 0;
- assert( group() != NULL );
- if( time == -1U
- || ( group()->userTime() != -1U
- && timestampCompare( group()->userTime(), time ) > 0 ))
- time = group()->userTime();
- return time;
- }
-
-/*!
- Sets the client's active state to \a act.
-
- This function does only change the visual appearance of the client,
- it does not change the focus setting. Use
- Workspace::activateClient() or Workspace::requestFocus() instead.
-
- If a client receives or looses the focus, it calls setActive() on
- its own.
-
- */
-void Client::setActive( bool act, bool updateOpacity_)
- {
- if ( active == act )
- return;
- active = act;
- workspace()->setActiveClient( act ? this : NULL, Allowed );
-
- if (updateOpacity_) updateOpacity();
- if (isModal() && transientFor())
- {
- if (!act) transientFor()->updateOpacity();
- else if (!transientFor()->custom_opacity) transientFor()->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
- }
- updateShadowSize();
-
- if ( active )
- {
- Notify::raise( Notify::Activate );
- if (options->shadowEnabled(true))
- {
- if (options->shadowEnabled(false))
- {
- // Wait for inactive shadow to expose occluded windows and give
- // them a chance to redraw before painting the active shadow
- removeShadow();
- drawDelayedShadow();
- if (!isDesktop() &&
- this != workspace()->topClientOnDesktop(desktop()))
- // If the newly activated window's isn't the desktop, wait
- // for its shadow to draw, then redraw any shadows
- // overlapping it.
- drawOverlappingShadows(true);
- }
- else
- drawShadow();
- }
- }
- else
- {
- removeShadow();
-
- if (options->shadowEnabled(false))
- if (this == workspace()->topClientOnDesktop(desktop()))
- {
- /* If the newly deactivated window is the top client on the
- * desktop, then the newly activated window is below it; ensure
- * that the deactivated window's shadow draws after the
- * activated window's shadow.
- */
- if ((shadowAfterClient = workspace()->activeClient()))
- drawShadowAfter(shadowAfterClient);
- }
- else
- drawDelayedShadow();
- }
-
- if( !active )
- cancelAutoRaise();
-
- if( !active && shade_mode == ShadeActivated )
- setShade( ShadeNormal );
-
- StackingUpdatesBlocker blocker( workspace());
- workspace()->updateClientLayer( this ); // active windows may get different layer
- // TODO optimize? mainClients() may be a bit expensive
- ClientList mainclients = mainClients();
- for( ClientList::ConstIterator it = mainclients.begin();
- it != mainclients.end();
- ++it )
- if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active
- workspace()->updateClientLayer( *it );
- if( decoration != NULL )
- decoration->activeChange();
- updateMouseGrab();
- updateUrgency(); // demand attention again if it's still urgent
- }
-
-void Client::startupIdChanged()
- {
- KStartupInfoId asn_id;
- KStartupInfoData asn_data;
- bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
- if( !asn_valid )
- return;
- // If the ASN contains desktop, move it to the desktop, otherwise move it to the current
- // desktop (since the new ASN should make the window act like if it's a new application
- // launched). However don't affect the window's desktop if it's set to be on all desktops.
- int desktop = workspace()->currentDesktop();
- if( asn_data.desktop() != 0 )
- desktop = asn_data.desktop();
- if( !isOnAllDesktops())
- workspace()->sendClientToDesktop( this, desktop, true );
- if( asn_data.xinerama() != -1 )
- workspace()->sendClientToScreen( this, asn_data.xinerama());
- Time timestamp = asn_id.timestamp();
- if( timestamp == 0 && asn_data.timestamp() != -1U )
- timestamp = asn_data.timestamp();
- if( timestamp != 0 )
- {
- bool activate = workspace()->allowClientActivation( this, timestamp );
- if( asn_data.desktop() != 0 && !isOnCurrentDesktop())
- activate = false; // it was started on different desktop than current one
- if( activate )
- workspace()->activateClient( this );
- else
- demandAttention();
- }
- }
-
-void Client::updateUrgency()
- {
- if( urgency )
- demandAttention();
- }
-
-void Client::shortcutActivated()
- {
- workspace()->activateClient( this, true ); // force
- }
-
-//****************************************
-// Group
-//****************************************
-
-void Group::startupIdChanged()
- {
- KStartupInfoId asn_id;
- KStartupInfoData asn_data;
- bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data );
- if( !asn_valid )
- return;
- if( asn_id.timestamp() != 0 && user_time != -1U
- && timestampCompare( asn_id.timestamp(), user_time ) > 0 )
- {
- user_time = asn_id.timestamp();
- }
- else if( asn_data.timestamp() != -1U && user_time != -1U
- && timestampCompare( asn_data.timestamp(), user_time ) > 0 )
- {
- user_time = asn_data.timestamp();
- }
- }
-
-void Group::updateUserTime( Time time )
- { // copy of Client::updateUserTime
- if( time == CurrentTime )
- time = GET_QT_X_TIME();
- if( time != -1U
- && ( user_time == CurrentTime
- || timestampCompare( time, user_time ) > 0 )) // time > user_time
- user_time = time;
- }
-
-} // namespace