Tk Source Code

Changes On Branch bug-22349fc78a
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch bug-22349fc78a Excluding Merge-Ins

This is equivalent to a diff from 087440d5 to 71089d07

2024-06-02
07:15
Fix [0fb337ea84]: ttk::combobox selection overruns downarrow element. Thanks to Emiliano Gavilan. check-in: ceab69ad user: fvogel tags: core-8-6-branch
2024-06-01
13:45
Merge core-8-6-branch Leaf check-in: 2f300f95 user: culler tags: chavez_cgimage_drawing
2024-05-31
17:11
merge core-8-6-branch Closed-Leaf check-in: 71089d07 user: fvogel tags: bug-22349fc78a
17:11
merge core-8-6-branch check-in: 1a015bb6 user: fvogel tags: bug-22349fc78a-v2
2024-05-29
20:11
Merge 8.6 check-in: c97bea36 user: jan.nijtmans tags: core-8-branch
19:56
Fix [0fb337ea84]: ttk::combobox selection overruns downarrow element. Thanks to Emiliano Gavilan. Closed-Leaf check-in: 0a36b34b user: fvogel tags: bug-0fb337ea84
18:49
merge core-8-6-branch check-in: 03fd50a4 user: fvogel tags: less_tests_constraints
18:46
Merge core-8-6-branch. check-in: 7dc8ff8b user: fvogel tags: trunk, main
18:45
Fix [8162e9b7a9]: Fonts are always scaled with UI when creating new instances. Many thanks to Csaba Nemethi! check-in: 087440d5 user: fvogel tags: core-8-6-branch
2024-05-28
21:22
Request font with pixel size 13 instead of 11, so that font-44.1 passes without Xft under xvfb too. Closed-Leaf check-in: a5ac9aad user: fvogel tags: bug-8162e9b7a9
2024-05-24
16:53
Make compilable with strict C99 compiler (missing declaration for XUnionRegion()) check-in: f2f2900e user: jan.nijtmans tags: core-8-6-branch
2024-04-14
15:17
Since in proc setup_win_mousepointer we're using $w, which is supposed to be a not-already visible window, we can [tkwait visibility] on it instead of [_pause]-ing. check-in: 6bdef79b user: fvogel tags: bug-22349fc78a

Changes to .github/workflows/linux-build.yml.
1
2
3
4
5
6
7

8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15







+







name: Linux
on:
  push:
    branches:
    - "main"
    - "core-8-branch"
    - "core-8-6-branch"
    - "bug-22349fc78a"
    tags:
    - "core-**"
permissions:
  contents: read
defaults:
  run:
    shell: bash
Changes to .github/workflows/mac-build.yml.
1
2
3
4
5
6
7

8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15







+







name: macOS
on:
  push:
    branches:
    - "main"
    - "core-8-branch"
    - "core-8-6-branch"
    - "bug-22349fc78a"
    tags:
    - "core-**"
permissions:
  contents: read
env:
  ERROR_ON_FAILURES: 1
jobs:
Changes to .github/workflows/win-build.yml.
1
2
3
4
5
6
7

8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15







+







name: Windows
on:
  push:
    branches:
    - "main"
    - "core-8-branch"
    - "core-8-6-branch"
    - "bug-22349fc78a"
    tags:
    - "core-**"
permissions:
  contents: read
env:
  ERROR_ON_FAILURES: 1
jobs:
Changes to generic/tkPointer.c.
29
30
31
32
33
34
35




36
37
38
39
40
41
42
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46







+
+
+
+







    unsigned lastState;		/* Last known state flags. */
    XPoint lastPos;		/* Last reported mouse position. */
    TkWindow *lastWinPtr;	/* Last reported mouse window. */
    TkWindow *restrictWinPtr;	/* Window to which all mouse events will be
				 * reported. */
    TkWindow *cursorWinPtr;	/* Window that is currently controlling the
				 * global cursor. */
    int pwIsDead;		/* 1 if destroyed, 0 if not */
    TkWindow deadWin;		/* Replacement for a destroyed pointer
    				 * window, used as the source window for
    				 * generating crossing events. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

/*
 * Forward declarations of procedures used in this file.
 */

