2008-07-23 09:28:06 +02:00
|
|
|
/*
|
|
|
|
*
|
2009-02-28 22:17:47 +01:00
|
|
|
* Copyright © 2006-2009 Simon Thum simon dot thum at gmx dot de
|
2008-07-23 09:28:06 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2008-07-10 15:03:39 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
|
|
#include <dix-config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <ptrveloc.h>
|
|
|
|
#include <inputstr.h>
|
2008-11-19 07:01:21 +01:00
|
|
|
#include <exevents.h>
|
|
|
|
#include <X11/Xatom.h>
|
2008-07-10 15:03:39 +02:00
|
|
|
#include <assert.h>
|
2008-07-23 12:03:17 +02:00
|
|
|
#include <os.h>
|
2008-07-10 15:03:39 +02:00
|
|
|
|
2008-11-19 07:01:21 +01:00
|
|
|
#include <xserver-properties.h>
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
/*****************************************************************************
|
2008-11-19 06:05:50 +01:00
|
|
|
* Predictable pointer acceleration
|
2008-07-10 15:03:39 +02:00
|
|
|
*
|
2009-02-28 22:17:47 +01:00
|
|
|
* 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de)
|
2008-07-10 15:03:39 +02:00
|
|
|
*
|
|
|
|
* Serves 3 complementary functions:
|
|
|
|
* 1) provide a sophisticated ballistic velocity estimate to improve
|
|
|
|
* the relation between velocity (of the device) and acceleration
|
|
|
|
* 2) make arbitrary acceleration profiles possible
|
|
|
|
* 3) decelerate by two means (constant and adaptive) if enabled
|
|
|
|
*
|
|
|
|
* Important concepts are the
|
|
|
|
*
|
|
|
|
* - Scheme
|
|
|
|
* which selects the basic algorithm
|
|
|
|
* (see devices.c/InitPointerAccelerationScheme)
|
|
|
|
* - Profile
|
|
|
|
* which returns an acceleration
|
|
|
|
* for a given velocity
|
|
|
|
*
|
2008-11-19 07:01:21 +01:00
|
|
|
* The profile can be selected by the user at runtime.
|
|
|
|
* The classic profile is intended to cleanly perform old-style
|
2008-07-10 15:03:39 +02:00
|
|
|
* function selection (threshold =/!= 0)
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/* fwds */
|
|
|
|
int
|
|
|
|
SetAccelerationProfile(DeviceVelocityPtr s, int profile_num);
|
|
|
|
static float
|
2008-07-29 10:07:43 +02:00
|
|
|
SimpleSmoothProfile(DeviceVelocityPtr pVel, float velocity,
|
|
|
|
float threshold, float acc);
|
2008-11-25 04:43:28 +01:00
|
|
|
static PointerAccelerationProfileFunc
|
|
|
|
GetAccelerationProfile(DeviceVelocityPtr s, int profile_num);
|
2008-08-06 09:42:32 +02:00
|
|
|
|
|
|
|
/*#define PTRACCEL_DEBUGGING*/
|
|
|
|
|
|
|
|
#ifdef PTRACCEL_DEBUGGING
|
|
|
|
#define DebugAccelF ErrorF
|
|
|
|
#else
|
|
|
|
#define DebugAccelF(...) /* */
|
|
|
|
#endif
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
/********************************
|
|
|
|
* Init/Uninit etc
|
|
|
|
*******************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Init struct so it should match the average case
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
void
|
2008-07-10 15:03:39 +02:00
|
|
|
InitVelocityData(DeviceVelocityPtr s)
|
|
|
|
{
|
2008-11-20 04:51:15 +01:00
|
|
|
memset(s, 0, sizeof(DeviceVelocityRec));
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
s->corr_mul = 10.0; /* dots per 10 milisecond should be usable */
|
|
|
|
s->const_acceleration = 1.0; /* no acceleration/deceleration */
|
|
|
|
s->reset_time = 300;
|
|
|
|
s->use_softening = 1;
|
|
|
|
s->min_acceleration = 1.0; /* don't decelerate */
|
2009-02-28 22:17:47 +01:00
|
|
|
s->max_rel_diff = 0.2;
|
|
|
|
s->max_diff = 1.0;
|
2009-04-01 10:53:10 +02:00
|
|
|
s->initial_range = 2;
|
2008-07-29 10:07:43 +02:00
|
|
|
s->average_accel = TRUE;
|
2008-07-23 11:28:09 +02:00
|
|
|
SetAccelerationProfile(s, AccelProfileClassic);
|
2009-02-28 22:17:47 +01:00
|
|
|
InitTrackers(s, 16);
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clean up
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
FreeVelocityData(DeviceVelocityPtr s){
|
2009-02-28 22:17:47 +01:00
|
|
|
xfree(s->tracker);
|
2008-07-10 15:03:39 +02:00
|
|
|
SetAccelerationProfile(s, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dix uninit helper, called through scheme
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
void
|
2008-11-19 06:05:50 +01:00
|
|
|
AccelerationDefaultCleanup(DeviceIntPtr pDev)
|
|
|
|
{
|
2008-07-10 15:03:39 +02:00
|
|
|
/*sanity check*/
|
|
|
|
if( pDev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable
|
|
|
|
&& pDev->valuator->accelScheme.accelData != NULL){
|
|
|
|
pDev->valuator->accelScheme.AccelSchemeProc = NULL;
|
|
|
|
FreeVelocityData(pDev->valuator->accelScheme.accelData);
|
|
|
|
xfree(pDev->valuator->accelScheme.accelData);
|
|
|
|
pDev->valuator->accelScheme.accelData = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-19 07:01:21 +01:00
|
|
|
|
|
|
|
/*************************
|
|
|
|
* Input property support
|
|
|
|
************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* choose profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
AccelSetProfileProperty(DeviceIntPtr dev, Atom atom,
|
|
|
|
XIPropertyValuePtr val, BOOL checkOnly)
|
|
|
|
{
|
|
|
|
DeviceVelocityPtr pVel;
|
|
|
|
int profile, *ptr = &profile;
|
|
|
|
int rc;
|
|
|
|
int nelem = 1;
|
|
|
|
|
|
|
|
if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER))
|
|
|
|
return Success;
|
|
|
|
|
|
|
|
pVel = GetDevicePredictableAccelData(dev);
|
|
|
|
if (!pVel)
|
|
|
|
return BadValue;
|
|
|
|
rc = XIPropToInt(val, &nelem, &ptr);
|
|
|
|
|
|
|
|
if(checkOnly)
|
|
|
|
{
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (GetAccelerationProfile(pVel, profile) == NULL)
|
|
|
|
return BadValue;
|
|
|
|
} else
|
|
|
|
SetAccelerationProfile(pVel, profile);
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
AccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr pVel)
|
|
|
|
{
|
|
|
|
int profile = pVel->statistics.profile_number;
|
|
|
|
Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
|
|
|
|
|
|
|
|
XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32,
|
|
|
|
PropModeReplace, 1, &profile, FALSE);
|
|
|
|
XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE);
|
|
|
|
XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* constant deceleration
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
AccelSetDecelProperty(DeviceIntPtr dev, Atom atom,
|
|
|
|
XIPropertyValuePtr val, BOOL checkOnly)
|
|
|
|
{
|
|
|
|
DeviceVelocityPtr pVel;
|
|
|
|
float v, *ptr = &v;
|
|
|
|
int rc;
|
|
|
|
int nelem = 1;
|
|
|
|
|
|
|
|
if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION))
|
|
|
|
return Success;
|
|
|
|
|
|
|
|
pVel = GetDevicePredictableAccelData(dev);
|
|
|
|
if (!pVel)
|
|
|
|
return BadValue;
|
|
|
|
rc = XIPropToFloat(val, &nelem, &ptr);
|
|
|
|
|
|
|
|
if(checkOnly)
|
|
|
|
{
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
return (v >= 1.0f) ? Success : BadValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(v >= 1.0f)
|
|
|
|
pVel->const_acceleration = 1/v;
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
AccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr pVel)
|
|
|
|
{
|
|
|
|
float fval = 1.0/pVel->const_acceleration;
|
|
|
|
Atom prop_const_decel = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
|
|
|
|
XIChangeDeviceProperty(dev, prop_const_decel,
|
|
|
|
XIGetKnownProperty(XATOM_FLOAT), 32,
|
|
|
|
PropModeReplace, 1, &fval, FALSE);
|
|
|
|
XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE);
|
|
|
|
XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* adaptive deceleration
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
AccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom,
|
|
|
|
XIPropertyValuePtr val, BOOL checkOnly)
|
|
|
|
{
|
|
|
|
DeviceVelocityPtr pVel;
|
|
|
|
float v, *ptr = &v;
|
|
|
|
int rc;
|
|
|
|
int nelem = 1;
|
|
|
|
|
|
|
|
if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION))
|
|
|
|
return Success;
|
|
|
|
|
|
|
|
pVel = GetDevicePredictableAccelData(dev);
|
|
|
|
if (!pVel)
|
|
|
|
return BadValue;
|
|
|
|
rc = XIPropToFloat(val, &nelem, &ptr);
|
|
|
|
|
|
|
|
if(checkOnly)
|
|
|
|
{
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
return (v >= 1.0f) ? Success : BadValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(v >= 1.0f)
|
|
|
|
pVel->min_acceleration = 1/v;
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
AccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr pVel)
|
|
|
|
{
|
|
|
|
float fval = 1.0/pVel->min_acceleration;
|
|
|
|
Atom prop_adapt_decel = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
|
|
|
|
|
|
|
|
XIChangeDeviceProperty(dev, prop_adapt_decel, XIGetKnownProperty(XATOM_FLOAT), 32,
|
|
|
|
PropModeReplace, 1, &fval, FALSE);
|
|
|
|
XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE);
|
|
|
|
XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* velocity scaling
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
AccelSetScaleProperty(DeviceIntPtr dev, Atom atom,
|
|
|
|
XIPropertyValuePtr val, BOOL checkOnly)
|
|
|
|
{
|
|
|
|
DeviceVelocityPtr pVel;
|
|
|
|
float v, *ptr = &v;
|
|
|
|
int rc;
|
|
|
|
int nelem = 1;
|
|
|
|
|
|
|
|
if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING))
|
|
|
|
return Success;
|
|
|
|
|
|
|
|
pVel = GetDevicePredictableAccelData(dev);
|
|
|
|
if (!pVel)
|
|
|
|
return BadValue;
|
|
|
|
rc = XIPropToFloat(val, &nelem, &ptr);
|
|
|
|
|
|
|
|
if (checkOnly)
|
|
|
|
{
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return (v > 0) ? Success : BadValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(v > 0)
|
|
|
|
pVel->corr_mul = v;
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
AccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr pVel)
|
|
|
|
{
|
|
|
|
float fval = pVel->corr_mul;
|
|
|
|
Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
|
|
|
|
|
|
|
|
XIChangeDeviceProperty(dev, prop_velo_scale, XIGetKnownProperty(XATOM_FLOAT), 32,
|
|
|
|
PropModeReplace, 1, &fval, FALSE);
|
|
|
|
XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE);
|
|
|
|
XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
InitializePredictableAccelerationProperties(DeviceIntPtr device)
|
|
|
|
{
|
2009-02-28 22:17:47 +01:00
|
|
|
DeviceVelocityPtr pVel = GetDevicePredictableAccelData(device);
|
2008-11-19 07:01:21 +01:00
|
|
|
|
|
|
|
if(!pVel)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
AccelInitProfileProperty(device, pVel);
|
|
|
|
AccelInitDecelProperty(device, pVel);
|
|
|
|
AccelInitAdaptDecelProperty(device, pVel);
|
|
|
|
AccelInitScaleProperty(device, pVel);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
/*********************
|
2009-02-28 22:17:47 +01:00
|
|
|
* Tracking logic
|
2008-07-10 15:03:39 +02:00
|
|
|
********************/
|
|
|
|
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
void
|
2009-02-28 22:17:47 +01:00
|
|
|
InitTrackers(DeviceVelocityPtr s, int ntracker)
|
2008-07-10 15:03:39 +02:00
|
|
|
{
|
2009-02-28 22:17:47 +01:00
|
|
|
if(ntracker < 1){
|
|
|
|
ErrorF("(dix ptracc) invalid number of trackers\n");
|
2008-07-10 15:03:39 +02:00
|
|
|
return;
|
|
|
|
}
|
2009-02-28 22:17:47 +01:00
|
|
|
xfree(s->tracker);
|
|
|
|
s->tracker = (MotionTrackerPtr)xalloc(ntracker * sizeof(MotionTracker));
|
|
|
|
memset(s->tracker, 0, ntracker * sizeof(MotionTracker));
|
|
|
|
s->num_tracker = ntracker;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return a bit field of possible directions.
|
|
|
|
* 0 = N, 2 = E, 4 = S, 6 = W, in-between is as you guess.
|
|
|
|
* There's no reason against widening to more precise directions (<45 degrees),
|
|
|
|
* should it not perform well. All this is needed for is sort out non-linear
|
|
|
|
* motion, so precision isn't paramount. However, one should not flag direction
|
|
|
|
* too narrow, since it would then cut the linear segment to zero size way too
|
|
|
|
* often.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
DoGetDirection(int dx, int dy){
|
|
|
|
float r;
|
|
|
|
int i1, i2;
|
|
|
|
/* on insignificant mickeys, flag 135 degrees */
|
|
|
|
if(abs(dx) < 2 && abs(dy < 2)){
|
|
|
|
/* first check diagonal cases */
|
|
|
|
if(dx > 0 && dy > 0)
|
|
|
|
return 4+8+16;
|
|
|
|
if(dx > 0 && dy < 0)
|
|
|
|
return 1+2+4;
|
|
|
|
if(dx < 0 && dy < 0)
|
|
|
|
return 1+128+64;
|
|
|
|
if(dx < 0 && dy > 0)
|
|
|
|
return 16+32+64;
|
|
|
|
/* check axis-aligned directions */
|
|
|
|
if(dx > 0)
|
|
|
|
return 2+4+8; /*E*/
|
|
|
|
if(dx < 0)
|
|
|
|
return 128+64+32; /*W*/
|
|
|
|
if(dy > 0)
|
|
|
|
return 32+16+8; /*S*/
|
|
|
|
if(dy < 0)
|
|
|
|
return 128+1+2; /*N*/
|
|
|
|
return 255; /* shouldn't happen */
|
|
|
|
}
|
|
|
|
/* else, compute angle and set appropriate flags */
|
|
|
|
#ifdef _ISOC99_SOURCE
|
|
|
|
r = atan2f(dy, dx);
|
|
|
|
#else
|
|
|
|
r = atan2(dy, dx);
|
|
|
|
#endif
|
|
|
|
/* find direction. We avoid r to become negative,
|
|
|
|
* since C has no well-defined modulo for such cases. */
|
|
|
|
r = (r+(M_PI*2.5))/(M_PI/4);
|
|
|
|
/* this intends to flag 2 directions (90 degrees),
|
|
|
|
* except on very well-aligned mickeys. */
|
|
|
|
i1 = (int)(r+0.1) % 8;
|
|
|
|
i2 = (int)(r+0.9) % 8;
|
|
|
|
if(i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
|
|
|
|
return 255; /* shouldn't happen */
|
|
|
|
return 1 << i1 | 1 << i2;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DIRECTION_CACHE_RANGE 5
|
|
|
|
#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
|
|
|
|
|
|
|
|
/* cache DoGetDirection(). */
|
|
|
|
static int
|
|
|
|
GetDirection(int dx, int dy){
|
|
|
|
static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE];
|
|
|
|
int i;
|
|
|
|
if (abs(dx) <= DIRECTION_CACHE_RANGE &&
|
|
|
|
abs(dy) <= DIRECTION_CACHE_RANGE) {
|
|
|
|
/* cacheable */
|
|
|
|
i = cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy];
|
|
|
|
if(i != 0){
|
|
|
|
return i;
|
2008-07-10 15:03:39 +02:00
|
|
|
}else{
|
2009-02-28 22:17:47 +01:00
|
|
|
i = DoGetDirection(dx, dy);
|
|
|
|
cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy] = i;
|
|
|
|
return i;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
2009-02-28 22:17:47 +01:00
|
|
|
}else{
|
|
|
|
/* non-cacheable */
|
|
|
|
return DoGetDirection(dx, dy);
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
#undef DIRECTION_CACHE_RANGE
|
|
|
|
#undef DIRECTION_CACHE_SIZE
|
2008-07-10 15:03:39 +02:00
|
|
|
|
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
/* convert offset (age) to array index */
|
|
|
|
#define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker)
|
2008-07-10 15:03:39 +02:00
|
|
|
|
2008-07-23 11:49:36 +02:00
|
|
|
static inline void
|
2009-02-28 22:17:47 +01:00
|
|
|
FeedTrackers(DeviceVelocityPtr s, int dx, int dy, int cur_t)
|
2008-07-23 11:49:36 +02:00
|
|
|
{
|
2009-02-28 22:17:47 +01:00
|
|
|
int n;
|
|
|
|
for(n = 0; n < s->num_tracker; n++){
|
|
|
|
s->tracker[n].dx += dx;
|
|
|
|
s->tracker[n].dy += dy;
|
2008-07-23 11:49:36 +02:00
|
|
|
}
|
2009-02-28 22:17:47 +01:00
|
|
|
n = (s->cur_tracker + 1) % s->num_tracker;
|
2009-04-01 10:53:10 +02:00
|
|
|
s->tracker[n].dx = 0;
|
|
|
|
s->tracker[n].dy = 0;
|
2009-02-28 22:17:47 +01:00
|
|
|
s->tracker[n].time = cur_t;
|
|
|
|
s->tracker[n].dir = GetDirection(dx, dy);
|
|
|
|
DebugAccelF("(dix prtacc) motion [dx: %i dy: %i dir:%i diff: %i]\n",
|
|
|
|
dx, dy, s->tracker[n].dir,
|
|
|
|
cur_t - s->tracker[s->cur_tracker].time);
|
|
|
|
s->cur_tracker = n;
|
2008-07-23 11:49:36 +02:00
|
|
|
}
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
/**
|
2009-02-28 22:17:47 +01:00
|
|
|
* calc velocity for given tracker, with
|
|
|
|
* velocity scaling.
|
|
|
|
* This assumes linear motion.
|
2008-07-10 15:03:39 +02:00
|
|
|
*/
|
2009-02-28 22:17:47 +01:00
|
|
|
static float
|
|
|
|
CalcTracker(DeviceVelocityPtr s, int offset, int cur_t){
|
|
|
|
int index = TRACKER_INDEX(s, offset);
|
|
|
|
float dist = sqrt( s->tracker[index].dx * s->tracker[index].dx
|
|
|
|
+ s->tracker[index].dy * s->tracker[index].dy);
|
2009-04-01 10:53:10 +02:00
|
|
|
int dtime = cur_t - s->tracker[index].time;
|
2009-02-28 22:17:47 +01:00
|
|
|
if(dtime > 0)
|
|
|
|
return (dist / dtime);
|
2008-07-10 15:03:39 +02:00
|
|
|
else
|
2009-02-28 22:17:47 +01:00
|
|
|
return 0;/* synonymous for NaN, since we're not C99 */
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
/* find the most plausible velocity. That is, the most distant
|
|
|
|
* (in time) tracker which isn't too old, beyond a linear partition,
|
|
|
|
* or simply too much off initial velocity.
|
|
|
|
*
|
2009-04-01 10:53:10 +02:00
|
|
|
* May return 0.
|
2008-07-10 15:03:39 +02:00
|
|
|
*/
|
2009-02-28 22:17:47 +01:00
|
|
|
static float
|
2009-04-01 10:53:10 +02:00
|
|
|
QueryTrackers(DeviceVelocityPtr s, int cur_t){
|
|
|
|
int n, offset, dir = 255, i = -1, age_ms;
|
2009-02-28 22:17:47 +01:00
|
|
|
/* initial velocity: a low-offset, valid velocity */
|
|
|
|
float iveloc = 0, res = 0, tmp, vdiff;
|
|
|
|
float vfac = s->corr_mul * s->const_acceleration; /* premultiply */
|
|
|
|
/* loop from current to older data */
|
2009-04-01 10:53:10 +02:00
|
|
|
for(offset = 1; offset < s->num_tracker; offset++){
|
2009-02-28 22:17:47 +01:00
|
|
|
n = TRACKER_INDEX(s, offset);
|
|
|
|
|
2009-04-01 10:53:10 +02:00
|
|
|
age_ms = cur_t - s->tracker[n].time;
|
|
|
|
|
|
|
|
/* bail out if data is too old and protect from overrun */
|
|
|
|
if (age_ms >= s->reset_time || age_ms < 0) {
|
2009-02-28 22:17:47 +01:00
|
|
|
DebugAccelF("(dix prtacc) query: tracker too old\n");
|
|
|
|
break;
|
|
|
|
}
|
2008-07-10 15:03:39 +02:00
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
/*
|
|
|
|
* this heuristic avoids using the linear-motion velocity formula
|
|
|
|
* in CalcTracker() on motion that isn't exactly linear. So to get
|
|
|
|
* even more precision we could subdivide as a final step, so possible
|
|
|
|
* non-linearities are accounted for.
|
|
|
|
*/
|
|
|
|
dir &= s->tracker[n].dir;
|
|
|
|
if(dir == 0){
|
|
|
|
DebugAccelF("(dix prtacc) query: no longer linear\n");
|
|
|
|
/* instead of breaking it we might also inspect the partition after,
|
|
|
|
* but actual improvement with this is probably rare. */
|
2008-07-10 15:03:39 +02:00
|
|
|
break;
|
2009-02-28 22:17:47 +01:00
|
|
|
}
|
2008-07-10 15:03:39 +02:00
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
tmp = CalcTracker(s, offset, cur_t) * vfac;
|
|
|
|
|
|
|
|
if ((iveloc == 0 || offset <= s->initial_range) && tmp != 0) {
|
|
|
|
/* set initial velocity and result */
|
|
|
|
res = iveloc = tmp;
|
|
|
|
i = offset;
|
|
|
|
} else if (iveloc != 0 && tmp != 0) {
|
|
|
|
vdiff = fabs(iveloc - tmp);
|
|
|
|
if (vdiff <= s->max_diff ||
|
|
|
|
vdiff/(iveloc + tmp) < s->max_rel_diff) {
|
|
|
|
/* we're in range with the initial velocity,
|
|
|
|
* so this result is likely better
|
|
|
|
* (it contains more information). */
|
|
|
|
res = tmp;
|
|
|
|
i = offset;
|
|
|
|
}else{
|
|
|
|
/* we're not in range, quit - it won't get better. */
|
|
|
|
DebugAccelF("(dix prtacc) query: tracker too different:"
|
|
|
|
" old %2.2f initial %2.2f diff: %2.2f\n",
|
|
|
|
tmp, iveloc, vdiff);
|
|
|
|
break;
|
|
|
|
}
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
}
|
2009-02-28 22:17:47 +01:00
|
|
|
if(offset == s->num_tracker){
|
|
|
|
DebugAccelF("(dix prtacc) query: last tracker in effect\n");
|
|
|
|
i = s->num_tracker-1;
|
|
|
|
}
|
|
|
|
if(i>=0){
|
|
|
|
n = TRACKER_INDEX(s, i);
|
|
|
|
DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n",
|
|
|
|
i,
|
|
|
|
s->tracker[n].dx,
|
|
|
|
s->tracker[n].dy,
|
|
|
|
cur_t - s->tracker[n].time);
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
2009-02-28 22:17:47 +01:00
|
|
|
return res;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
#undef TRACKER_INDEX
|
2008-07-10 15:03:39 +02:00
|
|
|
|
|
|
|
/**
|
2009-02-08 17:21:09 +01:00
|
|
|
* Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
|
2008-07-10 15:03:39 +02:00
|
|
|
* return true if non-visible state reset is suggested
|
|
|
|
*/
|
|
|
|
static short
|
2009-02-08 17:21:09 +01:00
|
|
|
ProcessVelocityData2D(
|
2008-07-23 11:49:36 +02:00
|
|
|
DeviceVelocityPtr s,
|
|
|
|
int dx,
|
|
|
|
int dy,
|
|
|
|
int time)
|
2008-07-10 15:03:39 +02:00
|
|
|
{
|
2009-02-28 22:17:47 +01:00
|
|
|
float velocity;
|
2009-02-08 17:21:09 +01:00
|
|
|
|
|
|
|
s->last_velocity = s->velocity;
|
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
FeedTrackers(s, dx, dy, time);
|
2008-07-10 15:03:39 +02:00
|
|
|
|
2009-04-01 10:53:10 +02:00
|
|
|
velocity = QueryTrackers(s, time);
|
2008-07-10 15:03:39 +02:00
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
s->velocity = velocity;
|
|
|
|
return velocity == 0;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* this flattens significant ( > 1) mickeys a little bit for more steady
|
|
|
|
* constant-velocity response
|
|
|
|
*/
|
|
|
|
static inline float
|
|
|
|
ApplySimpleSoftening(int od, int d)
|
|
|
|
{
|
|
|
|
float res = d;
|
|
|
|
if (d <= 1 && d >= -1)
|
|
|
|
return res;
|
|
|
|
if (d > od)
|
|
|
|
res -= 0.5;
|
|
|
|
else if (d < od)
|
|
|
|
res += 0.5;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
ApplySofteningAndConstantDeceleration(
|
|
|
|
DeviceVelocityPtr s,
|
|
|
|
int dx,
|
|
|
|
int dy,
|
|
|
|
float* fdx,
|
|
|
|
float* fdy,
|
|
|
|
short do_soften)
|
|
|
|
{
|
|
|
|
if (do_soften && s->use_softening) {
|
|
|
|
*fdx = ApplySimpleSoftening(s->last_dx, dx);
|
|
|
|
*fdy = ApplySimpleSoftening(s->last_dy, dy);
|
|
|
|
} else {
|
|
|
|
*fdx = dx;
|
|
|
|
*fdy = dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
*fdx *= s->const_acceleration;
|
|
|
|
*fdy *= s->const_acceleration;
|
|
|
|
}
|
|
|
|
|
2008-07-29 10:07:43 +02:00
|
|
|
/*
|
|
|
|
* compute the acceleration for given velocity and enforce min_acceleartion
|
|
|
|
*/
|
|
|
|
static float
|
|
|
|
BasicComputeAcceleration(
|
|
|
|
DeviceVelocityPtr pVel,
|
|
|
|
float velocity,
|
|
|
|
float threshold,
|
|
|
|
float acc){
|
|
|
|
|
|
|
|
float result;
|
|
|
|
result = pVel->Profile(pVel, velocity, threshold, acc);
|
|
|
|
|
|
|
|
/* enforce min_acceleration */
|
|
|
|
if (result < pVel->min_acceleration)
|
|
|
|
result = pVel->min_acceleration;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compute acceleration. Takes into account averaging, nv-reset, etc.
|
|
|
|
*/
|
|
|
|
static float
|
|
|
|
ComputeAcceleration(
|
|
|
|
DeviceVelocityPtr vel,
|
|
|
|
float threshold,
|
|
|
|
float acc){
|
|
|
|
float res;
|
|
|
|
|
2009-02-28 22:17:47 +01:00
|
|
|
if(vel->velocity <= 0){
|
2008-08-06 09:42:32 +02:00
|
|
|
DebugAccelF("(dix ptracc) profile skipped\n");
|
2008-07-29 10:07:43 +02:00
|
|
|
/*
|
2009-02-28 22:17:47 +01:00
|
|
|
* If we have no idea about device velocity, don't pretend it.
|
2008-07-29 10:07:43 +02:00
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(vel->average_accel && vel->velocity != vel->last_velocity){
|
|
|
|
/* use simpson's rule to average acceleration between
|
|
|
|
* current and previous velocity.
|
|
|
|
* Though being the more natural choice, it causes a minor delay
|
|
|
|
* in comparison, so it can be disabled. */
|
|
|
|
res = BasicComputeAcceleration(vel, vel->velocity, threshold, acc);
|
|
|
|
res += BasicComputeAcceleration(vel, vel->last_velocity, threshold, acc);
|
|
|
|
res += 4.0f * BasicComputeAcceleration(vel,
|
|
|
|
(vel->last_velocity + vel->velocity) / 2,
|
|
|
|
threshold, acc);
|
|
|
|
res /= 6.0f;
|
2008-08-06 09:42:32 +02:00
|
|
|
DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n",
|
|
|
|
vel->velocity, vel->last_velocity, res);
|
2008-07-29 10:07:43 +02:00
|
|
|
return res;
|
|
|
|
}else{
|
|
|
|
res = BasicComputeAcceleration(vel, vel->velocity, threshold, acc);
|
2008-08-06 09:42:32 +02:00
|
|
|
DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n",
|
2008-07-29 10:07:43 +02:00
|
|
|
vel->velocity, res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
2008-07-10 15:03:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*****************************************
|
|
|
|
* Acceleration functions and profiles
|
|
|
|
****************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Polynomial function similar previous one, but with f(1) = 1
|
|
|
|
*/
|
|
|
|
static float
|
2008-07-23 12:10:52 +02:00
|
|
|
PolynomialAccelerationProfile(
|
|
|
|
DeviceVelocityPtr pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
float velocity,
|
2008-07-23 12:10:52 +02:00
|
|
|
float ignored,
|
|
|
|
float acc)
|
2008-07-10 15:03:39 +02:00
|
|
|
{
|
2008-07-29 10:07:43 +02:00
|
|
|
return pow(velocity, (acc - 1.0) * 0.5);
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* returns acceleration for velocity.
|
|
|
|
* This profile selects the two functions like the old scheme did
|
|
|
|
*/
|
|
|
|
static float
|
|
|
|
ClassicProfile(
|
|
|
|
DeviceVelocityPtr pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
float velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
float threshold,
|
|
|
|
float acc)
|
|
|
|
{
|
|
|
|
if (threshold) {
|
|
|
|
return SimpleSmoothProfile (pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
threshold,
|
|
|
|
acc);
|
|
|
|
} else {
|
|
|
|
return PolynomialAccelerationProfile (pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
0,
|
|
|
|
acc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Power profile
|
|
|
|
* This has a completely smooth transition curve, i.e. no jumps in the
|
|
|
|
* derivatives.
|
|
|
|
*
|
|
|
|
* This has the expense of overall response dependency on min-acceleration.
|
|
|
|
* In effect, min_acceleration mimics const_acceleration in this profile.
|
|
|
|
*/
|
|
|
|
static float
|
|
|
|
PowerProfile(
|
|
|
|
DeviceVelocityPtr pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
float velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
float threshold,
|
|
|
|
float acc)
|
|
|
|
{
|
|
|
|
float vel_dist;
|
|
|
|
|
|
|
|
acc = (acc-1.0) * 0.1f + 1.0; /* without this, acc of 2 is unuseable */
|
|
|
|
|
2008-07-29 10:07:43 +02:00
|
|
|
if (velocity <= threshold)
|
2008-07-10 15:03:39 +02:00
|
|
|
return pVel->min_acceleration;
|
2008-07-29 10:07:43 +02:00
|
|
|
vel_dist = velocity - threshold;
|
2008-07-10 15:03:39 +02:00
|
|
|
return (pow(acc, vel_dist)) * pVel->min_acceleration;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* just a smooth function in [0..1] -> [0..1]
|
|
|
|
* - point symmetry at 0.5
|
|
|
|
* - f'(0) = f'(1) = 0
|
2008-07-23 12:10:52 +02:00
|
|
|
* - starts faster than a sinoid
|
|
|
|
* - smoothness C1 (Cinf if you dare to ignore endpoints)
|
2008-07-10 15:03:39 +02:00
|
|
|
*/
|
|
|
|
static inline float
|
|
|
|
CalcPenumbralGradient(float x){
|
|
|
|
x *= 2.0f;
|
|
|
|
x -= 1.0f;
|
|
|
|
return 0.5f + (x * sqrt(1.0f - x*x) + asin(x))/M_PI;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* acceleration function similar to classic accelerated/unaccelerated,
|
|
|
|
* but with smooth transition in between (and towards zero for adaptive dec.).
|
|
|
|
*/
|
|
|
|
static float
|
|
|
|
SimpleSmoothProfile(
|
|
|
|
DeviceVelocityPtr pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
float velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
float threshold,
|
|
|
|
float acc)
|
|
|
|
{
|
|
|
|
if(velocity < 1.0f)
|
|
|
|
return CalcPenumbralGradient(0.5 + velocity*0.5) * 2.0f - 1.0f;
|
|
|
|
if(threshold < 1.0f)
|
|
|
|
threshold = 1.0f;
|
|
|
|
if (velocity <= threshold)
|
|
|
|
return 1;
|
|
|
|
velocity /= threshold;
|
|
|
|
if (velocity >= acc)
|
|
|
|
return acc;
|
|
|
|
else
|
|
|
|
return 1.0f + (CalcPenumbralGradient(velocity/acc) * (acc - 1.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This profile uses the first half of the penumbral gradient as a start
|
|
|
|
* and then scales linearly.
|
|
|
|
*/
|
|
|
|
static float
|
|
|
|
SmoothLinearProfile(
|
|
|
|
DeviceVelocityPtr pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
float velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
float threshold,
|
|
|
|
float acc)
|
|
|
|
{
|
2008-07-23 12:10:52 +02:00
|
|
|
float res, nv;
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
if(acc > 1.0f)
|
|
|
|
acc -= 1.0f; /*this is so acc = 1 is no acceleration */
|
|
|
|
else
|
|
|
|
return 1.0f;
|
|
|
|
|
2008-07-29 10:07:43 +02:00
|
|
|
nv = (velocity - threshold) * acc * 0.5f;
|
2008-07-23 12:10:52 +02:00
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
if(nv < 0){
|
|
|
|
res = 0;
|
|
|
|
}else if(nv < 2){
|
|
|
|
res = CalcPenumbralGradient(nv*0.25f)*2.0f;
|
|
|
|
}else{
|
|
|
|
nv -= 2.0f;
|
|
|
|
res = nv * 2.0f / M_PI /* steepness of gradient at 0.5 */
|
|
|
|
+ 1.0f; /* gradient crosses 2|1 */
|
|
|
|
}
|
|
|
|
res += pVel->min_acceleration;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static float
|
|
|
|
LinearProfile(
|
|
|
|
DeviceVelocityPtr pVel,
|
2008-07-29 10:07:43 +02:00
|
|
|
float velocity,
|
2008-07-10 15:03:39 +02:00
|
|
|
float threshold,
|
|
|
|
float acc)
|
|
|
|
{
|
2008-07-29 10:07:43 +02:00
|
|
|
return acc * velocity;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-25 04:43:28 +01:00
|
|
|
static PointerAccelerationProfileFunc
|
|
|
|
GetAccelerationProfile(
|
2008-07-10 15:03:39 +02:00
|
|
|
DeviceVelocityPtr s,
|
|
|
|
int profile_num)
|
|
|
|
{
|
|
|
|
switch(profile_num){
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfileClassic:
|
2008-11-25 04:43:28 +01:00
|
|
|
return ClassicProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfileDeviceSpecific:
|
2008-11-25 04:43:28 +01:00
|
|
|
return s->deviceSpecificProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfilePolynomial:
|
2008-11-25 04:43:28 +01:00
|
|
|
return PolynomialAccelerationProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfileSmoothLinear:
|
2008-11-25 04:43:28 +01:00
|
|
|
return SmoothLinearProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfileSimple:
|
2008-11-25 04:43:28 +01:00
|
|
|
return SimpleSmoothProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfilePower:
|
2008-11-25 04:43:28 +01:00
|
|
|
return PowerProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfileLinear:
|
2008-11-25 04:43:28 +01:00
|
|
|
return LinearProfile;
|
2008-07-23 11:28:09 +02:00
|
|
|
case AccelProfileReserved:
|
|
|
|
/* reserved for future use, e.g. a user-defined profile */
|
2008-07-10 15:03:39 +02:00
|
|
|
default:
|
2008-11-25 04:43:28 +01:00
|
|
|
return NULL;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
2008-11-25 04:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the profile by number.
|
|
|
|
* Intended to make profiles exchangeable at runtime.
|
|
|
|
* If you created a profile, give it a number here and in the header to
|
|
|
|
* make it selectable. In case some profile-specific init is needed, here
|
|
|
|
* would be a good place, since FreeVelocityData() also calls this with -1.
|
|
|
|
* returns FALSE (0) if profile number is unavailable.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
SetAccelerationProfile(
|
|
|
|
DeviceVelocityPtr s,
|
|
|
|
int profile_num)
|
|
|
|
{
|
|
|
|
PointerAccelerationProfileFunc profile;
|
|
|
|
profile = GetAccelerationProfile(s, profile_num);
|
|
|
|
|
|
|
|
if(profile == NULL && profile_num != -1)
|
|
|
|
return FALSE;
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
if(s->profile_private != NULL){
|
|
|
|
/* Here one could free old profile-private data */
|
|
|
|
xfree(s->profile_private);
|
|
|
|
s->profile_private = NULL;
|
|
|
|
}
|
|
|
|
/* Here one could init profile-private data */
|
|
|
|
s->Profile = profile;
|
|
|
|
s->statistics.profile_number = profile_num;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2008-07-28 14:07:48 +02:00
|
|
|
/**********************************************
|
|
|
|
* driver interaction
|
|
|
|
**********************************************/
|
|
|
|
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
/**
|
|
|
|
* device-specific profile
|
|
|
|
*
|
|
|
|
* The device-specific profile is intended as a hook for a driver
|
|
|
|
* which may want to provide an own acceleration profile.
|
|
|
|
* It should not rely on profile-private data, instead
|
|
|
|
* it should do init/uninit in the driver (ie. with DEVICE_INIT and friends).
|
|
|
|
* Users may override or choose it.
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
void
|
2008-07-10 15:03:39 +02:00
|
|
|
SetDeviceSpecificAccelerationProfile(
|
2008-07-23 11:33:25 +02:00
|
|
|
DeviceVelocityPtr s,
|
2008-07-10 15:03:39 +02:00
|
|
|
PointerAccelerationProfileFunc profile)
|
2008-07-23 11:33:25 +02:00
|
|
|
{
|
|
|
|
if(s)
|
|
|
|
s->deviceSpecificProfile = profile;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if
|
|
|
|
* the predictable acceleration scheme is not in effect.
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
DeviceVelocityPtr
|
2008-07-23 11:33:25 +02:00
|
|
|
GetDevicePredictableAccelData(
|
|
|
|
DeviceIntPtr pDev)
|
2008-07-10 15:03:39 +02:00
|
|
|
{
|
|
|
|
/*sanity check*/
|
2008-07-28 14:07:48 +02:00
|
|
|
if(!pDev){
|
|
|
|
ErrorF("[dix] accel: DeviceIntPtr was NULL");
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-07-10 15:03:39 +02:00
|
|
|
if( pDev->valuator &&
|
|
|
|
pDev->valuator->accelScheme.AccelSchemeProc ==
|
|
|
|
acceleratePointerPredictable &&
|
|
|
|
pDev->valuator->accelScheme.accelData != NULL){
|
2008-07-23 11:33:25 +02:00
|
|
|
|
|
|
|
return (DeviceVelocityPtr)pDev->valuator->accelScheme.accelData;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
2008-07-23 11:33:25 +02:00
|
|
|
return NULL;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************************
|
|
|
|
* acceleration schemes
|
|
|
|
*******************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modifies valuators in-place.
|
|
|
|
* This version employs a velocity approximation algorithm to
|
|
|
|
* enable fine-grained predictable acceleration profiles.
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
void
|
2008-07-23 12:10:52 +02:00
|
|
|
acceleratePointerPredictable(
|
|
|
|
DeviceIntPtr pDev,
|
|
|
|
int first_valuator,
|
|
|
|
int num_valuators,
|
|
|
|
int *valuators,
|
|
|
|
int evtime)
|
2008-07-10 15:03:39 +02:00
|
|
|
{
|
|
|
|
float mult = 0.0;
|
|
|
|
int dx = 0, dy = 0;
|
|
|
|
int *px = NULL, *py = NULL;
|
|
|
|
DeviceVelocityPtr velocitydata =
|
|
|
|
(DeviceVelocityPtr) pDev->valuator->accelScheme.accelData;
|
2009-04-08 14:35:01 +02:00
|
|
|
float fdx, fdy, tmp; /* no need to init */
|
2009-02-08 17:21:09 +01:00
|
|
|
Bool soften = TRUE;
|
2008-07-10 15:03:39 +02:00
|
|
|
|
|
|
|
if (!num_valuators || !valuators || !velocitydata)
|
|
|
|
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){
|
2009-02-08 17:21:09 +01:00
|
|
|
/* reset non-visible state? */
|
|
|
|
if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
|
|
|
|
/* if nv-reset: set to center of pixel.
|
|
|
|
* makes sense as long as there are no means of passing on
|
|
|
|
* sub-pixel values to apps(XI2?). If you remove it, make
|
|
|
|
* sure suitable rounding is applied below.
|
2008-07-29 10:07:43 +02:00
|
|
|
*/
|
2009-02-28 22:17:47 +01:00
|
|
|
pDev->last.remainder[0] = pDev->last.remainder[1] = 0;
|
2009-02-08 17:21:09 +01:00
|
|
|
soften = FALSE;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) {
|
|
|
|
/* invoke acceleration profile to determine acceleration */
|
2008-07-29 10:07:43 +02:00
|
|
|
mult = ComputeAcceleration (velocitydata,
|
|
|
|
pDev->ptrfeed->ctrl.threshold,
|
|
|
|
(float)pDev->ptrfeed->ctrl.num /
|
|
|
|
(float)pDev->ptrfeed->ctrl.den);
|
2008-07-10 15:03:39 +02:00
|
|
|
|
|
|
|
if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
|
|
|
|
ApplySofteningAndConstantDeceleration( velocitydata,
|
2009-02-08 17:21:09 +01:00
|
|
|
dx, dy,
|
|
|
|
&fdx, &fdy,
|
|
|
|
(mult > 1.0) && soften);
|
|
|
|
|
2008-07-10 15:03:39 +02:00
|
|
|
if (dx) {
|
2009-04-08 14:35:01 +02:00
|
|
|
tmp = mult * fdx + pDev->last.remainder[0];
|
2009-04-30 12:58:48 +02:00
|
|
|
/* Since it may not be apparent: lrintf() does not offer
|
|
|
|
* strong statements about rounding; however because we
|
|
|
|
* process each axis conditionally, there's no danger
|
|
|
|
* of a toggling remainder. Its lack of guarantees hopefully
|
|
|
|
* makes it faster on the average target. */
|
|
|
|
*px = lrintf(tmp);
|
2009-04-08 14:35:01 +02:00
|
|
|
pDev->last.remainder[0] = tmp - (float)*px;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
if (dy) {
|
2009-04-08 14:35:01 +02:00
|
|
|
tmp = mult * fdy + pDev->last.remainder[1];
|
2009-04-30 12:58:48 +02:00
|
|
|
*py = lrintf(tmp);
|
2009-04-08 14:35:01 +02:00
|
|
|
pDev->last.remainder[1] = tmp - (float)*py;
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
2009-04-30 12:58:48 +02:00
|
|
|
DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n", *px, *py, pDev->last.remainder[0], pDev->last.remainder[1], fdx, fdy);
|
2008-07-10 15:03:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* remember last motion delta (for softening/slow movement treatment) */
|
|
|
|
velocitydata->last_dx = dx;
|
|
|
|
velocitydata->last_dy = dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Originally a part of xf86PostMotionEvent; modifies valuators
|
|
|
|
* in-place. Retained mostly for embedded scenarios.
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
void
|
2008-07-23 11:10:22 +02:00
|
|
|
acceleratePointerLightweight(
|
|
|
|
DeviceIntPtr pDev,
|
|
|
|
int first_valuator,
|
|
|
|
int num_valuators,
|
|
|
|
int *valuators,
|
|
|
|
int ignored)
|
2008-07-10 15:03:39 +02:00
|
|
|
{
|
|
|
|
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->last.remainder[0] = ((float)dx *
|
|
|
|
(float)(pDev->ptrfeed->ctrl.num)) /
|
|
|
|
(float)(pDev->ptrfeed->ctrl.den) +
|
|
|
|
pDev->last.remainder[0];
|
|
|
|
if (px) {
|
|
|
|
*px = (int)pDev->last.remainder[0];
|
|
|
|
pDev->last.remainder[0] = pDev->last.remainder[0] -
|
|
|
|
(float)(*px);
|
|
|
|
}
|
|
|
|
|
|
|
|
pDev->last.remainder[1] = ((float)dy *
|
|
|
|
(float)(pDev->ptrfeed->ctrl.num)) /
|
|
|
|
(float)(pDev->ptrfeed->ctrl.den) +
|
|
|
|
pDev->last.remainder[1];
|
|
|
|
if (py) {
|
|
|
|
*py = (int)pDev->last.remainder[1];
|
|
|
|
pDev->last.remainder[1] = pDev->last.remainder[1] -
|
|
|
|
(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->last.remainder[0] = mult * (float)dx +
|
|
|
|
pDev->last.remainder[0];
|
|
|
|
*px = (int)pDev->last.remainder[0];
|
|
|
|
pDev->last.remainder[0] = pDev->last.remainder[0] -
|
|
|
|
(float)(*px);
|
|
|
|
}
|
|
|
|
if (dy) {
|
|
|
|
pDev->last.remainder[1] = mult * (float)dy +
|
|
|
|
pDev->last.remainder[1];
|
|
|
|
*py = (int)pDev->last.remainder[1];
|
|
|
|
pDev->last.remainder[1] = pDev->last.remainder[1] -
|
|
|
|
(float)(*py);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|