Tk Source Code

Changes On Branch touchpad_events
Login

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

Changes In Branch touchpad_events Excluding Merge-Ins

This is equivalent to a diff from 5cc1a7e8 to 277c9b5a

2023-12-11
02:28
Merge implementation of TIP #684. check-in: 72276040 user: marc_culler tags: core-8-branch
2023-12-02
09:39
Updated the ttk::scrollbar test. Closed-Leaf check-in: 277c9b5a user: csaba tags: touchpad_events
2023-12-01
12:26
For X11 only: Minimize the number of artifacts caused by intermixed <MouseWheel> and <Shift-MouseWheel> events triggered by two-finger gestures. check-in: f7687c4d user: csaba tags: touchpad_events
2023-11-08
12:13
sync rules.vc with Tcl check-in: 745e75df user: jan.nijtmans tags: trunk, main
11:18
Rebase to trunk. check-in: 0f9b930d user: jan.nijtmans tags: tip-647
08:07
Rebase to trunk check-in: 13f14de9 user: jan.nijtmans tags: gripsize
2023-11-07
20:08
Implement touchpad events. See [de3bbbcb68]: macOS NSScrollWheel events not handled correctly by 8.7. check-in: 12371fec user: culler tags: touchpad_events
14:37
Merge core-8-6-branch check-in: 5cc1a7e8 user: culler tags: trunk, main
14:36
Fix [09a11fb1228f]: Aqua use-after-free can occur when a posted menu is destroyed. Thanks to Christopher Chavez. check-in: 2f9cdcb7 user: culler tags: core-8-6-branch
2023-11-06
20:17
merge mark check-in: c87ea001 user: fvogel tags: trunk, main

Changes to doc/bind.n.
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
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







-
-
-
-
-
+
+
+
+
+




















-
+













-
-
-
+
+
+
-
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







types; where two names appear together, they are synonyms.
.DS
.ta \w'\fBButton, ButtonPress\0\0\0\fR'u +\w'\fBKey, KeyPress\0\0\0\fR'u
\fBActivate\fR	\fBDestroy\fR	\fBMap\fR
\fBButton\fR, \fBButtonPress\fR	\fBEnter\fR	\fBMapRequest\fR
\fBButtonRelease\fR	\fBExpose\fR	\fBMotion\fR
\fBCirculate\fR	\fBFocusIn\fR	\fBMouseWheel\fR
\fBCirculateRequest\fR	\fBFocusOut\fR	\fBProperty\fR
\fBColormap\fR	\fBGravity\fR	\fBReparent\fR
\fBConfigure\fR	\fBKey\fR, \fBKeyPress\fR	\fBResizeRequest\fR
\fBConfigureRequest\fR	\fBKeyRelease\fR	\fBUnmap\fR
\fBCreate\fR	\fBLeave\fR	\fBVisibility\fR
\fBTouchpadScroll\fR	\fBCirculateRequest\fR	\fBFocusOut\fR
\fBProperty\fR	\fBColormap\fR	\fBGravity\fR
\fBReparent\fR	\fBConfigure\fR	\fBKey\fR, \fBKeyPress\fR
\fBResizeRequest\fR	\fBConfigureRequest\fR	\fBKeyRelease\fR
\fBUnmap\fR   \fBCreate\fR	\fBLeave\fR	\fBVisibility\fR
\fBDeactivate\fR
.DE
Most of the above events have the same fields and behaviors as events
in the X Windowing system.  You can find more detailed descriptions of
these events in any X window programming book.  A couple of the events
are extensions to the X event system to support features unique to the
Macintosh and Windows platforms.  We provide a little more detail on
these events here.  These include:
.IP "\fBActivate\fR, \fBDeactivate\fR" 5
These two events are sent to every sub-window of a toplevel when they
change state.  In addition to the focus Window, the Macintosh platform
and Windows platforms have a notion of an active window (which often
has but is not required to have the focus).  On the Macintosh, widgets
in the active window have a different appearance than widgets in
deactive windows.  The \fBActivate\fR event is sent to all the
sub-windows in a toplevel when it changes from being deactive to
active.  Likewise, the \fBDeactive\fR event is sent when the window's
state changes from active to deactive.  There are no useful percent
substitutions you would make when binding to these events.
.IP \fBMouseWheel\fR 5
Many contemporary mice support a mouse wheel, which is used
Many contemporary mice include a mouse wheel, which is used
for scrolling documents without using the scrollbars.  By rolling the
wheel, the system will generate \fBMouseWheel\fR events that the
application can use to scroll.  The event is routed to the
window currently under the mouse pointer. When the event
is received you can use the \fB%D\fR substitution to get the
\fIdelta\fR field for the event, which is an integer value describing how
the mouse wheel has moved.  The smallest value for which the
system will report is defined by the OS.  The sign of the
value determines which direction your widget should scroll.  Positive
values should scroll up and negative values should scroll down.
.RS
.PP
Horizontal scrolling uses \fBShift-MouseWheel\fR events, with positive
\fB%D\fR \fIdelta\fR substitution indicating left scrolling and negative
right scrolling.
Horizontal scrolling events may fire from
\fB%D\fR \fIdelta\fR substitution indicating left scrolling and
negative right scrolling. Horizontal scrolling events are generated
tilt wheels on some mice.  Horizontal scrolling can also be emulated
many different hardware units such as tilt wheels or touchpads.  Horizontal
scrolling can also be emulated by holding Shift and scrolling vertically.
by holding Shift and scrolling vertically.
.RE
.IP "\fBTouchpadScroll\fR" 5
On some platforms (currently Windows and macOS) there is support for
high-resolution scrolling devices, such as touchpads.  This is
provided via \fBTouchpadScroll\fR events.  These events store two
16 bit delta values in the integer provided by the \fB%D\fR
substitution.  The \fIX\fR delta is in the high order 16 bits and the
\fIY\fR delta is in the low order 16 bits.  These values can be
unpacked by using the tk::PreciseScrollDeltas utility procedure.  For
example:
.CS
lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
.CE
The \fB$#\fR substitution is a counter for \fBTouchpadScroll\fR events
which can be used by widgets that only support scrolling by units to
ignore some portion of the events.
.IP "\fBKeyPress\fR, \fBKeyRelease\fR" 5
The \fBKeyPress\fR and \fBKeyRelease\fR events are generated
whenever a key is pressed or released.  \fBKeyPress\fR and \fBKeyRelease\fR
.IP "\fBKey\fR, \fBKeyRelease\fR" 5
The \fBKey\fR and \fBKeyRelease\fR events are generated
whenever a key is pressed or released.  \fBKey\fR and \fBKeyRelease\fR
events are sent to the window which currently has the keyboard focus.
Changes to generic/tk.h.
665
666
667
668
669
670
671

672

673

674
675
676
677
678
679
680
665
666
667
668
669
670
671
672

673
674
675
676
677
678
679
680
681
682







+
-
+

+







 *----------------------------------------------------------------------
 */

#define VirtualEvent	    (MappingNotify + 1)
#define ActivateNotify	    (MappingNotify + 2)
#define DeactivateNotify    (MappingNotify + 3)
#define MouseWheelEvent     (MappingNotify + 4)
#define TouchpadScroll      (MappingNotify + 5)
#define TK_LASTEVENT	    (MappingNotify + 5)
#define TK_LASTEVENT	    (MappingNotify + 6)

#define TouchpadScrollMask  (1L << 27)
#define MouseWheelMask	    (1L << 28)
#define ActivateMask	    (1L << 29)
#define VirtualEventMask    (1L << 30)

/*
 * A virtual event shares most of its fields with the XKeyEvent and
 * XButtonEvent structures. 99% of the time a virtual event will be an
Changes to generic/tkBind.c.
524
525
526
527
528
529
530

531
532
533
534
535
536
537
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538







+







    {"Gravity",		GravityNotify,		StructureNotifyMask},
    {"Circulate",	CirculateNotify,	StructureNotifyMask},
    {"Property",	PropertyNotify,		PropertyChangeMask},
    {"Colormap",	ColormapNotify,		ColormapChangeMask},
    {"Activate",	ActivateNotify,		ActivateMask},
    {"Deactivate",	DeactivateNotify,	ActivateMask},
    {"MouseWheel",	MouseWheelEvent,	MouseWheelMask},
    {"TouchpadScroll",  TouchpadScroll,         TouchpadScrollMask},
    {"CirculateRequest", CirculateRequest,	SubstructureRedirectMask},
    {"ConfigureRequest", ConfigureRequest,	SubstructureRedirectMask},
    {"Create",		CreateNotify,		SubstructureNotifyMask},
    {"MapRequest",	MapRequest,		SubstructureRedirectMask},
    {"ResizeRequest",	ResizeRequest,		ResizeRedirectMask},
    {NULL,		0,			0}
};
628
629
630
631
632
633
634
635


636
637
638
639
640
641
642
629
630
631
632
633
634
635

636
637
638
639
640
641
642
643
644







-
+
+







   /* SelectionNotify */	0,
   /* ColormapNotify */		COLORMAP,
   /* ClientMessage */		0,
   /* MappingNotify */		0,
   /* VirtualEvent */		VIRTUAL,
   /* Activate */		ACTIVATE,
   /* Deactivate */		ACTIVATE,
   /* MouseWheel */		WHEEL
   /* MouseWheel */		WHEEL,
   /* TouchpadScroll */		WHEEL
};

/*
 * The following table is used to map between the location where an generated
 * event should be queued and the string used to specify the location.
 */

5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5014
5015
5016
5017
5018
5019
5020

5021
5022
5023
5024
5025
5026
5027







-







		}
		p = SkipFieldDelims(p);
	    }

	    eventFlags = 0;
	    if ((hPtr = Tcl_FindHashEntry(&eventTable, field))) {
		const EventInfo *eiPtr = (const EventInfo *)Tcl_GetHashValue(hPtr);

		patPtr->eventType = eiPtr->type;
		eventFlags = flagArray[eiPtr->type];
		eventMask = eiPtr->eventMask;
		p = GetField(SkipFieldDelims(p), field, sizeof(field));
	    }
	    if (*field) {
		unsigned button = GetButtonNumber(field);
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5088
5089
5090
5091
5092
5093
5094

5095
5096
5097
5098
5099
5100
5101







-







		return FinalizeParseEventDescription(
			interp,
			patPtr, 0,
			Tcl_NewStringObj("no event type or button # or keysym", TCL_INDEX_NONE), "UNMODIFIABLE");
	    } else if (patPtr->eventType == MotionNotify) {
		patPtr->info = ButtonNumberFromState(patPtr->modMask);
	    }

	    p = SkipFieldDelims(p);

	    if (*p != '>') {
		while (*p) {
		    ++p;
		    if (*p == '>') {
			return FinalizeParseEventDescription(
Changes to generic/tkEvent.c.
119
120
121
122
123
124
125
126


127
128
129
130
131
132
133
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134







-
+
+







    0,					/* SelectionNotify */
    ColormapChangeMask,			/* ColormapNotify */
    0,					/* ClientMessage */
    0,					/* Mapping Notify */
    VirtualEventMask,			/* VirtualEvents */
    ActivateMask,			/* ActivateNotify */
    ActivateMask,			/* DeactivateNotify */
    MouseWheelMask			/* MouseWheelEvent */
    MouseWheelMask,			/* MouseWheelEvent */
    TouchpadScrollMask			/* TouchpadScroll */
};

/*
 * For each exit handler created with a call to TkCreateExitHandler or
 * TkCreateThreadExitHandler there is a structure of the following type:
 */

Changes to library/demos/cscroll.tcl.
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29


30
31
32
33
34
35
36
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27


28
29
30
31
32
33
34
35
36







-
+







-
-
+
+







catch {destroy $w}
toplevel $w
wm title $w "Scrollable Canvas Demonstration"
wm iconname $w "cscroll"
positionWindow $w
set c $w.c

label $w.msg -font $font -wraplength 4i -justify left -text "This window displays a canvas widget that can be scrolled either using the scrollbars or by dragging with button 2 in the canvas.  If you click button 1 on one of the rectangles, its indices will be printed on stdout."
label $w.msg -font $font -wraplength 4i -justify left -text "This window displays a canvas widget that can be scrolled by using the scrollbars, by dragging with button 2 in the canvas, by using a mouse wheel, or with the two-finger gesture on a touchpad.  If you click button 1 on one of the rectangles, its indices will be printed on stdout."
pack $w.msg -side top

## See Code / Dismiss buttons
set btns [addSeeDismiss $w.buttons $w]
pack $btns -side bottom -fill x

frame $w.grid
ttk::scrollbar $w.hscroll -orient horizontal -command "$c xview"
ttk::scrollbar $w.vscroll -command "$c yview"
scrollbar $w.hscroll -orient horizontal -command "$c xview"
scrollbar $w.vscroll -command "$c yview"
canvas $c -relief sunken -borderwidth 2 -scrollregion {-11c -11c 50c 20c} \
	-xscrollcommand "$w.hscroll set" \
	-yscrollcommand "$w.vscroll set"
pack $w.grid -expand yes -fill both -padx 1 -pady 1
grid rowconfig    $w.grid 0 -weight 1 -minsize 0
grid columnconfig $w.grid 0 -weight 1 -minsize 0

103
104
105
106
107
108
109






110
111
112
113
114
115
116
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122







+
+
+
+
+
+







    }
    bind $c <Shift-Option-MouseWheel> {
	if {%D >= 0} {
	    %W xview scroll [expr {%D/-3}] units
	} else {
	    %W xview scroll [expr {(%D-2)/-3}] units
	}
    }
    bind $c <TouchpadScroll> {
	lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
	if {$deltaX != 0 || $deltaY != 0} {
	    tk::ScrollByPixels %W $deltaX $deltaY
	}
    }
}

if {[tk windowingsystem] eq "x11" && ![package vsatisfies [package provide Tk] 8.7-]} {
    # Support for mousewheels on Linux/Unix commonly comes through mapping
    # the wheel to the extended buttons.  If you have a mousewheel, find
    # Linux configuration info at:
Changes to library/demos/items.tcl.
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
47
48
49







+
+
+
+
+
+
+








canvas $c -scrollregion {0c 0c 30c 24c} -width 15c -height 10c \
	-relief sunken -borderwidth 2 \
	-xscrollcommand "$w.frame.hscroll set" \
	-yscrollcommand "$w.frame.vscroll set"
ttk::scrollbar $w.frame.vscroll -command "$c yview"
ttk::scrollbar $w.frame.hscroll -orient horizontal -command "$c xview"

bind $c <TouchpadScroll> {
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    if {$deltaX != 0 || $deltaY != 0} {
	tk::ScrollByPixels %W $deltaX $deltaY
    }
}

grid $c -in $w.frame \
    -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news
grid $w.frame.vscroll \
    -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news
grid $w.frame.hscroll \
    -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news
Changes to library/listbox.tcl.
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
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







-

-
+


-
+


-
+


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








bind Listbox <Button-2> {
    %W scan mark %x %y
}
bind Listbox <B2-Motion> {
    %W scan dragto %x %y
}

bind Listbox <MouseWheel> {
    tk::MouseWheel %W y %D -40.0
    tk::MouseWheel %W y %D -40.0 units
}
bind Listbox <Option-MouseWheel> {
    tk::MouseWheel %W y %D -12.0
    tk::MouseWheel %W y %D -12.0 units
}
bind Listbox <Shift-MouseWheel> {
    tk::MouseWheel %W x %D -40.0
    tk::MouseWheel %W x %D -40.0 units
}
bind Listbox <Shift-Option-MouseWheel> {
    tk::MouseWheel %W x %D -12.0
    tk::MouseWheel %W x %D -12.0 units
}
bind Listbox <TouchpadScroll> {
    if {[expr {%# %% 15}] != 0} {
	return
    }
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    if {$deltaX != 0} {
 	%W xview scroll [expr {-$deltaX}] units 
    }
    if {$deltaY != 0} {
	%W yview scroll [expr {-$deltaY}] units
    }
}

# ::tk::ListboxBeginSelect --
#
# This procedure is typically invoked on button-1 presses.  It begins
# the process of making a selection in the listbox.  Its exact behavior
# depends on the selection mode currently in effect for the listbox;
Changes to library/scrlbar.tcl.
125
126
127
128
129
130
131



132
133

134
135






136









137
138
139
140
141
142
143
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







+
+
+

-
+


+
+
+
+
+
+

+
+
+
+
+
+
+
+
+







    tk::ScrollToPos %W 0
}
bind Scrollbar <<LineEnd>> {
    tk::ScrollToPos %W 1
}
}

bind Scrollbar <Enter> {+ 
    set tk::Priv(xEvents) 0; set tk::Priv(yEvents) 0
}
bind Scrollbar <MouseWheel> {
    tk::ScrollByUnits %W hv %D -40.0
    tk::ScrollByUnits %W vh %D -40.0
}
bind Scrollbar <Option-MouseWheel> {
    tk::ScrollByUnits %W vh %D -12.0
}
bind Scrollbar <Shift-MouseWheel> {
    tk::ScrollByUnits %W hv %D -40.0
}
bind Scrollbar <Shift-Option-MouseWheel> {
    tk::ScrollByUnits %W hv %D -12.0
}
bind Scrollbar <TouchpadScroll> {
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    if {$deltaX != 0 && [%W cget -orient] eq "horizontal"} {
	tk::ScrollbarScrollByPixels %W h $deltaX
    }
    if {$deltaY != 0 && [%W cget -orient] eq "vertical"} {
	tk::ScrollbarScrollByPixels %W v $deltaY
    }
}

# tk::ScrollButtonDown --
# This procedure is invoked when a button is pressed in a scrollbar.
# It changes the way the scrollbar is displayed and takes actions
# depending on where the mouse is.
#
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    if {[$w cget -jump]} {
	set delta [$w delta [expr {$x - $Priv(pressX)}] \
		[expr {$y - $Priv(pressY)}]]
	ScrollToPos $w [expr {$Priv(initPos) + $delta}]
    }
    set Priv(initPos) ""
}

# ::tk::ScrollbarScrollByPixels --
# This procedure tells the scrollbar's associated widget to scroll up
# or down by a given number of pixels.  It only works with scrollbars
# because it uses the delta command.
#
# Arguments:
# w -		The scrollbar widget.
# orient -	Which kind of scrollbar this applies to: "h" for
#		horizontal, "v" for vertical.
# amount -	How many pixels to scroll.

proc ::tk::ScrollbarScrollByPixels {w orient amount} {
    set cmd [$w cget -command]
    if {$cmd eq ""} {
	return
    }
    set xyview [lindex [split $cmd] end]
    if {$orient eq "v"} {
	if {$xyview eq "xview"} {
	    return
	}
	set size [winfo height $w]
    }
    if {$orient eq "h"} {
	if {$xyview eq "yview"} {
	    return
	}
	set size [winfo width $w]
    }

    # The code below works with both the current and old syntax for
    # the scrollbar get command.

    set info [$w get]
    if {[llength $info] == 2} {
	set first [lindex $info 0]
    } else {
	set first [lindex $info 2]
    }
    set pixels [expr {-$amount}]
    uplevel #0 $cmd moveto [expr $first + [$w delta $pixels $pixels]]
}

# ::tk::ScrollByUnits --
# This procedure tells the scrollbar's associated widget to scroll up
# or down by a given number of units.  It notifies the associated widget
# in different ways for old and new command syntaxes.
#
# Arguments:
# w -		The scrollbar widget.
# orient -	Which kinds of scrollbars this applies to:  "h" for
#		horizontal, "v" for vertical, "hv" for both.
#		horizontal, "v" for vertical, "hv" or "vh" for both.
# amount -	How many units to scroll:  typically 1 or -1.

proc ::tk::ScrollByUnits {w orient amount {factor 1.0}} {
    set cmd [$w cget -command]
    if {$cmd eq "" || ([string first \
	    [string index [$w cget -orient] 0] $orient] < 0)} {
	return
    }

    if {[string length $orient] == 2 && $factor != 1.0} {
	# Count both the <MouseWheel> and <Shift-MouseWheel>
	# events, and ignore the non-dominant ones

	variable ::tk::Priv
	set axis [expr {[string index $orient 0] eq "h" ? "x" : "y"}]
	incr Priv(${axis}Events)
	if {($Priv(xEvents) + $Priv(yEvents) > 10) &&
		($axis eq "x" && $Priv(xEvents) < $Priv(yEvents) ||
		 $axis eq "y" && $Priv(yEvents) < $Priv(xEvents))} {
	    return
	}
    }

    set info [$w get]
    if {[llength $info] == 2} {
	uplevel #0 $cmd scroll [expr {$amount/$factor}] units
    } else {
	uplevel #0 $cmd [expr {[lindex $info 2] + [expr {$amount/$factor}]}]
    }
}
Changes to library/text.tcl.
463
464
465
466
467
468
469









470
471
472
473
474
475
476
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485







+
+
+
+
+
+
+
+
+







    tk::MouseWheel %W y [tk::ScaleNum %D] -1.2 pixels
}
bind Text <Shift-MouseWheel> {
    tk::MouseWheel %W x [tk::ScaleNum %D] -4.0 pixels
}
bind Text <Shift-Option-MouseWheel> {
    tk::MouseWheel %W x [tk::ScaleNum %D] -1.2 pixels
}
bind Text <TouchpadScroll> {
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    if {$deltaX != 0} {
	%W xview scroll [tk::ScaleNum [expr {-$deltaX}]] pixels
    }
    if {$deltaY != 0} {
	%W yview scroll [tk::ScaleNum [expr {-$deltaY}]] pixels
    }
}