127
128
129
130
131
132
133

















134




135
136
137
138
139
140
141
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+







{
    int crossed = 0;		/* 1 if mouse crossed a window boundary */
    ThreadSpecificData *tsdPtr =
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
    TkWindow *restrictWinPtr = tsdPtr->restrictWinPtr;
    TkWindow *lastWinPtr = tsdPtr->lastWinPtr;

    if (tsdPtr->pwIsDead) {
	XEvent event;
	TkWindow *deadWinPtr = &tsdPtr->deadWin;
	TkWindow *targetPtr = deadWinPtr->parentPtr;

	if (targetPtr && (targetPtr->window != None)) {

	    /*
	     * Only generate Enter events. We can't deliver Leave events
	     * since the windows to receive them don't exist anymore.
	     */

	    InitializeEvent(&event, targetPtr, EnterNotify, x, y, state,
		    NotifyInferior);
	    TkInOutEvents(&event, deadWinPtr, winPtr, 0,
		    EnterNotify, TCL_QUEUE_TAIL);
	}
    if (winPtr != tsdPtr->lastWinPtr) {
	tsdPtr->lastWinPtr = winPtr;
	tsdPtr->pwIsDead = 0;
	crossed = 1;
    } else if (winPtr != lastWinPtr) {
	if (restrictWinPtr) {
	    int newPos, oldPos;

	    newPos = TkPositionInTree(winPtr, restrictWinPtr);
	    oldPos = TkPositionInTree(lastWinPtr, restrictWinPtr);

	    /*
174
175
176
177
178
179
180
181


182
183
184
185

186
187
188
189
190
191
192
198
199
200
201
202
203
204

205
206
207
208
209

210
211
212
213
214
215
216
217







-
+
+



-
+







		targetPtr = lastWinPtr;
	    }

	    if (targetPtr && (targetPtr->window != None)) {
		XEvent event;

		/*
		 * Generate appropriate Enter/Leave events.
		 * Generate Enter/Leave events for the old and new pointer
		 * window and their intermediates.
		 */

		InitializeEvent(&event, targetPtr, LeaveNotify, x, y, state,
			NotifyNormal);
			NotifyAncestor);

		TkInOutEvents(&event, lastWinPtr, winPtr, LeaveNotify,
			EnterNotify, TCL_QUEUE_TAIL);
		crossed = 1;
	    }
	}
	tsdPtr->lastWinPtr = winPtr;
383
384
385
386
387
388
389
390

391
392
393
394
395
396
397
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422







-
+







	    targetWinPtr = tsdPtr->restrictWinPtr;
	} else if (tsdPtr->grabWinPtr && !winPtr) {
	    targetWinPtr = tsdPtr->grabWinPtr;
	}

	if (targetWinPtr != NULL) {
	    InitializeEvent(&event, targetWinPtr, MotionNotify, x, y,
		    tsdPtr->lastState, NotifyNormal);
		    tsdPtr->lastState, NotifyAncestor);
	    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
	}
	tsdPtr->lastPos = pos;
    }
}

/*
491
492
493
494
495
496
497











498

499
500
501
502
503
504
505
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







+
+
+
+
+
+
+
+
+
+
+
-
+







TkPointerDeadWindow(
    TkWindow *winPtr)
{
    ThreadSpecificData *tsdPtr =
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    if (winPtr == tsdPtr->lastWinPtr) {
	TkWindow *deadWinPtr = &tsdPtr->deadWin;
	tsdPtr->pwIsDead = 1;

	/*
	 * Save the pointer window information that GenerateEnterLeave()
	 * needs for generating crossing events.
	 */

	deadWinPtr->parentPtr = winPtr->parentPtr;
	deadWinPtr->flags = winPtr->flags;

	tsdPtr->lastWinPtr = TkGetContainer(winPtr);
	tsdPtr->lastWinPtr = winPtr->parentPtr;
    }
    if (winPtr == tsdPtr->grabWinPtr) {
	tsdPtr->grabWinPtr = NULL;
    }
    if (winPtr == tsdPtr->restrictWinPtr) {
	tsdPtr->restrictWinPtr = NULL;
    }
