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

kwin

  • kwin
activation.cpp
1 /*****************************************************************
2  KWin - the KDE window manager
3  This file is part of the KDE project.
4 
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7 
8 You can Freely distribute this program under the GNU General Public
9 License. See the file "COPYING" for the exact licensing terms.
10 ******************************************************************/
11 
12 /*
13 
14  This file contains things relevant to window activation and focus
15  stealing prevention.
16 
17 */
18 
19 #include <tqpopupmenu.h>
20 #include <kxerrorhandler.h>
21 #include <kstartupinfo.h>
22 #include <kstringhandler.h>
23 #include <klocale.h>
24 
25 #include "client.h"
26 #include "workspace.h"
27 #include <fixx11h.h>
28 
29 #include "notifications.h"
30 #include "atoms.h"
31 #include "group.h"
32 #include "rules.h"
33 
34 namespace KWinInternal
35 {
36 
37 /*
38  Prevention of focus stealing:
39 
40  KWin tries to prevent unwanted changes of focus, that would result
41  from mapping a new window. Also, some nasty applications may try
42  to force focus change even in cases when ICCCM 4.2.7 doesn't allow it
43  (e.g. they may try to activate their main window because the user
44  definitely "needs" to see something happened - misusing
45  of TQWidget::setActiveWindow() may be such case).
46 
47  There are 4 ways how a window may become active:
48  - the user changes the active window (e.g. focus follows mouse, clicking
49  on some window's titlebar) - the change of focus will
50  be done by KWin, so there's nothing to solve in this case
51  - the change of active window will be requested using the _NET_ACTIVE_WINDOW
52  message (handled in RootInfo::changeActiveWindow()) - such requests
53  will be obeyed, because this request is meant mainly for e.g. taskbar
54  asking the WM to change the active window as a result of some user action.
55  Normal applications should use this request only rarely in special cases.
56  See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER.
57  - the change of active window will be done by performing XSetInputFocus()
58  on a window that's not currently active. ICCCM 4.2.7 describes when
59  the application may perform change of input focus. In order to handle
60  misbehaving applications, KWin will try to detect focus changes to
61  windows that don't belong to currently active application, and restore
62  focus back to the currently active window, instead of activating the window
63  that got focus (unfortunately there's no way to FocusChangeRedirect similar
64  to e.g. SubstructureRedirect, so there will be short time when the focus
65  will be changed). The check itself that's done is
66  Workspace::allowClientActivation() (see below).
67  - a new window will be mapped - this is the most complicated case. If
68  the new window belongs to the currently active application, it may be safely
69  mapped on top and activated. The same if there's no active window,
70  or the active window is the desktop. These checks are done by
71  Workspace::allowClientActivation().
72  Following checks need to compare times. One time is the timestamp
73  of last user action in the currently active window, the other time is
74  the timestamp of the action that originally caused mapping of the new window
75  (e.g. when the application was started). If the first time is newer than
76  the second one, the window will not be activated, as that indicates
77  futher user actions took place after the action leading to this new
78  mapped window. This check is done by Workspace::allowClientActivation().
79  There are several ways how to get the timestamp of action that caused
80  the new mapped window (done in Client::readUserTimeMapTimestamp()) :
81  - the window may have the _NET_WM_USER_TIME property. This way
82  the application may either explicitly request that the window is not
83  activated (by using 0 timestamp), or the property contains the time
84  of last user action in the application.
85  - KWin itself tries to detect time of last user action in every window,
86  by watching KeyPress and ButtonPress events on windows. This way some
87  events may be missed (if they don't propagate to the toplevel window),
88  but it's good as a fallback for applications that don't provide
89  _NET_WM_USER_TIME, and missing some events may at most lead
90  to unwanted focus stealing.
91  - the timestamp may come from application startup notification.
92  Application startup notification, if it exists for the new mapped window,
93  should include time of the user action that caused it.
94  - if there's no timestamp available, it's checked whether the new window
95  belongs to some already running application - if yes, the timestamp
96  will be 0 (i.e. refuse activation)
97  - if the window is from session restored window, the timestamp will
98  be 0 too, unless this application was the active one at the time
99  when the session was saved, in which case the window will be
100  activated if there wasn't any user interaction since the time
101  KWin was started.
102  - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp
103  is used. For every toplevel window that is created (see CreateNotify
104  handling), this property is set to the at that time current time.
105  Since at this time it's known that the new window doesn't belong
106  to any existing application (better said, the application doesn't
107  have any other window mapped), it is either the very first window
108  of the application, or its the only window of the application
109  that was hidden before. The latter case is handled by removing
110  the property from windows before withdrawing them, making
111  the timestamp empty for next mapping of the window. In the sooner
112  case, the timestamp will be used. This helps in case when
113  an application is launched without application startup notification,
114  it creates its mainwindow, and starts its initialization (that
115  may possibly take long time). The timestamp used will be older
116  than any user action done after launching this application.
117  - if no timestamp is found at all, the window is activated.
118  The check whether two windows belong to the same application (same
119  process) is done in Client::belongToSameApplication(). Not 100% reliable,
120  but hopefully 99,99% reliable.
121 
122  As a somewhat special case, window activation is always enabled when
123  session saving is in progress. When session saving, the session
124  manager allows only one application to interact with the user.
125  Not allowing window activation in such case would result in e.g. dialogs
126  not becoming active, so focus stealing prevention would cause here
127  more harm than good.
128 
129  Windows that attempted to become active but KWin prevented this will
130  be marked as demanding user attention. They'll get
131  the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark
132  them specially (blink, etc.). The state will be reset when the window
133  eventually really becomes active.
134 
135  There are one more ways how a window can become obstrusive, window stealing
136  focus: By showing above the active window, by either raising itself,
137  or by moving itself on the active desktop.
138  - KWin will refuse raising non-active window above the active one,
139  unless they belong to the same application. Applications shouldn't
140  raise their windows anyway (unless the app wants to raise one
141  of its windows above another of its windows).
142  - KWin activates windows moved to the current desktop (as that seems
143  logical from the user's point of view, after sending the window
144  there directly from KWin, or e.g. using pager). This means
145  applications shouldn't send their windows to another desktop
146  (SELI TODO - but what if they do?)
147 
148  Special cases I can think of:
149  - konqueror reusing, i.e. kfmclient tells running Konqueror instance
150  to open new window
151  - without focus stealing prevention - no problem
152  - with ASN (application startup notification) - ASN is forwarded,
153  and because it's newer than the instance's user timestamp,
154  it takes precedence
155  - without ASN - user timestamp needs to be reset, otherwise it would
156  be used, and it's old; moreover this new window mustn't be detected
157  as window belonging to already running application, or it wouldn't
158  be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly)
159  hack
160  - konqueror preloading, i.e. window is created in advance, and kfmclient
161  tells this Konqueror instance to show it later
162  - without focus stealing prevention - no problem
163  - with ASN - ASN is forwarded, and because it's newer than the instance's
164  user timestamp, it takes precedence
165  - without ASN - user timestamp needs to be reset, otherwise it would
166  be used, and it's old; also, creation timestamp is changed to
167  the time the instance starts (re-)initializing the window,
168  this ensures creation timestamp will still work somewhat even in this case
169  - KUniqueApplication - when the window is already visible, and the new instance
170  wants it to activate
171  - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
172  - with ASN - ASN is forwarded, and set on the already visible window, KWin
173  treats the window as new with that ASN
174  - without ASN - _NET_ACTIVE_WINDOW as application request is used,
175  and there's no really usable timestamp, only timestamp
176  from the time the (new) application instance was started,
177  so KWin will activate the window *sigh*
178  - the bad thing here is that there's absolutely no chance to recognize
179  the case of starting this KUniqueApp from Konsole (and thus wanting
180  the already visible window to become active) from the case
181  when something started this KUniqueApp without ASN (in which case
182  the already visible window shouldn't become active)
183  - the only solution is using ASN for starting applications, at least silent
184  (i.e. without feedback)
185  - when one application wants to activate another application's window (e.g. KMail
186  activating already running KAddressBook window ?)
187  - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
188  - with ASN - can't be here, it's the KUniqueApp case then
189  - without ASN - _NET_ACTIVE_WINDOW as application request should be used,
190  KWin will activate the new window depending on the timestamp and
191  whether it belongs to the currently active application
192 
193  _NET_ACTIVE_WINDOW usage:
194  data.l[0]= 1 ->app request
195  = 2 ->pager request
196  = 0 - backwards compatibility
197  data.l[1]= timestamp
198 */
199 
200 
201 //****************************************
202 // Workspace
203 //****************************************
204 
205 
214 void Workspace::setActiveClient( Client* c, allowed_t )
215  {
216  if ( active_client == c )
217  return;
218  if( active_popup && active_popup_client != c && set_active_client_recursion == 0 )
219  closeActivePopup();
220  StackingUpdatesBlocker blocker( this );
221  ++set_active_client_recursion;
222  updateFocusMousePosition( TQCursor::pos());
223  if( active_client != NULL )
224  { // note that this may call setActiveClient( NULL ), therefore the recursion counter
225  active_client->setActive( false, !c || !c->isModal() || c != active_client->transientFor() );
226  }
227  active_client = c;
228  if (set_active_client_recursion == 1)
229  {
230  // Only unset next_active_client if activateClient() wasn't called by
231  // Client::setActive() to set the active window to null before
232  // activating another window.
233  next_active_client = NULL;
234  }
235  Q_ASSERT( c == NULL || c->isActive());
236  if( active_client != NULL )
237  last_active_client = active_client;
238  if ( active_client )
239  {
240  updateFocusChains( active_client, FocusChainMakeFirst );
241  active_client->demandAttention( false );
242  }
243  pending_take_activity = NULL;
244 
245  updateCurrentTopMenu();
246  updateToolWindows( false );
247  if( c )
248  disableGlobalShortcutsForClient( c->rules()->checkDisableGlobalShortcuts( false ));
249  else
250  disableGlobalShortcutsForClient( false );
251 
252  updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
253 
254  rootInfo->setActiveWindow( active_client? active_client->window() : 0 );
255  updateColormap();
256  --set_active_client_recursion;
257  }
258 
270 void Workspace::activateClient( Client* c, bool force )
271  {
272  if( c == NULL )
273  {
274  focusToNull();
275  setActiveClient( NULL, Allowed );
276  return;
277  }
278  raiseClient( c );
279  if (!c->isOnDesktop(currentDesktop()) )
280  {
281  ++block_focus;
282  setCurrentDesktop( c->desktop() );
283  --block_focus;
284  }
285  if( c->isMinimized())
286  c->unminimize();
287 
288 // TODO force should perhaps allow this only if the window already contains the mouse
289  if( options->focusPolicyIsReasonable() || force )
290  requestFocus( c, force );
291 
292  // Don't update user time for clients that have focus stealing workaround.
293  // As they usually belong to the current active window but fail to provide
294  // this information, updating their user time would make the user time
295  // of the currently active window old, and reject further activation for it.
296  // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround),
297  // and then kdesktop shows dialog about SSL certificate.
298  // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp().
299  if( !c->ignoreFocusStealing())
300  c->updateUserTime();
301  }
302 
310 void Workspace::requestFocus( Client* c, bool force )
311  {
312  takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false);
313  }
314 
315 void Workspace::takeActivity( Client* c, int flags, bool handled )
316  {
317  // the 'if( c == active_client ) return;' optimization mustn't be done here
318  if (!focusChangeEnabled() && ( c != active_client) )
319  flags &= ~ActivityFocus;
320 
321  if ( !c )
322  {
323  focusToNull();
324  return;
325  }
326 
327  if( flags & ActivityFocus )
328  {
329  Client* modal = c->findModal();
330  if( modal != NULL && modal != c )
331  {
332  next_active_client = modal;
333  if( !modal->isOnDesktop( c->desktop()))
334  {
335  modal->setDesktop( c->desktop());
336  if( modal->desktop() != c->desktop()) // forced desktop
337  activateClient( modal );
338  }
339  // if the click was inside the window (i.e. handled is set),
340  // but it has a modal, there's no need to use handled mode, because
341  // the modal doesn't get the click anyway
342  // raising of the original window needs to be still done
343  if( flags & ActivityRaise )
344  raiseClient( c );
345  c = modal;
346  handled = false;
347  }
348  cancelDelayFocus();
349  }
350  if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) )
351  flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced
352  if( c->isShade())
353  {
354  if( c->wantsInput() && ( flags & ActivityFocus ))
355  {
356  // client cannot accept focus, but at least the window should be active (window menu, et. al. )
357  c->setActive( true );
358  focusToNull();
359  }
360  if( c->wantsInput())
361  next_active_client = c;
362  flags &= ~ActivityFocus;
363  handled = false; // no point, can't get clicks
364  }
365  if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed
366  {
367  next_active_client = c;
368  kdWarning( 1212 ) << "takeActivity: not shown" << endl;
369  return;
370  }
371  c->takeActivity( flags, handled, Allowed );
372  if( !c->isOnScreen( active_screen ))
373  active_screen = c->screen();
374  }
375 
376 void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags )
377  {
378  if( pending_take_activity != c ) // pending_take_activity is reset when doing restack or activation
379  return;
380  if(( flags & ActivityRaise ) != 0 )
381  raiseClient( c );
382  if(( flags & ActivityFocus ) != 0 && c->isShown( false ))
383  c->takeFocus( Allowed );
384  pending_take_activity = NULL;
385  }
386 
394 void Workspace::clientHidden( Client* c )
395  {
396  assert( !c->isShown( true ) || !c->isOnCurrentDesktop());
397  activateNextClient( c );
398  }
399 
400 // deactivates 'c' and activates next client
401 bool Workspace::activateNextClient( Client* c )
402  {
403  // if 'c' is not the active or the to-become active one, do nothing
404  if( !( c == active_client
405  || ( should_get_focus.count() > 0 && c == should_get_focus.last())))
406  return false;
407  closeActivePopup();
408  if( c != NULL )
409  {
410  if( c == active_client )
411  setActiveClient( NULL, Allowed );
412  should_get_focus.remove( c );
413  }
414  if( focusChangeEnabled())
415  {
416  if ( options->focusPolicyIsReasonable())
417  { // search the focus_chain for a client to transfer focus to
418  // if 'c' is transient, transfer focus to the first suitable mainwindow
419  Client* get_focus = NULL;
420  const ClientList mainwindows = ( c != NULL ? c->mainClients() : ClientList());
421  for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
422  it != focus_chain[currentDesktop()].end();
423  --it )
424  {
425  if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
426  continue;
427  if( options->separateScreenFocus )
428  {
429  if( c != NULL && !(*it)->isOnScreen( c->screen()))
430  continue;
431  if( c == NULL && !(*it)->isOnScreen( activeScreen()))
432  continue;
433  }
434  if( mainwindows.contains( *it ))
435  {
436  get_focus = *it;
437  break;
438  }
439  if( get_focus == NULL )
440  get_focus = *it;
441  }
442  if( get_focus == NULL )
443  get_focus = findDesktop( true, currentDesktop());
444  if( get_focus != NULL )
445  requestFocus( get_focus );
446  else
447  focusToNull();
448  }
449  else
450  return false;
451  }
452  else
453  // if blocking focus, move focus to the desktop later if needed
454  // in order to avoid flickering
455  focusToNull();
456  return true;
457  }
458 
459 void Workspace::setCurrentScreen( int new_screen )
460  {
461  if (new_screen < 0 || new_screen > numScreens())
462  return;
463  if ( !options->focusPolicyIsReasonable())
464  return;
465  closeActivePopup();
466  Client* get_focus = NULL;
467  for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
468  it != focus_chain[currentDesktop()].end();
469  --it )
470  {
471  if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
472  continue;
473  if( !(*it)->screen() == new_screen )
474  continue;
475  get_focus = *it;
476  break;
477  }
478  if( get_focus == NULL )
479  get_focus = findDesktop( true, currentDesktop());
480  if( get_focus != NULL && get_focus != mostRecentlyActivatedClient())
481  requestFocus( get_focus );
482  active_screen = new_screen;
483  }
484 
485 void Workspace::gotFocusIn( const Client* c )
486  {
487  if( should_get_focus.contains( const_cast< Client* >( c )))
488  { // remove also all sooner elements that should have got FocusIn,
489  // but didn't for some reason (and also won't anymore, because they were sooner)
490  while( should_get_focus.first() != c )
491  should_get_focus.pop_front();
492  should_get_focus.pop_front(); // remove 'c'
493  }
494  }
495 
496 void Workspace::setShouldGetFocus( Client* c )
497  {
498  should_get_focus.append( c );
499  updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
500  }
501 
502 // focus_in -> the window got FocusIn event
503 // session_active -> the window was active when saving session
504 bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in )
505  {
506  // options->focusStealingPreventionLevel :
507  // 0 - none - old KWin behaviour, new windows always get focus
508  // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed
509  // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed,
510  // this is the default
511  // 3 - high - new window gets focus only if it belongs to the active application,
512  // or when no window is currently active
513  // 4 - extreme - no window gets focus without user intervention
514  if( time == -1U )
515  time = c->userTime();
516  int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
517  if( session_saving && level <= 2 ) // <= normal
518  {
519  return true;
520  }
521  Client* ac = mostRecentlyActivatedClient();
522  if( focus_in )
523  {
524  if( should_get_focus.contains( const_cast< Client* >( c )))
525  return true; // FocusIn was result of KWin's action
526  // Before getting FocusIn, the active Client already
527  // got FocusOut, and therefore got deactivated.
528  ac = last_active_client;
529  }
530  if( time == 0 ) // explicitly asked not to get focus
531  return false;
532  if( level == 0 ) // none
533  return true;
534  if( level == 4 ) // extreme
535  return false;
536  if( !c->isOnCurrentDesktop())
537  return false; // allow only with level == 0
538  if( c->ignoreFocusStealing())
539  return true;
540  if( ac == NULL || ac->isDesktop())
541  {
542 // kdDebug( 1212 ) << "Activation: No client active, allowing" << endl;
543  return true; // no active client -> always allow
544  }
545  // TODO window urgency -> return true?
546  if( Client::belongToSameApplication( c, ac, true ))
547  {
548 // kdDebug( 1212 ) << "Activation: Belongs to active application" << endl;
549  return true;
550  }
551  if( level == 3 ) // high
552  return false;
553  if( time == -1U ) // no time known
554  {
555 // kdDebug( 1212 ) << "Activation: No timestamp at all" << endl;
556  if( level == 1 ) // low
557  return true;
558  // no timestamp at all, don't activate - because there's also creation timestamp
559  // done on CreateNotify, this case should happen only in case application
560  // maps again already used window, i.e. this won't happen after app startup
561  return false;
562  }
563  // level == 2 // normal
564  Time user_time = ac->userTime();
565 // kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time
566 // << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
567  return timestampCompare( time, user_time ) >= 0; // time >= user_time
568  }
569 
570 // basically the same like allowClientActivation(), this time allowing
571 // a window to be fully raised upon its own request (XRaiseWindow),
572 // if refused, it will be raised only on top of windows belonging
573 // to the same application
574 bool Workspace::allowFullClientRaising( const Client* c, Time time )
575  {
576  int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
577  if( session_saving && level <= 2 ) // <= normal
578  {
579  return true;
580  }
581  Client* ac = mostRecentlyActivatedClient();
582  if( level == 0 ) // none
583  return true;
584  if( level == 4 ) // extreme
585  return false;
586  if( ac == NULL || ac->isDesktop())
587  {
588 // kdDebug( 1212 ) << "Raising: No client active, allowing" << endl;
589  return true; // no active client -> always allow
590  }
591  if( c->ignoreFocusStealing())
592  return true;
593  // TODO window urgency -> return true?
594  if( Client::belongToSameApplication( c, ac, true ))
595  {
596 // kdDebug( 1212 ) << "Raising: Belongs to active application" << endl;
597  return true;
598  }
599  if( level == 3 ) // high
600  return false;
601  Time user_time = ac->userTime();
602 // kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time
603 // << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
604  return timestampCompare( time, user_time ) >= 0; // time >= user_time
605  }
606 
607 // called from Client after FocusIn that wasn't initiated by KWin and the client
608 // wasn't allowed to activate
609 void Workspace::restoreFocus()
610  {
611  // this updateXTime() is necessary - as FocusIn events don't have
612  // a timestamp *sigh*, kwin's timestamp would be older than the timestamp
613  // that was used by whoever caused the focus change, and therefore
614  // the attempt to restore the focus would fail due to old timestamp
615  updateXTime();
616  if( should_get_focus.count() > 0 )
617  requestFocus( should_get_focus.last());
618  else if( last_active_client )
619  requestFocus( last_active_client );
620  }
621 
622 void Workspace::clientAttentionChanged( Client* c, bool set )
623  {
624  if( set )
625  {
626  attention_chain.remove( c );
627  attention_chain.prepend( c );
628  }
629  else
630  attention_chain.remove( c );
631  }
632 
633 // This is used when a client should be shown active immediately after requestFocus(),
634 // without waiting for the matching FocusIn that will really make the window the active one.
635 // Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows,
636 bool Workspace::fakeRequestedActivity( Client* c )
637  {
638  if( should_get_focus.count() > 0 && should_get_focus.last() == c )
639  {
640  if( c->isActive())
641  return false;
642  c->setActive( true );
643  return true;
644  }
645  return false;
646  }
647 
648 void Workspace::unfakeActivity( Client* c )
649  {
650  if( should_get_focus.count() > 0 && should_get_focus.last() == c )
651  { // TODO this will cause flicker, and probably is not needed
652  if( last_active_client != NULL )
653  last_active_client->setActive( true );
654  else
655  c->setActive( false );
656  }
657  }
658 
659 
660 //********************************************
661 // Client
662 //********************************************
663 
670 void Client::updateUserTime( Time time )
671  { // copied in Group::updateUserTime
672  if( time == CurrentTime )
673  time = GET_QT_X_TIME();
674  if( time != -1U
675  && ( user_time == CurrentTime
676  || timestampCompare( time, user_time ) > 0 )) // time > user_time
677  user_time = time;
678  group()->updateUserTime( user_time );
679  }
680 
681 Time Client::readUserCreationTime() const
682  {
683  long result = -1; // Time == -1 means none
684  Atom type;
685  int format, status;
686  unsigned long nitems = 0;
687  unsigned long extra = 0;
688  unsigned char *data = 0;
689  KXErrorHandler handler; // ignore errors?
690  status = XGetWindowProperty( qt_xdisplay(), window(),
691  atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL,
692  &type, &format, &nitems, &extra, &data );
693  if (status == Success )
694  {
695  if (data && nitems > 0)
696  result = *((long*) data);
697  XFree(data);
698  }
699  return result;
700  }
701 
702 void Client::demandAttention( bool set )
703  {
704  if( isActive())
705  set = false;
706  if( demands_attention == set )
707  return;
708  demands_attention = set;
709  if( demands_attention )
710  {
711  // Demand attention flag is often set right from manage(), when focus stealing prevention
712  // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place
713  // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry
714  // to be set.
715  // Delayed call to KNotify also solves the problem of having X server grab in manage(),
716  // which may deadlock when KNotify (or KLauncher when launching KNotify) need to access X.
717  Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
718  // Setting the demands attention state needs to be done directly in KWin, because
719  // KNotify would try to set it, resulting in a call to KNotify again, etc.
720  if( Notify::makeDemandAttention( e ))
721  info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
722 
723  if( demandAttentionKNotifyTimer == NULL )
724  {
725  demandAttentionKNotifyTimer = new TQTimer( this );
726  connect( demandAttentionKNotifyTimer, TQT_SIGNAL( timeout()), TQT_SLOT( demandAttentionKNotify()));
727  }
728  demandAttentionKNotifyTimer->start( 1000, true );
729  }
730  else
731  info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
732  workspace()->clientAttentionChanged( this, set );
733  }
734 
735 void Client::demandAttentionKNotify()
736  {
737  Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
738  Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this );
739  demandAttentionKNotifyTimer->stop();
740  demandAttentionKNotifyTimer->deleteLater();
741  demandAttentionKNotifyTimer = NULL;
742  }
743 
744 // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it
745 KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*,
746  // ignore already existing splashes, toolbars, utilities, menus and topmenus,
747  // as the app may show those before the main window
748  !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu()
749  && Client::belongToSameApplication( cl, value, true ) && cl != value);
750 
751 Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data,
752  bool session ) const
753  {
754  Time time = info->userTime();
755 // kdDebug( 1212 ) << "User timestamp, initial:" << time << endl;
756  // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0
757  // helps e.g. with konqy reusing
758  if( asn_data != NULL && time != 0 )
759  {
760  // prefer timestamp from ASN id (timestamp from data is obsolete way)
761  if( asn_id->timestamp() != 0
762  && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 ))
763  {
764  time = asn_id->timestamp();
765  }
766  else if( asn_data->timestamp() != -1U
767  && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 ))
768  {
769  time = asn_data->timestamp();
770  }
771  }
772 // kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl;
773  if( time == -1U )
774  { // The window doesn't have any timestamp.
775  // If it's the first window for its application
776  // (i.e. there's no other window from the same app),
777  // use the _KDE_NET_WM_USER_CREATION_TIME trick.
778  // Otherwise, refuse activation of a window
779  // from already running application if this application
780  // is not the active one (unless focus stealing prevention is turned off).
781  Client* act = workspace()->mostRecentlyActivatedClient();
782  if( act != NULL && !belongToSameApplication( act, this, true ))
783  {
784  bool first_window = true;
785  if( isTransient())
786  {
787  if( act->hasTransient( this, true ))
788  ; // is transient for currently active window, even though it's not
789  // the same app (e.g. kcookiejar dialog) -> allow activation
790  else if( groupTransient() &&
791  findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL )
792  ; // standalone transient
793  else
794  first_window = false;
795  }
796  else
797  {
798  if( workspace()->findClient( SameApplicationActiveHackPredicate( this )))
799  first_window = false;
800  }
801  // don't refuse if focus stealing prevention is turned off
802  if( !first_window && rules()->checkFSP( options->focusStealingPreventionLevel ) > 0 )
803  {
804 // kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl;
805  return 0; // refuse activation
806  }
807  }
808  // Creation time would just mess things up during session startup,
809  // as possibly many apps are started up at the same time.
810  // If there's no active window yet, no timestamp will be needed,
811  // as plain Workspace::allowClientActivation() will return true
812  // in such case. And if there's already active window,
813  // it's better not to activate the new one.
814  // Unless it was the active window at the time
815  // of session saving and there was no user interaction yet,
816  // this check will be done in manage().
817  if( session )
818  return -1U;
819  if( ignoreFocusStealing() && act != NULL )
820  time = act->userTime();
821  else
822  time = readUserCreationTime();
823  }
824 // kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl;
825  return time;
826  }
827 
828 Time Client::userTime() const
829  {
830  Time time = user_time;
831  if( time == 0 ) // doesn't want focus after showing
832  return 0;
833  assert( group() != NULL );
834  if( time == -1U
835  || ( group()->userTime() != -1U
836  && timestampCompare( group()->userTime(), time ) > 0 ))
837  time = group()->userTime();
838  return time;
839  }
840 
852 void Client::setActive( bool act, bool updateOpacity_)
853  {
854  if ( active == act )
855  return;
856  active = act;
857  workspace()->setActiveClient( act ? this : NULL, Allowed );
858 
859  if (updateOpacity_) updateOpacity();
860  if (isModal() && transientFor())
861  {
862  if (!act) transientFor()->updateOpacity();
863  else if (!transientFor()->custom_opacity) transientFor()->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
864  }
865  updateShadowSize();
866 
867  if ( active )
868  {
869  Notify::raise( Notify::Activate );
870  if (options->shadowEnabled(true))
871  {
872  if (options->shadowEnabled(false))
873  {
874  // Wait for inactive shadow to expose occluded windows and give
875  // them a chance to redraw before painting the active shadow
876  removeShadow();
877  drawDelayedShadow();
878  if (!isDesktop() &&
879  this != workspace()->topClientOnDesktop(desktop()))
880  // If the newly activated window's isn't the desktop, wait
881  // for its shadow to draw, then redraw any shadows
882  // overlapping it.
883  drawOverlappingShadows(true);
884  }
885  else
886  drawShadow();
887  }
888  }
889  else
890  {
891  removeShadow();
892 
893  if (options->shadowEnabled(false))
894  if (this == workspace()->topClientOnDesktop(desktop()))
895  {
896  /* If the newly deactivated window is the top client on the
897  * desktop, then the newly activated window is below it; ensure
898  * that the deactivated window's shadow draws after the
899  * activated window's shadow.
900  */
901  if ((shadowAfterClient = workspace()->activeClient()))
902  drawShadowAfter(shadowAfterClient);
903  }
904  else
905  drawDelayedShadow();
906  }
907 
908  if( !active )
909  cancelAutoRaise();
910 
911  if( !active && shade_mode == ShadeActivated )
912  setShade( ShadeNormal );
913 
914  StackingUpdatesBlocker blocker( workspace());
915  workspace()->updateClientLayer( this ); // active windows may get different layer
916  // TODO optimize? mainClients() may be a bit expensive
917  ClientList mainclients = mainClients();
918  for( ClientList::ConstIterator it = mainclients.begin();
919  it != mainclients.end();
920  ++it )
921  if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active
922  workspace()->updateClientLayer( *it );
923  if( decoration != NULL )
924  decoration->activeChange();
925  updateMouseGrab();
926  updateUrgency(); // demand attention again if it's still urgent
927  }
928 
929 void Client::startupIdChanged()
930  {
931  KStartupInfoId asn_id;
932  KStartupInfoData asn_data;
933  bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
934  if( !asn_valid )
935  return;
936  // If the ASN contains desktop, move it to the desktop, otherwise move it to the current
937  // desktop (since the new ASN should make the window act like if it's a new application
938  // launched). However don't affect the window's desktop if it's set to be on all desktops.
939  int desktop = workspace()->currentDesktop();
940  if( asn_data.desktop() != 0 )
941  desktop = asn_data.desktop();
942  if( !isOnAllDesktops())
943  workspace()->sendClientToDesktop( this, desktop, true );
944  if( asn_data.xinerama() != -1 )
945  workspace()->sendClientToScreen( this, asn_data.xinerama());
946  Time timestamp = asn_id.timestamp();
947  if( timestamp == 0 && asn_data.timestamp() != -1U )
948  timestamp = asn_data.timestamp();
949  if( timestamp != 0 )
950  {
951  bool activate = workspace()->allowClientActivation( this, timestamp );
952  if( asn_data.desktop() != 0 && !isOnCurrentDesktop())
953  activate = false; // it was started on different desktop than current one
954  if( activate )
955  workspace()->activateClient( this );
956  else
957  demandAttention();
958  }
959  }
960 
961 void Client::updateUrgency()
962  {
963  if( urgency )
964  demandAttention();
965  }
966 
967 void Client::shortcutActivated()
968  {
969  workspace()->activateClient( this, true ); // force
970  }
971 
972 //****************************************
973 // Group
974 //****************************************
975 
976 void Group::startupIdChanged()
977  {
978  KStartupInfoId asn_id;
979  KStartupInfoData asn_data;
980  bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data );
981  if( !asn_valid )
982  return;
983  if( asn_id.timestamp() != 0 && user_time != -1U
984  && timestampCompare( asn_id.timestamp(), user_time ) > 0 )
985  {
986  user_time = asn_id.timestamp();
987  }
988  else if( asn_data.timestamp() != -1U && user_time != -1U
989  && timestampCompare( asn_data.timestamp(), user_time ) > 0 )
990  {
991  user_time = asn_data.timestamp();
992  }
993  }
994 
995 void Group::updateUserTime( Time time )
996  { // copy of Client::updateUserTime
997  if( time == CurrentTime )
998  time = GET_QT_X_TIME();
999  if( time != -1U
1000  && ( user_time == CurrentTime
1001  || timestampCompare( time, user_time ) > 0 )) // time > user_time
1002  user_time = time;
1003  }
1004 
1005 } // 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.8.3.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |