summaryrefslogtreecommitdiffstats
path: root/twin/activation.cpp
blob: d00efc3d657e643c2d7bec3f9c3b0cd3585182a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
/*****************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>

You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/

/*

 This file contains things relevant to window activation and focus
 stealing prevention.

*/

#include <tqpopupmenu.h>
#include <kxerrorhandler.h>
#include <tdestartupinfo.h>
#include <kstringhandler.h>
#include <tdelocale.h>

#include "client.h"
#include "workspace.h"
#include <fixx11h.h>

#include "notifications.h"
#include "atoms.h"
#include "group.h"
#include "rules.h"

namespace KWinInternal
{

/*
 Prevention of focus stealing:

 KWin tries to prevent unwanted changes of focus, that would result
 from mapping a new window. Also, some nasty applications may try
 to force focus change even in cases when ICCCM 4.2.7 doesn't allow it
 (e.g. they may try to activate their main window because the user
 definitely "needs" to see something happened - misusing
 of TQWidget::setActiveWindow() may be such case).

 There are 4 ways how a window may become active:
 - the user changes the active window (e.g. focus follows mouse, clicking
   on some window's titlebar) - the change of focus will
   be done by KWin, so there's nothing to solve in this case
 - the change of active window will be requested using the _NET_ACTIVE_WINDOW
   message (handled in RootInfo::changeActiveWindow()) - such requests
   will be obeyed, because this request is meant mainly for e.g. taskbar
   asking the WM to change the active window as a result of some user action.
   Normal applications should use this request only rarely in special cases.
   See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER.
 - the change of active window will be done by performing XSetInputFocus()
   on a window that's not currently active. ICCCM 4.2.7 describes when
   the application may perform change of input focus. In order to handle
   misbehaving applications, KWin will try to detect focus changes to
   windows that don't belong to currently active application, and restore
   focus back to the currently active window, instead of activating the window
   that got focus (unfortunately there's no way to FocusChangeRedirect similar
   to e.g. SubstructureRedirect, so there will be short time when the focus
   will be changed). The check itself that's done is
   Workspace::allowClientActivation() (see below).
 - a new window will be mapped - this is the most complicated case. If
   the new window belongs to the currently active application, it may be safely
   mapped on top and activated. The same if there's no active window,
   or the active window is the desktop. These checks are done by
   Workspace::allowClientActivation().
    Following checks need to compare times. One time is the timestamp
   of last user action in the currently active window, the other time is
   the timestamp of the action that originally caused mapping of the new window
   (e.g. when the application was started). If the first time is newer than
   the second one, the window will not be activated, as that indicates
   futher user actions took place after the action leading to this new
   mapped window. This check is done by Workspace::allowClientActivation().
    There are several ways how to get the timestamp of action that caused
   the new mapped window (done in Client::readUserTimeMapTimestamp()) :
     - the window may have the _NET_WM_USER_TIME property. This way
       the application may either explicitly request that the window is not
       activated (by using 0 timestamp), or the property contains the time
       of last user action in the application.
     - KWin itself tries to detect time of last user action in every window,
       by watching KeyPress and ButtonPress events on windows. This way some
       events may be missed (if they don't propagate to the toplevel window),
       but it's good as a fallback for applications that don't provide
       _NET_WM_USER_TIME, and missing some events may at most lead
       to unwanted focus stealing.
     - the timestamp may come from application startup notification.
       Application startup notification, if it exists for the new mapped window,
       should include time of the user action that caused it.
     - if there's no timestamp available, it's checked whether the new window
       belongs to some already running application - if yes, the timestamp
       will be 0 (i.e. refuse activation)
     - if the window is from session restored window, the timestamp will
       be 0 too, unless this application was the active one at the time
       when the session was saved, in which case the window will be
       activated if there wasn't any user interaction since the time
       KWin was started.
     - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp
       is used. For every toplevel window that is created (see CreateNotify
       handling), this property is set to the at that time current time.
       Since at this time it's known that the new window doesn't belong
       to any existing application (better said, the application doesn't
       have any other window mapped), it is either the very first window
       of the application, or its the only window of the application
       that was hidden before. The latter case is handled by removing
       the property from windows before withdrawing them, making
       the timestamp empty for next mapping of the window. In the sooner
       case, the timestamp will be used. This helps in case when
       an application is launched without application startup notification,
       it creates its mainwindow, and starts its initialization (that
       may possibly take long time). The timestamp used will be older
       than any user action done after launching this application.
     - if no timestamp is found at all, the window is activated.
    The check whether two windows belong to the same application (same
   process) is done in Client::belongToSameApplication(). Not 100% reliable,
   but hopefully 99,99% reliable.

 As a somewhat special case, window activation is always enabled when
 session saving is in progress. When session saving, the session
 manager allows only one application to interact with the user.
 Not allowing window activation in such case would result in e.g. dialogs
 not becoming active, so focus stealing prevention would cause here
 more harm than good.

 Windows that attempted to become active but KWin prevented this will
 be marked as demanding user attention. They'll get
 the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark
 them specially (blink, etc.). The state will be reset when the window
 eventually really becomes active.

 There are one more ways how a window can become obstrusive, window stealing
 focus: By showing above the active window, by either raising itself,
 or by moving itself on the active desktop.
     - KWin will refuse raising non-active window above the active one,
         unless they belong to the same application. Applications shouldn't
         raise their windows anyway (unless the app wants to raise one
         of its windows above another of its windows).
     - KWin activates windows moved to the current desktop (as that seems
         logical from the user's point of view, after sending the window
         there directly from KWin, or e.g. using pager). This means
         applications shouldn't send their windows to another desktop
         (SELI TODO - but what if they do?)

 Special cases I can think of:
    - konqueror reusing, i.e. kfmclient tells running Konqueror instance
        to open new window
        - without focus stealing prevention - no problem
        - with ASN (application startup notification) - ASN is forwarded,
            and because it's newer than the instance's user timestamp,
            it takes precedence
        - without ASN - user timestamp needs to be reset, otherwise it would
            be used, and it's old; moreover this new window mustn't be detected
            as window belonging to already running application, or it wouldn't
            be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly)
            hack
    - konqueror preloading, i.e. window is created in advance, and kfmclient
        tells this Konqueror instance to show it later
        - without focus stealing prevention - no problem
        - with ASN - ASN is forwarded, and because it's newer than the instance's
            user timestamp, it takes precedence
        - without ASN - user timestamp needs to be reset, otherwise it would
            be used, and it's old; also, creation timestamp is changed to
            the time the instance starts (re-)initializing the window,
            this ensures creation timestamp will still work somewhat even in this case
    - KUniqueApplication - when the window is already visible, and the new instance
        wants it to activate
        - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
        - with ASN - ASN is forwarded, and set on the already visible window, KWin
            treats the window as new with that ASN
        - without ASN - _NET_ACTIVE_WINDOW as application request is used,
                and there's no really usable timestamp, only timestamp
                from the time the (new) application instance was started,
                so KWin will activate the window *sigh*
                - the bad thing here is that there's absolutely no chance to recognize
                    the case of starting this KUniqueApp from Konsole (and thus wanting
                    the already visible window to become active) from the case
                    when something started this KUniqueApp without ASN (in which case
                    the already visible window shouldn't become active)
                - the only solution is using ASN for starting applications, at least silent
                    (i.e. without feedback)
    - when one application wants to activate another application's window (e.g. KMail
        activating already running KAddressBook window ?)
        - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
        - with ASN - can't be here, it's the KUniqueApp case then
        - without ASN - _NET_ACTIVE_WINDOW as application request should be used,
            KWin will activate the new window depending on the timestamp and
            whether it belongs to the currently active application

 _NET_ACTIVE_WINDOW usage:
 data.l[0]= 1 ->app request
          = 2 ->pager request
          = 0 - backwards compatibility
 data.l[1]= timestamp
*/


//****************************************
// Workspace
//****************************************


/*!
  Informs the workspace about the active client, i.e. the client that
  has the focus (or None if no client has the focus). This functions
  is called by the client itself that gets focus. It has no other
  effect than fixing the focus chain and the return value of
  activeClient(). And of course, to propagate the active client to the
  world.
 */
void Workspace::setActiveClient( Client* c, allowed_t )
    {
    if ( active_client == c )
        return;
    if( active_popup && active_popup_client != c && set_active_client_recursion == 0 ) 
        closeActivePopup();
    StackingUpdatesBlocker blocker( this );
    ++set_active_client_recursion;
    updateFocusMousePosition( TQCursor::pos());
    if( active_client != NULL )
        { // note that this may call setActiveClient( NULL ), therefore the recursion counter
        active_client->setActive( false, !c || !c->isModal() || c != active_client->transientFor() );
        }
    active_client = c;
    if (set_active_client_recursion == 1)
        {
        // Only unset next_active_client if activateClient() wasn't called by
        // Client::setActive() to set the active window to null before
        // activating another window.
        next_active_client = NULL;
        }
    Q_ASSERT( c == NULL || c->isActive());
    if( active_client != NULL )
        last_active_client = active_client;
    if ( active_client ) 
        {
        updateFocusChains( active_client, FocusChainMakeFirst );
        active_client->demandAttention( false );
        }
    pending_take_activity = NULL;

    updateCurrentTopMenu();
    updateToolWindows( false );
    if( c )
        disableGlobalShortcutsForClient( c->rules()->checkDisableGlobalShortcuts( false ));
    else
        disableGlobalShortcutsForClient( false );

    updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active

    rootInfo->setActiveWindow( active_client? active_client->window() : 0 );
    updateColormap();
    --set_active_client_recursion;
    }

/*!
  Tries to activate the client \a c. This function performs what you
  expect when clicking the respective entry in a taskbar: showing and
  raising the client (this may imply switching to the another virtual
  desktop) and putting the focus onto it. Once X really gave focus to
  the client window as requested, the client itself will call
  setActiveClient() and the operation is complete. This may not happen
  with certain focus policies, though.

  \sa stActiveClient(), requestFocus()
 */
void Workspace::activateClient( Client* c, bool force )
    {
    if( c == NULL )
        {
        focusToNull();
        setActiveClient( NULL, Allowed );
        return;
        }
    raiseClient( c );
    if (!c->isOnDesktop(currentDesktop()) )
        {
        ++block_focus;
        setCurrentDesktop( c->desktop() );
        --block_focus;
        }
    if( c->isMinimized())
        c->unminimize();

// TODO force should perhaps allow this only if the window already contains the mouse
    if( options->focusPolicyIsReasonable() || force )
        requestFocus( c, force );

    // Don't update user time for clients that have focus stealing workaround.
    // As they usually belong to the current active window but fail to provide
    // this information, updating their user time would make the user time
    // of the currently active window old, and reject further activation for it.
    // E.g. typing URL in minicli which will show tdeio_uiserver dialog (with workaround),
    // and then kdesktop shows dialog about SSL certificate.
    // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp().
    if( !c->ignoreFocusStealing())
        c->updateUserTime();
    }

/*!
  Tries to activate the client by asking X for the input focus. This
  function does not perform any show, raise or desktop switching. See
  Workspace::activateClient() instead.

  \sa Workspace::activateClient()
 */
void Workspace::requestFocus( Client* c, bool force )
    {
    takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false);
    }
    
void Workspace::takeActivity( Client* c, int flags, bool handled )
    {
     // the 'if( c == active_client ) return;' optimization mustn't be done here
    if (!focusChangeEnabled() && ( c != active_client) )
        flags &= ~ActivityFocus;

    if ( !c ) 
        {
        focusToNull();
        return;
        }

    if( flags & ActivityFocus )
        {
        Client* modal = c->findModal();
        if( modal != NULL && modal != c )	
            { 
            next_active_client = modal;
            if( !modal->isOnDesktop( c->desktop()))
                {
                modal->setDesktop( c->desktop());
                if( modal->desktop() != c->desktop()) // forced desktop
                    activateClient( modal );
                }
            // if the click was inside the window (i.e. handled is set),
            // but it has a modal, there's no need to use handled mode, because
            // the modal doesn't get the click anyway
            // raising of the original window needs to be still done
            if( flags & ActivityRaise )
                raiseClient( c );
            c = modal;
            handled = false;
            }
        cancelDelayFocus();
        }
    if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) )
        flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced
    if( c->isShade())
        {
        if( c->wantsInput() && ( flags & ActivityFocus ))
            {
        // client cannot accept focus, but at least the window should be active (window menu, et. al. )
            c->setActive( true );
            focusToNull();
            }
	if( c->wantsInput())
	    next_active_client = c;
        flags &= ~ActivityFocus;
        handled = false; // no point, can't get clicks
        }
    if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed
        {
	next_active_client = c;
        kdWarning( 1212 ) << "takeActivity: not shown" << endl;
        return;
        }
    c->takeActivity( flags, handled, Allowed );
    if( !c->isOnScreen( active_screen ))
        active_screen = c->screen();
    }

void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags )
    {
    if( pending_take_activity != c ) // pending_take_activity is reset when doing restack or activation
        return;
    if(( flags & ActivityRaise ) != 0 )
        raiseClient( c );
    if(( flags & ActivityFocus ) != 0 && c->isShown( false ))
        c->takeFocus( Allowed );
    pending_take_activity = NULL;
    }

/*!
  Informs the workspace that the client \a c has been hidden. If it
  was the active client (or to-become the active client),
  the workspace activates another one.

  \a c may already be destroyed
 */
void Workspace::clientHidden( Client* c )
    {
    assert( !c->isShown( true ) || !c->isOnCurrentDesktop());
    activateNextClient( c );
    }

// deactivates 'c' and activates next client
bool Workspace::activateNextClient( Client* c )
    {
    // if 'c' is not the active or the to-become active one, do nothing
    if( !( c == active_client
            || ( should_get_focus.count() > 0 && c == should_get_focus.last())))
        return false;
    closeActivePopup();
    if( c != NULL )
        {
        if( c == active_client )
            {
            setActiveClient( NULL, Allowed );
            }
        should_get_focus.remove( c );
        }
    if( focusChangeEnabled())
        {
        if ( options->focusPolicyIsReasonable())
            { // search the focus_chain for a client to transfer focus to
              // if 'c' is transient, transfer focus to the first suitable mainwindow
            Client* get_focus = NULL;
            const ClientList mainwindows = ( c != NULL ? c->mainClients() : ClientList());
            for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
                 it != focus_chain[currentDesktop()].end();
                 --it )
                {
                if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
                    continue;
                if( options->separateScreenFocus )
                    {
                    if( c != NULL && !(*it)->isOnScreen( c->screen()))
                        continue;
                    if( c == NULL && !(*it)->isOnScreen( activeScreen()))
                        continue;
                    }
                if( mainwindows.contains( *it ))
                    {
                    get_focus = *it;
                    break;
                    }
                if( get_focus == NULL )
                    get_focus = *it;
                }
            if( get_focus == NULL )
                get_focus = findDesktop( true, currentDesktop());
            if( get_focus != NULL )
                {
                requestFocus( get_focus );
                get_focus->setActive( true, true );
                }
            else
                focusToNull();
            }
            else
                return false;
        }
    else
        // if blocking focus, move focus to the desktop later if needed
        // in order to avoid flickering
        focusToNull();
    return true;
    }