Changes to tests/event.test.
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
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
1017
1018
1019
1020
1021



1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036



1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055






1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078







1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115






1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184







+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+





                on which this test is run does NOT have a diaeresis \
                physical key - in this case, test is actually void."
    }
} -cleanup {
    deleteWindows
} -result {OK}

proc waitForWindowEvent {w event {timeout 1000}} {
# This proc is intended to overcome latency of windowing system
# notifications when toplevel windows are involved. These latencies vary
# considerably with the window manager in use, with the system load,
# with configured scheduling priorities for processes, etc ...
# Waiting for the corresponding window events evades the trouble that is
# associated with the alternative: waiting or halting the Tk process for a
# fixed amount of time (using "after ms"). With the latter strategy it's
# always a gamble how much waiting time is enough on an end user's system.
# It also leads to long fixed waiting times in order to be on the safe side.
test event-9.1 {enter . window by destroying a toplevel - bug b1d115fa60} -setup {
    set EnterBind [bind . <Enter>]

    variable _windowEvent

    # Use counter as a unique ID to prevent subsequent waits
    # from interfering with each other.
    set counter [incr _windowEvent(counter)]
    set _windowEvent($counter) 1
    set savedBinding [bind $w $event]
    bind $w $event [list +waitForWindowEvent.signal $counter]
    set afterID [after $timeout [list set _windowEvent($counter) -1]]
    vwait _windowEvent($counter)
    set late [expr {$_windowEvent($counter) == -1}]
    bind $w $event $savedBinding
    unset _windowEvent($counter)
    if {$late} {
	puts "waiting for $event event on $w timed out (> $timeout ms)"
    } else {
        after cancel $afterID
    }
}
proc waitForWindowEvent.signal {counter} {
# Helper proc that records the triggering of a window event.
    incr ::_windowEvent($counter)
}

proc create_and_pack_frames {{w {}}} {
    frame $w.f1 -bg blue -width 200 -height 200
    pack propagate $w.f1 0
    frame $w.f1.f2 -bg yellow -width 100 -height 100
    pack $w.f1.f2 $w.f1 -side bottom -anchor se
}

proc setup_win_mousepointer {w} {
# Position the window and the mouse pointer as an initial state for some tests.
# The so-called "pointer window" is the $w window that will now contain the mouse pointer.
    wm geometry . +700+700; # root window out of our way - must not cover windows from event-9.1*
    toplevel $w
    pack propagate $w 0
    wm geometry $w 300x300+100+100
    tkwait visibility $w
    update; # service remaining screen drawing events (e.g. <Expose>)
    set pointerWin [winfo containing [winfo pointerx $w] [winfo pointery $w]]
    event generate $w <Motion> -warp 1 -x 250 -y 250
    if {[tk windowingsystem] eq "aqua"} {
        # Generate a NSMouseMoved NSevent with no mouse pointer position change.
        # This is to let event-9.1? tests pass on macOS aqua and is only a workaround
        # since macOS aqua should send the adequate NSevent by itself.
        movemouse [expr {[winfo rootx $w] + 250}] [expr {[winfo rooty $w] + 250}]
    }
    if {($pointerWin ne $w) && ([tk windowingsystem] ne "aqua")} {
        waitForWindowEvent $w <Enter>
    } else {
        controlPointerWarpTiming
    }
}

test event-9.11 {pointer window container = parent} -setup {
    setup_win_mousepointer .one
    wm withdraw .one
    create_and_pack_frames .one
    wm deiconify .one
    tkwait visibility .one.f1.f2
    _pause 200
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    wm geometry . 200x200+300+300
    wm deiconify .
    _pause 200
    toplevel .top2 -width 200 -height 200
    destroy .one.f1.f2
    if {[tk windowingsystem] eq "aqua"} {
        # Generate a NSMouseMoved NSevent with no mouse pointer position change.
        # This is to let event-9.1? tests pass on macOS aqua and is only a workaround
        # since macOS aqua should send the adequate NSevent by itself.
        movemouse [expr {[winfo rootx .one] + 250}] [expr {[winfo rooty .one] + 250}]
    }
    _pause 200; # service crossing events
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|<Enter> NotifyInferior .one.f1|}

    wm geometry .top2 +[expr {[winfo rootx .]+50}]+[expr {[winfo rooty .]+50}]
    wm deiconify .top2
    raise .top2
    _pause 400
test event-9.12 {pointer window container != parent} -setup {
    setup_win_mousepointer .one
    wm withdraw .one
    create_and_pack_frames .one
    pack propagate .one.f1.f2 0
    pack [frame .one.g -bg orange -width 80 -height 80] -anchor se -side bottom -in .one.f1.f2
    wm deiconify .one
    tkwait visibility .one.g
    _pause 200
    event generate .top2 <Motion> -warp 1 -x 50 -y 50
    _pause 100
    bind . <Enter> {lappend res %W}
    set res [list ]
    destroy .top2
    _pause 200
    set res
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .one.g
    _pause 200; # service crossing events
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|<Enter> NotifyNonlinearVirtual .one.f1|<Enter> NotifyNonlinear .one.f1.f2|}

test event-9.13 {pointer window is a toplevel, toplevel destination} -setup {
    setup_win_mousepointer .one
    wm withdraw .one
    toplevel .two
    wm geometry .two 300x300+150+150
    wm deiconify .one
    wm deiconify .two
    waitForWindowEvent .two <Enter>
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .two
    waitForWindowEvent .one <Enter>
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|<Enter> NotifyNonlinear .one|}
    deleteWindows
    bind . <Enter> $EnterBind
} -result {.}
test event-9.2 {enter toplevel window by destroying a toplevel - bug b1d115fa60} -setup {
    set iconified false
    if {[winfo ismapped .]} {
        wm iconify .

test event-9.14 {pointer window is a toplevel, tk internal destination} -setup {
    setup_win_mousepointer .one
    wm withdraw .one
    create_and_pack_frames .one
    toplevel .two
    wm geometry .two 300x300+150+150
    wm deiconify .one
    wm deiconify .two
    waitForWindowEvent .two <Enter>
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .two
    waitForWindowEvent .one.f1.f2 <Enter>
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
        update
        set iconified true
    }
    unset result
} -result {|<Enter> NotifyNonlinearVirtual .one|<Enter> NotifyNonlinearVirtual .one.f1|<Enter> NotifyNonlinear .one.f1.f2|}

test event-9.15 {pointer window is a toplevel, destination is screen root} -setup {
    setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test)
    toplevel .two
    wm geometry .two 300x300+150+150
    wm deiconify .two
    waitForWindowEvent .two <Enter>
    event generate .two <Motion> -warp 1 -x 275 -y 275
    controlPointerWarpTiming
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    toplevel .top1
    wm geometry .top1 200x200+300+300
    wm deiconify .top1
    destroy .two
    _pause 200; # ensure servicing of all scheduled events (only <Expose> events expected)
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|}

test event-9.16 {Successive destructions (pointer window + parent), single generation of crossing events} -setup {
    # Tests correctness of overwriting the dead window struct in
    # TkPointerDeadWindow() and subsequent reading in GenerateEnterLeave().
    setup_win_mousepointer .one
    wm withdraw .one
    create_and_pack_frames .one
    wm deiconify .one
    tkwait visibility .one.f1.f2
    _pause 200
    toplevel .top2 -width 200 -height 200
    wm geometry .top2 +[expr {[winfo rootx .top1]+50}]+[expr {[winfo rooty .top1]+50}]
    _pause 200
    wm deiconify .top2
    raise .top2
    _pause 400
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .one.f1
    _pause 200; # service crossing events
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|<Enter> NotifyInferior .one|}

test event-9.17 {Successive destructions (pointer window + parent), separate crossing events} -setup {
    # Tests correctness of overwriting the dead window struct in
    # TkPointerDeadWindow() and subsequent reading in GenerateEnterLeave().
    setup_win_mousepointer .one
    wm withdraw .one
    create_and_pack_frames .one
    wm deiconify .one
    tkwait visibility .one.f1.f2
    _pause 200
    event generate .top2 <Motion> -warp 1 -x 50 -y 50
    _pause 100
    bind .top1 <Enter> {lappend res %W}
    set res [list ]
    destroy .top2
    _pause 200
    set res
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .one.f1.f2
    _pause 200; # service crossing events
    destroy .one.f1
    _pause 200; # service crossing events
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|<Enter> NotifyInferior .one.f1|<Enter> NotifyInferior .one|}

