[PATCH] evdev: smooth scrolling support for wheel emulation

Max Schwarz max.schwarz at online.de
Sat Jan 11 16:19:10 PST 2014


Disables the button click generation (EvdevWheelEmuInertia) and
generates scroll valuator events directly.

Signed-off-by: Max Schwarz <max.schwarz at online.de>
---
I tried to support turning wheel emulation on/off at runtime.
This involves calling InitValuatorClassDeviceStruct() again
to add the new valuators or remove them. If that is not
correct usage of the API, it might be easier to disallow
activation at runtime.

 src/emuWheel.c | 30 ++++++++++++++++++++++++------
 src/evdev.c    | 44 +++++++++++++++++++++++++++++++++-----------
 src/evdev.h    |  5 +++++
 3 files changed, 62 insertions(+), 17 deletions(-)

diff --git a/src/emuWheel.c b/src/emuWheel.c
index 04487cf..ea49d12 100644
--- a/src/emuWheel.c
+++ b/src/emuWheel.c
@@ -51,7 +51,9 @@ static Atom prop_wheel_timeout  = 0;
 static Atom prop_wheel_button   = 0;
 
 /* Local Funciton Prototypes */
+#ifndef HAVE_SMOOTH_SCROLLING
 static int EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value);
+#endif
 
 /* Filter mouse button events */
 BOOL
@@ -95,7 +97,8 @@ BOOL
 EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
 {
     EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
-    WheelAxisPtr pAxis = NULL, pOtherAxis = NULL;
+    WheelAxisPtr pAxis = NULL;
+    _X_UNUSED WheelAxisPtr pOtherAxis = NULL;
     int value = pEv->value;
 
     /* Has wheel emulation been configured to be enabled? */
@@ -147,11 +150,20 @@ EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
 	   wheel.  Reset the inertia of the other axis when a scroll event
 	   was sent to avoid the buildup of erroneous scroll events if the
 	   user doesn't move in a perfectly straight line.
+	   If the axis is not configured, simply eat the motion.
 	 */
-	if (pAxis)
+	if (pAxis && pAxis->up_button)
 	{
+#ifndef HAVE_SMOOTH_SCROLLING
 	    if (EvdevWheelEmuInertia(pInfo, pAxis, value))
 		pOtherAxis->traveled_distance = 0;
+#else
+	    /* Simply queue scroll valuator events. The Y axis is flipped. */
+	    if (pAxis->code == REL_WHEEL)
+		EvdevQueueRelativeMotion(pInfo, pAxis->code, -value);
+	    else
+		EvdevQueueRelativeMotion(pInfo, pAxis->code, value);
+#endif
 	}
 
 	/* Eat motion events while emulateWheel button pressed. */
@@ -161,6 +173,8 @@ EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
     return FALSE;
 }
 
+
+#ifndef HAVE_SMOOTH_SCROLLING
 /* Simulate inertia for our emulated mouse wheel.
    Returns the number of wheel events generated.
  */
@@ -172,10 +186,6 @@ EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
     int inertia;
     int rc = 0;
 
-    /* if this axis has not been configured, just eat the motion */
-    if (!axis->up_button)
-	return rc;
-
     axis->traveled_distance += value;
 
     if (axis->traveled_distance < 0) {
@@ -194,6 +204,7 @@ EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
     }
     return rc;
 }
+#endif
 
 /* Handle button mapping here to avoid code duplication,
 returns true if a button mapping was found. */
@@ -296,6 +307,10 @@ EvdevWheelEmuPreInit(InputInfoPtr pInfo)
 
     pEvdev->emulateWheel.timeout = timeout;
 
+    /* Default axes */
+    pEvdev->emulateWheel.Y.code = REL_WHEEL;
+    pEvdev->emulateWheel.X.code = REL_HWHEEL;
+
     /* Configure the Y axis or default it */
     if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y),
                 "YAxisMapping")) {
@@ -357,6 +372,9 @@ EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
                             16, PropModeReplace, 1,
                             &pEvdev->emulateWheel.inertia, TRUE);
             }
+
+            /* We might need to add/remove valuators for the scroll axes */
+            EvdevAddRelValuatorClass(dev);
         }
     }
     else if (atom == prop_wheel_button)
diff --git a/src/evdev.c b/src/evdev.c
index d1ed9ee..6ab464b 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -405,6 +405,18 @@ EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count)
     }
 }
 
+void
+EvdevQueueRelativeMotion(InputInfoPtr pInfo, int code, int value)
+{
+    int map;
+    EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
+
+    pEvdev->rel_queued = 1;
+    pEvdev->delta[code] += value;
+    map = pEvdev->rel_axis_map[code];
+    valuator_mask_set(pEvdev->vals, map, value);
+}
+
 static void
 EvdevSwapAbsValuators(EvdevPtr pEvdev, ValuatorMask *mask)
 {
@@ -714,10 +726,7 @@ EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
             if (EvdevWheelEmuFilterMotion(pInfo, ev))
                 return;
 
-            pEvdev->rel_queued = 1;
-            pEvdev->delta[ev->code] += value;
-            map = pEvdev->rel_axis_map[ev->code];
-            valuator_mask_set(pEvdev->vals, map, value);
+            EvdevQueueRelativeMotion(pInfo, ev->code, value);
             break;
     }
 }
