Commit c9eb0e87 authored by Simon Thum's avatar Simon Thum Committed by Peter Hutterer
Browse files

Add support for multiple pointer acceleration schemes. #8583

Available acceleration schemes:
 - xorg classic scheme.
 - the new "Predictable" polynomial accel scheme.

X.Org Bug 8583 <http://bugs.freedesktop.org/show_bug.cgi?id=8583

>

Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent e7abe167
......@@ -28,6 +28,7 @@ libdix_la_SOURCES = \
pixmap.c \
privates.c \
property.c \
ptrveloc.c \
registry.c \
resource.c \
selection.c \
......
......@@ -62,6 +62,7 @@ SOFTWARE.
#include "scrnintstr.h"
#include "cursorstr.h"
#include "dixstruct.h"
#include "ptrveloc.h"
#include "site.h"
#ifndef XKB_IN_SERVER
#define XKB_IN_SERVER
......@@ -172,6 +173,7 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart)
/* last valuators */
memset(dev->last.valuators, 0, sizeof(dev->last.valuators));
memset(dev->last.remainder, 0, sizeof(dev->last.remainder));
dev->last.numValuators = 0;
/* device properties */
......@@ -785,6 +787,10 @@ CloseDevice(DeviceIntPtr dev)
if (dev->isMaster && dev->spriteInfo->sprite)
screen->DeviceCursorCleanup(dev, screen);
/* free acceleration info */
if(dev->valuator && dev->valuator->accelScheme.AccelCleanupProc)
dev->valuator->accelScheme.AccelCleanupProc(dev);
xfree(dev->name);
classes = (ClassesPtr)&dev->key;
......@@ -1196,8 +1202,6 @@ InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes,
valc->mode = mode;
valc->axes = (AxisInfoPtr)(valc + 1);
valc->axisVal = (int *)(valc->axes + numAxes);
valc->dxremaind = 0;
valc->dyremaind = 0;
dev->valuator = valc;
AllocateMotionHistory(dev);
......@@ -1209,6 +1213,59 @@ InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes,
}
dev->last.numValuators = numAxes;
if(!dev->isMaster) /* master devs do not accelerate */
InitPointerAccelerationScheme(dev, PtrAccelDefault);
return TRUE;
}
/* global list of acceleration schemes */
ValuatorAccelerationRec pointerAccelerationScheme[] = {
{PtrAccelNoOp, NULL, NULL, NULL},
{PtrAccelPredictable, acceleratePointerPredictable, NULL, AccelerationDefaultCleanup},
{PtrAccelClassic, acceleratePointerClassic, NULL, NULL},
{-1, NULL, NULL, NULL} /* terminator */
};
_X_EXPORT Bool
InitPointerAccelerationScheme(DeviceIntPtr dev,
int scheme)
{
int x, i = -1;
void* data = NULL;
ValuatorClassPtr val;
if(dev->isMaster) /* bail out if called for master devs */
return FALSE;
for(x = 0; pointerAccelerationScheme[x].number >= 0; x++) {
if(pointerAccelerationScheme[x].number == scheme){
i = x;
break;
}
}
if(-1 == i)
return FALSE;
/* init scheme-specific data */
switch(scheme){
case PtrAccelPredictable:
{
DeviceVelocityPtr s;
s = (DeviceVelocityPtr)xalloc(sizeof(DeviceVelocityRec));
InitVelocityData(s);
data = s;
break;
}
default:
break;
}
val = dev->valuator;
val->accelScheme = pointerAccelerationScheme[i];
val->accelScheme.accelData = data;
return TRUE;
}
......
......@@ -487,80 +487,6 @@ GetMaximumEventsNum(void) {
}
/* Originally a part of xf86PostMotionEvent; modifies valuators
* in-place. */
static void
acceleratePointer(DeviceIntPtr pDev, int first_valuator, int num_valuators,
int *valuators)
{
float mult = 0.0;
int dx = 0, dy = 0;
int *px = NULL, *py = NULL;
if (!num_valuators || !valuators)
return;
if (first_valuator == 0) {
dx = valuators[0];
px = &valuators[0];
}
if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
dy = valuators[1 - first_valuator];
py = &valuators[1 - first_valuator];
}
if (!dx && !dy)
return;
if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) {
/* modeled from xf86Events.c */
if (pDev->ptrfeed->ctrl.threshold) {
if ((abs(dx) + abs(dy)) >= pDev->ptrfeed->ctrl.threshold) {
pDev->valuator->dxremaind = ((float)dx *
(float)(pDev->ptrfeed->ctrl.num)) /
(float)(pDev->ptrfeed->ctrl.den) +
pDev->valuator->dxremaind;
if (px) {
*px = (int)pDev->valuator->dxremaind;
pDev->valuator->dxremaind = pDev->valuator->dxremaind -
(float)(*px);
}
pDev->valuator->dyremaind = ((float)dy *
(float)(pDev->ptrfeed->ctrl.num)) /
(float)(pDev->ptrfeed->ctrl.den) +
pDev->valuator->dyremaind;
if (py) {
*py = (int)pDev->valuator->dyremaind;
pDev->valuator->dyremaind = pDev->valuator->dyremaind -
(float)(*py);
}
}
}
else {
mult = pow((float)dx * (float)dx + (float)dy * (float)dy,
((float)(pDev->ptrfeed->ctrl.num) /
(float)(pDev->ptrfeed->ctrl.den) - 1.0) /
2.0) / 2.0;
if (dx) {
pDev->valuator->dxremaind = mult * (float)dx +
pDev->valuator->dxremaind;
*px = (int)pDev->valuator->dxremaind;
pDev->valuator->dxremaind = pDev->valuator->dxremaind -
(float)(*px);
}
if (dy) {
pDev->valuator->dyremaind = mult * (float)dy +
pDev->valuator->dyremaind;
*py = (int)pDev->valuator->dyremaind;
pDev->valuator->dyremaind = pDev->valuator->dyremaind -
(float)(*py);
}
}
}
}
/**
* Clip an axis to its bounds, which are declared in the call to
* InitValuatorAxisClassStruct.
......@@ -889,6 +815,8 @@ GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons,
int *v0 = NULL, *v1 = NULL;
int i;
ms = GetTimeInMillis(); /* before pointer update to help precision */
/* Sanity checks. */
if (type != MotionNotify && type != ButtonPress && type != ButtonRelease)
return 0;
......@@ -901,8 +829,6 @@ GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons,
if (type == MotionNotify && num_valuators <= 0)
return 0;
ms = GetTimeInMillis();
/* Do we need to send a DeviceValuator event? */
if (num_valuators) {
if ((((num_valuators - 1) / 6) + 1) > MAX_VALUATOR_EVENTS)
......@@ -952,9 +878,11 @@ GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons,
}
}
else {
if (flags & POINTER_ACCELERATE)
acceleratePointer(pDev, first_valuator, num_valuators,
valuators);
if (flags & POINTER_ACCELERATE &&
pDev->valuator->accelScheme.AccelSchemeProc){
pDev->valuator->accelScheme.AccelSchemeProc(
pDev, first_valuator, num_valuators, valuators, ms);
}
if(v0) x += *v0;
if(v1) y += *v1;
......
This diff is collapsed.
......@@ -82,12 +82,125 @@
#include "mi.h"
#include <ptrveloc.h> /* dix pointer acceleration */
#ifdef XFreeXDGA
#include "dgaproc.h"
#endif
EventListPtr xf86Events = NULL;
/**
* Eval config and modify DeviceVelocityRec accordingly
*/
static void
ProcessVelocityConfiguration(char* devname, pointer list, DeviceVelocityPtr s){
int tempi, i;
float tempf, tempf2;
if(!s)
return;
tempf = xf86SetRealOption(list, "FilterHalflife", 20);
xf86Msg(X_CONFIG, "%s: (accel) filter halflife %.1f ms\n", devname, tempf);
if(tempf > 0)
tempf = 1.0 / tempf; /* set reciprocal if possible */
else
tempf = 10000; /* else set fairly high */
tempf2 = xf86SetRealOption(list, "FilterChainProgression", 2.0);
xf86Msg(X_CONFIG, "%s: (accel) filter chain progression: %.2f\n",
devname, tempf2);
if(tempf2 < 1)
tempf2 = 2;
tempi = xf86SetIntOption(list, "FilterChainLength", 1);
if(tempi < 1 || tempi > MAX_VELOCITY_FILTERS)
tempi = 1;
InitFilterChain(s, tempf, tempf2, tempi, 40);
for(i = 0; i < tempi; i++)
xf86Msg(X_CONFIG, "%s: (accel) filter stage %i: %.2f ms\n",
devname, i, 1.0f / (s->filters[i].rdecay));
tempf = xf86SetIntOption(list, "ConstantDeceleration", 1);
if(tempf > 1.0){
xf86Msg(X_CONFIG, "%s: (accel) constant deceleration by %.1f\n",
devname, tempf);
s->const_acceleration = 1.0 / tempf; /* set reciprocal deceleration
alias acceleration */
}
tempf = xf86SetIntOption(list, "AdaptiveDeceleration", 1);
if(tempf > 1.0){
xf86Msg(X_CONFIG, "%s: (accel) adaptive deceleration by %.1f\n",
devname, tempf);
s->min_acceleration = 1.0 / tempf; /* set minimum acceleration */
}
tempf = xf86SetRealOption(list, "VelocityCoupling", 0.2);
xf86Msg(X_CONFIG, "%s: (accel) velocity coupling is %.1f%%\n", devname,
tempf*100.0);
s->coupling = tempf;
/* Configure softening. If const deceleration is used, this is expected
* to provide better subpixel information so we enable
* softening by default only if ConstantDeceleration is not used
*/
s->use_softening = xf86SetBoolOption(list, "Softening",
s->const_acceleration == 1.0);
s->reset_time = xf86SetIntOption(list, "VelocityReset", 300);
tempf = xf86SetRealOption(list, "ExpectedRate", 0);
if(tempf > 0){
s->corr_mul = 1000.0 / tempf;
}else{
s->corr_mul = xf86SetRealOption(list, "VelocityScale", 10);
}
/* select profile by number */
tempi= xf86SetIntOption(list, "AccelerationProfile", 0);
if(SetAccelerationProfile(s, tempi)){
xf86Msg(X_CONFIG, "%s: (accel) set acceleration profile %i\n", devname, tempi);
}else{
xf86Msg(X_CONFIG, "%s: (accel) acceleration profile %i is unknown\n",
devname, tempi);
}
}
static void
ApplyAccelerationSettings(DeviceIntPtr dev){
int scheme;
DeviceVelocityPtr pVel;
LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate;
if(dev->valuator){
scheme = xf86SetIntOption(local->options, "AccelerationScheme", 1);
/* reinit scheme if needed */
if(dev->valuator->accelScheme.number != scheme){
if(dev->valuator->accelScheme.AccelCleanupProc){
dev->valuator->accelScheme.AccelCleanupProc(dev);
}
xf86Msg(X_CONFIG, "%s: (accel) init acceleration scheme %i\n", local->name, scheme);
InitPointerAccelerationScheme(dev, scheme);
}else{
xf86Msg(X_CONFIG, "%s: (accel) keeping acceleration scheme %i\n", local->name, scheme);
}
/* process special configuration */
switch(scheme){
case 1:
pVel = (DeviceVelocityPtr) dev->valuator->accelScheme.accelData;
ProcessVelocityConfiguration (local->name, local->options,
pVel);
break;
}
}
}
static Bool
xf86SendDragEvents(DeviceIntPtr device)
{
......@@ -838,6 +951,9 @@ xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum)
dev->valuator->axisVal[1] = screenInfo.screens[0]->height / 2;
dev->last.valuators[1] = dev->valuator->axisVal[1];
}
if(axnum == 0) /* to prevent double invocation */
ApplyAccelerationSettings(dev);
}
......
......@@ -35,6 +35,7 @@ sdk_HEADERS = \
privates.h \
property.h \
propertyst.h \
ptrveloc.h \
region.h \
regionstr.h \
registry.h \
......
......@@ -63,6 +63,12 @@ SOFTWARE.
#define POINTER_ABSOLUTE (1 << 2)
#define POINTER_ACCELERATE (1 << 3)
/*int constants for pointer acceleration schemes*/
#define PtrAccelNoOp 0
#define PtrAccelPredictable 1
#define PtrAccelClassic 2
#define PtrAccelDefault PtrAccelPredictable
#define MAX_VALUATORS 36 /* XXX from comment in dix/getevents.c */
#define NO_AXIS_LIMITS -1
......@@ -155,6 +161,17 @@ typedef void (*DeviceUnwrapProc)(
void* /*data*/
);
/* pointer acceleration handling */
typedef void (*PointerAccelSchemeProc)(
DeviceIntPtr /*pDev*/,
int /*first_valuator*/,
int /*num_valuators*/,
int* /*valuators*/,
int /*evtime*/);
typedef void (*DeviceCallbackProc)(
DeviceIntPtr /*pDev*/);
typedef struct _DeviceRec {
pointer devicePrivate;
ProcessInputProc processInputProc; /* current */
......@@ -280,6 +297,10 @@ extern Bool InitValuatorClassDeviceStruct(
int /*numMotionEvents*/,
int /*mode*/);
extern Bool InitPointerAccelerationScheme(
DeviceIntPtr /*dev*/,
int /*scheme*/);
extern Bool InitAbsoluteClassDeviceStruct(
DeviceIntPtr /*device*/);
......
......@@ -166,6 +166,13 @@ typedef struct _AxisInfo {
int max_value;
} AxisInfo, *AxisInfoPtr;
typedef struct _ValuatorAccelerationRec {
int number;
PointerAccelSchemeProc AccelSchemeProc;
void *accelData; /* at disposal of AccelScheme */
DeviceCallbackProc AccelCleanupProc;
} ValuatorAccelerationRec, *ValuatorAccelerationPtr;
typedef struct _ValuatorClassRec {
int numMotionEvents;
int first_motion;
......@@ -177,8 +184,8 @@ typedef struct _ValuatorClassRec {
AxisInfoPtr axes;
unsigned short numAxes;
int *axisVal; /* always absolute, but device-coord system */
float dxremaind, dyremaind; /* for acceleration */
CARD8 mode;
ValuatorAccelerationRec accelScheme;
} ValuatorClassRec, *ValuatorClassPtr;
typedef struct _ButtonClassRec {
......@@ -467,9 +474,12 @@ typedef struct _DeviceIntRec {
/* last valuator values recorded, not posted to client;
* for slave devices, valuators is in device coordinates
* for master devices, valuators is in screen coordinates
* see dix/getevents.c */
* see dix/getevents.c
* remainder supports acceleration
*/
struct {
int valuators[MAX_VALUATORS];
float remainder[MAX_VALUATORS];
int numValuators;
} last;
......
/*
* 2006-2008 by Simon Thum
*/
#ifndef POINTERVELOCITY_H
#define POINTERVELOCITY_H
#include <input.h> /* DeviceIntPtr */
#define MAX_VELOCITY_FILTERS 8
struct _DeviceVelocityRec;
/**
* profile
* returns actual acceleration depending on velocity, acceleration control,...
*/
typedef float (*PointerAccelerationProfileFunc)
(struct _DeviceVelocityRec* /*pVel*/,
float /*threshold*/, float /*acc*/);
/**
* a filter stage contains the data for the adaptive IIR filtering.
* To improve results, one may run several parallel filters
* which have different decays. Since more integration means more
* delay, a given filter only does good matches in a specific phase of
* a stroke.
*
* Basically, the coupling feature makes one filter fairly enough,
* so that is the default.
*/
typedef struct _FilterStage {
float* fading_lut; /* lookup for adaptive IIR filter */
int fading_lut_size; /* size of lookup table */
float rdecay; /* reciprocal weighting halflife in ms */
float current;
} FilterStage, *FilterStagePtr;
/**
* Contains all data needed to implement mouse ballistics
*/
typedef struct _DeviceVelocityRec {
FilterStage filters[MAX_VELOCITY_FILTERS];
float velocity; /* velocity as guessed by algorithm */
int lrm_time; /* time the last motion event was processed */
int last_dx, last_dy; /* last motion delta */
int last_diff; /* last time-diff */
float corr_mul; /* config: multiply this into velocity */
float const_acceleration; /* config: (recipr.) const deceleration */
float min_acceleration; /* config: minimum acceleration */
short reset_time; /* config: reset non-visible state after # ms */
short use_softening; /* config: use softening of mouse values */
float coupling; /* config: max. divergence before coupling */
PointerAccelerationProfileFunc Profile;
PointerAccelerationProfileFunc deviceSpecificProfile;
void* profile_private;/* extended data, see SetAccelerationProfile() */
struct { /* to be able to query this information */
int profile_number;
int filter_usecount[MAX_VELOCITY_FILTERS];
} statistics;
} DeviceVelocityRec, *DeviceVelocityPtr;
extern void
InitVelocityData(DeviceVelocityPtr s);
extern void
InitFilterChain(DeviceVelocityPtr s, float rdecay, float degression,
int lutsize, int stages);
extern int
SetAccelerationProfile(DeviceVelocityPtr s, int profile_num);
extern void
SetDeviceSpecificAccelerationProfile(DeviceIntPtr s,
PointerAccelerationProfileFunc profile);
extern void
AccelerationDefaultCleanup(DeviceIntPtr pDev);
extern void
acceleratePointerPredictable(DeviceIntPtr pDev, int first_valuator,
int num_valuators, int *valuators, int evtime);
extern void
acceleratePointerClassic(DeviceIntPtr pDev, int first_valuator,
int num_valuators, int *valuators, int ignore);
#endif /* POINTERVELOCITY_H */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment