]> git.eshelyaron.com Git - emacs.git/commitdiff
Explain confusing aspects of XInput 2 scroll wheel reporting
authorPo Lu <luangruo@yahoo.com>
Fri, 26 Nov 2021 11:59:54 +0000 (19:59 +0800)
committerPo Lu <luangruo@yahoo.com>
Fri, 26 Nov 2021 13:06:22 +0000 (21:06 +0800)
* src/xterm.c (x_init_master_valuators): Explain how XInput 2
reports scroll wheel movement.

(handle_one_xevent): Explain why XI2 scroll valuators
are reset after each enter events.

src/xterm.c

index 821c92c4dda15466466bfe84e0d6a2c6ef68e0a6..c7950c6f9fc73f60158fa5f5847877fc7391bac2 100644 (file)
@@ -372,6 +372,29 @@ x_free_xi_devices (struct x_display_info *dpyinfo)
   unblock_input ();
 }
 
+/* The code below handles the tracking of scroll valuators on XInput
+   2, in order to support scroll wheels that report information more
+   granular than a screen line.
+
+   On X, when the XInput 2 extension is being utilized, the states of
+   the mouse wheels in each axis are stored as absolute values inside
+   "valuators" attached to each mouse device.  To obtain the delta of
+   the scroll wheel from a motion event (which is used to report that
+   some valuator has changed), it is necessary to iterate over every
+   valuator that changed, and compare its previous value to the
+   current value of the valuator.
+
+   Each individual valuator also has an "interval", which is the
+   amount you must divide that delta by in order to obtain a delta in
+   the terms of scroll units.
+
+   This delta however is still intermediate, to make driver
+   implementations easier.  The XInput developers recommend (and most
+   programs use) the following algorithm to convert from scroll unit
+   deltas to pixel deltas:
+
+     pixels_scrolled = pow (window_height, 2.0 / 3.0) * delta;  */
+
 /* Setup valuator tracking for XI2 master devices on
    DPYINFO->display.  */
 
@@ -9874,6 +9897,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
            x_display_set_last_user_time (dpyinfo, xi_event->time);
            x_detect_focus_change (dpyinfo, any, event, &inev.ie);
 
+           /* One problem behind the design of XInput 2 scrolling is
+              that valuators are not unique to each window, but only
+              the window that has grabbed the valuator's device or
+              the window that the device's pointer is on top of can
+              receive motion events.  There is also no way to
+              retrieve the value of a valuator outside of each motion
+              event.
+
+              As such, to prevent wildly inaccurate results when the
+              valuators have changed outside Emacs, we reset our
+              records of each valuator's value whenever the pointer
+              re-enters a frame after its valuators have potentially
+              been changed elsewhere.  */
            if (enter->detail != XINotifyInferior
                && enter->mode != XINotifyPassiveUngrab
                && enter->mode != XINotifyUngrab && any)
@@ -9947,6 +9983,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    struct xi_scroll_valuator_t *val;
                    double delta, scroll_unit;
 
+
+                   /* See the comment on top of
+                      x_init_master_valuators for more details on how
+                      scroll wheel movement is reported on XInput 2.  */
                    delta = x_get_scroll_valuator_delta (dpyinfo, xev->deviceid,
                                                         i, *values, &val);
 
@@ -9972,7 +10012,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                              goto XI_OTHER;
                          }
 
-                       scroll_unit = pow (FRAME_PIXEL_HEIGHT (f), 2.0 / 3.0);
                        found_valuator = true;
 
                        if (signbit (delta) != signbit (val->emacs_value))
@@ -9999,6 +10038,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          |= x_x_to_emacs_modifiers (dpyinfo,
                                                     xev->mods.effective);
 
+                       scroll_unit = pow (FRAME_PIXEL_HEIGHT (f), 2.0 / 3.0);
+
                        if (val->horizontal)
                          {
                            inev.ie.arg