@@ -1664,7 +1673,8 @@ EvdevSetScrollValuators(DeviceIntPtr device)
     return Success;
 }
 
-static int
+/* Note: Can be called multiple times if wheelEmulation.enabled changes */
+int
 EvdevAddRelValuatorClass(DeviceIntPtr device)
 {
     InputInfoPtr pInfo;
@@ -1696,6 +1706,12 @@ EvdevAddRelValuatorClass(DeviceIntPtr device)
 
     if (num_axes <= 0)
         goto out;
+#else
+    /* If wheel emulation is enabled, we need REL_WHEEL and REL_HWHEEL even
+     * if the device does not support them */
+    if(pEvdev->emulateWheel.enabled) {
+        num_axes += 2;
+    }
 #endif
 
     if (num_axes > MAX_VALUATORS) {
@@ -1705,6 +1721,9 @@ EvdevAddRelValuatorClass(DeviceIntPtr device)
 
     pEvdev->num_vals = num_axes;
     if (num_axes > 0) {
+        if(pEvdev->vals)
+            valuator_mask_free(&pEvdev->vals);
+
         pEvdev->vals = valuator_mask_new(num_axes);
         if (!pEvdev->vals)
             goto out;
@@ -1719,8 +1738,12 @@ EvdevAddRelValuatorClass(DeviceIntPtr device)
         if (axis == REL_WHEEL || axis == REL_HWHEEL || axis == REL_DIAL)
             continue;
 #endif
-        if (!libevdev_has_event_code(pEvdev->dev, EV_REL, axis))
+        /* If wheel emulation is enabled, we need REL_WHEEL and REL_HWHEEL even
+         * if the device does not support them */
+        if (!libevdev_has_event_code(pEvdev->dev, EV_REL, axis)
+              && (!pEvdev->emulateWheel.enabled || (axis != REL_WHEEL && axis != REL_HWHEEL)))
             continue;
+
         pEvdev->rel_axis_map[axis] = map;
         map++;
     }
@@ -2121,7 +2144,7 @@ EvdevForceXY(InputInfoPtr pInfo, int mode)
 static int
 EvdevProbe(InputInfoPtr pInfo)
 {
-    int i, has_rel_axes, has_abs_axes, has_keys, num_buttons, has_scroll;
+    int i, has_rel_axes, has_abs_axes, has_keys, num_buttons;
     int has_lmr; /* left middle right */
     int has_mt; /* multitouch */
     int ignore_abs = 0, ignore_rel = 0;
@@ -2156,7 +2179,6 @@ EvdevProbe(InputInfoPtr pInfo)
     has_rel_axes = FALSE;
     has_abs_axes = FALSE;
     has_keys = FALSE;
-    has_scroll = FALSE;
     has_lmr = FALSE;
     has_mt = FALSE;
     num_buttons = 0;
@@ -2196,7 +2218,6 @@ EvdevProbe(InputInfoPtr pInfo)
             libevdev_has_event_code(pEvdev->dev, EV_REL, REL_HWHEEL) ||
             libevdev_has_event_code(pEvdev->dev, EV_REL, REL_DIAL)) {
             xf86IDrvMsg(pInfo, X_PROBED, "Found scroll wheel(s)\n");
-            has_scroll = TRUE;
             if (!num_buttons)
                 xf86IDrvMsg(pInfo, X_INFO,
                             "Forcing buttons for scroll wheel(s)\n");
@@ -2365,8 +2386,9 @@ EvdevProbe(InputInfoPtr pInfo)
         rc = 0;
     }
 
-    if (has_scroll &&
-        (has_rel_axes || has_abs_axes || num_buttons || has_keys))
+    /* Even if we do not have a scroll wheel, we might provide scroll axes
+     * through wheel emulation */
+    if (has_rel_axes || has_abs_axes || num_buttons || has_keys)
     {
         xf86IDrvMsg(pInfo, X_INFO, "Adding scrollwheel support\n");
         pEvdev->flags |= EVDEV_BUTTON_EVENTS;
diff --git a/src/evdev.h b/src/evdev.h
index 520d017..35bb5e9 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -126,6 +126,7 @@ enum ButtonAction {
 typedef struct {
     int up_button;
     int down_button;
+    int code;
     int traveled_distance;
 } WheelAxis, *WheelAxisPtr;
 
@@ -252,10 +253,14 @@ typedef struct {
     char *type_name;
 } EvdevRec, *EvdevPtr;
 
+/* (Re-)initialization */
+int EvdevAddRelValuatorClass(DeviceIntPtr device);
+
 /* Event posting functions */
 void EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value);
 void EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value);
 void EvdevQueueProximityEvent(InputInfoPtr pInfo, int value);
+void EvdevQueueRelativeMotion(InputInfoPtr pInfo, int code, int value);
 #ifdef MULTITOUCH
 void EvdevQueueTouchEvent(InputInfoPtr pInfo, unsigned int touch,
                           ValuatorMask *mask, uint16_t type);
-- 
1.8.3.2


More information about the xorg-devel mailing list