void Workspace::setCurrentScreen( int new_screen )
    {
    if (new_screen < 0 || new_screen > numScreens())
        return;
    if ( !options->focusPolicyIsReasonable())
        return;
    closeActivePopup();
    Client* get_focus = NULL;
    for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
         it != focus_chain[currentDesktop()].end();
         --it )
        {
        if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop())
            continue;
        if( !(*it)->screen() == new_screen )
            continue;
        get_focus = *it;
        break;
        }
    if( get_focus == NULL )
        get_focus = findDesktop( true, currentDesktop());
    if( get_focus != NULL && get_focus != mostRecentlyActivatedClient())
        requestFocus( get_focus );
    active_screen = new_screen;
    }

void Workspace::gotFocusIn( const Client* c )
    {
    if( should_get_focus.contains( const_cast< Client* >( c )))
        { // remove also all sooner elements that should have got FocusIn,
      // but didn't for some reason (and also won't anymore, because they were sooner)
        while( should_get_focus.first() != c )
            should_get_focus.pop_front();
        should_get_focus.pop_front(); // remove 'c'
        }
    }

void Workspace::setShouldGetFocus( Client* c )
    {
    should_get_focus.append( c );
    updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
    }

// focus_in -> the window got FocusIn event
// session_active -> the window was active when saving session
bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in )
    {
    // options->focusStealingPreventionLevel :
    // 0 - none    - old KWin behaviour, new windows always get focus
    // 1 - low     - focus stealing prevention is applied normally, when unsure, activation is allowed
    // 2 - normal  - focus stealing prevention is applied normally, when unsure, activation is not allowed,
    //              this is the default
    // 3 - high    - new window gets focus only if it belongs to the active application,
    //              or when no window is currently active
    // 4 - extreme - no window gets focus without user intervention
    if( time == -1U )
        time = c->userTime();
    int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
    if( session_saving && level <= 2 ) // <= normal
        {
        return true;
        }
    Client* ac = mostRecentlyActivatedClient();
    if( focus_in )
        {
        if( should_get_focus.contains( const_cast< Client* >( c )))
            return true; // FocusIn was result of KWin's action
        // Before getting FocusIn, the active Client already
        // got FocusOut, and therefore got deactivated.
        ac = last_active_client;
        }
    if( time == 0 ) // explicitly asked not to get focus
        return false;
    if( level == 0 ) // none
        return true;
    if( level == 4 ) // extreme
        return false;
    if( !c->isOnCurrentDesktop())
        return false; // allow only with level == 0
    if( c->ignoreFocusStealing())
        return true;
    if( ac == NULL || ac->isDesktop())
        {
//        kdDebug( 1212 ) << "Activation: No client active, allowing" << endl;
        return true; // no active client -> always allow
        }
    // TODO window urgency  -> return true?
    if( Client::belongToSameApplication( c, ac, true ))
        {
//        kdDebug( 1212 ) << "Activation: Belongs to active application" << endl;
        return true;
        }
    if( level == 3 ) // high
        return false;
    if( time == -1U )  // no time known
        {
//        kdDebug( 1212 ) << "Activation: No timestamp at all" << endl;
        if( level == 1 ) // low
            return true;
        // no timestamp at all, don't activate - because there's also creation timestamp
        // done on CreateNotify, this case should happen only in case application
        // maps again already used window, i.e. this won't happen after app startup
        return false; 
        }
    // level == 2 // normal
    Time user_time = ac->userTime();
//    kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time
//        << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
    return timestampCompare( time, user_time ) >= 0; // time >= user_time
    }