# ::tk::TextClosestGap --
# Given x and y coordinates, this procedure finds the closest boundary
# between characters to the given coordinates and returns the index
# of the character just after the boundary.
#
Changes to library/tk.tcl.
545
546
547
548
549
550
551







552
553
554
555
556
557
558
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565







+
+
+
+
+
+
+








## ::tk::MouseWheel $w $dir $amount $factor $units

proc ::tk::MouseWheel {w dir amount {factor -120.0} {units units}} {
    $w ${dir}view scroll [expr {$amount/$factor}] $units
}

## ::tk::PreciseScrollDeltas $dxdy
proc ::tk::PreciseScrollDeltas {dxdy} {
    set deltaX [expr {$dxdy >> 16}]
    set low [expr {$dxdy & 0xffff}]
    set deltaY [expr {$low < 0x8000 ? $low : $low - 0x10000}] 
    return [list $deltaX $deltaY]
}

# ::tk::TabToWindow --
# This procedure moves the focus to the given widget.
# It sends a <<TraverseOut>> virtual event to the previous focus window,
# if any, before changing the focus, and a <<TraverseIn>> event
# to the new focus window afterwards.
#
833
834
835
836
837
838
839















840
841
842
843
844
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





    option add *Scrollbar.width		8.25p widgetDefault
}

# Run the Ttk themed widget set initialization
if {$::ttk::library ne ""} {
    uplevel \#0 [list source -encoding utf-8 $::ttk::library/ttk.tcl]
}

# Helper for smooth scrolling of widgets that support xview moveto,
# yview moveto, height and width.

proc ::tk::ScrollByPixels {w deltaX deltaY} {
    set width [expr {1.0 * [$w cget -width]}]
    set height [expr {1.0 * [$w cget -height]}]
    set X [lindex [$w xview] 0]
    set Y [lindex [$w yview] 0]
    set x [expr {$X - $deltaX / $width}]
    set y [expr {$Y - $deltaY / $height}]
    $w xview moveto $x
    $w yview moveto $y
}


# Local Variables:
# mode: tcl
# fill-column: 78
# End:
Changes to library/ttk/combobox.tcl.
48
49
50
51
52
53
54
55
56











57
58
59
60
61
62
63
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







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







bind TCombobox <Button-1> 		{ ttk::combobox::Press "" %W %x %y }
bind TCombobox <Shift-Button-1>		{ ttk::combobox::Press "s" %W %x %y }
bind TCombobox <Double-Button-1> 	{ ttk::combobox::Press "2" %W %x %y }
bind TCombobox <Triple-Button-1> 	{ ttk::combobox::Press "3" %W %x %y }
bind TCombobox <B1-Motion>		{ ttk::combobox::Drag %W %x }
bind TCombobox <Motion>			{ ttk::combobox::Motion %W %x %y }

ttk::bindMouseWheel TCombobox [list ttk::combobox::Scroll %W]

ttk::bindMouseWheel TCombobox		{ ttk::combobox::Scroll %W }
bind TCombobox <Shift-MouseWheel> {
    # Ignore the event
}
bind TCombobox <TouchpadScroll> {
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    # TouchpadScroll events fire about 60 times per second.
    if {$deltaY != 0 && [expr {%# %% 15}] == 0} {
	ttk::combobox::Scroll %W [expr {$deltaY > 0 ? -1 : 1}]
    }
}
bind TCombobox <<TraverseIn>> 		{ ttk::combobox::TraverseIn %W }

### Combobox listbox bindings.
#
bind ComboboxListbox <ButtonRelease-1>	{ ttk::combobox::LBSelected %W }
bind ComboboxListbox <Return>		{ ttk::combobox::LBSelected %W }
bind ComboboxListbox <Escape>		{ ttk::combobox::LBCancel %W }
Changes to library/ttk/notebook.tcl.
12
13
14
15
16
17
18


















19



20
21
22
23
24
25
26
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







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







bind TNotebook <Control-Tab>		{ ttk::notebook::CycleTab %W  1; break }
bind TNotebook <Control-Shift-Tab>	{ ttk::notebook::CycleTab %W -1; break }
catch {
bind TNotebook <Control-ISO_Left_Tab>	{ ttk::notebook::CycleTab %W -1; break }
}
bind TNotebook <Destroy>		{ ttk::notebook::Cleanup %W }

bind TNotebook <Enter> {
    set tk::Priv(xEvents) 0; set tk::Priv(yEvents) 0
}
bind TNotebook <MouseWheel> {
    ttk::notebook::CondCycleTab1 %W y %D -120.0
}
bind TNotebook <Option-MouseWheel> {
    ttk::notebook::CondCycleTab1 %W y %D -12.0
}
bind TNotebook <Shift-MouseWheel> {
    ttk::notebook::CondCycleTab1 %W x %D -120.0
}
bind TNotebook <Shift-Option-MouseWheel> {
    ttk::notebook::CondCycleTab1 %W x %D -12.0
}
bind TNotebook <TouchpadScroll> {
    # TouchpadScroll events fire about 60 times per second.
    if {[expr {%# %% 30}] == 0} {
ttk::bindMouseWheel TNotebook		[list ttk::notebook::CycleTab %W]
	ttk::notebook::CondCycleTab2 %W %D
    }
}

# ActivateTab $nb $tab --
#	Select the specified tab and set focus.
#
#  Desired behavior:
#	+ take focus when reselecting the currently-selected tab;
#	+ keep focus if the notebook already has it;
70
71
72
73
74
75
76



































77
78
79
80
81
82
83
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	    set select [expr {($select + $step) % $tabCount}]
	}
	if {$select != $current} {
	    ActivateTab $w $select
	}
    }
}

# CondCycleTab1 --
#	Conditionally invoke the ttk::notebook::CycleTab proc.
#
proc ttk::notebook::CondCycleTab1 {w axis dir {factor 1.0}} {
    # Count both the <MouseWheel> and <Shift-MouseWheel>
    # events, and ignore the non-dominant ones

    variable ::tk::Priv
    incr Priv(${axis}Events)
    if {($Priv(xEvents) + $Priv(yEvents) > 10) &&
	    ($axis eq "x" && $Priv(xEvents) < $Priv(yEvents) ||
	     $axis eq "y" && $Priv(yEvents) < $Priv(xEvents))} {
	return
    }

    CycleTab $w $dir $factor
}

# CondCycleTab2 --
#	Conditionally invoke the ttk::notebook::CycleTab proc.
#
proc ttk::notebook::CondCycleTab2 {w dxdy} {
    if {[set style [$w cget -style]] eq ""} {
	set style TNotebook
    }
    set tabSide [string index [ttk::style lookup $style -tabposition {} nw] 0]

    lassign [tk::PreciseScrollDeltas $dxdy] deltaX deltaY
    if {$tabSide in {n s} && $deltaX != 0} {
	CycleTab $w [expr {$deltaX < 0 ? -1 : 1}]
    } elseif {$tabSide in {w e} && $deltaY != 0} {
	CycleTab $w [expr {$deltaY < 0 ? -1 : 1}]
    }
}

# MnemonicTab $nb $key --
#	Scan all tabs in the specified notebook for one with the
#	specified mnemonic. If found, returns path name of tab;
#	otherwise returns ""
#
proc ttk::notebook::MnemonicTab {nb key} {
Changes to library/ttk/scrollbar.tcl.
13
14
15
16
17
18
19
20

21
22
23









24
25
26
27
28
29
30
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







-
+

-
-
+
+
+
+
+
+
+
+
+







bind TScrollbar <B1-Motion>		{ ttk::scrollbar::Drag %W %x %y }
bind TScrollbar <ButtonRelease-1>	{ ttk::scrollbar::Release %W %x %y }

bind TScrollbar <Button-2> 		{ ttk::scrollbar::Jump %W %x %y }
bind TScrollbar <B2-Motion>		{ ttk::scrollbar::Drag %W %x %y }
bind TScrollbar <ButtonRelease-2>	{ ttk::scrollbar::Release %W %x %y }

# Redirect scrollwheel bindings to the scrollbar widget
# Copy the mouse wheel event bindings from Scrollbar to TScrollbar
#
bind TScrollbar <MouseWheel> [bind Scrollbar <MouseWheel>]
bind TScrollbar <Option-MouseWheel> [bind Scrollbar <Option-MouseWheel>]
bind TScrollbar <Enter> {
    set tk::Priv(xEvents) 0; set tk::Priv(yEvents) 0
}
foreach event {<MouseWheel> <Option-MouseWheel>
	       <Shift-MouseWheel> <Shift-Option-MouseWheel>
	       <TouchpadScroll>} {
    bind TScrollbar $event [bind Scrollbar $event]
}
unset event

proc ttk::scrollbar::Scroll {w n units} {
    set cmd [$w cget -command]
    if {$cmd ne ""} {
	uplevel #0 $cmd scroll $n $units
    }
}
Changes to library/ttk/spinbox.tcl.
19
20
21
22
23
24
25
26











27
28
29
30
31
32
33
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







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








bind TSpinbox <Up>			{ event generate %W <<Increment>> }
bind TSpinbox <Down> 			{ event generate %W <<Decrement>> }

bind TSpinbox <<Increment>>		{ ttk::spinbox::Spin %W +1 }
bind TSpinbox <<Decrement>> 		{ ttk::spinbox::Spin %W -1 }

ttk::bindMouseWheel TSpinbox 		[list ttk::spinbox::Spin %W]
ttk::bindMouseWheel TSpinbox 		{ ttk::spinbox::Spin %W }
bind TSpinbox <Shift-MouseWheel> {
    # Ignore the event
}
bind TSpinbox <TouchpadScroll> {
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    # TouchpadScroll events fire about 60 times per second.
    if {$deltaY != 0 && [expr {%# %% 12}] == 0} {
	ttk::spinbox::Spin %W [expr {$deltaY > 0 ? -1 : 1}]
    }
}

## Motion --
#	Sets cursor.
#
proc ttk::spinbox::Motion {w x y} {
    variable State
    ttk::saveCursor $w State(userConfCursor) [ttk::cursor text]
Changes to library/ttk/utils.tcl.
297
298
299
300
301
302
303














304
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318







+
+
+
+
+
+
+
+
+
+
+
+
+
+

bind TtkScrollable <Option-MouseWheel> \
	{ tk::MouseWheel %W y %D -12.0 }
bind TtkScrollable <Shift-MouseWheel> \
	{ tk::MouseWheel %W x %D -40.0 }
bind TtkScrollable <Shift-Option-MouseWheel> \
	{ tk::MouseWheel %W x %D -12.0 }

## Touchpad scrolling
#
bind TtkScrollable <TouchpadScroll> {
    if {[expr {%# %% 15}] != 0} {
        return
    }
    lassign [tk::PreciseScrollDeltas %D] deltaX deltaY
    if {$deltaX != 0} {
 	%W xview scroll [expr {-$deltaX}] units 
    }
    if {$deltaY != 0} {
	%W yview scroll [expr {-$deltaY}] units
    }
}
#*EOF*
Changes to macosx/tkMacOSXMouseEvent.c.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
21
22
23
24
25
26
27







28
29
30
31
32
33
34







-
-
-
-
-
-
-







    unsigned int state;
    long delta;
    Window window;
    Point global;
    Point local;
} MouseEventData;

typedef struct {
    uint64_t wheelTickPrev;	/* For high resolution wheels. */
    double vWheelAcc;		/* For high resolution wheels (vertical). */
    double hWheelAcc;		/* For high resolution wheels (horizontal). */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

static Tk_Window captureWinPtr = NULL;	/* Current capture window; may be
					 * NULL. */

static int		GenerateButtonEvent(MouseEventData *medPtr);

#pragma mark TKApplication(TKMouseEvent)

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
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







+
+
-
+
+

-
-
-
-







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

-
+
-



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

-
+
-







		 */

	    }
	} else {
	    Tk_UpdatePointer(target, global.x, global.y, state);
	}
    } else {
	static int scrollCounter = 0;
	int delta;
	CGFloat delta;
	CGFloat Delta;
	Bool deltaIsPrecise = [theEvent hasPreciseScrollingDeltas];
	XEvent xEvent = {0};
	ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
		Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

	xEvent.type = MouseWheelEvent;
	xEvent.xbutton.x = win_x;
	xEvent.xbutton.y = win_y;
	xEvent.xbutton.x_root = global.x;
	xEvent.xbutton.y_root = global.y;
	xEvent.xany.send_event = false;
	xEvent.xany.display = Tk_Display(target);
	xEvent.xany.window = Tk_WindowId(target);

#define WHEEL_DELTA 120
#define WHEEL_DELAY 300000000
	uint64_t wheelTick = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW);
	if (deltaIsPrecise) {
	    int deltaX = [theEvent scrollingDeltaX];
	    int deltaY = [theEvent scrollingDeltaY];
	    delta = (deltaX << 16) | (deltaY & 0xffff);
	Bool timeout = (wheelTick - tsdPtr->wheelTickPrev) >= WHEEL_DELAY;
	if (timeout) {
	    tsdPtr->vWheelAcc = tsdPtr->hWheelAcc = 0;
	}
	tsdPtr->wheelTickPrev = wheelTick;
	delta = [theEvent deltaY];
	if (delta != 0.0) {
	    delta = (tsdPtr->vWheelAcc += delta);
	    if (delta != 0) {
	     	xEvent.type = TouchpadScroll;
	     	xEvent.xbutton.state = state;
	     	xEvent.xkey.keycode = delta;
	     	xEvent.xany.serial = scrollCounter++;
	     	Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
	    }
	} else {
	    /*
	     * A low precision scroll is 120 or -120 wheel units per click.
	     * Each click generates one event.
	     */
	    Delta = [theEvent scrollingDeltaY];
	    if (Delta != 0.0) {
		xEvent.type = MouseWheelEvent;
	    if (timeout && fabs(delta) < 1.0) {
		delta = ((delta < 0.0) ? -1.0 : 1.0);
	    }
	    if (fabs(delta) >= 0.6) {
		int intDelta = round(delta);
		xEvent.xbutton.state = state;
		xEvent.xkey.keycode = WHEEL_DELTA * intDelta;
		xEvent.xkey.keycode = Delta > 0.0 ? 120 : -120;
		tsdPtr->vWheelAcc -= intDelta;
		xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
		Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
	    }
	}
	delta = [theEvent deltaX];
	if (delta != 0.0) {
	    delta = (tsdPtr->hWheelAcc += delta);
	    Delta = [theEvent scrollingDeltaX];
	    if (Delta != 0.0) {
		xEvent.type = MouseWheelEvent;
	    if (timeout && fabs(delta) < 1.0) {
		delta = ((delta < 0.0) ? -1.0 : 1.0);
	    }
	    if (fabs(delta) >= 0.6) {
	    int intDelta = round(delta);
		xEvent.xbutton.state = state | ShiftMask;
		xEvent.xkey.keycode = WHEEL_DELTA * intDelta;
		xEvent.xkey.keycode = Delta > 0 ? 120 : -120;
		tsdPtr->hWheelAcc -= intDelta;
		xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
		Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
	    }
	}
    }

    /*
Changes to tests/scrollbar.test.
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
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







+















+














+







    destroy .t .s
} -body {
    pack [text .t -yscrollcommand {.s set}] -side left
    for {set i 1} {$i < 100} {incr i} {.t insert end "Line $i\n"}
    pack [scrollbar .s -command {.t yview}] -fill y -expand 1 -side left
    update
    focus -force .s
    event generate .s <Enter>
    event generate .s <MouseWheel> -delta -120
    after 200 {set eventprocessed 1} ; vwait eventprocessed
    .t index @0,0
} -cleanup {
    destroy .t .s
} -result {4.0}

test scrollbar-10.2 {<MouseWheel> event on scrollbar} -setup {
    destroy .t .s
} -body {
    pack [text .t -xscrollcommand {.s set} -wrap none] -side top
    for {set i 1} {$i < 100} {incr i} {.t insert end "Char $i "}
    pack [scrollbar .s -command {.t xview} -orient horizontal] -fill x -expand 1 -side top
    update
    focus -force .s
    event generate .s <Enter>
    event generate .s <Shift-MouseWheel> -delta -120
    after 200 {set eventprocessed 1} ; vwait eventprocessed
    .t index @0,0
} -cleanup {
    destroy .t .s
} -result {1.3}
test scrollbar-10.3 {<MouseWheel> event on horizontal scrollbar} -setup {
    destroy .t .s
} -body {
    pack [text .t -xscrollcommand {.s set} -wrap none] -side top
    for {set i 1} {$i < 100} {incr i} {.t insert end "Char $i "}
    pack [scrollbar .s -command {.t xview} -orient horizontal] -fill x -expand 1 -side top
    update
    focus -force .s
    event generate .s <Enter>
    event generate .s <MouseWheel> -delta -120
    after 200 {set eventprocessed 1} ; vwait eventprocessed
    .t index @0,0
} -cleanup {
    destroy .t .s
} -result {1.3}

Changes to tests/ttk/scrollbar.test.
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
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







+















+














+







    destroy .t .s
} -body {
    pack [text .t -yscrollcommand {.s set}] -side left
    for {set i 1} {$i < 100} {incr i} {.t insert end "Line $i\n"}
    pack [ttk::scrollbar .s -command {.t yview}] -fill y -expand 1 -side left
    update
    focus -force .s
    event generate .s <Enter>
    event generate .s <MouseWheel> -delta -120
    after 200 {set eventprocessed 1} ; vwait eventprocessed
    .t index @0,0
} -cleanup {
    destroy .t .s
} -result {4.0}

test scrollbar-10.2.1 {<Shift-MouseWheel> event on horizontal scrollbar} -setup {
    destroy .t .s
} -body {
    pack [text .t -xscrollcommand {.s set} -wrap none] -side top
    for {set i 1} {$i < 100} {incr i} {.t insert end "Char $i "}
    pack [ttk::scrollbar .s -command {.t xview} -orient horizontal] -fill x -expand 1 -side top
    update
    focus -force .s
    event generate .s <Enter>
    event generate .s <Shift-MouseWheel> -delta -120
    after 200 {set eventprocessed 1} ; vwait eventprocessed
    .t index @0,0
} -cleanup {
    destroy .t .s
} -result {1.3}
test scrollbar-10.2.2 {<MouseWheel> event on horizontal scrollbar} -setup {
    destroy .t .s
} -body {
    pack [text .t -xscrollcommand {.s set} -wrap none] -side top
    for {set i 1} {$i < 100} {incr i} {.t insert end "Char $i "}
    pack [ttk::scrollbar .s -command {.t xview} -orient horizontal] -fill x -expand 1 -side top
    update
    focus -force .s
    event generate .s <Enter>
    event generate .s <MouseWheel> -delta -120
    after 200 {set eventprocessed 1} ; vwait eventprocessed
    .t index @0,0
} -cleanup {
    destroy .t .s
} -result {1.3}

Changes to win/tkWinX.c.
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
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







+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#include "tkWinInt.h"

#include <commctrl.h>
#ifdef _MSC_VER
#   pragma comment (lib, "comctl32.lib")
#   pragma comment (lib, "advapi32.lib")
#endif


/*
 * The zmouse.h file includes the definition for WM_MOUSEWHEEL.
 */

#include <zmouse.h>

/*
 * WM_MOUSEHWHEEL is normally defined by Winuser.h for Vista/2008 or later,
 * but is also usable on 2000/XP if IntelliPoint drivers are installed.
 */

#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
#endif

/* A WM_MOUSEWHEEL message sent by a trackpad contains the number of pixels as
 * the delta value, while low precision scrollwheels always send an integer
 * multiple of WHEELDELTA (= 120) as the delta value.
 */

#define WHEELDELTA 120

/*
 * Our heuristic for deciding whether a WM_MOUSEWHEEL message
 * comes from a high resolution scrolling device is that we
 * assume it is high resolution unless there are two consecutive
 * delta values that are both multiples of 120.  This is static,
 * rather than thread-specific, since input devices are shared
 * by all threads.
 */

static int lastMod = 0;

/* 
 * The serial field of TouchpadScroll events is a counter for
 * events of this type only.
 */

static int scrollCounter = 0;

/*
 * imm.h is needed by HandleIMEComposition
 */

#include <imm.h>
#ifdef _MSC_VER
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
103
104
105
106
107
108
109



110
111
112
113
114
115
116







-
-
-







 */

typedef struct {
    TkDisplay *winDisplay;	/* TkDisplay structure that represents Windows
				 * screen. */
    int updatingClipboard;	/* If 1, we are updating the clipboard. */
    int surrogateBuffer;	/* Buffer for first of surrogate pair. */
    DWORD wheelTickPrev;	/* For high resolution wheels. */
    int vWheelAcc;		/* For high resolution wheels (vertical). */
    int hWheelAcc;		/* For high resolution wheels (horizontal). */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

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

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
553
554
555
556
557
558
559



560
561
562
563
564
565
566







-
-
-







    display = XkbOpenDisplay((char *)display_name, NULL, NULL, NULL, NULL, NULL);
    TkWinDisplayChanged(display);

    tsdPtr->winDisplay =(TkDisplay *) ckalloc(sizeof(TkDisplay));
    memset(tsdPtr->winDisplay, 0, sizeof(TkDisplay));
    tsdPtr->winDisplay->display = display;
    tsdPtr->updatingClipboard = FALSE;
    tsdPtr->wheelTickPrev = GetTickCount();
    tsdPtr->vWheelAcc = 0;
    tsdPtr->hWheelAcc = 0;

    /*
     * Key map info must be available immediately, because of "send event".
     */
    TkpInitKeymapInfo(tsdPtr->winDisplay);

    /*
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
1185
1186
1187




1188
1189
1190



1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
















1204


1205
1206
1207
1208
1209
1210
1211
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



1185
1186
1187
1188
1189











1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205

1206
1207
1208
1209
1210
1211
1212
1213
1214







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

-
-
-
-
+
+
+
+
+



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



-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-

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


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








	/*
	 * Now set up event specific fields.
	 */

	switch (message) {
	case WM_MOUSEWHEEL: {
	    /*
	     * Support for high resolution wheels (vertical).
	     */

	    
	    DWORD wheelTick = GetTickCount();
	    BOOL timeout = wheelTick - tsdPtr->wheelTickPrev >= 300;
	    int intDelta;

	    tsdPtr->wheelTickPrev = wheelTick;
	    if (timeout) {
		tsdPtr->vWheelAcc = tsdPtr->hWheelAcc = 0;
	    }
	    tsdPtr->vWheelAcc += (short) HIWORD(wParam);
	    if (!tsdPtr->vWheelAcc || (!timeout && abs(tsdPtr->vWheelAcc) < WHEEL_DELTA * 6 / 10)) {
		return;
	    }

	    /*
	     * We have invented a new X event type to handle this event. It
	     * still uses the KeyPress struct. However, the keycode field has
	     * been overloaded to hold the zDelta of the wheel. Set nbytes to
	     * 0 to prevent conversion of the keycode to a keysym in
	     * Send an Xevent using a KeyPress struct, but with the type field
	     * set to MouseWheelEvent for low resolution scrolls and to
	     * TouchpadScroll for high resolution scroll events. The Y delta
	     * is stored in the low order 16 bits of the keycode field.  Set
	     * nbytes to 0 to prevent conversion of the keycode to a keysym in
	     * TkpGetString. [Bug 1118340].
	     */

	    intDelta = (abs(tsdPtr->vWheelAcc) + WHEEL_DELTA/2) / WHEEL_DELTA * WHEEL_DELTA;
	    if (intDelta == 0) {
	    int delta = (short) HIWORD(wParam);
	    int mod = delta % WHEELDELTA;
		intDelta = (tsdPtr->vWheelAcc < 0) ? -WHEEL_DELTA : WHEEL_DELTA;
	    } else if (tsdPtr->vWheelAcc < 0) {
		intDelta = -intDelta;
	    }
	    event.x.type = MouseWheelEvent;
	    event.x.xany.send_event = -1;
	    event.key.nbytes = 0;
	    event.x.xkey.keycode = intDelta;
	    if ( mod != 0 || lastMod != 0) {
		/* High resolution. */
		event.x.type = TouchpadScroll;
		event.x.xany.send_event = -1;
		event.key.nbytes = 0;
		event.x.xkey.state = state;
		event.x.xany.serial = scrollCounter++;
		event.x.xkey.keycode = (unsigned int) delta;
	    } else {
		event.x.type = MouseWheelEvent;
		event.x.xany.send_event = -1;
		event.key.nbytes = 0;
		event.x.xkey.keycode = (unsigned int) delta;
	    tsdPtr->vWheelAcc -= intDelta;
	    }
	    lastMod = mod;
	    break;
	}
	case WM_MOUSEHWHEEL: {
	    /*
	     * Support for high resolution wheels (horizontal).
	     */

	    DWORD wheelTick = GetTickCount();
	    BOOL timeout = wheelTick - tsdPtr->wheelTickPrev >= 300;
	    int intDelta;

	    tsdPtr->wheelTickPrev = wheelTick;
	    if (timeout) {
		tsdPtr->vWheelAcc = tsdPtr->hWheelAcc = 0;
	    }
	    tsdPtr->hWheelAcc -= (short) HIWORD(wParam);
	    if (!tsdPtr->hWheelAcc || (!timeout && abs(tsdPtr->hWheelAcc) < WHEEL_DELTA * 6 / 10)) {
		return;
	    }

	    /*
	     * We have invented a new X event type to handle this event. It
	     * still uses the KeyPress struct. However, the keycode field has
	     * Send an Xevent using a KeyPress struct, but with the type field
	     * set to MouseWheelEvent for low resolution scrolls and to
	     * TouchpadScroll for high resolution scroll events.  For low
	     * resolution scrolls the X delta is stored in the keycode field
	     * been overloaded to hold the zDelta of the wheel. Set nbytes to
	     * 0 to prevent conversion of the keycode to a keysym in
	     * TkpGetString. [Bug 1118340].
	     * and For high resolution scrolls the X delta is in the high word
	     * of the keycode.  Set nbytes to 0 to prevent conversion of the
	     * keycode to a keysym in TkpGetString. [Bug 1118340].
	     */

	    intDelta =  (abs(tsdPtr->hWheelAcc) + WHEEL_DELTA/2) / WHEEL_DELTA * WHEEL_DELTA;
	    if (intDelta == 0) {
		intDelta = (tsdPtr->hWheelAcc < 0) ? -WHEEL_DELTA : WHEEL_DELTA;
	    } else if (tsdPtr->hWheelAcc < 0) {
		intDelta = -intDelta;
	    }
	    event.x.type = MouseWheelEvent;
	    event.x.xany.send_event = -1;
	    event.key.nbytes = 0;
	    event.x.xkey.state |= ShiftMask;
	    event.x.xkey.keycode = intDelta;
	    int delta = (short) HIWORD(wParam);
	    int mod = delta % WHEELDELTA;
	    if ( mod != 0 || lastMod != 0) {
		/* High resolution. */
		event.x.type = TouchpadScroll;
		event.x.xany.send_event = -1;
		event.key.nbytes = 0;
		event.x.xkey.state = state;
		event.x.xany.serial = scrollCounter++;
		event.x.xkey.keycode = (unsigned int)(-(delta << 16));
	    } else {
		event.x.type = MouseWheelEvent;
		event.x.xany.send_event = -1;
		event.key.nbytes = 0;
		event.x.xkey.state |= ShiftMask;
		event.x.xkey.keycode = delta;
	    tsdPtr->hWheelAcc -= intDelta;
	    }
	    lastMod = mod;
	    break;
	}
	case WM_SYSKEYDOWN:
	case WM_KEYDOWN:
	    /*
	     * Check for translated characters in the event queue. Setting
	     * xany.send_event to -1 indicates to the Windows implementation