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

twin

  • twin
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 <tdestartupinfo.h>
22 #include <kstringhandler.h>
23 #include <tdelocale.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  - TDEUniqueApplication - 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 tdeio_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 must not 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  {
412  setActiveClient( NULL, Allowed );
413  }
414  should_get_focus.remove( c );
415  }
416  if( focusChangeEnabled())
417  {
418  if ( options->focusPolicyIsReasonable())
419  { // search the focus_chain for a client to transfer focus to
420  // if 'c' is transient, transfer focus to the first suitable mainwindow
421  Client* get_focus = NULL;
422  const ClientList mainwindows = ( c != NULL ? c->mainClients() : ClientList());
423  for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
424  it != focus_chain[currentDesktop()].end();
425  --it )
426  {
427  if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
428  continue;
429  if( options->separateScreenFocus )
430  {
431  if( c != NULL && !(*it)->isOnScreen( c->screen()))
432  continue;
433  if( c == NULL && !(*it)->isOnScreen( activeScreen()))
434  continue;
435  }
436  if( mainwindows.contains( *it ))
437  {
438  get_focus = *it;
439  break;
440  }
441  if( get_focus == NULL )
442  get_focus = *it;
443  }
444  if( get_focus == NULL )
445  get_focus = findDesktop( true, currentDesktop());
446  if( get_focus != NULL )
447  {
448  requestFocus( get_focus );
449  }
450  else
451  focusToNull();
452  }
453  else
454  return false;
455  }
456  else
457  // if blocking focus, move focus to the desktop later if needed
458  // in order to avoid flickering
459  focusToNull();
460  return true;
461  }
462 
463 void Workspace::setCurrentScreen( int new_screen )
464  {
465  if (new_screen < 0 || new_screen > numScreens())
466  return;
467  if ( !options->focusPolicyIsReasonable())
468  return;
469  closeActivePopup();
470  Client* get_focus = NULL;
471  for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
472  it != focus_chain[currentDesktop()].end();
473  --it )
474  {
475  if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
476  continue;
477  if( !(*it)->screen() == new_screen )
478  continue;
479  get_focus = *it;
480  break;
481  }
482  if( get_focus == NULL )
483  get_focus = findDesktop( true, currentDesktop());
484  if( get_focus != NULL && get_focus != mostRecentlyActivatedClient())
485  requestFocus( get_focus );
486  active_screen = new_screen;
487  }
488 
489 void Workspace::gotFocusIn( const Client* c )
490  {
491  if( should_get_focus.contains( const_cast< Client* >( c )))
492  { // remove also all sooner elements that should have got FocusIn,
493  // but didn't for some reason (and also won't anymore, because they were sooner)
494  while( should_get_focus.first() != c )
495  should_get_focus.pop_front();
496  should_get_focus.pop_front(); // remove 'c'
497  }
498  }
499 
500 void Workspace::setShouldGetFocus( Client* c )
501  {
502  should_get_focus.append( c );
503  updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
504  }
505 
506 // focus_in -> the window got FocusIn event
507 // session_active -> the window was active when saving session
508 bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in )
509  {
510  // options->focusStealingPreventionLevel :
511  // 0 - none - old KWin behaviour, new windows always get focus
512  // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed
513  // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed,
514  // this is the default
515  // 3 - high - new window gets focus only if it belongs to the active application,
516  // or when no window is currently active
517  // 4 - extreme - no window gets focus without user intervention
518  if( time == -1U )
519  time = c->userTime();
520  int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
521  if( session_saving && level <= 2 ) // <= normal
522  {
523  return true;
524  }
525  Client* ac = mostRecentlyActivatedClient();
526  if( focus_in )
527  {
528  if( should_get_focus.contains( const_cast< Client* >( c )))
529  return true; // FocusIn was result of KWin's action
530  // Before getting FocusIn, the active Client already
531  // got FocusOut, and therefore got deactivated.
532  ac = last_active_client;
533  }
534  if( time == 0 ) // explicitly asked not to get focus
535  return false;
536  if( level == 0 ) // none
537  return true;
538  if( level == 4 ) // extreme
539  return false;
540  if( !c->isOnCurrentDesktop())
541  return false; // allow only with level == 0
542  if( c->ignoreFocusStealing())
543  return true;
544  if( ac == NULL || ac->isDesktop())
545  {
546 // kdDebug( 1212 ) << "Activation: No client active, allowing" << endl;
547  return true; // no active client -> always allow
548  }
549  // TODO window urgency -> return true?
550  if( Client::belongToSameApplication( c, ac, true ))
551  {
552 // kdDebug( 1212 ) << "Activation: Belongs to active application" << endl;
553  return true;
554  }
555  if( level == 3 ) // high
556  return false;
557  if( time == -1U ) // no time known
558  {
559 // kdDebug( 1212 ) << "Activation: No timestamp at all" << endl;
560  if( level == 1 ) // low
561  return true;
562  // no timestamp at all, don't activate - because there's also creation timestamp
563  // done on CreateNotify, this case should happen only in case application
564  // maps again already used window, i.e. this won't happen after app startup
565  return false;
566  }
567  // level == 2 // normal
568  Time user_time = ac->userTime();
569 // kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time
570 // << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
571  return timestampCompare( time, user_time ) >= 0; // time >= user_time
572  }
573 
574 // basically the same like allowClientActivation(), this time allowing
575 // a window to be fully raised upon its own request (XRaiseWindow),
576 // if refused, it will be raised only on top of windows belonging
577 // to the same application
578 bool Workspace::allowFullClientRaising( const Client* c, Time time )
579  {
580  int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
581  if( session_saving && level <= 2 ) // <= normal
582  {
583  return true;
584  }
585  Client* ac = mostRecentlyActivatedClient();
586  if( level == 0 ) // none
587  return true;
588  if( level == 4 ) // extreme
589  return false;
590  if( ac == NULL || ac->isDesktop())
591  {
592 // kdDebug( 1212 ) << "Raising: No client active, allowing" << endl;
593  return true; // no active client -> always allow
594  }
595  if( c->ignoreFocusStealing())
596  return true;
597  // TODO window urgency -> return true?
598  if( Client::belongToSameApplication( c, ac, true ))
599  {
600 // kdDebug( 1212 ) << "Raising: Belongs to active application" << endl;
601  return true;
602  }
603  if( level == 3 ) // high
604  return false;
605  Time user_time = ac->userTime();
606 // kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time
607 // << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
608  return timestampCompare( time, user_time ) >= 0; // time >= user_time
609  }
610 
611 // called from Client after FocusIn that wasn't initiated by KWin and the client
612 // wasn't allowed to activate
613 void Workspace::restoreFocus()
614  {
615  // this updateXTime() is necessary - as FocusIn events don't have
616  // a timestamp *sigh*, twin's timestamp would be older than the timestamp
617  // that was used by whoever caused the focus change, and therefore
618  // the attempt to restore the focus would fail due to old timestamp
619  updateXTime();
620  if( should_get_focus.count() > 0 )
621  requestFocus( should_get_focus.last());
622  else if( last_active_client )
623  requestFocus( last_active_client );
624  }
625 
626 void Workspace::clientAttentionChanged( Client* c, bool set )
627  {
628  if( set )
629  {
630  attention_chain.remove( c );
631  attention_chain.prepend( c );
632  }
633  else
634  attention_chain.remove( c );
635  }
636 
637 // This is used when a client should be shown active immediately after requestFocus(),
638 // without waiting for the matching FocusIn that will really make the window the active one.
639 // Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows,
640 bool Workspace::fakeRequestedActivity( Client* c )
641  {
642  if( should_get_focus.count() > 0 && should_get_focus.last() == c )
643  {
644  if( c->isActive())
645  return false;
646  c->setActive( true );
647  return true;
648  }
649  return false;
650  }
651 
652 void Workspace::unfakeActivity( Client* c )
653  {
654  if( should_get_focus.count() > 0 && should_get_focus.last() == c )
655  { // TODO this will cause flicker, and probably is not needed
656  if( last_active_client != NULL )
657  last_active_client->setActive( true );
658  else
659  c->setActive( false );
660  }
661  }
662 
663 
664 //********************************************
665 // Client
666 //********************************************
667 
674 void Client::updateUserTime( Time time )
675  { // copied in Group::updateUserTime
676  if( time == CurrentTime )
677  time = get_tqt_x_time();
678  if( time != -1U
679  && ( user_time == CurrentTime
680  || timestampCompare( time, user_time ) > 0 )) // time > user_time
681  user_time = time;
682  group()->updateUserTime( user_time );
683  }
684 
685 Time Client::readUserCreationTime() const
686  {
687  long result = -1; // Time == -1 means none
688  Atom type;
689  int format, status;
690  unsigned long nitems = 0;
691  unsigned long extra = 0;
692  unsigned char *data = 0;
693  KXErrorHandler handler; // ignore errors?
694  status = XGetWindowProperty( tqt_xdisplay(), window(),
695  atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL,
696  &type, &format, &nitems, &extra, &data );
697  if (status == Success )
698  {
699  if (data && nitems > 0)
700  result = *((long*) data);
701  XFree(data);
702  }
703  return result;
704  }
705 
706 void Client::demandAttention( bool set )
707  {
708  if( isActive())
709  set = false;
710  if( demands_attention == set )
711  return;
712  demands_attention = set;
713  if( demands_attention )
714  {
715  // Demand attention flag is often set right from manage(), when focus stealing prevention
716  // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place
717  // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry
718  // to be set.
719  // Delayed call to KNotify also solves the problem of having X server grab in manage(),
720  // which may deadlock when KNotify (or TDELauncher when launching KNotify) need to access X.
721  Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
722  // Setting the demands attention state needs to be done directly in KWin, because
723  // KNotify would try to set it, resulting in a call to KNotify again, etc.
724  if( Notify::makeDemandAttention( e ))
725  info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
726 
727  if( demandAttentionKNotifyTimer == NULL )
728  {
729  demandAttentionKNotifyTimer = new TQTimer( this );
730  connect( demandAttentionKNotifyTimer, TQ_SIGNAL( timeout()), TQ_SLOT( demandAttentionKNotify()));
731  }
732  demandAttentionKNotifyTimer->start( 1000, true );
733  }
734  else
735  info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
736  workspace()->clientAttentionChanged( this, set );
737  }
738 
739 void Client::demandAttentionKNotify()
740  {
741  Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
742  Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this );
743  demandAttentionKNotifyTimer->stop();
744  demandAttentionKNotifyTimer->deleteLater();
745  demandAttentionKNotifyTimer = NULL;
746  }
747 
748 // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it
749 KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*,
750  // ignore already existing splashes, toolbars, utilities, menus and topmenus,
751  // as the app may show those before the main window
752  !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu()
753  && Client::belongToSameApplication( cl, value, true ) && cl != value);
754 
755 Time Client::readUserTimeMapTimestamp( const TDEStartupInfoId* asn_id, const TDEStartupInfoData* asn_data,
756  bool session ) const
757  {
758  Time time = info->userTime();
759 // kdDebug( 1212 ) << "User timestamp, initial:" << time << endl;
760  // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0
761  // helps e.g. with konqy reusing
762  if( asn_data != NULL && time != 0 )
763  {
764  // prefer timestamp from ASN id (timestamp from data is obsolete way)
765  if( asn_id->timestamp() != 0
766  && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 ))
767  {
768  time = asn_id->timestamp();
769  }
770  else if( asn_data->timestamp() != -1U
771  && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 ))
772  {
773  time = asn_data->timestamp();
774  }
775  }
776 // kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl;
777  if( time == -1U )
778  { // The window doesn't have any timestamp.
779  // If it's the first window for its application
780  // (i.e. there's no other window from the same app),
781  // use the _TDE_NET_WM_USER_CREATION_TIME trick.
782  // Otherwise, refuse activation of a window
783  // from already running application if this application
784  // is not the active one (unless focus stealing prevention is turned off).
785  Client* act = workspace()->mostRecentlyActivatedClient();
786  if( act != NULL && !belongToSameApplication( act, this, true ))
787  {
788  bool first_window = true;
789  if( isTransient())
790  {
791  if( act->hasTransient( this, true ))
792  ; // is transient for currently active window, even though it's not
793  // the same app (e.g. kcookiejar dialog) -> allow activation
794  else if( groupTransient() &&
795  findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL )
796  ; // standalone transient
797  else
798  first_window = false;
799  }
800  else
801  {
802  if( workspace()->findClient( SameApplicationActiveHackPredicate( this )))
803  first_window = false;
804  }
805  // don't refuse if focus stealing prevention is turned off
806  if( !first_window && rules()->checkFSP( options->focusStealingPreventionLevel ) > 0 )
807  {
808 // kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl;
809  return 0; // refuse activation
810  }
811  }
812  // Creation time would just mess things up during session startup,
813  // as possibly many apps are started up at the same time.
814  // If there's no active window yet, no timestamp will be needed,
815  // as plain Workspace::allowClientActivation() will return true
816  // in such case. And if there's already active window,
817  // it's better not to activate the new one.
818  // Unless it was the active window at the time
819  // of session saving and there was no user interaction yet,
820  // this check will be done in manage().
821  if( session )
822  return -1U;
823  if( ignoreFocusStealing() && act != NULL )
824  time = act->userTime();
825  else
826  time = readUserCreationTime();
827  }
828 // kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl;
829  return time;
830  }
831 
832 Time Client::userTime() const
833  {
834  Time time = user_time;
835  if( time == 0 ) // doesn't want focus after showing
836  return 0;
837  assert( group() != NULL );
838  if( time == -1U
839  || ( group()->userTime() != -1U
840  && timestampCompare( group()->userTime(), time ) > 0 ))
841  time = group()->userTime();
842  return time;
843  }
844 
856 void Client::setActive( bool act, bool updateOpacity_)
857  {
858  if ( active == act )
859  return;
860  active = act;
861  workspace()->setActiveClient( act ? this : NULL, Allowed );
862 
863  if (updateOpacity_) updateOpacity();
864  if (isModal() && transientFor())
865  {
866  if (!act) transientFor()->updateOpacity();
867  else if (!transientFor()->custom_opacity) transientFor()->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
868  }
869  updateShadowSize();
870 
871  if ( active )
872  {
873  Notify::raise( Notify::Activate );
874  if (options->shadowEnabled(true))
875  {
876  if (options->shadowEnabled(false))
877  {
878  // Wait for inactive shadow to expose occluded windows and give
879  // them a chance to redraw before painting the active shadow
880  removeShadow();
881  drawDelayedShadow();
882  if (!isDesktop() &&
883  this != workspace()->topClientOnDesktop(desktop()))
884  // If the newly activated window's isn't the desktop, wait
885  // for its shadow to draw, then redraw any shadows
886  // overlapping it.
887  drawOverlappingShadows(true);
888  }
889  else
890  drawShadow();
891  }
892  }
893  else
894  {
895  removeShadow();
896 
897  if (options->shadowEnabled(false))
898  {
899  if (this == workspace()->topClientOnDesktop(desktop()))
900  {
901  /* If the newly deactivated window is the top client on the
902  * desktop, then the newly activated window is below it; ensure
903  * that the deactivated window's shadow draws after the
904  * activated window's shadow.
905  */
906  if ((shadowAfterClient = workspace()->activeClient()))
907  {
908  drawShadowAfter(shadowAfterClient);
909  }
910  }
911  else
912  {
913  drawDelayedShadow();
914  }
915  }
916  }
917 
918  if( !active )
919  cancelAutoRaise();
920 
921  if( !active && shade_mode == ShadeActivated )
922  setShade( ShadeNormal );
923 
924  StackingUpdatesBlocker blocker( workspace());
925  workspace()->updateClientLayer( this ); // active windows may get different layer
926  // TODO optimize? mainClients() may be a bit expensive
927  ClientList mainclients = mainClients();
928  for( ClientList::ConstIterator it = mainclients.begin();
929  it != mainclients.end();
930  ++it )
931  if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active
932  workspace()->updateClientLayer( *it );
933  if( decoration != NULL )
934  decoration->activeChange();
935  updateMouseGrab();
936  updateUrgency(); // demand attention again if it's still urgent
937  }
938 
939 void Client::startupIdChanged()
940  {
941  TDEStartupInfoId asn_id;
942  TDEStartupInfoData asn_data;
943  bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
944  if( !asn_valid )
945  return;
946  // If the ASN contains desktop, move it to the desktop, otherwise move it to the current
947  // desktop (since the new ASN should make the window act like if it's a new application
948  // launched). However don't affect the window's desktop if it's set to be on all desktops.
949  int desktop = workspace()->currentDesktop();
950  if( asn_data.desktop() != 0 )
951  desktop = asn_data.desktop();
952  if( !isOnAllDesktops())
953  workspace()->sendClientToDesktop( this, desktop, true );
954  if( asn_data.xinerama() != -1 )
955  workspace()->sendClientToScreen( this, asn_data.xinerama());
956  Time timestamp = asn_id.timestamp();
957  if( timestamp == 0 && asn_data.timestamp() != -1U )
958  timestamp = asn_data.timestamp();
959  if( timestamp != 0 )
960  {
961  bool activate = workspace()->allowClientActivation( this, timestamp );
962  if( asn_data.desktop() != 0 && !isOnCurrentDesktop())
963  activate = false; // it was started on different desktop than current one
964  if( activate )
965  workspace()->activateClient( this );
966  else
967  demandAttention();
968  }
969  }
970 
971 void Client::updateUrgency()
972  {
973  if( urgency )
974  demandAttention();
975  }
976 
977 void Client::shortcutActivated()
978  {
979  workspace()->activateClient( this, true ); // force
980  }
981 
982 //****************************************
983 // Group
984 //****************************************
985 
986 void Group::startupIdChanged()
987  {
988  TDEStartupInfoId asn_id;
989  TDEStartupInfoData asn_data;
990  bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data );
991  if( !asn_valid )
992  return;
993  if( asn_id.timestamp() != 0 && user_time != -1U
994  && timestampCompare( asn_id.timestamp(), user_time ) > 0 )
995  {
996  user_time = asn_id.timestamp();
997  }
998  else if( asn_data.timestamp() != -1U && user_time != -1U
999  && timestampCompare( asn_data.timestamp(), user_time ) > 0 )
1000  {
1001  user_time = asn_data.timestamp();
1002  }
1003  }
1004 
1005 void Group::updateUserTime( Time time )
1006  { // copy of Client::updateUserTime
1007  if( time == CurrentTime )
1008  time = get_tqt_x_time();
1009  if( time != -1U
1010  && ( user_time == CurrentTime
1011  || timestampCompare( time, user_time ) > 0 )) // time > user_time
1012  user_time = time;
1013  }
1014 
1015 } // namespace
KWinInternal::Client::caption
TQString caption(bool full=true) const
Definition: client.cpp:2377
KWinInternal::Client::desktop
int desktop() const
Definition: client.h:753
KWinInternal::Client
The Client class encapsulates a window decoration frame.
Definition: client.h:46
KWinInternal::Client::setActive
void setActive(bool, bool updateOpacity=true)
Definition: activation.cpp:856
KWinInternal
Definition: activation.cpp:34
KWinInternal::Client::updateUserTime
void updateUserTime(Time time=CurrentTime)
Definition: activation.cpp:674

twin

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

twin

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