// basically the same like allowClientActivation(), this time allowing
// a window to be fully raised upon its own request (XRaiseWindow),
// if refused, it will be raised only on top of windows belonging
// to the same application
bool Workspace::allowFullClientRaising( const Client* c, Time time )
    {
    int level = c->rules()->checkFSP( options->focusStealingPreventionLevel );
    if( session_saving && level <= 2 ) // <= normal
        {
        return true;
        }
    Client* ac = mostRecentlyActivatedClient();
    if( level == 0 ) // none
        return true;
    if( level == 4 ) // extreme
        return false;
    if( ac == NULL || ac->isDesktop())
        {
//        kdDebug( 1212 ) << "Raising: No client active, allowing" << endl;
        return true; // no active client -> always allow
        }
    if( c->ignoreFocusStealing())
        return true;
    // TODO window urgency  -> return true?
    if( Client::belongToSameApplication( c, ac, true ))
        {
//        kdDebug( 1212 ) << "Raising: Belongs to active application" << endl;
        return true;
        }
    if( level == 3 ) // high
        return false;
    Time user_time = ac->userTime();
//    kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time
//        << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl;
    return timestampCompare( time, user_time ) >= 0; // time >= user_time
    }

// called from Client after FocusIn that wasn't initiated by KWin and the client
// wasn't allowed to activate
void Workspace::restoreFocus()
    {
    // this updateXTime() is necessary - as FocusIn events don't have
    // a timestamp *sigh*, twin's timestamp would be older than the timestamp
    // that was used by whoever caused the focus change, and therefore
    // the attempt to restore the focus would fail due to old timestamp
    updateXTime();
    if( should_get_focus.count() > 0 )
        requestFocus( should_get_focus.last());
    else if( last_active_client )
        requestFocus( last_active_client );
    }

void Workspace::clientAttentionChanged( Client* c, bool set )
    {
    if( set )
        {
        attention_chain.remove( c );
        attention_chain.prepend( c );
        }
    else
        attention_chain.remove( c );
    }

// This is used when a client should be shown active immediately after requestFocus(),
// without waiting for the matching FocusIn that will really make the window the active one.
// Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows,
bool Workspace::fakeRequestedActivity( Client* c )
    {
    if( should_get_focus.count() > 0 && should_get_focus.last() == c )
        {
        if( c->isActive())
            return false;
        c->setActive( true );
        return true;
        }
    return false;
    }

void Workspace::unfakeActivity( Client* c )
    {
    if( should_get_focus.count() > 0 && should_get_focus.last() == c )
        { // TODO this will cause flicker, and probably is not needed
        if( last_active_client != NULL )
            last_active_client->setActive( true );
        else
            c->setActive( false );
        }
    }


//********************************************
// Client
//********************************************

/*!
  Updates the user time (time of last action in the active window).
  This is called inside  twin for every action with the window
  that qualifies for user interaction (clicking on it, activate it
  externally, etc.).
 */
void Client::updateUserTime( Time time )
    { // copied in Group::updateUserTime
    if( time == CurrentTime )
        time = GET_QT_X_TIME();
    if( time != -1U
        && ( user_time == CurrentTime
            || timestampCompare( time, user_time ) > 0 )) // time > user_time
        user_time = time;
    group()->updateUserTime( user_time );
    }

Time Client::readUserCreationTime() const
    {
    long result = -1; // Time == -1 means none
    Atom type;
    int format, status;
    unsigned long nitems = 0;
    unsigned long extra = 0;
    unsigned char *data = 0;
    KXErrorHandler handler; // ignore errors?
    status = XGetWindowProperty( tqt_xdisplay(), window(),
        atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL,
        &type, &format, &nitems, &extra, &data );
    if (status  == Success )
        {
        if (data && nitems > 0)
            result = *((long*) data);
        XFree(data);
        }
    return result;       
    }

void Client::demandAttention( bool set )
    {
    if( isActive())
        set = false;
    if( demands_attention == set )
        return;
    demands_attention = set;
    if( demands_attention )
        {
        // Demand attention flag is often set right from manage(), when focus stealing prevention
        // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place
        // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry
        // to be set.
        // Delayed call to KNotify also solves the problem of having X server grab in manage(),
        // which may deadlock when KNotify (or TDELauncher when launching KNotify) need to access X.
        Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
        // Setting the demands attention state needs to be done directly in KWin, because
        // KNotify would try to set it, resulting in a call to KNotify again, etc.
        if( Notify::makeDemandAttention( e ))
            info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );

        if( demandAttentionKNotifyTimer == NULL )
            {
            demandAttentionKNotifyTimer = new TQTimer( this );
            connect( demandAttentionKNotifyTimer, TQT_SIGNAL( timeout()), TQT_SLOT( demandAttentionKNotify()));
            }
        demandAttentionKNotifyTimer->start( 1000, true );
        }
    else
        info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention );
    workspace()->clientAttentionChanged( this, set );
    }

void Client::demandAttentionKNotify()
    {
    Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther;
    Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this );
    demandAttentionKNotifyTimer->stop();
    demandAttentionKNotifyTimer->deleteLater();
    demandAttentionKNotifyTimer = NULL;
    }

// TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it
KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*,
    // ignore already existing splashes, toolbars, utilities, menus and topmenus,
    // as the app may show those before the main window
    !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu()
    && Client::belongToSameApplication( cl, value, true ) && cl != value);

Time Client::readUserTimeMapTimestamp( const TDEStartupInfoId* asn_id, const TDEStartupInfoData* asn_data,
    bool session ) const
    {
    Time time = info->userTime();
//    kdDebug( 1212 ) << "User timestamp, initial:" << time << endl;
    // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0
    // helps e.g. with konqy reusing
    if( asn_data != NULL && time != 0 )
        {
        // prefer timestamp from ASN id (timestamp from data is obsolete way)
        if( asn_id->timestamp() != 0
            && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 ))
            {
            time = asn_id->timestamp();
            }
        else if( asn_data->timestamp() != -1U
            && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 ))
            {
            time = asn_data->timestamp();
            }
        }
//    kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl;
    if( time == -1U )
        { // The window doesn't have any timestamp.
      // If it's the first window for its application
      // (i.e. there's no other window from the same app),
      // use the _TDE_NET_WM_USER_CREATION_TIME trick.
      // Otherwise, refuse activation of a window
      // from already running application if this application
      // is not the active one (unless focus stealing prevention is turned off).
        Client* act = workspace()->mostRecentlyActivatedClient();
        if( act != NULL && !belongToSameApplication( act, this, true ))
            {
            bool first_window = true;
            if( isTransient())
                {
                if( act->hasTransient( this, true ))
                    ; // is transient for currently active window, even though it's not
                      // the same app (e.g. kcookiejar dialog) -> allow activation
                else if( groupTransient() &&
                    findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL )
                    ; // standalone transient
                else
                    first_window = false;
                }
            else
                {
                if( workspace()->findClient( SameApplicationActiveHackPredicate( this )))
                    first_window = false;
                }
            // don't refuse if focus stealing prevention is turned off
            if( !first_window && rules()->checkFSP( options->focusStealingPreventionLevel ) > 0 )
                {
//                kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl;
                return 0; // refuse activation
                }
            }
        // Creation time would just mess things up during session startup,
        // as possibly many apps are started up at the same time.
        // If there's no active window yet, no timestamp will be needed,
        // as plain Workspace::allowClientActivation() will return true
        // in such case. And if there's already active window,
        // it's better not to activate the new one.
        // Unless it was the active window at the time
        // of session saving and there was no user interaction yet,
        // this check will be done in manage().
        if( session )
            return -1U;
        if( ignoreFocusStealing() && act != NULL )
            time = act->userTime();
        else
            time = readUserCreationTime();
        }
//    kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl;
    return time;
    }

Time Client::userTime() const
    {
    Time time = user_time;
    if( time == 0 ) // doesn't want focus after showing
        return 0;
    assert( group() != NULL );
    if( time == -1U
         || ( group()->userTime() != -1U
                 && timestampCompare( group()->userTime(), time ) > 0 ))
        time = group()->userTime();
    return time;
    }

/*!
  Sets the client's active state to \a act.

  This function does only change the visual appearance of the client,
  it does not change the focus setting. Use
  Workspace::activateClient() or Workspace::requestFocus() instead.

  If a client receives or looses the focus, it calls setActive() on
  its own.

 */