test event-9.18 {Successive destructions (pointer window + ancestors including its toplevel), destination is non-root toplevel} -setup {
    setup_win_mousepointer .one
    toplevel .two
    pack propagate .two 0
    wm geometry .two 300x300+100+100
    create_and_pack_frames .two
    wm deiconify .two
    waitForWindowEvent .two.f1.f2 <Enter>
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .two
    waitForWindowEvent .one <Enter>
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|<Enter> NotifyNonlinear .one|}
    deleteWindows ; # destroy all children of ".", this already includes .top1
    if {$iconified} {
        wm deiconify .
        update
    }
} -result {.top1}

test event-9.19 {Successive destructions (pointer window + ancestors including its toplevel), destination is internal window, bypass root win} -setup {
    setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test)
    toplevel .two
    pack propagate .two 0
    wm geometry .two 300x300+100+100
    create_and_pack_frames .two
    wm deiconify .two
    waitForWindowEvent .two.f1.f2 <Enter>
    toplevel .three
    pack propagate .three 0
    wm geometry .three 300x300+110+110
    create_and_pack_frames .three
    wm deiconify .three
    waitForWindowEvent .three.f1.f2 <Enter>
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .three
    waitForWindowEvent .two.f1.f2 <Enter>
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .two
    destroy .one
    unset result
} -result {|<Enter> NotifyNonlinearVirtual .two|<Enter> NotifyNonlinearVirtual .two.f1|<Enter> NotifyNonlinear .two.f1.f2|}

test event-9.20 {Successive destructions (pointer window + ancestors including its toplevel), destination is screen root} -setup {
    setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test)
    wm withdraw .one
    toplevel .two
    pack propagate .two 0
    wm geometry .two 300x300+100+100
    create_and_pack_frames .two
    wm deiconify .two
    waitForWindowEvent .two.f1.f2 <Enter>
    bind all <Leave> {append result "<Leave> %d %W|"}
    bind all <Enter> {append result "<Enter> %d %W|"}
    set result "|"
} -body {
    destroy .two
    _pause 200; # service events (only screen drawing events expected)
    set result
} -cleanup {
    bind all <Leave> {}
    bind all <Enter> {}
    destroy .one
    unset result
} -result {|}

# cleanup
update
unset -nocomplain keypress_lookup
rename _init_keypress_lookup {}
rename _keypress_lookup {}
rename _keypress {}
rename _pause {}
rename _text_ind_to_x_y {}
rename _get_selection {}
rename create_and_pack_frames {}
rename setup_win_mousepointer {}

cleanupTests
return