[PATCH] mouse wheel acceleration

albert.zeyer at rwth-aachen.de albert.zeyer at rwth-aachen.de
Thu Sep 2 05:44:00 PDT 2010


From: Albert Zeyer <albert.zeyer at rwth-aachen.de>

---
 Xi/xiproperty.c              |    2 +
 dix/Makefile.am              |    1 +
 dix/devices.c                |   23 ++++
 dix/getevents.c              |   34 ++++++-
 dix/wheelveloc.c             |  247 ++++++++++++++++++++++++++++++++++++++++++
 hw/xfree86/loader/sdksyms.sh |    1 +
 include/input.h              |    7 ++
 include/inputstr.h           |    7 ++
 include/wheelveloc.h         |   61 +++++++++++
 include/xserver-properties.h |    5 +
 10 files changed, 387 insertions(+), 1 deletions(-)
 create mode 100644 dix/wheelveloc.c
 create mode 100644 include/wheelveloc.h

diff --git a/Xi/xiproperty.c b/Xi/xiproperty.c
index 2482171..ba6be2b 100644
--- a/Xi/xiproperty.c
+++ b/Xi/xiproperty.c
@@ -57,6 +57,8 @@ static struct dev_properties
     {0, ACCEL_PROP_CONSTANT_DECELERATION},
     {0, ACCEL_PROP_ADAPTIVE_DECELERATION},
     {0, ACCEL_PROP_VELOCITY_SCALING},
+    {0, WHEELACCEL_PROP_SPEEDMULT},
+    {0, WHEELACCEL_PROP_MAXSPEED},
     {0, AXIS_LABEL_PROP},
     {0, AXIS_LABEL_PROP_REL_X},
     {0, AXIS_LABEL_PROP_REL_Y},
diff --git a/dix/Makefile.am b/dix/Makefile.am
index 8917aea..03b9e4c 100644
--- a/dix/Makefile.am
+++ b/dix/Makefile.am
@@ -32,6 +32,7 @@ libdix_la_SOURCES = 	\
 	privates.c	\
 	property.c	\
 	ptrveloc.c	\
+	wheelveloc.c	\
 	registry.c	\
 	resource.c	\
 	selection.c	\
diff --git a/dix/devices.c b/dix/devices.c
index 87b6dc7..59eeaba 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -62,6 +62,7 @@ SOFTWARE.
 #include "cursorstr.h"
 #include "dixstruct.h"
 #include "ptrveloc.h"
+#include "wheelveloc.h"
 #include "site.h"
 #include "xkbsrv.h"
 #include "privates.h"
@@ -843,6 +844,10 @@ CloseDevice(DeviceIntPtr dev)
     if(dev->valuator && dev->valuator->accelScheme.AccelCleanupProc)
 	dev->valuator->accelScheme.AccelCleanupProc(dev);
 
+    /* free wheel acceleration info */
+    if(dev->button && dev->button->wheelAccelScheme.AccelCleanupProc)
+	dev->button->wheelAccelScheme.AccelCleanupProc(dev);
+
     while (dev->xkb_interest)
 	XkbRemoveResourceClient((DevicePtr)dev,dev->xkb_interest->resource);
 
@@ -1152,6 +1157,8 @@ InitButtonClassDeviceStruct(DeviceIntPtr dev, int numButtons, Atom* labels,
 {
     ButtonClassPtr butc;
     int i;
+    
+    dev->button = NULL; /* reset it initially. some functions in InitWheelVelocityData rely on that */
 
     butc = xcalloc(1, sizeof(ButtonClassRec));
     if (!butc)
@@ -1163,6 +1170,22 @@ InitButtonClassDeviceStruct(DeviceIntPtr dev, int numButtons, Atom* labels,
     for (i = numButtons + 1; i < MAP_LENGTH; i++)
         butc->map[i] = i;
     memcpy(butc->labels, labels, numButtons * sizeof(Atom));
+
+    /* wheel acceleration */
+    butc->wheelAccelScheme.AccelSchemeProc = NULL;
+    butc->wheelAccelScheme.accelData = NULL;
+    butc->wheelAccelScheme.AccelCleanupProc = NULL;
+    if (!IsMaster(dev) && /* do not accelerate master or xtest devices */
+        !IsXTestDevice(dev, NULL)) {
+	DeviceWheelVelocityPtr data = xalloc(sizeof(DeviceWheelVelocityRec));
+	if (data) {
+	    InitWheelVelocityData(dev, data);
+	    butc->wheelAccelScheme.AccelSchemeProc = ProcessWheelAccel;
+	    butc->wheelAccelScheme.accelData = data;
+	    butc->wheelAccelScheme.AccelCleanupProc = CleanupWheelAccel;
+	}
+    }
+    
     dev->button = butc;
     return TRUE;
 }
diff --git a/dix/getevents.c b/dix/getevents.c
index 82bb77b..a27f87c 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -65,6 +65,9 @@
 /* Number of motion history events to store. */
 #define MOTION_HISTORY_SIZE 256
 
+/* Wheel acceleration: maximum button press multiplier */
+#define MAX_WHEELBTN_MULTIPLIER	100
+
 /* InputEventList is the container list for all input events generated by the
  * DDX. The DDX is expected to call GetEventList() and then pass the list into
  * Get{Pointer|Keyboard}Events.
@@ -565,9 +568,10 @@ int
 GetMaximumEventsNum(void) {
     /* One raw event
      * One device event
+     * Evtl. additional events for wheel acceleration, MAX_WHEELBTN_MULTIPLIER * 2 (up + down)
      * One possible device changed event
      */
-    return 3;
+    return 1 + 1 + MAX_WHEELBTN_MULTIPLIER * 2 + 1;
 }
 
 
@@ -1120,6 +1124,34 @@ GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons,
 
     set_valuators(pDev, event, first_valuator, num_valuators, valuators);
 
+    /* wheel acceleration */
+    if(type == ButtonPress && (/* wheel scrolling */ buttons >= 4 && buttons < 8))
+    {
+	int add_num_button_presses = 0;
+	if (pDev->button->wheelAccelScheme.AccelSchemeProc)
+	    pDev->button->wheelAccelScheme.AccelSchemeProc(pDev, buttons, &add_num_button_presses, ms);
+
+	if(add_num_button_presses > MAX_WHEELBTN_MULTIPLIER)
+	    add_num_button_presses = MAX_WHEELBTN_MULTIPLIER;
+
+	add_num_button_presses *= 2; /* we handle even numbers as button release, odd as btn press */
+	while(add_num_button_presses > 0) {
+	    events++;
+	    num_events++;
+	    event = (DeviceEvent*) events->event;
+	    
+	    init_event(pDev, event, ms);
+	    event->type = (add_num_button_presses % 2 == 0) ? ET_ButtonRelease : ET_ButtonPress;
+	    event->detail.button = buttons;
+	    event->root_x = cx; /* root_x/y always in screen coords */
+	    event->root_y = cy;
+	    event->root_x_frac = cx_frac;
+	    event->root_y_frac = cy_frac;
+
+	    add_num_button_presses--;
+	}
+    }
+    
     return num_events;
 }
 
