• 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
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8You can Freely distribute this program under the GNU General Public
9License. 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
34namespace 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
214void 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
270void 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
310void Workspace::requestFocus( Client* c, bool force )
311 {
312 takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false);
313 }
314
315void 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
376void 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
394void Workspace::clientHidden( Client* c )
395 {
396 assert( !c->isShown( true ) || !c->isOnCurrentDesktop());
397 activateNextClient( c );
398 }
399
400// deactivates 'c' and activates next client
401bool 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
463void 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
489void 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
500void 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
508bool 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
578bool 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
613void 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
626void 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,
640bool 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
652void 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
674void 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
685Time 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
706void 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
739void 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
749KWIN_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
755Time 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
832Time 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
856void 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
939void 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
971void Client::updateUrgency()
972 {
973 if( urgency )
974 demandAttention();
975 }
976
977void Client::shortcutActivated()
978 {
979 workspace()->activateClient( this, true ); // force
980 }
981
982//****************************************
983// Group
984//****************************************
985
986void 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
1005void 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::desktop
int desktop() const
Definition: client.h:751
KWinInternal::Client::setActive
void setActive(bool, bool updateOpacity=true)
Definition: activation.cpp:856
KWinInternal::Client::Client
Client(Workspace *ws)
Definition: client.cpp:94
KWinInternal::Client::updateUserTime
void updateUserTime(Time time=CurrentTime)
Definition: activation.cpp:674
KWinInternal::Client::caption
TQString caption(bool full=true) const
Definition: client.cpp:2377

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.9.4
This website is maintained by Timothy Pearson.