summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormarc_culler <marc.culler@gmail.com>2023-12-11 02:28:43 (GMT)
committermarc_culler <marc.culler@gmail.com>2023-12-11 02:28:43 (GMT)
commit730e3b750ea3dfdd1e339bc82743ee4d4b9bffbd (patch)
treec891bd75c4bc34d13042ffae147599a11ce9ebab
parent8a1948af29ccf03658724cf589bdf9deb773911b (diff)
parentfa30d6e92c313f3f6cfb3d1a9f99da4c04fe4022 (diff)
downloadtk-730e3b750ea3dfdd1e339bc82743ee4d4b9bffbd.zip
tk-730e3b750ea3dfdd1e339bc82743ee4d4b9bffbd.tar.gz
tk-730e3b750ea3dfdd1e339bc82743ee4d4b9bffbd.tar.bz2
Merge implementation of TIP #684.
-rw-r--r--doc/bind.n36
-rw-r--r--generic/tk.h4
-rw-r--r--generic/tkBind.c6
-rw-r--r--generic/tkEvent.c3
-rw-r--r--library/demos/cscroll.tcl12
-rw-r--r--library/demos/items.tcl7
-rw-r--r--library/listbox.tcl21
-rw-r--r--library/scrlbar.tcl52
-rw-r--r--library/text.tcl9
-rw-r--r--library/tk.tcl22
-rw-r--r--library/ttk/combobox.tcl8
-rw-r--r--library/ttk/notebook.tcl35
-rw-r--r--library/ttk/scrollbar.tcl3
-rw-r--r--library/ttk/spinbox.tcl7
-rw-r--r--library/ttk/utils.tcl14
-rw-r--r--macosx/tkMacOSXMouseEvent.c68
-rw-r--r--win/tkWinX.c141
17 files changed, 306 insertions, 142 deletions
diff --git a/doc/bind.n b/doc/bind.n
index 6e47637..9fb5c07 100644
--- a/doc/bind.n
+++ b/doc/bind.n
@@ -173,11 +173,11 @@ types; where two names appear together, they are synonyms.
\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
@@ -198,7 +198,7 @@ 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
@@ -212,12 +212,26 @@ 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
-many different hardware units such as tilt wheels or touchpads. Horizontal
-scrolling can also be emulated by holding Shift and scrolling vertically.
+\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
+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
diff --git a/generic/tk.h b/generic/tk.h
index 1eb2254..6a5df0d 100644
--- a/generic/tk.h
+++ b/generic/tk.h
@@ -670,8 +670,10 @@ typedef struct Tk_GeomMgr {
#define ActivateNotify (MappingNotify + 2)
#define DeactivateNotify (MappingNotify + 3)
#define MouseWheelEvent (MappingNotify + 4)
-#define TK_LASTEVENT (MappingNotify + 5)
+#define TouchpadScroll (MappingNotify + 5)
+#define TK_LASTEVENT (MappingNotify + 6)
+#define TouchpadScrollMask (1L << 27)
#define MouseWheelMask (1L << 28)
#define ActivateMask (1L << 29)
#define VirtualEventMask (1L << 30)
diff --git a/generic/tkBind.c b/generic/tkBind.c
index acd982b..6c83dd0 100644
--- a/generic/tkBind.c
+++ b/generic/tkBind.c
@@ -528,6 +528,7 @@ static const EventInfo eventArray[] = {
{"Activate", ActivateNotify, ActivateMask},
{"Deactivate", DeactivateNotify, ActivateMask},
{"MouseWheel", MouseWheelEvent, MouseWheelMask},
+ {"TouchpadScroll", TouchpadScroll, TouchpadScrollMask},
{"CirculateRequest", CirculateRequest, SubstructureRedirectMask},
{"ConfigureRequest", ConfigureRequest, SubstructureRedirectMask},
{"Create", CreateNotify, SubstructureNotifyMask},
@@ -632,7 +633,8 @@ static const int flagArray[TK_LASTEVENT] = {
/* VirtualEvent */ VIRTUAL,
/* Activate */ ACTIVATE,
/* Deactivate */ ACTIVATE,
- /* MouseWheel */ WHEEL
+ /* MouseWheel */ WHEEL,
+ /* TouchpadScroll */ WHEEL
};
/*
@@ -5016,7 +5018,6 @@ ParseEventDescription(
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;
@@ -5091,7 +5092,6 @@ ParseEventDescription(
} else if (patPtr->eventType == MotionNotify) {
patPtr->info = ButtonNumberFromState(patPtr->modMask);
}
-
p = SkipFieldDelims(p);
if (*p != '>') {
diff --git a/generic/tkEvent.c b/generic/tkEvent.c
index b6ee204..59b4e49 100644
--- a/generic/tkEvent.c
+++ b/generic/tkEvent.c
@@ -123,7 +123,8 @@ static const unsigned long eventMasks[TK_LASTEVENT] = {
VirtualEventMask, /* VirtualEvents */
ActivateMask, /* ActivateNotify */
ActivateMask, /* DeactivateNotify */
- MouseWheelMask /* MouseWheelEvent */
+ MouseWheelMask, /* MouseWheelEvent */
+ TouchpadScrollMask /* TouchpadScroll */
};
/*
diff --git a/library/demos/cscroll.tcl b/library/demos/cscroll.tcl
index eea0e2e..ed21310 100644
--- a/library/demos/cscroll.tcl
+++ b/library/demos/cscroll.tcl
@@ -17,7 +17,7 @@ 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
@@ -25,8 +25,8 @@ 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"
@@ -108,6 +108,12 @@ if {([tk windowingsystem] eq "aqua") && ![package vsatisfies [package provide Tk
%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-]} {
diff --git a/library/demos/items.tcl b/library/demos/items.tcl
index 5f51a90..335971b 100644
--- a/library/demos/items.tcl
+++ b/library/demos/items.tcl
@@ -34,6 +34,13 @@ canvas $c -scrollregion {0c 0c 30c 24c} -width 15c -height 10c \
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 \
diff --git a/library/listbox.tcl b/library/listbox.tcl
index f0009bf..a27ae1e 100644
--- a/library/listbox.tcl
+++ b/library/listbox.tcl
@@ -175,18 +175,29 @@ bind Listbox <Button-2> {
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 --
diff --git a/library/scrlbar.tcl b/library/scrlbar.tcl
index 35ff251..283b7a2 100644
--- a/library/scrlbar.tcl
+++ b/library/scrlbar.tcl
@@ -144,6 +144,15 @@ bind Scrollbar <Shift-MouseWheel> {
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.
@@ -304,6 +313,49 @@ proc ::tk::ScrollEndDrag {w x y} {
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
diff --git a/library/text.tcl b/library/text.tcl
index e5a4c11..caa2844 100644
--- a/library/text.tcl
+++ b/library/text.tcl
@@ -468,6 +468,15 @@ bind Text <Shift-MouseWheel> {
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
diff --git a/library/tk.tcl b/library/tk.tcl
index a6dc37c..656ad00 100644
--- a/library/tk.tcl
+++ b/library/tk.tcl
@@ -549,6 +549,13 @@ 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.
@@ -837,6 +844,21 @@ if {[tk windowingsystem] eq "x11"} {
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
diff --git a/library/ttk/combobox.tcl b/library/ttk/combobox.tcl
index 9756376..c253eb0 100644
--- a/library/ttk/combobox.tcl
+++ b/library/ttk/combobox.tcl
@@ -56,7 +56,13 @@ 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.
diff --git a/library/ttk/notebook.tcl b/library/ttk/notebook.tcl
index d8ed23b..7fb0ad5 100644
--- a/library/ttk/notebook.tcl
+++ b/library/ttk/notebook.tcl
@@ -20,16 +20,22 @@ bind TNotebook <Enter> {
set tk::Priv(xEvents) 0; set tk::Priv(yEvents) 0
}
bind TNotebook <MouseWheel> {
- ttk::notebook::CondCycleTab %W y %D -120.0
+ ttk::notebook::CondCycleTab1 %W y %D -120.0
}
bind TNotebook <Option-MouseWheel> {
- ttk::notebook::CondCycleTab %W y %D -12.0
+ ttk::notebook::CondCycleTab1 %W y %D -12.0
}
bind TNotebook <Shift-MouseWheel> {
- ttk::notebook::CondCycleTab %W x %D -120.0
+ ttk::notebook::CondCycleTab1 %W x %D -120.0
}
bind TNotebook <Shift-Option-MouseWheel> {
- ttk::notebook::CondCycleTab %W x %D -12.0
+ ttk::notebook::CondCycleTab1 %W x %D -12.0
+}
+bind TNotebook <TouchpadScroll> {
+ # TouchpadScroll events fire about 60 times per second.
+ if {[expr {%# %% 30}] == 0} {
+ ttk::notebook::CondCycleTab2 %W %D
+ }
}
# ActivateTab $nb $tab --
@@ -89,10 +95,10 @@ proc ttk::notebook::CycleTab {w dir {factor 1.0}} {
}
}
-# CondCycleTab --
+# CondCycleTab1 --
# Conditionally invoke the ttk::notebook::CycleTab proc.
#
-proc ttk::notebook::CondCycleTab {w axis dir {factor 1.0}} {
+proc ttk::notebook::CondCycleTab1 {w axis dir {factor 1.0}} {
# Count both the <MouseWheel> and <Shift-MouseWheel>
# events, and ignore the non-dominant ones
@@ -107,6 +113,23 @@ proc ttk::notebook::CondCycleTab {w axis dir {factor 1.0}} {
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;
diff --git a/library/ttk/scrollbar.tcl b/library/ttk/scrollbar.tcl
index 4f73f1f..7c31511 100644
--- a/library/ttk/scrollbar.tcl
+++ b/library/ttk/scrollbar.tcl
@@ -23,7 +23,8 @@ bind TScrollbar <Enter> {
set tk::Priv(xEvents) 0; set tk::Priv(yEvents) 0
}
foreach event {<MouseWheel> <Option-MouseWheel>
- <Shift-MouseWheel> <Shift-Option-MouseWheel>} {
+ <Shift-MouseWheel> <Shift-Option-MouseWheel>
+ <TouchpadScroll>} {
bind TScrollbar $event [bind Scrollbar $event]
}
unset event
diff --git a/library/ttk/spinbox.tcl b/library/ttk/spinbox.tcl
index 5aca894..0160d35 100644
--- a/library/ttk/spinbox.tcl
+++ b/library/ttk/spinbox.tcl
@@ -27,6 +27,13 @@ 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.
diff --git a/library/ttk/utils.tcl b/library/ttk/utils.tcl
index c2c7e8f..ea7dc72 100644
--- a/library/ttk/utils.tcl
+++ b/library/ttk/utils.tcl
@@ -301,4 +301,18 @@ bind TtkScrollable <Shift-MouseWheel> \
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*
diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c
index d4ed5ad..59fb00d 100644
--- a/macosx/tkMacOSXMouseEvent.c
+++ b/macosx/tkMacOSXMouseEvent.c
@@ -25,13 +25,6 @@ typedef struct {
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. */
@@ -550,12 +543,11 @@ enum {
Tk_UpdatePointer(target, global.x, global.y, state);
}
} else {
- CGFloat delta;
+ static int scrollCounter = 0;
+ int 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;
@@ -563,41 +555,35 @@ enum {
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);
- 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 (timeout && fabs(delta) < 1.0) {
- delta = ((delta < 0.0) ? -1.0 : 1.0);
+ if (deltaIsPrecise) {
+ int deltaX = [theEvent scrollingDeltaX];
+ int deltaY = [theEvent scrollingDeltaY];
+ delta = (deltaX << 16) | (deltaY & 0xffff);
+ if (delta != 0) {
+ xEvent.type = TouchpadScroll;
+ xEvent.xbutton.state = state;
+ xEvent.xkey.keycode = delta;
+ xEvent.xany.serial = scrollCounter++;
+ Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
}
- if (fabs(delta) >= 0.6) {
- int intDelta = round(delta);
+ } 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;
xEvent.xbutton.state = state;
- xEvent.xkey.keycode = WHEEL_DELTA * intDelta;
- tsdPtr->vWheelAcc -= intDelta;
+ xEvent.xkey.keycode = Delta > 0.0 ? 120 : -120;
xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
}
- }
- delta = [theEvent deltaX];
- if (delta != 0.0) {
- delta = (tsdPtr->hWheelAcc += delta);
- if (timeout && fabs(delta) < 1.0) {
- delta = ((delta < 0.0) ? -1.0 : 1.0);
- }
- if (fabs(delta) >= 0.6) {
- int intDelta = round(delta);
+ Delta = [theEvent scrollingDeltaX];
+ if (Delta != 0.0) {
+ xEvent.type = MouseWheelEvent;
xEvent.xbutton.state = state | ShiftMask;
- xEvent.xkey.keycode = WHEEL_DELTA * intDelta;
- tsdPtr->hWheelAcc -= intDelta;
+ xEvent.xkey.keycode = Delta > 0 ? 120 : -120;
xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
}
diff --git a/win/tkWinX.c b/win/tkWinX.c
index a705bcf..1841481 100644
--- a/win/tkWinX.c
+++ b/win/tkWinX.c
@@ -20,6 +20,7 @@
# pragma comment (lib, "advapi32.lib")
#endif
+
/*
* The zmouse.h file includes the definition for WM_MOUSEWHEEL.
*/
@@ -35,6 +36,31 @@
#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
*/
@@ -81,9 +107,6 @@ typedef struct {
* 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;
@@ -534,9 +557,6 @@ TkpOpenDisplay(
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".
@@ -1126,82 +1146,65 @@ GenerateXEvent(
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) {
- intDelta = (tsdPtr->vWheelAcc < 0) ? -WHEEL_DELTA : WHEEL_DELTA;
- } else if (tsdPtr->vWheelAcc < 0) {
- intDelta = -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;
+ } else {
+ event.x.type = MouseWheelEvent;
+ event.x.xany.send_event = -1;
+ event.key.nbytes = 0;
+ event.x.xkey.keycode = (unsigned int) delta;
}
- event.x.type = MouseWheelEvent;
- event.x.xany.send_event = -1;
- event.key.nbytes = 0;
- event.x.xkey.keycode = intDelta;
- 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
- * 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].
+ * 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
+ * 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;
+ 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;
}
- event.x.type = MouseWheelEvent;
- event.x.xany.send_event = -1;
- event.key.nbytes = 0;
- event.x.xkey.state |= ShiftMask;
- event.x.xkey.keycode = intDelta;
- tsdPtr->hWheelAcc -= intDelta;
+ lastMod = mod;
break;
}
case WM_SYSKEYDOWN: