--- 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 +#endif + +#include +#include +#include +#include + +#include + +/***************************************************************************** + * 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 /* 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