diff --git a/dix/wheelveloc.c b/dix/wheelveloc.c
new file mode 100644
index 0000000..9efc9a6
--- /dev/null
+++ b/dix/wheelveloc.c
@@ -0,0 +1,247 @@
+/*
+ *
+ * Copyright © 2010 Albert Zeyer          albert . zeyer @ rwth - aachen . de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <math.h>
+#include <wheelveloc.h>
+#include <exevents.h>
+#include <X11/Xatom.h>
+
+#include <xserver-properties.h>
+
+/*****************************************************************************
+ * mouse wheel acceleration
+ *
+ * 2010 by Albert Zeyer (albert . zeyer @ rwth - aachen . de)
+ *
+ * Because mouse wheel scrolling is done via button press/release events,
+ * we will post multiple such events to emulate a wheel scroll acceleration.
+ *
+ * The functions provided here will calculate a simple linear acceleration.
+ *
+ ****************************************************************************/
+
+/* fwd */
+
+static void
+InitializeWheelAccelerationProperties(DeviceIntPtr dev, DeviceWheelVelocityPtr vel);
+
+
+
+/********************************
+ *  Init/Uninit
+ *******************************/
+
+/**
+ * Init struct so it should match the average case
+ */
+void
+InitWheelVelocityData(DeviceIntPtr dev, DeviceWheelVelocityPtr vel)
+{
+    memset(vel, 0, sizeof(DeviceWheelVelocityRec));
+
+    for(int i = 0; i < sizeof(vel->lastWheelEvent)/sizeof(vel->lastWheelEvent[0]); ++i) {
+	vel->lastWheelEvent[i].delta = 0;
+	vel->lastWheelEvent[i].time = 0;
+    }
+    vel->speed_mult = 1.0;
+    vel->max_speed = 0;  /* no acceleration */
+
+    InitializeWheelAccelerationProperties(dev, vel);
+}
+
+
+/*
+ *  dix uninit helper, called through scheme
+ */
+void
+CleanupWheelAccel(DeviceIntPtr dev)
+{
+    dev->button->wheelAccelScheme.AccelSchemeProc = NULL;
+
+    if (dev->button->wheelAccelScheme.accelData != NULL) {
+        xfree(dev->valuator->accelScheme.accelData);
+        dev->valuator->accelScheme.accelData = NULL;
+    }
+}
+
+
+/*************************
+ * Input property support
+ ************************/
+
+/**
+ * constant deceleration
+ */
+static int
+WheelAccelSetSpeedMultProperty(DeviceIntPtr dev, Atom atom,
+                               XIPropertyValuePtr val, BOOL checkOnly)
+{
+    DeviceWheelVelocityPtr vel;
+    float v, *ptr = &v;
+    int rc;
+    int nelem = 1;
+
+    if (atom != XIGetKnownProperty(WHEELACCEL_PROP_SPEEDMULT))
+        return Success;
+
+    if (!dev->button)
+        return BadValue;
+    vel = (DeviceWheelVelocityPtr)dev->button->wheelAccelScheme.accelData;
+    if (!vel)
+        return BadValue;
+    rc = XIPropToFloat(val, &nelem, &ptr);
+
+    if(checkOnly)
+    {
+        if (rc)
+            return rc;
+	return (v >= 0.0f) ? Success : BadValue;
+    }
+
+    if (v >= 0.0f)
+	vel->speed_mult = v;
+
+    return Success;
+}
+
+static void
+WheelAccelInitSpeedMultProperty(DeviceIntPtr dev, DeviceWheelVelocityPtr vel)
+{
+    float fval = vel->speed_mult;
+    Atom prop_speedmult = XIGetKnownProperty(WHEELACCEL_PROP_SPEEDMULT);
+    XIChangeDeviceProperty(dev, prop_speedmult,
+                           XIGetKnownProperty(XATOM_FLOAT), 32,
+                           PropModeReplace, 1, &fval, FALSE);
+    XISetDevicePropertyDeletable(dev, prop_speedmult, FALSE);
+    XIRegisterPropertyHandler(dev, WheelAccelSetSpeedMultProperty, NULL, NULL);
+}
+
+
+/**
+ * choose profile
+ */
+static int
+WheelAccelSetMaxSpeedProperty(DeviceIntPtr dev, Atom atom,
+                              XIPropertyValuePtr val, BOOL checkOnly)
+{
+    DeviceWheelVelocityPtr vel;
+    int v, *ptr = &v;
+    int rc;
+    int nelem = 1;
+
+    if (atom != XIGetKnownProperty(WHEELACCEL_PROP_MAXSPEED))
+        return Success;
+
+    if (!dev->button)
+        return BadValue;
+    vel = (DeviceWheelVelocityPtr)dev->button->wheelAccelScheme.accelData;
+    if (!vel)
+        return BadValue;
+    rc = XIPropToInt(val, &nelem, &ptr);
+
+    if(checkOnly)
+    {
+        if (rc)
+            return rc;
+        return (v >= 0) ? Success : BadValue;
+    }
+
+    if (v >= 0)
+	vel->max_speed = v;
+    
+    return Success;
+}
+
+static void
+WheelAccelInitMaxSpeedProperty(DeviceIntPtr dev, DeviceWheelVelocityPtr vel)
+{
+    int maxspeed = vel->max_speed;
+    Atom prop_maxspeed = XIGetKnownProperty(WHEELACCEL_PROP_MAXSPEED);
+
+    XIChangeDeviceProperty(dev, prop_maxspeed, XA_INTEGER, 32,
+                           PropModeReplace, 1, &maxspeed, FALSE);
+    XISetDevicePropertyDeletable(dev, prop_maxspeed, FALSE);
+    XIRegisterPropertyHandler(dev, WheelAccelSetMaxSpeedProperty, NULL, NULL);
+}
+
+static void
+InitializeWheelAccelerationProperties(DeviceIntPtr dev, DeviceWheelVelocityPtr vel)
+{
+    WheelAccelInitSpeedMultProperty(dev, vel);
+    WheelAccelInitMaxSpeedProperty(dev, vel);
+}
+
+
+/********************************
+ *  acceleration proc
+ *******************************/
+
+
+void
+ProcessWheelAccel(
+    DeviceIntPtr dev,
+    int button,
+    int* add_num_button_presses,
+    int evtime)
+{
+    DeviceWheelVelocityPtr velocitydata =
+	(DeviceWheelVelocityPtr) dev->button->wheelAccelScheme.accelData;
+    int axeIndex = 0;
+    int delta = 0;
+    float dt;
+    *add_num_button_presses = 0;
+
+    if (!add_num_button_presses || !velocitydata)
+        return;
+
+    switch(button) {
+	case 4: axeIndex = 0; delta = -1; break;
+	case 5: axeIndex = 0; delta = 1; break;
+	case 6: axeIndex = 1; delta = -1; break;
+	case 7: axeIndex = 1; delta = 1; break;
+	default: /* unknown wheel button number */ return;
+    }
+
+    if(velocitydata->lastWheelEvent[axeIndex].delta != delta)
+	/* we scrolled in a different direction than before -> dont accel */
+	goto finish;
+
+    if(evtime < velocitydata->lastWheelEvent[axeIndex].time)
+	/* this should not happen */
+	goto finish;
+
+    dt = evtime - velocitydata->lastWheelEvent[axeIndex].time;
+    *add_num_button_presses = (int) (velocitydata->speed_mult / dt);
+    if(*add_num_button_presses > velocitydata->max_speed)
+	*add_num_button_presses = velocitydata->max_speed;
+
+finish:
+    velocitydata->lastWheelEvent[axeIndex].delta = delta;
+    velocitydata->lastWheelEvent[axeIndex].time = evtime;    
+}
+
diff --git a/hw/xfree86/loader/sdksyms.sh b/hw/xfree86/loader/sdksyms.sh
index eea0240..fbe4663 100755
--- a/hw/xfree86/loader/sdksyms.sh
+++ b/hw/xfree86/loader/sdksyms.sh
@@ -296,6 +296,7 @@ cat > sdksyms.c << EOF
 #include "property.h"
 #include "propertyst.h"
 #include "ptrveloc.h"