void Client::setActive( bool act, bool updateOpacity_)
    {
    if ( active == act )
        return;
    active = act;
    workspace()->setActiveClient( act ? this : NULL, Allowed );

    if (updateOpacity_) updateOpacity();
    if (isModal() && transientFor())
    {
        if (!act) transientFor()->updateOpacity();
        else if (!transientFor()->custom_opacity) transientFor()->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
    }
    updateShadowSize();

    if ( active )
    {
        Notify::raise( Notify::Activate );
        if (options->shadowEnabled(true))
            {
            if (options->shadowEnabled(false))
                {
                // Wait for inactive shadow to expose occluded windows and give
                // them a chance to redraw before painting the active shadow
                removeShadow();
                drawDelayedShadow();
                if (!isDesktop() &&
                       this != workspace()->topClientOnDesktop(desktop()))
                    // If the newly activated window's isn't the desktop, wait
                    // for its shadow to draw, then redraw any shadows
                    // overlapping it.
                    drawOverlappingShadows(true);
                }
            else
                drawShadow();
            }
        }
    else
        {
        removeShadow();

        if (options->shadowEnabled(false))
            {
            if (this == workspace()->topClientOnDesktop(desktop()))
                {
                /* If the newly deactivated window is the top client on the
                 * desktop, then the newly activated window is below it; ensure
                 * that the deactivated window's shadow draws after the
                 * activated window's shadow.
                 */
                if ((shadowAfterClient = workspace()->activeClient()))
                    {
                    drawShadowAfter(shadowAfterClient);
                    }
                }
            else
                {
                drawDelayedShadow();
                }
            }
        }

    if( !active )
        cancelAutoRaise();

    if( !active && shade_mode == ShadeActivated )
        setShade( ShadeNormal );
        
    StackingUpdatesBlocker blocker( workspace());
    workspace()->updateClientLayer( this ); // active windows may get different layer
    // TODO optimize? mainClients() may be a bit expensive
    ClientList mainclients = mainClients();
    for( ClientList::ConstIterator it = mainclients.begin();
         it != mainclients.end();
         ++it )
        if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active
            workspace()->updateClientLayer( *it );
    if( decoration != NULL )
        decoration->activeChange();
    updateMouseGrab();
    updateUrgency(); // demand attention again if it's still urgent
    }

void Client::startupIdChanged()
    {
    TDEStartupInfoId asn_id;
    TDEStartupInfoData asn_data;
    bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
    if( !asn_valid )
        return;
    // If the ASN contains desktop, move it to the desktop, otherwise move it to the current
    // desktop (since the new ASN should make the window act like if it's a new application
    // launched). However don't affect the window's desktop if it's set to be on all desktops.
    int desktop = workspace()->currentDesktop();
    if( asn_data.desktop() != 0 )
        desktop = asn_data.desktop();
    if( !isOnAllDesktops())
        workspace()->sendClientToDesktop( this, desktop, true );
    if( asn_data.xinerama() != -1 )
        workspace()->sendClientToScreen( this, asn_data.xinerama());
    Time timestamp = asn_id.timestamp();
    if( timestamp == 0 && asn_data.timestamp() != -1U )
        timestamp = asn_data.timestamp();
    if( timestamp != 0 )
        {
        bool activate = workspace()->allowClientActivation( this, timestamp );
        if( asn_data.desktop() != 0 && !isOnCurrentDesktop())
            activate = false; // it was started on different desktop than current one
        if( activate )
            workspace()->activateClient( this );
        else
            demandAttention();
        }
    }

void Client::updateUrgency()
    {
    if( urgency )
        demandAttention();
    }

void Client::shortcutActivated()
    {
    workspace()->activateClient( this, true ); // force
    }

//****************************************
// Group
//****************************************
    
void Group::startupIdChanged()
    {
    TDEStartupInfoId asn_id;
    TDEStartupInfoData asn_data;
    bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data );
    if( !asn_valid )
        return;
    if( asn_id.timestamp() != 0 && user_time != -1U
        && timestampCompare( asn_id.timestamp(), user_time ) > 0 )
        {
        user_time = asn_id.timestamp();
        }
    else if( asn_data.timestamp() != -1U && user_time != -1U
        && timestampCompare( asn_data.timestamp(), user_time ) > 0 )
        {
        user_time = asn_data.timestamp();
        }
    }

void Group::updateUserTime( Time time )
    { // copy of Client::updateUserTime
    if( time == CurrentTime )
        time = GET_QT_X_TIME();
    if( time != -1U
        && ( user_time == CurrentTime
            || timestampCompare( time, user_time ) > 0 )) // time > user_time
        user_time = time;
    }

} // namespace