1706f2543Smrg/*
2706f2543Smrg *
3706f2543Smrg * Copyright © 2006-2009 Simon Thum             simon dot thum at gmx dot de
4706f2543Smrg *
5706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
6706f2543Smrg * copy of this software and associated documentation files (the "Software"),
7706f2543Smrg * to deal in the Software without restriction, including without limitation
8706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
10706f2543Smrg * Software is furnished to do so, subject to the following conditions:
11706f2543Smrg *
12706f2543Smrg * The above copyright notice and this permission notice (including the next
13706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the
14706f2543Smrg * Software.
15706f2543Smrg *
16706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19706f2543Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20706f2543Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21706f2543Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22706f2543Smrg * DEALINGS IN THE SOFTWARE.
23706f2543Smrg */
24706f2543Smrg
25706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
26706f2543Smrg#include <dix-config.h>
27706f2543Smrg#endif
28706f2543Smrg
29706f2543Smrg#include <math.h>
30706f2543Smrg#include <ptrveloc.h>
31706f2543Smrg#include <exevents.h>
32706f2543Smrg#include <X11/Xatom.h>
33706f2543Smrg
34706f2543Smrg#include <xserver-properties.h>
35706f2543Smrg
36706f2543Smrg/*****************************************************************************
37706f2543Smrg * Predictable pointer acceleration
38706f2543Smrg *
39706f2543Smrg * 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de)
40706f2543Smrg *
41706f2543Smrg * Serves 3 complementary functions:
42706f2543Smrg * 1) provide a sophisticated ballistic velocity estimate to improve
43706f2543Smrg *    the relation between velocity (of the device) and acceleration
44706f2543Smrg * 2) make arbitrary acceleration profiles possible
45706f2543Smrg * 3) decelerate by two means (constant and adaptive) if enabled
46706f2543Smrg *
47706f2543Smrg * Important concepts are the
48706f2543Smrg *
49706f2543Smrg * - Scheme
50706f2543Smrg *      which selects the basic algorithm
51706f2543Smrg *      (see devices.c/InitPointerAccelerationScheme)
52706f2543Smrg * - Profile
53706f2543Smrg *      which returns an acceleration
54706f2543Smrg *      for a given velocity
55706f2543Smrg *
56706f2543Smrg *  The profile can be selected by the user at runtime.
57706f2543Smrg *  The classic profile is intended to cleanly perform old-style
58706f2543Smrg *  function selection (threshold =/!= 0)
59706f2543Smrg *
60706f2543Smrg ****************************************************************************/
61706f2543Smrg
62706f2543Smrg/* fwds */
63706f2543Smrgint
64706f2543SmrgSetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
65706f2543Smrgstatic float
66706f2543SmrgSimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, float velocity,
67706f2543Smrg                    float threshold, float acc);
68706f2543Smrgstatic PointerAccelerationProfileFunc
69706f2543SmrgGetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
70706f2543Smrg
71706f2543Smrg/*#define PTRACCEL_DEBUGGING*/
72706f2543Smrg
73706f2543Smrg#ifdef PTRACCEL_DEBUGGING
74706f2543Smrg#define DebugAccelF ErrorF
75706f2543Smrg#else
76706f2543Smrg#define DebugAccelF(...) /* */
77706f2543Smrg#endif
78706f2543Smrg
79706f2543Smrg/********************************
80706f2543Smrg *  Init/Uninit
81706f2543Smrg *******************************/
82706f2543Smrg
83706f2543Smrg/* some int which is not a profile number */
84706f2543Smrg#define PROFILE_UNINITIALIZE (-100)
85706f2543Smrg
86706f2543Smrg
87706f2543Smrg/**
88706f2543Smrg * Init struct so it should match the average case
89706f2543Smrg */
90706f2543Smrgvoid
91706f2543SmrgInitVelocityData(DeviceVelocityPtr vel)
92706f2543Smrg{
93706f2543Smrg    memset(vel, 0, sizeof(DeviceVelocityRec));
94706f2543Smrg
95706f2543Smrg    vel->corr_mul = 10.0;      /* dots per 10 milisecond should be usable */
96706f2543Smrg    vel->const_acceleration = 1.0;   /* no acceleration/deceleration  */
97706f2543Smrg    vel->reset_time = 300;
98706f2543Smrg    vel->use_softening = 1;
99706f2543Smrg    vel->min_acceleration = 1.0; /* don't decelerate */
100706f2543Smrg    vel->max_rel_diff = 0.2;
101706f2543Smrg    vel->max_diff = 1.0;
102706f2543Smrg    vel->initial_range = 2;
103706f2543Smrg    vel->average_accel = TRUE;
104706f2543Smrg    SetAccelerationProfile(vel, AccelProfileClassic);
105706f2543Smrg    InitTrackers(vel, 16);
106706f2543Smrg}
107706f2543Smrg
108706f2543Smrg
109706f2543Smrg/**
110706f2543Smrg * Clean up
111706f2543Smrg */
112706f2543Smrgvoid
113706f2543SmrgFreeVelocityData(DeviceVelocityPtr vel){
114706f2543Smrg    free(vel->tracker);
115706f2543Smrg    SetAccelerationProfile(vel, PROFILE_UNINITIALIZE);
116706f2543Smrg}
117706f2543Smrg
118706f2543Smrg
119706f2543Smrg/*
120706f2543Smrg *  dix uninit helper, called through scheme
121706f2543Smrg */
122706f2543Smrgvoid
123706f2543SmrgAccelerationDefaultCleanup(DeviceIntPtr dev)
124706f2543Smrg{
125706f2543Smrg    /*sanity check*/
126706f2543Smrg    if( dev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable
127706f2543Smrg            && dev->valuator->accelScheme.accelData != NULL){
128706f2543Smrg        dev->valuator->accelScheme.AccelSchemeProc = NULL;
129706f2543Smrg        FreeVelocityData(dev->valuator->accelScheme.accelData);
130706f2543Smrg        free(dev->valuator->accelScheme.accelData);
131706f2543Smrg        dev->valuator->accelScheme.accelData = NULL;
132706f2543Smrg        DeletePredictableAccelerationProperties(dev);
133706f2543Smrg    }
134706f2543Smrg}
135706f2543Smrg
136706f2543Smrg
137706f2543Smrg/*************************
138706f2543Smrg * Input property support
139706f2543Smrg ************************/
140706f2543Smrg
141706f2543Smrg/**
142706f2543Smrg * choose profile
143706f2543Smrg */
144706f2543Smrgstatic int
145706f2543SmrgAccelSetProfileProperty(DeviceIntPtr dev, Atom atom,
146706f2543Smrg                        XIPropertyValuePtr val, BOOL checkOnly)
147706f2543Smrg{
148706f2543Smrg    DeviceVelocityPtr vel;
149706f2543Smrg    int profile, *ptr = &profile;
150706f2543Smrg    int rc;
151706f2543Smrg    int nelem = 1;
152706f2543Smrg
153706f2543Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER))
154706f2543Smrg        return Success;
155706f2543Smrg
156706f2543Smrg    vel = GetDevicePredictableAccelData(dev);
157706f2543Smrg    if (!vel)
158706f2543Smrg        return BadValue;
159706f2543Smrg    rc = XIPropToInt(val, &nelem, &ptr);
160706f2543Smrg
161706f2543Smrg    if(checkOnly)
162706f2543Smrg    {
163706f2543Smrg        if (rc)
164706f2543Smrg            return rc;
165706f2543Smrg
166706f2543Smrg        if (GetAccelerationProfile(vel, profile) == NULL)
167706f2543Smrg            return BadValue;
168706f2543Smrg    } else
169706f2543Smrg	SetAccelerationProfile(vel, profile);
170706f2543Smrg
171706f2543Smrg    return Success;
172706f2543Smrg}
173706f2543Smrg
174706f2543Smrgstatic long
175706f2543SmrgAccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
176706f2543Smrg{
177706f2543Smrg    int profile = vel->statistics.profile_number;
178706f2543Smrg    Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
179706f2543Smrg
180706f2543Smrg    XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32,
181706f2543Smrg                           PropModeReplace, 1, &profile, FALSE);
182706f2543Smrg    XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE);
183706f2543Smrg    return XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL);
184706f2543Smrg}
185706f2543Smrg
186706f2543Smrg/**
187706f2543Smrg * constant deceleration
188706f2543Smrg */
189706f2543Smrgstatic int
190706f2543SmrgAccelSetDecelProperty(DeviceIntPtr dev, Atom atom,
191706f2543Smrg                      XIPropertyValuePtr val, BOOL checkOnly)
192706f2543Smrg{
193706f2543Smrg    DeviceVelocityPtr vel;
194706f2543Smrg    float v, *ptr = &v;
195706f2543Smrg    int rc;
196706f2543Smrg    int nelem = 1;
197706f2543Smrg
198706f2543Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION))
199706f2543Smrg        return Success;
200706f2543Smrg
201706f2543Smrg    vel = GetDevicePredictableAccelData(dev);
202706f2543Smrg    if (!vel)
203706f2543Smrg        return BadValue;
204706f2543Smrg    rc = XIPropToFloat(val, &nelem, &ptr);
205706f2543Smrg
206706f2543Smrg    if(checkOnly)
207706f2543Smrg    {
208706f2543Smrg        if (rc)
209706f2543Smrg            return rc;
210706f2543Smrg	return (v >= 1.0f) ? Success : BadValue;
211706f2543Smrg    }
212706f2543Smrg
213706f2543Smrg    if(v >= 1.0f)
214706f2543Smrg	vel->const_acceleration = 1/v;
215706f2543Smrg
216706f2543Smrg    return Success;
217706f2543Smrg}
218706f2543Smrg
219706f2543Smrgstatic long
220706f2543SmrgAccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
221706f2543Smrg{
222706f2543Smrg    float fval = 1.0/vel->const_acceleration;
223706f2543Smrg    Atom prop_const_decel = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
224706f2543Smrg    XIChangeDeviceProperty(dev, prop_const_decel,
225706f2543Smrg                           XIGetKnownProperty(XATOM_FLOAT), 32,
226706f2543Smrg                           PropModeReplace, 1, &fval, FALSE);
227706f2543Smrg    XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE);
228706f2543Smrg    return XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL);
229706f2543Smrg}
230706f2543Smrg
231706f2543Smrg
232706f2543Smrg/**
233706f2543Smrg * adaptive deceleration
234706f2543Smrg */
235706f2543Smrgstatic int
236706f2543SmrgAccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom,
237706f2543Smrg                           XIPropertyValuePtr val, BOOL checkOnly)
238706f2543Smrg{
239706f2543Smrg    DeviceVelocityPtr veloc;
240706f2543Smrg    float v, *ptr = &v;
241706f2543Smrg    int rc;
242706f2543Smrg    int nelem = 1;
243706f2543Smrg
244706f2543Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION))
245706f2543Smrg        return Success;
246706f2543Smrg
247706f2543Smrg    veloc = GetDevicePredictableAccelData(dev);
248706f2543Smrg    if (!veloc)
249706f2543Smrg        return BadValue;
250706f2543Smrg    rc = XIPropToFloat(val, &nelem, &ptr);
251706f2543Smrg
252706f2543Smrg    if(checkOnly)
253706f2543Smrg    {
254706f2543Smrg        if (rc)
255706f2543Smrg            return rc;
256706f2543Smrg	return (v >= 1.0f) ? Success : BadValue;
257706f2543Smrg    }
258706f2543Smrg
259706f2543Smrg    if(v >= 1.0f)
260706f2543Smrg	veloc->min_acceleration = 1/v;
261706f2543Smrg
262706f2543Smrg    return Success;
263706f2543Smrg}
264706f2543Smrg
265706f2543Smrgstatic long
266706f2543SmrgAccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
267706f2543Smrg{
268706f2543Smrg    float fval = 1.0/vel->min_acceleration;
269706f2543Smrg    Atom prop_adapt_decel = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
270706f2543Smrg
271706f2543Smrg    XIChangeDeviceProperty(dev, prop_adapt_decel, XIGetKnownProperty(XATOM_FLOAT), 32,
272706f2543Smrg                           PropModeReplace, 1, &fval, FALSE);
273706f2543Smrg    XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE);
274706f2543Smrg    return XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL, NULL);
275706f2543Smrg}
276706f2543Smrg
277706f2543Smrg
278706f2543Smrg/**
279706f2543Smrg * velocity scaling
280706f2543Smrg */
281706f2543Smrgstatic int
282706f2543SmrgAccelSetScaleProperty(DeviceIntPtr dev, Atom atom,
283706f2543Smrg                      XIPropertyValuePtr val, BOOL checkOnly)
284706f2543Smrg{
285706f2543Smrg    DeviceVelocityPtr vel;
286706f2543Smrg    float v, *ptr = &v;
287706f2543Smrg    int rc;
288706f2543Smrg    int nelem = 1;
289706f2543Smrg
290706f2543Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING))
291706f2543Smrg        return Success;
292706f2543Smrg
293706f2543Smrg    vel = GetDevicePredictableAccelData(dev);
294706f2543Smrg    if (!vel)
295706f2543Smrg        return BadValue;
296706f2543Smrg    rc = XIPropToFloat(val, &nelem, &ptr);
297706f2543Smrg
298706f2543Smrg    if (checkOnly)
299706f2543Smrg    {
300706f2543Smrg        if (rc)
301706f2543Smrg            return rc;
302706f2543Smrg
303706f2543Smrg        return (v > 0) ? Success : BadValue;
304706f2543Smrg    }
305706f2543Smrg
306706f2543Smrg    if(v > 0)
307706f2543Smrg	vel->corr_mul = v;
308706f2543Smrg
309706f2543Smrg    return Success;
310706f2543Smrg}
311706f2543Smrg
312706f2543Smrgstatic long
313706f2543SmrgAccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
314706f2543Smrg{
315706f2543Smrg    float fval = vel->corr_mul;
316706f2543Smrg    Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
317706f2543Smrg
318706f2543Smrg    XIChangeDeviceProperty(dev, prop_velo_scale, XIGetKnownProperty(XATOM_FLOAT), 32,
319706f2543Smrg                           PropModeReplace, 1, &fval, FALSE);
320706f2543Smrg    XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE);
321706f2543Smrg    return XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL);
322706f2543Smrg}
323706f2543Smrg
324706f2543SmrgBOOL
325706f2543SmrgInitializePredictableAccelerationProperties(DeviceIntPtr dev)
326706f2543Smrg{
327706f2543Smrg    DeviceVelocityPtr  vel = GetDevicePredictableAccelData(dev);
328706f2543Smrg
329706f2543Smrg    if(!vel)
330706f2543Smrg	return FALSE;
331706f2543Smrg
332706f2543Smrg    vel->prop_handlers[0] = AccelInitProfileProperty(dev, vel);
333706f2543Smrg    vel->prop_handlers[1] = AccelInitDecelProperty(dev, vel);
334706f2543Smrg    vel->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel);
335706f2543Smrg    vel->prop_handlers[3] = AccelInitScaleProperty(dev, vel);
336706f2543Smrg
337706f2543Smrg    return TRUE;
338706f2543Smrg}
339706f2543Smrg
340706f2543SmrgBOOL
341706f2543SmrgDeletePredictableAccelerationProperties(DeviceIntPtr dev)
342706f2543Smrg{
343706f2543Smrg    DeviceVelocityPtr  vel;
344706f2543Smrg    Atom prop;
345706f2543Smrg    int i;
346706f2543Smrg
347706f2543Smrg    prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
348706f2543Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
349706f2543Smrg    prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
350706f2543Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
351706f2543Smrg    prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
352706f2543Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
353706f2543Smrg    prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
354706f2543Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
355706f2543Smrg
356706f2543Smrg    vel = GetDevicePredictableAccelData(dev);
357706f2543Smrg    for (i = 0; vel && i < NPROPS_PREDICTABLE_ACCEL; i++)
358706f2543Smrg	if (vel->prop_handlers[i])
359706f2543Smrg	    XIUnregisterPropertyHandler(dev, vel->prop_handlers[i]);
360706f2543Smrg
361706f2543Smrg    return TRUE;
362706f2543Smrg}
363706f2543Smrg
364706f2543Smrg/*********************
365706f2543Smrg * Tracking logic
366706f2543Smrg ********************/
367706f2543Smrg
368706f2543Smrgvoid
369706f2543SmrgInitTrackers(DeviceVelocityPtr vel, int ntracker)
370706f2543Smrg{
371706f2543Smrg    if(ntracker < 1){
372706f2543Smrg	ErrorF("(dix ptracc) invalid number of trackers\n");
373706f2543Smrg	return;
374706f2543Smrg    }
375706f2543Smrg    free(vel->tracker);
376706f2543Smrg    vel->tracker = (MotionTrackerPtr)malloc(ntracker * sizeof(MotionTracker));
377706f2543Smrg    memset(vel->tracker, 0, ntracker * sizeof(MotionTracker));
378706f2543Smrg    vel->num_tracker = ntracker;
379706f2543Smrg}
380706f2543Smrg
381706f2543Smrg/**
382706f2543Smrg * return a bit field of possible directions.
383706f2543Smrg * 0 = N, 2 = E, 4 = S, 6 = W, in-between is as you guess.
384706f2543Smrg * There's no reason against widening to more precise directions (<45 degrees),
385706f2543Smrg * should it not perform well. All this is needed for is sort out non-linear
386706f2543Smrg * motion, so precision isn't paramount. However, one should not flag direction
387706f2543Smrg * too narrow, since it would then cut the linear segment to zero size way too
388706f2543Smrg * often.
389706f2543Smrg */
390706f2543Smrgstatic int
391706f2543SmrgDoGetDirection(int dx, int dy){
392706f2543Smrg    float r;
393706f2543Smrg    int i1, i2;
394706f2543Smrg    /* on insignificant mickeys, flag 135 degrees */
395706f2543Smrg    if(abs(dx) < 2 && abs(dy) < 2){
396706f2543Smrg	/* first check diagonal cases */
397706f2543Smrg	if(dx > 0 && dy > 0)
398706f2543Smrg	    return 4+8+16;
399706f2543Smrg	if(dx > 0 && dy < 0)
400706f2543Smrg	    return 1+2+4;
401706f2543Smrg	if(dx < 0 && dy < 0)
402706f2543Smrg	    return 1+128+64;
403706f2543Smrg	if(dx < 0 && dy > 0)
404706f2543Smrg	    return 16+32+64;
405706f2543Smrg        /* check axis-aligned directions */
406706f2543Smrg	if(dx > 0)
407706f2543Smrg            return 2+4+8; /*E*/
408706f2543Smrg        if(dx < 0)
409706f2543Smrg            return 128+64+32; /*W*/
410706f2543Smrg        if(dy > 0)
411706f2543Smrg            return 32+16+8; /*S*/
412706f2543Smrg        if(dy < 0)
413706f2543Smrg            return 128+1+2; /*N*/
414706f2543Smrg        return 255; /* shouldn't happen */
415706f2543Smrg    }
416706f2543Smrg    /* else, compute angle and set appropriate flags */
417706f2543Smrg#ifdef _ISOC99_SOURCE
418706f2543Smrg    r = atan2f(dy, dx);
419706f2543Smrg#else
420706f2543Smrg    r = atan2(dy, dx);
421706f2543Smrg#endif
422706f2543Smrg    /* find direction. We avoid r to become negative,
423706f2543Smrg     * since C has no well-defined modulo for such cases. */
424706f2543Smrg    r = (r+(M_PI*2.5))/(M_PI/4);
425706f2543Smrg    /* this intends to flag 2 directions (90 degrees),
426706f2543Smrg     * except on very well-aligned mickeys. */
427706f2543Smrg    i1 = (int)(r+0.1) % 8;
428706f2543Smrg    i2 = (int)(r+0.9) % 8;
429706f2543Smrg    if(i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
430706f2543Smrg	return 255; /* shouldn't happen */
431706f2543Smrg    return 1 << i1 | 1 << i2;
432706f2543Smrg}
433706f2543Smrg
434706f2543Smrg#define DIRECTION_CACHE_RANGE 5
435706f2543Smrg#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
436706f2543Smrg
437706f2543Smrg/* cache DoGetDirection(). */
438706f2543Smrgstatic int
439706f2543SmrgGetDirection(int dx, int dy){
440706f2543Smrg    static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE];
441706f2543Smrg    int i;
442706f2543Smrg    if (abs(dx) <= DIRECTION_CACHE_RANGE &&
443706f2543Smrg	abs(dy) <= DIRECTION_CACHE_RANGE) {
444706f2543Smrg	/* cacheable */
445706f2543Smrg	i = cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy];
446706f2543Smrg	if(i != 0){
447706f2543Smrg	    return i;
448706f2543Smrg	}else{
449706f2543Smrg	    i = DoGetDirection(dx, dy);
450706f2543Smrg	    cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy] = i;
451706f2543Smrg	    return i;
452706f2543Smrg	}
453706f2543Smrg    }else{
454706f2543Smrg	/* non-cacheable */
455706f2543Smrg	return DoGetDirection(dx, dy);
456706f2543Smrg    }
457706f2543Smrg}
458706f2543Smrg
459706f2543Smrg#undef DIRECTION_CACHE_RANGE
460706f2543Smrg#undef DIRECTION_CACHE_SIZE
461706f2543Smrg
462706f2543Smrg
463706f2543Smrg/* convert offset (age) to array index */
464706f2543Smrg#define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker)
465706f2543Smrg
466706f2543Smrgstatic inline void
467706f2543SmrgFeedTrackers(DeviceVelocityPtr vel, int dx, int dy, int cur_t)
468706f2543Smrg{
469706f2543Smrg    int n;
470706f2543Smrg    for(n = 0; n < vel->num_tracker; n++){
471706f2543Smrg	vel->tracker[n].dx += dx;
472706f2543Smrg	vel->tracker[n].dy += dy;
473706f2543Smrg    }
474706f2543Smrg    n = (vel->cur_tracker + 1) % vel->num_tracker;
475706f2543Smrg    vel->tracker[n].dx = 0;
476706f2543Smrg    vel->tracker[n].dy = 0;
477706f2543Smrg    vel->tracker[n].time = cur_t;
478706f2543Smrg    vel->tracker[n].dir = GetDirection(dx, dy);
479706f2543Smrg    DebugAccelF("(dix prtacc) motion [dx: %i dy: %i dir:%i diff: %i]\n",
480706f2543Smrg                dx, dy, vel->tracker[n].dir,
481706f2543Smrg                cur_t - vel->tracker[vel->cur_tracker].time);
482706f2543Smrg    vel->cur_tracker = n;
483706f2543Smrg}
484706f2543Smrg
485706f2543Smrg/**
486706f2543Smrg * calc velocity for given tracker, with
487706f2543Smrg * velocity scaling.
488706f2543Smrg * This assumes linear motion.
489706f2543Smrg */
490706f2543Smrgstatic float
491706f2543SmrgCalcTracker(DeviceVelocityPtr vel, int offset, int cur_t){
492706f2543Smrg    int index = TRACKER_INDEX(vel, offset);
493706f2543Smrg    float dist = sqrt(  vel->tracker[index].dx * vel->tracker[index].dx
494706f2543Smrg                      + vel->tracker[index].dy * vel->tracker[index].dy);
495706f2543Smrg    int dtime = cur_t - vel->tracker[index].time;
496706f2543Smrg    if(dtime > 0)
497706f2543Smrg	return dist / dtime;
498706f2543Smrg    else
499706f2543Smrg	return 0;/* synonymous for NaN, since we're not C99 */
500706f2543Smrg}
501706f2543Smrg
502706f2543Smrg/* find the most plausible velocity. That is, the most distant
503706f2543Smrg * (in time) tracker which isn't too old, beyond a linear partition,
504706f2543Smrg * or simply too much off initial velocity.
505706f2543Smrg *
506706f2543Smrg * May return 0.
507706f2543Smrg */
508706f2543Smrgstatic float
509706f2543SmrgQueryTrackers(DeviceVelocityPtr vel, int cur_t){
510706f2543Smrg    int n, offset, dir = 255, i = -1, age_ms;
511706f2543Smrg    /* initial velocity: a low-offset, valid velocity */
512706f2543Smrg    float iveloc = 0, res = 0, tmp, vdiff;
513706f2543Smrg    float vfac =  vel->corr_mul * vel->const_acceleration; /* premultiply */
514706f2543Smrg    /* loop from current to older data */
515706f2543Smrg    for(offset = 1; offset < vel->num_tracker; offset++){
516706f2543Smrg	n = TRACKER_INDEX(vel, offset);
517706f2543Smrg
518706f2543Smrg	age_ms = cur_t - vel->tracker[n].time;
519706f2543Smrg
520706f2543Smrg	/* bail out if data is too old and protect from overrun */
521706f2543Smrg	if (age_ms >= vel->reset_time || age_ms < 0) {
522706f2543Smrg	    DebugAccelF("(dix prtacc) query: tracker too old\n");
523706f2543Smrg	    break;
524706f2543Smrg	}
525706f2543Smrg
526706f2543Smrg	/*
527706f2543Smrg	 * this heuristic avoids using the linear-motion velocity formula
528706f2543Smrg	 * in CalcTracker() on motion that isn't exactly linear. So to get
529706f2543Smrg	 * even more precision we could subdivide as a final step, so possible
530706f2543Smrg	 * non-linearities are accounted for.
531706f2543Smrg	 */
532706f2543Smrg	dir &= vel->tracker[n].dir;
533706f2543Smrg	if(dir == 0){
534706f2543Smrg	    DebugAccelF("(dix prtacc) query: no longer linear\n");
535706f2543Smrg	    /* instead of breaking it we might also inspect the partition after,
536706f2543Smrg	     * but actual improvement with this is probably rare. */
537706f2543Smrg	    break;
538706f2543Smrg	}
539706f2543Smrg
540706f2543Smrg	tmp = CalcTracker(vel, offset, cur_t) * vfac;
541706f2543Smrg
542706f2543Smrg	if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) {
543706f2543Smrg	    /* set initial velocity and result */
544706f2543Smrg	    res = iveloc = tmp;
545706f2543Smrg	    i = offset;
546706f2543Smrg	} else if (iveloc != 0 && tmp != 0) {
547706f2543Smrg	    vdiff = fabs(iveloc - tmp);
548706f2543Smrg	    if (vdiff <= vel->max_diff ||
549706f2543Smrg		vdiff/(iveloc + tmp) < vel->max_rel_diff) {
550706f2543Smrg		/* we're in range with the initial velocity,
551706f2543Smrg		 * so this result is likely better
552706f2543Smrg		 * (it contains more information). */
553706f2543Smrg		res = tmp;
554706f2543Smrg		i = offset;
555706f2543Smrg	    }else{
556706f2543Smrg		/* we're not in range, quit - it won't get better. */
557706f2543Smrg		DebugAccelF("(dix prtacc) query: tracker too different:"
558706f2543Smrg		            " old %2.2f initial %2.2f diff: %2.2f\n",
559706f2543Smrg		            tmp, iveloc, vdiff);
560706f2543Smrg		break;
561706f2543Smrg	    }
562706f2543Smrg	}
563706f2543Smrg    }
564706f2543Smrg    if(offset == vel->num_tracker){
565706f2543Smrg	DebugAccelF("(dix prtacc) query: last tracker in effect\n");
566706f2543Smrg	i = vel->num_tracker-1;
567706f2543Smrg    }
568706f2543Smrg    if(i>=0){
569706f2543Smrg        n = TRACKER_INDEX(vel, i);
570706f2543Smrg	DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n",
571706f2543Smrg	            i,
572706f2543Smrg	            vel->tracker[n].dx,
573706f2543Smrg	            vel->tracker[n].dy,
574706f2543Smrg	            cur_t - vel->tracker[n].time);
575706f2543Smrg    }
576706f2543Smrg    return res;
577706f2543Smrg}
578706f2543Smrg
579706f2543Smrg#undef TRACKER_INDEX
580706f2543Smrg
581706f2543Smrg/**
582706f2543Smrg * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
583706f2543Smrg * return true if non-visible state reset is suggested
584706f2543Smrg */
585706f2543Smrgshort
586706f2543SmrgProcessVelocityData2D(
587706f2543Smrg    DeviceVelocityPtr vel,
588706f2543Smrg    int dx,
589706f2543Smrg    int dy,
590706f2543Smrg    int time)
591706f2543Smrg{
592706f2543Smrg    float velocity;
593706f2543Smrg
594706f2543Smrg    vel->last_velocity = vel->velocity;
595706f2543Smrg
596706f2543Smrg    FeedTrackers(vel, dx, dy, time);
597706f2543Smrg
598706f2543Smrg    velocity = QueryTrackers(vel, time);
599706f2543Smrg
600706f2543Smrg    vel->velocity = velocity;
601706f2543Smrg    return velocity == 0;
602706f2543Smrg}
603706f2543Smrg
604706f2543Smrg/**
605706f2543Smrg * this flattens significant ( > 1) mickeys a little bit for more steady
606706f2543Smrg * constant-velocity response
607706f2543Smrg */
608706f2543Smrgstatic inline float
609706f2543SmrgApplySimpleSoftening(int od, int d)
610706f2543Smrg{
611706f2543Smrg    float res = d;
612706f2543Smrg    if (d <= 1 && d >= -1)
613706f2543Smrg        return res;
614706f2543Smrg    if (d > od)
615706f2543Smrg        res -= 0.5;
616706f2543Smrg    else if (d < od)
617706f2543Smrg        res += 0.5;
618706f2543Smrg    return res;
619706f2543Smrg}
620706f2543Smrg
621706f2543Smrg
622706f2543Smrgstatic void
623706f2543SmrgApplySofteningAndConstantDeceleration(
624706f2543Smrg        DeviceVelocityPtr vel,
625706f2543Smrg        int dx,
626706f2543Smrg        int dy,
627706f2543Smrg        float* fdx,
628706f2543Smrg        float* fdy,
629706f2543Smrg        short do_soften)
630706f2543Smrg{
631706f2543Smrg    if (do_soften && vel->use_softening) {
632706f2543Smrg        *fdx = ApplySimpleSoftening(vel->last_dx, dx);
633706f2543Smrg        *fdy = ApplySimpleSoftening(vel->last_dy, dy);
634706f2543Smrg    } else {
635706f2543Smrg        *fdx = dx;
636706f2543Smrg        *fdy = dy;
637706f2543Smrg    }
638706f2543Smrg
639706f2543Smrg    *fdx *= vel->const_acceleration;
640706f2543Smrg    *fdy *= vel->const_acceleration;
641706f2543Smrg}
642706f2543Smrg
643706f2543Smrg/*
644706f2543Smrg * compute the acceleration for given velocity and enforce min_acceleartion
645706f2543Smrg */
646706f2543Smrgfloat
647706f2543SmrgBasicComputeAcceleration(
648706f2543Smrg    DeviceIntPtr dev,
649706f2543Smrg    DeviceVelocityPtr vel,
650706f2543Smrg    float velocity,
651706f2543Smrg    float threshold,
652706f2543Smrg    float acc){
653706f2543Smrg
654706f2543Smrg    float result;
655706f2543Smrg    result = vel->Profile(dev, vel, velocity, threshold, acc);
656706f2543Smrg
657706f2543Smrg    /* enforce min_acceleration */
658706f2543Smrg    if (result < vel->min_acceleration)
659706f2543Smrg	result = vel->min_acceleration;
660706f2543Smrg    return result;
661706f2543Smrg}
662706f2543Smrg
663706f2543Smrg/**
664706f2543Smrg * Compute acceleration. Takes into account averaging, nv-reset, etc.
665706f2543Smrg */
666706f2543Smrgstatic float
667706f2543SmrgComputeAcceleration(
668706f2543Smrg    DeviceIntPtr dev,
669706f2543Smrg    DeviceVelocityPtr vel,
670706f2543Smrg    float threshold,
671706f2543Smrg    float acc){
672706f2543Smrg    float res;
673706f2543Smrg
674706f2543Smrg    if(vel->velocity <= 0){
675706f2543Smrg	DebugAccelF("(dix ptracc) profile skipped\n");
676706f2543Smrg        /*
677706f2543Smrg         * If we have no idea about device velocity, don't pretend it.
678706f2543Smrg         */
679706f2543Smrg	return 1;
680706f2543Smrg    }
681706f2543Smrg
682706f2543Smrg    if(vel->average_accel && vel->velocity != vel->last_velocity){
683706f2543Smrg	/* use simpson's rule to average acceleration between
684706f2543Smrg	 * current and previous velocity.
685706f2543Smrg	 * Though being the more natural choice, it causes a minor delay
686706f2543Smrg	 * in comparison, so it can be disabled. */
687706f2543Smrg	res = BasicComputeAcceleration(
688706f2543Smrg	          dev, vel, vel->velocity, threshold, acc);
689706f2543Smrg	res += BasicComputeAcceleration(
690706f2543Smrg	          dev, vel, vel->last_velocity, threshold, acc);
691706f2543Smrg	res += 4.0f * BasicComputeAcceleration(dev, vel,
692706f2543Smrg	                   (vel->last_velocity + vel->velocity) / 2,
693706f2543Smrg	                   threshold, acc);
694706f2543Smrg	res /= 6.0f;
695706f2543Smrg	DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n",
696706f2543Smrg	            vel->velocity, vel->last_velocity, res);
697706f2543Smrg        return res;
698706f2543Smrg    }else{
699706f2543Smrg	res = BasicComputeAcceleration(dev, vel,
700706f2543Smrg	                               vel->velocity, threshold, acc);
701706f2543Smrg	DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n",
702706f2543Smrg               vel->velocity, res);
703706f2543Smrg	return res;
704706f2543Smrg    }
705706f2543Smrg}
706706f2543Smrg
707706f2543Smrg
708706f2543Smrg/*****************************************
709706f2543Smrg *  Acceleration functions and profiles
710706f2543Smrg ****************************************/
711706f2543Smrg
712706f2543Smrg/**
713706f2543Smrg * Polynomial function similar previous one, but with f(1) = 1
714706f2543Smrg */
715706f2543Smrgstatic float
716706f2543SmrgPolynomialAccelerationProfile(
717706f2543Smrg    DeviceIntPtr dev,
718706f2543Smrg    DeviceVelocityPtr vel,
719706f2543Smrg    float velocity,
720706f2543Smrg    float ignored,
721706f2543Smrg    float acc)
722706f2543Smrg{
723706f2543Smrg   return pow(velocity, (acc - 1.0) * 0.5);
724706f2543Smrg}
725706f2543Smrg
726706f2543Smrg
727706f2543Smrg/**
728706f2543Smrg * returns acceleration for velocity.
729706f2543Smrg * This profile selects the two functions like the old scheme did
730706f2543Smrg */
731706f2543Smrgstatic float
732706f2543SmrgClassicProfile(
733706f2543Smrg    DeviceIntPtr dev,
734706f2543Smrg    DeviceVelocityPtr vel,
735706f2543Smrg    float velocity,
736706f2543Smrg    float threshold,
737706f2543Smrg    float acc)
738706f2543Smrg{
739706f2543Smrg    if (threshold > 0) {
740706f2543Smrg	return SimpleSmoothProfile (dev,
741706f2543Smrg	                            vel,
742706f2543Smrg	                            velocity,
743706f2543Smrg                                    threshold,
744706f2543Smrg                                    acc);
745706f2543Smrg    } else {
746706f2543Smrg	return PolynomialAccelerationProfile (dev,
747706f2543Smrg	                                      vel,
748706f2543Smrg	                                      velocity,
749706f2543Smrg                                              0,
750706f2543Smrg                                              acc);
751706f2543Smrg    }
752706f2543Smrg}
753706f2543Smrg
754706f2543Smrg
755706f2543Smrg/**
756706f2543Smrg * Power profile
757706f2543Smrg * This has a completely smooth transition curve, i.e. no jumps in the
758706f2543Smrg * derivatives.
759706f2543Smrg *
760706f2543Smrg * This has the expense of overall response dependency on min-acceleration.
761706f2543Smrg * In effect, min_acceleration mimics const_acceleration in this profile.
762706f2543Smrg */
763706f2543Smrgstatic float
764706f2543SmrgPowerProfile(
765706f2543Smrg    DeviceIntPtr dev,
766706f2543Smrg    DeviceVelocityPtr vel,
767706f2543Smrg    float velocity,
768706f2543Smrg    float threshold,
769706f2543Smrg    float acc)
770706f2543Smrg{
771706f2543Smrg    float vel_dist;
772706f2543Smrg
773706f2543Smrg    acc = (acc-1.0) * 0.1f + 1.0; /* without this, acc of 2 is unuseable */
774706f2543Smrg
775706f2543Smrg    if (velocity <= threshold)
776706f2543Smrg        return vel->min_acceleration;
777706f2543Smrg    vel_dist = velocity - threshold;
778706f2543Smrg    return (pow(acc, vel_dist)) * vel->min_acceleration;
779706f2543Smrg}
780706f2543Smrg
781706f2543Smrg
782706f2543Smrg/**
783706f2543Smrg * just a smooth function in [0..1] -> [0..1]
784706f2543Smrg *  - point symmetry at 0.5
785706f2543Smrg *  - f'(0) = f'(1) = 0
786706f2543Smrg *  - starts faster than a sinoid
787706f2543Smrg *  - smoothness C1 (Cinf if you dare to ignore endpoints)
788706f2543Smrg */
789706f2543Smrgstatic inline float
790706f2543SmrgCalcPenumbralGradient(float x){
791706f2543Smrg    x *= 2.0f;
792706f2543Smrg    x -= 1.0f;
793706f2543Smrg    return 0.5f + (x * sqrt(1.0f - x*x) + asin(x))/M_PI;
794706f2543Smrg}
795706f2543Smrg
796706f2543Smrg
797706f2543Smrg/**
798706f2543Smrg * acceleration function similar to classic accelerated/unaccelerated,
799706f2543Smrg * but with smooth transition in between (and towards zero for adaptive dec.).
800706f2543Smrg */
801706f2543Smrgstatic float
802706f2543SmrgSimpleSmoothProfile(
803706f2543Smrg    DeviceIntPtr dev,
804706f2543Smrg    DeviceVelocityPtr vel,
805706f2543Smrg    float velocity,
806706f2543Smrg    float threshold,
807706f2543Smrg    float acc)
808706f2543Smrg{
809706f2543Smrg    if(velocity < 1.0f)
810706f2543Smrg        return CalcPenumbralGradient(0.5 + velocity*0.5) * 2.0f - 1.0f;
811706f2543Smrg    if(threshold < 1.0f)
812706f2543Smrg        threshold = 1.0f;
813706f2543Smrg    if (velocity <= threshold)
814706f2543Smrg        return 1;
815706f2543Smrg    velocity /= threshold;
816706f2543Smrg    if (velocity >= acc)
817706f2543Smrg        return acc;
818706f2543Smrg    else
819706f2543Smrg        return 1.0f + (CalcPenumbralGradient(velocity/acc) * (acc - 1.0f));
820706f2543Smrg}
821706f2543Smrg
822706f2543Smrg
823706f2543Smrg/**
824706f2543Smrg * This profile uses the first half of the penumbral gradient as a start
825706f2543Smrg * and then scales linearly.
826706f2543Smrg */
827706f2543Smrgstatic float
828706f2543SmrgSmoothLinearProfile(
829706f2543Smrg    DeviceIntPtr dev,
830706f2543Smrg    DeviceVelocityPtr vel,
831706f2543Smrg    float velocity,
832706f2543Smrg    float threshold,
833706f2543Smrg    float acc)
834706f2543Smrg{
835706f2543Smrg    float res, nv;
836706f2543Smrg
837706f2543Smrg    if(acc > 1.0f)
838706f2543Smrg        acc -= 1.0f; /*this is so acc = 1 is no acceleration */
839706f2543Smrg    else
840706f2543Smrg        return 1.0f;
841706f2543Smrg
842706f2543Smrg    nv = (velocity - threshold) * acc * 0.5f;
843706f2543Smrg
844706f2543Smrg    if(nv < 0){
845706f2543Smrg        res = 0;
846706f2543Smrg    }else if(nv < 2){
847706f2543Smrg        res = CalcPenumbralGradient(nv*0.25f)*2.0f;
848706f2543Smrg    }else{
849706f2543Smrg        nv -= 2.0f;
850706f2543Smrg        res = nv * 2.0f / M_PI  /* steepness of gradient at 0.5 */
851706f2543Smrg              + 1.0f; /* gradient crosses 2|1 */
852706f2543Smrg    }
853706f2543Smrg    res += vel->min_acceleration;
854706f2543Smrg    return res;
855706f2543Smrg}
856706f2543Smrg
857706f2543Smrg
858706f2543Smrg/**
859706f2543Smrg * From 0 to threshold, the response graduates smoothly from min_accel to
860706f2543Smrg * acceleration. Beyond threshold it is exactly the specified acceleration.
861706f2543Smrg */
862706f2543Smrgstatic float
863706f2543SmrgSmoothLimitedProfile(
864706f2543Smrg    DeviceIntPtr dev,
865706f2543Smrg    DeviceVelocityPtr vel,
866706f2543Smrg    float velocity,
867706f2543Smrg    float threshold,
868706f2543Smrg    float acc)
869706f2543Smrg{
870706f2543Smrg    float res;
871706f2543Smrg
872706f2543Smrg    if(velocity >= threshold || threshold == 0.0f)
873706f2543Smrg	return acc;
874706f2543Smrg
875706f2543Smrg    velocity /= threshold; /* should be [0..1[ now */
876706f2543Smrg
877706f2543Smrg    res = CalcPenumbralGradient(velocity) * (acc - vel->min_acceleration);
878706f2543Smrg
879706f2543Smrg    return vel->min_acceleration + res;
880706f2543Smrg}
881706f2543Smrg
882706f2543Smrg
883706f2543Smrgstatic float
884706f2543SmrgLinearProfile(
885706f2543Smrg    DeviceIntPtr dev,
886706f2543Smrg    DeviceVelocityPtr vel,
887706f2543Smrg    float velocity,
888706f2543Smrg    float threshold,
889706f2543Smrg    float acc)
890706f2543Smrg{
891706f2543Smrg    return acc * velocity;
892706f2543Smrg}
893706f2543Smrg
894706f2543Smrgstatic float
895706f2543SmrgNoProfile(
896706f2543Smrg    DeviceIntPtr dev,
897706f2543Smrg    DeviceVelocityPtr vel,
898706f2543Smrg    float velocity,
899706f2543Smrg    float threshold,
900706f2543Smrg    float acc)
901706f2543Smrg{
902706f2543Smrg    return 1.0f;
903706f2543Smrg}
904706f2543Smrg
905706f2543Smrgstatic PointerAccelerationProfileFunc
906706f2543SmrgGetAccelerationProfile(
907706f2543Smrg    DeviceVelocityPtr vel,
908706f2543Smrg    int profile_num)
909706f2543Smrg{
910706f2543Smrg    switch(profile_num){
911706f2543Smrg        case AccelProfileClassic:
912706f2543Smrg            return ClassicProfile;
913706f2543Smrg        case AccelProfileDeviceSpecific:
914706f2543Smrg            return vel->deviceSpecificProfile;
915706f2543Smrg        case AccelProfilePolynomial:
916706f2543Smrg            return PolynomialAccelerationProfile;
917706f2543Smrg        case AccelProfileSmoothLinear:
918706f2543Smrg            return SmoothLinearProfile;
919706f2543Smrg        case AccelProfileSimple:
920706f2543Smrg            return SimpleSmoothProfile;
921706f2543Smrg        case AccelProfilePower:
922706f2543Smrg            return PowerProfile;
923706f2543Smrg        case AccelProfileLinear:
924706f2543Smrg            return LinearProfile;
925706f2543Smrg        case AccelProfileSmoothLimited:
926706f2543Smrg            return SmoothLimitedProfile;
927706f2543Smrg        case AccelProfileNone:
928706f2543Smrg            return NoProfile;
929706f2543Smrg        default:
930706f2543Smrg            return NULL;
931706f2543Smrg    }
932706f2543Smrg}
933706f2543Smrg
934706f2543Smrg/**
935706f2543Smrg * Set the profile by number.
936706f2543Smrg * Intended to make profiles exchangeable at runtime.
937706f2543Smrg * If you created a profile, give it a number here and in the header to
938706f2543Smrg * make it selectable. In case some profile-specific init is needed, here
939706f2543Smrg * would be a good place, since FreeVelocityData() also calls this with
940706f2543Smrg * PROFILE_UNINITIALIZE.
941706f2543Smrg *
942706f2543Smrg * returns FALSE if profile number is unavailable, TRUE otherwise.
943706f2543Smrg */
944706f2543Smrgint
945706f2543SmrgSetAccelerationProfile(
946706f2543Smrg    DeviceVelocityPtr vel,
947706f2543Smrg    int profile_num)
948706f2543Smrg{
949706f2543Smrg    PointerAccelerationProfileFunc profile;
950706f2543Smrg    profile = GetAccelerationProfile(vel, profile_num);
951706f2543Smrg
952706f2543Smrg    if(profile == NULL && profile_num != PROFILE_UNINITIALIZE)
953706f2543Smrg	return FALSE;
954706f2543Smrg
955706f2543Smrg    /* Here one could free old profile-private data */
956706f2543Smrg    free(vel->profile_private);
957706f2543Smrg    vel->profile_private = NULL;
958706f2543Smrg    /* Here one could init profile-private data */
959706f2543Smrg    vel->Profile = profile;
960706f2543Smrg    vel->statistics.profile_number = profile_num;
961706f2543Smrg    return TRUE;
962706f2543Smrg}
963706f2543Smrg
964706f2543Smrg/**********************************************
965706f2543Smrg * driver interaction
966706f2543Smrg **********************************************/
967706f2543Smrg
968706f2543Smrg
969706f2543Smrg/**
970706f2543Smrg * device-specific profile
971706f2543Smrg *
972706f2543Smrg * The device-specific profile is intended as a hook for a driver
973706f2543Smrg * which may want to provide an own acceleration profile.
974706f2543Smrg * It should not rely on profile-private data, instead
975706f2543Smrg * it should do init/uninit in the driver (ie. with DEVICE_INIT and friends).
976706f2543Smrg * Users may override or choose it.
977706f2543Smrg */
978706f2543Smrgvoid
979706f2543SmrgSetDeviceSpecificAccelerationProfile(
980706f2543Smrg        DeviceVelocityPtr vel,
981706f2543Smrg        PointerAccelerationProfileFunc profile)
982706f2543Smrg{
983706f2543Smrg    if(vel)
984706f2543Smrg	vel->deviceSpecificProfile = profile;
985706f2543Smrg}
986706f2543Smrg
987706f2543Smrg/**
988706f2543Smrg * Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if
989706f2543Smrg * the predictable acceleration scheme is not in effect.
990706f2543Smrg */
991706f2543SmrgDeviceVelocityPtr
992706f2543SmrgGetDevicePredictableAccelData(
993706f2543Smrg	DeviceIntPtr dev)
994706f2543Smrg{
995706f2543Smrg    /*sanity check*/
996706f2543Smrg    if(!dev){
997706f2543Smrg	ErrorF("[dix] accel: DeviceIntPtr was NULL");
998706f2543Smrg	return NULL;
999706f2543Smrg    }
1000706f2543Smrg    if( dev->valuator &&
1001706f2543Smrg	dev->valuator->accelScheme.AccelSchemeProc ==
1002706f2543Smrg	    acceleratePointerPredictable &&
1003706f2543Smrg	dev->valuator->accelScheme.accelData != NULL){
1004706f2543Smrg
1005706f2543Smrg	return (DeviceVelocityPtr)dev->valuator->accelScheme.accelData;
1006706f2543Smrg    }
1007706f2543Smrg    return NULL;
1008706f2543Smrg}
1009706f2543Smrg
1010706f2543Smrg/********************************
1011706f2543Smrg *  acceleration schemes
1012706f2543Smrg *******************************/
1013706f2543Smrg
1014706f2543Smrg/**
1015706f2543Smrg * Modifies valuators in-place.
1016706f2543Smrg * This version employs a velocity approximation algorithm to
1017706f2543Smrg * enable fine-grained predictable acceleration profiles.
1018706f2543Smrg */
1019706f2543Smrgvoid
1020706f2543SmrgacceleratePointerPredictable(
1021706f2543Smrg    DeviceIntPtr dev,
1022706f2543Smrg    int first_valuator,
1023706f2543Smrg    int num_valuators,
1024706f2543Smrg    int *valuators,
1025706f2543Smrg    int evtime)
1026706f2543Smrg{
1027706f2543Smrg    float mult = 0.0;
1028706f2543Smrg    int dx = 0, dy = 0;
1029706f2543Smrg    int *px = NULL, *py = NULL;
1030706f2543Smrg    DeviceVelocityPtr velocitydata =
1031706f2543Smrg	(DeviceVelocityPtr) dev->valuator->accelScheme.accelData;
1032706f2543Smrg    float fdx, fdy, tmp; /* no need to init */
1033706f2543Smrg    Bool soften = TRUE;
1034706f2543Smrg
1035706f2543Smrg    if (!num_valuators || !valuators || !velocitydata)
1036706f2543Smrg        return;
1037706f2543Smrg
1038706f2543Smrg    if (velocitydata->statistics.profile_number == AccelProfileNone &&
1039706f2543Smrg	velocitydata->const_acceleration == 1.0f) {
1040706f2543Smrg	return; /*we're inactive anyway, so skip the whole thing.*/
1041706f2543Smrg    }
1042706f2543Smrg
1043706f2543Smrg    if (first_valuator == 0) {
1044706f2543Smrg        dx = valuators[0];
1045706f2543Smrg        px = &valuators[0];
1046706f2543Smrg    }
1047706f2543Smrg    if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
1048706f2543Smrg        dy = valuators[1 - first_valuator];
1049706f2543Smrg        py = &valuators[1 - first_valuator];
1050706f2543Smrg    }
1051706f2543Smrg
1052706f2543Smrg    if (dx || dy){
1053706f2543Smrg        /* reset non-visible state? */
1054706f2543Smrg        if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
1055706f2543Smrg            soften = FALSE;
1056706f2543Smrg        }
1057706f2543Smrg
1058706f2543Smrg        if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
1059706f2543Smrg            /* invoke acceleration profile to determine acceleration */
1060706f2543Smrg            mult = ComputeAcceleration (dev, velocitydata,
1061706f2543Smrg					dev->ptrfeed->ctrl.threshold,
1062706f2543Smrg					(float)dev->ptrfeed->ctrl.num /
1063706f2543Smrg					(float)dev->ptrfeed->ctrl.den);
1064706f2543Smrg
1065706f2543Smrg            if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
1066706f2543Smrg                ApplySofteningAndConstantDeceleration( velocitydata,
1067706f2543Smrg						       dx, dy,
1068706f2543Smrg						       &fdx, &fdy,
1069706f2543Smrg						       (mult > 1.0) && soften);
1070706f2543Smrg
1071706f2543Smrg                if (dx) {
1072706f2543Smrg                    tmp = mult * fdx + dev->last.remainder[0];
1073706f2543Smrg                    /* Since it may not be apparent: lrintf() does not offer
1074706f2543Smrg                     * strong statements about rounding; however because we
1075706f2543Smrg                     * process each axis conditionally, there's no danger
1076706f2543Smrg                     * of a toggling remainder. Its lack of guarantees likely
1077706f2543Smrg                     * makes it faster on the average target. */
1078706f2543Smrg                    *px = lrintf(tmp);
1079706f2543Smrg                    dev->last.remainder[0] = tmp - (float)*px;
1080706f2543Smrg                }
1081706f2543Smrg                if (dy) {
1082706f2543Smrg                    tmp = mult * fdy + dev->last.remainder[1];
1083706f2543Smrg                    *py = lrintf(tmp);
1084706f2543Smrg                    dev->last.remainder[1] = tmp - (float)*py;
1085706f2543Smrg                }
1086706f2543Smrg                DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n",
1087706f2543Smrg                            *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy);
1088706f2543Smrg            }
1089706f2543Smrg        }
1090706f2543Smrg    }
1091706f2543Smrg    /* remember last motion delta (for softening/slow movement treatment) */
1092706f2543Smrg    velocitydata->last_dx = dx;
1093706f2543Smrg    velocitydata->last_dy = dy;
1094706f2543Smrg}
1095706f2543Smrg
1096706f2543Smrg
1097706f2543Smrg
1098706f2543Smrg/**
1099706f2543Smrg * Originally a part of xf86PostMotionEvent; modifies valuators
1100706f2543Smrg * in-place. Retained mostly for embedded scenarios.
1101706f2543Smrg */
1102706f2543Smrgvoid
1103706f2543SmrgacceleratePointerLightweight(
1104706f2543Smrg    DeviceIntPtr dev,
1105706f2543Smrg    int first_valuator,
1106706f2543Smrg    int num_valuators,
1107706f2543Smrg    int *valuators,
1108706f2543Smrg    int ignored)
1109706f2543Smrg{
1110706f2543Smrg    float mult = 0.0;
1111706f2543Smrg    int dx = 0, dy = 0;
1112706f2543Smrg    int *px = NULL, *py = NULL;
1113706f2543Smrg
1114706f2543Smrg    if (!num_valuators || !valuators)
1115706f2543Smrg        return;
1116706f2543Smrg
1117706f2543Smrg    if (first_valuator == 0) {
1118706f2543Smrg        dx = valuators[0];
1119706f2543Smrg        px = &valuators[0];
1120706f2543Smrg    }
1121706f2543Smrg    if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
1122706f2543Smrg        dy = valuators[1 - first_valuator];
1123706f2543Smrg        py = &valuators[1 - first_valuator];
1124706f2543Smrg    }
1125706f2543Smrg
1126706f2543Smrg    if (!dx && !dy)
1127706f2543Smrg        return;
1128706f2543Smrg
1129706f2543Smrg    if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
1130706f2543Smrg        /* modeled from xf86Events.c */
1131706f2543Smrg        if (dev->ptrfeed->ctrl.threshold) {
1132706f2543Smrg            if ((abs(dx) + abs(dy)) >= dev->ptrfeed->ctrl.threshold) {
1133706f2543Smrg                dev->last.remainder[0] = ((float)dx *
1134706f2543Smrg                                             (float)(dev->ptrfeed->ctrl.num)) /
1135706f2543Smrg                                             (float)(dev->ptrfeed->ctrl.den) +
1136706f2543Smrg                                            dev->last.remainder[0];
1137706f2543Smrg                if (px) {
1138706f2543Smrg                    *px = (int)dev->last.remainder[0];
1139706f2543Smrg                    dev->last.remainder[0] = dev->last.remainder[0] -
1140706f2543Smrg                                                (float)(*px);
1141706f2543Smrg                }
1142706f2543Smrg
1143706f2543Smrg                dev->last.remainder[1] = ((float)dy *
1144706f2543Smrg                                             (float)(dev->ptrfeed->ctrl.num)) /
1145706f2543Smrg                                             (float)(dev->ptrfeed->ctrl.den) +
1146706f2543Smrg                                            dev->last.remainder[1];
1147706f2543Smrg                if (py) {
1148706f2543Smrg                    *py = (int)dev->last.remainder[1];
1149706f2543Smrg                    dev->last.remainder[1] = dev->last.remainder[1] -
1150706f2543Smrg                                                (float)(*py);
1151706f2543Smrg                }
1152706f2543Smrg            }
1153706f2543Smrg        }
1154706f2543Smrg        else {
1155706f2543Smrg	    mult = pow((float)dx * (float)dx + (float)dy * (float)dy,
1156706f2543Smrg                       ((float)(dev->ptrfeed->ctrl.num) /
1157706f2543Smrg                        (float)(dev->ptrfeed->ctrl.den) - 1.0) /
1158706f2543Smrg                       2.0) / 2.0;
1159706f2543Smrg            if (dx) {
1160706f2543Smrg                dev->last.remainder[0] = mult * (float)dx +
1161706f2543Smrg                                            dev->last.remainder[0];
1162706f2543Smrg                *px = (int)dev->last.remainder[0];
1163706f2543Smrg                dev->last.remainder[0] = dev->last.remainder[0] -
1164706f2543Smrg                                            (float)(*px);
1165706f2543Smrg            }
1166706f2543Smrg            if (dy) {
1167706f2543Smrg                dev->last.remainder[1] = mult * (float)dy +
1168706f2543Smrg                                            dev->last.remainder[1];
1169706f2543Smrg                *py = (int)dev->last.remainder[1];
1170706f2543Smrg                dev->last.remainder[1] = dev->last.remainder[1] -
1171706f2543Smrg                                            (float)(*py);
1172706f2543Smrg            }
1173706f2543Smrg        }
1174706f2543Smrg    }
1175706f2543Smrg}
1176