+#include "wheelveloc.h"
 #include "region.h"
 #include "regionstr.h"
 #include "registry.h"
diff --git a/include/input.h b/include/input.h
index 4a845be..e3bc7f8 100644
--- a/include/input.h
+++ b/include/input.h
@@ -146,6 +146,13 @@ typedef void (*PointerAccelSchemeProc)(
     int* /*valuators*/,
     int /*evtime*/);
 
+/* wheel acceleration handling */
+typedef void (*WheelAccelSchemeProc)(
+    DeviceIntPtr /*pDev*/,
+    int /*button*/,
+    int* /*add_num_button_presses*/,
+    int /*evtime*/);
+
 typedef void (*DeviceCallbackProc)(
               DeviceIntPtr /*pDev*/);
 
diff --git a/include/inputstr.h b/include/inputstr.h
index 15184d0..3de7519 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -241,6 +241,12 @@ typedef struct _ValuatorClassRec {
     ValuatorAccelerationRec	accelScheme;
 } ValuatorClassRec, *ValuatorClassPtr;
 
+typedef struct _WheelAccelerationRec {
+    WheelAccelSchemeProc        AccelSchemeProc;
+    void                       *accelData; /* at disposal of AccelScheme */
+    DeviceCallbackProc          AccelCleanupProc;
+} WheelAccelerationRec, *WheelAccelerationPtr;
+
 typedef struct _ButtonClassRec {
     int			sourceid;
     CARD8		numButtons;
@@ -256,6 +262,7 @@ typedef struct _ButtonClassRec {
     CARD8		map[MAP_LENGTH];
     union _XkbAction    *xkb_acts;
     Atom		labels[MAX_BUTTONS];
+    WheelAccelerationRec	wheelAccelScheme;
 } ButtonClassRec, *ButtonClassPtr;
 
 typedef struct _FocusClassRec {
diff --git a/include/wheelveloc.h b/include/wheelveloc.h
new file mode 100644
index 0000000..13c6146
--- /dev/null
+++ b/include/wheelveloc.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright © 2010 Albert Zeyer		albert . zeyer @ rwth - aachen . de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef WHEELVELOCITY_H
+#define WHEELVELOCITY_H
+
+#include <input.h> /* DeviceIntPtr */
+
+/**
+ * a motion history, with just enough information to
+ * calc mean velocity
+ */
+typedef struct _WheelTracker {
+    int delta;	/* accumulated delta */
+    int time;	/* time of creation */
+} WheelTrackerRec, *WheelTrackerPtr;
+
+/**
+ * Contains all data needed to implement wheel ballistics
+ */
+typedef struct _DeviceWheelVelocityRec {
+    WheelTrackerRec lastWheelEvent[2]; /* 2 for both Z and W axes */
+    int     add_num_button_presses;
+    float   speed_mult;      /* config: speed multiplier */
+    int	    max_speed;       /* config: max speed */
+} DeviceWheelVelocityRec, *DeviceWheelVelocityPtr;
+
+
+extern _X_EXPORT void
+InitWheelVelocityData(DeviceIntPtr dev, DeviceWheelVelocityPtr vel);
+
+extern _X_INTERNAL void
+CleanupWheelAccel(DeviceIntPtr dev);
+
+extern _X_INTERNAL void
+ProcessWheelAccel(DeviceIntPtr dev, int button,
+	          int* add_num_button_presses, int evtime);
+
+
+#endif  /* POINTERVELOCITY_H */
diff --git a/include/xserver-properties.h b/include/xserver-properties.h
index 626d0ad..fc0fe56 100644
--- a/include/xserver-properties.h
+++ b/include/xserver-properties.h
@@ -45,6 +45,11 @@
 /* FLOAT, format 32 */
 #define ACCEL_PROP_VELOCITY_SCALING "Device Accel Velocity Scaling"
 
+/* Wheel acceleration properties */
+/* FLOAT, format 32*/
+#define WHEELACCEL_PROP_SPEEDMULT "Device Wheel Accel Speed Multiplier"
+/* INTEGER of any format. 0 - no acceleration */
+#define WHEELACCEL_PROP_MAXSPEED "Device Wheel Accel Max Speed"
 
 /* Axis labels */
 #define AXIS_LABEL_PROP "Axis Labels"
-- 
1.7.0.4



More information about the xorg-devel mailing list